diff --git a/.gitignore b/.gitignore index d820081..8475578 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ configs/node-password.txt configs/admin-password.txt configs/authelia-session-secret.txt configs/authelia-jwt-secret.txt +configs/gitea-password.txt diff --git a/manifests/admin/portal.yaml b/manifests/admin/portal.yaml index ba96682..cfe631c 100644 --- a/manifests/admin/portal.yaml +++ b/manifests/admin/portal.yaml @@ -23,6 +23,7 @@ data:

planck

Grafana + Gitea (git + CI) Account & 2FA settings

20 nodes · k3s · carr.pub

diff --git a/manifests/git/act-runner.yaml b/manifests/git/act-runner.yaml new file mode 100644 index 0000000..8a4ee28 --- /dev/null +++ b/manifests/git/act-runner.yaml @@ -0,0 +1,75 @@ +# act_runner x3 with docker-in-docker sidecars, so Gitea Actions jobs +# run as containers on the worker nodes (ARM64). +apiVersion: apps/v1 +kind: Deployment +metadata: + name: act-runner + namespace: git +spec: + replicas: 3 + selector: + matchLabels: {app: act-runner} + template: + metadata: + labels: {app: act-runner} + spec: + containers: + - name: runner + image: gitea/act_runner:latest + env: + - {name: GITEA_INSTANCE_URL, value: "http://gitea.git.svc:3000"} + - {name: GITEA_RUNNER_REGISTRATION_TOKEN, value: "eEsa5oEXUCQyTM6Gj9iAqDzofDj0TmoTYftyPOLd"} + - {name: DOCKER_HOST, value: "tcp://127.0.0.1:2375"} + - {name: CONFIG_FILE, value: /config/config.yaml} + resources: + requests: {cpu: 50m, memory: 64Mi} + limits: {memory: 256Mi} + volumeMounts: + - {name: work, mountPath: /data} + - {name: config, mountPath: /config} + - name: dind + image: docker:27-dind + securityContext: + privileged: true + env: + - {name: DOCKER_TLS_CERTDIR, value: ""} + resources: + requests: {cpu: 100m, memory: 256Mi} + limits: {memory: 1Gi} + volumeMounts: + - {name: work, mountPath: /data} + volumes: + - name: work + emptyDir: {} + - name: config + configMap: {name: act-runner-config} + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: {app: act-runner} + topologyKey: kubernetes.io/hostname +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: act-runner-config + namespace: git +data: + config.yaml: | + runner: + capacity: 4 + timeout: 30m + insecure: false + fetch_timeout: 5s + labels: + - "arm64:host" + - "ubuntu-latest:docker://node:22-bookworm" + cache: + enabled: false + container: + privileged: false + options: "" + valid_volumes: [] diff --git a/manifests/git/gitea.yaml b/manifests/git/gitea.yaml new file mode 100644 index 0000000..d9ebdc9 --- /dev/null +++ b/manifests/git/gitea.yaml @@ -0,0 +1,86 @@ +# Gitea: git hosting + CI, with repo data on Synology NFS. +apiVersion: v1 +kind: Namespace +metadata: + name: git +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: gitea-data + namespace: git +spec: + accessModes: [ReadWriteOnce] + storageClassName: nfs-synology + resources: + requests: {storage: 20Gi} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gitea + namespace: git +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: {app: gitea} + template: + metadata: + labels: {app: gitea} + spec: + containers: + - name: gitea + image: gitea/gitea:1.24 + env: + - {name: USER_UID, value: "1000"} + - {name: USER_GID, value: "1000"} + - {name: GITEA__database__DB_TYPE, value: sqlite3} + - {name: GITEA__server__DOMAIN, value: git.carr.pub} + - {name: GITEA__server__ROOT_URL, value: "https://git.carr.pub/"} + - {name: GITEA__server__HTTP_PORT, value: "3000"} + - {name: GITEA__actions__ENABLED, value: "true"} + - {name: GITEA__service__DISABLE_REGISTRATION, value: "true"} + ports: [{containerPort: 3000}] + resources: + requests: {cpu: 100m, memory: 256Mi} + limits: {memory: 1Gi} + volumeMounts: + - {name: data, mountPath: /data} + volumes: + - name: data + persistentVolumeClaim: {claimName: gitea-data} +--- +apiVersion: v1 +kind: Service +metadata: + name: gitea + namespace: git +spec: + selector: {app: gitea} + ports: + - port: 3000 + targetPort: 3000 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: gitea + namespace: git + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + # NOTE: intentionally NOT behind the SSO gate - git clients and CI + # need direct token auth; Gitea has its own accounts +spec: + tls: + - hosts: [git.carr.pub] + secretName: git-carr-pub-tls + rules: + - host: git.carr.pub + http: + paths: + - path: / + pathType: Prefix + backend: + service: {name: gitea, port: {number: 3000}} diff --git a/manifests/monitoring/nfs-provisioner.yaml b/manifests/monitoring/nfs-provisioner.yaml new file mode 100644 index 0000000..cc8a7aa --- /dev/null +++ b/manifests/monitoring/nfs-provisioner.yaml @@ -0,0 +1,114 @@ +# NFS dynamic provisioning from the Synology (192.168.1.157). +# Base dir /volume1/plancknetboot/k8s-volumes; each PVC gets a subdir. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: nfs-provisioner + namespace: monitoring +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: nfs-provisioner +rules: + - apiGroups: [""] + resources: [persistentvolumes] + verbs: [get, list, watch, create, delete] + - apiGroups: [""] + resources: [persistentvolumeclaims] + verbs: [get, list, watch, update] + - apiGroups: ["storage.k8s.io"] + resources: [storageclasses, volumeattributesclasses] + verbs: [get, list, watch] + - apiGroups: [""] + resources: [events] + verbs: [create, update, patch] + - apiGroups: ["storage.k8s.io"] + resources: [csidrivers] + verbs: [get, list] + - apiGroups: [""] + resources: [nodes] + verbs: [get, list, watch] + - apiGroups: ["storage.k8s.io"] + resources: [volumeattachments] + verbs: [get, list, watch, delete] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: nfs-provisioner +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: nfs-provisioner +subjects: + - kind: ServiceAccount + name: nfs-provisioner + namespace: monitoring +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: nfs-provisioner + namespace: monitoring +rules: + - apiGroups: [""] + resources: [endpoints] + verbs: [get, list, watch, create, update, patch] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: nfs-provisioner + namespace: monitoring +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: nfs-provisioner +subjects: + - kind: ServiceAccount + name: nfs-provisioner + namespace: monitoring +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nfs-provisioner + namespace: monitoring +spec: + replicas: 1 + selector: + matchLabels: {app: nfs-provisioner} + template: + metadata: + labels: {app: nfs-provisioner} + spec: + serviceAccountName: nfs-provisioner + containers: + - name: nfs-provisioner + image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 + volumeMounts: + - {name: nfs, mountPath: /persistentvolumes} + env: + - {name: PROVISIONER_NAME, value: nfs-synology} + - {name: NFS_SERVER, value: "192.168.1.157"} + - {name: NFS_PATH, value: /volume1/plancknetboot/k8s-volumes} + - {name: ENABLE_LEADER_ELECTION, value: "false"} + resources: + requests: {cpu: 20m, memory: 32Mi} + limits: {memory: 128Mi} + volumes: + - name: nfs + nfs: + server: "192.168.1.157" + path: /volume1/plancknetboot/k8s-volumes +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: nfs-synology +provisioner: nfs-synology +reclaimPolicy: Delete +volumeBindingMode: Immediate +parameters: + archiveOnDelete: "true"