클라우드 서비스에 github workflow 구성하기 w/ Azure CLI
(1)편에 이어 Azure 서비스에 본격적으로 배포 자동화를 구축한다. Azure에 컨테이너 인스턴스를 생성하도록 github action을 구성한다. 진행하기 위해서 두 가지 필요조건이 있다.
- azure CLI
- azure container registry
azure CLI를 이용할 것이다. azure CLI를 설치하려면 여기를 참고한다 ☞ Install the Azure CLI on Linux | Microsoft Learn
Azure 리소스 그룹 생성하기
리소스 그룹을 꼭 사용해야 하는 것인가? 찾아본 설명에 따르면, azure 리소스 그룹은 리소스 관리의 기본 단위이므로 리소스를 생성할 때마다 리소스 그룹을 지정해야 한다고 한다. 따라서 리소스 그룹을 사용하는 것이 필수적이다.
그렇다면 리소스 그룹 안에 포함될 수 있는 리소스에는 어떤 것들이 있을까?
가상 머신, 스토리지 계정, SQL 데이터베이스, 함수 앱, 컨테이너 인스턴스 등 Azure가 지원하는 다양한 서비스와 리소스를 포함시킬 수 있다.
한 마디로 프로젝트 디렉토리 안에 관련된 프로젝트 파일을 넣듯, 리소스 그룹 안에 특정 프로젝트와 관련된 리소스를 담는 것이라 보면 되겠다.
리소스 그룹은 이런 다양한 리소스를 관리하고 조직화하는데 도움이 된다고 한다. (라이프 사이클 관리, 액세스 관리, 비용 추적 관리 등이 용이해진다고 한다.)
Azure > 리소스 그룹에서 리소스 그룹을 생성한다. 직관적으로 진행하면 되며 어려운 것은 별로 없었다. OsscaDevops-RG
라고 이름을 지었다.
Azure authentication을 위해 자격증명 생성하기
여기를 참고하였다 ☞ Deploy container instance by GitHub Actions - Azure Container Instances | Microsoft Learn
groupId=$(az group show \
--name <리소스-그룹-이름> \
--query id --output tsv)
위와 리소스 그룹 네임에 내가 방금 지은 리소스 그룹 이름을 넣고, 위와 같은 명령어를 입력하면 $groupId
에 리소스 그룹의 id 값이 담긴다.
아무 일도 일어나지 않는데, 결과는 아래 명령어를 치고 확인할 수 있다.
echo $groupId
이제 service principal을 생성하기 위한 작업을 시작한다.
[!NOTE] Service Principal이란?
애플리케이션, 서비스 또는 자동화 도구가 Azure API를 사용해 리소스를 관리할 수 있도록 하는 보안 자격 증명이다. Azure AD에서 애플리케이션과 연결되며, 이는 Azure의 리소스에 대해 특정 권한을 갖고 있다. 이를 통해 유저가 직접 로그인해 작업을 수행할 필요 없이, 애플리케이션이나 서비스가 리소스에 액세스하는 작업을 자동으로 이루어지게 할 때(예를 들어 CI/CD 파이프라인 같은 자동화 도구를 이용하는 경우) 필요하다.
az ad sp create-for-rbac \
--scope $groupId \
--role Contributor \
--sdk-auth
그러면 보안에 주의하라는 메시지와 함께 아래와 같은 JSON 값이 출력된다. 코드에 포함시키거나 유출시키지 말자. 다음 스텝에서 사용할 예정이므로 따로 저장해두는 것이 좋다.
{
"clientId": "xxxx6ddc-xxxx-xxxx-xxx-ef78a99dxxxx",
"clientSecret": "xxxx79dc-xxxx-xxxx-xxxx-aaaaaec5xxxx",
"subscriptionId": "xxxx251c-xxxx-xxxx-xxxx-bf99a306xxxx",
"tenantId": "xxxx88bf-xxxx-xxxx-xxxx-2d7cd011xxxx",
"activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
"resourceManagerEndpointUrl": "https://management.azure.com/",
"activeDirectoryGraphResourceId": "https://graph.windows.net/",
"sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
"galleryEndpointUrl": "https://gallery.azure.com/",
"managementEndpointUrl": "https://management.core.windows.net/"
}
Azure > 리소스 그룹 > 액세스 제어(IAM) 에서도 아래와 같이 역할이 추가된 것을 볼 수 있다.
ACR 생성하고 service principal 업데이트하기
이 단계에서는 깃헙 워크플로우가 service principal을 이용하여 내가 빌드한 도커 이미지가 업로드된 저장소에 접근해 이미지를 push, pull 할 수 있는 권한을 가질 수 있게끔 한다.
아, 그런데 대뜸 나의 container registry 이름을 내놓으라고 한다. 지금까지 로컬에서만 도커 이미지를 빌드하고 실행만 했을 뿐 저장소를 생성하지 않았다. 그러므로 Azure에서 제공하는 Azure Container Registry를 생성한다.
Azure > 모든 서비스 > 컨테이너 레지스트리에서 컨테이너 레지스트리를 만든다.
리소스 그룹은 방금 만든 "OsscaDevops-RG"로 설정하였다.
컨테이너 저장소 이름을 지어야 하는데, 이름 짓는 건 항상 고민이기에 레지스트리 네이밍 관행에 관해 GPT4로부터 조언을 받았다. "osscadevopsimages"라고 지었다. 특수문자는 불가능하다. 무료 구독에서는 이것들 외에 건드릴 필드가 없다.
리소스 그룹 대시보드에 아래처럼 레지스트리가 추가된 것이 보인다.
이제 레지스트리 이름을 알았으니 다시 service principal 파트로 되돌아간다.
registryId=$(az acr show \
--name <레지스트리-이름> \
--resource-group <리소스-그룹-이름> \
--query id --output tsv)
그 다음 저장소에 push/pull 접근 권한을 주기위해 az role assignment create
명령어를 이용한다.
아까 만든 리소스 그룹의 clientId가 필요하다.
az role assignment create \
--assignee <ClientId> \
--scope $registryId \
--role AcrPush
성공적으로 역할 할당이 수행되면 JSON 형태로 관련 정보가 출력된다.
Github 저장소에 자격증명 저장하기
이제 azure에 도커 이미지 저장소도 만들었고, 이 이미지에 접근할 수 있는 자격증명도 생성했다.
내가 원하는 것은 깃헙에 push하면 자동으로 도커 이미지가 빌드되어 azure의 저장소에 push되고, 이를 자동으로 배포하게끔 만드는 것이다. 깃헙 저장소가 azure의 도커 이미지 저장소에 접근하려면, 방금 만든 이 자격증명 정보를 알려줘야 한다.
깃헙 저장소에서 setting > security > secrets and variables > actions에서 new repository secret을 눌러 아래 변수를 하나하나 추가한다.
AZURE_CREDENTIALS
service principal을 생성할때 출력된 전체 JSON 결과
REGISTRY_LOGIN_SERVER
Azure 컨테이너 레지스트리(registry) 서버 주소
예시: myregistry.azurecr.io
REGISTRY_USERNAME
service principal 생성시 clientId 값
REGISTRY_PASSWORD
service principal 생성시 clientSecret 값
RESOURCE_GROUP
service principal 생성에 사용하고, 리소스가 위치한 그룹 이름
Name 필드가 말하자면 변수명이고, 값을 secret 필드에 복붙한다. (처음에는 .env
파일 작성하듯이 모든 변수명과 상응하는 값을 하나의 시크릿에 다 작성하는 줄 알았다.)
해당 부분은 Azure document에도 설명이 잘 되어있다.
workflow 파일 만들기
아래와 같은 deploy를 위한 workflow 파일을 생성한다. 위에서 생성한 자격증명 값을 가리키는 변수가 포함되어 있다.
name: flask app deployment to ACI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
# checkout the repo
- name: 'Checkout GitHub Action'
uses: actions/checkout@main
- name: 'Login via Azure CLI'
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: 'Build and push image'
uses: azure/docker-login@v1
with:
login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- run: |
docker build . -t ${{ secrets.REGISTRY_LOGIN_SERVER }}/flaskapp:${{ github.sha }}
docker push ${{ secrets.REGISTRY_LOGIN_SERVER }}/flaskapp:${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: 'Login via Azure CLI'
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: 'Deploy to Azure Container Instances'
uses: 'azure/aci-deploy@v1'
with:
resource-group: ${{ secrets.RESOURCE_GROUP }}
dns-name-label: ${{ secrets.RESOURCE_GROUP }}${{ github.run_number }}
image: ${{ secrets.REGISTRY_LOGIN_SERVER }}/flaskapp:${{ github.sha }}
registry-login-server: ${{ secrets.REGISTRY_LOGIN_SERVER }}
registry-username: ${{ secrets.REGISTRY_USERNAME }}
registry-password: ${{ secrets.REGISTRY_PASSWORD }}
name: aci-flaskapp
location: 'korea central'
❗ISSUE 도커 빌드 에러
쭉쭉 잘 되다가 도커 빌드하는 부분에서 에러가 발생했다.
도커 이미지는 잘 생성한 것 같은데🤔 unauthorized 에러이다. clientID가 반드시 guid여야 한다고 한다. guid 값을 잘 넘겨주었는데도 불구하고 service principal의 clientID를 제대로 인식하지 못하는 것 같다.
이 때, repository secret을 생성하는 과정에서 의심스러웠던 부분이 하나 떠올랐다.
바로 secret 필드에 토큰 값을 따옴표로 묶어주느냐 마느냐!
고민하다가 토큰 값을 "abcded"
처럼 따옴표로 묶어서 생성했는데 아무래도 그게 오류를 일으킨 것 같다는 느낌이 왔다. 그래서 다시 따옴표를 제거하여 secret 값을 모두 다시 업데이트하였다.
잘 된다. 😊😊
deploy에서 다음과 같은 오류가 뜨는 것은 예상대로이다. 왜냐하면 아직 컨테이너 인스턴스 서비스를 구성하지 않았기 때문.
Azure 홈 > 컨테이너 레지스트리 > 리포지토리에서 방금 push한 "flaskapp"이라는 이미지가 보인다. 나이스.
CLI로는 아래 명령을 입력하면 현재 Azure 컨테이너 저장소에 push된 이미지 리스트를 확인할 수 있다.
az acr repository list \
--name <나의-저장소-이름> \
--output table
빌드와 저장소 푸시까지 잘 되는 것을 확인하였으니 이제 Azure Container Instance를 구성하러 가자.
Azure Container Instances 구성하기
ACI란 도커 컨테이너를 빠르고 간편하게 실행할 수 있게 해주는 서비스이다. 서버리스 컴퓨팅의 한 형태라고 하며, 컨테이너를 실행하기 위한 가상 머신이나 서버를 직접 관리할 필요가 없다. 서버리스 + 컨테이너 환경이다보니 빠르고 유연하게 작업을 시작하고 종료할 수 있다.
먼저 컨테이너를 직접 deploy 한다. 아래 명령어에서 <>
로 감싸져 있는 부분을 상응하는 값으로 대체하여 입력한다. 이때 aciDnsLabel
은 해당 리전에서 고유해야 한다고 하니 주의.
az container create \
--resource-group <리소스-그룹-이름> \
--name <컨테이너-이름> \
--image <acrLoginServer>/<컨테이너-이름>:v1 \
--cpu 1 \
--memory 1 \
--registry-login-server <acrLoginServer> \
--registry-username <service-principal-ID> \
--registry-password <service-principal-password> \
--ip-address Public \
--dns-name-label <aciDnsLabel> \
--ports 80
성공하면 Azure response를 받을 것이며, 리소스 그룹 대시보드에서 잘 돌아가고 있는 인스턴스가 보인다.
아래 명령어를 입력하면 컨테이너 DNS를 알 수 있고 웹 페이지를 띄우도록 했다면 확인할 수 있다.
az container show \
--resource-group <리소스-그룹-이름> \
--name <컨테이너-인스턴스-이름> \
--query ipAddress.fqdn
나는 웹 서버가 아닌 간단한 출력 (“hi”, “1.5”) 을 남기는 코드를 짰기 때문에, 컨테이너 DNS로 접속해서 확인할 순 없고 컨테이너의 콘솔에 출력문이 잘 찍히는지 확인해봐야 했다.
로그를 확인하려면 아래 명령어를 입력하면 된다.
az container logs \
--resource-group <리소스-그룹-이름> \
--name <컨테이너-인스턴스-이름>
아니면 방금 실행한 컨테이너의 대시보드에 들어가 설정 > 컨테이너 > 로그를 직접 확인해도 된다.
둘 다 잘 찍힌다! 성공이다. 👍
잘 되는 것을 확인했으니 우선 해당 리소스를 삭제하고, 이번엔 github actions으로도 잘 배포되는지 확인할 필요가 있다.
Github Actions으로 deploy하기
github workflow에서 re-run failed jobs 버튼을 클릭해 아까 실패했던 잡을 재실행한다.
이번에는 성공적으로 잘 수행되었다.
Azure에도 컨테이너가 잘 돌아가고 있음을 확인할 수 있었고, 출력 로그도 잘 찍혀 있는 것을 확인했다. 만세~🙌
이로써 메인 브랜치에 push부터 컨테이너 배포까지 자동화 파이프라인이 구축되었다. (잘 돌아가는 것 확인했으니 컨테이너 삭제 + 불필요한 이미지를 삭제한다.)
마치며
이제 다음 편에서는 한 단계 더 나아가서 ACI 뿐만 아니라 Azure의 다른 서비스인 AKS (Azure Kubernetes Service)에도 배포 자동화를 구축할 예정이다. 고고.