Skip to content

Commit 6d9c0ce

Browse files
committed
add 0703
1 parent 8cc1e4e commit 6d9c0ce

File tree

5 files changed

+560
-0
lines changed

5 files changed

+560
-0
lines changed
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
---
2+
layout: post
3+
title: "Actions Runner Controller 구축하기"
4+
subtitle: "MLOps CI 환경 구축하기"
5+
feature-img: "assets/img/2025-07-03/0.webp"
6+
tags: [MLOps, Infra]
7+
---
8+
9+
### Intro
10+
11+
최근 AI를 활용한 개발을 즐겨하면서, 테스트 환경의 중요성을 더욱 절감하고 있습니다.
12+
13+
가장 대표적인 방법으로는 GitHub Actions을 이용한 CI 구축이 있겠지만, MLOps에서는 CI를 위해 고사양의 인스턴스가 필요한 경우가 꽤 많습니다.
14+
15+
물론 GitHub Actions에서 제공하는 [GPU 인스턴스(Linux 4 코어)](https://docs.github.com/ko/billing/managing-billing-for-your-products/about-billing-for-github-actions)도 존재하지만, 현재 기준으로 분당 $0.07라는 매우 비싼 금액으로 설정되어 있어 사용하기 부담스럽습니다.
16+
17+
GPU의 종류도 Nvidia T4 GPU로, 모델의 크기가 점점 커지는 상황에서 성능상 제약이 있습니다.
18+
19+
이런 상황에서 대안으로 Self-hosted runner가 존재합니다.
20+
21+
말 그대로, 직접 Runner를 설정하고 해당 Runner에서 GitHub workflow를 실행시키는 방법입니다.
22+
23+
해당 방법은 아래와 같이, GitHub에서 제공해주는 [자체 호스트형 실행기 추가](https://docs.github.com/ko/actions/how-tos/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners)를 통해 설정할 수 있습니다.
24+
25+
하지만, 해당 방법은 CI 머신을 항상 켜둬야 하는(online 상태) 문제점이 있습니다. CI/CD 작업이 드물게 일어나는 경우 비효율적일 수 있습니다.
26+
27+
이러한 배경에서 **Actions Runner Controller(ARC)**가 훌륭한 대안으로 떠오릅니다.
28+
29+
[Actions Runner Controller](https://github.com/actions/actions-runner-controller)는 GitHub Actions의 Runner를 Kubernetes 환경에서 동작할 수 있도록 제어해주는 오픈소스입니다.
30+
31+
이를 이용하면, GitHub Actions의 Workflow가 실행될 경우에만 본인이 운영하는 Kubernetes 리소스를 이용해 CI를 테스트할 수 있습니다.
32+
33+
34+
### Actions Runner Controller 설치하기
35+
36+
ARC 설치 과정은 크게 두 단계로 나뉩니다.
37+
1. GitHub과의 통신 및 인증을 위한 **GitHub Personal Access Token** 생성
38+
2. Helm을 이용한 **ARC 설치** 및 생성한 token을 통한 인증
39+
40+
#### 1. GitHub Personal Access Token 생성하기
41+
42+
ARC가 GitHub API와 상호작용하며 Runner를 등록하고 관리하려면 인증이 필요합니다. 이를 위해 GitHub Personal Access Token(PAT)을 생성합니다.
43+
44+
* **경로**: `Settings` > `Developer settings` > `Personal access tokens` > `Tokens (classic)` > `Generate new token`
45+
46+
Personal Access Token 생성 시 [적절한 권한](https://github.com/actions/actions-runner-controller/blob/master/docs/authenticating-to-the-github-api.md#deploying-using-pat-authentication)을 선택해야 합니다. ( 편의상 풀 권한 부여 )
47+
48+
> 보안을 위해서, 최소권한 및 키 만료기한 설정을 권장합니다.
49+
50+
Personal Access Token(PAT) 방식보다, Github App 방식으로 인증하는 것을 권장하는 것처럼 보입니다.
51+
52+
생성한 Personal Access Token은 다음 단계에서 ARC를 설치할 때 필요하므로 잘 보관해 둡니다.
53+
54+
#### 2. Helm으로 ARC 설치하기
55+
56+
57+
ARC를 설치하기 전, cert-manager가 필요합니다. cert-manager가 클러스터에 셋팅되어 있지 않으면 설치합니다.
58+
59+
```bash
60+
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.2/cert-manager.yaml
61+
```
62+
63+
64+
이제 Helm을 사용하여 쿠버네티스 클러스터에 ARC를 설치할 차례입니다.
65+
66+
67+
68+
69+
70+
71+
앞에서 생성한 Personal Access Token을 사용하여 ARC를 설치합니다. 아래 명령어에서 `YOUR_GITHUB_TOKEN` 값을 앞에서 생성한 PAT 값으로 변경해주세요.
72+
73+
74+
```bash
75+
helm repo add actions-runner-controller https://actions-runner-controller.github.io/actions-runner-controller
76+
77+
helm repo update
78+
79+
helm pull actions-runner-controller/actions-runner-controller
80+
81+
tar -zxvf actions-runner-controller-*.tgz
82+
83+
export GITHUB_TOKEN=YOUR_GITHUB_TOKEN
84+
85+
helm upgrade --install actions-runner-controller ./actions-runner-controller \
86+
--namespace actions-runner-system \
87+
--create-namespace \
88+
--set authSecret.create=true \
89+
--set authSecret.github_token="${GITHUB_TOKEN}"
90+
```
91+
92+
설치 완료 후, 다음 명령어로 ARC 컨트롤러가 정상적으로 실행되고 있는지 확인합니다:
93+
94+
```bash
95+
kubectl get pods -n actions-runner-system
96+
```
97+
98+
위 명령이 성공적으로 실행되면, `actions-runner-system` 네임스페이스에 ARC 컨트롤러 매니저 포드가 실행되는 것을 확인할 수 있습니다.
99+
100+
이제 ARC가 GitHub과 통신할 준비를 마쳤습니다! 다음 단계는 실제로 워크플로우를 실행할 Runner를 정의하는 것입니다.
101+
102+
### 3. Runner 설정하기
103+
104+
ARC 컨트롤러는 설치했지만, 아직 워크플로우를 실행할 Runner 자체는 없습니다. 이제 GitHub Actions 워크플로우 잡에 따라 Runner Pod를 생성해야 합니다.
105+
106+
이를 위해 두 가지 리소스를 사용합니다.
107+
1. `RunnerDeployment`: Runner 포드의 템플릿 역할을 합니다. 어떤 컨테이너 이미지를 사용하고, 어떤 GitHub 저장소에 연결되며, 어떤 레이블을 가질지 등을 정의합니다.
108+
2. `HorizontalRunnerAutoscaler` (HRA): `RunnerDeployment`를 관찰하면서, GitHub에 대기 중인 잡의 수에 따라 `RunnerDeployment`의 복제본(replicas) 수를 자동으로 조절합니다.
109+
110+
#### RunnerDeployment 정의
111+
112+
먼저, `runner-deployment.yml`이라는 이름으로 아래와 같이 파일을 작성합니다. `spec.template.spec.repository` 값을 자신의 GitHub 저장소 이름으로 변경해주세요.
113+
114+
> repository 뿐만 아니라, 권한이 있는 경우 organization으로도 지정할 수 있습니다.
115+
116+
```yaml
117+
apiVersion: actions.summerwind.dev/v1alpha1
118+
kind: RunnerDeployment
119+
metadata:
120+
name: example-runner-deployment
121+
namespace: actions-runner-system
122+
spec:
123+
replicas: 1
124+
template:
125+
spec:
126+
repository: <YOUR_NAME>/<YOUR_REPO_NAME>
127+
labels:
128+
- self-hosted
129+
- arc-runner
130+
```
131+
132+
다음과 같이 설정하면, Github Repo Actions self-hosted runner를 확인할 수 있습니다.
133+
134+
<img src="/assets/img/2025-07-03/1.png">
135+
136+
배포가 완료되면 잠시 후 GitHub 저장소의 **Settings > Actions > Runners** 탭에 `self-hosted`와 `arc-runner` 레이블을 가진 새로운 Runner가 등록된 것을 확인할 수 있습니다.
137+
138+
139+
#### HorizontalRunnerAutoscaler 정의
140+
141+
다음으로, 위에서 만든 `RunnerDeployment`를 자동으로 스케일링할 HRA를 정의합니다. `hra.yml` 파일을 작성합니다.
142+
143+
```yaml
144+
apiVersion: actions.summerwind.dev/v1alpha1
145+
kind: HorizontalRunnerAutoscaler
146+
metadata:
147+
name: example-hra
148+
namespace: actions-runner-system
149+
spec:
150+
scaleTargetRef:
151+
name: example-runner-deployment
152+
minReplicas: 0
153+
maxReplicas: 5
154+
```
155+
156+
minReplicas와 maxReplicas를 지정하여, 리소스에 따라 스케일업, 다운 할 수 있습니다.
157+
158+
혹은 추가적인 metrics를 지정하여 workflow trigger가 있을 때마다, pod를 생성하도록 구성할 수도 있습니다. 이 외에도 다양한 metrics가 존재합니다.
159+
160+
> HorizontalRunnerAutoscaler 를 구성했을 경우, 필요할 때만 Runner가 생성되는 구조로, 평시에는(runner가 0개일 경우) Github UI에서 Runner를 확인할 수 없습니다.
161+
162+
<img src="/assets/img/2025-07-03/2.png">
163+
164+
165+
166+
167+
```yaml
168+
apiVersion: actions.summerwind.dev/v1alpha1
169+
kind: HorizontalRunnerAutoscaler
170+
metadata:
171+
name: example-hra
172+
namespace: actions-runner-system
173+
spec:
174+
scaleTargetRef:
175+
name: example-runner-deployment
176+
minReplicas: 0
177+
maxReplicas: 5
178+
metrics:
179+
- type: TotalNumberOfQueuedAndInProgressWorkflowRuns
180+
repositoryNames: ["<YOUR_NAME>/<YOUR_REPO_NAME>"]
181+
182+
위는 제가 가장 선호하는 metric으로, workflow 실행이 필요할 때(Queue 상태 일 때) Scale up하는 메트릭입니다.
183+
이와 같이, 필요에 따라 metrics를 지정하여 좋은 결과를 만들 수 있습니다.
184+
185+
186+
### 4. GitHub Actions 워크플로우에서 사용하기
187+
188+
이제 모든 설정이 끝났습니다! 새로 만든 ARC Runner를 사용하는 것은 매우 간단합니다. 워크플로우 파일에서 `runs-on` 키에 `RunnerDeployment`에서 지정한 레이블을 넣어주기만 하면 됩니다.
189+
190+
저장소의 `.github/workflows/` 디렉토리에 아래와 같이 간단한 테스트 워크플로우(`test-arc.yml`)를 추가해봅시다.
191+
192+
```yaml
193+
name: ARC Runner Test
194+
195+
on:
196+
push:
197+
branches:
198+
- main
199+
200+
jobs:
201+
test-job:
202+
runs-on: [self-hosted, arc-runner]
203+
steps:
204+
- name: Checkout code
205+
uses: actions/checkout@v3
206+
207+
- name: Test
208+
run: |
209+
echo "Hello from an ARC runner!"
210+
echo "This runner is running inside a Kubernetes pod."
211+
sleep 10
212+
```
213+
214+
`runs-on: [self-hosted, arc-runner]` 부분이 핵심입니다. 이 워크플로우가 실행되면 GitHub은 `self-hosted`와 `arc-runner` 레이블을 모두 가진 Runner에게 잡을 할당합니다. ARC는 이 이벤트를 감지하고, HRA 설정에 따라 필요하다면 새로운 Runner 포드를 생성하여 잡을 처리합니다.
215+
216+
> self-hosted로 구성할 경우, 깃헙에서 기본으로 제공하는 runner와 달리 일부 패키지들의 설치를 workflow에서 진행해야할 수 있습니다.
217+
218+
219+
### 삽질 기록
220+
221+
CI/CD를 위해서 Docker를 자주 사용하는데, 자주 겪는 문제중 하나가 DinD(Docker in Docker) 문제입니다.
222+
223+
ARC의 경우, 기본적으로 runner라는 스케줄링 컨테이너와 docker라는 docker 데몬 컨테이너가 사이드카 구조로 같이 뜹니다.
224+
225+
이런 경우를 해결하기 위해, DinD를 지원하는 docker image가 존재합니다.
226+
227+
다음 yaml 파일같이, image 및 dockerdWithinRunnerContainer를 지정하면, runner 안에서 docker 데몬이 실행되고
228+
229+
workflow가 해당 runner에서 실행됩니다.
230+
231+
```yaml
232+
apiVersion: actions.summerwind.dev/v1alpha1
233+
kind: RunnerDeployment
234+
metadata:
235+
name: example-runner-deployment
236+
namespace: actions-runner-system
237+
spec:
238+
replicas: 1
239+
template:
240+
spec:
241+
repository: <YOUR_NAME>/<YOUR_REPO_NAME>
242+
labels:
243+
- self-hosted
244+
- arc-runner
245+
image: "summerwind/actions-runner-dind:latest"
246+
dockerdWithinRunnerContainer: true
247+
```
248+
249+
특히 GPU를 필요로 하는 docker 테스트의 경우 NVIDIA Container Toolkit이 설치된 cluster에서 위의 DinD 이미지를 사용하면
250+
GPU를 인식할 수 있습니다.
251+
252+
실행하고자 하는 workflow에서 아래와 같이 설정하면, DinD 상황에서도 GPU가 정상적으로 설정된 것을 확인할 수 있습니다.
253+
(NVIDIA Container Toolkit 및 NVIDIA GPU Driver Plugin 버전 확인은 필요합니다!)
254+
255+
```bash
256+
# GPU 디바이스 확인
257+
ls -la /dev/nvidia*
258+
259+
# device library setup
260+
smi_path=$(find / -name "nvidia-smi" 2>/dev/null | head -n 1)
261+
lib_path=$(find / -name "libnvidia-ml.so" 2>/dev/null | head -n 1)
262+
lib_dir=$(dirname "$lib_path")
263+
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(dirname "$lib_path")
264+
export NVIDIA_VISIBLE_DEVICES=all
265+
export NVIDIA_DRIVER_CAPABILITIES=compute,utility
266+
267+
# nvidia runtime 없이 직접 GPU 디바이스와 라이브러리 마운트
268+
docker run -it \
269+
--device=/dev/nvidia0:/dev/nvidia0 \
270+
--device=/dev/nvidiactl:/dev/nvidiactl \
271+
--device=/dev/nvidia-uvm:/dev/nvidia-uvm \
272+
--device=/dev/nvidia-uvm-tools:/dev/nvidia-uvm-tools \
273+
-v "$lib_dir:$lib_dir:ro" \
274+
-v "$(dirname $smi_path):$(dirname $smi_path):ro" \
275+
-e LD_LIBRARY_PATH="$LD_LIBRARY_PATH" \
276+
-e NVIDIA_VISIBLE_DEVICES="$NVIDIA_VISIBLE_DEVICES" \
277+
-e NVIDIA_DRIVER_CAPABILITIES="$NVIDIA_DRIVER_CAPABILITIES" \
278+
pytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime
279+
```
280+
281+
### 마무리하며
282+
283+
지금까지 쿠버네티스 환경에 Actions Runner Controller를 구축하여 동적으로 확장되는 Self-hosted runner 환경을 만드는 방법을 알아보았습니다.
284+
285+
ARC를 사용하면 GitHub에서 제공하는 Runner를 사용할 때의 비싼 비용 문제와, 직접 VM을 관리하며 Runner를 운영할 때의 비효율성을 모두 해결할 수 있습니다. 특히 GPU가 필요하거나, 복잡한 의존성을 가진 MLOps CI/CD 환경을 구축할 때 ARC는 매우 강력한 도구가 됩니다.
286+
287+
초기 설정 과정이 다소 복잡하게 느껴질 수 있지만, 한번 구축해두면 CI/CD 비용을 크게 절감하고 운영 부담을 덜어주므로 MLOps를 고민하고 있다면 꼭 한번 도입을 검토해보시길 바랍니다.
288+

0 commit comments

Comments
 (0)