# Build Docker images on k3s with Gitea Actions → push to Harbor This repository contains a complete, reproducible configuration that lets you: 1. Run a **Gitea Actions runner** (`act_runner`) **on a k3s node**. 2. Build **Docker/OCI container images** inside the CI pipeline using a **Docker-in-Docker (DinD)** sidecar (so plain `docker build` / `docker push` work). 3. **Push the built images to a Harbor** registry. The whole pipeline is driven by **Gitea Actions** (Gitea's built-in, GitHub-Actions-compatible CI/CD), so no external CI system (Jenkins/Drone/etc.) is required. ``` ┌──────────────┐ registers ┌─────────────────────────────────────┐ │ Gitea │◄────────────────│ act_runner (Deployment on k3s) │ │ (Actions) │ job dispatch │ ┌───────────────┐ ┌──────────────┐ │ │ │────────────────►│ │ act_runner │ │ dockerd │ │ └──────────────┘ │ │ container │─►│ (DinD side- │ │ ▲ │ └───────────────┘ │ car, │ │ │ git push │ docker build │ privileged)│ │ ┌────┴─────┐ │ docker push └──────────────┘ │ │ developer│ └───────────────────────────┬─────────┘ └──────────┘ │ push image ▼ ┌──────────────────┐ │ Harbor registry │ └──────────────────┘ ``` --- ## Repository layout | File | Purpose | |------|---------| | [k3s/00-namespace.yaml](k3s/00-namespace.yaml) | Dedicated `gitea-runner` namespace. | | [k3s/10-runner-secret.yaml.example](k3s/10-runner-secret.yaml.example) | Gitea registration token secret (copy & fill in). | | [k3s/20-runner-config.yaml](k3s/20-runner-config.yaml) | `act_runner` config (`config.yaml`) as a ConfigMap. | | [k3s/30-runner-deployment.yaml](k3s/30-runner-deployment.yaml) | The `act_runner` + DinD Deployment. | | [k3s/40-harbor-secret.yaml.example](k3s/40-harbor-secret.yaml.example) | Optional cluster-side Harbor pull secret. | | [workflows/build-and-push.yaml](workflows/build-and-push.yaml) | Example Gitea Actions workflow (put in your app repo). | | [examples/Dockerfile](examples/Dockerfile) | Minimal sample image to build. | | [scripts/deploy.sh](scripts/deploy.sh) | Helper to apply all manifests. | | [.gitea/workflows/build-and-push.yaml](.gitea/workflows/build-and-push.yaml) | Same workflow, pre-placed so *this* repo self-builds. | --- ## Prerequisites * A running **k3s** cluster and `kubectl` access to it (`~/.kube/config` or `/etc/rancher/k3s/k3s.yaml`). * A reachable **Gitea** instance (>= 1.20) with **Actions enabled** (`[actions] ENABLED = true` in Gitea's `app.ini`). * A running **Harbor** registry and a **robot account** (recommended) with `push` permission on the target project. --- ## Step 1 — Enable Gitea Actions (on the Gitea server) In Gitea's `app.ini`: ```ini [actions] ENABLED = true ``` Restart Gitea. Then get a **runner registration token**: * **Instance level:** *Site Administration → Actions → Runners → Create new Runner* → copy the token. * **Org/Repo level:** *Settings → Actions → Runners → Create new Runner*. --- ## Step 2 — Create the runner registration secret ```bash cp k3s/10-runner-secret.yaml.example k3s/10-runner-secret.yaml # edit k3s/10-runner-secret.yaml and paste your GITEA_INSTANCE_URL + token kubectl apply -f k3s/10-runner-secret.yaml ``` ## Step 3 — Deploy the runner on k3s ```bash ./scripts/deploy.sh # or manually: kubectl apply -f k3s/00-namespace.yaml kubectl apply -f k3s/20-runner-config.yaml kubectl apply -f k3s/30-runner-deployment.yaml ``` Verify the runner appears **online** in Gitea (*Actions → Runners*) and that both containers are ready: ```bash kubectl -n gitea-runner rollout status deploy/act-runner kubectl -n gitea-runner get pods ``` ## Step 4 — Configure Harbor credentials for the pipeline In your **Gitea repository** (or org) → *Settings → Actions → Secrets*, add: | Secret name | Value | |-------------|-------| | `HARBOR_REGISTRY` | e.g. `harbor.example.com` | | `HARBOR_USERNAME` | Harbor user / robot account, e.g. `robot$ci` | | `HARBOR_PASSWORD` | the robot account token / password | > Using a **Harbor robot account** (Project → Robot Accounts) instead of a personal > login is strongly recommended. ## Step 5 — Add the workflow to your app repo Copy [workflows/build-and-push.yaml](workflows/build-and-push.yaml) into your application repository at `.gitea/workflows/build-and-push.yaml`, commit, and push. Gitea will dispatch the job to your k3s runner, which builds the image via DinD and pushes it to Harbor. --- ## How image building works (no Docker on k3s needed) k3s uses **containerd**, not Docker, and has no image-build capability. This setup therefore runs a **dockerd DinD sidecar** next to `act_runner`. The runner container talks to it over `tcp://localhost:2376` (TLS), so inside jobs the standard `docker` CLI works transparently — `docker build`, `docker login`, `docker push` all behave like on a normal Docker host. If you prefer a **rootless / non-privileged** approach (Kaniko or Buildah), see the "Alternative: rootless builds" section at the bottom of [workflows/build-and-push.yaml](workflows/build-and-push.yaml). ## Security notes * The DinD sidecar requires `privileged: true`. Keep this runner in its own namespace and, ideally, on a dedicated node (see the `nodeSelector`/`tolerations` comments in the Deployment). * Prefer Harbor **robot accounts** with the least privilege (push to one project only). * Store all credentials as Kubernetes/Gitea secrets — never commit the filled-in `*.yaml` (non-`.example`) files. See [.gitignore](.gitignore).