Optimalkan Pengembangan Software: Implementasi GitLab, Kubernetes, dan Cloudflare Tunnel untuk CI/CD Pipeline

Ardhan Dikri Achmad Fahrudin
DOT Intern
Published in
12 min readMar 19, 2024

Pendahuluan

Dalam dunia pengembangan perangkat lunak, Continuous Integration (CI) dan Continuous Deployment atau Continuous Delivery (CD) adalah praktik yang penting untuk mempercepat dan menyederhanakan proses pengembangan perangkat lunak itu sendiri. CI/CD tidak hanya meningkatkan efisiensi kecepatan pengiriman perangkat lunak, tetapi juga dapat memastikan kualitas tinggi dan pengiriman yang dapat diandalkan. Dalam artikel ini, saya akan mengimplementasikan CI/CD dan bagaimana integrasi GitLab, Kubernetes, dan Cloudflare Tunnel.

Persyaratan

Sebelum mengimplementasikan CI/CD, pastikan persyaratan berikut terpenuhi:

  • Source Code Aplication
  • Account Docker Hub
  • 2 MongoDB Database (Staging & Production)
  • 2 Virtual Machine Kubernetes Cluster Minikube (Sudah Terinstall Kubectl, Docker dan Helm)
  • 1 Virtual Machine Docker (Opsional)
  • Public Domain (Aktif)

Untuk Virtual Machine Docker bersifat opsional, di sini saya tidak menggunakannya karena untuk stage build dan test pada pipeline, saya menggunakan runner yang disediakan oleh Gitlab.

Step Instalasi

Clone Project

  • Clone terlebih dahulu Source Code Project.
git clone https://gitlab.com/ArdhanFah/jobs-publish-and-seeker.git
  • Masuk ke dalam direktori jobs-publish-and-seeker, di folder inilah tempat workdir anda untuk project ini. Untuk itu hapus terlebih dahulu folder .git didalam folder jobs-publish-and-seeker.
cd jobs-publish-and-seeker
rm -rf .git

Docker Hub

  • Buat 2 repository di docker hub, masing-masing untuk jobs backend dan frontend nantinya.
Repository Image - Docker Hub

noted: simpan access token ke dalam notepad atau text editor

Dapatkan Runner Token

Sebelum melakukan setup Kubernetes, diperlukan runner token dari gitlab terlebih dahulu, oleh karna itu generate terlebih dahulu token tersebut.

  • Buka repository project Gitlab -> Settings -> CI/CD -> Runners -> New Project Runners.
  • Buat 2 Runner, masing-masing untuk deployment-staging dan deployment-production.
Runner Inactive - Gitlab
  • Jangan lupa untuk menyimpan token yang didapat. Setelah token didapat, kita bisa melakukan setup kubernetes runner.

Setup Kubernetes Runner

Sebelum setup Gitlab CI/CD, lakukan setup terlebih dahulu pada runner staging dan production menggunakan Helm Chart.

  • Masuk ke dalam Kubernetes cluster staging dan production, lalu tambahkan GitLab Helm repository:
helm repo add gitlab https://charts.gitlab.io
  • Untuk melihat list versi dari GitLab Runner, jalankan command di bawah.
helm search repo -l gitlab/gitlab-runner
  • Buat namespace gitlab-runner.
kubectl create ns gitlab-runner
  • Download file values.yaml menggunakan raw url di bawah.
wget https://gitlab.com/ArdhanFah/jobs-publish-and-seeker/-/raw/main/Helm/values.yaml?ref_type=heads

Ubah isi dari file values.yaml pada <gitlab-runner-token>. Ubah dengan token yang didapat dari Gitlab sebelumnya.

noted: yang membedakan antara runner staging dan production terletak pada token yang anda masukkan.

  • Install runner menggunakan perintah Helm di bawah.
helm install --namespace gitlab-runner -f values.yaml  gitlab-runner gitlab/gitlab-runner
  • Untuk memastikan apakah runner sudah berjalan, Anda dapat melakukan langkah-langkah berikut:
Runner Active - Gitlab

Setelah kedua runner berjalan, setup Cloudflare Tunnel dengan mengikuti langkah-langkah di bawah ini.

Setup Cloudflare Tunnel

Buat sebuah Tunnel untuk aplikasi kita nantinya, supaya bisa diakses melalui publik tanpa harus menggunakan ip publik, maka dari itu disini saya menggunakan layanan Cloudflare Tunnel.

  • Buka Browser anda. Selanjutnya, masuk ke website cloudflare.com dan login ke dalam akun yang sudah memiliki domain aktif didalamnya.
  • Klik pada domain yang ingin digunakan, dalam artikel ini saya akan menggunakan domain ardhanfahrudin.cloud.
Domain Publik - Cloudflare
  • Klik pada Menu -> Access -> Launch Zero Trust.
Menu Cloudflare Tunnel Zero Trust
  • Pada menu access klik Tunnel -> Create a Tunnel, buat 2 tunnel untuk staging dan production.
Menu Cloudflare Tunnel
  • Lakukan encode terhadap Token tunnel yang didapat menggunakan base64 seperti pada contoh command di bawah.
echo -n "your token tunnel" | base64
  • Lakukan terhadap kedua token yang didapat, setelah itu simpan token yang sudah diencode ke dalam text editor. Setelah itu, pada bagian public hostname buat masing-masing 2 hostname, yaitu untuk backend dan frontend untuk setiap tunnel.
Production Tunnel - Cloudflare
Staging Tunnel - Cloudflare
  • Untuk layanan ini, disesuaikan dengan nama layanan dari backend dan frontend yang akan didefinisikan dalam manifest Kubernetes. Oleh karena itu, ketika membuat manifest layanan, nama layanan harus disesuaikan dengan tabel di bawah ini.

Setelah setup tunnel selesai, saatnya membuat file manifest Kubernetes app backend, frontend untuk staging dan production.

Buat Manifest Kubernetes

Buatlah manifest kubernetes untuk production dan staging, seperti manifest backend, frontend dan juga tunnel.

  1. Staging Manifest
Struktur Project Folder Staging

Buatlah staging folder, dengan 3 sub directory didalamnya, masing-masing untuk backend, frontend dan tunnel.

Masuk ke dalam folder backend-staging dan buat file-file di bawah ini.

  • backend.secret.config.staging.yaml
apiVersion: v1
kind: Secret
metadata:
name: secret-db-staging
namespace: default
labels:
app: jobs-app-staging
type: secret-staging
type: Opaque
data:
MONGO_USER: YWRtaW4=
MONGO_PASS: c3VwZXJhZG1pbg==

---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-db-staging
namespace: default
labels:
app: jobs-app-staging
type: config-staging
data:
MONGO_HOST: staging.ardhanfahrudin.cloud
MONGO_PORT: '65534'
  • backend.staging.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-app-stg
namespace: default
labels:
app: jobs-app-staging
spec:
replicas: 1
selector:
matchLabels:
app: jobs-app-staging
tier: backend-staging
template:
metadata:
labels:
app: jobs-app-staging
tier: backend-staging
spec:
containers:
- image: ardhaninc/backend-jobs:latest
imagePullPolicy: Always
name: backend-staging
ports:
- name: backend-staging
containerPort: 8080
env:
- name: APP_PORT
value: "8080"
- name: MONGO_USER
valueFrom:
secretKeyRef:
name: secret-db-staging
key: MONGO_USER
- name: MONGO_PASS
valueFrom:
secretKeyRef:
name: secret-db-staging
key: MONGO_PASS
- name: MONGO_HOST
valueFrom:
configMapKeyRef:
name: config-db-staging
key: MONGO_HOST
- name: MONGO_PORT
valueFrom:
configMapKeyRef:
name: config-db-staging
key: MONGO_PORT
resources:
limits:
memory: 512Mi
cpu: 256m
requests:
memory: 256Mi
cpu: 128m
  • backend.svc.staging.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-svc-stg
namespace: default
labels:
app: jobs-app-staging
type: service-staging
spec:
ports:
- name: backend-staging
port: 8080
targetPort: backend-staging
selector:
app: jobs-app-staging
tier: backend-staging

Masuk ke dalam folder frontend-staging dan buat file-file di bawah ini:

  • frontend.staging.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-stg
namespace: default
labels:
app: jobs-app-staging
spec:
replicas: 1
selector:
matchLabels:
app: jobs-app-staging
tier: frontend-staging
template:
metadata:
labels:
app: jobs-app-staging
tier: frontend-staging
spec:
containers:
- image: ardhaninc/frontend-jobs:latest
imagePullPolicy: Always
name: frontend-stg
ports:
- containerPort: 8003
name: frontend-stg
env:
- name: VUE_APP_BACKEND
value: https://backstage.ardhanfahrudin.cloud
resources:
limits:
memory: 1024Mi
cpu: 512m
requests:
memory: 512Mi
cpu: 256m
  • frontend.svc.staging.yaml
apiVersion: v1
kind: Service
metadata:
name: frontend-svc-stg
namespace: default
labels:
app: jobs-app-staging
type: service
spec:
ports:
- name: frontend-stg
port: 8003
targetPort: frontend-stg
selector:
app: jobs-app-staging
tier: frontend-staging

Masuk ke dalam folder tunnel-staging dan buat file-file di bawah ini:

  • tunnel.secret.staging.yaml
apiVersion: v1
kind: Secret
metadata:
name: tunnel-web-staging
namespace: default
labels:
app: jobs-app-staging
type: secret-staging
type: Opaque
data:
TUNNEL_TOKEN: <your base64 encode tunnel token> # Replace with your encode tunnel token staging
  • tunnel.staging.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tunnel-app-stg
namespace: default
labels:
app: jobs-app-staging
spec:
replicas: 1
selector:
matchLabels:
app: jobs-app-staging
tier: tunnel-staging
template:
metadata:
labels:
app: jobs-app-staging
tier: tunnel-staging
spec:
containers:
- image: cloudflare/cloudflared:latest
imagePullPolicy: IfNotPresent
name: tunnel-staging
command: ["cloudflared", "tunnel", "--no-autoupdate", "run"]
args: ["--token", "$(TUNNEL_TOKEN)"]
env:
- name: TUNNEL_TOKEN
valueFrom:
secretKeyRef:
name: tunnel-web-staging
key: TUNNEL_TOKEN

Setelah manifest-staging dibuat, selanjutnya buat file manifest untuk production.

2. Porduction Manifest

Struktur Project Folder

Masuk ke dalam folder backend dan buat file-file di bawah ini.

  • backend.secret.config.yaml
apiVersion: v1
kind: Secret
metadata:
name: secret-db
namespace: default
labels:
app: jobs-app
type: secret
type: Opaque
data:
MONGO_USER: YWRtaW4=
MONGO_PASS: c3VwZXJhZG1pbg==

---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-db
namespace: default
labels:
app: jobs-app
type: config
data:
MONGO_HOST: ardhanfahrudin.cloud
MONGO_PORT: '65535'
  • backend.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-app
namespace: default
labels:
app: jobs-app
spec:
replicas: 2
selector:
matchLabels:
app: jobs-app
tier: backend
template:
metadata:
labels:
app: jobs-app
tier: backend
spec:
containers:
- image: ardhaninc/backend-jobs:latest
imagePullPolicy: Always
name: backend
ports:
- name: backend
containerPort: 8080
env:
- name: APP_PORT
value: "8080"
- name: MONGO_USER
valueFrom:
secretKeyRef:
name: secret-db
key: MONGO_USER
- name: MONGO_PASS
valueFrom:
secretKeyRef:
name: secret-db
key: MONGO_PASS
- name: MONGO_HOST
valueFrom:
configMapKeyRef:
name: config-db
key: MONGO_HOST
- name: MONGO_PORT
valueFrom:
configMapKeyRef:
name: config-db
key: MONGO_PORT
resources:
limits:
memory: 512Mi
cpu: 256m
requests:
memory: 256Mi
cpu: 128m

  • backend.svc.yaml
apiVersion: v1
kind: Service
metadata:
name: backend-svc
namespace: default
labels:
app: jobs-app
type: service
spec:
ports:
- name: backend
port: 8080
targetPort: backend
selector:
app: jobs-app
tier: backend

Masuk ke dalam folder frontend dan buat file-file di bawah ini:

  • frontend.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-jobs-app
namespace: default
labels:
app: jobs-app
spec:
replicas: 2
selector:
matchLabels:
app: jobs-app
tier: frontend
template:
metadata:
labels:
app: jobs-app
tier: frontend
spec:
containers:
- image: ardhaninc/frontend-jobs:latest
imagePullPolicy: Always
name: frontend
ports:
- containerPort: 8003
name: frontend
env:
- name: VUE_APP_BACKEND
value: https://backend.ardhanfahrudin.cloud
resources:
limits:
memory: 1024Mi
cpu: 512m
requests:
memory: 512Mi
cpu: 256m
  • frontend.svc.yaml
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
namespace: default
labels:
app: jobs-app
type: service
spec:
ports:
- name: frontend
port: 8003
targetPort: frontend
selector:
app: jobs-app
tier: frontend

Masuk ke dalam folder tunnel dan buat file-file di bawah ini:

  • tunnel.secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: tunnel-web
namespace: default
labels:
app: jobs-app
type: secret
type: Opaque
data:
TUNNEL_TOKEN: <your base64 encode tunnel token> # Replace with your encode tunnel token
  • tunnel.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tunnel-jobs-app
namespace: default
labels:
app: jobs-app
spec:
replicas: 1
selector:
matchLabels:
app: jobs-app
tier: tunnel
template:
metadata:
labels:
app: jobs-app
tier: tunnel
spec:
containers:
- image: cloudflare/cloudflared:latest
imagePullPolicy: IfNotPresent
name: tunnel
command: ["cloudflared", "tunnel", "--no-autoupdate", "run"]
args: ["--token", "$(TUNNEL_TOKEN)"]
env:
- name: TUNNEL_TOKEN
valueFrom:
secretKeyRef:
name: tunnel-web
key: TUNNEL_TOKEN

Sedikit penjelasan mengenai manifest-manifest yang memiliki credentials-credentials penting seperti token, username dan password. nantinya tidak akan di push ke Gitlab repositori secara langsung, melainkan akan di simpan ke dalam variable file pada Settings -> CI/CD -> Variables.

Gitlab CI/CD

Sebelum melakukan push project ke dalam repositori Gitlab, buat terlebih dahulu file .gitlab-ci.yml. File ini berisi instruksi-instruksi yang digunakan untuk menjalanakan CI/CD.

  • Buat file .gitlab-ci.yaml pada root direktori project.
stages:
- build
- test
- deploy-staging
- deploy-prod

build_backend:
image: docker:24.0.7
services:
- docker:24.0.7-dind
stage: build
before_script:
- cd backend/
- echo $DOCKER_TOKEN | docker login -u $DOCKER_USER --password-stdin
script:
- docker buildx build -t $BACKEND_IMAGE:latest .
- docker tag $BACKEND_IMAGE:latest $BACKEND_IMAGE:v1.$CI_PIPELINE_IID
- docker push $BACKEND_IMAGE:latest
- docker push $BACKEND_IMAGE:v1.$CI_PIPELINE_IID
after_script:
- docker logout
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
changes:
- backend/*
- .gitlab-ci.yml


build_frontend:
image: docker:24.0.7
services:
- docker:24.0.7-dind
stage: build
before_script:
- cd frontend/
- echo $DOCKER_TOKEN | docker login -u $DOCKER_USER --password-stdin
script:
- docker buildx build -t $FRONTEND_IMAGE:latest .
- docker tag $FRONTEND_IMAGE:latest $FRONTEND_IMAGE:v1.$CI_PIPELINE_IID
- docker push $FRONTEND_IMAGE:latest
- docker push $FRONTEND_IMAGE:v1.$CI_PIPELINE_IID
after_script:
- docker logout
rules:
- if: '$CI_COMMIT_BRANCH == "dev"'
changes:
- frontend/*
- .gitlab-ci.yml

test_backend:
image: $BACKEND_IMAGE:latest
stage: test
script:
- cd /go/src/backend-jobs && /build/web &
- sleep 3
- curl http://localhost:8080
only:
- dev

test_frontend:
image: $FRONTEND_IMAGE:latest
stage: test
script:
- cd /app && npm run serve &
- sleep 5
- curl http://localhost:8003 | grep -q "frontend-jobs"
only:
- dev

deploy_staging:
stage: deploy-staging
before_script:
- apk add --no-cache kubectl
script:
- kubectl apply -f $SECRET_TUNNEL_STAGING
- kubectl apply -f $SECRET_BACKEND_STAGING
- kubectl apply -f kubernetes-staging/tunnel-staging/
- kubectl apply -f kubernetes-staging/backend-staging/
- kubectl apply -f kubernetes-staging/frontend-staging/
tags:
- kube-staging
only:
- merge_request

deploy_prodduction:
stage: deploy-prod
before_script:
- apk add --no-cache kubectl
script:
- kubectl apply -f $SECRET_TUNNEL
- kubectl apply -f $SECRET_BACKEND
- kubectl apply -f kubernetes/tunnel/
- kubectl apply -f kubernetes/backend/
- kubectl apply -f kubernetes/frontend/
- kubectl autoscale deployment backend-app --cpu-percent=90 --min=2 --max=4 -n default
- kubectl autoscale deployment frontend-app --cpu-percent=90 --min=2 --max=4 -n default
tags:
- kube-prod
only:
- main

Setelah membuat file .gitlab-ci.yml, langkah selanjutnya adalah mendaftarkan variable-variable yang nantinya digunakan saat proses pipeline.

  • Untuk mendaftarkan variable, buka repository gitlab untuk project ini.
Repository Project - Gitlab
  • Klik menu Settings -> CI/CD -> Variables
  • Buat variable-variable berdasarkan file .gitlab-ci.yaml
Pipeline Variable - Gitlab
  • Sedikit penjelasan singkat mengenai variable-variable di atas:
  • Setelah selesai setup variable di atas, selanjutnya push code anda yang terdapat manifest-manifest file Kubernetes ke dalam Gitlab repository anda.
git init
git add .
git commit -m "version 1.0"
git branch -m dev
git remote add origin <your repository remote url>
git push -f origin dev
  • Lihat kembali pada repository gitlab, apakah branch dev sudah terpush.
  • Untuk melihat pipeline yang berjalan, pergi ke menu Build -> Pipelines, di sana anda akan menemukan pipeline yang sedang berjalan.
Continous Integration (CI) - Gitlab

Pipeline step akan berhenti di test stage, hal ini disebabkan karena pada file .gitlab-ci.yml hanya stage build dan deploy yang akan dijalankan saat berada di branch dev. Simplenya pada branch dev hanya menjalankan proses Continous Integration (CI) saja.

  • Sebelum melakukan merge_request pastikan terlebih dahulu bahwa image benar-benar terpush ke Docker Hub.
Backend Image - Docker Hub
Frontend Image - Docker Hub

Jika image hasil Continous Integration (CI) sudah terpush ke dalam Docker Hub. Selanjutnya, buka kembali Gitlab repository project.

  • Lakukan merge request dari branch dev ke branch main, klik menu Code -> merge_request -> New Merge Request.
  • Klik Compare branches and continue.

Anda bisa menambahkan deskripsi untuk merge_request. Setelah sudah, klik create merge request.

  • Setelah itu anda akan di arahkan ke halaman merge request, sebelum melakukan approve dan merge, pastikan terlebih dahulu bahwa deploy staging passed.
  • Setelah deploy staging passed.
  • Coba akses web staging yang sudah di deploy, dengan mengunjungi alamat url yang didefinisikan untuk staging pada Cloudflare Tunnel sebelumnya.
Fitur Home - Staging
  • Untuk melakukan tes fungsionalitas dari aplikasi apakah sudah berjalan dengan normal atau belum, coba tambahkan data pada manage jobs.
Fitur New Job - Staging
  • Klik Publish dan lihat kembali pada halaman Home dari aplikasi.
Fitur Home - Staging
  • Setelah melewati test layak uji dan tes fungsionalitas dari aplikasi. Barulah anda bisa melakukan approve dan merge agar aplikasi dapat di deploy ke production.
Approval dan Merge Gitlab
  • Setelah merge berhasil, coba lihat pada pipeline apakah sudah passed atau belum.
Pipeline Passed
  • Jika sudah passed seperti di atas, coba akses url production yang sudah didefinisikan pada Cloudflare Tunnel sebelumnya.
Fitur Home - Production
  • Untuk melakukan tes fungsionalitas production, coba tambahkan data jobs pada menu Manage Jobs.
Fitur New Job - Production
  • Setelah itu, klik publish dan lihat kembali pada halaman Home dari aplikasi. Jika data jobs berhasil ditambahkan, bisa dipastikan aplikasi sudah berhasil dideploy dengan benar.
Fitur Home - Production

Kesimpulan

Implementasi CI/CD pipeline menggunakan GitLab, Kubernetes, dan Cloudflare Tunnel memberikan solusi terintegrasi untuk meningkatkan efisiensi siklus hidup perangkat lunak. Proses otomatisasi pengujian, integrasi, dan pengiriman memungkinkan pengembangan yang lebih cepat dan handal. GitLab CI memfasilitasi otomatisasi langkah-langkah, Kubernetes mendukung deployment dan scaling aplikasi secara dinamis, dan Cloudflare Tunnel menjaga keamanan komunikasi antara server dan pengguna akhir. Integrasi ini menciptakan lingkungan pengembangan yang efisien, aman, dan terautomatisasi, memungkinkan pengembang fokus pada peningkatan kualitas perangkat lunak tanpa khawatir tentang proses pengiriman manual.

--

--