diff --git a/README.md b/README.md index a10e6d6..4adc8e1 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # home-server -Kubernetes manifests that define my home server +This definitely grew more complex than I expected. -THIS SPIRALED +## Latest Update -Originally a kustomize but I wanted to be able to let others use it and tweak -hostname values, so it became my first ever Helm chart. - -I know I don't love Helm, but I needed to us it sadly. +Have removed Flux from my home system because I was sick to death of chicken-egg +issues. I have moved to ArgoCD because A) that's what we now use at work, and B) +because it has waves which solve this issue. diff --git a/applications/00-namespace/namespace.yaml b/applications/00-namespace/namespace.yaml new file mode 100644 index 0000000..f956aa2 --- /dev/null +++ b/applications/00-namespace/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: home-server diff --git a/applications/10-replicator/replicator.yaml b/applications/10-replicator/replicator.yaml new file mode 100644 index 0000000..e8ec276 --- /dev/null +++ b/applications/10-replicator/replicator.yaml @@ -0,0 +1,98 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kubernetes-replicator + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kubernetes-replicator +rules: + - apiGroups: ["", "apps", "extensions"] + resources: + - secrets + - configmaps + - roles + - rolebindings + - cronjobs + - deployments + - events + - ingresses + - jobs + - pods + - pods/attach + - pods/exec + - pods/log + - pods/portforward + - services + - namespaces + - serviceaccounts + verbs: ["*"] + - apiGroups: ["batch"] + resources: + - configmaps + - cronjobs + - deployments + - events + - ingresses + - jobs + - pods + - pods/attach + - pods/exec + - pods/log + - pods/portforward + - services + verbs: ["*"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: + - roles + - rolebindings + - clusterrolebindings + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kubernetes-replicator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubernetes-replicator +subjects: + - kind: ServiceAccount + name: kubernetes-replicator + namespace: kube-system +--- +apiVersion: source.toolkit.fluxcd.io/v1beta2 +kind: HelmRepository +metadata: + name: mittwald + namespace: flux-system +spec: + url: https://helm.mittwald.de + interval: 1h +--- +apiVersion: helm.toolkit.fluxcd.io/v2beta1 +kind: HelmRelease +metadata: + name: kubernetes-replicator + namespace: kube-system +spec: + interval: 5m + chart: + spec: + chart: kubernetes-replicator + sourceRef: + kind: HelmRepository + name: mittwald + namespace: flux-system + install: + createNamespace: false + upgrade: + disableWait: false + values: + serviceAccount: + create: false + name: kubernetes-replicator diff --git a/applications/20-databases/influxdb.yaml b/applications/20-databases/influxdb.yaml new file mode 100644 index 0000000..a70d171 --- /dev/null +++ b/applications/20-databases/influxdb.yaml @@ -0,0 +1,77 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: influxdb-pvc + namespace: database +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: influxdb-pv + namespace: database +spec: + capacity: + storage: 20Gi + accessModes: + - ReadWriteOnce + storageClassName: local-storage + hostPath: + path: /dpool/services/influxdb/data + type: DirectoryOrCreate + persistentVolumeReclaimPolicy: Retain + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - dck.hxme.net + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: influxdb + namespace: database +spec: + replicas: 1 + selector: + matchLabels: + app: influxdb + template: + metadata: + labels: + app: influxdb + spec: + containers: + - name: influxdb + image: influxdb:2.7.12 + ports: + - containerPort: 8086 + volumeMounts: + - name: influxdb-storage + mountPath: /var/lib/influxdb2 + env: + - name: DOCKER_INFLUXDB_INIT_MODE + value: "setup" + - name: DOCKER_INFLUXDB_INIT_USERNAME + value: "admin" + - name: DOCKER_INFLUXDB_INIT_PASSWORD + value: "adminpassword" + - name: DOCKER_INFLUXDB_INIT_ORG + value: "myorg" + - name: DOCKER_INFLUXDB_INIT_BUCKET + value: "mybucket" + volumes: + - name: influxdb-storage + persistentVolumeClaim: + claimName: influxdb-pvc diff --git a/applications/20-databases/mariadb.yaml b/applications/20-databases/mariadb.yaml new file mode 100644 index 0000000..ae86891 --- /dev/null +++ b/applications/20-databases/mariadb.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: mariadb-operator + namespace: flux-system +spec: + url: https://helm.mariadb.com/mariadb-operator + interval: 1h + +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: mariadb-operator-crds + namespace: database +spec: + interval: 30m + chart: + spec: + chart: mariadb-operator-crds + version: 0.38.1 + sourceRef: + kind: HelmRepository + name: mariadb-operator + namespace: flux-system + install: + createNamespace: true + upgrade: + disableWait: true + timeout: 5m + +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: mariadb-operator + namespace: database +spec: + interval: 30m + chart: + spec: + chart: mariadb-operator + version: 0.38.1 + sourceRef: + kind: HelmRepository + name: mariadb-operator + namespace: flux-system + install: + createNamespace: true + dependsOn: + - name: mariadb-operator-crds + namespace: database diff --git a/applications/20-databases/namespace.yaml b/applications/20-databases/namespace.yaml new file mode 100644 index 0000000..4c34a8f --- /dev/null +++ b/applications/20-databases/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: database diff --git a/applications/20-databases/postgresql.yaml b/applications/20-databases/postgresql.yaml new file mode 100644 index 0000000..5f65a9d --- /dev/null +++ b/applications/20-databases/postgresql.yaml @@ -0,0 +1,145 @@ +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: postgres-pv + namespace: database +spec: + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-path + hostPath: + path: /dpool/services/postgres/data + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + namespace: database +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 5Gi + volumeName: postgres-pv + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres + namespace: database +spec: + replicas: 1 + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + nodeSelector: + role: dck + containers: + - name: postgres + image: postgres:15 + ports: + - containerPort: 5432 + envFrom: + - secretRef: + name: postgres-secret + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + volumes: + - name: postgres-data + persistentVolumeClaim: + claimName: postgres-pvc + +--- +apiVersion: v1 +kind: Service +metadata: + name: postgres + namespace: database +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 + +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: postgres-backup-pv +spec: + capacity: + storage: 5Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-path + hostPath: + path: /dpool/services/postgres/backup + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-backup-pvc + namespace: database +spec: + accessModes: + - ReadWriteOnce + storageClassName: local-path + resources: + requests: + storage: 5Gi + + +--- +apiVersion: batch/v1 +kind: CronJob +metadata: + name: postgres-backup + namespace: database +spec: + schedule: "0 2 * * *" # Every day at 2 AM + jobTemplate: + spec: + template: + spec: + nodeSelector: + role: dck + restartPolicy: OnFailure + containers: + - name: pg-backup + image: postgres:15 + envFrom: + - secretRef: + name: postgres-secret + command: + - /bin/sh + - -c + - | + mkdir -p /backup + PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U $POSTGRES_USER -h localhost $POSTGRES_DB > /backup/backup-$(date +'%Y-%m-%d').sql + volumeMounts: + - name: backup-volume + mountPath: /backup + volumes: + - name: backup-volume + persistentVolumeClaim: + claimName: postgres-backup-pvc + + + diff --git a/applications/20-databases/redis.yaml b/applications/20-databases/redis.yaml new file mode 100644 index 0000000..5c6f090 --- /dev/null +++ b/applications/20-databases/redis.yaml @@ -0,0 +1,45 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: redis + namespace: database +spec: + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + nodeSelector: + role: dck + containers: + - name: redis + image: redis:7 + ports: + - containerPort: 6379 + volumeMounts: + - name: redis-data + mountPath: /data + volumes: + - name: redis-data + hostPath: + path: /dpool/services/redis/data + type: DirectoryOrCreate + +--- +apiVersion: v1 +kind: Service +metadata: + name: redis + namespace: database +spec: + selector: + app: redis + ports: + - port: 6379 + targetPort: 6379 + diff --git a/applications/30-domain-names/bind.yaml b/applications/30-domain-names/bind.yaml new file mode 100644 index 0000000..b77a3ba --- /dev/null +++ b/applications/30-domain-names/bind.yaml @@ -0,0 +1,138 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bind-master-config + namespace: home-server +data: + named.conf: | + include "/etc/bind/externaldns-key.conf"; + + options { + directory "/var/cache/bind"; + + recursion yes; + allow-query { any; }; + + listen-on port 53 { any; }; + listen-on-v6 port 53 { any; }; + + forwarders { + 10.40.0.254; + }; + + dnssec-validation auto; + }; + + zone "." IN { + type hint; + file "/usr/share/dns/root.hints"; + }; + + zone "hxme.net." IN { + type master; + file "/etc/bind/db.hxme.net"; + allow-update { key "externaldns-key"; }; + }; + db.hxme.net: | + $TTL 3600 + @ IN SOA ns1.hxme.net. admin.hxme.net. ( + 1 ; Serial + 7200 ; Refresh + 1800 ; Retry + 1209600 ; Expire + 86400 ) ; Negative Cache TTL + ; + @ IN NS ns1.hxme.net. + ns1 IN A 10.40.0.110 + @ IN A 10.40.0.110 + www IN A 10.40.0.110 +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: bind-master + namespace: home-server +spec: + selector: + matchLabels: + app: bind-master + template: + metadata: + labels: + app: bind-master + spec: + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + securityContext: + fsGroup: 999 + initContainers: + - name: fetch-root-hints + image: debian:12 + command: + - sh + - -c + - | + apt update && apt -y install curl + curl -sfSL https://www.internic.net/domain/named.cache -o /usr/share/dns/root.hints + volumeMounts: + - mountPath: /usr/share/dns + name: root-hints + containers: + - name: bind-master + image: internetsystemsconsortium/bind9:9.18 + command: ["named", "-g", "-c", "/etc/bind/named.conf"] + ports: + - containerPort: 53 + protocol: UDP + - containerPort: 53 + protocol: TCP + volumeMounts: + - name: config + mountPath: /etc/bind/named.conf + subPath: named.conf + - name: config + mountPath: /etc/bind/db.hxme.net + subPath: db.hxme.net + - name: dns-secrets + mountPath: /etc/bind/externaldns-key.conf + subPath: externaldns-key.conf + - name: bind-cache + mountPath: /var/cache/bind + - name: bind-rundir + mountPath: /var/run/named + - name: root-hints + mountPath: /usr/share/dns + volumes: + - name: dns-secrets + secret: + secretName: dns-secrets + - name: config + configMap: + name: bind-master-config + - name: bind-cache + emptyDir: {} + - name: bind-rundir + emptyDir: {} + - name: root-hints + emptyDir: {} + +--- +apiVersion: v1 +kind: Service +metadata: + name: bind-master + namespace: home-server +spec: + selector: + app: bind-master + ports: + - name: dns-udp + port: 53 + protocol: UDP + targetPort: 53 + - name: dns-tcp + port: 53 + protocol: TCP + targetPort: 53 + diff --git a/applications/30-domain-names/certmanager.yaml b/applications/30-domain-names/certmanager.yaml new file mode 100644 index 0000000..95511fd --- /dev/null +++ b/applications/30-domain-names/certmanager.yaml @@ -0,0 +1,73 @@ +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: HelmRepository +metadata: + name: jetstack + namespace: flux-system +spec: + url: https://charts.jetstack.io + interval: 1h +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + name: cert-manager + namespace: home-server +spec: + interval: 30m + chart: + spec: + chart: cert-manager + version: v1.18.2 + sourceRef: + kind: HelmRepository + name: jetstack + namespace: flux-system + install: + crds: CreateReplace + createNamespace: true + values: + installCRDs: true + extraArgs: + - --dns01-recursive-nameservers-only + - --dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53 +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-rfc2136 +spec: + acme: + email: admin@hxme.net + server: https://acme-v02.api.letsencrypt.org/directory + #server: https://acme-staging-v02.api.letsencrypt.org/directory + privateKeySecretRef: + name: letsencrypt-rfc2136 + solvers: + - dns01: + rfc2136: + nameserver: hawke.hxst.com.au:53 + tsigKeyName: "hxme-update-key" + tsigAlgorithm: HMACSHA512 + tsigSecretSecretRef: + name: hxme-update-key + key: hxme-update-key +--- +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: wildcard-hxme-net + namespace: home-server +spec: + secretName: wildcard-hxme-net + secretTemplate: + annotations: + replicator.v1.mittwald.de/replication-allowed: "true" + replicator.v1.mittwald.de/replicate-to: "home-media" + issuerRef: + name: letsencrypt-rfc2136 + kind: ClusterIssuer + commonName: "hxme.net" + dnsNames: + - "hxme.net" + - "*.hxme.net" diff --git a/applications/30-domain-names/externaldns.yaml b/applications/30-domain-names/externaldns.yaml new file mode 100644 index 0000000..c3a9736 --- /dev/null +++ b/applications/30-domain-names/externaldns.yaml @@ -0,0 +1,78 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: external-dns +rules: + - apiGroups: [""] + resources: ["services","endpoints","pods"] + verbs: ["get","watch","list"] + - apiGroups: ["extensions","networking.k8s.io"] + resources: ["ingresses"] + verbs: ["get","watch","list"] + - apiGroups: [""] + resources: ["nodes"] + verbs: ["list","watch"] + - apiGroups: ["discovery.k8s.io"] + resources: ["endpointslices"] + verbs: ["get", "watch", "list"] + # Add DNS provider specific rules here if needed (e.g., for AWS IAM, GCP etc.) +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: external-dns-viewer +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: external-dns +subjects: + - kind: ServiceAccount + name: external-dns + namespace: home-server +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: external-dns + namespace: home-server +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: external-dns + namespace: home-server +spec: + replicas: 1 + selector: + matchLabels: + app: external-dns + template: + metadata: + labels: + app: external-dns + spec: + serviceAccountName: external-dns + containers: + - name: external-dns + image: bitnami/external-dns:latest + args: + - --source=service + - --source=ingress + - --provider=rfc2136 + - --rfc2136-host=bind-master.home-server.svc.cluster.local + - --rfc2136-port=53 + - --rfc2136-zone=hxme.net + - --rfc2136-tsig-secret=$(RFC2136_TSIG_SECRET) + - --rfc2136-tsig-secret-alg=hmac-sha256 + - --rfc2136-tsig-keyname=externaldns-key + - --policy=sync + - --registry=txt + - --txt-owner-id=my-cluster + env: + - name: RFC2136_TSIG_SECRET + valueFrom: + secretKeyRef: + name: dns-secrets + key: externaldns-secret + diff --git a/deploy/server.yaml b/deploy/server.yaml new file mode 100644 index 0000000..bce36e5 --- /dev/null +++ b/deploy/server.yaml @@ -0,0 +1,35 @@ +--- +apiVersion: argoproj.io/v1alpha1 +kind: ApplicationSet +metadata: + name: home-server-applications + namespace: argocd +spec: + generators: + - list: + elements: + - name: namespace + path: applications/00-namespace + - name: replicator + path: applications/10-replicator + - name: databases + path: applications/20-databases + - name: domain-names + path: applications/30-domain-names + template: + metadata: + name: '{{name}}' + spec: + project: default + source: + repoURL: 'https://repobase.net/j/home-server.git' + targetRevision: HEAD + path: '{{path}}' + destination: + server: 'https://kubernetes.default.svc' + namespace: default + syncPolicy: + automated: + prune: true + selfHeal: true +