Kenapa pakai autopilot cluster-nya Google Kubernetes Engine (GKE) adalah untuk meminimalisir cost dari penggunaan VM di Google Cloud Platform (GCE). Kenapa pakai private karena alasan keamanan. Cara membuat private autopilot cluster ada di link ini, berikut configurasi Cloud NAT.
Untuk mengakses private cluster selain menggunakan Cloud Shell, bisa dengan menggunakan VM yang VPC network sama dengan cluster-nya. Untuk mengetahui node tag firewall dari autopilot cluster, bisa dengan cara melihat existing firewall rule yang dibuat oleh Google, biasanya dengan nama rule <cluster_name>-<number>-all, atau <cluster_name>-<number>-master.
Cara installasi Gitlab Runner kubernetes bisa di link ini, dengan menggunakan helm chart. Untuk referensi values.yaml yang lengkap bisa lihat di sini.
Minimum values.yaml:
gitlabUrl: https://gitlab.com
runnerRegistrationToken: "${GITLAB_REGISTRATION_TOKEN}"
rbac:
create: true
runners:
tags: "${GITLAB_RUNNERS_TAGS}"
runUntagged: false
config: |
concurrent = 10
[[runners]]
[runners.kubernetes]
namespace = "{{ .Release.Namespace }}"
image = "ubuntu:16.04"
poll_timeout = 360
Atur nilai poll_timeout (dalam detik) karena ini waktu yang dibutuhkan sebuah job untuk menunggu pod ready bisa digunakan. Untuk autopilot cluster nilainya bisa lebih besar karena autopilot cluster perlu untuk menyiapkan node terlebih dahulu. Untuk konfigurasi RBAC, maka perlu konfigurasi ClusterRoleBinding:
kubectl create clusterrolebinding ${CRB_USER_CLUSTER_NAME} \
--clusterrole cluster-admin \
--user ${USER_AT_PROJECT_IAM}
Perintah di atas akan mem-binding user yang mengeksekusi helm dengan ClusterRole cluster-admin.
Setelah itu jalankan helm install:
helm install --namespace $NAMESPACE $RELEASENAME -f values.yaml gitlab/gitlab-runner
JOB POD RESOURCE CUSTOMIZATION
Setiap job yang jalan menggunakan resource CPU, RAM, dan Ephemeral Storage yang sama. Namun jika ada job yang perlu resource lebih maka bisa mengunakan gitlab variables pada job yang diinginkan, bisa di lihat di sini.
Agar bisa di-override maka di config.toml harus di-set nilai maximum dari resource yang di perbolehkan. Sehingga file values.yaml seperti ini:
...
runners:
...
config: |
...
[[runners]]
...
[runners.kubernetes]
...
# build container
cpu_limit = "2000m"
memory_limit = "6Gi"
# service containers
service_cpu_limit = "1000m"
service_memory_limit = "1Gi"
# helper container
helper_cpu_limit = "1000m"
helper_memory_limit = "1Gi"
# allow override
cpu_limit_overwrite_max_allowed = "4000m"
memory_limit_overwrite_max_allowed = "6Gi"
cpu_request_overwrite_max_allowed = "4000m"
memory_request_overwrite_max_allowed = "6Gi"
ephemeral_storage_limit_overwrite_max_allowed = "10Gi"
ephemeral_storage_request_overwrite_max_allowed = "10Gi"
helper_cpu_limit_overwrite_max_allowed = "2000m"
helper_memory_limit_overwrite_max_allowed = "2Gi"
helper_cpu_request_overwrite_max_allowed = "2000m"
helper_memory_request_overwrite_max_allowed = "2Gi"
helper_ephemeral_storage_limit_overwrite_max_allowed = "10Gi"
helper_ephemeral_storage_request_overwrite_max_allowed = "10Gi"
service_cpu_limit_overwrite_max_allowed = "2000m"
service_memory_limit_overwrite_max_allowed = "2Gi"
service_cpu_request_overwrite_max_allowed = "2000m"
service_memory_request_overwrite_max_allowed = "2Gi"
service_ephemeral_storage_limit_overwrite_max_allowed = "10Gi"
service_ephemeral_storage_request_overwrite_max_allowed = "10Gi"
Setup Cache Using Persistent Volume Claim (PVC)
Kubernetes runner tidak mendukung local cache secara biasa karena pod (beserta storage nya) untuk setiap job akan di hapus setelah job selesai. Maka perlu tambahan konfigurasi untuk cache ini menggunakan distributed cache, misal ke S3 atau GCS.
Namun ada pilihan lain dengan menggunakan storage dari Kubernetes yaitu Persistent Volume Claim. Setiap pod akan me-mount PVC storage ini sebagai cache direktori, seolah-olah sebagai local storage.
Karena tiap job yang jalan di pod tidak menggunakan node yang sama maka menggunakan tipe HostPath tidak memungkinkan apalagi dilarang pada autopilot cluster yang node-nya managed. Cache storage ini harus bisa dibaca-tulis bersamaan oleh lebih dari satu pods, maka perlu akses ReadWriteMany, bukan ReadWriteOnce. Untuk itu yang paling sederhana adalah dengan menggunakan NFS Server (NFS bukan Need For Speed) bisa dilihat di sini.
Maka perlu update values.yaml agar menggunakan cache dari VPC:
...
runners:
...
config: |
...
[[runners]]
...
cache_dir = "${CACHE_DIR}"
[runners.kubernetes]
...
[[runners.kubernetes.volumes.pvc]]
name = "${PVC_NAME}"
mount_path = "${CACHE_DIR}"
Troubleshooting
Karena pod akan hilang ketika job selesai, maka cara pertama adalah me-monitor pod ketika job di-retry, bisa dari Google Cloud Console atau dari kubectl logs atau kubectl top.
Selain itu bisa menggunakan kubectl get events -n $NAMESPACE, misal untuk mengetahui apakah resource yang digunakan kurang seperti ephemeral storage ketika job men-download packages.
Cache penuh akan membuat job failed. Mengosongkan cache tidak cukup menggunakan UI dari Pipelines Gitlab. Termasuk untuk branch yang dihapus, cache tidak ikut terhapus. Jadi harus hapus manual di NFS servernya, atau cache key nya di-optimalisasi misal menggunakan lock file dari package.