fix(cicd): use Kaniko Jobs for building Docker images in Gitea Actions
Some checks failed
Build & Deploy to K8s / build-and-deploy (push) Failing after 10s

- Replace docker build with Kaniko Jobs (runner has no Docker daemon)
- Add batch/jobs RBAC for act_runner to create Kaniko Jobs
- Use MinIO ExternalName pointing to existing minio namespace
- Skip build when only K8s configs changed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ho Ngoc Hai
2026-04-10 20:15:20 +07:00
parent 48bb30b009
commit 43f0c79478
3 changed files with 156 additions and 161 deletions

View File

@@ -1,8 +1,8 @@
# EN: Build & Deploy GoodGo Platform to K8s staging via Gitea Actions
# VI: Build & Deploy GoodGo Platform len K8s staging qua Gitea Actions
#
# Flow: Push → detect changed services → docker build → push Harbor → kubectl deploy
# Runner: act_runner (host mode, in-cluster kubectl)
# Flow: Push → detect changed services → Kaniko build → push Harbor → kubectl deploy
# Runner: act_runner (host mode, in-cluster kubectl + Kaniko Jobs)
name: Build & Deploy to K8s
@@ -14,7 +14,7 @@ jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Clone source from GitHub
- name: Clone source from Gitea
run: |
git clone --depth 2 --branch ${{ github.ref_name }} \
https://admin:Velik%402026@gitea.techbi.org/admin/pos-system.git .
@@ -77,61 +77,149 @@ jobs:
echo "CHANGED_SERVICES=${SERVICES}" >> $GITHUB_ENV
echo "Changed services: ${SERVICES:-none}"
- name: Build and push changed services
- name: Build and push via Kaniko
run: |
if [ -z "$CHANGED_SERVICES" ]; then
echo "No services changed, skipping build"
# Filter out __k8s_config__ to get only real services
BUILD_SERVICES=""
for svc in $CHANGED_SERVICES; do
if [ "$svc" != "__k8s_config__" ]; then
BUILD_SERVICES="${BUILD_SERVICES} ${svc}"
fi
done
if [ -z "$BUILD_SERVICES" ]; then
echo "No services to build, skipping"
exit 0
fi
HARBOR="harbor.techbi.org"
PROJECT="goodgo"
GITHUB_REPO="https://github.com/hongochai10/Microservices-Development.git"
BRANCH="${{ github.ref_name }}"
NS="staging"
echo "${{ secrets.HARBOR_PASSWORD }}" | docker login "${HARBOR}" -u "${{ secrets.HARBOR_USERNAME }}" --password-stdin
# Create Harbor docker config secret for Kaniko
kubectl create secret docker-registry kaniko-harbor-secret -n "${NS}" \
--docker-server="${HARBOR}" \
--docker-username="${{ secrets.HARBOR_USERNAME }}" \
--docker-password="${{ secrets.HARBOR_PASSWORD }}" \
--docker-email=admin@techbi.org \
--dry-run=client -o yaml | kubectl apply -f -
build_service() {
local name="$1"
local context="$2"
local image="${HARBOR}/${PROJECT}/${name}-net"
echo "=== Building ${name} ==="
docker build -t "${image}:${IMAGE_TAG}" -t "${image}:latest" "${context}"
docker push "${image}:${IMAGE_TAG}"
docker push "${image}:latest"
echo "=== ${name} pushed ==="
get_context() {
case "$1" in
iam-service) echo "services/iam-service-net" ;;
merchant-service) echo "services/merchant-service-net" ;;
order-service) echo "services/order-service-net" ;;
fnb-engine) echo "services/fnb-engine-net" ;;
catalog-service) echo "services/catalog-service-net" ;;
inventory-service) echo "services/inventory-service-net" ;;
wallet-service) echo "services/wallet-service-net" ;;
storage-service) echo "services/storage-service-net" ;;
booking-service) echo "services/booking-service-net" ;;
chat-service) echo "services/chat-service-net" ;;
social-service) echo "services/social-service-net" ;;
promotion-service) echo "services/promotion-service-net" ;;
membership-service) echo "services/membership-service-net" ;;
mining-service) echo "services/mining-service-net" ;;
mission-service) echo "services/mission-service-net" ;;
ads-manager-service) echo "services/ads-manager-service-net" ;;
ads-serving-service) echo "services/ads-serving-service-net" ;;
ads-billing-service) echo "services/ads-billing-service-net" ;;
ads-tracking-service) echo "services/ads-tracking-service-net" ;;
ads-analytics-service) echo "services/ads-analytics-service-net" ;;
mkt-facebook-service) echo "services/mkt-facebook-service-net" ;;
mkt-whatsapp-service) echo "services/mkt-whatsapp-service-net" ;;
mkt-x-service) echo "services/mkt-x-service-net" ;;
mkt-zalo-service) echo "services/mkt-zalo-service-net" ;;
pos-web) echo "apps/web-client-tpos-net" ;;
esac
}
for svc in $CHANGED_SERVICES; do
case "$svc" in
iam-service) build_service "iam-service" "./services/iam-service-net" ;;
merchant-service) build_service "merchant-service" "./services/merchant-service-net" ;;
order-service) build_service "order-service" "./services/order-service-net" ;;
fnb-engine) build_service "fnb-engine" "./services/fnb-engine-net" ;;
catalog-service) build_service "catalog-service" "./services/catalog-service-net" ;;
inventory-service) build_service "inventory-service" "./services/inventory-service-net" ;;
wallet-service) build_service "wallet-service" "./services/wallet-service-net" ;;
storage-service) build_service "storage-service" "./services/storage-service-net" ;;
booking-service) build_service "booking-service" "./services/booking-service-net" ;;
chat-service) build_service "chat-service" "./services/chat-service-net" ;;
social-service) build_service "social-service" "./services/social-service-net" ;;
promotion-service) build_service "promotion-service" "./services/promotion-service-net" ;;
membership-service) build_service "membership-service" "./services/membership-service-net" ;;
mining-service) build_service "mining-service" "./services/mining-service-net" ;;
mission-service) build_service "mission-service" "./services/mission-service-net" ;;
ads-manager-service) build_service "ads-manager-service" "./services/ads-manager-service-net" ;;
ads-serving-service) build_service "ads-serving-service" "./services/ads-serving-service-net" ;;
ads-billing-service) build_service "ads-billing-service" "./services/ads-billing-service-net" ;;
ads-tracking-service) build_service "ads-tracking-service" "./services/ads-tracking-service-net" ;;
ads-analytics-service) build_service "ads-analytics-service" "./services/ads-analytics-service-net" ;;
mkt-facebook-service) build_service "mkt-facebook-service" "./services/mkt-facebook-service-net" ;;
mkt-whatsapp-service) build_service "mkt-whatsapp-service" "./services/mkt-whatsapp-service-net" ;;
mkt-x-service) build_service "mkt-x-service" "./services/mkt-x-service-net" ;;
mkt-zalo-service) build_service "mkt-zalo-service" "./services/mkt-zalo-service-net" ;;
pos-web) build_service "web-client-tpos" "./apps/web-client-tpos-net" ;;
__k8s_config__) echo "K8s config changed, will apply manifests" ;;
get_image_name() {
case "$1" in
pos-web) echo "web-client-tpos-net" ;;
*) echo "${1}-net" ;;
esac
}
build_with_kaniko() {
local svc="$1"
local context=$(get_context "$svc")
local image_name=$(get_image_name "$svc")
local full_image="${HARBOR}/${PROJECT}/${image_name}"
local job_name="kaniko-build-$(echo ${svc} | tr '_' '-')-${IMAGE_TAG}"
echo "=== Building ${svc} via Kaniko ==="
# Create Kaniko Job
cat <<JOBEOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: ${job_name}
namespace: ${NS}
spec:
backoffLimit: 1
ttlSecondsAfterFinished: 300
template:
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:latest
args:
- "--dockerfile=Dockerfile"
- "--context=git://${GITHUB_REPO}#refs/heads/${BRANCH}#${context}"
- "--destination=${full_image}:${IMAGE_TAG}"
- "--destination=${full_image}:latest"
- "--cache=true"
- "--cache-repo=${full_image}/cache"
- "--skip-tls-verify"
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 4Gi
restartPolicy: Never
volumes:
- name: docker-config
secret:
secretName: kaniko-harbor-secret
items:
- key: .dockerconfigjson
path: config.json
JOBEOF
echo "Waiting for Kaniko job ${job_name}..."
kubectl wait --for=condition=complete job/${job_name} -n ${NS} --timeout=600s 2>/dev/null
local result=$?
if [ $result -ne 0 ]; then
echo "ERROR: Kaniko build failed for ${svc}"
kubectl logs job/${job_name} -n ${NS} --tail=30 2>/dev/null
return 1
fi
echo "=== ${svc} built and pushed ==="
}
FAILED=0
for svc in $BUILD_SERVICES; do
if ! build_with_kaniko "$svc"; then
FAILED=$((FAILED + 1))
fi
done
if [ $FAILED -gt 0 ]; then
echo "ERROR: ${FAILED} service(s) failed to build"
exit 1
fi
- name: Deploy to K8s
run: |
if [ -z "$CHANGED_SERVICES" ]; then
@@ -166,7 +254,7 @@ jobs:
kubectl set image "deployment/${name}" "${name}=${image}" -n "${NS}"
kubectl patch deployment "${name}" -n "${NS}" \
-p '{"spec":{"template":{"spec":{"imagePullSecrets":[{"name":"harbor-pull-secret"}],"containers":[{"name":"'"${name}"'","imagePullPolicy":"Always"}]}}}}'
fi
}
for svc in $CHANGED_SERVICES; do
case "$svc" in
@@ -202,11 +290,9 @@ jobs:
FAILED=0
for svc in $CHANGED_SERVICES; do
if [ "$svc" = "__k8s_config__" ]; then continue; fi
SVC_NAME="$svc"
if [ "$svc" = "pos-web" ]; then SVC_NAME="pos-web"; fi
echo "Waiting for ${SVC_NAME}..."
if ! kubectl rollout status "deployment/${SVC_NAME}" -n "${NS}" --timeout=180s 2>/dev/null; then
echo "WARNING: ${SVC_NAME} rollout did not complete"
echo "Waiting for ${svc}..."
if ! kubectl rollout status "deployment/${svc}" -n "${NS}" --timeout=180s 2>/dev/null; then
echo "WARNING: ${svc} rollout did not complete"
FAILED=$((FAILED + 1))
fi
done

View File

@@ -2,6 +2,7 @@
# VI: RBAC cho Gitea act_runner deploy vao namespace staging
#
# The act_runner ServiceAccount (in gitea namespace) needs permissions to:
# - Create Kaniko Jobs for building Docker images
# - Apply manifests (deployments, services, configmaps, secrets, ingress, hpa, networkpolicies, pvc)
# - Patch deployments (set image, rollout restart)
# - Read pods/logs (rollout status)
@@ -15,6 +16,9 @@ rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["services", "configmaps", "secrets", "persistentvolumeclaims"]
verbs: ["get", "list", "watch", "create", "update", "patch"]

View File

@@ -1,7 +1,11 @@
# EN: MinIO - S3-compatible object storage for file uploads and media
# VI: MinIO - Luu tru doi tuong tuong thich S3 cho file upload va media
apiVersion: apps/v1
kind: Deployment
# EN: MinIO ExternalName Service - Points to existing MinIO in minio namespace
# VI: MinIO ExternalName Service - Tro den MinIO hien tai trong namespace minio
#
# Existing MinIO: https://minio.techbi.org (namespace: minio)
# Console: https://minio-console.techbi.org
---
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: staging
@@ -11,88 +15,8 @@ metadata:
platform: goodgo
tier: infrastructure
spec:
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
environment: staging
spec:
containers:
- name: minio
image: minio/minio:latest
command:
- server
- /data
- "--console-address"
- ":9001"
ports:
- containerPort: 9000
protocol: TCP
- containerPort: 9001
protocol: TCP
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: goodgo-secrets
key: Storage__MinIO__AccessKey
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: goodgo-secrets
key: Storage__MinIO__SecretKey
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /minio/health/live
port: 9000
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /minio/health/ready
port: 9000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
volumeMounts:
- name: minio-data
mountPath: /data
volumes:
- name: minio-data
persistentVolumeClaim:
claimName: minio-pvc
---
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: staging
labels:
app: minio
environment: staging
spec:
selector:
app: minio
ports:
- name: minio
protocol: TCP
port: 9000
targetPort: 9000
type: ClusterIP
type: ExternalName
externalName: minio.minio.svc.cluster.local
---
apiVersion: v1
kind: Service
@@ -100,29 +24,10 @@ metadata:
name: minio-console
namespace: staging
labels:
app: minio
app: minio-console
environment: staging
platform: goodgo
tier: infrastructure
spec:
selector:
app: minio
ports:
- name: console
protocol: TCP
port: 9001
targetPort: 9001
type: ClusterIP
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-pvc
namespace: staging
labels:
app: minio
environment: staging
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
type: ExternalName
externalName: minio-console.minio.svc.cluster.local