연습 - Azure 리소스 프로비저닝

완료됨

이 연습에서는 Terraform 리소스를 프로비저닝하도록 자동화 워크플로를 구성합니다.

애플리케이션 예제 액세스

이 연습에서는 Terraform 프로비저닝을 위한 모든 코드를 포함하는 템플릿에서 GitHub 리포지토리를 만듭니다.

  1. GitHub에서 리포지토리의 기본 페이지로 이동합니다.

    https://github.com/MicrosoftDocs/mslearn-java-petclinic-simplified
    
  2. 파일 목록 위에서 이 템플릿 사용을 선택한 다음, 새 리포지토리 만들기를 선택합니다.

    Screenshot of the

  3. 리포지토리 이름 상자에 리포지토리의 고유한 이름을 입력합니다. GitHub 리포지토리의 명명 규칙을 따라야 합니다.

  4. 비공개 옵션이 선택되어 있는지 확인한 다음 리포지토리 만들기를 선택합니다.

    Screenshot of the

워크플로

방금 만든 리포지토리의 프로젝트 디렉터리를 보시면 terraform이라는 디렉터리가 있고 그 안에 main.tf라는 파일이 있습니다.

모듈의 구성을 정의하는 데 사용할 수 있는 몇 가지 섹션을 살펴보겠습니다.

  • 공급자: Terraform 구성 파일은 공급자의 사양으로 시작됩니다. Azure를 사용하는 경우 공급자 블록에 Azure 공급자(azurerm)를 지정합니다.
  • Terraform: 작업 중인 Terraform 버전입니다.
  • 데이터: 기존 서비스에서 데이터를 가져옵니다.
  • 로컬: 함수 및 식을 사용하여 새 변수를 생성합니다.
  • 리소스: 리소스 및 종속성을 설명합니다.
  • 모듈: 재사용성 및 복잡성 추상화입니다.

애플리케이션과 데이터베이스를 프로비저닝하려면 공급자리소스 섹션만 포함하면 됩니다.

그런 다음 main.tf 파일을 열고 개요와 주석을 검토합니다.

provider "azurerm" {
  version = "=2.20.0"
  features {}
}

# Creates a resource group
resource "azurerm_resource_group" "main" {
  name     = var.resource_group
  location = var.location

  tags = {
    "Terraform" = "true"
  }
}

resource "random_password" "password" {
  length = 32
  special = true
  override_special = "_%@"
}

# Creates a MySQL server
resource "azurerm_mysql_server" "main" {
  name                              = "${azurerm_resource_group.main.name}-mysql-server"
  location                          = azurerm_resource_group.main.location
  resource_group_name               = azurerm_resource_group.main.name

  administrator_login               = "petclinic"
  administrator_login_password      = random_password.password.result

  sku_name   = "B_Gen5_1"
  storage_mb = 5120
  version    = "5.7"
  auto_grow_enabled                 = true
  backup_retention_days             = 7
  geo_redundant_backup_enabled      = false
  infrastructure_encryption_enabled = false
  public_network_access_enabled     = true
  ssl_enforcement_enabled           = true
  ssl_minimal_tls_version_enforced  = "TLS1_2"
}

# The database that your application will use
resource "azurerm_mysql_database" "main" {
  name                = "${azurerm_resource_group.main.name}_mysql_db"
  resource_group_name = azurerm_resource_group.main.name
  server_name         = azurerm_mysql_server.main.name
  charset             = "utf8"
  collation           = "utf8_unicode_ci"
}

# Enables the 'Allow access to Azure services' check box
resource "azurerm_mysql_firewall_rule" "main" {
  name                = "${azurerm_resource_group.main.name}-mysql-firewall"
  resource_group_name = azurerm_resource_group.main.name
  server_name         = azurerm_mysql_server.main.name
  start_ip_address    = "0.0.0.0"
  end_ip_address      = "0.0.0.0"
}

# Creates the plan that the service uses
resource "azurerm_app_service_plan" "main" {
  name                = "${var.application_name}-plan"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  kind                = "Linux"
  reserved            = true

  sku {
    tier = "PremiumV2"
    size = "P1v2"
  }
}

# Creates the service definition
resource "azurerm_app_service" "main" {
  name                = var.application_name
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name
  app_service_plan_id = azurerm_app_service_plan.main.id
  https_only          = true

  site_config {
    always_on        = true
    linux_fx_version = "JAVA|8-jre8"
  }

  # Contains application-specific environment variables
  app_settings = {
    "WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"

    "SPRING_PROFILES_ACTIVE"     = "mysql"
    "SPRING_DATASOURCE_URL"      = "jdbc:mysql://${azurerm_mysql_server.main.fqdn}:3306/${azurerm_mysql_database.main.name}?useUnicode=true&characterEncoding=utf8&useSSL=true&useLegacyDatetimeCode=false&serverTimezone=UTC"
    "SPRING_DATASOURCE_USERNAME" = "${azurerm_mysql_server.main.administrator_login}@${azurerm_mysql_server.main.name}"
    "SPRING_DATASOURCE_PASSWORD" = azurerm_mysql_server.main.administrator_login_password
  }
}

Terraform을 사용하여 GitHub Actions 워크플로 설정

GitHub 워크플로에 Azure 계정에 대한 액세스 권한을 제공해 보겠습니다.

Azure CLI에서 다음 명령을 실행하여 서비스 주체를 만듭니다.

중요

<yourServicePrincipalName>을 사용하려는 서비스 주체 이름으로 바꿉니다.

az ad sp create-for-rbac --name "<yourServicePrincipalName>" --role contributor --scopes /subscriptions/<subscriptionId> --sdk-auth

앞의 명령은 다음 JSON을 반환합니다. 다음 단계에서 사용할 수 있도록 복사합니다.

{
  "clientId": "XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXXX",
  "clientSecret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  "subscriptionId": "XXXXXXXXX-XXXXX-XXXX-XXXX-XXXXXXXXXXXXX",
  "tenantId": "XXXXXXXX-XXXXX-XXXX-XXXX-XXXXXXXXXXX",
  ...
}

GitHub 비밀

GitHub 리포지토리에는 Terraform이 Azure에 인증하는 데 사용하는 중요한 정보를 저장할 수 있는 비밀이라는 기능이 있습니다.

이전 단계에서 필요한 ID와 비밀을 만들었다면 이 단원의 다음 단계는 이를 GitHub 프로젝트의 비밀 저장소에 추가하는 것입니다.

이 연습에서는 다음 비밀을 저장해야 합니다.

  • AZURE_CLIENT_ID
  • AZURE_CLIENT_SECRET
  • AZURE_SUBSCRIPTION_ID
  • AZURE_TENANT_ID

비밀을 저장하려면 포크된 GitHub 리포지토리로 이동하고 설정을 선택한 다음 비밀 및 변수를 선택하고 왼쪽 창에서 동작을 선택합니다.

서비스 주체를 만들 때 반환된 값을 사용하여 4개의 암호를 만듭니다.

다음 스크린샷에 나온 것처럼 큰따옴표(" ") 없이 비밀을 저장해야 합니다.

Screenshot showing stored secrets on the Secrets pane under GitHub Settings.

워크플로 파일

프로젝트 디렉터리 안에는 .github/workflows라는 디렉터리가 있고, 이 디렉터리 안에는 main.yml이라는 파일이 있습니다.

main.yml 파일은 GitHub 워크플로입니다. 이 파일은 방금 구성한 비밀을 사용하여 Azure 구독에 애플리케이션을 배포합니다.

main.yml 워크플로 파일에는 다음 콘텐츠가 있습니다.

name: TERRAFORM

on:
  push:
    branches: [ main ]
    paths:
    - 'terraform/**'
  pull_request:
    branches: [ main ]
    paths:
    - 'terraform/**'

  workflow_dispatch:
jobs:
  terraform:
    runs-on: ubuntu-latest

    env:
      ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
      ARM_CLIENT_SECRET: ${{secrets.AZURE_CLIENT_SECRET}}
      ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}

    defaults:
      run:
        working-directory: ./terraform
    steps:
      - uses: actions/checkout@v2

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v1

      - name: Terraform Init
        run: terraform init

      - name: Terraform Plan
        run: terraform plan

      - name: Terraform Apply
        run: terraform apply -auto-approve

해당 워크플로는 다음 작업을 수행합니다.

  • 구성의 형식이 제대로 지정되었는지 확인합니다.
  • 모든 끌어오기 요청의 계획을 생성합니다.
  • terraform 디렉터리에서 파일을 업데이트할 때 구성을 트리거합니다.

참고

동작으로 이동하고, Terraform 워크플로를 선택하고, 기존 작업 다시 실행을 선택하여 GitHub Actions 워크플로를 트리거할 수도 있습니다.

워크플로 트리거

다음으로, 리포지토리에서 다음과 같이 GitHub 동작을 트리거합니다.

  1. 기본 제공 GitHub 텍스트 편집기나 선택한 편집기에서 다음과 같이 terraform/variables.tf를 편집합니다.

    a. "CHANGE_ME_RESOURCE_GROUP"을 원하는 리소스 그룹 이름으로 변경합니다.
    b. "CHANGE_ME_APP_NAME"을 원하는 애플리케이션 이름으로 변경합니다. 애플리케이션 이름이 고유해야 합니다.

    variable "resource_group" {
      description = "The resource group"
      default = "CHANGE_ME_RESOURCE_GROUP"
    }
    
    variable "application_name" {
      description = "The Spring Boot application name"
      default     = "CHANGE_ME_APP_NAME"
    }
    
    variable "location" {
      description = "The Azure location where all resources in this example should be created"
      default     = "westeurope"
    }
    
  2. 변경 내용을 커밋합니다.

GitHub Actions 빌드 확인

  1. 리포지토리에서 동작을 선택한 다음 왼쪽 창에서 TERRAFORM 워크플로를 선택합니다.

  2. 단계 목록에서 Terraform Init, Terraform 계획Terraform 유효성 검사가 트리거되었는지 확인합니다.

    Screenshot displaying the results of the Terraform workflow run.

  3. 단계 목록에서 Terraform 적용을 확장하고 다음을 확인합니다.

  • Terraform이 리소스를 만들었고 Azure 인스턴스 URL을 표시합니다.

  • Azure 앱 인스턴스를 공개적으로 사용할 수 있습니다.

    Screenshot showing that the Azure app instance is publicly available.

다음 단계

다음 연습에서는 GitHub Actions를 사용하여 샘플 Spring Boot 애플리케이션을 배포합니다.

애플리케이션 이름 및 Azure 리소스 그룹 설정

GitHub 리포지토리에서 다음 작업을 수행하여 Azure 리소스 이름을 편집합니다.

  1. 기본 제공 GitHub 텍스트 편집기나 선택한 편집기에서 다음과 같이 terraform/variables.tf를 편집합니다.

    a. "<CHANGE_ME_RESOURCE_GROUP>"을 원하는 리소스 그룹 이름으로 변경합니다.
    b. "<CHANGE_ME_APP_NAME>"을 원하는 애플리케이션 이름으로 변경합니다. 애플리케이션 이름이 고유해야 합니다.

    variable "resource_group" {
      description = "The resource group"
      default = "<CHANGE_ME_RESOURCE_GROUP>"
    }
    
    variable "application_name" {
      description = "The Spring Boot application name"
      default     = "CHANGE_ME_APP_NAME"
    }
    
    variable "location" {
      description = "The Azure location where all resources in this example should be created"
      default     = "westeurope"
    }
    
  2. 변경 내용을 커밋합니다

Terraform 리소스를 프로비저닝하는 Azure Pipeline 만들기

Azure DevOps 프로젝트에서 프로비저닝 및 빌드 및 배포를 위한 별도의 두 파이프라인을 만듭니다. 프로비저닝 파이프라인은 나중에 빌드 및 배포 파이프라인을 통해 릴리스될 Azure 리소스를 만듭니다.

첫 번째 프로비저닝 파이프라인을 만들어 보겠습니다.

  1. 조직을 선택한 다음 새 프로젝트를 선택합니다.

  2. 다음 매개 변수를 지정합니다.

    매개 변수 설명
    프로젝트 이름 필수
    설명 선택
    가시 거리 프라이빗 선택
    고급
    버전 제어 GIT 선택
    작업 항목 프로세스 기본을 선택
  3. 프로젝트 만들기를 선택하여 프로젝트를 만들고 홈페이지를 엽니다.

Screenshot displaying the new Azure Project form.

Azure Pipeline 서비스 연결 설정

Azure Pipeline에 Azure 계정에 대한 액세스를 제공하겠습니다.

  1. Azure DevOps의 프로젝트 설정 페이지에서 서비스 연결 페이지를 엽니다.

  2. 서비스 연결 만들기, Azure Resource Manager, 다음을 차례로 선택합니다.

  3. 서비스 주체(자동)를 선택한 다음, 다음을 선택합니다.

  4. 다음 매개 변수를 지정합니다.

    매개 변수 Description
    범위 수준 Azure 구독 선택
    구독 기존 Azure 구독 선택
    리소스 그룹 사용자가 구독 내에 정의된 모든 리소스에 액세스할 수 있도록 하려면 비워두세요.
    연결 이름 필수 요소. 작업 속성에서 이 서비스 연결을 참조하는 데 사용할 이름입니다. 이 이름은 Azure 구독의 이름이 아닙니다.
  5. 저장을 선택하여 연결을 만듭니다.

프로비저닝 파이프라인 만들기

중요

이 모듈을 진행하기 위해서는 Terraform Azure Pipelines 확장을 설치해야 했습니다. 설치하지 않았다면 파이프라인이 실행되지 않습니다.

Azure에 대한 프로젝트 및 연결을 설정한 후 Terraform 리소스를 프로비저닝하기 위해 Azure 파이프라인을 만들어야 합니다.

Azure DevOps에서 프로젝트로 이동하고 왼쪽 메뉴에서 파이프라인을 선택한 다음 파이프라인 만들기를 선택합니다.

  1. 연결” 탭에서 “GitHub”(YAML 파일)를 선택합니다.
  2. GitHub 액세스 권한을 부여하라는 메시지가 표시되면 GitHub 자격 증명을 입력하고 요청된 권한을 사용하여 Azure Pipelines에 대한 액세스를 승인합니다.
  3. 선택” 탭에서 템플릿을 포함하는 GitHub 리포지토리를 선택합니다.
  4. 인벤토리 탭에서 파이프라인 구성을 선택합니다.
  5. 구성” 탭에서 “기존 Azure Pipelines YAML 파일”을 사용하려면 선택합니다.
  6. 경로에서 “/azuredevops/provision.yml”을 선택합니다.
  7. 계속을 선택하여 검토 탭으로 이동하고 파이프라인을 검토합니다.

Screenshot displaying the new Azure Pipeline form.

“파이프라인 YAML 검토” 화면에서 파이프라인을 만드는 데 사용하는 Yaml 파일을 살펴봅니다.

name: Provision Resources

trigger: none

pool:
  vmImage: 'ubuntu-latest'

steps:

# Initialize the Terraform environment and bind to your Service Connection
- task: TerraformTaskV1@0
  inputs:
    provider: 'azurerm'
    command: 'init'
    workingDirectory: $(Build.Repository.LocalPath)/terraform
    backendServiceArm: $(serviceConnection)
    backendAzureRmResourceGroupName: $(serviceConnection)
    backendAzureRmStorageAccountName: $(serviceConnection)
    backendAzureRmContainerName: 'tfstate'
    backendAzureRmKey: 'tf/terraform.tfstate'

# Apply the Terraform config and deploy to Azure
- task: TerraformTaskV1@0
  inputs:
    provider: 'azurerm'
    command: 'apply'
    workingDirectory: $(Build.Repository.LocalPath)/terraform
    backendAzureRmContainerName: 'tfstate'
    backendAzureRmKey: 'tf/terraform.tfstate'
    environmentServiceNameAzureRM: $(serviceConnection)

구성에서 사용하는 일부 필드를 살펴보겠습니다.

  • serviceConnection: 이전에 설정한 Azure 파이프라인 서비스 연결
  • 명령: Terraform 워크플로 명령: init 또는 apply
  • backendAzure: 팀 환경에서 공유 상태를 저장하는 데 필요한 필수 필드입니다.\

파이프라인을 저장하고 실행하기 전에 서비스 연결에 바인딩할 변수를 추가해야 합니다.

  1. 변수(오른쪽 위)를 선택하고 값을 사용자의 서비스 연결 이름으로 하여 “serviceConnection”인 변수를 추가합니다.
  2. 변수를 저장하려면 확인(오른쪽 아래 모서리)을 선택합니다.

Screenshot displaying the new Service Principal variable.

마지막으로 “실행”(오른쪽 위 모서리)을 선택하여 파이프라인을 저장하고 실행합니다.

파이프라인 실행 확인

작업 아래에서 각 단계를 통해 빌드 프로세스를 추적합니다.

파이프라인이 실행되면 첫 번째 Terraform이 스테이지를 init하는 것과, 다음으로 두 번째 Terraform이 스테이지를 적용하는 것을 확인합니다. 그러면 파란색(실행 중)에서 녹색(완료됨)으로 변경됩니다. 스테이지를 선택하여 실행 중인 파이프라인을 볼 수 있습니다.

Screenshot displaying the new Azure Pipeline run.

메일을 확인합니다. 실행 결과와 함께 빌드 알림을 이미 수신했을 수 있습니다. 이러한 알림을 사용하여 각 빌드의 통과 또는 실패 여부를 알 수 있습니다.

다음 단계

다음 연습에서는 Azure Pipelines를 사용하여 샘플 Spring Boot 애플리케이션을 빌드 및 배포합니다.