연습 - 웹 애플리케이션 배포
완구 회사 웹 사이트 개발 팀은 최신 버전의 웹 사이트를 Git 리포지토리에 커밋했습니다. 이제 워크플로를 업데이트하여 웹 사이트를 빌드하고 Azure App Service에 배포할 수 있습니다.
프로세스에서 다음을 수행합니다.
- 빌드 작업에 대해 새로 호출된 워크플로를 추가합니다.
- 빌드 작업을 포함하도록 워크플로를 업데이트합니다.
- 새 스모크 테스트를 추가합니다.
- 배포 작업을 업데이트하여 애플리케이션을 배포합니다.
- 워크플로를 실행합니다.
빌드 작업에 대해 재사용 가능한 워크플로 추가
여기서는 웹 사이트 애플리케이션을 빌드하는 데 필요한 단계를 포함하는 새 작업 정의를 추가합니다.
Visual Studio Code를 엽니다.
.github/workflows 폴더에 build.yml이라는 새 파일을 만듭니다.
build.yml 워크플로 파일에 다음 내용을 추가합니다.
name: build-website on: workflow_call: jobs: build-application: name: Build application runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Install .NET Core uses: actions/setup-dotnet@v3 with: dotnet-version: 3.1 - name: Build publishable website run: | dotnet publish --configuration Release working-directory: ./src/ToyCompany/ToyCompany.Website - name: Zip publishable website run: | zip -r publish.zip . working-directory: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish - name: Upload website as workflow artifact uses: actions/upload-artifact@v3 with: name: website path: ./src/ToyCompany/ToyCompany.Website/bin/Release/netcoreapp3.1/publish/publish.zip
이 작업은 .NET SDK를 설치하여 솔루션을 빌드합니다. 그런 다음 빌드 단계를 실행하여 웹 사이트 애플리케이션의 소스 코드를 Azure에서 실행할 준비가 된 컴파일된 파일로 전환합니다. 이후 작업은 컴파일된 아티팩트를 압축하고 이를 워크플로 아티팩트로 업로드합니다.
파일의 변경 내용을 저장합니다.
워크플로에 빌드 작업 추가
‘workflow.yml’ 파일을 엽니다.
jobs: 라인 아래 lint 작업 앞에 정의한 재사용 가능한 워크플로를 사용하는 build라는 새 작업을 추가합니다.
name: deploy-toy-website-end-to-end concurrency: toy-company on: push: branches: - main workflow_dispatch: permissions: id-token: write contents: read jobs: # Build the application and database. build: uses: ./.github/workflows/build.yml # Lint the Bicep file. lint: uses: ./.github/workflows/lint.yml
deploy-test 작업을 새로운 build 작업에 종속되도록 업데이트합니다.
# Deploy to the test environment. deploy-test: uses: ./.github/workflows/deploy.yml needs: [build, lint] with: environmentType: Test resourceGroupName: ToyWebsiteTest reviewApiUrl: https://sandbox.contoso.com/reviews secrets: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_TEST }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} reviewApiKey: ${{ secrets.REVIEW_API_KEY_TEST }}
deploy-production 작업 또한 build 및 lint 작업에 종속되도록 업데이트합니다.
# Deploy to the production environment. deploy-production: uses: ./.github/workflows/deploy.yml needs: - lint - build - deploy-test with: environmentType: Production resourceGroupName: ToyWebsiteProduction reviewApiUrl: https://api.contoso.com/reviews secrets: AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID_PRODUCTION }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} reviewApiKey: ${{ secrets.REVIEW_API_KEY_PRODUCTION }}
프로덕션 배포는 테스트 배포에 따라 달라지므로 반드시 종속성을 지정할 필요는 없습니다. 하지만 작업 또는 환경을 다시 정렬하거나 제거하는 경우 워크플로가 잘못 실행되지 않도록 명시적으로 하는 것이 좋습니다.
두 가지 방법으로
needs
목록을 지정합니다. 테스트 환경 배포의 종속성이 단일 줄에 나열되고, 프로덕션 환경은 여러 줄 목록을 사용하여 나열됩니다. 두 가지 방법은 동일합니다.파일의 변경 내용을 저장합니다.
스모크 테스트 파일 업데이트
웹 사이트 개발자가 웹 사이트에 상태 엔드포인트를 추가했습니다. 이 엔드포인트는 웹 사이트가 온라인 상태이고 데이터베이스에 연결할 수 있는지 확인합니다. 여기서는 배포 워크플로에서 상태 검사를 호출하는 새 스모크 테스트를 추가합니다.
‘deploy’ 폴더에서 ‘Website.Tests.ps1’ 파일을 엽니다.
상태 검사를 호출하는 새 테스트 사례를 추가합니다. 응답 코드가 성공을 나타내는 200이 아닌 경우 테스트 사례가 실패합니다.
param( [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string] $HostName ) Describe 'Toy Website' { It 'Serves pages over HTTPS' { $request = [System.Net.WebRequest]::Create("https://$HostName/") $request.AllowAutoRedirect = $false $request.GetResponse().StatusCode | Should -Be 200 -Because "the website requires HTTPS" } It 'Does not serves pages over HTTP' { $request = [System.Net.WebRequest]::Create("http://$HostName/") $request.AllowAutoRedirect = $false $request.GetResponse().StatusCode | Should -BeGreaterOrEqual 300 -Because "HTTP is not secure" } It 'Returns a success code from the health check endpoint' { $response = Invoke-WebRequest -Uri "https://$HostName/health" -SkipHttpErrorCheck Write-Host $response.Content $response.StatusCode | Should -Be 200 -Because "the website and configuration should be healthy" } }
파일의 변경 내용을 저장합니다.
Bicep 파일에 출력 추가
곧 웹 사이트를 Azure App Service에 게시하는 배포 단계를 추가할 예정입니다. 게시 단계에는 App Service 앱의 이름이 필요합니다. 여기서는 앱 이름을 Bicep 파일의 출력으로 노출합니다.
‘deploy’ 폴더에서 ‘main.bicep’ 파일을 엽니다.
파일 내용의 끝에 App Service 앱의 이름을 출력으로 추가합니다.
output appServiceAppName string = appServiceApp.name output appServiceAppHostName string = appServiceApp.properties.defaultHostName
파일의 변경 내용을 저장합니다.
출력을 전파하도록 배포 작업 업데이트
이제 Bicep 배포의 출력 값을 받아서 나머지 워크플로에서 사용할 수 있도록 deploy 작업을 업데이트해야 합니다.
.github/workflows 폴더에서 deploy.yml 파일을 엽니다.
deploy 작업의 정의에서
appServiceAppName
에 대한 새 출력을 추가합니다.deploy: needs: validate environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest outputs: appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }} appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }} steps:
참고
Visual Studio Code 내 YAML 파일 작업을 시작할 때 문제가 있음을 알리는 빨간색 물결선이 표시될 수 있습니다. YAML 파일의 Visual Studio Code 확장이 파일의 스키마를 잘못 추측하기 때문입니다.
확장에서 보고하는 문제를 무시할 수 있습니다. 또는 원하는 경우 파일 상단에 다음 코드를 추가하여 확장의 추측을 억제할 수 있습니다.
# yaml-language-server: $schema=./deploy.yml
웹 사이트를 배포하는 작업 추가
deploy 작업 정의 아래와 smoke-test 작업 정의 위에서 웹 사이트를 App Service에 배포할 새 작업을 정의합니다.
deploy-website: needs: deploy environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - uses: azure/webapps-deploy@v2 name: Deploy website with: app-name: ${{ needs.deploy.outputs.appServiceAppName }} package: website/publish.zip
참고
YAML 파일의 들여쓰기에 유의하여 새 작업이
deploy
작업과 동일한 수준으로 들여쓰기되도록 해야 합니다. 확실하지 않은 경우 다음 단계의 예제에서 전체 ‘deploy.yml’ 파일 내용을 복사합니다.작업은
needs
키워드를 사용하는 deploy 작업에 따라 달라집니다. 이 종속성이 유지되면 인프라가 준비될 때까지 웹 사이트가 배포되지 않습니다. 또한 작업에서 deploy 작업의appServiceAppName
출력에 액세스할 수 있습니다.또한 이 작업에는 워크플로 아티팩트 다운로드 및 Azure에 로그인하는 단계가 포함됩니다. 각 작업은 자체 실행기에서 실행되므로 자체 포함되어야 합니다.
파일의 변경 내용을 저장합니다.
deploy.yml 파일 내용을 확인하고 변경 내용을 커밋합니다.
deploy.yml 파일이 다음 예제와 같이 표시되는지 확인합니다.
name: deploy on: workflow_call: inputs: environmentType: required: true type: string resourceGroupName: required: true type: string reviewApiUrl: required: true type: string secrets: AZURE_CLIENT_ID: required: true AZURE_TENANT_ID: required: true AZURE_SUBSCRIPTION_ID: required: true reviewApiKey: required: true jobs: validate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - if: inputs.environmentType != 'Production' uses: azure/arm-deploy@v1 name: Run preflight validation with: deploymentName: ${{ github.run_number }} resourceGroupName: ${{ inputs.resourceGroupName }} template: ./deploy/main.bicep parameters: > environmentType=${{ inputs.environmentType }} reviewApiUrl=${{ inputs.reviewApiUrl }} reviewApiKey=${{ secrets.reviewApiKey }} deploymentMode: Validate - if: inputs.environmentType == 'Production' uses: azure/arm-deploy@v1 name: Run what-if with: failOnStdErr: false resourceGroupName: ${{ inputs.resourceGroupName }} template: ./deploy/main.bicep parameters: > environmentType=${{ inputs.environmentType }} reviewApiUrl=${{ inputs.reviewApiUrl }} reviewApiKey=${{ secrets.reviewApiKey }} additionalArguments: --what-if deploy: needs: validate environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest outputs: appServiceAppName: ${{ steps.deploy.outputs.appServiceAppName }} appServiceAppHostName: ${{ steps.deploy.outputs.appServiceAppHostName }} steps: - uses: actions/checkout@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - uses: azure/arm-deploy@v1 id: deploy name: Deploy Bicep file with: failOnStdErr: false deploymentName: ${{ github.run_number }} resourceGroupName: ${{ inputs.resourceGroupName }} template: ./deploy/main.bicep parameters: > environmentType=${{ inputs.environmentType }} reviewApiUrl=${{ inputs.reviewApiUrl }} reviewApiKey=${{ secrets.reviewApiKey }} deploy-website: needs: deploy environment: ${{ inputs.environmentType }} runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v3 - uses: azure/login@v1 name: Sign in to Azure with: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - uses: azure/webapps-deploy@v2 name: Deploy website with: app-name: ${{ needs.deploy.outputs.appServiceAppName }} package: website/publish.zip smoke-test: runs-on: ubuntu-latest needs: deploy steps: - uses: actions/checkout@v3 - run: | $container = New-PesterContainer ` -Path 'deploy/Website.Tests.ps1' ` -Data @{ HostName = '${{needs.deploy.outputs.appServiceAppHostName}}' } Invoke-Pester ` -Container $container ` -CI name: Run smoke tests shell: pwsh
파일의 변경 내용을 저장합니다.
Visual Studio Code 터미널에서 다음 명령을 실행하여 변경 내용을 커밋하고 Git 리포지토리에 푸시합니다.
git add . git commit -m "Build and deploy website application" git push
이 리포지토리에 처음 푸시하는 것이므로 로그인하라는 메시지가 표시됩니다.
Windows에서는 1을 입력하여 웹 브라우저로 인증하고 Enter를 선택합니다.
macOS에서는 권한 부여를 선택합니다.
브라우저 창이 표시됩니다. GitHub에 다시 로그인해야 할 수도 있습니다. 권한 부여를 선택합니다.
워크플로 실행
브라우저에서 작업으로 이동합니다.
‘초기 커밋’이라는 레이블이 지정된 워크플로의 첫 번째 실행은 실패로 표시됩니다. GitHub는 리포지토리를 만들 때 워크플로를 자동으로 실행했습니다. 암호는 해당 시점에 준비되지 않았기 때문에 실패했습니다. 이 오류는 무시해도 됩니다.
deploy-toy-website-end-to-end 워크플로를 선택합니다.
가장 최근에 실행한 워크플로를 선택합니다.
build 작업이 성공적으로 완료될 때까지 기다립니다.
deploy-test / deploy 작업이 성공적으로 완료되기를 기다립니다.
일부 경고는 주석 패널에 나열됩니다. 이러한 경고는 Bicep가 워크플로 로그에 정보 메시지를 쓰는 방식 때문에 발생합니다. 이러한 경고는 무시하면 됩니다.
그런 다음 워크플로는 deploy-test / smoke-test 작업을 실행하지만 스모크 테스트는 실패합니다.
deploy-test / smoke-test 작업을 선택하여 워크플로 로그를 엽니다.
스모크 테스트 실행 단계를 선택하여 연결된 워크플로 로그 섹션을 확인합니다.
워크플로 로그에서 웹 사이트 및 구성이 정상이 아님을 나타냅니다. 애플리케이션과 Azure SQL Database 간 통신에 문제가 있습니다. 데이터베이스를 아직 배포하거나 구성하지 않았기 때문에 웹 사이트가 데이터베이스에 액세스할 수 없습니다. 이 문제는 곧 해결할 예정입니다.