1. บทนำ
GitLab Runner คือเครื่องมือที่รัน jobs จาก GitLab CI/CD pipeline โดยปกติแล้วเราต้องตั้งค่า Runner ด้วยตนเองและจัดการInfrastructure (VM, Container) เอง แต่เมื่อผสมผสานกับ Kubernetes Autoscaling จะทำให้ GitLab Runner สามารถปรับขนาดได้อัตโนมัติตาม workload ที่เข้ามา
ข้อดีของ GitLab Runner Autoscaling on Kubernetes:
- ลดต้นทุน: เริ่มต้นด้วย runner 0 ตัว แล้วเพิ่มขึ้นเมื่อมี jobs เข้ามา
- ประสิทธิภาพ: รองรับ job spikes อัตโนมัติโดยไม่ต้องกังวลว่าจะ runner จะหมด
- High Availability: Kubernetes ดูแล runner ให้ automatically
- Isolation: แต่ละ job รันใน pod แยก ปลอดภัยจาก side effects
Architecture (Architecture)
2. สิ่งที่ต้องเตรียม (Prerequisites)
Kubernetes Cluster
- Kubernetes cluster ที่ operational
- Kubectl ติดตั้ง และเชื่อมต่อได้
- Cluster ต้องรองรับ Persistent Volumes (สำหรับ caching)
GitLab & Helm
- GitLab self-hosted หรือ GitLab.com
- Helm 3.x ติดตั้งแล้ว
- GitLab Personal Access Token (PAT) หรือ CI/CD token
GitLab Runner Requirements
- GitLab Runner chart version 0.65.0+ (รองรับ autoscaling)
- GitLab Runner Kubernetes executor
- Service account with proper RBAC permissions
Infrastructure
- Storage class สำหรับ caching (Redis/MinIO หรือ using persistent volumes)
- Network policies ที่อนุญาตให้ runner ติดต่อกับ Kubernetes API
- Load balancer (ถ้าใช้ GitLab self-hosted)
3. การติดตั้ง GitLab Runner พร้อม Kubernetes Autoscaling
มี 2 วิธีในการติดตั้ง GitLab Runner บน Kubernetes:
- วิธีที่ 1: ใช้ Helm chart โดยตรง (แนะนำ)
- วิธีที่ 2: ใช้ GitLab Kubernetes Agent (สำหรับ GitLab 14.0+)
วิธีที่ 1: ติดตั้งด้วย Helm Chart (แนะนำ)
ขั้นตอนที่ 1: เพิ่ม GitLab Helm Repository
# เพิ่ม GitLab Helm repository
helm repo add gitlab https://charts.gitlab.io
helm repo update
# ตรวจสอบเวอร์ชันล่าสุด
helm search repo gitlab/gitlab-runner
ขั้นตอนที่ 2: สร้าง values.yaml สำหรับ autoscaling
# values.yaml - GitLab Runner dengan Kubernetes Autoscaling
gitlabUrl: https://gitlab.example.com/
runnerRegistrationToken: "YOUR_RUNNER_TOKEN"
runnerConfig: |
[[runners]]
name = "kubernetes-runner"
url = "https://gitlab.example.com/"
token = "TOKEN"
executor = "kubernetes"
[runners.kubernetes]
namespace = "gitlab-runners"
image = "alpine:latest"
[runners.kubernetes.pod_security_context]
run_as_non_root = true
[runners.kubernetes.env]
RUNNER_UPDATE_NAME = "true"
[runners.kubernetes.volumes]
{ name = "cache", mount_path = "/cache", type = "persistent_volume_claim", claim_name = "gitlab-runner-cache" }
[runners.kubernetes.node_selector]
kubernetes.io/os = "linux"
# Autoscaling configuration
[runners.autoscale]
replicas = 5
min = 0
max = 10
time_window = "90s"
scale_down_delay = "30s"
scale_down_unneeded = "2m"
scale_down_running = "2m"
[[runners.kubernetes.services]]
name = "redis"
alias = "redis-cache"
ขั้นตอนที่ 3: สร้าง Persistent Volume Claim สำหรับ Cache
# Create PVC for caching
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitlab-runner-cache
namespace: gitlab-runners
spec:
accessModes:
- ReadWriteMany
storageClassName: standard
resources:
requests:
storage: 10Gi
ขั้นตอนที่ 4: ติดตั้งด้วย Helm
# สร้าง namespace
kubectl create namespace gitlab-runners
# Deploy GitLab Runner
helm install gitlab-runner gitlab/gitlab-runner --namespace gitlab-runners -f values.yaml --wait --timeout 5m
คำอธิบาย Configuration
replicas = 5- จำนวน runner pods ที่จะสร้างเริ่มต้นmin = 0- จำนวน minimum runners (0 หมายถึง scale down ได้ถึง 0)max = 10- จำนวน maximum runners สูงสุดtime_window = "90s"- ช่วงเวลาที่จะตรวจสอบปริมาณ jobs ในการตัดสินใจ scale upscale_down_delay = "30s"- ความล่าช้าก่อน scale downscale_down_unneeded = "2m"- ระยะเวลาที่ runner ไม่ได้ใช้งานก่อน scale down
วิธีที่ 2: ใช้ GitLab Kubernetes Agent
# ขั้นตอน
# 1. เปิด GitLab Project → Settings → CI/CD → Kubernetes
# 2. Add Kubernetes cluster → Install GitLab Agent
# 3. Select your cluster
# 4. Agent will be installed with autoscaling enabled by default
วิธีนี้ง่ายกว่าและรองรับ gitOps workflows ได้ดีกว่า โดย GitLab จะจัดการ runner lifecycle ให้โดยอัตโนมัติพร้อม autoscaling
โครงสร้างหลังติดตั้ง
4. การปรับขนาดอัตโนมัติ (HPA Configuration)
GitLab Runner autoscaling ใช้ HPA (Horizontal Pod Autoscaler) ในการปรับขนาด runner pods ตามจำนวน jobs ที่ค้างอยู่ใน queue
HPA Auto-scale Logic:
# Scale-up conditions
scale_up:
if queue_depth > (replicas * 2): # ถ้า jobs ในคิว > 2x runners
create new runner pod
# Scale-down conditions
scale_down:
if runner_idle_time > 2m: # runner idle > 2 นาที
terminate runner pod
Scale Timings
- scale_up_delay: 10-30 วินาที (รอให้ jobs ค้างก่อน scale up)
- scale_down_delay: 2-5 นาที (รอให้ jobs จบก่อน scale down)
- time_window: 90 วินาที (ตรวจสอบ jobs ที่เข้ามาในช่วงนี้)
ตัวอย่าง HPA YAML Manual
# ตัวอย่าง HPA configuration
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gitlab-runner
namespace: gitlab-runners
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gitlab-runner-gitlab-runner
minReplicas: 0
maxReplicas: 10
# Scale based on GitLab queue depth
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Percent
value: 100
periodSeconds: 30
- type: Pods
value: 2
periodSeconds: 30
selectPolicy: Max
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 1
periodSeconds: 60
selectPolicy: Min
กระบวนการ Autoscaling
5. การปรับแต่งประสิทธิภาพ (Performance Optimization)
1. Custom Cache Configuration
GitLab Runner รองรับหลาย types of cache:
S3 Cache
ใช้ S3-compatible storage (MinIO, AWS S3) สำหรับ cache ที่ shared ระหว่าง runners
Google Cloud Storage
สำหรับโปรเจคที่ host บน GCP ใช้ GCS สำหรับ cache
NFS Cache
For shared cache across runners using NFS
In-Memory Cache
Redis for low-latency cache access
2. Optimized Pod Template
# Optimized Pod Template
apiVersion: v1
kind: Pod
metadata:
name: gitlab-runner-pod-template
namespace: gitlab-runners
labels:
app: gitlab-runner
spec:
# Security Context
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# Prefer low-latency nodes
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: node-type
operator: In
values: ["compute-optimized"]
# Containers
containers:
- name: gitlab-runner
image: gitlab/gitlab-runner:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Resource limits
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "2Gi"
cpu: "1000m"
# Environment
env:
- name:-ci-runner
value: "true"
# Volume mounts
volumeMounts:
- name: cache
mountPath: /cache
- name: config
mountPath: /etc/gitlab-runner
readOnly: true
# Volumes
volumes:
- name: cache
emptyDir: {}
- name: config
configMap:
name: gitlab-runner-config
3. Advanced Autoscaling Settings
# Advanced HPA with custom metrics
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: gitlab-runner-advanced
namespace: gitlab-runners
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: gitlab-runner-gitlab-runner
minReplicas: 0
maxReplicas: 20
behavior:
# Fast scale-up for burst jobs
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Pods
value: 4
periodSeconds: 60
- type: Percent
value: 100
periodSeconds: 60
selectPolicy: Max
# Conservative scale-down
scaleDown:
stabilizationWindowSeconds: 300 # 5 minutes
policies:
- type: Percent
value: 25
periodSeconds: 60
- type: Pods
value: 2
periodSeconds: 60
selectPolicy: Min
# Metrics based on GitLab job queue
metrics:
- type: Pods
pods:
metric:
name: gitlab_jobs_pending
target:
type: AverageValue
averageValue: "1"
6. ตัวอย่างโค้ด (Code Examples)
1. GitLab CI/CD Pipeline ตัวอย่าง
# .gitlab-ci.yml - Pipeline ตัวอย่างสำหรับใช้กับ Autoscaling Runner
stages:
- build
- test
- security
- deploy
variables:
# Use Helm to deploy
DEPLOYMENT_STRATEGY: helm
# Enable debug logging
CI_DEBUG_TRACE: "false"
# Cache settings
CACHE_KEY: "\${CI_COMMIT_REF_NAME}"
DOCKER_TLS_CERTDIR: "/certs"
# Build stage
build:
stage: build
image: docker:24.0
services:
- docker:24.0-dind
script:
- docker build -t \${CI_REGISTRY_IMAGE}:\${CI_COMMIT_SHA} .
- docker push \${CI_REGISTRY_IMAGE}:\${CI_COMMIT_SHA}
artifacts:
paths:
- docker-image.tar
expire_in: 1 hour
# Test stage
test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm test
cache:
key: "\${CI_COMMIT_REF_NAME}"
paths:
- node_modules/
- .npm/
# Security scan
security-scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image \${CI_REGISTRY_IMAGE}:\${CI_COMMIT_SHA}
allow_failure: true
# Deploy to Kubernetes
deploy:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/my-app my-app=\${CI_REGISTRY_IMAGE}:\${CI_COMMIT_SHA}
- kubectl rollout status deployment/my-app
environment:
name: production
url: https://app.example.com
only:
- main
2. GitLab Runner Deployment YAML
# GitLab Runner Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab-runner
namespace: gitlab-runners
labels:
app: gitlab-runner
spec:
replicas: 1
selector:
matchLabels:
app: gitlab-runner
template:
metadata:
labels:
app: gitlab-runner
spec:
serviceAccountName: gitlab-runner
securityContext:
runAsNonRoot: true
fsGroup: 65534
containers:
- name: gitlab-runner
image: gitlab/gitlab-runner:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
env:
- name: CI_SERVER_URL
value: "https://gitlab.example.com/"
- name: REGISTRATION_TOKEN
valueFrom:
secretKeyRef:
name: gitlab-runner-token
key: registration-token
- name: RUNNER_EXECUTOR
value: "kubernetes"
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "1Gi"
volumeMounts:
- name: config
mountPath: /etc/gitlab-runner
- name: cache
mountPath: /cache
- name: docker-config
mountPath: /certs/client
volumes:
- name: config
configMap:
name: gitlab-runner-config
- name: cache
emptyDir: {}
- name: docker-config
emptyDir: {}
3. Common Jobs Troubleshooting
# ตัวอย่าง Jobs ที่พบบ่อยและวิธีแก้ไข
# ปัญหา: Runner ไม่ scale up
# แก้ไข: เพิ่ม time_window และ scale_up_delay
[runners.autoscale]
time_window = "120s" # เพิ่มเวลาตรวจสอบ jobs
scale_up_delay = "30s" # Delay ก่อน scale up
# ปัญหา: Jobs ค้างใน queue
# แก้ไข: ตรวจสอบ resources ของ runner pods
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "512Mi"
cpu: "500m"
# ปัญหา: Cache miss บ่อย
# แก้ไข: ใช้ S3 cache แทน default
[runners.cache]
Type = "s3"
Path = "gitlab-runner/cache"
Shared = true
[runners.cache.s3]
ServerAddress = "s3.amazonaws.com"
AccessKey = "YOUR_ACCESS_KEY"
SecretKey = "YOUR_SECRET_KEY"
BucketName = "gitlab-cache"
7. แก้ไขปัญหาที่พบบ่อย (Troubleshooting)
ปัญหา: Runner ไม่ scale up
Symptoms: Jobs ค้างใน queue แต่ runner pods ไม่เพิ่มขึ้น
Root Cause:
- HPA configuration ผิด
- scale_up_delay ตั้งค่าสูงเกินไป
- max ต่ำเกินไป
Solution:
- ตรวจสอบ HPA:
kubectl get hpa -n gitlab-runners - ลด scale_up_delay:
scale_up_delay = "10s" - เพิ่ม max runners:
max = 20
ปัญหา: Jobs ค้างใน queue
Symptoms: Jobs ไม่รันแม้จะมี runner อยู่
Root Cause:
- Runner pods ไม่พร้อมใช้งาน (CrashLoopBackOff)
- RBAC permissions ผิด
- Kubernetes executor configuration ผิด
Solution:
- ตรวจสอบ logs:
kubectl logs -n gitlab-runners -l app=gitlab-runner - ตรวจสอบ pods:
kubectl get pods -n gitlab-runners - ตรวจสอบ RBAC:
kubectl auth can-i create pods --as=system:serviceaccount:gitlab-runners:gitlab-runner
ปัญหา: Out of Memory
Symptoms: Runner pods CrashLoopBackOff หรือ OOMKilled
Root Cause:
- Resource limits ต่ำเกินไป
- Jobs ต้องการ memory มาก
- No memory request ตั้งค่า
Solution:
- เพิ่ม memory requests:
requests.memory: "512Mi" - เพิ่ม memory limits:
limits.memory: "2Gi" - ใช้ smaller image:
image: alpine:latest
ปัญหา: Connection timeout
Symptoms: Runner ไม่สามารถเชื่อมต่อ GitLab server
Root Cause:
- Network policies บล็อค traffic
- Firewall rules
- DNS resolution issues
Solution:
- ตรวจสอบ network policy:
kubectl get networkpolicy -n gitlab-runners - ตรวจสอบ connectivity:
kubectl run test --image=curlimages/curl -it --rm -- curl -v https://gitlab.example.com - ตรวจสอบ DNS:
kubectl exec -it -n gitlab-runners -l app=gitlab-runner -- nslookup gitlab.example.com
Diagnostic Commands
# 1. Check HPA status
kubectl get hpa -n gitlab-runners
kubectl describe hpa gitlab-runner -n gitlab-runners
# 2. Check runner pods
kubectl get pods -n gitlab-runners -l app=gitlab-runner
kubectl logs -n gitlab-runners -l app=gitlab-runner --tail=100
# 3. Check deployments
kubectl get deployments -n gitlab-runners
kubectl describe deployment gitlab-runner -n gitlab-runners
# 4. Check services
kubectl get services -n gitlab-runners
kubectl describe service gitlab-runner -n gitlab-runners
# 5. Check secrets
kubectl get secrets -n gitlab-runners
kubectl get secret gitlab-runner-token -n gitlab-runners -o yaml
# 6. Test connectivity
kubectl run test-runner --image=alpine:latest -it --rm -- wget -qO- https://gitlab.example.com/api/v4/version
# 7. Check events
kubectl get events -n gitlab-runners --sort-by='.lastTimestamp'
# 8. Check PVC
kubectl get pvc -n gitlab-runners
kubectl describe pvc gitlab-runner-cache -n gitlab-runners