Đăng vào

Thiết lập local cache cho GitHub ARC self-hosted runner

Giới thiệu

Trong các bài viết trước, mình đã giới thiệu về cách cài đặt và thiết lập GitHub self-hosted trên Kubernetes để cho phép chúng ta có thẩ thực hiện được các GitHub workflow trên cơ sỏ hạ tầng (Infrastructure) sẵn có.

Tuy nhiên, khi sử dụng self-hosted runner thì chúng ta cần cấu hình lại để cho phép tính năng cache có thể hoạt động được

Cache trong trường hợp ở đây hiểu nôm na là bộ nhớ đệm, chúng ta sẽ lưu lại những file thường được sử dụng để hệ thống có thể truy xuất nhanh, không phải download lại từ bên ngoài, lợi ích là tiết kiệm băng thông, giảm thời gian chờ ...

Thiết lập HostedToolCache PVC

Tùy chỉnh lại Runner image

HostedToolCache trong GitHub runner là một thư mục chứa các công cụ được cài đặt sẵn trên các GitHub-hosted runners. Nó giúp tăng tốc độ thực thi các workflow bằng cách cung cấp sẵn các phiên bản của các công cụ phổ biến như Node.js, Python, .NET,... và nhiều công cụ khác. Mục đích của nó là giảm thời gian cài đặt công cụ trong các workflow CI/CD.

Đối với hosted runner (các runner của GitHub cung cấp) thì nó đã được thiết lập sẵn. Sau đây, mình sẽ hướng dẫn thiết lập HostedToolCache trên GitHub ARC.

Đầu tiên, trong phần Dockerfile của runner cần định nghĩa biến RUNNER_TOOL_CACHE cũng như khởi tạo thư mục để lưu dữ liệu

...
ENV RUNNER_TOOL_CACHE=/opt/hostedtoolcache
RUN mkdir /opt/hostedtoolcache \
    && chgrp docker /opt/hostedtoolcache \
    && chmod g+rwx /opt/hostedtoolcache
...

Dockerfile hoàn chỉnh có thể tham khảo tại đây: gha-runner-dind/Dockerfile

Cài đặt runner cùng với HostedToolCache PVC

Đầu tiên, khởi tạo 1 PVC để các runner có thể mount vào

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: hostedtoolcache-pvc
  namespace: arc-runners # Phải cùng namspece runner của bạn
spec:
  storageClassName: 'nfs-csi' # Đổi lại cho phù hợp với cluster của bạn
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 128Gi 

Tiếp theo, tạo file custom-template.yaml để cấu hình lại pod template của runner:

custom-template.yaml
template:
  spec:
    containers:
      - name: runner
        image: ghcr.io/nvtienanh/gha-runner-dind:2.323.0 # Có thể tự bỏ runner image của bạn vào đây
        securityContext:
          privileged: true
        volumeMounts:
          - name: hostedtoolcache
            mountPath: /opt/hostedtoolcache
    volumes:
      - name: hostedtoolcache
        persistentVolumeClaim:
          claimName: hostedtoolcache-pvc

Cuối cùng, dùng helm để deploy runner như hướng dẫn từ GitHub configuring a runner scale set Điểm khác biệt duy nhất là chúng ta thêm phần custom-template.yaml để tùy chỉnh lại pod template để hỗ trợ dind và hostedtoolcache

INSTALLATION_NAME="dind-runner"
NAMESPACE="arc-runners"
GITHUB_CONFIG_URL="https://github.com/<your_enterprise/org/repo>"
GITHUB_PAT="<PAT>"
helm install "${INSTALLATION_NAME}" \
    --namespace "${NAMESPACE}" \
    --create-namespace \
    --set githubConfigUrl="${GITHUB_CONFIG_URL}" \
    --set githubConfigSecret.github_token="${GITHUB_PAT}" \
    -f custom-template.yaml \
    oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set

Kiểm tra

actions/setup-python@v5

Mình sẽ sử dụng actions/setup-python@v5 để chạy thử một Workflow đơn giản của Python trên GitHub Actions

name: Python GitHub Action
on:
  push:
    branches:
      - main

jobs:
  python:
    runs-on: dind-runner
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.9'
      - run: python -V

Ở lần chạy đầu tiên, thì python được download và lưu vào hostedtoolcache folder lúc này dĩ nhiên sẽ tốn một khoảng thời gian chờ download, bước này tốn tầm khoảng 3m 21s

Ở các lần tiếp theo, do python package đã được lưu lại trong hostedtoolcache nen không cần download lại, thời gian cho việc setup python chỉ còn 16s

GitHub ARC Self-hosted runner local cache

Ở phía trên chúng ta đã thiết lập hostedtoolcache để cache các tệp thực thi (binary packages) của các công cụ phổ biến. Tiếp theo mình sẽ hướng dẫn cache các thư viện (dependecies cache) của các công cụ trên. Chẳng hạn như khi bạn lập trình Python thì thường cần download các thư viện bằng pip install ..., hay nodejs thì sẽ là npm install ..., với C/C++ thì conan install ... Việc download đó cũng sẽ tốn khá nhiều thời gian, nên việc cache là rất quan trọng trong các dự án lớn.

Mặc định thì GitHub Action đã hỗ trợ với actions/cache@v4, tuy nhiên dữ liệu sẽ được lưu trữ trên máy chủ của GitHub, nếu chúng ta sử dụng self-hosted runner, thì việc download cache từ máy chủ GitHub về chẳng khác nào download trực tiếp các thư viện, vì vậy chúng ta phải tìm cách để lưu trữ cache trên cùng với network nơi self-hosted runner đang chạy có thể dễ dàng truy xuất với tốc độ cao.

Sau một thời gian tìm hiểu, mình thấy có một action hỗ trợ việc này maxnowack/local-cache@v2 action này sẽ tạo file tar lưu các dependeciese tại thư mục /opt/hostedtoolcache/$GITHUB_REPOSITORY. GITHUB_REPOSITORY sẽ có dạng (org/repo) nên đảm bảo mỗi git repository sẽ có một folder cache riêng, đảm bảo tính bảo mật, tránh bị xung đột.

Dưới đây là ví dụ mình sử dụng để cache conan packages trên GitHub Actions

name: Conan2 caching
on:
  push:
    branches:
      - main

jobs:
  conan:
    runs-on: dind-runner
    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Install build-essential
        run: |
          sudo apt-get update
          sudo apt-get install -y build-essential

      - name: Setup python
        uses: actions/setup-python@v5
        with:
          python-version: '3.9'
          cache: 'pip'
        
      - name: Install conan
        run: pip install conan
      
      - name: Caching conan packages
        uses: maxnowack/local-cache@v2
        id: conan-cache
        with:
          path: /home/runner/.conan2/p
          key: ${{ runner.os }}-${{ hashFiles('**/conanfile.py') }}

      - name: Conan install
        run: |
          conan profile detect
          conan install .
          conan list "*"--format=compact

Đây là conanfile.py đơn giản mà mình dùng:

from conan import ConanFile
from conan.tools.cmake import cmake_layout


class ProjectRecipe(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "CMakeToolchain", "CMakeDeps"

    def requirements(self):
        self.requires("zlib/1.3.1")
        self.requires("spdlog/1.14.1")

    def layout(self):
        cmake_layout(self)

Ở lần chạy đầu tiên,

Ở các lần chạy tiếp theo, chúng ta có thể thấy các package không cần download lại vì đã được khôi phục từ cache.

Hy vọng bài viết này sẽ hữu ích Chúc thành công,

ANH NGUYỄN