Memakai GitHub Actions dan Github Packages Untuk Mengelola Image Docker
Salah satu tugas yang umum dilakukan oleh DevOps engineer adalah mengotomatisasikan proses building kode program yang dibuat oleh programmer. Misalnya, saat ada commit baru di branch master
, Continuous Integration (CI) platform seperti Jenkins akan bekerja menghasilkan artifact yang dibuat dari kode program terbaru (misalnya file Jar/War untuk aplikasi Java, Python modules untuk aplikasi Python, dan sebagainya). Khusus untuk aplikasi yang dijalankan di cluster Kubernetes, artifact yang dihasilkan adalah sebuah image Docker yang biasanya dibuat berdasarkan isi file Dockerfile
. Pada tulisan ini, saya akan mencoba menggunakan Github Actions sebagai CI platform untuk menghasilkan image Docker dan mempublikasikannya di GitHub Packages sehingga dapat saya pakai di cluster Kubernetes nantinya. Kedua fitur tersebut dapat dipakai tanpa biaya dengan batas eksekusi 2.000 menit untuk GitHub Actions dan penyimpanan 500 MB untuk Github Packages.
Pada latihan-k8s, saya memiliki dua service berbeda di folder stock-item-service
dan folder web
. Tujuan akhir yang ingin saya capai adalah setiap kali terdapat perubahan di branch main
di folder stock-item-service
, sebuah image Docker terbaru dengan nama JockiHendry/latihan-k8s-stock-item-service:edge
akan dipublikasikan di GitHub Container registry. Begitu juga perubahan di folder web
yang harus membuat image Docker baru dengan nama JockiHendry/latihan-k8s-web:edge
.
Saya akan mulai dengan folder web karena sudah terdapat sebuah Dockerfile di folder ini. Karena nantinya akan ada banyak service yang menggunakan Dockerfile, saya akan membuat sebuah workflow khusus untuk men-build dan mempublikasikan image berdasarkan Dockerfile. Workflow ini nantinya dapat dipanggil oleh workflow yang membutuhkannya. Sebagai contoh, saya membuat sebuah file baru dengan nama build-push.yaml
di folder .github/workflows
dengan isi seperti berikut ini:
Workflow ini membutuhkan masukan berupa registry
, imageName
, dan directory
. Nilai registry
harus berupa ghcr.io
bila image akan disimpan di Github Packages atau registry.hub.docker.com
bila disimpan di Docker Hub. Sebenarnya Docker Hub juga menyediakan paket tanpa biaya untuk public repository, bahkan tanpa batasan ukuran, hanya batas 200 operasi pull setiap 6 jam. Pada tulisan ini, saya akan menggunakan Github Packages supaya saya tidak perlu membuat akun baru lagi dan dapat menggunakan secrets.GITHUB_TOKEN
yang sudah disediakan oleh Github Actions.
Pada workflow di atas, saya menggunakan docker/metadata-action untuk menghasilkan tags
dan labels
yang dapat saya pakai untuk image yang dibuat. Ada beberapa jenis tags
yang dapat dihasilkan oleh action ini, misalnya berdasarkan nama branch dan tag. Jika ingin tag berdasarkan waktu eksekusi, saya dapat menggunakan nilai tags
seperti type=raw,value=main-{{date 'YYYYMMDDHHmm'}}
. Yang saya pakai di atas adalah type=edge
sehingga nantinya saya bisa men-pull image dengan menggunakan nama seperti JockiHendry/latihan-k8s-angular-web:edge
. Proses eksekusi building berdasarkan Dockerfile dan pushing ke Github Packages yang sesungguhnya baru terjadi saat memanggil docker/build-push-action di langkah terakhir.
Untuk memakai workflow di atas, saya membuat file baru dengan nama web.yaml
di folder yang sama dengan isi seperti berikut ini:
Pada key on
, saya menggunakan nilai push
dengan branches
dan paths
yang menunjukkan bahwa konfigurasi ini akan dikerjakan jika terdapat commit baru ke branch main
dan commit tersebut mengandung perubahan di folder web
. Selain itu, saya juga menambahkan workflow_dispatch
di on
sehingga workflow ini bisa dijalankan secara manual. Dengan workflow_dispatch
, akan muncul tombol Run workflow saat membuka tab Actions di halaman GitHub untuk mengerjakan workflow tersebut seperti yang terlihat pada gambar berikut ini:
Hal ini sangat berguna dalam pengujian workflow. Saat saya menemukan kesalahan di deklarasi YAML untuk workflow dan memperbaikinya, workflow tidak akan otomatis dikerjakan. Saya perlu melakukan perubahan di folder web dan membuat commit yang seharusnya tidak perlu ada untuk memicu eksekusi ulang bila seandainya tidak ada workflow_dispatch
.
Setelah workflow selesai dikerjakan, saya dapat mengakses image Docker yang dihasilkannya dari komputer lain dengan menggunakan perintah seperti:
Selain itu, bila saya membuka tab Packages di halaman profil GitHub saya, saya dapat men-klik image yang dihasilkan untuk mendapatkan informasi seperti yang terlihat pada gambar berikut ini:
Berikutnya saya akan membuat image Docker untuk folder stock-item-service. Ini adalah proyek Spring Boot yang tidak memiliki Dockerfile
. Sebagai gantinya, image Docker dihasilkan secara otomatis melalui task Gradle bernama bootBuildImage
. Saya perlu melakukan sedikit perubahan di file build.gradle supaya menyertakan informasi registry Docker Packages seperti yang terlihat pada:
Saya tidak ingin menyertakan informasi seperti password di file ini karena file ini dapat dilihat siapa saja. Oleh sebab itu, saya menggunakan variabel registryUsername
dan registryPassword
di deklarasi task Gradle di atas. Nilai kedua variabel ini nantinya dapat saya sertakan saat memanggil Gradle dengan menggunakan parameter -P
. Untuk itu, saya membuat file .github/workflows/stock-item-service.yaml
dengan isi yang terlihat seperti berikut ini:
Pada workflow di atas, saya menggunakan OpenJDK 17 dari Eclipse Temurin untuk melakukan proses building kode program Spring Boot tersebut. Pada langkah terakhir, saya memanggil ./gradlew bootBuildImage
untuk men-build dan sekaligus mempublikasikan image hasil build ke GitHub Packages. Disini saya menngisi variabel registryUsername
, registryPassword
, dan juga menentukan nama image secara lengkap beserta dengan tag-nya. Satu hal yang menarik disini adalah walaupun saya menyertakan password seperti -PregistryPassword=${{ secrets.GITHUB_TOKEN }}
, di hasil eksekusi Github Actions, password tersebut secara otomatis di-masking seperti yang terlihat pada gambar berikut ini:
Ini adalah fitur yang meningkatkan keamanan sehingga tidak ada kebocoran password, bahkan saya sendiri tidak tahu apa password yang dipakai. Walaupun demikian, saya tetap perlu berhati-hati bila menyetel password ini sebagai environment variable atau melewatkannya ke action yang tidak dikenal.
Sampai disini, saya dapat melihat semua eksekusi GitHub Actions di https://github.com/JockiHendry/latihan-k8s/actions. Bila ada proses build yang gagal, saya dapat segera mengetahuinya dan memperbaikinya. Selain itu, saya juga bisa menggunakan badge seperti dan untuk memperoleh informasi build tanpa harus masuk ke halaman tersebut.
Untuk melihat daftar image Docker yang sudah dibuat, saya dapat membuka tab Packages dari halaman profil. Bila saya men-klik salah satu image, maka informasi untuk memakainya melalui perintah docker
beserta daftar riwayat tag-nya akan muncul. Namun, bagaimana bila saya ingin menggunakannya di file manifest Kubernetes? Karena saya menggunakan repository publik yang dapat diakses siapa saja, saya cukup mengubah image
dari format seperti angular-web:latest
menjadi seperti ghcr.io/jockihendry/latihan-k8s-angular-web:edge
seperti yang terlihat pada baris berikut ini:
Bila sebelumnya saya menggunakan nilai Never
untuk imagePullPolicy
, saya perlu menghapus baris tersebut atau setidaknya mengubahnya menjadi Always
supaya Kubernetes mau mengambil image dari luar. Untuk image di GitHub Packages yang dipublikasikan dari repository private, saya perlu menggunakan <imagePullSecrets>
seperti yang tertera pada panduan di https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/.