彻底删除旧静态 Gitea Runner #130
@@ -1,14 +0,0 @@
|
|||||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
|
||||||
kind: Kustomization
|
|
||||||
metadata:
|
|
||||||
name: gitea-actions
|
|
||||||
namespace: flux-system
|
|
||||||
spec:
|
|
||||||
interval: 10m
|
|
||||||
path: ./platform/gitea-runner
|
|
||||||
prune: true
|
|
||||||
sourceRef:
|
|
||||||
kind: GitRepository
|
|
||||||
name: flux-system
|
|
||||||
timeout: 3m
|
|
||||||
wait: false
|
|
||||||
@@ -7,7 +7,6 @@ resources:
|
|||||||
- apps/envoy-gateway.yaml
|
- apps/envoy-gateway.yaml
|
||||||
- apps/external-secrets.yaml
|
- apps/external-secrets.yaml
|
||||||
- apps/gitea.yaml
|
- apps/gitea.yaml
|
||||||
- apps/gitea-actions.yaml
|
|
||||||
- apps/http-echo.yaml
|
- apps/http-echo.yaml
|
||||||
- apps/openebs.yaml
|
- apps/openebs.yaml
|
||||||
- apps/nats.yaml
|
- apps/nats.yaml
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
# Gitea Actions runner
|
|
||||||
|
|
||||||
This is the bootstrap runner for Gitea Actions. One persistent runner Pod accepts
|
|
||||||
up to four jobs; each job runs in a dynamically created container inside a
|
|
||||||
Docker-in-Docker daemon. The official chart runs DinD privileged. Rootless DinD
|
|
||||||
would still be privileged and is blocked by the node's AppArmor user-namespace
|
|
||||||
policy, so this deployment uses regular DinD instead of weakening that host-wide
|
|
||||||
policy. Only trusted workflows may target this runner.
|
|
||||||
|
|
||||||
DinD 同时使用 `--mtu=1450` 和
|
|
||||||
`--default-network-opt=bridge=com.docker.network.driver.mtu=1450`,与 k3s Pod 的
|
|
||||||
`eth0` 一致。前者只覆盖 Docker 默认 bridge;act 为每个 job 创建 user-defined
|
|
||||||
bridge,必须由后者设置默认 MTU。不要在未验证节点 Pod MTU 的情况下删除或修改这
|
|
||||||
两个参数:MTU 1500 的 job 容器虽然能够解析 GitHub、甚至建立 TCP 连接,但较大的
|
|
||||||
TLS 数据包会在嵌套网络路径中丢失,表现为 `github.com` / `api.github.com` 超时或
|
|
||||||
`setup-go` 每次请求卡满 6 分钟后重试。Pod 网络和默认 Docker bridge 正常不代表
|
|
||||||
Actions job bridge 正常。
|
|
||||||
|
|
||||||
The runner is registered at instance scope so it is available to every repository
|
|
||||||
on this Gitea instance. Repository permissions and protected-branch review are
|
|
||||||
therefore the security boundary; do not enable Actions for untrusted repositories.
|
|
||||||
|
|
||||||
The runner registration token is authoritative in OpenBao at
|
|
||||||
`kv/k8s/gitea-runner`. External Secrets Operator projects its `token` property to
|
|
||||||
the `gitea-runner-token` Secret. Never put the token in this directory or a Helm
|
|
||||||
command line.
|
|
||||||
|
|
||||||
## SPIRE 与 OCI 发布
|
|
||||||
|
|
||||||
runner Pod 使用专用 ServiceAccount `gitea-actions`,并由精确匹配 namespace、
|
|
||||||
ServiceAccount 隐含的 Pod、以及 chart labels 的 `ClusterSPIFFEID` 获得:
|
|
||||||
|
|
||||||
```text
|
|
||||||
spiffe://ddupan.top/ci/gitea-actions
|
|
||||||
```
|
|
||||||
|
|
||||||
SPIFFE CSI socket 同时只读挂载到 runner 和 DinD。act 的 volume allowlist 只允许
|
|
||||||
`/run/spire/agent-sockets`;workflow 仍必须在 job container 中显式请求该 bind
|
|
||||||
mount。原因是 bind mount 由 DinD 内的 dockerd 解析,只挂 runner 容器无法让 job
|
|
||||||
访问 Workload API。
|
|
||||||
|
|
||||||
该身份不是通用 registry 管理员。zot 只对明确列出的 CI 镜像仓库授予
|
|
||||||
`read/create/update`,不授予 delete 或其他仓库写入。workflow 应获取
|
|
||||||
`aud=zot` 的短期 JWT-SVID,并经 stdin 传给 registry client,不得把 JWT、X.509
|
|
||||||
SVID 或 Docker auth 写入 workspace/artifact。
|
|
||||||
|
|
||||||
## Flux 接管状态
|
|
||||||
|
|
||||||
该 release 最初通过下述 review-first 流程手动 bootstrap。下一个 GitOps 阶段将
|
|
||||||
使用 Flux `HelmRelease` 接管它,并首先固定现有 chart `0.1.1`,不在接管 PR 中升级。
|
|
||||||
|
|
||||||
迁移前审计发现:Helm 保存的 user-supplied values 和 release manifest 仍描述失败的
|
|
||||||
rootless DinD 尝试,但 live StatefulSet 与本目录 `values.yaml` 都已经使用 regular
|
|
||||||
DinD。首次 reconcile 的验收条件是修正 Helm 存储状态,同时 live Pod spec、PVC
|
|
||||||
identity、runner capacity 和在线状态保持不变。接管稳定后再用独立 PR 升级 chart。
|
|
||||||
|
|
||||||
接管分两阶段:第一阶段提交 `suspend: true` 的 HelmRelease、HelmRepository 和由
|
|
||||||
`values.yaml` 生成的 ConfigMap。Flux 只登记这些对象,不执行 Helm action。合并后
|
|
||||||
检查 HelmRepository Ready,并用固定 chart 重复比较期望清单与 live StatefulSet;
|
|
||||||
第二阶段解除 suspend。第一阶段已经确认 source Ready、完整 chart render 与 live
|
|
||||||
资源零差异,且登记过程中现有 runner 没有 rollout。失败重试使用
|
|
||||||
`RetryOnFailure`,不会用 stored rootless release 做 rollback。
|
|
||||||
|
|
||||||
## 历史 review-first bootstrap
|
|
||||||
|
|
||||||
这是 Flux 安装前执行过的一次性手动部署流程,保留用于恢复和审计:
|
|
||||||
|
|
||||||
1. Merge the reviewed PR.
|
|
||||||
2. As a Gitea site administrator, create an instance-scoped runner registration
|
|
||||||
token under **Site Administration → Actions → Runners**.
|
|
||||||
3. Store it as the `token` property at `kv/k8s/gitea-runner` without exposing it
|
|
||||||
in shell history:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
read -rsp 'Runner token: ' runner_token
|
|
||||||
printf '%s' "$runner_token" | bao kv put kv/k8s/gitea-runner token=-
|
|
||||||
unset runner_token
|
|
||||||
```
|
|
||||||
|
|
||||||
4. From the updated `main`, create the namespace and ExternalSecret, then wait
|
|
||||||
for `SecretSynced=True`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
KUBECONFIG="$HOME/.kube/config" k3s kubectl apply \
|
|
||||||
-f platform/gitea-runner/namespace.yaml
|
|
||||||
KUBECONFIG="$HOME/.kube/config" k3s kubectl apply \
|
|
||||||
-f platform/gitea-runner/external-secret.yaml
|
|
||||||
KUBECONFIG="$HOME/.kube/config" k3s kubectl wait \
|
|
||||||
--namespace gitea-actions \
|
|
||||||
--for=condition=Ready externalsecret/gitea-runner-token \
|
|
||||||
--timeout=60s
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Install chart `actions` version `0.1.1` from
|
|
||||||
`https://dl.gitea.com/charts/` with this `values.yaml`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm repo add gitea-charts https://dl.gitea.com/charts/
|
|
||||||
helm repo update gitea-charts
|
|
||||||
helm upgrade --install gitea-actions gitea-charts/actions \
|
|
||||||
--namespace gitea-actions \
|
|
||||||
--version 0.1.1 \
|
|
||||||
--values platform/gitea-runner/values.yaml \
|
|
||||||
--wait --timeout 10m
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Confirm the runner is online, then re-run the queued lint workflow.
|
|
||||||
|
|
||||||
Do not deploy from an unmerged feature branch. Do not use `--set` for the token.
|
|
||||||
|
|
||||||
The 1 GiB PVC preserves `.runner` identity. Docker image layers are ephemeral;
|
|
||||||
the Pod has a 20 GiB ephemeral-storage limit. Terraform apply jobs must use a
|
|
||||||
workflow concurrency group because runner capacity does not serialize access to
|
|
||||||
a shared state.
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
apiVersion: spire.spiffe.io/v1alpha1
|
|
||||||
kind: ClusterSPIFFEID
|
|
||||||
metadata:
|
|
||||||
name: gitea-actions
|
|
||||||
spec:
|
|
||||||
className: spire-mgmt-spire
|
|
||||||
spiffeIDTemplate: spiffe://{{ .TrustDomain }}/ci/gitea-actions
|
|
||||||
namespaceSelector:
|
|
||||||
matchLabels:
|
|
||||||
kubernetes.io/metadata.name: gitea-actions
|
|
||||||
podSelector:
|
|
||||||
matchLabels:
|
|
||||||
app.kubernetes.io/instance: gitea-actions
|
|
||||||
app.kubernetes.io/name: actions-runner
|
|
||||||
workloadSelectorTemplates:
|
|
||||||
- k8s:ns:gitea-actions
|
|
||||||
- k8s:sa:gitea-actions
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
apiVersion: external-secrets.io/v1
|
|
||||||
kind: ExternalSecret
|
|
||||||
metadata:
|
|
||||||
name: gitea-runner-token
|
|
||||||
namespace: gitea-actions
|
|
||||||
spec:
|
|
||||||
refreshInterval: 1h
|
|
||||||
secretStoreRef:
|
|
||||||
kind: ClusterSecretStore
|
|
||||||
name: openbao
|
|
||||||
target:
|
|
||||||
creationPolicy: Owner
|
|
||||||
name: gitea-runner-token
|
|
||||||
data:
|
|
||||||
- secretKey: token
|
|
||||||
remoteRef:
|
|
||||||
key: k8s/gitea-runner
|
|
||||||
property: token
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
|
||||||
kind: HelmRelease
|
|
||||||
metadata:
|
|
||||||
name: gitea-actions
|
|
||||||
namespace: gitea-actions
|
|
||||||
spec:
|
|
||||||
chart:
|
|
||||||
spec:
|
|
||||||
chart: actions
|
|
||||||
interval: 1h
|
|
||||||
sourceRef:
|
|
||||||
kind: HelmRepository
|
|
||||||
name: gitea-charts
|
|
||||||
version: 0.1.1
|
|
||||||
driftDetection:
|
|
||||||
mode: enabled
|
|
||||||
install:
|
|
||||||
strategy:
|
|
||||||
name: RetryOnFailure
|
|
||||||
retryInterval: 5m
|
|
||||||
interval: 30m
|
|
||||||
releaseName: gitea-actions
|
|
||||||
targetNamespace: gitea-actions
|
|
||||||
timeout: 10m
|
|
||||||
upgrade:
|
|
||||||
strategy:
|
|
||||||
name: RetryOnFailure
|
|
||||||
retryInterval: 5m
|
|
||||||
valuesFrom:
|
|
||||||
- kind: ConfigMap
|
|
||||||
name: gitea-actions-values
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
apiVersion: source.toolkit.fluxcd.io/v1
|
|
||||||
kind: HelmRepository
|
|
||||||
metadata:
|
|
||||||
name: gitea-charts
|
|
||||||
namespace: gitea-actions
|
|
||||||
spec:
|
|
||||||
interval: 1h
|
|
||||||
url: https://dl.gitea.com/charts/
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
||||||
kind: Kustomization
|
|
||||||
generatorOptions:
|
|
||||||
disableNameSuffixHash: true
|
|
||||||
labels:
|
|
||||||
reconcile.fluxcd.io/watch: Enabled
|
|
||||||
configMapGenerator:
|
|
||||||
- name: gitea-actions-values
|
|
||||||
namespace: gitea-actions
|
|
||||||
files:
|
|
||||||
- values.yaml=values.yaml
|
|
||||||
resources:
|
|
||||||
- namespace.yaml
|
|
||||||
- serviceaccount.yaml
|
|
||||||
- clusterspiffeid.yaml
|
|
||||||
- external-secret.yaml
|
|
||||||
- helmrepository.yaml
|
|
||||||
- helmrelease.yaml
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: Namespace
|
|
||||||
metadata:
|
|
||||||
name: gitea-actions
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: gitea-actions
|
|
||||||
namespace: gitea-actions
|
|
||||||
automountServiceAccountToken: false
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
enabled: true
|
|
||||||
|
|
||||||
giteaRootURL: http://gitea-http.gitea.svc.cluster.local:3000
|
|
||||||
existingSecret: gitea-runner-token
|
|
||||||
existingSecretKey: token
|
|
||||||
|
|
||||||
statefulset:
|
|
||||||
replicas: 0
|
|
||||||
timezone: Etc/UTC
|
|
||||||
serviceAccountName: gitea-actions
|
|
||||||
extraVolumes:
|
|
||||||
- name: spiffe-workload-api
|
|
||||||
csi:
|
|
||||||
driver: csi.spiffe.io
|
|
||||||
readOnly: true
|
|
||||||
securityContext:
|
|
||||||
fsGroup: 1000
|
|
||||||
# Chart 0.1.1 applies this block to both runner and DinD containers.
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 250m
|
|
||||||
memory: 512Mi
|
|
||||||
ephemeral-storage: 2Gi
|
|
||||||
limits:
|
|
||||||
cpu: "4"
|
|
||||||
memory: 6Gi
|
|
||||||
ephemeral-storage: 20Gi
|
|
||||||
persistence:
|
|
||||||
size: 1Gi
|
|
||||||
|
|
||||||
runner:
|
|
||||||
registry: docker.io
|
|
||||||
repository: gitea/runner
|
|
||||||
tag: 2.3.0
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
extraVolumeMounts:
|
|
||||||
- name: spiffe-workload-api
|
|
||||||
mountPath: /run/spire/agent-sockets
|
|
||||||
readOnly: true
|
|
||||||
config: |
|
|
||||||
log:
|
|
||||||
level: info
|
|
||||||
runner:
|
|
||||||
file: .runner
|
|
||||||
capacity: 4
|
|
||||||
timeout: 3h
|
|
||||||
shutdown_timeout: 3h
|
|
||||||
labels:
|
|
||||||
- self-hosted:docker://docker.gitea.com/runner-images:ubuntu-latest
|
|
||||||
cache:
|
|
||||||
enabled: false
|
|
||||||
container:
|
|
||||||
require_docker: true
|
|
||||||
docker_timeout: 300s
|
|
||||||
# Workflows must still request this exact bind mount explicitly. The
|
|
||||||
# allowlist prevents arbitrary host paths from reaching job containers.
|
|
||||||
valid_volumes:
|
|
||||||
- /run/spire/agent-sockets
|
|
||||||
|
|
||||||
dind:
|
|
||||||
# The node enforces AppArmor's unprivileged-userns restriction, which blocks
|
|
||||||
# rootlesskit even though this chart must run DinD privileged either way.
|
|
||||||
rootless: false
|
|
||||||
registry: docker.io
|
|
||||||
repository: docker
|
|
||||||
tag: 29.7.1-dind
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
# Bind mounts are resolved by dockerd, so the CSI socket must exist in the
|
|
||||||
# DinD container as well as in the runner container.
|
|
||||||
extraVolumeMounts:
|
|
||||||
- name: spiffe-workload-api
|
|
||||||
mountPath: /run/spire/agent-sockets
|
|
||||||
readOnly: true
|
|
||||||
# k3s uses a 1450-byte pod MTU. Without matching it here, nested Actions
|
|
||||||
# networks advertise 1500 and GitHub TLS packets disappear on the outer
|
|
||||||
# overlay path while direct pod traffic remains healthy.
|
|
||||||
extraArgs:
|
|
||||||
- --mtu=1450
|
|
||||||
# --mtu only changes Docker's default bridge. act creates a user-defined
|
|
||||||
# bridge per job, so give every new bridge the same explicit default.
|
|
||||||
- --default-network-opt=bridge=com.docker.network.driver.mtu=1450
|
|
||||||
Reference in New Issue
Block a user