Github Actions self hosted runner with own GPUs
Github Actions는 Gtihub을 기반으로 하는 CI/CD, 여러 개발 Workflow 자동화 툴이다.
그런데 사용하다 보면 Github Actions workflow를 Github-hosted runner에서만 실행시키는데 한계가 있는 경우가 있다.
A GitHub-hosted runner is a virtual machine hosted by GitHub with the GitHub Actions runner application installed.
구체적으로 어떤 경우가 있을 수 있는지 'ML application을 위한 Workflow를 Github Actions로 실행하고자 할 때'를 예시로 살펴보자면
- ML application inference code의 Unit test, Integration Test 시에 GPU 사용을 해야할 때 Github-hosted runner에서는 이러한 컴퓨팅 기능을 제공해주지 않는다.
- 보안 이슈가 있는 데이터를 사용해서 해당 Workflow가 실행 되어야 하는 경우 해당 데이터를 Github에 업로드할 수 없음으로 인해 Github-hosted runner에서는 실행할 수 없다.
이런 한계를 해결할 수 있느 방법 중 하나가 바로 self-hosted runners를 사용하는 것이다
A Self-hosted runners is runners that you manage, but are accessible to your CI/CD system for executing jobs.
그래서 자신의 서버가 있거나 아니면 AWS의 EC2 등의 서버를 Github Actions의 self-hosted runner로 추가하고,
위의 ML application 개발 과정처럼 GPU 사용이 필요한 경우, GPU가 탑재된 self-hosted runner에서 GPU가 사용될 수 있게 셋업 및 스크립트 작성하는 방법을 정리해보고자 한다.
Add self-hosted runners at various levels
You can add self-hosted runners at various levels in the management hierarchy:
- Repository-level runners are dedicated to a single repository.
- Organization-level runners can process jobs for multiple repositories in an organization.
- Enterprise-level runners can be assigned to multiple organizations in an enterprise account.
만약 자신의 github organization에서 관리하는 모든 repo에서 전반적으로 사용하고 싶다면 Organization-level runners로 추가하는 것이 편리하다. 하지만 이 과정은 organization owner가 발급한 token이 필요하므로 owner만 진행 가능하다.
아래의 과정은 모두 Repository-level로 runners를 추가하는 방법에 대해서 다룰 것이다.
Github actions Self hosted Runner 추가하기
방법 1) "Docker-in-Docker" container로 Github actions Self hosted Runner 추가하기
https://github.com/myoung34/docker-github-actions-runner 에서 제공하는 도커 이미지를 사용하여 추가할 수 있다.
이는 자신이 사용할 서버에 self-hosted runners를 docker container로 띄우고,
Github actions workflow를 수행할 것이 있으면 이를 실제 수행하는 container가 runner container 안에 떠서 github action script를 수행하는 방식으로 동작하는 self-hosted runner를 추가하는 방법이다.
① prerequisite
If you want to run workflows that use Docker container actions or service containers, you must use a Linux machine and Docker must be installed.
② Create RUNNER_TOKEN something called a personal access token
runner를 추가할 repo에 접근 권한을 확인하기 위해 github personal access token을 발급받아야 한다.
그리고 어떤 권한을 부여할건지 정하는 selecting box에서는
- repo (all)
- admin:org (all) (mandatory for organization-wide runner)
- admin:enterprise (all) (mandatory for enterprise-wide runner)
- admin:public_key - read:public_key
- admin:repo_hook - read:repo_hook
- admin:org_hook
- notifications
- workflow
이러한 것들을 추가해준다.
그리고 나서 Generate token을 누르면 token이 발급되고, 그걸 복사하여 사용하면 된다!
※ Organization-level runners을 추가하고 싶은 경우 Owner가 위의 방식.으로 발급한 token을 가지고 진행해야지만 추가가 가능하다.
③ Let's going to run a docker github actions runner docker container that listens for workflows from Github.
https://github.com/myoung34/docker-github-actions-runner#manual에 나와있는 명령어를 참고해서
docker run 명령어나 shell wrapper 또는 docker-compose 파일로 작성하여 실행시켜주면 된다.
# per repo
docker run -d --restart always --name github-runner \
-e REPO_URL="https://github.com/myoung34/repo" \ # 추가할 repo url
-e RUNNER_NAME="foo-runner" \ # runner name 지정
-e RUNNER_TOKEN="footoken" \ # 위에서 발급 받은 github PAT token
-e RUNNER_WORKDIR="/tmp/github-runner-your-repo" \ # The working directory for the runner. Runners on the same host should not share this directory. Default is '/_work'. 자세한 내용은 https://github.com/myoung34/docker-github-actions-runner#environment-variables 참고
-e RUNNER_GROUP="my-group" \ # Settings > Actions > Runners에 가면 Default가 기본으로 있습니다. Group설정을 따로 해주지 않고 넘어가면 디폴트로 Default group에 추가가 됩니다.
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/github-runner-your-repo:/tmp/github-runner-your-repo \
myoung34/github-runner:latest
여기까지 정상적으로 되었다면 runner을 추가한 repo에 Settings > Actions > Runners에 위의 RUNNER_NAME으로 runner가 추가되어 있는 것을 확인할 수 있다.
방법 2) Github.com에서 제공하는 방식에 따라 Github actions Self hosted Runner 추가하기
repository level runners를 추가할 경우에는 공식문서인 Adding a self-hosted runner to a repository 를 보고 그대로 진행하면 된다.
- On GitHub, navigate to the main page of the repository.
- Under your repository name, click Settings.
- In the left sidebar, click Actions.
- In the left sidebar, under "Actions", click Runners.
- Click New self-hosted runner.
- 여기까지 하면 아래와 같은 명령어들이 나온다. runner로 사용할 서버의 shell에서 아래의 명령어를 순서대로 입력한다.
- Configure 단계에서 명령어를 입력하면 몇 가지 설정을 할 수 있는 입력 칸이 뜬다. runner의 name을 정하는 것은 일반적인 것이고 생소한 것은 Group과 Label일 것이다.
- Group에 뭘 적어야 하나?? Settings > Actions > Runners에 가면 Default가 기본으로 있다. Group설정을 따로 해주지 않고 넘어가면 디폴트로 Default group에 추가가 된다. 다른 Group에 추가하고 싶다면 그 Group의 이름을 적어주면 된다.
- Label에는 뭘 적어야 하나?? 여러 runners가 있을 때 github actions script에서 어떤 runner를 사용하라고 Label을 통해 지정해 줄 수 있는데 그때 Label이 사용된다. 그래서 해당 runner의 구분자처럼 쓸 수 있도록 몇 가지를 , 를 기준으로 몇 개 적어주자! 자세한 내용은 Using the configuration script to create and assign labels을 참고 할 수 있다.
- Group에는 아무것도 적어주지 않아서 Default를 사용하고, Label에는 self-hosted, linux, x64,103이라고 설정하고 여기까지 정상적으로 되었다면 runner을 추가한 repo에 Settings > Actions > Runners에 아래처럼 runner가 추가되어 있는 것을 확인할 수 있다.
Github actions Self hosted Runner 사용하기
여기서는 앞서 <Github actions Self hosted Runner 추가하기>에서 두 번째 방법으로 GPU가 있는 자체 서버를 Self hosted Runner로 추가하였다고 가정하고,
이걸 사용하기 위한 workflow yml 스크립트 작성을 해 볼 것이다.
service 설치 (Installing the service)
먼저 처음 사용하기 전에 self-hosted runner service를 설치하고 상태 관리를 해야한다.
- Stop the self-hosted runner application if it is currently running.
- Install the service with the following command:
sudo ./svc.sh install
service 시작 (Starting the service)
sudo ./svc.sh start
Github Actions with own GPUs 사용하기 위해 필요한 것 설정
GPU가 있는 self-hosted runner 서버에서 GPU를 사용해야 하는 github actions workflow(ex. ML application inference code unittest.. 등)가 실행되어야 할 때 필요한 것이 있다.
github actions workflow는 docker가 뜨면서 실행된다. 그래서 docker와 docker container 내에서 NVIDIA GPU를 이용할 수 있도록 해주는 plug-in 또는 Wrapper인 nvidia-docker가 필요하다.
- https://github.com/NVIDIA/nvidia-docker
- https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#docker
이 두 자료를 보고 nvidia-docker까지 설치 완료 한 후
sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi
자신이 설치한 버전에 맞게 위의 명령어를 수정/입력했을 때 아래 사진과 같이 되었다면 이제 준비는 다 된 것이다.
workflow yml 스크립트 작성 (Using self-hosted runners in a workflow)
self-hosted runner을 사용해서 이 스크립트를 실행하고 싶다면 runs-on 값에 self-hosted 라고 적어주면 기본적으로 self-hosted runner가 실행된다.
runs-on은 앞서 설정해준 label을 기준으로 어떤 runner을 실행시킬 것인지 결정하게 되는데 기본적으로 모든 self hosted runner는 self-hosted label을 가지고 있기 때문이다.
jobs:
test:
runs-on: self-hosted
만약에 self-hosted로 등록된 runner가 여러 개 있고, 이를 구분해서 사용하고 싶다면 앞서 설정한 labels를 활용하면 된다.
예를 들어 GPU가 있는 서버를 runner로 추가 할 때 label로 'gpu'를 적어주었고, 이 runner가 필요하다면
jobs:
test:
runs-on: [self-hosted, gpu]
이런 식으로 해당 runner를 식별가능하게 해 준다.
실행 결과 slack alert 하기
어떤 workflow가 실행완료되는데 몇 분이 걸린다면 그게 진행되는 동안 다른 것을 하는 경우가 많을 것이다.
이런 경우 실행 결과를 slack으로 받아 볼 수 있다면 굉장히 편리해지고 신경 써야 할 것이 하나 줄어든다.
자세한 내용은 Building a CI/CD Pipeline with GitHub Actions and Docker (Part 1) 글에 <Feedback using Slack Notifications>에 잘 나와있다.
이 글에서는 simple-slack-notify@master 을 사용하고 있는데, rtCamp/action-slack-notify도 비슷한 기능을 제공하고 star도 훨씬 많다.
나 같은 경우는 성공/실패/취소했을 때 받는 메시지를 다 지정해주고 싶어서 simple-slack-notify@master 을 사용해서 script를 작성했다. 또 어떤 Branch에서 발생한 actions인지도 알고 싶어서 branch 이름을 먼저 extract 하는 step도 추가해주었다.
최종적으로
- name: Extract branch name
shell: bash
run: echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/} | tr / -)" >> $GITHUB_ENV
- name: Slack Notification
if: always()
uses: edge/simple-slack-notify@master
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
with:
username: 'Github Actions Runner'
status: ${{ job.status }}
success_text: 'Unittest completed successfully'
failure_text: 'Unittest failed'
cancelled_text: 'Unittest was cancelled'
fields: |
[{ "title": "Repository", "value": "${env.GITHUB_REPOSITORY}", "short": true },
{ "title": "Branch", "value": "${env.BRANCH}", "short": true },
{ "title": "Action URL", "value": "${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}"}]
이런 스크립트를 추가해 주었다.
실행
name: ML application unittest
on:
push:
branches: [test]
jobs:
test:
runs-on: [self-hosted, gpu]
steps:
- uses: actions/checkout@v2
- name: ML inference code Unittest
run: docker-compose -f docker-compose-unittest.yml up --exit-code-from [SERVICE] # see https://libertegrace.tistory.com/entry/211021-docker-compose-exit-code-from
- name: Extract branch name
shell: bash
run: echo "BRANCH=$(echo ${GITHUB_REF#refs/heads/} | tr / -)" >> $GITHUB_ENV
- name: Slack Notification
if: always()
uses: edge/simple-slack-notify@master
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
with:
username: 'Github Actions Runner'
status: ${{ job.status }}
success_text: 'Unittest completed successfully'
failure_text: 'Unittest failed'
cancelled_text: 'Unittest was cancelled'
fields: |
[{ "title": "Repository", "value": "${env.GITHUB_REPOSITORY}", "short": true },
{ "title": "Branch", "value": "${env.BRANCH}", "short": true },
{ "title": "Action URL", "value": "${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}"}]
최종적으로 이렇게 작성된 .github/workflows/unittest.yml이 있을 때
test branch로 push가 되면 self-hosted runner로 추가한 GPU self-hosted runner의 상태가 Active로 바뀌면서 잘 진행되는 것을 확인할 수 있다!
그리고 성공적으로 끝났다면 아래와 같은 슬랙 알람이 도착한다.
Reference
MLOps Tutorial #4: GitHub Actions with your own GPUs
CML self-hosted runners on demand with GPUs
GitHub Actions로 간단히 CI 서버 대신하기
Building a CI/CD Pipeline with GitHub Actions and Docker (Part 1)
'Dev > DevOps, Infra' 카테고리의 다른 글
[Kubernetes] 쿠버네티스 Intro. 필요성, 구성, 동작 방식 (0) | 2022.03.04 |
---|---|
[Docker, AWS] Docker log를 AWS Cloudwatch에서 보기 (0) | 2021.10.22 |
[AWS, Post-mortem] EC2 리소스 과다 사용으로 인한 인스턴스 상태 확인 실패 문제와 SQS dead-letter queues (0) | 2021.09.12 |
[Github, CI/CD] 특정 파일이나 폴더에 대해서만 Github Actions가 trigger되는 방법 (1) | 2021.08.09 |
[HTTP, AWS] 3xx 대 Status와 Lambda@Edge (0) | 2021.06.24 |