deploy
Deploy dev environment to kubernetes cluster / Build and push image (push) Has been cancelled Details
Deploy dev environment to kubernetes cluster / Deploy to kubernetes cluster (push) Has been cancelled Details

This commit is contained in:
MuhammadReza 2025-02-03 10:57:42 +07:00
parent 2e8379c1c2
commit 60c24c13aa
5 changed files with 5147 additions and 0 deletions

View File

@ -0,0 +1,80 @@
name: Deploy dev environment to kubernetes cluster
on:
push:
branches:
- main
jobs:
build:
name: Build and push image
runs-on: ubuntu-latest
container:
image: ghcr.io/catthehacker/ubuntu:act-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Get composer.json content
id: get-version
uses: ActionsTools/read-json-action@main
with:
file_path: "composer.json"
- name: Get short SHA
id: get-short-sha
run: |
id=$(echo ${{ github.sha }} | cut -c 1-7)
echo "::set-output name=id::$id"
- name: Login to registry
uses: docker/login-action@v3
with:
registry: git.winteraccess.id
username: ${{ vars.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
- name: Build and push to registry
uses: docker/build-push-action@v5
with:
context: .
file: deploy/docker/Dockerfile
push: true
tags: |
git.winteraccess.id/${{ gitea.repository }}:${{ steps.get-version.outputs.version }}main${{ steps.get-short-sha.outputs.id }}
deploy:
name: Deploy to kubernetes cluster
runs-on: ubuntu-latest
needs:
- build
container:
image: ghcr.io/catthehacker/ubuntu:act-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- name: Get composer.json content
id: get-version
uses: ActionsTools/read-json-action@main
with:
file_path: "composer.json"
- name: Get short SHA
id: get-short-sha
run: |
id=$(echo ${{ github.sha }} | cut -c 1-7)
echo "::set-output name=id::$id"
- name: Create Kubeconfig
run: |
mkdir $HOME/.kube
echo "${{ secrets.KUBECONFIG }}" > $HOME/.kube/config
- name: Setup Kubectl
uses: azure/setup-kubectl@v4
- name: Set the Kubernetes context
uses: azure/k8s-set-context@v2
with:
method: service-account
k8s-url: ${{ vars.KUBE_URL }}
k8s-secret: ${{ secrets.KUBE_SECRET }}
- name: Deploy to the Kubernetes cluster
uses: azure/k8s-deploy@v5
with:
action: deploy
namespace: internship-pos-live
manifests: |
deploy/kubernetes/live.yaml
images: |
git.winteraccess.id/${{ gitea.repository }}:${{ steps.get-version.outputs.version }}main${{ steps.get-short-sha.outputs.id }}

167
deploy/docker/Dockerfile Normal file
View File

@ -0,0 +1,167 @@
######
# STAGE 1: GET REQUIRED PHP PACKAGES
######
FROM git.winteraccess.id/docker/composer:2.7.2-ubuntu-php8.0 AS php_build
LABEL maintainer="<Muhamad Aditya Prima> aprimediet@gmail.com"
WORKDIR /build
# ADD COMPOSER file
ADD composer* ./
# INSTALL REQUIRED PHP EXT
RUN --mount=type=cache,target=/var/cache/apt/archives \
apt -y update && apt -y upgrade && apt -y install \
php${PHP_VERSION}-curl php${PHP_VERSION}-dom php${PHP_VERSION}-xml \
php${PHP_VERSION}-gd php${PHP_VERSION}-zip
# RUN COMPOSER UPDATE FIRST
RUN --mount=type=cache,target=${COMPOSER_CACHE_DIR} \
composer update -n --prefer-dist \
--no-install --no-autoloader --no-scripts; exit 0
# RUN COMPOSER INSTALL
RUN --mount=type=cache,target=${COMPOSER_CACHE_DIR} \
composer install -n --prefer-dist \
--no-autoloader --no-scripts
###### END OF STAGE 1
######
# STAGE 2: GET REQUIRED NODEJS PACKAGES
######
FROM git.winteraccess.id/docker/nodejs:18-alpine AS static_build
LABEL maintainer="<Muhamad Aditya Prima> aprimediet@gmail.com"
WORKDIR /build
# ADD REQUIRED NODE FILES
ADD . .
# INSTALL REQUIRED NODE PACKAGES
RUN --mount=type=cache,target=${YARN_CACHE_DIR} \
yarn install --non-interactive
# BUILD ASSETS THROUGH VITE
# RUN --mount=type=cache,target=${YARN_CACHE_DIR} \
# yarn build
###### END OF STAGE 2
######
# STAGE 3: GET WKHTMLTOPDF FOR ALPINE
######
FROM ghcr.io/surnet/alpine-wkhtmltopdf:3.19.0-0.12.6-full AS wkhtmltopdf
###### END OF STAGE 3
######
# STAGE 4: Build Runtime Container
######
FROM git.winteraccess.id/docker/php:8.0-apache-alpine AS runtime
LABEL maintainer="<Muhamad Aditya Prima> aprimediet@gmail.com"
ARG BUILD_ENV=dev
# SET WORKDIR
WORKDIR /app/htdocs
# UPDATE AND UPGRADE PACKAGES FIRST
RUN apk update && apk upgrade
# INSTALL REQUIRED PHP EXT
RUN --mount=type=cache,target=/var/cache/apk \
apk add \
php${PHP_VERSION}-pdo php${PHP_VERSION}-session php${PHP_VERSION}-fileinfo \
php${PHP_VERSION}-curl php${PHP_VERSION}-tokenizer php${PHP_VERSION}-dom \
php${PHP_VERSION}-gd php${PHP_VERSION}-xml php${PHP_VERSION}-xmlreader \
php${PHP_VERSION}-xmlwriter php${PHP_VERSION}-zip php${PHP_VERSION}-simplexml \
php${PHP_VERSION}-mbstring php${PHP_VERSION}-pgsql php${PHP_VERSION}-pdo_pgsql \
fcgi
# INSTALL PHP OpenSSL
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-openssl; exit 0
# Install php83-openssl from testing if main or community doesn't exists
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-openssl@testing; exit 0
# INSTALL PHP PHAR
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-phar; exit 0
# Install php83-phar from testing if main or community doesn't exists
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-phar@testing; exit 0
# Install php83-json
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-json; exit 0
# Install php83-json from testing if main or community doesn't exists
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-json@testing; exit 0
# Install php83-iconv
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-iconv; exit 0
# Install php83-iconv from testing if main or community doesn't exists
RUN --mount=type=cache,target=/var/cache/apk \
apk add php${PHP_VERSION}-iconv@testing; exit 0
# INSTALL REQUIRED WKHTMLTOPDF DEPENDENCIES
RUN --mount=type=cache,target=/var/cache/apk \
apk add libstdc++ libx11 libxrender libxext \
libssl1.1 ca-certificates fontconfig \
freetype ttf-droid ttf-freefont ttf-liberation
# Reconfigure apache mod
RUN sed -i "s|#LoadModule rewrite_module|LoadModule rewrite_module|" /etc/apache2/httpd.conf && \
sed -i "s|#LoadModule deflate_module|LoadModule deflate_module|" /etc/apache2/httpd.conf && \
sed -i "s|#LoadModule expires_module|LoadModule expires_module|" /etc/apache2/httpd.conf && \
sed -i "s|DocumentRoot \"/app/htdocs\"|DocumentRoot \"/app/htdocs/public\"|" /etc/apache2/httpd.conf && \
sed -i "s|Directory \"/app/htdocs\"|Directory \"/app/htdocs/public\"|" /etc/apache2/httpd.conf
# COPYING REQUIRED FILES
ADD ./deploy/docker/scripts/initialize /scripts/initialize
ADD . .
RUN chmod +x /scripts/* && \
mv ./public ./public-tmp
# COPY BUILD FILES
COPY --from=php_build /build/vendor ./vendor
COPY --from=php_build /build/composer.lock ./composer.lock
COPY --from=php_build /usr/local/bin/composer /usr/local/bin/composer
COPY --from=static_build /build/node_modules ./node_modules
# COPY --from=static_build /build/public/build ./public/build
COPY --from=wkhtmltopdf /bin/wkhtmltopdf /bin/libwkhtmltox.so /bin/wkhtmltoimage /bin/
# REMOVE BOOTSTRAP CACHE
RUN rm -rf ./bootstrap/cache/*
# CLEAR CACHE AND DEPLOY FILES
RUN rm -vrf /var/cache/apk/* && rm -rf ./deploy
# RUN DUMP AUTOLOAD && GENERATE APP_KEY
# TO ENSURE APP_KEY IS IDENTICAL BETWEEN ALL
# CONTAINERS
RUN composer dump-autoload
RUN ln -s /var/log/apache2 /app/logs && \
ln -s /usr/lib/apache2 /app/modules && \
ln -s /run/apache2 /app/run
EXPOSE 80
###### END OF STAGE 4

View File

@ -0,0 +1,59 @@
#!/bin/sh
echo ""
echo "-----------------------------------"
echo " Ensuring storage directory exists "
echo "-----------------------------------"
echo ""
mkdir -p ${APP_DIR}/storage/app
mkdir -p ${APP_DIR}/storage/logs
mkdir -p ${APP_DIR}/storage/framework/cache
mkdir -p ${APP_DIR}/storage/framework/views
mkdir -p ${APP_DIR}/storage/framework/sessions
echo ""
echo "------------------------------"
echo " Fixing Directory Permissions "
echo "------------------------------"
echo ""
chown -R apache:apache ${APP_DIR}/storage
chmod -R 755 ${APP_DIR}/storage/app
chmod -R 755 ${APP_DIR}/storage/logs
chmod -R 755 ${APP_DIR}/storage/framework/cache
chmod -R 755 ${APP_DIR}/storage/framework/views
chmod -R 755 ${APP_DIR}/storage/framework/sessions
echo ""
echo "------------------------------"
echo " Moving public-tmp to public "
echo "------------------------------"
echo ""
rm -rf ${APP_DIR}/public/*
chown -R apache:apache ${APP_DIR}/public/*
cp -vR ${APP_DIR}/public-tmp/* ${APP_DIR}/public/
cp -v ${APP_DIR}/public-tmp/.htaccess ${APP_DIR}/public/
echo ""
echo "------------------------------"
echo " Optimizing Laravel "
echo "------------------------------"
echo ""
composer dump-autoload
php ${APP_DIR}/artisan optimize:clear
php ${APP_DIR}/artisan optimize
php ${APP_DIR}/artisan route:cache
php ${APP_DIR}/artisan view:cache
php ${APP_DIR}/artisan config:clear
php ${APP_DIR}/artisan storage:link
echo ""
echo "------------------------------"
echo " Initialization done "
echo "------------------------------"
echo ""
exit 0

374
deploy/kubernetes/live.yaml Normal file
View File

@ -0,0 +1,374 @@
apiVersion: v1
kind: Secret
metadata:
name: dashboard-pos-system-secret
namespace: internship-pos-live
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
type: Opaque
data:
DB_PASSWORD: QVZOU18tV2VPd1o2OC03RlJRNHROUURj
---
apiVersion: v1
kind: ConfigMap
metadata:
name: dashboard-pos-system-config
namespace: internship-pos-live
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
data:
".env": |
APP_NAME="POS-System"
APP_ENV=local
APP_KEY=base64:dOICXdX0z7CSglOXUJQgm7LpFNDNu4ffrOTpY612cGM=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=https://admin-pos-system.winteraccess.id
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
PHP_CLI_SERVER_WORKERS=4
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=pgsql
DB_HOST=pg-3079c6fb-ariqakbar-48ad.l.aivencloud.com
DB_PORT=11504
DB_DATABASE=pos_system
DB_USERNAME=avnadmin
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
CACHE_PREFIX=
MEMCACHED_HOST=127.0.0.1
REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
volume.alpha.kubernetes.io/storage-class: generic
volume.beta.kubernetes.io/storage-provisioner: cluster.local/nfs-nfs-subdir-external-provisioner
volume.kubernetes.io/storage-provisioner: cluster.local/nfs-nfs-subdir-external-provisioner
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
name: dashboard-pos-system-storage
namespace: internship-pos-live
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs
volumeMode: Filesystem
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
volume.alpha.kubernetes.io/storage-class: generic
volume.beta.kubernetes.io/storage-provisioner: cluster.local/nfs-nfs-subdir-external-provisioner
volume.kubernetes.io/storage-provisioner: cluster.local/nfs-nfs-subdir-external-provisioner
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
name: dashboard-pos-system-public
namespace: internship-pos-live
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs
volumeMode: Filesystem
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dashboard-pos-system
namespace: internship-pos-live
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
spec:
progressDeadlineSeconds: 1200
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
template:
metadata:
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
topologyKey: "kubernetes.io/hostname"
containers:
- name: web
image: git.winteraccess.id/internship/pos-system:main
imagePullPolicy: Always
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: dashboard-pos-system-secret
key: DB_PASSWORD
resources:
limits:
cpu: "500m"
memory: 1024M
requests:
cpu: "100m"
memory: 512M
ports:
- containerPort: 80
name: http
protocol: TCP
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
add: ["NET_ADMIN", "SYS_TIME"]
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /app/logs
name: logs
- mountPath: /run/apache2
name: run
- mountPath: /tmp
name: tmp
- mountPath: /app/htdocs/.env
subPath: '.env'
name: config
- mountPath: /app/htdocs/bootstrap/cache
name: cache
- mountPath: /app/htdocs/storage
name: storage
- mountPath: /app/htdocs/public
name: public
initContainers:
- name: init
image: git.winteraccess.id/internship/pos-system:main
imagePullPolicy: Always
command: ["/scripts/initialize"]
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: dashboard-pos-system-secret
key: DB_PASSWORD
resources:
limits:
cpu: "500m"
memory: 1024M
requests:
cpu: "100m"
memory: 512M
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
add: ["NET_ADMIN", "SYS_TIME"]
readOnlyRootFilesystem: true
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /app/logs
name: logs
- mountPath: /run/apache2
name: run
- mountPath: /tmp
name: tmp
- mountPath: /app/htdocs/.env
subPath: '.env'
name: config
- mountPath: /app/htdocs/bootstrap/cache
name: cache
- mountPath: /app/htdocs/storage
name: storage
- mountPath: /app/htdocs/public
name: public
imagePullSecrets:
- name: winter-registry
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: cache
emptyDir: {}
- name: run
emptyDir: {}
- name: logs
emptyDir: {}
- name: tmp
emptyDir: {}
- name: config
configMap:
name: dashboard-pos-system-config
- name: public
persistentVolumeClaim:
claimName: dashboard-pos-system-public
- name: storage
persistentVolumeClaim:
claimName: dashboard-pos-system-storage
---
apiVersion: v1
kind: Service
metadata:
name: dashboard-pos-system
namespace: internship-pos-live
annotations:
traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
traefik.ingress.kubernetes.io/service.sticky.cookie.name: "dashboard-pos-system"
traefik.ingress.kubernetes.io/service.sticky.cookie.secure: "true"
traefik.ingress.kubernetes.io/service.sticky.cookie.samesite: "none"
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
selector:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
sessionAffinity: None
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: traefik
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: default-https-redirect@kubernetescrd
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
name: dashboard-pos-system-http
namespace: internship-pos-live
spec:
ingressClassName: traefik
rules:
- host: admin-pos-system.winteraccess.id
http:
paths:
- backend:
service:
name: dashboard-pos-system
port:
number: 80
path: /
pathType: Prefix
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-production
kubernetes.io/ingress.class: traefik
traefik.ingress.kubernetes.io/router.entrypoints: websecure
labels:
app.kubernetes.io/instance: dashboard-pos-system
app.kubernetes.io/name: dashboard-pos-system
io.portainer.kubernetes.application.name: dashboard-pos-system
io.portainer.kubernetes.application.owner: admin
name: dashboard-pos-system-https
namespace: internship-pos-live
spec:
ingressClassName: traefik
rules:
- host: admin-pos-system.winteraccess.id
http:
paths:
- backend:
service:
name: dashboard-pos-system
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- admin-pos-system.winteraccess.id
secretName: dashboard-pos-system-tls

4467
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff