티스토리 뷰

이 글은 독일 Allianz에서 아케텍트로 재직 중인 Julie Ng님의 블로그 글을 번역한 것으로 사전에 번역 게시에 대해 협의가 되었습니다. 좋은 글을 공유해주시고 게시를 허락해주신 Julie Ng님께 감사 드립니다.

 

<출처> https://julie.io/writing/terraform-on-azure-pipelines-best-practices/

 

 

 

 

Tip #1 - UI가 아닌 YAML 파이프라인 사용

Azure DevOps 서비스는 Visual Studio Team Foundation Server에 뿌리를 두고 있으며, 따라서 Classic Pipeline과 같은 기존 기능을 제공합니다. 새 파이프라인을 만드는 경우 클래식 파이프라인으로 시작하지 마십시오. 클래식 파이프라인이 있는 경우 YAML로 마이그레이션을 계획하세요. 업계에서의 모범 사례는 Pipeline을 Code로(Pipelines as Code) 작성하는 것이며 Azure Pipeline에서는 YAML Pipeline을 의미합니다.

클래식 파이프라인을 사용하고 있다라도 당황하지는 마십시오.  잠시 동안은 함께 갈 것입니다. 그러나 공개 기능 타임 라인과 공개 로드맵에서 알 수 있 듯이 Microsoft는 YAML 파이프라인에 더 많은 투자를 하고 있습니다. 미래에 대비하려면 YAML 파이프라인을 선택하십시오.

 

 

 

 

Tip #2 - YAML 작업이 아닌 명령줄 사용

저는 파이프라인 태스크와 애증의 관계를 맺고 있습니다. 추상화로 진입 장벽을 낮춰줍니다. 태스크 플랫폼을 독립적으로 만들고(Windows vs Linux) 반환 코드를 전달하여 직접 stderr 및 stdout을 처리할 필요가 없습니다. 다른 이점에 대한 것은 Source Repo on GitHub를 참조하십시오.

그러나 README 자체에서 다음과 같이 말합니다.

빌드/릴리스에 사용자 지정 기능이 필요한 경우 일반적으로 PowerShell 또는 Bash 태스크와 같은 태스크를 실행하는 기존 스크립트를 사용하는 것이 더 간단합니다.

 

그리고 실제로 Bash에서 일반적인 CLI 명령을 사용하는 것이 더 간단합니다. 시간이 지남에 따라 "Hello World" 예제를 넘어 맞춤형 파이프라인을 반복하고 생성하면 태스크가 디버깅해야 할 또 다른 계층이 될 수도 있습니다. 예를 들어 AzCopy 태스크는 Windows 전용이므로 파이프라인이 실패 할 때까지 몇 분간 기다려야 했습니다.

 

더 빠르게 반복

명령줄을 사용하면 각 파이프라인 잡이 실행될 때까지 몇 분 동안 기다리지 않고도 로컬 시스템에서 원하는 결과를 얻기 위해 어떤 -var 및 기타 옵션을 terraform에 전달해야 하는지 정확히 파악할 수 있습니다. CLI 명령이 확실해지면 YAML 파이프라인에 명령을 저장할 수 있습니다.

 

태스크가 아닌 기술(Technology)을 마스터

일반적으로 모든 엔지니어는 명령줄에서 기술 사용 방법을 배울 것을 권장합니다. 코드 편집기에서 git 확장자를 사용하는 방법을 배우지 마십시오. 명령행에서 어떤 것을 배우면, git이든, terraform이든, 그것이 어떻게 작동하는지 배울 수 있습니다. 삶을 더 쉽게 만들 필요가 없는 YAML 태스크와 같은 추상화 계층을 스킵할 수 있으므로 디버깅이 훨씬 덜 번거로울 것입니다.

예를 들어 Azure 설명서에서 이 예제에서 찾을 수 있는 자세한 형식(verbose format)을 스킵하는 것이 좋습니다.

# Verbose 😑
- task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI@0
  displayName: 'Run terraform plan'
  inputs:
    command: plan
    workingDirectory: $(terraformWorkingDirectory)
    environmentServiceName: $(serviceConnection)
    commandOptions: -var location=$(azureLocation)

 

bash를 사용해도 같은 것을 수행할 수 있으며, 가령 -var 또는 -out 플래그만 넘길 수 있습니다. 

# Less noise 👌
- bash: terraform plan -out=deployment.tfplan
  displayName: Terraform Plan (ignores drift)

 

태스크를 사용하지 않기 때문에 environmentServiceName 및 기타 특성이 무엇을 하고 기대하는지를 더 이상 설명서에서 찾을 필요가 없습니다. Terraform만 알면 됩니다. Microsoft에서 제공하는 코드일지라도 종속성을 디버깅하는 대신 나의 코드에 집중할 수 있습니다.

 

Terraform을 설치하지 마십시오. “latest”을 유지하십시오.

Azure Pipeline 샘플에는 "installer" 태스크가 포함된 공식 예가 많이 있습니다. 의존성 버전도 중요하지만, 테라폼은 좀처럼 변화하지 않는 안정적인 기술 중 하나라는 것을 알게 되었습니다. 하나의 버전으로 고정하기 보다는 항상 최신 버전으로 실행하는 것을 고려하십시오. 일반적으로 나중에 기능 개발을 차단하는 대형 리랙토링을 갖는 것보다 점진적 변경 및 수정 작업을 수행하는 것이 더 쉽습니다.

GitHub에서 Microsoft 호스팅 빌드 에이전트에 설치된 버전을 확인할 수 있습니다(예: Ubuntu 18.04). 이러한 빌드 에이전트는 Azure Pipeline 및 GitHub Actions에서 모두 사용됩니다.

 

CLI는 공급 업체에 구애받지 않습니다.

YAML 태스크에 대한 CLI 숙달에 대한 이 기본 설정은 Terraform에만 국한되지 않습니다. GitHub에서 다양한 데모를 살펴보면 일반적으로 동등한 YAML 태스크보다 명령줄에서 Docker 및 Node.js를 선호합니다.

산업은 빠른 속도로 진행되고 있습니다. CLI를 사용하면 새 벤더로의 마이그레이션 경로도 쉽게 만들 수 있습니다. 나중에 GitHub Actions이 성숙되어 Azure Pipeline에서 마이그레이션하려는 경우 YAML 태스크 추상화 계층을 마이그레이션할 필요가 없습니다. CLI를 사용하여 미래의 생활을 보다 쉽게 만들 수 있습니다.

 

그렇다면 Azure에 어떻게 인증합니까?

고객으로부터 받는 일반적인 질문입니다. 계속 읽어 보세요. 이에 대한 내용은 또한 파이프라인의 비밀 관리에 대해 논의하는 이 글의 마지막 섹션에도 있습니다.

 

 

 

 

Tip #3 - Terraform 부분 구성 사용

이 주제는 자체적인 글로 다룰만한 것입니다. 그러나 가장 중요한 점을 언급하겠습니다. 다른 엔지니어와 공동 작업하거나 헤드리스 빌드 에이전트에서 배포 할 때 상태 파일이 필요합니다.

Local State로 시작하세요.

인프라가 어떻게 보일지 모르는 경우 로컬에서 실험하세요. 즉, CI/CD 대기 시간 (분)을 피하기 위해 원격 백엔드를 사용하지 마세요.

시도해 보면 아마 일을 망칠 것입니다. 이 단계에서는 문제를 해결하는 대신 rm -rf .terraform을 수행하여 모든 것을 분해하고 다시 시작합니다.

인프라 아키텍처가 안정되면 원격 상태 파일 생성을 진행합니다.

 

상태 파일에 대한 스토리지 계정 생성

Terraform에는 Azure Blob Storage 계정이 필요합니다.

ProTip: Azure CLI를 사용하여 직접 저장소 계정을 만듭니다.

$ az storage account create \
  --name mystorageaccountname \
  --resource-group myresourcegroupname \
  --kind StorageV2 \
  --sku Standard_LRS \
  --https-only true \
  --allow-blob-public-access false

 

Terraform 상태 파일은 비밀을 포함한 모든 것을 일반 텍스트로 저장하므로 보안에 특히 주의해야합니다. 공용 Blob 액세스를 비활성화했는지 확인합니다.

파이프라인 태스크에 의존하여 계정을 생성하지 마십시오! 이렇게 하는 태스크가 있긴 하지만, 스토리지 계정은 기본적으로 Blob 파일에 대한 공용 액세스를 허용하도록 구성됩니다. 개별 상태 파일 자체가 보호됩니다. 그러나 기본값은 안전하지 않아 보안 위험이 기다리고 있습니다.

 

기본 구성을 사용하지 마십시오.

원격 백엔드를 사용하는 경우 상태 파일이 어디에 있는지 Terraform에 알려야 합니다. 공식 문서의 구성은 다음과 같습니다.

# Don't do this
terraform {
  backend "azurerm" {
    resource_group_name  = "StorageAccount-ResourceGroup"
    storage_account_name = "abcd1234"
    container_name       = "tfstate"
    key                  = "prod.terraform.tfstate"
 
    # Definitely don't do this!
    access_key           = "..."
  }
}

 

부분 구성을 사용하십시요.

부분 구성 사용추가 문서에서 Terraform은 해당 속성을 이동하고 부분 구성을 사용하도록 권장합니다.

# This is better
terraform {
  backend "azurerm" {
  }
}

 

백엔드 구성 파일 생성 및 무시

Azure Storage 계정 액세스 키를 사용하는 대신 수명이 짧은 SAS (공유 액세스 서명) 토큰을 사용합니다. 따라서 다음과 같은 로컬 azure.conf 파일을 만듭니다.

# azure.conf, must be in .gitignore
storage_account_name="azurestorageaccountname"
container_name="storagecontainername"
key="project.tfstate"
sas_token="?sv=2019-12-12..."

 

azure.conf가 .gitignore 파일에 추가되었는지 세 번 확인하여 코드 리포지토리에 체크인되지 않도록합니다.

 

로컬 개발에서 파일을 사용하는 것은 괜찮습니다.

내 로컬 컴퓨터에서 전체 구성 파일을 전달하여 Terraform을 초기화합니다.

$ terraform init -backend-config=azure.conf

 

참고 : SAS 토큰을 사용하는 이유 중 하나는 일반적으로 프로젝트의 초기 단계에서 원격 상태 파일만 사용하면 되기 때문입니다. 액세스 키를 그대로 두는 대신 로컬 컴퓨터에 만료된 SAS 토큰이 있습니다.

 

구성은 .tfvars 파일이 아니어야 합니다.

.tfvars 확장이 있는 변수가 자동으로 로드되는데, 이는 사고가 일어나기를 기다리는 것입니다. 이것이 사람들이 실수로 git에 자격 증명을 체크 하는 방법입니다. 그런 사람이나 동료가 되지 마세요. 약간의 마찰을 더하여 -backend-config=azure.conf CLI 옵션을 사용하십시오.

또한 편집기에서 구문 강조를 수행할 수 있도록 파일에 .hcl 확장자를 지정할 수도 있습니다. 나는 이 파일이 민감한 정보를 포함할 수 있으며 보호되어야 한다는 경고를 표시하기 위해 .conf를 규칙으로 사용합니다.

 

CI/CD 빌드에서 키 값 쌍 사용합니다.

개인적으로 저는 Azure Pipelines에서 Secure Files를 사용하지 않습니다. 다른 곳에서 찾아서 디버깅해야하는 자격 증명을 갖고 싶지 않기 때문입니다. 첫 번째 문제를 해결하기 위해 Key Vault를 사용합니다 (계속 읽기).

두 번째 문제를 해결하기 위해 구성을 개별 변수로 terraform init 명령에 전달합니다.

$ terraform init \
  -backend-config="storage_account_name=$TF_STATE_BLOB_ACCOUNT_NAME" \
  -backend-config="container_name=$TF_STATE_BLOB_CONTAINER_NAME" \
  -backend-config="key=$TF_STATE_BLOB_FILE" \
  -backend-config="sas_token=$TF_STATE_BLOB_SAS_TOKEN"

 

SAS 토큰을 사용하지 않는 경우 -backend-config="access_key=…"를 사용하여 스토리지 계정 액세스 키를 전달할 수 있습니다.

키 값 쌍을 사용하여 명시적이며 모든 단계에서 온전성 검사를 수행하고 추적 가능성을 높입니다. 미래의 당신 자신에게 스스로 감사할 것입니다. 또한 내 변수의 이름은 디버깅에 도움이 되도록 TF_ 접두사로 지정됩니다.

따라서 YAML의 전체 단계는 다음과 같습니다.

# Load secrets from Key Vault
variables:
  - group: e2e-gov-demo-kv
 
# Initialize with explicitly mapped secrets
steps:
- bash: |
    terraform init \
      -backend-config="storage_account_name=$TF_STATE_BLOB_ACCOUNT_NAME" \
      -backend-config="container_name=$TF_STATE_BLOB_CONTAINER_NAME" \
      -backend-config="key=$TF_STATE_BLOB_FILE" \
      -backend-config="sas_token=$TF_STATE_BLOB_SAS_TOKEN"
  displayName: Terraform Init
  env:
    TF_STATE_BLOB_ACCOUNT_NAME:   $(kv-tf-state-blob-account)
    TF_STATE_BLOB_CONTAINER_NAME: $(kv-tf-state-blob-container)
    TF_STATE_BLOB_FILE:           $(kv-tf-state-blob-file)
    TF_STATE_BLOB_SAS_TOKEN:      $(kv-tf-state-sas-token)

 

계속해서 Key Vault 통합이 작동하는 방식을 알아보세요. 또한 이 전략을 사용하여 인프라를 관리하기 위해 Azure에 인증합니다.

 

 

 

 

Tip #4 - Azure Key Vault에 저장된 서비스 주체 자격 증명으로 인증

우리는 종종 로컬 컴퓨터에서 무언가가 작동되는 것을 축하합니다. 불행히도 파티를 하기에는 너무 이릅니다. 이러한 동일한 단계를 자동화 파이프라인으로 옮기는 것은 개념적으로 이해하기 어려운 더 많은 노력을 필요로합니다.

 

CI/CD에서 az login이 동작하지 않는 이유는 무엇입니까?

간단히 말해서 빌드 에이전트가 헤드리스이기 때문에 작동하지 않습니다. 그것은 인간이 아닙니다. 대화형 방식으로 Terraform(또는 Azure)과 상호 작용할 수 없습니다. 일부 고객은 CLI를 통해 인증을 시도하고 헤드리스 에이전트가 조직에 있는 MFA(다단계 인증)를 통과하도록 하는 방법을 묻습니다. 그점이 바로 로그인에 Azure CLI를 사용하지 않는 이유입니다. Terraform 문서에서 다음과 같이 설명하는대로

Terraform을 비대화 형으로 실행하는 경우 (예 : CI 서버에서 Terraform을 실행할 때) 서비스 주체 또는 관리형 서비스 ID를 사용하고 Terraform을 로컬에서 실행할 때 Azure CLI를 사용하여 인증하는 것이 좋습니다.

 

따라서 서비스 주체의 client secret을 환경 변수로 설정하여 Azure Resource Manager API에 인증합니다.

- bash: terraform apply -auto-approve deployment.tfplan
  displayName: Terraform Apply
  env:
    ARM_SUBSCRIPTION_ID: $(kv-arm-subscription-id)
    ARM_CLIENT_ID:       $(kv-arm-client-id)
    ARM_CLIENT_SECRET:   $(kv-arm-client-secret)
    ARM_TENANT_ID:       $(kv-arm-tenant-id)

 

환경 변수의 이름(예: ARM_CLIENT_ID)은 본 Terraform Documentation에서 확인할 수 있습니다. 여러분 중 일부는 환경 변수가 안전한가라고 생각할 수 있습니다. 네. 그런데 공식 Azure CLI 태스크도 태스크 소스 코드에서 라인 43을 검사하면 동일한 작업을 수행합니다.

명확하게 하기 위해 클라이언트 ID와 시크릿을 환경 변수로 설정하여 헤드리스 빌드 에이전트를 인증합니다. 이는 일반적인 관행입니다. 모범 사례 부분은 이러한 비밀을 보호하는 것입니다.

 

파이프라인 암호를 사용하고 있는지 다시 확인

그러나 환경에 자격 증명이 있는 Azure Pipelines에서는 파이프라인 변수를 비밀로 표시하는 경우에만 안전합니다.

  • 변수는 유휴 상태에서 암호화 됩니다.
  • Azure Pipelines는 *** (최선의 노력 기준)로 값을 마스킹합니다.

 

잠금 아이콘을 찾아 변수를 비밀로 표시했는지 확인하세요.

 

비밀 사용에 대한 주의 사항은 모든 파이프라인 단계에서 모든 비밀을 환경 변수에 명시적으로 매핑해야 한다는 것입니다. 지루할 수도 있지만 의도적이며 보안상의 의미를 분명히 합니다. 또한 배포할 때마다 소규모 보안 검토를 수행하는 것과 같습니다. 이 리뷰들은 생명을 구하기 위해 과학적으로 증명된 체크리스트와 동일한 목적을 가지고 있습니다. 보안을 위해 명시적이어야 합니다.

 

추가 정보 - Key Vault 통합

파이프라인 비밀을 사용하고 있는지 확인하는 것으로 충분할 수 있습니다. 한 단계 더 나아가고 싶다면 YAML 태스크가 아닌 비밀 변수를 통해 Key Vault를 통합하는 것이 좋습니다.

"Link secrets…"토글을 사용하여 Key Vault를 통합합니다.

 

참고: 여기서 "Azure subscription"은 서비스 연결을 나타냅니다. msdn-sub-reader-sp-e2e-governance-demo라는 이름을 사용하여 내부의 서비스 주체가 내 Azure 리소스에 대한 read-only 액세스 권한만 가지고 있음을 나타냅니다.

 

대기업과 기업이 이 경로를 선택할 수 있는 이유는 다음과 같습니다.

  • Azure DevOps 프로젝트 및 Azure DevOps 조직에서 비밀을 재사용합니다. 프로젝트간에만 서비스 연결을 공유 할 수 있습니다.
  • Azure Key Vault로 더 강력한 보안. 적절한 서비스 주체 권한 및 Key Vault 액세스 정책과 함께 사용하면 Azure DevOps에서 암호를 변경하거나 삭제할 수 없습니다.
  • 확장 가능한 비밀 로테이션. 장기간의 자격 증명보다 단기간의 토큰을 선호합니다. Azure Pipeline은 빌드 런타임 시작 시 암호를 가져오기 때문에 항상 최신 상태로 유지됩니다. 정기적으로 자격 증명을 순환하는 경우 한 곳(Key Vault)에서만 변경하면 됩니다.
  • 감소된 공격 표면. 자격 증명을 Key Vault에 넣으면 내 서비스 주체에 대한 클라이언트 암호는 해당 위치가 있는 A) Azure Active Directory 및 B) Azure Key Vault의 두 위치에만 저장됩니다.

서비스 연결을 사용할 경우 공격 표면을 3개 위치로 늘렸습니다. Azure DevOps는 우리의 비밀을 지켜주는 관리형 서비스라고 믿습니다. 그러나 조직에서는 누군가가 권한을 구성(잘못)할 때 실수로 이러한 권한을 손상시킬 수 있습니다.

 

ProTip: 위의 변수에는 모두 kv- 접두사가 붙습니다. 이 이름은 해당 값이 Key Vault에 저장되었음을 나타내는 데 사용하는 명명 규칙입니다.

 

 

 

 

Tip #5 Terraform에 대한 사용자 지정 역할 생성

보안 및 RBAC 모범 사례는 위험을 최소화하는 데 필요한만큼만  액세스 권한을 부여하는 것입니다. 그렇다면 Terraform에서 사용하는 서비스 주체를 할당하는 Azure 역할은 무엇입니까? 소유자 또는 기여자?

둘 다 아닙니다. 인프라를 배포하고 있기 때문에, 예를 들어 높은 권한이 필요한 Key Vault 액세스 정책을 만듭니다. 기여자에게 부족한 권한을 확인하기 위해 다음 Azure CLI 명령을 실행할 수 있습니다.

az role definition list \
  --name "Contributor" \
  --output json \
  --query '[].{actions:permissions[0].actions, notActions:permissions[0].notActions}'

 

다음을 출력합니다.

[
  {
    "actions": [
      "*"
    ],
    "notActions": [
      "Microsoft.Authorization/*/Delete",
      "Microsoft.Authorization/*/Write",
      "Microsoft.Authorization/elevateAccess/Action",
      "Microsoft.Blueprint/blueprintAssignments/write",
      "Microsoft.Blueprint/blueprintAssignments/delete"
    ]
  }
]

 

Key Vault 액세스 정책을 만들려면 서비스 주체에게 "Microsoft.Authorization/*/Write" 권한이 필요합니다. 가장 쉬운 해결책은 서비스 주체에게 소유자 역할을 부여하는 것입니다. 그러나 이것은 God 모드와 동일합니다.

삭제의 결과

대기업뿐만 아니라 규정 준수 산업에도 중요하지만 좋은 차이가 있습니다. 소규모의 Fintech 스타트업이라면, 이것도 마찬가지입니다. 일부 데이터는 법으로 삭제할 수 없습니다. 예를 들어 세무조사에 필요한 재무 데이터입니다. 이러한 데이터 손실의 심각성과 법적 결과로 인해 리소스에 관리 잠금을 적용하여 데이터가 삭제되지 않도록 하는 것이 일반적인 클라우드 관행입니다.

우리는 여전히 Terraform이 인프라를 생성하고 관리하기를 원하므로 쓰기 권한을 부여합니다. 그러나 다음과 같은 이유로 삭제 권한을 부여하지 않습니다.

  • 자동화는 강력합니다. 그리고 강력한 힘에는 큰 책임이 따릅니다. 우리는 헤드리스 (따라서 두뇌가 없는) 빌드 에이전트를 부여하고 싶지 않습니다.
  • git (서명 된 커밋 포함)은 기술적 추적성을 제공하지만 조직에서는 법적 감사 가능성에 대한 요구 사항을 충족하지 못할 수 있음을 이해하는 것이 중요합니다.

따라서 Pull Requests 및 보호된 브랜치로 워크플로를 보호했더라도 충분하지 않을 수 있습니다. 따라서 삭제 작업을 git 계층에서 클라우드 관리 계층(즉, 관리 잠금을 사용하는 감사 기능을 위한 Azure)으로 이동합니다.

따라서 사용자 지정 역할을 만들고 다음 notActions가 있는지 확인하십시오.

{
  "notActions": [
    "Microsoft.Authorization/*/Delete"
  ]
}

 

코드는 Azure Blueprint를 지정하지 않습니다. 위의 동일한 추론을 사용하여 사용 사례에서 액세스가 필요한지 여부와 제한시기를 결정하십시오.

 

 

 

 

요약

이 긴 가이드에서는 파이프라인(YAML)을 코드로 사용하고 Terraform 및 기타 기술을 마스터하는 데 도움이 되는 명령줄을 사용하는 몇 가지 일반적인 Azure Pipeline 모범 사례에 대해 설명했습니다. 또한 일반적인 gotchas에 대해 Azure와 함께 상태 파일을 적절히 보호하고 인증하는 방법에 대해서도 살펴봤습니다. 끝으로, 마지막 두 가지 주제는 Key Vault 통합과 Terraform에 대한 사용자 정의 역할 생성에 대한 것이 었습니다.

이 문서에 보안이 너무 많이 포함되어 있어도 괜찮습니다. 모든 관행을 동시에 이행하지 마십시오. 한 번에 하나씩 실행하십시오. 그리고 시간이 지남에 따라 최소한 몇 달 동안 보안 모범 사례는 제 2의 특성이 됩니다.

이 문서는 특히 Azure Pipelines를 사용할 때 모범 사례에 중점을 둡니다. git 워크 플로를 사용하고 여러 환경에서 인프라를 관리하는 방법을 설명하는 일반적인 모범 사례에 대한 다른 글을 계속 지켜봐주십시오.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함