해당 스터디는 90DaysOfDevOps
https://github.com/MichaelCade/90DaysOfDevOps
를 기반으로 진행한 내용입니다.
Day 21 - Azure ARM now got Bicep
IaC (Infrastructure as Code, 코드형 인프라) 는 마우스로 대시보드에서 직접 클릭하여 수동으로 인프라를 관리하는 대신, 코드를 통해 인프라를 정의하고 관리하는 방식이다.
기존의 클라우드에 인프라를 생성하는 방법은, 포털에 들어가서 가상 네트워크, 게이트 웨이 등을 직접 필요할때 다시 들어가서 배포하므로 일관성이 없으며, 실수가 발생하기 쉽다.
하지만, IaC 방식을 사용하게 되면, 미리 설계된 청사진 (BluePrint)를 만들어두었기 때문에, 자동으로 동일한 구조의 인프라를 생성해준다.
해당 방식을 통해
IaC 도구의 예시로는 AWS의 CloudFormation, 멀리클라우드를 지원하는 Terraform, Azure의 ARM 템플릿, Powershell, CLI 등이 존재한다.
IaC 도구를 이해하기 위해서는 반드시 알아야되는 개념으로, 두 가지 코드 작성 방식의 차이점을 알아야한다.
어떻게 (HOW) 할 것인지, 작업의 절차와 순서를 하나하나 명시하는 방식
무엇(What)을 원하는지, 최종적으로 원하는 결과(상태)를 정의하는 방식
그 결과에 도달하기 위한 과정은 도구 알아서 처리함.
Azure의 핵심 관리 계층 (Management Layer)이다.
Azure 포털, CLI, API 등 사용자가 Azure와 상호작용하는 모든 요청은 반드시 해당 ARM을 거쳐 처리된다.
즉, Azure 포털에서 마우스로 리소스를 만들든, Azure CLI나 PowerShell로 명령어를 입력하든, REST API로 직접 요청을 보내든, 모든 상호작용은 예외 없이 ARM으로 전달된다.
추가적으로, ARM이 직접적으로 이해하고 처리할 수 있는 언어는 JSON 형식의 ARM 템플릿으로, 과거에는 JSON 코드를 직접 작성하여 인프라를 배포하였다.
Bicep은 복잡하고 어려운 JSON ARM 템플릿 작성을 피하기 위해 Microsoft가 직접 만든 도메인 특화 언어(DSL)이다.
즉, 오직 Azure 인프라를 코드로 정의하기 위한 목적으로 탄생한 선언형 언어이다.
Bicep의 핵심은 트랜스파일레이션 (Transpilation)이라는 변환과정에 존재한다.
결국, Bicep은 개발자가 JSON의 복잡성 (수많은 중괄호, 따옴표, 복잡한 함수 문법 등)을 신경쓰지 않게 해주는 편리한 추상화 계층이다.
기존의 ARM 템플릿 (JSON) 대신 Bicep을 사용해야 하는 이유는 다음과 같은 장점들이 존재한다.
더 간단한 구문 (Simpler Syntax): 복잡한 괄호와 따옴표로 가득 찬 JSON에 비해 Bicep은 훨씬 간결하고 가독성이 높아 코드 작성이 쉽고 실수를 줄일 수 있음.
모듈화 (Modularity): 특정 리소스 묶음을 '모듈'로 만들어 저장해두고, 필요할 때마다 가져와 재사용할 수 있어 코드의 효율성과 재사용성이 극대화되는 장점이 존재함.
자동화된 종속성 관리 (Automated Dependency Management): 리소스 간의 배포 순서 (종속성)를 대부분 자동으로 파악하여 처리해주어 편리함.
인텔리센스 (Intellisense): Visual Studio Code와 같은 편집기에서 코드 자동 완성, 구문 검사 등 강력한 기능을 지원하여 생산성을 높여줌.
Azure 네이티브 (Azure Native): Microsoft가 직접 만든 언어이므로, Azure에 새로운 서비스나 기능이 출시되면 출시 첫날(Day-One)부터 바로 Bicep으로 지원됨.
상태 관리 없음 (No State Management): Terraform처럼 별도의 상태 파일 (.tfstate)을 관리할 필요 없이, Bicep은 현재 Azure의 실제 상태를 직접 소스로 삼아 작업을 수행함.
쉬운 전환 (Easy Transition): 기존에 사용하던 복잡한 JSON ARM 템플릿을 Bicep 코드로 변환하는 기능을 제공하여 마이그레이션이 용이함.
다만, Bicep은 Azure 환경에 고도로 통합되어 있어 멀티클라우드 환경에는 적합하지 않다.
즉, Azure 환경이 아닌 경우에 IaC를 사용하려하면, Terraform이나 특정 벤더사 IaC를 사용하는 것이 좋다.
강의에서 제시한 단계를 참고하여 Bicep의 핵심 기능들을 각 단계별 예시 코드를 통해 사용법을 알아보겠다.
1단계: 리소스 정의
가장 먼저 할 일은 배포하려는 Azure 리소스를 코드로 선언하는 것이다.
resource 키워드를 사용하여, 리소스의 심볼릭 이름 (Bicep 파일 내에서 사용할 별칭), 리소스 종류와 API 버전, 그리고 중괄호 {} 안에 필요한 속성들을 정의
// 'storage.bicep' 파일
// Azure 스토리지 계정 리소스 정의
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'stuniquebicepdemo' // 해당이름은 Azure 전체에서 고유해야 함.
location: 'koreacentral' // 리소스가 배포될 위치
sku: {
name: 'Standard_LRS' // 성능 및 복제 옵션 (LRS: 로컬 중복 저장소)
}
kind: 'StorageV2' // 스토리지 계정의 종류
}
해당 코드는 storageAccount라는 별칭으로, 한국 중부(koreacentral) 지역에 Standard_LRS 등급의 범용 v2 스토리지 계정 하나를 만드는 가장 기본적인 설계도이다.
모든 값이 하드코딩되어 있어 재사용성이 떨어진다.
2단계: 매개변수와 변수
하드코딩된 값을 외부에서 받아오거나 파일 내부에서 동적으로 생성하기 위해 매개변수(param)와 변수(var)를 사용한다.
param: Bicep 파일을 배포할 때 사용자가 직접 전달하는 값 (예: 배포 환경, 위치)
var: Bicep 파일 내부에서 값을 조합하거나 가공하여 사용하는 로컬 변수입
// 'storage.bicep' 파일
// 배포 환경 이름을 외부에서 입력받음
@allowed([ 'dev', 'prod' ]) // 'dev' 또는 'prod' 값만 허용
param environmentName string = 'dev' // 기본값 'dev'로 설정
// 배포 위치 외부에서 입력받기
param location string = 'koreacentral'
// 고유한 스토리지 계정 이름을 동적으로 생성
var storageAccountName = 'st${environmentName}${uniqueString(resourceGroup().id)}'
// ------------------ 리소스 정의 ------------------
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName // 변수로 생성한 이름 사용
location: location // 매개변수로 받은 위치 사용
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
해당 매개변수와 변수를 사용하여 environmentName과 location을 배포 시점에 지정할 수 있다.
이때, storageAccountName 변수는 입력받은 환경 이름과 리소스 그룹의 고유 ID를 조합하여 절대 겹치지 않는 스토리지 계정 이름을 만들어준다.
3단계: 조건부 표현식
환경에 따라 다른 구성을 적용하기 위해 조건부 표현식을 사용한다.
가장 흔하게는 삼항 연산자 (조건 ? 참일때 값 : 거짓일때 값)를 사용한다.
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
sku: {
// environmentName이 'prod'이면 'Standard_GRS', 아니면 'Standard_LRS'를 사용 명세
name: (environmentName == 'prod') ? 'Standard_GRS' : 'Standard_LRS'
}
kind: 'StorageV2'
}
sku.name 속성에 조건문을 추가하여, 해당 코드를 prod 환경으로 배포하면 데이터센터 간 복제가 되는 비싼 GRS(지역 중복 저장소)가, dev 환경으로 배포하면 비용이 저렴한 LRS가 자동으로 선택되게 한다.
4단계: 루프 (대량으로 만들기)
동일한 형태의 리소스를 여러 개 만들어야 할 때 루프(for)를 사용한다.
코드를 복사-붙여넣기 할 필요 없이 원하는 개수만큼 리소스를 생성할 수 있다.
param storageCount int = 3 // 몇 개의 스토리지 계정을 만들지 외부에서 입력받음
//변수 정의는 이전과 동일
// storageCount 개수만큼 스토리지 계정을 생성
resource storageAccounts 'Microsoft.Storage/storageAccounts@2023-01-01' = [for i in range(0, storageCount): {
// 이름이 겹치지 않도록 루프 인덱스(i)를 이름에 포함시킴
name: 'st${environmentName}${uniqueString(resourceGroup().id)}${i}'
location: location
sku: {
name: (environmentName == 'prod') ? 'Standard_GRS' : 'Standard_LRS'
}
kind: 'StorageV2'
}]
resource를 [for ... : { ... }] 구문으로 감싸서 선언하고, 배포 시 storageCount 매개변수에 3을 전달하면, 이름 끝에 0, 1, 2가 붙은 3개의 스토리지 계정이 동시에 생성된다.
5단계: 모듈
Bicep 파일을 모듈 (Module)로 만들어 다른 Bicep 파일에서 가져와 사용할 수 있다.
// 'storage.bicep' 모듈을 사용하여 개발 환경용 스토리지를 배포
module devStorage 'storage.bicep' = {
name: 'devStorageDeployment' // 모듈 배포 작업의 이름
params: {
location: 'koreacentral'
environmentName: 'dev'
}
}
// 'storage.bicep' 모듈을 재사용하여 운영 환경용 스토리지를 배포
module prodStorage 'storage.bicep' = {
name: 'prodStorageDeployment'
params: {
location: 'japaneast'
environmentName: 'prod'
}
}
module 키워드를 사용해 이 부품을 두 번 호출하여, 한 번은 한국 중부 리전에 개발용으로, 한 번은 일본 동부 리전에 운영용으로 동일한 코드를 재사용하여 각각 다른 구성의 리소스를 손쉽게 배포 가능하다.
Azure Bicep은 복잡한 Azure의 JSON ARM 템플릿의 한계를 극복하기 위해 등장한 IaC 솔루션이다.
간결한 선언형 언어를 통해 개발자는 Azure 인프라를 더 쉽고 효율적으로 자동화할 수 있으며, 일관성 있고 재현 가능한 환경을 구축할 수 있도록 지원한다.
다만 Bicep은 Azure 환경에 특화되어 있으므로, 여러 클라우드를 함께 사용하거나 (멀티 클라우드) AWS 같은 다른 플랫폼을 관리해야 할 경우에는 Terraform이나 AWS CloudFormation 같은 도구를 사용하는 것이 더 적합하다.