Files
Will Anderson 422cca6962 scale gitea-runner to 0 while Autopilot DinD blocker is pending
Set replicas: 0 to prevent crash-loop restarts. The runner requires a Docker
daemon (even in host mode) which Autopilot won't provide. Scale to 1 when a
Standard node pool or external Docker TCP endpoint is available.
2026-05-05 10:22:21 -05:00

117 lines
4.3 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:
# Set to 0 until the DinD/privileged-pod blocker is resolved (see comments above).
# Change to 1 when a Docker daemon is available (Standard node pool or external TCP).
replicas: 0
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: {}