On iki kurulum

Kök olmadan kapsayıcı görüntüleri oluşturan bir CI çalıştırıcısı

Bir Ryzen örneğinde, Buildah ile rootsuz OCI görüntüleri oluşturan ve bunları aynı makinedeki özel bir kayıt defterine gönderen, kendi kendine barındırılan bir Actions çalıştırıcısı.

Bu neyi kurar

Kendi forge'nize bağlı, bir depoyu kontrol eden, daemon'sız ve root'suz bir konteyner imajı oluşturan ve sonucu aynı sunucuda çalışan özel bir kayıt defterine gönderen bir CI runner'ı. Hiçbir yerde ayrıcalıklı bir soket bağlı değildir ve boru hattında hiçbir şey uid sıfır olarak çalışmaz.

Çoğu kendi kendine barındırılan CI kurulumu, imaj oluşturmayı ana bilgisayar konteyner daemon'unu işe monte ederek çözer. Bu işe yarar ve her boru hattına, birisinin pull request açtığı da dahil, makinenin tam kontrolünü verir. Rootless modda Buildah, bunların hiçbiri olmadan aynı işi yapar ve özel çekirdeklerde daha yavaş değildir.

Başlamadan önce

  • Bir R-8. İmaj oluşturma, çoğu ekibin çalıştırdığı en tek çekirdek bağlamlı iştir ve Zen 5 sattığımız en hızlı çekirdek başınadır. Dört yüz gigabayt NVMe, pek çok katmanı tutar.
  • Zaten çalıştırdığınız, Actions protokolünü konuşan bir forge ve içinde bir runner kayıt token'ı oluşturma izni.
  • Hostname'ler: runner için ci.example.com ve kayıt defteri için registry.example.com.

1. Ayrıcalıksız bir kullanıcı ve namespace aralığı

apt update && apt install -y podman buildah skopeo fuse-overlayfs uidmap slirp4netns git nodejs nginx apache2-utils
useradd -m -s /bin/bash runner
echo "runner:200000:65536" >> /etc/subuid
echo "runner:200000:65536" >> /etc/subgid
loginctl enable-linger runner

Bu iki aralık, rootless konteynerleri mümkün kılan şeydir: runner hesabı altmış beş bin alt kimliğe sahiptir, bu nedenle bir konteynerde root olduğuna inanan bir süreç, dışarıda ayrıcalıksız bir kimliğe eşlenir. Lingering, kullanıcı oturumunu canlı tutar, böylece bu hesap altındaki systemd birimleri çıkış yapıldıktan sonra da hayatta kalır.

Node, çoğu yeniden kullanılabilir eylem JavaScript olduğu için kuruludur ve runner bu yapılandırmada onları ana bilgisayarda çalıştırır. Bunu checkout adımında keşfetmek yaygın bir on dakikalık sapma yapar.

2. NVMe üzerinde rootless depolama

sudo -u runner mkdir -p /home/runner/.config/containers
sudo -u runner tee /home/runner/.config/containers/storage.conf <<EOF
[storage]
driver = "overlay"
graphroot = "/home/runner/.local/share/containers/storage"

[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"
EOF
sudo -u runner podman info --format "{{.Store.GraphDriverName}} {{.Host.Security.Rootless}}"

Son komut overlay true yanıtını vermelidir. vfs sürücüsü yerine fuse-overlayfs eksik demektir ve vfs her derlemede her katmanı tam olarak kopyalar, bu da doksan saniyelik bir boru hattını altı dakikalık bir boru hattına çevirir.

3. Özel bir kayıt defteri

apt install -y docker-registry
htpasswd -c /etc/docker/registry/htpasswd ci
certbot certonly --standalone -d registry.example.com

Kayıt defterini loopback'e bağlayın ve dışarı bakan her şeyin sahibi, kimlik doğrulama dahil, nginx olsun. /etc/docker/registry/config.yml içinde:

version: 0.1
storage:
  filesystem:
    rootdirectory: /srv/registry
  delete:
    enabled: true
http:
  addr: 127.0.0.1:5000

Kayıt defterinin kendisi hiçbir kimlik bilgisi taşımaz çünkü proxy'den geçmemiş bir istek asla almaz. Bir şifrenin kontrol edileceği tek yer, aynı fikirde olmayabilecek iki yerden daha iyidir.

server {
  listen 443 ssl;
  server_name registry.example.com;
  ssl_certificate     /etc/letsencrypt/live/registry.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/registry.example.com/privkey.pem;
  client_max_body_size 0;
  chunked_transfer_encoding on;

  location /v2/ {
    auth_basic "restricted";
    auth_basic_user_file /etc/docker/registry/htpasswd;
    proxy_pass http://127.0.0.1:5000;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 900s;
  }
}

client_max_body_size 0 yükleme sınırını kaldırır. nginx varsayılanını yerinde bırakın ve bir megabaytın üzerindeki her katman, gönderimin yaklaşık üçte ikisinde bir 413 ile başarısız olur, bu unutulmaz bir öğleden sonradır.

systemctl enable --now docker-registry nginx

4. Runner

cd /usr/local/bin
wget -O act_runner https://code.forgejo.org/forgejo/runner/releases/download/v6.3.1/forgejo-runner-6.3.1-linux-amd64
chmod +x act_runner
sudo -u runner mkdir -p /home/runner/.runner-cfg
cd /home/runner/.runner-cfg && sudo -u runner /usr/local/bin/act_runner generate-config > config.yaml

Oluşturulan dosyayı, işler bir konteyner içinde değil ana bilgisayarda çalışacak şekilde düzenleyin; çünkü Buildah zaten izolasyonu sağlar ve bu ikisinin iç içe geçmesi karmaşıklıktan başka bir şey eklemez:

runner:
  capacity: 2
  timeout: 1h
  labels:
    - "debian-13:host"
host:
  workdir_parent: /home/runner/work
cache:
  enabled: true
  dir: /home/runner/cache

Sekiz çekirdekte iki kapasite bilinçlidir: derlemeler büyük ölçüde seridir ve her biri dört çekirdekli iki eşzamanlı iş, aynı önbellek için savaşan dört işten daha erken biter. Forge'unuzun oluşturduğu token ile kayıt olun:

cd /home/runner/.runner-cfg
sudo -u runner /usr/local/bin/act_runner register --no-interactive \
  --instance https://forge.example.com --token <registration token> \
  --name ci-ams --labels debian-13:host

Sonra, ayrıcalıksız hesap olarak çalışan bir birim:

[Unit]
Description=Actions runner
After=network-online.target

[Service]
User=runner
WorkingDirectory=/home/runner/.runner-cfg
ExecStart=/usr/local/bin/act_runner daemon --config /home/runner/.runner-cfg/config.yaml
Restart=always
Environment=HOME=/home/runner
Environment=XDG_RUNTIME_DIR=/run/user/3001
NoNewPrivileges=yes

[Install]
WantedBy=multi-user.target

runner hesabının gerçek uid'ini XDG_RUNTIME_DIR içine koyun; id -u runner onu yazdırır. Rootless podman'ın bu dizinin var olmasına ihtiyacı vardır, bunu da ilk adımdaki linger ayarı garanti eder.

systemctl daemon-reload && systemctl enable --now act-runner

5. Derleyen ve gönderen bir iş akışı

Depoda, .forgejo/workflows/image.yaml konumunda:

on:
  push:
    branches: [main]

jobs:
  image:
    runs-on: debian-13
    steps:
      - uses: actions/checkout@v4
      - name: Build
        run: |
          buildah bud --layers --format oci -t app:${{ github.sha }} .
      - name: Push
        run: |
          buildah login -u ci -p ${{ secrets.REGISTRY_PASSWORD }} registry.example.com
          buildah push app:${{ github.sha }} docker://registry.example.com/app:${{ github.sha }}
          buildah push app:${{ github.sha }} docker://registry.example.com/app:latest

--layers katman önbelleğe almayı etkinleştirir; bu, bağımlılıklarınızı her taahhütte yeniden oluşturmak ile onları değiştiklerinde yeniden oluşturmak arasındaki farktır.

Doğrulayın

Runner, birim başladıktan sonra saniyeler içinde forge'un runner listesinde çevrimiçi olarak görünmelidir. Ardından bir taahhüt gönderin ve işi makineden izleyin:

journalctl -fu act-runner

Bittiğinde, görüntünün yalnızca başarı raporu vermek yerine gerçekten geldiğini doğrulayın:

skopeo inspect --creds ci:<password> docker://registry.example.com/app:latest | head -20
skopeo list-tags --creds ci:<password> docker://registry.example.com/app

Özeti, katman listesini ve her iki etiketi de istersiniz. Şimdi çalıştığını kanıtlayın, elinizde varsa farklı bir makinede:

podman run --rm registry.example.com/app:latest --version

Son olarak, bu kurulumun dayandığı iddia. Bir iş çalışırken, süreçlerin sahibine bakın:

ps -eo user,pid,comm | grep -E "buildah|podman" | head
sudo -u runner podman info --format "{{.Host.Security.Rootless}}"

Her süreç runner'a aittir ve güvenlik kontrolü true yanıtını verir. Boru hattında root tutan hiçbir şey yoktur; bu, ele geçirilmiş bir derleme betiğinin kayıt defteri anahtarlarınızı ve hipervizörünüzü değil, ayrıcalıksız bir hesap ve bir namespace alacağı anlamına gelir.

Sonrasında

Kayıt defteri depolaması, eski etiketleri kaldıran bir şey olmadan sınırsız büyür; bu nedenle, inandığınız bir saklama kuralına sahip olduğunuzda haftalık bir zamanlayıcıda registry garbage-collect çalıştırın. Derlemeler testlerden çok darboğaz haline gelirse, karşılaştırma sayfası bir sonraki boyutun neye benzediğini gösterir; daha fazla çekirdek, çoğu insanın beklediğinden çok daha az yardımcı olur ve daha hızlı çekirdekler çok daha fazla yardımcı olur.

Hazır olduğunda

Bir şehir seç. Bir boyut seç. Kripto ile öde.

Kim olduğunuzla ilgili formlar yok, onay için insan bekleme yok, doğrulama için telefon görüşmesi yok. Ödeme onaylanır ve kimlik bilgileri gelen kutunuza düşer.