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.
This commit is contained in:
Will Anderson
2026-05-05 10:22:02 -05:00
parent c31edc8b83
commit 9a368986b2
+34 -41
View File
@@ -1,16 +1,30 @@
---
# Gitea Actions runner on GKE.
# Gitea Actions runner on GKE Autopilot.
#
# Architecture:
# - init container registers the runner with Gitea (--no-interactive, idempotent)
# - main container runs act_runner daemon
# - docker:dind sidecar provides Docker for build jobs (privileged)
# - emptyDir /data persists runner state within a pod lifetime
#
# The runner uses Docker-in-Docker (DinD) rather than a host socket because
# GKE Autopilot does not expose the node's Docker socket.
# DinD requires privileged mode — the ci namespace has
# pod-security.kubernetes.io/enforce: privileged (see namespace.yaml).
# 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:
@@ -45,28 +59,28 @@ spec:
--instance "${GITEA_INSTANCE_URL}" \
--token "${GITEA_RUNNER_REGISTRATION_TOKEN}" \
--name "gke-runner-$(hostname)" \
--labels "ubuntu-latest:docker://us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest,ubuntu-24.04:docker://us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest,linux,x64" \
--labels "ubuntu-latest:host,ubuntu-24.04:host,linux,x64" \
--no-interactive
fi
cat > /data/config.yaml << 'CONFIGEOF'
runner:
capacity: 2
timeout: 3h
container:
network: host
docker_host: "tcp://localhost:2375"
force_pull: true
valid_volumes: []
default_image: "us-central1-docker.pkg.dev/neuron-785695/neuron-ci/ci-base:latest"
env:
GITEA_SSH_PRIVATE_KEY: ""
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: "https://git.neuralplatform.ai"
- name: DOCKER_HOST
value: "tcp://localhost:2375"
value: "http://gitea.gitea.svc.cluster.local:80"
envFrom:
- secretRef:
name: gitea-runner-token
@@ -80,9 +94,7 @@ spec:
command: ["act_runner", "daemon", "--config", "/data/config.yaml"]
env:
- name: GITEA_INSTANCE_URL
value: "https://git.neuralplatform.ai"
- name: DOCKER_HOST
value: "tcp://localhost:2375"
value: "http://gitea.gitea.svc.cluster.local:80"
envFrom:
- secretRef:
name: gitea-runner-token
@@ -97,25 +109,6 @@ spec:
- mountPath: /data
name: data
workingDir: /data
- name: dind
image: docker:dind
securityContext:
privileged: true
env:
- name: DOCKER_TLS_CERTDIR
value: ""
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "4"
memory: "4Gi"
volumeMounts:
- mountPath: /var/lib/docker
name: docker-storage
volumes:
- name: data
emptyDir: {}
- name: docker-storage
emptyDir: {}