Prerequisites
Laravel Project.
Kubernetes Cluster. (Minikube)
Redis.
Helm.
Artikel ini berasumsi bahwa Anda sudah memiliki proyek Laravel dan contoh penempatan ini dilakukan pada versi Laravel 8. Jika Anda menggunakan versi Laravel yang berbeda, Anda perlu memodifikasi dan mengubah dockerfile di bawah ini untuk bagian backend.
Contoh proyek ini menggunakan database MySQL, jika Anda menggunakan database yang berbeda, Anda perlu memodifikasi dockerfile untuk backend untuk menginstal paket-paket yang diperlukan untuk database Anda.
Kita akan menggunakan chart Helm untuk menginstal MySQL dan Redis di klaster k8s, yang merupakan cara yang paling direkomendasikan untuk mendeploy layanan-layanan ini.
Mengapa Anda memerlukan Redis?
Secara default, sesi Laravel disimpan di memori server, yang dapat berisi sesi kredensial pengguna jika aplikasi Laravel Anda menggunakan cara lama untuk mengautentikasi pengguna (Token Opaque) yang menggunakan file sesi untuk validasi. Metode autentikasi ini tidak kompatibel dengan arsitektur mikro layanan. Pendekatan Mikro Layanan menyarankan penggunaan token JWT untuk autentikasi atau menggunakan Redis untuk menyimpan sesi.
Cara terbaik adalah dengan menyimpan sesi dari aplikasi Laravel Anda di Redis.
Mengkonfigurasi Laravel untuk Menyimpan Sesi di Redis
Untuk mengkonfigurasi aplikasi Laravel Anda agar mulai menyimpan informasi sesi di Redis, dari file config/session.php ubah yang berikut ini:
'driver' => env('SESSION_DRIVER', 'file'),
menjadi
'driver' => env('SESSION_DRIVER', 'redis'),
Kemudian jalankan perintah composer berikut untuk menambahkan paket predis ke file composer.json.
composer require predis/predis
Task 1. Running Application On Local
git clone https://github.com/mdrdani/app-recordCounseling.git
cd app-recordCounseling/
cp .env.example .env
composer update
install composer link jika belum terinstall.
php artisan key:generate
php artisan migrate
php artisan storage:link
php artisan serve
Task 2. Create Container Image with Github Actions (CI)
sebelum membuat workflow container image, hal pertama yaitu kita perlu membuat konfigurasi docker aplikasi laravel nya, buat folder dengan nama docker didalam aplikasi laravel dan buatkan beberapa file YAML seperti ini.
nginx.Dockerfile.yml
FROM node:20.5.1-alpine AS assets-build
WORKDIR /var/www/html
COPY . /var/www/html/
RUN npm ci
RUN npm run build
FROM nginx:1.19-alpine AS nginx
COPY /docker/vhost.conf /etc/nginx/conf.d/default.conf
COPY --from=assets-build /var/www/html/public /var/www/html/
fpm.Dockerfile.yml
FROM php:8.1-fpm-alpine AS base
ENV EXT_APCU_VERSION=master
RUN curl -vvv https://github.com/krakjoe/apcu.git
RUN apk add --update zlib-dev libpng-dev libzip-dev $PHPIZE_DEPS
RUN docker-php-ext-install exif
RUN docker-php-ext-install gd
RUN docker-php-ext-install zip
RUN docker-php-ext-install pdo_mysql
# RUN pecl install apcu
RUN docker-php-source extract \
&& apk -Uu add git \
&& git clone --branch $EXT_APCU_VERSION --depth 1 https://github.com/krakjoe/apcu.git /usr/src/php/ext/apcu \
&& cd /usr/src/php/ext/apcu && git submodule update --init \
&& docker-php-ext-install apcu
RUN docker-php-ext-enable apcu
FROM base AS dev
COPY /composer.json composer.json
COPY /composer.lock composer.lock
COPY /app app
COPY /bootstrap bootstrap
COPY /config config
COPY /artisan artisan
FROM base AS build-fpm
WORKDIR /var/www/html
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY /artisan artisan
COPY . /var/www/html
# COPY /composer.json composer.json
RUN composer install --prefer-dist --no-ansi --no-dev
COPY /bootstrap bootstrap
COPY /app app
COPY /config config
COPY /routes routes
# COPY . /var/www/html
RUN composer dump-autoload -o
FROM build-fpm AS fpm
COPY --from=build-fpm /var/www/html /var/www/html
vhost.conf
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index /public/index.php;
error_page 404 /public/index.php;
location / {
try_files $uri $uri/ /public/index.php;
}
location ~ \.php$ {
fastcgi_pass localhost:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
}
Selanjutnya untuk push image ke DockerHub kita perlu menyimpan Username dan Token Dockerhub di Secrets Github, create secrets yang gunanya untuk menyimpan hal-hal sensitif seperti username dan token agar tidak terpublish di public.
ke Repo Github > Settings > Security > Secrets and variables > actions > New repository secret.
jika sudah selanjutnya membuat workflow.
Ke Repo Github > Actions > New Workflow, pilih Docker Image klik configure
untuk workflows yang pertama beri nama dengan backend-image.yml
name: Docker Image Backend CI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login ke Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build docker image
run: docker build . -f docker/fpm.Dockerfile -t cehamot/backend-appoinmentapp:${{ github.run_number }}
- name: Push docker image
run: docker push cehamot/backend-appoinmentapp:${{ github.run_number }}
dan kedua worflows di beri nama frontend-image.yml
name: Docker Image Frontend CI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login ke Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build docker image
run: docker build . -f docker/nginx.Dockerfile -t cehamot/frontend-appoinmentapp:${{ github.run_number }}
- name: Push docker image
run: docker push cehamot/frontend-appoinmentapp:${{ github.run_number }}
simpan dengan memilih Commit Changes.
check di Docker Hub apakah sudah ada container image Backend dan Frontend.
Oke Build Docker Container image (Continuous Integration) sudah selesai.
Task 3. Deploy To Kubernetes (CD)
Sebelum memulai membuat berkas manifest K8s, kita perlu menjalankan MySQL dan Redis di dalam kluster terlebih dahulu, jadi saya akan menggunakan chart Helm untuk itu.
Pastikan Anda telah menginstal Helm di mesin Anda, lalu jalankan perintah berikut untuk menambahkan repositori bitnami.
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
Mulai menginstal MySQL menggunakan chart Helm.
Pada saat perintah pemasangan, Anda akan memberikan nama basis data, pengguna basis data, dan kata sandi basis data. Informasi ini akan digunakan dalam konfigurasi dan rahasia (secret) dari penyebaran (deployment).
helm install mysql bitnami/mysql --set auth.database="application" --set auth.username="dani" --set auth.password="Secret123!!"
Menginstal Redis menggunakan chart Helm, pastikan untuk menyimpan kata sandi Redis yang diberikan pada perintah saat menginstal.
helm install redis bitnami/redis --set auth.password="Secret123!!"
Periksa status pod MySQL dan Redis, seharusnya keduanya sudah berjalan.
kubectl get pod
Jika keduanya sudah berjalan, Anda siap untuk mulai membuat berkas manifest Kubernetes.
Sebelumnya kita buat direktori Kubernetes di project.
mkdir ./k8s
Buat config file untuk deployment.
touch ./k8s/app_config.yaml
di bawah ini content app_config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: application-config
data:
APP_NAME: appointment-app
APP_ENV: production
APP_DEBUG: "false"
APP_URL: http://localhost:8080
LOG_CHANNEL: stack
LOG_LEVEL: debug
DB_CONNECTION: mysql
DB_HOST: mysql
DB_PORT: "3306"
DB_DATABASE: application
DB_USERNAME: dani
BROADCAST_DRIVER: log
CACHE_DRIVER: redis
QUEUE_CONNECTION: sync
SESSION_DRIVER: redis
SESSION_LIFETIME: "120"
PROMETHEUS_NAMESPACE: default
PROMETHEUS_METRICS_ROUTE_ENABLED: "true"
PROMETHEUS_METRICS_ROUTE_PATH: metrics
PROMETHEUS_METRICS_ROUTE_MIDDLEWARE: "null"
PROMETHEUS_STORAGE_ADAPTER: memory
REDIS_HOST: redis-master
REDIS_PORT: "6379"
REDIS_CLIENT: "predis"
PROMETHEUS_REDIS_PREFIX: PROMETHEUS_
Pastikan untuk menuliskan nilai yang benar untuk DB_HOST
, DB_USERNAME
, dan REDIS_HOST
sesuai dengan yang diberikan saat menjalankan perintah helm install
untuk MySQL dan Redis. Nilai-nilai ini akan digunakan dalam konfigurasi berkas manifest Kubernetes .
Buat config file untuk Secret.
touch ./k8s/app_secret.yaml
di bawah ini content app_secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: application-secret
type: Opaque
data:
REDIS_PASSWORD: U2VjcmV0MTIz
DB_PASSWORD: U2VjcmV0MTIz
APP_KEY: YmFzZTY0OlYweEFXc3JjeUFQTVkxK2tneXNBV20ycHRWU0ExMStVQXU3OG8vTXFEakk9Cg==
Catat bahwa REDIS_PASSWORD
dan DB_PASSWORD
adalah nilai yang diberikan saat menginstal dengan chart Helm, dan nilainya dienkripsi dalam format base64.
Mengenai APP_KEY
, kita bisa membuat nya kemudian mengenkripsinya dalam format base64 dan menggantinya dengan yang ada.
Buat config file untuk Deployment.
Berikut ini adalah contoh berkas deployment Kubernetes yang mencakup dua kontainer (backend dan frontend) serta initContainer untuk menjalankan migrasi PHP guna mempersiapkan database.
touch ./k8s/app_deployment.yaml
di bawah ini content app_deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-application
spec:
replicas: 1
selector:
matchLabels:
app: web-application
template:
metadata:
labels:
app: web-application
annotations:
prometheus.io/scrape: "true"
prometheus.io/path: /metrics
prometheus.io/port: "80"
spec:
volumes:
- name: logs
emptyDir: {}
- name: views
emptyDir: {}
securityContext:
fsGroup: 82
initContainers:
- name: database-migrations
image: cehamot/backend-appoinmentapp:2
imagePullPolicy: IfNotPresent
envFrom:
- configMapRef:
name: application-config
- secretRef:
name: application-secret
command:
- "php"
args:
- "artisan"
- "migrate:fresh"
- "--seed"
containers:
- name: nginx
imagePullPolicy: IfNotPresent
image: cehamot/frontend-appoinmentapp:5
resources:
{}
# limits:
# cpu: 500m
# memory: 50M
ports:
- containerPort: 80
- name: fpm
imagePullPolicy: IfNotPresent
envFrom:
- configMapRef:
name: application-config
- secretRef:
name: application-secret
securityContext:
runAsUser: 82
readOnlyRootFilesystem: true
volumeMounts:
- name: logs
mountPath: /var/www/html/storage/logs
- name: views
mountPath: /var/www/html/storage/framework/views
resources: {}
image: cehamot/backend-appoinmentapp:2
ports:
- containerPort: 9000
Pastikan nama image benar di dalam file deployment.
Buat config file untuk Service.
Berkas terakhir yang diperlukan adalah untuk membuat layanan (service) guna mengekspos aplikasi. Untuk praktik terbaik, disarankan untuk membuat layanan dan ingress. Namun, untuk contoh ini, saya akan menggunakan tipe layanan NodePort saja tanpa membuat ingress.
touch ./k8s/app_service.yaml
di bawah ini content app_service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: web-application-svc
name: web-application-svc
spec:
type: NodePort
selector:
app: web-application
ports:
- name: targetport
port: 80
protocol: TCP
targetPort: 80
nodePort: 30007
Apply k8s manifest files.
kubectl apply -f ./k8s/app_config.yaml
kubectl apply -f ./k8s/app_secret.yaml
kubectl apply -f ./k8s/app_deployment.yaml
kubectl apply -f ./k8s/app_service.yaml
check status Pods
kubectl get pod
Jika terdapat error di pod atau pod tidak running, check log pod, di bawah ini contoh log command.
kubectl logs pod <pod_Name> -c database-migrations
kubectl logs pod <pod_Name> -c nginx
kubectl logs pod <pod_Name> -c fpm
atau bisa describe pod untuk melihat keseluruhan detil untuk troubleshoot.
kubectl describe pod <pod_Name>