Episode ini membahas integrasi Docker ke pipeline: menjalankan job di dalam container untuk isolasi lingkungan build, lalu mengotomasi build dan push image multi-platform ke GHCR dengan layer caching agar pipeline tetap cepat.

Di episode 11 kita belajar mengemas logika pipeline agar tidak diduplikasi antar repository. Sekarang tiba giliran masalah klasik yang menjengkelkan semua tim: "works on my machine". Kode yang lolos test di laptop developer sering berulah begitu sampai di server produksi — beda versi Node.js, beda sistem operasi, beda dependensi sistem.
Episode 12 menjawabnya dengan Docker: mengunci lingkungan build di dalam container sehingga apa yang diuji di pipeline sama persis dengan apa yang berjalan di produksi. Kita akan belajar menjalankan job di dalam container, lalu mengotomasi build dan push image ke registry (GHCR) dengan workflow production-grade.
Secara default, job GitHub Actions berjalan di atas sistem operasi runner yang sudah diisi banyak perangkat lunak (yang juga terus berubah antar versi). Dengan key container, kalian bisa memerintahkan job berjalan di dalam container tertentu — lingkungan userland yang dijamin identik setiap kali:
jobs:
test:
runs-on: ubuntu-latest
container:
image: node:20-alpine
env:
NODE_ENV: test
options: --memory 512m --cpus 1
steps:
- uses: actions/checkout@v4
- run: node --version
- run: npm testRunner VM tetap menyediakan kernel dan Docker, tetapi seluruh langkah job berjalan di dalam image node:20-alpine — versi Node.js yang spesifik, tanpa toolchain yang tidak diinginkan. env di dalam container menyetel variabel lingkungan image, dan options meneruskan flag tambahan ke docker run (misalnya batas memori). Image alpine dipilih karena ringan, sehingga startup job cepat.
Note
Penting dibedakan: key container adalah untuk menjalankan job di dalam container, bukan untuk membangun image. Membangun dan mengirim image ke registry menggunakan rangkaian action Docker di bawah — keduanya sering tertukar oleh pemula.
Tujuan akhir bab ini: setiap push ke main menghasilkan image Docker baru, dikirim ke registry, siap dipakai deployment. Registry yang kita pakai adalah GHCR (ghcr.io), yang terintegrasi natural dengan GitHub — login-nya bahkan bisa memakai GITHUB_TOKEN, tanpa menyimpan password registry terpisah.
Tiga action kunci yang membentuk rantai build:
docker/login-action@v3 — autentikasi ke registry (GHCR atau Docker Hub).docker/setup-buildx-action@v3 — mengaktifkan BuildKit dengan dukungan multi-platform.docker/build-push-action@v6 — membangun dan (jika push: true) mengirim image.name: Build and Push Image
on:
push:
branches: [main]
tags: ["v*"]
permissions:
contents: read
packages: write
jobs:
docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/devvnull/app
tags: |
type=semver,pattern={{version}}
type=sha
- uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=maxMari kita bedah dari atas: permission packages: write adalah syarat untuk push ke GHCR — ini penerapan least privilege dari episode 9. Login memakai GITHUB_TOKEN sebagai password, sehingga tidak ada secret tambahan yang perlu dijaga. metadata-action menghasilkan tag image secara dinamis: tag versi semver saat ada git tag v*, dan tag hash commit untuk setiap push. platforms: linux/amd64,linux/arm64 membuat image berjalan baik di server x86 maupun prosesor ARM (misalnya Apple Silicon atau instance ARM di cloud). Terakhir, cache-from dan cache-to dengan backend type=gha menyambungkan layer caching — kita bahas sebentar lagi.
Tanpa cache, setiap build mengulang seluruh proses dari awal: mengunduh base image, menyalin kode, menginstal dependensi — meskipun hanya satu baris kode yang berubah. Docker menyimpan hasil antar-langkah dalam layer, dan layer yang tidak berubah bisa dipakai ulang dari build sebelumnya. Ini seperti save game bertahap: hanya bagian yang berubah yang diulang.
Backend type=gha menyimpan cache build di GitHub Actions cache, sehingga bertahan lintas run:
cache-from: type=gha
cache-to: type=gha,mode=maxmode=max memaksa cache menyimpan semua layer antar platform, bukan hanya layer terakhir. Tanpa itu, cache untuk linux/arm64 bisa hilang dan build multi-platform tidak mendapat manfaat penuh.
Warning
Cache type=gha memakai kuota penyimpanan cache yang sama dengan actions/cache (batas default 10 GB per repository). Image yang besar bisa cepat menghabiskan kuota ini — pantau halaman settings cache, dan pertimbangkan menulis cache hanya untuk branch default agar tidak mengonsumsi kuota untuk tiap branch fitur.
Tidak semua proyek memakai GHCR. Untuk Docker Hub, alurnya sama persis, hanya registry dan kredensialnya yang berbeda — password berupa personal access token, bukan password akun biasa:
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}Setelah login, image dikirim ke docker.io dengan tag seperti devvnull/app:latest dan devvnull/app:v1.2.3.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Lupa permission packages: write | Push ke GHCR ditolak dengan 403 | Tambahkan permissions: packages: write |
push: true tidak disetel | Image hanya ada di runner, hilang setelah job | Setel push: true pada build-push-action |
Cache tanpa mode=max | Cache multi-platform tidak optimal | Gunakan cache-to: type=gha,mode=max |
| Base image tidak di-pin | Build rusak karena tag base berubah | Pin digest image base di Dockerfile |
| Mengira container job untuk membangun image | Image tidak pernah dibuat | Pakai buildx + build-push-action |
Docker menutup celah antara lingkungan build dan lingkungan produksi:
container: image: node:20-alpine) mengunci lingkungan eksekusi test dan build.login-action@v3, setup-buildx-action@v3, build-push-action@v6 — mengotomasi build dan push ke GHCR.linux/amd64 dan linux/arm64 dari satu pipeline.type=gha membuat build berikutnya jauh lebih cepat.Di episode 13 berikutnya, kita masuk bab Continuous Deployment (CD): Deployment ke Server via SSH, Rsync, dan Ansible — mengirim aplikasi yang sudah dibangun dan dikemas ini ke server produksi secara otomatis, tanpa login manual ke VPS. Dari pipeline hingga produksi!