Files
infrastructure/servers/gcp/k8s/gitea-runner/deployment.yaml
T
Will Anderson 9a368986b2 fix gitea-runner deployment for GKE Autopilot constraints
Switch to host-mode labels and document the DinD limitation: Autopilot's
Warden blocks privileged containers at the node level. Use internal Gitea
service URL (gitea.gitea.svc.cluster.local) to bypass Cloudflare Access.
Document three resolution paths (Standard node pool, Cloud Build, external
Docker TCP). GCE VM runner remains active in the interim.
2026-05-05 10:22:02 -05:00

115 lines
4.1 KiB
YAML

---
# Gitea Actions runner on GKE Autopilot.
#
# Architecture:
# - init container registers the runner with Gitea (--no-interactive, idempotent)
# - main container runs act_runner daemon
#
# LIMITATION: GKE Autopilot does not allow privileged containers, so Docker-in-Docker
# (DinD) is not available. The runner uses "host" label mode, which runs steps
# directly inside the runner pod container rather than spawning Docker containers.
# forgejo-runner v11 still requires a Docker daemon to start — this deployment
# will fail until one of the following is resolved:
#
# Option A (recommended): Switch to a GKE Standard node pool for the ci namespace.
# Add a standard node pool with sandbox.config.sandboxType=gvisor or allow
# privileged pods on specific node pools.
#
# Option B: Use Cloud Build for docker build steps; use this runner for shell steps.
# Requires forking all workflow files to remove docker steps.
#
# Option C: Point docker_host at an external Docker TCP daemon (e.g., on the GCE runner VM).
#
# Until resolved, the existing GCE VM runner (gitea-runner-1 in us-central1-a)
# handles CI jobs. This deployment is a placeholder for when privileged pods are available.
#
# The ci namespace has pod-security.kubernetes.io/enforce: privileged set
# (see namespace.yaml) but Autopilot's Warden enforces this at the node level.
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea-runner
namespace: ci
labels:
app: gitea-runner
spec:
replicas: 1
selector:
matchLabels:
app: gitea-runner
template:
metadata:
labels:
app: gitea-runner
spec:
serviceAccountName: gitea-runner
securityContext:
runAsNonRoot: false
initContainers:
- name: register
image: us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest
command: ["/bin/sh", "-c"]
args:
- |
# Idempotent registration — skip if already registered (.runner file exists).
if [ -f /data/.runner ]; then
echo "Runner already registered, skipping."
else
act_runner register \
--instance "${GITEA_INSTANCE_URL}" \
--token "${GITEA_RUNNER_REGISTRATION_TOKEN}" \
--name "gke-runner-$(hostname)" \
--labels "ubuntu-latest:host,ubuntu-24.04:host,linux,x64" \
--no-interactive
fi
cat > /data/config.yaml << 'CONFIGEOF'
runner:
capacity: 2
timeout: 3h
envs:
BASH_ENV: "/usr/local/bin/git-ssh-init.sh"
container:
# GKE Autopilot blocks privileged DinD — steps run directly
# in the runner container (host mode). Labels use :host suffix.
network: host
docker_host: "-"
force_pull: false
valid_volumes: []
CONFIGEOF
env:
# Use the internal Gitea service to avoid Cloudflare Access auth.
# Gitea is in the same GKE cluster — use the ClusterIP service.
- name: GITEA_INSTANCE_URL
value: "http://gitea.gitea.svc.cluster.local:80"
envFrom:
- secretRef:
name: gitea-runner-token
volumeMounts:
- mountPath: /data
name: data
workingDir: /data
containers:
- name: runner
image: us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest
command: ["act_runner", "daemon", "--config", "/data/config.yaml"]
env:
- name: GITEA_INSTANCE_URL
value: "http://gitea.gitea.svc.cluster.local:80"
envFrom:
- secretRef:
name: gitea-runner-token
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "4"
memory: "4Gi"
volumeMounts:
- mountPath: /data
name: data
workingDir: /data
volumes:
- name: data
emptyDir: {}