Tự deploy Github Action Self-hosted Runner dưới máy tính của bạn.
Mở đầu
Trong bài viết trước, chúng ta đã được giới thiệu về Github Actions, một công cụ mạnh mẽ để tự động hóa quy trình phát triển phần mềm, và cách sử dụng nó tiết kiệm hơn, bao gồm các ưu điểm của Github Actions như tích hợp mạnh mẽ, dễ dàng cấu hình và hỗ trợ đa nền tảng. Giải thích về chi phí sử dụng Github Actions, lợi ích của việc sử dụng Github self-hosted runner để tối ưu chi phí và hướng dẫn triển khai self-hosted runner trên Docker, Kubernetes và Azure VM.
Tiếp theo, chúng ta hãy thử deploy 1 cụm Runner ngay dưới máy để thực thi các workflows của Github Action trên Github nha.
Nhắc lại về Github action self-hosted Runner
Github self-hosted runner cho phép bạn tự quản lý và triển khai các runner cho Github Actions trên môi trường của riêng bạn. Thay vì sử dụng các runner được cung cấp sẵn bởi Github, bạn có thể tự thiết lập và quản lý các runner trên các máy chủ của bạn, trong môi trường mạng và hệ thống của bạn. Điều này sẽ giúp bạn tối ưu về chi phí khi Github Repository không đáp ứng các yêu cầu để sử dụng miễn phí, hoặc với các công việc đòi hỏi cần chạy trên các Large Runner (option không được miễn phí). Bằng cách sử dụng Github self-hosted runner, bạn có thể tránh chi phí mỗi phút chạy của các runner do Github cung cấp, bạn chỉ cần trả chi phí cho việc duy trì và vận hành máy chủ riêng của mình.
Bắt đầu thôi
Các công cụ cần thiết
Để triển khai một cụm Github Actions self-hosted runner có khả năng tự động mở rộng, bạn cần sử dụng bốn công cụ chính: Docker, Kind (Kubernetes in Docker), Helm và Actions Runner Controller.
Docker là nền tảng ảo hóa ứng dụng, cho phép đóng gói và chạy các ứng dụng trong các container cô lập. Sử dụng Docker, bạn có thể tạo ra các runner nhẹ, dễ dàng triển khai và quản lý. Docker giúp tăng tính di động và nhất quán trong việc triển khai self-hosted runner.
Kind là công cụ giúp chạy Kubernetes cục bộ bằng cách sử dụng các container Docker. Với Kind, bạn có thể thiết lập một cụm Kubernetes nhỏ gọn để quản lý các self-hosted runner. Kind cung cấp môi trường Kubernetes đầy đủ, cho phép bạn tận dụng các tính năng mở rộng tự động và quản lý tài nguyên của Kubernetes mà không cần triển khai một cụm Kubernetes đầy đủ.
Helm là một công cụ quản lý gói cho Kubernetes, giúp đơn giản hóa việc triển khai và quản lý các ứng dụng Kubernetes. Bằng cách sử dụng Helm, bạn có thể dễ dàng cài đặt và cấu hình các self-hosted runner, đảm bảo quá trình triển khai nhanh chóng và chính xác. Helm charts cung cấp cấu trúc có thể tái sử dụng, giúp quản lý các bản phát hành ứng dụng dễ dàng hơn.
Actions Runner Controller (ARC) là công cụ quản lý tự động các runner cho Github Actions trên Kubernetes. ARC giám sát và tự động tạo, mở rộng hoặc thu hẹp số lượng runner dựa trên tải công việc hiện tại. Khi kết hợp với Kind và Helm, ARC cho phép triển khai các runner tự động mở rộng dựa trên nhu cầu, giúp tối ưu hóa chi phí và tài nguyên sử dụng.
Bằng cách kết hợp Docker, Kind, Helm và Actions Runner Controller, bạn có thể thiết lập một hệ thống self-hosted runner linh hoạt và hiệu quả. Docker cung cấp nền tảng cơ bản cho các container, Kind mang lại môi trường Kubernetes nhẹ nhàng, Helm giúp quản lý triển khai và ARC giúp quản lý và tự động mở rộng các runner dựa trên tải công việc, đảm bảo hệ thống luôn hoạt động mượt mà và tiết kiệm chi phí.
Việc cài đặt Docker, Kind, Helm, mọi người có thể tham khảo hướng dẫn dưới dây:
Chuẩn bị cụm Kubernetess dưới máy tính
Bởi vì chúng ta sử dụng Actions Runner Controller (ARC), công cụ quản lý tự động các runner cho Github Actions trên Kubernetes vì vậy ta cần chuẩn bị 1 cụm K8s trước. Bạn có thể tham khảo cấu hình dưới dây để tạo ra cụm K8s bằng Kind
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
Sau đó thực thi command này và chúng ta đã có 1 cụm K8s gồm 3 node: 1 Control Plane và 2 Worker Node
$ kind create cluster --config ~/workspace/k8s-demo/kind-config.yaml
$ kubectl get node
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane 14d v1.27.3
kind-worker Ready <none> 14d v1.27.3
kind-worker2 Ready <none> 14d v1.27.3
Triển khai Actions Runner Controller lên cụm
Actions Runner Controller (ARC) là một Kubernetes operator được thiết kế để quản lý và mở rộng tự động các runner tự host cho GitHub Actions. Bằng cách sử dụng Kubernetes, ARC cho phép điều phối các runner này một cách hiệu quả, đảm bảo khả năng mở rộng linh hoạt dựa trên nhu cầu của các workflow. Khả năng mở rộng này rất quan trọng để duy trì hiệu suất và tối ưu hóa việc sử dụng tài nguyên khi nhu cầu chạy workflow biến động.
ARC hoạt động bằng cách triển khai một tập hợp các tài nguyên Kubernetes, bao gồm các pod chứa các runner tự host. Nó sử dụng Helm charts để cài đặt, giúp quá trình triển khai trở nên đơn giản và dễ quản lý. Một trong những tính năng chính của ARC là khả năng tạo các tài nguyên AutoScalingRunnerSet, cho phép hệ thống tự động điều chỉnh số lượng runner dựa trên yêu cầu công việc hiện tại. Điều này được thực hiện thông qua việc giám sát liên tục và giao tiếp với dịch vụ GitHub Actions, đảm bảo rằng các runner mới được khởi tạo hoặc dừng lại một cách hiệu quả khi cần thiết.
Các thành phần điều khiển của ARC tương tác với các API của GitHub để quản lý các nhóm runner và các bộ scaleset. Khi một workflow được kích hoạt, các pod lắng nghe của ARC nhận thông báo và quyết định xem có cần tăng số lượng runner hay không. Các runner này sau đó được đăng ký với GitHub Actions, sẵn sàng thực thi các công việc được giao. Sau khi công việc hoàn thành, runner có thể bị tắt, giúp duy trì hoạt động gọn nhẹ và tiết kiệm chi phí.
Bằng cách sử dụng ARC, các tổ chức có thể tận dụng khả năng điều phối mạnh mẽ của Kubernetes để quản lý các runner của GitHub Actions, đảm bảo họ có một pipeline CI/CD có thể mở rộng, linh hoạt và hiệu quả.
Chi tiết hơn về ARC, bạn có thể đọc qua bài viết trên Github: https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/quickstart-for-actions-runner-controller
Bạn tạo values file dùng cho Helm chart của ARC như dưới đây:
# Default values for gha-runner-scale-set-controller.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
labels: {}
# leaderElection will be enabled when replicaCount>1,
# So, only one replica will in charge of reconciliation at a given time
# leaderElectionId will be set to {{ define gha-runner-scale-set-controller.fullname }}.
replicaCount: 1
image:
repository: "ghcr.io/actions/gha-runner-scale-set-controller"
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
env:
## Define environment variables for the controller pod
# - name: "ENV_VAR_NAME_1"
# value: "ENV_VAR_VALUE_1"
# - name: "ENV_VAR_NAME_2"
# valueFrom:
# secretKeyRef:
# key: ENV_VAR_NAME_2
# name: secret-name
# optional: true
serviceAccount:
# Specifies whether a service account should be created for running the controller pod
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
# You can not use the default service account for this.
name: ""
podAnnotations: {}
podLabels: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
resources: {}
## We usually recommend not to specify default resources and to leave this as a conscious
## choice for the user. This also increases chances charts run on environments with little
## resources, such as Minikube. If you do want to specify resources, uncomment the following
## lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
#
#affinity:
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: node-restriction.kubernetes.io/dedicated
# operator: In
# values:
# - runner
#tolerations:
# - effect: NoSchedule
# key: dedicated
# value: runner
# Leverage a PriorityClass to ensure your pods survive resource shortages
# ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/
# PriorityClass: system-cluster-critical
priorityClassName: ""
## If `metrics:` object is not provided, or commented out, the following flags
## will be applied the controller-manager and listener pods with empty values:
## `--metrics-addr`, `--listener-metrics-addr`, `--listener-metrics-endpoint`.
## This will disable metrics.
##
## To enable metrics, uncomment the following lines.
metrics:
controllerManagerAddr: ":8080"
listenerAddr: ":8080"
listenerEndpoint: "/metrics"
flags:
## Log level can be set here with one of the following values: "debug", "info", "warn", "error".
## Defaults to "debug".
logLevel: "debug"
## Log format can be set with one of the following values: "text", "json"
## Defaults to "text"
logFormat: "text"
## Restricts the controller to only watch resources in the desired namespace.
## Defaults to watch all namespaces when unset.
# watchSingleNamespace: ""
## Defines how the controller should handle upgrades while having running jobs.
##
## The strategies available are:
## - "immediate": (default) The controller will immediately apply the change causing the
## recreation of the listener and ephemeral runner set. This can lead to an
## overprovisioning of runners, if there are pending / running jobs. This should not
## be a problem at a small scale, but it could lead to a significant increase of
## resources if you have a lot of jobs running concurrently.
##
## - "eventual": The controller will remove the listener and ephemeral runner set
## immediately, but will not recreate them (to apply changes) until all
## pending / running jobs have completed.
## This can lead to a longer time to apply the change but it will ensure
## that you don't have any overprovisioning of runners.
updateStrategy: "immediate"
Tiếp theo, chúng ta dùng Helm để tạo release cho ARC
$ helm upgrade --install -n arc-systems --create-namespace arc oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller -f gha-arc.values.yaml
Release "arc" does not exist. Installing it now.
Pulled: ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller:0.9.3
Digest: sha256:4fda46fd8c4e08fe2e3d47340573f98d7a9eeb4c3a474b0e2b08057ab24d50a9
NAME: arc
LAST DEPLOYED: Thu Jun 27 14:31:58 2024
NAMESPACE: arc-systems
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing gha-runner-scale-set-controller.
Your release is named arc.
WARNING: Older version of the listener (githubrunnerscalesetlistener) is deprecated and will be removed in the future gha-runner-scale-set-0.10.0 release. If you are using environment variable override to force the old listener, please remove the environment variable and use the new listener (ghalistener) instead.
# Kiểm tra ARC đã deploy thành công chưa
$ kubectl get pod -n arc-systems
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
arc-gha-rs-controller-7cfdcddcb5-ncnv4 1/1 Running 0 80s 10.244.2.2 kind-worker <none> <none>
Như vậy là chúng ta đã deploy thành công ARC, công cụ quản lý Runner.
Triển khai Self-hosted Runner thông qua Actions Runner Scale Set
Action Runner Scale Set là một tính năng trong Actions Runner Controller (ARC) giúp quản lý và mở rộng các self-hosted runners một cách tự động và linh hoạt. Điều này rất quan trọng đối với các dự án lớn hoặc có tải công việc không đều đặn, vì nó cho phép tăng số lượng runners khi cần thiết và giảm khi không cần thiết, giúp tối ưu hóa tài nguyên và chi phí.
Scale set hoạt động bằng cách theo dõi các yêu cầu công việc (jobs) từ GitHub Actions và tự động điều chỉnh số lượng runners tương ứng. Khi có nhiều công việc chờ xử lý, hệ thống sẽ tạo thêm các runners mới để xử lý. Ngược lại, khi số lượng công việc giảm, các runners dư thừa sẽ tự động bị xóa bỏ. Quá trình này được điều khiển bởi các quy tắc và cấu hình mà bạn thiết lập, cho phép tùy chỉnh chi tiết cách mở rộng và thu hẹp.
Để triển khai Runner Scale Set, bạn cần cài đặt và cấu hình ARC trên một cluster Kubernetes. Điều này bao gồm việc sử dụng Helm để cài đặt các biểu đồ cần thiết và cấu hình các tham số liên quan đến việc mở rộng tự động. Một khi đã được thiết lập, ARC sẽ quản lý việc mở rộng và thu hẹp các runners dựa trên nhu cầu thực tế từ các workflow của bạn trên GitHub.
Sử dụng Action Runner Scale Set giúp đảm bảo rằng hệ thống của bạn luôn có đủ tài nguyên để xử lý các công việc CI/CD mà không phải lo lắng về việc quản lý thủ công các runners, từ đó giúp tăng hiệu quả và giảm chi phí vận hành.
Bạn tạo values file dùng cho Helm chart của Actions Runner Scale Set như dưới đây:
## githubConfigUrl is the GitHub url for where you want to configure runners
## ex: https://github.com/myorg/myrepo or https://github.com/myorg
githubConfigUrl: https://github.com/<org-name>/gha-test
## githubConfigSecret is the k8s secrets to use when auth with GitHub API.
## You can choose to use GitHub App or a PAT token
githubConfigSecret:
### GitHub PAT Configuration
github_token: ghp_xxx
## maxRunners is the max number of runners the autoscaling runner set will scale up to.
maxRunners: 5
## minRunners is the min number of idle runners. The target number of runners created will be
## calculated as a sum of minRunners and the number of jobs assigned to the scale set.
minRunners: 1
#runnerGroup: "RnDArc"
## name of the runner scale set to create. Defaults to the helm release name
# runnerScaleSetName: ""
template:
spec:
dnsConfig:
searches: []
initContainers:
- name: init-dind-externals
image: ghcr.io/actions/actions-runner:latest
command: ["cp", "-r", "-v", "/home/runner/externals/.", "/home/runner/tmpDir/"]
volumeMounts:
- name: dind-externals
mountPath: /home/runner/tmpDir
containers:
- name: runner
image: ghcr.io/actions/actions-runner:latest
command:
- /home/runner/run.sh
env:
- name: DOCKER_HOST
value: unix:///run/docker/docker.sock
volumeMounts:
- name: work
mountPath: /home/runner/_work
- name: dind-sock
mountPath: /run/docker
readOnly: true
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: 2
memory: 4Gi
- name: dind
image: docker:24.0.9-dind
args:
- sh
- -c
- |-
sed -E "/search/d" /etc/resolv.conf > /tmp/resolv.conf
cat /tmp/resolv.conf > /etc/resolv.conf
dockerd --host=unix:///run/docker/docker.sock --group=$(DOCKER_GROUP_GID)
env:
- name: DOCKER_GROUP_GID
value: "123"
# - name: DOCKER_DRIVER
# value: overlay2
securityContext:
privileged: true
volumeMounts:
- name: work
mountPath: /home/runner/_work
- name: dind-sock
mountPath: /run/docker
- name: dind-externals
mountPath: /home/runner/externals
volumes:
- name: work
emptyDir: {}
- name: dind-sock
emptyDir: {}
- name: dind-externals
emptyDir: {}
controllerServiceAccount:
namespace: arc-systems
name: arc-gha-rs-controller
Trong file values trên có các trường bạn cần lưu ý:
githubConfigUrl
: là url của repository của bạn trên GithubgithubConfigSecret.github_token
: là Github Personal accesss token, bạn có thể tạo ở đây https://github.com/settings/tokensmaxRunners
: Số runner tối đa có thể chạy đồng thờiminRunners
: Số runner tối thiểu, có thể set về 0 thì chỉ khi trên Github có job thì ARC mới tạo ra runner
Sau đó bạn thực thi command để deploy chúng lên cụm:
$ helm upgrade --install -n arc-runners --create-namespace arc-runners oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set --version=0.9.3 -f arc-scaleset.values.yaml
Release "arc-runners" does not exist. Installing it now.
Pulled: ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set:0.9.3
Digest: sha256:ec6acc8503fe1140e7371656b2142c42be67741d93222de7af63e167f825e531
NAME: arc-runners
LAST DEPLOYED: Thu Jun 27 14:46:19 2024
NAMESPACE: arc-runners
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing gha-runner-scale-set.
Your release is named arc-runners.
Khi đã deploy xong Scale set, ở namespace arc-systems
sẽ xuất hiện 1 listener chuyên lắng nghe các jobs ở trên Github, và ở namespace arc-runners
sẽ xuất hiện 1 runner(như trong cấu hình minRunners)
$ kubectl get pod -n arc-systems
NAME READY STATUS RESTARTS AGE
arc-gha-rs-controller-7cfdcddcb5-ncnv4 1/1 Running 0 16m
arc-runners-754b578d-listener 1/1 Running 0 109s
$ kubectl get pod -n arc-runners
NAME READY STATUS RESTARTS AGE
arc-runners-9q4mg-runner-npjhz 0/2 Init:0/1 0 78s
Như vậy là chúng ta đã hoàn thiện việc triển khai Github Action Self-hosted Runner phía dưới máy chúng ta rồi.
Kiểm tra thành quả trên Github
Bây giờ chúng ta cùng kiểm tra kết quả ở trên Github, chúng ta cấu hình cho repo ở link https://github.com/<org-name>/gha-test/settings/actions/runners
sẽ thấy Runner scale sets vừa deploy ở trên
Bây giờ chúng ta cùng test thử 1 workflows chạy trên self-hosted Runner này:
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
# Triggers the workflow on push or pull request events but only for the $default-branch branch
push:
paths: .github/workflows/blank.yml
pull_request:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
self_hosted:
# The type of runner that the job will run on
runs-on: arc-runners
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
# Check container image
- name: Hello from Self-hosted Runner
run: echo "Hello from Self-hosted Runner"
Sau khi push code lên Github Repository, chúng ta cùng check Actions nhá.
Logs cho thấy job này đang chạy trên Runner trên máy local của chúng ta thay vì Github Runner
Current runner version: '2.317.0'
Runner name: 'arc-runners-9q4mg-runner-npjhz'
Runner group name: 'Default'
Machine name: 'arc-runners-9q4mg-runner-npjhz'
Kết bài
Như vậy, chúng ta đã cùng nhau khám phá và thực hiện các bước triển khai Github Actions self-hosted runner trên máy tính cá nhân, từ việc thiết lập Docker, Kind, Helm cho đến triển khai Actions Runner Controller và Actions Runner Scale Set. Việc sử dụng các self-hosted runner không chỉ giúp tối ưu hóa chi phí mà còn mang lại sự linh hoạt và kiểm soát tốt hơn cho các quy trình CI/CD của bạn. Bằng cách tận dụng sức mạnh của Kubernetes và các công cụ quản lý như ARC, chúng ta có thể đảm bảo hệ thống luôn hoạt động hiệu quả, đáp ứng được các nhu cầu công việc biến động và tối ưu hóa tài nguyên sử dụng. Hãy tiếp tục theo dõi và tối ưu hóa cấu hình của bạn để đạt được hiệu quả cao nhất trong các dự án phần mềm.
All rights reserved