Instalasi Gitea di Kubernetes
Pengenalan
Panduan ini menjelaskan cara deploy Gitea di Kubernetes cluster menggunakan manifests yang production-ready. Gitea adalah self-hosted Git service yang ringan dan powerful, sangat cocok untuk deployment di Kubernetes dengan high availability dan scalability.
Mengapa Gitea di Kubernetes?
Keuntungan Deployment Kubernetes
- 🚀 Scalability - Easy horizontal scaling
- 🔄 High Availability - Multi-replica deployment
- 📦 Container Native - Optimized untuk container workloads
- 🔧 Easy Management - Declarative configuration
- 💾 Persistent Storage - StatefulSet dengan PVC
- 🌐 Service Discovery - Built-in DNS dan load balancing
- 🔒 Security - Network policies dan RBAC
Prerequisites
Cluster Requirements
- Kubernetes: v1.24+ (K3s, K8s, atau managed Kubernetes)
- kubectl: Configured dan connected ke cluster
- Storage Class: Default atau custom storage class tersedia
- Ingress Controller: Nginx, Traefik, atau Envoy Gateway
- Cert-Manager: Untuk SSL/TLS certificates (optional)
External Dependencies
- PostgreSQL Database: Production-grade database
- Host: Accessible dari cluster
- Database: Created dan ready
- User: Dengan proper permissions
- DNS: Domain name untuk akses external
Resource Requirements
Minimum untuk Gitea:
- CPU: 512m request, 1 core limit
- Memory: 1Gi request, 2Gi limit
- Storage: 10Gi untuk repositories dan data
Arsitektur Deployment
┌─────────────────────────────────────────────────────────┐
│ Internet Traffic │
└────────────────────┬────────────────────────────────────┘
│ HTTPS (443)
▼
┌─────────────────────────────────────────────────────────┐
│ Ingress / Gateway │
│ (Nginx / Envoy / Traefik) │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Gitea Service (ClusterIP) │
│ Port: 3000 │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Gitea StatefulSet (Pod) │
│ ┌──────────────────────────────────────────────┐ │
│ │ Container: gitea/gitea:latest │ │
│ │ - HTTP Port: 3000 │ │
│ │ - SSH Port: 22 │ │
│ │ - Volume: /data (10Gi PVC) │ │
│ └──────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ PostgreSQL Database │
│ Host: postgres.namespace:5432 │
└─────────────────────────────────────────────────────────┘
Installation Steps
Step 1: Persiapan Namespace
Buat dedicated namespace untuk Gitea:
# Create namespace
kubectl create namespace gitea
# Or using manifest
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: gitea
labels:
name: gitea
app: gitea
EOF
Step 2: Setup Database Secret
Buat secret untuk PostgreSQL credentials:
# Create secret from literals
kubectl create secret generic postgres-secret \
--from-literal=POSTGRES_DB=gitea \
--from-literal=POSTGRES_USER=gitea \
--from-literal=POSTGRES_PASSWORD=your_secure_password \
-n gitea
# Verify secret
kubectl get secret postgres-secret -n gitea
Atau menggunakan manifest:
# postgres-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: postgres-secret
namespace: gitea
type: Opaque
stringData:
POSTGRES_DB: gitea
POSTGRES_USER: gitea
POSTGRES_PASSWORD: your_secure_password
Apply:
kubectl apply -f postgres-secret.yaml
Step 3: Persistent Storage
Buat PersistentVolumeClaim untuk Gitea data:
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitea-data
namespace: gitea
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path # Adjust ke storage class Anda
Apply:
kubectl apply -f pvc.yaml
# Verify PVC status
kubectl get pvc -n gitea
Step 4: Deploy Gitea StatefulSet
Buat StatefulSet untuk Gitea:
# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: gitea
namespace: gitea
labels:
app: gitea
spec:
serviceName: gitea
replicas: 1
selector:
matchLabels:
app: gitea
template:
metadata:
labels:
app: gitea
spec:
containers:
- name: gitea
image: gitea/gitea:1.21.5 # Use specific version
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 3000
protocol: TCP
- name: ssh
containerPort: 22
protocol: TCP
env:
# User/Group ID
- name: USER_UID
value: "1000"
- name: USER_GID
value: "1000"
# Database configuration
- name: GITEA__database__DB_TYPE
value: postgres
- name: GITEA__database__HOST
value: postgres.gitea:5432 # Adjust ke PostgreSQL service
- name: GITEA__database__NAME
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_DB
- name: GITEA__database__USER
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_USER
- name: GITEA__database__PASSWD
valueFrom:
secretKeyRef:
name: postgres-secret
key: POSTGRES_PASSWORD
# Server configuration
- name: GITEA__server__DOMAIN
value: git.example.com # Your domain
- name: GITEA__server__ROOT_URL
value: https://git.example.com
- name: GITEA__server__HTTP_PORT
value: "3000"
- name: GITEA__server__SSH_PORT
value: "22"
volumeMounts:
- name: data
mountPath: /data
resources:
requests:
cpu: 512m
memory: 1Gi
limits:
cpu: 1
memory: 2Gi
livenessProbe:
httpGet:
path: /api/healthz
port: http
initialDelaySeconds: 120
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/healthz
port: http
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path
Apply:
kubectl apply -f statefulset.yaml
# Watch pod creation
kubectl get pods -n gitea -w
# Check logs
kubectl logs -n gitea -l app=gitea -f
Step 5: Create Service
Buat Service untuk internal access:
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: gitea
namespace: gitea
labels:
app: gitea
spec:
type: ClusterIP
ports:
- name: http
protocol: TCP
port: 3000
targetPort: 3000
- name: ssh
protocol: TCP
port: 22
targetPort: 22
selector:
app: gitea
Apply:
kubectl apply -f service.yaml
# Verify service
kubectl get svc -n gitea
Step 6: Configure Ingress
Option A: Nginx Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitea
namespace: gitea
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "500m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
spec:
tls:
- hosts:
- git.example.com
secretName: gitea-tls
rules:
- host: git.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitea
port:
number: 3000
Option B: Gateway API (Envoy)
# httproute.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: gitea
namespace: gitea
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: envoy-gateway
namespace: staging
hostnames:
- git.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: ''
kind: Service
name: gitea
port: 3000
weight: 1
timeouts:
request: 360s
Apply:
kubectl apply -f ingress.yaml
# or
kubectl apply -f httproute.yaml
# Verify
kubectl get ingress -n gitea
# or
kubectl get httproute -n gitea
Step 7: SSL Certificate (Cert-Manager)
Jika menggunakan Cert-Manager:
# certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: gitea-cert
namespace: gitea
spec:
secretName: gitea-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- git.example.com
Apply:
kubectl apply -f certificate.yaml
# Check certificate status
kubectl get certificate -n gitea
kubectl describe certificate gitea-cert -n gitea
Verification
Check Deployment Status
# Check all resources
kubectl get all -n gitea
# Check StatefulSet
kubectl get statefulset -n gitea
kubectl describe statefulset gitea -n gitea
# Check pods
kubectl get pods -n gitea
kubectl logs -n gitea gitea-0
# Check PVC
kubectl get pvc -n gitea
# Check services
kubectl get svc -n gitea
# Check ingress
kubectl get ingress -n gitea
Test Internal Access
# Port forward untuk testing
kubectl port-forward -n gitea svc/gitea 3000:3000
# Access di browser
# http://localhost:3000
Test External Access
# Check DNS resolution
nslookup git.example.com
# Test HTTPS
curl -I https://git.example.com
# Access di browser
# https://git.example.com
Initial Configuration
First Time Setup
-
Access Gitea URL:
https://git.example.com -
Initial Configuration Form:
Database Type: PostgreSQL
Host: postgres.gitea:5432
Username: gitea
Password: [from secret]
Database Name: gitea
Site Title: Your Organization Git
Repository Root Path: /data/git/repositories
LFS Root Path: /data/git/lfs
Run As User: git
Server Domain: git.example.com
SSH Server Port: 22
HTTP Port: 3000
Application URL: https://git.example.com -
Administrator Account:
Username: admin
Password: [strong password]
Email: admin@example.com -
Click "Install Gitea"
Configuration Management
Update Configuration via ConfigMap
# gitea-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-config
namespace: gitea
data:
app.ini: |
[server]
DOMAIN = git.example.com
ROOT_URL = https://git.example.com
HTTP_PORT = 3000
DISABLE_SSH = false
SSH_PORT = 22
[database]
DB_TYPE = postgres
HOST = postgres.gitea:5432
[security]
INSTALL_LOCK = true
[service]
DISABLE_REGISTRATION = true
REQUIRE_SIGNIN_VIEW = false
[repository]
ROOT = /data/git/repositories
Mount ConfigMap in StatefulSet:
volumeMounts:
- name: config
mountPath: /data/gitea/conf
volumes:
- name: config
configMap:
name: gitea-config
Scaling Considerations
Single Replica (Current)
- Pros: Simple, no synchronization issues
- Cons: No high availability, downtime during updates
Multi-Replica (Advanced)
Untuk multi-replica deployment:
spec:
replicas: 3 # Multiple replicas
Requirements:
- ReadWriteMany storage (NFS, CephFS, etc.)
- Session persistence (Redis/Memcached)
- Database connection pooling
- Load balancer configuration
Backup Strategy
Backup Script
#!/bin/bash
# backup-gitea.sh
NAMESPACE="gitea"
POD_NAME=$(kubectl get pod -n $NAMESPACE -l app=gitea -o jsonpath='{.items[0].metadata.name}')
BACKUP_DIR="/backup/gitea/$(date +%Y%m%d-%H%M%S)"
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup data volume
kubectl exec -n $NAMESPACE $POD_NAME -- tar czf - /data > $BACKUP_DIR/data.tar.gz
# Backup database
kubectl exec -n $NAMESPACE $POD_NAME -- pg_dump -h postgres.gitea -U gitea gitea > $BACKUP_DIR/database.sql
echo "Backup completed: $BACKUP_DIR"
Automated Backup with CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: gitea-backup
namespace: gitea
spec:
schedule: "0 2 * * *" # Daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: bitnami/kubectl:latest
command:
- /bin/bash
- -c
- |
# Backup script here
restartPolicy: OnFailure
Troubleshooting
Pod Not Starting
# Check pod status
kubectl get pods -n gitea
# Check events
kubectl describe pod -n gitea gitea-0
# Check logs
kubectl logs -n gitea gitea-0
# Common issues:
# - PVC not bound
# - Secret not found
# - Database connection failed
Database Connection Issues
# Test database connectivity
kubectl run -it --rm debug --image=postgres:15 --restart=Never -- \
psql -h postgres.gitea -U gitea -d gitea
# Check secret values
kubectl get secret postgres-secret -n gitea -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d
Storage Issues
# Check PVC status
kubectl get pvc -n gitea
# Check PV
kubectl get pv
# Check storage class
kubectl get storageclass
# Describe PVC for events
kubectl describe pvc gitea-data -n gitea
Ingress Not Working
# Check ingress
kubectl describe ingress gitea -n gitea
# Check ingress controller logs
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller
# Test internal service
kubectl run -it --rm debug --image=curlimages/curl --restart=Never -- \
curl http://gitea.gitea:3000
Security Best Practices
1. Use Specific Image Versions
image: gitea/gitea:1.21.5 # Not :latest
2. Non-Root User
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
3. Network Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: gitea-netpol
namespace: gitea
spec:
podSelector:
matchLabels:
app: gitea
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 3000
egress:
- to:
- namespaceSelector:
matchLabels:
name: gitea
ports:
- protocol: TCP
port: 5432
4. Resource Limits
Always set resource limits untuk prevent resource exhaustion.
5. Regular Updates
# Update to new version
kubectl set image statefulset/gitea gitea=gitea/gitea:1.21.6 -n gitea
# Monitor rollout
kubectl rollout status statefulset/gitea -n gitea
Monitoring
Add Prometheus Annotations
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
ServiceMonitor for Prometheus Operator
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: gitea
namespace: gitea
spec:
selector:
matchLabels:
app: gitea
endpoints:
- port: http
path: /metrics
interval: 30s
Next Steps
Setelah Gitea berhasil di-deploy:
- ✅ Gitea Usage Guide - Learn basic operations
- ✅ Setup Gitea Runner - Configure CI/CD runner
- ✅ CI/CD Implementation - Create pipelines
- ✅ Best Practices - Production guidelines
Congratulations! Gitea Anda sudah running di Kubernetes! 🎉