DevOps & MLOps

2023 오픈소스 컨트리뷰션 DevOps & MLOps (7) MLOps편 (Azure MLOps 템플릿 + Github Actions로 파이프라인 만들기)

jamie-lee 2023. 8. 19. 01:24

MLOps 구현하기 2: Azure MLOps (v2) Solution Accelerator 템플릿 사용하기 (Github Actions로 솔루션 엑셀러레이터 배포하기)


참고: Azure/mlops-v2: Azure MLOps (v2) solution accelerators. Enterprise ready templates to deploy your machine learning models on the Azure Platform. (Github.com)
mlops-v2/documentation/deployguides/README.md at main · Azure/mlops-v2 (Github.com)

들어가기 전에

솔루션 엑셀러레이터라는 것을 사용해 머신러닝 프로젝트를 Github Actions으로 자동화하는 과정을 알아본다. (Azure DevOps로 진행하는 옵션도 있다.)

솔루션 엑셀레이터는 Azure를 이용한 MLOps를 구축하기 위해 모듈식 접근 방식을 제공한다. 그 이유는 각 조직의 상황에 맞는 솔루션이 제공되어야 하는 경우가 많기 때문이다.

MLOps 구현하기 1에서는 로컬에서 파이썬 코드로 모델을 학습시키고 배포하는 파이프라인을 만들었다. 이번에는 Azure가 제공하는 mlops 템플릿을 이용하여 Github Actions를 사용하여 학습부터 배포까지 자동화를 구축해볼 것이다.

필요조건

  1. Azure 구독
  2. Github 소스 코드 저장소
  3. Github Actions
  4. Github Client
  5. Azure CLI
  6. Github Actions에서 Azure 리소스로 접근하기 위한 Azure service principals
  7. 로컬 머신의 Git bash, WSL 혹은 다른 쉘 스크립트 에디터
    • WSL을 사용하는 경우 다음을 유의한다!
      1. WSL 환경에서 작업하기: 저장소 복제, 파일 경로 설정 등 전체 작업을 유닉스 환경 내에서 수행하기
      2. VSCode 연결(VSCode 사용자의 경우): “Remote -SSH” 확장 프로그램를 사용하거나, WSL 환경에서 code . 명령어를 사용하여 “Remote - WSL” 확장 프로그램을 사용해 VSCode와 WSL을 연결하여 사용하기
      3. Github CLI 설치: GitHub CLI는 명령 줄에서 GitHub와 상호작용할 수 있는 도구다. sudo apt-get install gh으로 설치하여 사용하기
      4. Github 로그인: gh auth login 명령어로 Github에 로그인한다. 이로서 CLI에서 GItHub 저장소와 상호작용이 가능 (※ 이때 HTTPS 프로토콜이 아닌 SSH 프로토콜을 이용해 로그인한다. 아래 이슈 해결 사례 있음)
      5. Git 로컬 설정: Git의 사용자 이메일과 이름을 설정. git config --global user.email "you@example.com"git config --global user.name "Your Name" 명령어로 설정

Github 환경 구성하기

1. MLOps-V2 템플릿 저장소를 깃헙에 복제한다.

https://github.com/Azure/mlops-templates/fork를 클릭하여 템플릿을 포크한다. 이 레포는 재사용가능한 MLOps 코드로 다양한 프로젝트에서 사용될 수 있다.

Pasted image 20230818150842.png

그 다음 https://github.com/Azure/mlops-project-template/generate를 클릭하여 "mlops-project-template"라고 명명한 저장소를 하나 만든다. 이것은 이후 단계에서 예제 프로젝트를 가져오는데 사용할 모노레포이다.

[!NOTE] 모노레포란?
모노레포(Monorepo)는 여러 프로젝트, 라이브러리, 패키지, 응용 프로그램 등이 하나의 공통 코드 저장소(repository) 안에서 함께 관리되는 저장소 구조를 말한다. 이러한 구조는 대규모 조직이나 복잡한 프로젝트에서 특히 유용할 수 있으며, 대표적인 예로 Google, Facebook, Microsoft와 같은 기업들이 모노레포를 사용하고 있다고 한다.

Pasted image 20230818151430.png

나는 생성해두었기 때문에 이미 존재한다는 경고가 뜬다.

2. mlops-v2 저장소를 로컬에 클론한다.

mlprojects
└ mlops-v2
└ mlops-projects-template

이런 식으로 디렉토리를 구성할 것이다. 먼저 "mlprojects"라는 디렉토리를 생성하고 그 안에서 git clone https://github.com/Azure/mlops-v2.git 명령어를 입력해 mlops-v2 저장소를 클론한다.

3. sparse checkout 구성하기

방금 클론한 저장소의 디렉토리로 들어가서 sparse_checkout.sh를 에디터로 실행한다. Github actions을 이용해 mlops를 구성하기 위한 스크립트라고 보면 된다. vi sparse_checkout.sh 명령어를 이용했다. 그리고 필요에 따라 다음 사용 환경에 맞게 다음 변수를 편집하여 사용하면 된다.

Pasted image 20230818153353.png

  • infrastructure_version: 클라우드 리소스를 배포하는데 사용할 도구이다. terraform, bicep
    • bicep은 Azure 전용, terraform은 다양한 플랫폼 사용 가능
  • project_type: 프로젝트의 AI 워크로드 유형을 선택한다. classical(classical ml), cv(computer vision), nlp
  • mlops_version: Azure 머신러닝과 상호작용할 방식을 선택한다. aml-cli-v2, python-sdk-v1, python-sdk-v2, rai-aml-cli-v2
  • orchestration: 사용할 CI/CD 오케스트레이션 (이 경우는 github-actions). azure-devops, github-actions
  • git_folder_location: 앞 단계에서 mlops-v2 저장소를 복제한 루트 프로젝트 디렉토리
  • project_name: 프로젝트 이름으로 대소문자 구분한다. 이 이름으로 Github 저장소가 생성된다.
  • github_org_name: Githug 조직 이름 혹은 사용자 이름.
  • project_template_github_url: 1단계에서 생성한 mlops-project-template 저장소의 원본이나 혹은 생성된 복제본 URL

[!NOTE] Terraform이란?
Terraform은 인프라스트럭처를 코드로 관리하기 위한 오픈 소스 도구이다. HashiCorp에서 개발되었으며, AWS, Azure, GCP 등 다양한 클라우드 플랫폼에서 일관된 환경으로 인프라를 코드로 관리할 수 있다. Infrastructure as Code (IaC)라는 접근 방식을 사용하여 서버, 데이터베이스, 네트워크, 로드 밸런서 등의 인프라 구성 요소를 자동화하고 관리한다.

나는 아래와 같이 변수를 변경했다.

infrastructure_version=terraform   #options: terraform / bicep
project_type=classical      #options: classical / cv / nlp
mlops_version=aml-cli-v2   #options: aml-cli-v2 / python-sdk-v1 / python-sdk-v2 / rai-aml-cli-v2
orchestration=github-actions #options: github-actions / azure-devops
git_folder_location='/home/jamie/mlprojects'   #replace with the local root folder location where you want to create the project folder
project_name=taxi-fare-regression   #replace with your project name
github_org_name=gcount85   #replace with your github org name
project_template_github_url=https://github.com/gcount85/mlops-project-template   #replace with the url for the project template for your organization created in step 2.2, or leave for demo purposes

4. sparse checkout 실행하기

mlops-v2 디렉토리로 가서 bash sparse_checkout.sh 명령어를 입력해 해당 스크립트를 실행한다. 그러면 스크립트에서 구성한 옵션대로 ML 프로젝트 저장소를 로컬에 복제한다. 그리고 github 저장소를 생성하고 프로젝트 코드를 원격 저장소에 푸시한다.

나의 경우는 taxi-fare-regression이라는 프로젝트가 복제되며, 혹시 문제가 생기는 경우 rm -rf 명령어를 이용해서 이 폴더를 지운 후에 스크립트를 재실행했다.

❗ERROR: github Permission denied (publickey)

앞에서 gh auth login 명령어를 입력해 로그인에 성공했었지만, sparse_checkout.sh 스크립트를 실행하며 faxi-fare-regression 저장소를 클론하는 도중 퍼미션 에러가 떴다.

우선 앞서 로그인 절차는 다음과 같이 진행했다.

Pasted image 20230818155053.png

HTTPS 프로토콜 > Web 브라우저 로그인으로 진행했는데 문제가 있는 듯 하다. 이유를 살펴보니 sparse_checkout.sh 스크립트가 SSH를 사용한 인증을 필요로 한다고 한다.

따라서 gh auth login 명령어를 입력해 재인증을 시도했고 이번에는 HTTPS 프로토콜이 아닌 SSH 프로토콜을 선택하여 인증을 진행했다. 나는 기존에 생성해둔 SSH 키가 WSL 환경에 존재해서 바로 진행했지만, 혹시 SSH 키가 뭔지 모르겠다거나 문제가 있다면 Generating a new SSH key and adding it to the ssh-agent - GitHub Docs 이 부분을 참고하는 것이 좋다.

다시 bash sparse_checkoout.sh를 입력했더니 permission 에러 없이 저장소 클론을 완료했다.

Github 환경 구성하기 (이어서)

5. Github Actions 시크릿 구성하기

이 단계에서는 Azure service principal과 Github 시크릿을 생성하고 Github 워크플로우가 Azure 머신러닝 리소스와 상호작용할 수 있도록 돕는다.

나는 DevOps 과정을 진행하면서 만들어둔 service principal이 있기 때문에 그걸 사용하기로 했다. (참고: 2023 오픈소스 컨트리뷰션 DevOps & MLOps (2) DevOps편 (지속적 배포, Azure Container Instances))

만약 없다면 원하는 service principal 이름과 함께 아래 명령어를 입력하면 service principal이 생성된다. service principal 관련 Azure CLI 명령어의 정보는 여기서 ☞ az ad sp | Microsoft Learn

az ad sp create-for-rbac \
	--name <service_principal_name> \
	--role contributor \
	--scope /subscriptions/<subscription_id> \
	--sdk-auth

service principal이 잘 생성됐으면 아래와 같은 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/"
}

taxi-fare-regression 원격 저장소로 가서 Settings > Secrets and variables > Actions로 들어간다. 아래와 같은 secret을 만든다.

Pasted image 20230818162013.png

여기서 AZURE_CREDENTIALS는 위의 JSON 값을 그대로 괄호까지 복사해서 붙여넣기 하면 된다. 나머지 Secret 변수는 sparse_checkout.sh 파일에서 infrastructure_version 변수를 terraform으로 선택한 경우에 추가해주어야 한다.

이상으로 Github 구성은 끝이다.

Github Actions를 이용해 머신러닝 프로젝트 인프라 배포하기

1. Azure 머신러닝 환경 매개변수 구성하기

자, 다시 “taxi-fare-regression” 프로젝트 저장소로 돌아가면, 루트 폴더에 두 가지 구성 파일이 존재한다. config-infra-dev.yml 파일과 config-infra-prod.yml 파일이 그것이다.

이 파일은 Azure 머신러닝의 개발 환경과 프로덕션 환경을 배포하고 정의하기 위해 사용된다. 기본 배포 구성은, main 브랜치에서 작업할 때는 config-infra-prod.yml이 사용되도록 하고 config-infra-dev.yml 파일은 main이 아닌 브랜치에서 작업할 때 사용되도록 정의되어 있다.

우선 main에서 dev 브랜치를 생성하고 dev 환경을 먼저 배포하는 것이 권장된다.

보통 인프라를 배포하는 것은 한번 실행하고 끝인 경우가 많지만, 나중에 다른 환경을 구축할 수도 있기 때문에 남겨둔다고 한다. (예를 들어 release 브랜치)

각각의 파일을 수정하여 namespace, postifx string, azure location, environment 값을 구성하자. 기본 값은 아래와 같이 나타나있다.

namespace: mlopsv2 #Note: A namespace with many characters will cause storage account creation to fail due to storage account names having a limit of 24 characters.  
postfix: 0001  
location: eastus  
environment: dev  
enable_aml_computecluster: true  
enable_monitoring: false  

namespace 부터 environment 까지는 Azure 환경과 포함하는 리소스를 위해 전역적으로 유일한 이름을 생성하기 위해 사용된다. 수정한 뒤에 저장하고 커밋, 푸시(혹은 PR)까지 진행한다.

만약 CV나 NLP같은 딥러닝 워크로드를 실행하고 있다면, 구독과 Azure location이 가용한 GPU 리소스를 가지고 있는지 확인해야 한다.

[!NOTE]
enable_monitoring 플래그는 false로 기본 설정 되어있다. 이를 true로 설정할 경우 Azure ML 모니터링을 지원하기 위한 추가적인 요소가 필요하므로 비용이 더 들 수 있다고 한다.

나는 아래와 같이 수정했다. (※ namespace 알파벳 소문자, 숫자만 가능하며 길이 이슈가 있으니 11자 이내로 짓는 것 추천. 아래에 관련 이슈 해결 있음)

namespace: osscamlopsv2 
postfix: 0001  
location: koreacentral  
environment: dev  
enable_aml_computecluster: true  
enable_monitoring: false  

2. Azure 머신러닝 인프라 배포

원격 저장소로 돌아가서 Actions 탭을 클릭해 미리 정의한 workflows를 살펴보자. 전통적인 머신러닝 프로젝트에는 다음과 같은 workflow가 정의되어 있다.

케이스에 따라 workflow는 달라질 수 있다. tf-gha-deploy-infra.yml 워크플로우를 선택한다. 이 워크플로우는 Azure 머신러닝 인프라를 Github Actions와 Terraform을 이용해 배포할 것이다.

오른쪽에서 run workflow를 선택해 배포하고자 하는 브랜치를 선택한다. dev 브랜치를 먼저 배포할 것이다.

Pasted image 20230818164137.png

워크플로우 파이프라인 작업업이 성공적으로 마무리되면, Azure 포털에서 생성된 인프라를 확인해볼 차례이다.

리소스 그룹 목록에서 새롭게 생성 된 두 개의 리소스 그룹을 확인할 수 있다. 끝에 terraform의 약자인 tf가 붙은 리소스 그룹이 하나 더 있다. (demo9로 namespace를 바꿔서 다시 배포한 케이스이다.)

Pasted image 20230819165712.png

첫 번째 리소스 그룹으로 들어가면 다음과 같은 리소스가 생성된 것을 볼 수 있다.

Pasted image 20230819165934.png

Azure ML 워크스페이스에서 생성된 워크스페이스를 확인할 수 있다.

Pasted image 20230818224506.png

이제는 익숙할 Azure 머신러닝 스튜디오 > Workspaces 에서 새롭게 생긴 워크스페이스를 확인할 수 있다.

다음 단계에서는 모델을 훈련시키고 추론 값을 내는 과정을 파이프라인으로 빌드하고 새로운 Azure 머신러닝 환경에서 배포할 것이다.

❗ERROR: Github deploy infra 워크플로우 AuthorizationFailed

해당 workflow를 실행하던 도중 “create-tfstate-resource-group” 단계에서 아래와 같은 에러를 마주했다.

ERROR: (AuthorizationFailed) The client '<sp-object-id>' with object id '<sp-object-id>' does not have authorization to perform action 'Microsoft.Resources/subscriptions/resourcegroups/write' over scope '/subscriptions/***/resourcegroups/rg-ossca-mlopsv2-0001dev-tf' or the scope is invalid. If access was recently granted, please refresh your credentials.

음, 내가 사용한 service principal의 scope 문제인 것 같다고 추측되었다. 내가 가지고 있던 기존 service principal의 scope가 수행하고자 하는 작업을 포함하고 있지 않은 듯 하다.

먼저 나의 기존 SP의 scope를 확인하기 위해 관련 cli를 찾아보았다. 다음과 같은 명령어로 입력하면 입력하면 나의 현재 구독 하의 service principal에 할당된 모든 역할을 알 수 있다. (참고: az role | Microsoft Learn)

az role assignment list --assignee <sp-object-id> --all

위 에러에서 알려주고 있는 나의 sp-object-id를 집어넣는다. JSON 타입의 값을 응답으로 받는다. 나의 경우 두 개의 롤을 보여주고 있고, 아래처럼 두 개의 스코프가 나와있다.

{
...
"roleDefinitionName": "Contributor",
"scope": "/subscriptions/<나의-구독-id>/resourceGroups/OsscaDevops-RG",
... 
}
,
{
...
"roleDefinitionName": "AcrPush",
"scope": "/subscriptions/<나의-구독-id>/resourceGroups/OsscaDevops-RG/providers/Microsoft.ContainerRegistry/registries/osscadevopsimages"
...
}

OsscaDevops-RG라는 리소스 그룹에 대한 스코프와 Azure Container Registry에 대한 스코프이다. 여기다가 내가 지금 하려는 MLOps 워크플로우가 요구하는 scope도 추가하면 될 것 같다.

az role assignment create \
	--assignee <sp-object-id> \
	--role contributor \
	--scope /subscriptions/<나의-구독-id>

위와 같은 명령어를 입력해 scope를 추가해주었다. 이건 어떻게 알았냐면, 위의 “Github 환경 구성하기” 5단계에서 service principal을 생성하는 명령어에 포함되어 있는 정보이다.

구독 단위의 스코프를 다루고 있어서 기존의 service principal의 scope 보다 훨씬 큰 스코프이다. 그래서 퍼미션 에러가 났던 듯 하다. (나중에 확인하겠지만 여러 리소스 그룹에 접근해야 하기 때문이다. )

다시 az role assignment list --assignee <sp-object-id> --all 명령어를 입력하면 이번엔 3개의 JSON 값을 받을 수 있고, 마지막 JSON 값이 가장 최근에 업데이트 된 방금 추가한 scope를 담고 있음을 확인할 수 있다.

{
...
"roleDefinitionName": "Contributor",
"scope": "/subscriptions/<나의-구독-id>",
"updatedOn": "2023-08-18T13:00:05.899745+00:00"
}

다시 workflow 화면으로 돌아가 Re-run all jobs를 클릭하고 재시도한다. 다행히도 “create-tfstate-resource-group” 단계를 잘 통과하는 걸 확인할 수 있었다. ✌️

❗ERROR: Github deploy infra 워크플로우 AccountNameInvalid

잘 되나 싶더니, 바로 다음 스텝인 "create-tfstate-storage-account"에서 또 에러가 발생했다.

Starting script execution via docker image mcr.microsoft.com/azure-cli:2.30.0

ERROR: (AccountNameInvalid) stossca-mlopsv20001devtf is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only.

Code: AccountNameInvalid

Message: stossca-mlopsv20001devtf is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only.

"stossca-mlopsv20001devtf is not a valid storage account name. Storage account name must be between 3 and 24 characters in length and use numbers and lower-case letters only."라는 문구가 들어온다.

아… 내가 “config-infra-dev.yml” 명세 파일에서 namespace를 "ossca-mlopsv2"로 지정했는데 이 부분이 문제가 된 듯 하다. 숫자와 알파벳 소문자만 작성하라는 메시지이다. 게다가 24 글자 내로 작성하라는데 아슬아슬하다.

다시 yml 파일로 돌아가서 해당 부분을 수정해준 후, 커밋하고 푸시했다.

Pasted image 20230818222728.png

성공적으로 해당 job이 돌아간 것을 확인할 수 있었다. 만세~

모델 학습 및 배포 시나리오

솔루션 엑셀러레이터에는 뉴욕시의 택시 요금을 예측하기 위해 선형 회귀를 실행하는 샘플 머신러닝 파이프라인을 빌드할 수 있는 코드와 데이터가 포함되어 있다. 파이프라인은 각각 다른 기능을 제공하는 컴포넌트로 구성되어 있으며, 이전 편에서도 보였듯이 워크스페이스에 등록하고 버전을 관리하고 다양한 입출력 값과 함께 재사용할 수 있다.

컴퓨터 비전이나 NLP의 경우는 이와는 다른 절차를 밟는다.

지금 다루고자 하는 파이프라인에는 다음 단계들을 포함한다.

  1. 데이터 준비
    • 다양한 택시 데이터셋(노란색, 녹색)을 가져와 데이터를 병합하고 필터링한다
    • 훈련/검증/평가를 위한 데이터 셋을 준비한다
    • input: ./data/ (여러 개의 .csv 파일들)
    • output: 단일 데이터셋 (.csv), 훈련/검증/평가 데이터셋
  2. 모델 학습
    • 이 컴포넌트는 학습 데이터에 대해 선형 회귀를 실행한다.
    • input: 훈련 데이터셋
    • output: 훈련된 모델 (pickle 포맷)
  3. 모델 평가
    • 이 컴포넌트는 훈련된 모델에 평가 데이터를 가지고 택시 요금을 예측한다.
    • 이 컴포넌트는 새 데이터 셋의 모델 성능을 이전에 배포된 모든 모델과 비교하여 모델을 프로덕션으로 올릴 건지 말 건지 여부를 결정한다. 모델을 프로덕션 환경으로 올리려면 Azure 머신러닝 워크스페이스에 모델을 등록해야 한다.
    • input: ML 모델과 평가 데이터
    • output: 모델의 성능, 배포 여부를 알려주는 플래그
  4. 모델 등록
    • 이 컴포넌트는 평가 데이터 셋에서 예측이 얼마나 정확한지에 따라 모델에 점수를 매긴다.
    • input: 훈련된 모델, 배포 플래그
    • output: Azure 머신러닝에 등록된 모델

모델 학습 파이프라인을 테스트 환경에 배포하기

이제 모델 학습 파이프라인을 새로운 Azure 머신러닝 워크스페이스에 배포할 것이다.

이 파이프라인은

  1. 컴퓨팅 클러스터 인스턴스를 생성하고
  2. 필수적인 도커 이미지와 파이썬 패키지를 정의하는 학습 환경을 등록하고
  3. 훈련 데이터셋을 등록하고
  4. 마지막으로 훈련 파이프라인을 시작시킨다.

다시 taxi-fare-regression 원격 저장소로 돌아가서 Actions을 클릭하자. 이번에는 “deploy-model-training-pipeline” 워크플로우를 선택하고 Run Workflow를 클릭한다.

워크플로우가 모두 수행되는데 약 15분 정도 소요되며, job이 모두 끝나면 학습된 모델은 Azure 머신러닝 워크스페이스에 등록되어 배포를 위해 쓰일 수 있다.

❗ERROR: Github deploy model training pipeline 워크플로우 Quota 이슈

“create-compute-using-tier” job에서 에러가 났다.

ERROR: (BadRequest) ***"id":"[https://resourceprovider.batchai-koreacentral.svc/subscriptions/***/providers/Microsoft.BatchAI/locations/koreacentral/operationresults/1f0b75fc-9a48-4fbf-880b-96dbc7e07b94](https://resourceprovider.batchai-koreacentral.svc/subscriptions/***/providers/Microsoft.BatchAI/locations/koreacentral/operationresults/1f0b75fc-9a48-4fbf-880b-96dbc7e07b94)","name":"1f0b75fc-9a48-4fbf-880b-96dbc7e07b94","status":"Failed","startTime":"2023-08-18T13:44:14.53Z","endTime":"2023-08-18T13:44:19.889Z","error":***"code":"ClusterMinNodesExceedCoreQuota","message":"The specified subscription has a total vCPU quota of 0 and cannot accomodate for at least 1 requested managed compute node which maps to 4 vCPUs. Talk to your Subscription Admin or refer to [https://docs.microsoft.com/azure/machine-learning/how-to-manage-quotas#request-quota-increases](https://docs.microsoft.com/azure/machine-learning/how-to-manage-quotas#request-quota-increases) to increase the total quota"***

“The specified subscription has a total vCPU quota of 0 and cannot accomodate for at least 1 requested managed compute node which maps to 4 vCPUs.” 라는 문구를 주목하면 quota가 없다고 말하고 있다.

에러만 봐서는 어떤 인스턴스 타입을 사용하고 있는지 알 수 없으므로 workflow를 정의하고 있는 yml 파일로 가본다.

프로젝트 폴더의 .github/workflows/deploy-model-training-pipeline-classcial.yml 을 열어서 “create-compute” job을 살펴봤더니, 아래와 같이 정의되어 있는 것을 볼 수 있다.

  create-compute:
    needs: [get-config]
    uses: Azure/mlops-templates/.github/workflows/create-compute.yml@main
    with:
      cluster_name: cpu-cluster
      size: Standard_DS3_v2
      min_instances: 0
      max_instances: 4
      cluster_tier: low_priority
      resource_group: ${{ needs.get-config.outputs.resource_group }}
      workspace_name: ${{ needs.get-config.outputs.aml_workspace }}

여기서 “Standard_DS3_v2” 인스턴스 타입을 사용하고 있음을 확인했다.

Azure 포털 > 구독 > 사용량 및 할당량에서 머신러닝, korea central로 필터링하여 할당량을 확인했다.

Pasted image 20230818232931.png

흠, 하지만 할당량은 널널했다. 👀👀?

인스턴스 타입이 Region에 없는 인스턴스 타입인가 싶어서 "Standard_D2a_v4"로도 바꿔보았지만 여전히 실패했다.

uses의 “Azure/mlops-templates/.github/workflows/create-compute.yml” 파일에 찾아가 내가 놓친 사항이 있는지 확인해보았다.

Pasted image 20230818233645.png

여기가 문제의 부분이다.

내 job은 "create-compute-using-tier"로 넘어가고 있었고 그것의 분기가 되는 조건은 cluster_tier 값이 있느냐 없느냐였다. 디폴트 값은 dedicated이다.

CPU cluster를 구성할 때 경험상 region에 따라 달라지는 것들이 있었기 때문에 cluster_tier를 살펴봐야겠다는 생각이 들었다.

나의 현재 yml은 low_priority라는 클러스터 티어가 지정되어 있고, dedicated라는 단어가 많이 낯익었다. 내 region인 "korea central"의 클러스터 티어는 죄다 dedicated 였던 것 같은데…?

다시 “사용량 및 할당량” 대시보드로 들어가 확인해보니,

Pasted image 20230818234037.png

Pasted image 20230818234110.png

위의 이미지처럼 "low"로 검색했을때와 "dedicated"로 검색했을 때 다른 cluster가 검색되는 것을 알 수 있었고 “low priority” 클러스터 티어는 korea central 지역에 할당되지 않았다!

그래서 다시 yml 파일로 돌아가 cluster_tier의 값을 아래처럼 바꾸어주었다.

  create-compute:
    needs: [get-config]
    uses: Azure/mlops-templates/.github/workflows/create-compute.yml@main
    with:
      cluster_name: cpu-cluster
      size: Standard_D2a_v4
      min_instances: 0
      max_instances: 1
      cluster_tier: dedicated
      resource_group: ${{ needs.get-config.outputs.resource_group }}
      workspace_name: ${{ needs.get-config.outputs.aml_workspace }}

이번에는 잘 돌아간다! 대시보드에서도 리소스가 잘 할당된 것을 볼 수 있었다. (난 혹시 몰라 인스턴스 타입과 맥스 인스턴스 개수도 바꾸었다.)

Pasted image 20230818234444.png

방금 배포한 모델 학습 파이프라인 Azure 포털에서 확인하기

Azure 머신러닝 스튜디오 > 해당하는 워크스페이스(내 경우 “mlw-osscamlopsv2-0001dev”) > Pipelines에서 아래와 같이 방금 만든 파이프라인이 올라간 걸 볼 수 있다. Good!

Pasted image 20230818234917.png

Pasted image 20230819173657.png

Dev 브랜치의 학습된 모델 배포하기

두 가지 시나리오가 있다.

  1. 온라인 엔드포인트로 모델 배포하여 실시간 scoring
  2. batch scoring

dev 브랜치에서 이러한 워크플로우 중 하나 혹은 둘 모두를 실행하여 Azure 머신러닝 워크스페이스에서 모델 성능을 테스트할 수 있다.

Actions로 다시 가보자.

온라인 엔드포인트에 배포하기

“deploy-online-endpoint-pipeline” 워크플로우를 선택하여 dev 브랜치로 선택하고 Run Workflow를 클릭한다. 이 워크플로우에서는 다음과 같은 스텝을 수행한다.

  1. Azure 머신러닝 워크스페이스에 엔드포인트를 생성한다
  2. 이 엔드포인트에 모델을 배포한다.
  3. 엔드포인트에 트래픽을 할당한다.

이전 편에서 python 코드로 수행했던 부분이기도 하다.

완료되면 Azure 대시보드에서 다음과 같이 엔드포인트를 확인할 수 있다.

Pasted image 20230819004627.png

아래처럼 트래픽 할당도 잘 되어 디플로이먼트에 성공했다. test도 가능하다. 이전 편에서 머신러닝 엔드포인트 테스트하듯이 동일하게 진행하면 된다.

Pasted image 20230819174043.png

#✅todo/테스트인풋

❗ERROR: 온라인 엔드포인트 네이밍 길이 이슈

workflow가 실행되던 도중 “create-environment-from-file” job에서 아래와 같은 에러 메시지를 만났다.

 az ml online-endpoint create --name taxi-gha-oep-osscamlopsv2-0001dev \
  -f /home/runner/work/taxi-fare-regression/taxi-fare-regression/mlops/azureml/deploy/online/online-endpoint.yml --resource-group rg-osscamlopsv2-0001dev \
  --workspace-name mlw-osscamlopsv2-0001dev
  shell: /usr/bin/bash -e ***0***
  env:
    AZURE_HTTP_USER_AGENT: 
    AZUREPS_HOST_ENVIRONMENT: 
ERROR: Met error <class 'Exception'>:

1) One or more fields are invalid

Details: 

(x) The name for an endpoint must be at least 3 and at most 32 characters long (inclusive of both limits).

"The name for an endpoint must be at least 3 and at most 32 characters long"이라고 하는 걸 보니 또 네이밍 문제가 생겼다.

“taxi-gha-oep-osscamlopsv2-0001dev” 요것이 자동으로 생성된 나의 온라인 엔드포인트 이름인 것 같은데, 개수를 세보니 딱 33자이다…!

이름을 수정해주러 떠나야겠다.

“deploy-online-endpoint-pipeline-classical.yml” 파일에서 아래 부분의 endpoint_name 부분을 참고한다.

  create-endpoint:
    needs: get-config
    uses: Azure/mlops-templates/.github/workflows/create-endpoint.yml@main
    with:
      resource_group: ${{ needs.get-config.outputs.resource_group }}
      workspace_name: ${{ needs.get-config.outputs.aml_workspace }}
      endpoint_file: mlops/azureml/deploy/online/online-endpoint.yml
      endpoint_name: ${{ format('taxi-gha-{0}', needs.get-config.outputs.oep) }}
      endpoint_type: online
    secrets:
      creds: ${{secrets.AZURE_CREDENTIALS}}

endpoint_name: ${{ format('taxi-gha-{0}', needs.get-config.outputs.oep) }} 이 부분이 문제이다. 처음에 이름 좀 짧게 지을 걸… 약간 후회를 했다.

하지만 지금 와서 "config-infra-dev.yml"로 돌아가 namespace를 짧게 수정하면 문제가 생길 수 있다. 왜냐하면 이미 해당 namespace를 이용해 리소스 그룹과 workspace가 생성되었고 그대로 참조하도록 yml 파일이 정의되어 있기 때문이다. (아래 참조)

  # "config-infra-dev.yml" 
  # For pipeline reference
  resource_group: rg-$(namespace)-$(postfix)$(environment)
  aml_workspace: mlw-$(namespace)-$(postfix)$(environment)
  application_insights: mlw-$(namespace)-$(postfix)$(environment)
  key_vault: kv-$(namespace)-$(postfix)$(environment)
  container_registry: cr$(namespace)$(postfix)$(environment)
  storage_account: st$(namespace)$(postfix)$(environment)

따라서 나는 임시 방편으로 gha(github action)을 gh 으로 바꿔주는 조치를 취했다. 주의할 점은 해당 yml 파일에서 gha를 모두 gh로 바꿔주어야 참조 에러가 안날 것이다. 그리고 겸사겸사 batch endpoint yml 파일에서도 동일한 조치를 취했다.

다시 워크플로우를 실행하자 네이밍 이슈는 잘 해결됨을 확인했다.

❗ERROR: 엔드포인트 이름 중복 에러

해당 워크플로우를 여러 번 실행하다보면 다음과 같은 에러를 볼 수 있다.

ERROR: (UserError) An endpoint with this name already exists. If you are trying to create a new endpoint, use a
different name. If you are trying to update an existing endpoint, use `az ml online-endpoint update` instead.

다른 단계에서 에러를 해결하려고 여러 번 돌리다가 맞닥뜨렸는데 엔드포인트 이름이 유니크해야 해서 발생하는 일이다. 이전 워크플로우를 수행하며 생성된 엔드포인트가 남아있어서 그렇다.

좀 귀찮지만 Azure 머신러닝 스튜디오 > Endpoints에서 생성된 엔드포인트를 삭제해주어야 한다.

❗ERROR: deploy online endpoint Quota 이슈

“create-deployment” Job을 수행하던 도중 또 Quota 이슈를 만났다.

Exception Details:	(InferencingClientCreateDeploymentFailed) InferencingClient HttpRequest error, error detail: ***"errors":***"VmSize":["Not enough quota available for Standard_DS3_v2 in SubscriptionId ***. Current usage/limit: 0/6. Additional needed: 8

8개 cpu가 더 필요한데 6개 리밋이라 불가능하다는 문구가 눈에 띈다. 아무래도 인스턴스 타입을 더 낮은 수준으로 낮춰서 다시 시도해야 할 것 같다. "mlops/azureml/deploy/online/online-deployment.yml"에서 인스턴스 타입을 "Standard_DS3_v2"에서 "Standard_DS2_v2"로 바꿔줬다.

$schema: https://azuremlschemas.azureedge.net/latest/managedOnlineDeployment.schema.json
name: blue
endpoint_name: taxi-fare-online
model: azureml:taxi-model@latest
instance_type: Standard_DS2_v2
instance_count: 1

잘 돌아간다.

Batch 엔드포인트에 배포하기

마찬가지로 이번엔 “deploy-batch-endpoint-pipeline” 워크플로우를 dev 브랜치로 수행한다.

이 워크플로우에서는,

  1. Batch scoring을 실행할 새로운 AmlCompute 클러스터를 만든다.
  2. 워크스페이스에 배치 엔드포인트를 만든다.
  3. 모델을 이 엔드포인트에 배포한다.

※ 클러스터를 만드는 과정은 위에서 "❗ERROR: Github deploy model training pipeline 워크플로우 Quota 이슈"에서 언급했듯이 cluster_tierdedicated로 바꾸고 max_instances 수를 최소한으로 낮추어야 Quota 이슈를 피할 수 있다!!!

워크플로우가 무사히 잘 수행되었고 엔드포인트가 생성된 것은 아래처럼 확인할 수 있다.

Pasted image 20230819010429.png

또한 디플로이먼트도 성공적으로 마쳤다.

Pasted image 20230819010700.png

마치며

이로써 템플릿을 이용해 MLOps를 구축해보는 두 번째 챕터까지 마무리했다. 첫 번째 챕터는 로컬에서 파이썬 코드로 파이프라인을 빌드하는 과정이었다면 이번 챕터는 Github 저장소에서 워크플로우를 이용해 자동화를 구축하는 과정이었다.

기본적인 절차를 따랐음에도 불구하고 DevOps보다 MLOps가 훨씬 더 복잡하다. 하지만 그만큼 또 지식의 지평을 넓힐 수 있는 기회가 되었다. 이제 다음 단계는 지금 배운 것을 토대로 실제 프로젝트에 적용해 볼 단계이다.