From 25760ea373355f655e8036dac742e69bfd32bcf0 Mon Sep 17 00:00:00 2001 From: Oleksandr Berezovskyi Date: Mon, 11 May 2026 22:21:36 +0300 Subject: [PATCH] feat(k8s/blog): add static blog served via git-sync + nginx --- kubernetes/app/blog/configmap-nginx.yaml | 28 +++++ kubernetes/app/blog/deployment.yaml | 143 +++++++++++++++++++++++ kubernetes/app/blog/ingress.yaml | 23 ++++ kubernetes/app/blog/namespace.yaml | 4 + kubernetes/app/blog/networkpolicy.yaml | 26 +++++ kubernetes/app/blog/secret.sops.yaml | 23 ++++ kubernetes/app/blog/service.yaml | 12 ++ kubernetes/config/cluster-vars.sops.yaml | 11 +- 8 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 kubernetes/app/blog/configmap-nginx.yaml create mode 100644 kubernetes/app/blog/deployment.yaml create mode 100644 kubernetes/app/blog/ingress.yaml create mode 100644 kubernetes/app/blog/namespace.yaml create mode 100644 kubernetes/app/blog/networkpolicy.yaml create mode 100644 kubernetes/app/blog/secret.sops.yaml create mode 100644 kubernetes/app/blog/service.yaml diff --git a/kubernetes/app/blog/configmap-nginx.yaml b/kubernetes/app/blog/configmap-nginx.yaml new file mode 100644 index 0000000..638b347 --- /dev/null +++ b/kubernetes/app/blog/configmap-nginx.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: blog-nginx-config + namespace: blog +data: + default.conf: | + server { + listen 8080; + server_name _; + root /git/current; + index index.html; + + location /health { + access_log off; + return 200 "OK"; + add_header Content-Type text/plain; + } + + location ~ /\.git { + deny all; + return 404; + } + + location / { + try_files $uri $uri/ =404; + } + } diff --git a/kubernetes/app/blog/deployment.yaml b/kubernetes/app/blog/deployment.yaml new file mode 100644 index 0000000..a125d78 --- /dev/null +++ b/kubernetes/app/blog/deployment.yaml @@ -0,0 +1,143 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: blog + namespace: blog + labels: + app: blog +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: blog + template: + metadata: + labels: + app: blog + spec: + securityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + # git-sync (uid 65533) writes files; fsGroup makes the emptyDir group-writable by that uid + fsGroup: 65533 + initContainers: + - name: git-sync-init + image: registry.k8s.io/git-sync/git-sync:v4.5.1 + imagePullPolicy: IfNotPresent + env: + - name: GITSYNC_REPO + value: "https://${GITEA_HOST}/oleksandr/blog.git" + - name: GITSYNC_ROOT + value: "/git" + - name: GITSYNC_REF + value: "gh-pages" + - name: GITSYNC_LINK + value: "current" + - name: GITSYNC_DEPTH + value: "1" + - name: GITSYNC_ONE_TIME + value: "true" + - name: GITSYNC_USERNAME + valueFrom: + secretKeyRef: + name: blog-git-credentials + key: GITSYNC_USERNAME + - name: GITSYNC_PASSWORD + valueFrom: + secretKeyRef: + name: blog-git-credentials + key: GITSYNC_PASSWORD + securityContext: + runAsUser: 65533 + runAsGroup: 65533 + volumeMounts: + - name: git-content + mountPath: /git + resources: + requests: + cpu: 10m + memory: 64Mi + limits: + memory: 128Mi + containers: + - name: nginx + image: nginxinc/nginx-unprivileged:1.28.3-alpine3.23 + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + name: http + protocol: TCP + securityContext: + runAsUser: 101 + runAsGroup: 101 + volumeMounts: + - name: git-content + mountPath: /git + - name: nginx-config + mountPath: /etc/nginx/conf.d/default.conf + subPath: default.conf + livenessProbe: + httpGet: + port: 8080 + path: /health + initialDelaySeconds: 10 + periodSeconds: 30 + readinessProbe: + httpGet: + port: 8080 + path: /health + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + requests: + cpu: 10m + memory: 32Mi + limits: + memory: 64Mi + - name: git-sync + image: registry.k8s.io/git-sync/git-sync:v4.5.1 + imagePullPolicy: IfNotPresent + env: + - name: GITSYNC_REPO + value: "https://${GITEA_HOST}/oleksandr/blog.git" + - name: GITSYNC_ROOT + value: "/git" + - name: GITSYNC_REF + value: "gh-pages" + - name: GITSYNC_LINK + value: "current" + - name: GITSYNC_DEPTH + value: "1" + - name: GITSYNC_PERIOD + value: "30s" + - name: GITSYNC_USERNAME + valueFrom: + secretKeyRef: + name: blog-git-credentials + key: GITSYNC_USERNAME + - name: GITSYNC_PASSWORD + valueFrom: + secretKeyRef: + name: blog-git-credentials + key: GITSYNC_PASSWORD + securityContext: + runAsUser: 65533 + runAsGroup: 65533 + volumeMounts: + - name: git-content + mountPath: /git + resources: + requests: + cpu: 5m + memory: 32Mi + limits: + memory: 64Mi + volumes: + - name: git-content + emptyDir: {} + - name: nginx-config + configMap: + name: blog-nginx-config diff --git a/kubernetes/app/blog/ingress.yaml b/kubernetes/app/blog/ingress.yaml new file mode 100644 index 0000000..27650d6 --- /dev/null +++ b/kubernetes/app/blog/ingress.yaml @@ -0,0 +1,23 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: blog + namespace: blog + annotations: + cert-manager.io/cluster-issuer: letsencrypt +spec: + tls: + - hosts: + - ${BLOG_HOST} + secretName: blog-tls + rules: + - host: ${BLOG_HOST} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: blog + port: + number: 8080 diff --git a/kubernetes/app/blog/namespace.yaml b/kubernetes/app/blog/namespace.yaml new file mode 100644 index 0000000..8c905cd --- /dev/null +++ b/kubernetes/app/blog/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: blog diff --git a/kubernetes/app/blog/networkpolicy.yaml b/kubernetes/app/blog/networkpolicy.yaml new file mode 100644 index 0000000..49d0065 --- /dev/null +++ b/kubernetes/app/blog/networkpolicy.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny-ingress + namespace: blog +spec: + podSelector: {} + policyTypes: + - Ingress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-ingress-controller + namespace: blog +spec: + podSelector: + matchLabels: + app: blog + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: traefik diff --git a/kubernetes/app/blog/secret.sops.yaml b/kubernetes/app/blog/secret.sops.yaml new file mode 100644 index 0000000..4b2e68f --- /dev/null +++ b/kubernetes/app/blog/secret.sops.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Secret +metadata: + name: blog-git-credentials + namespace: blog +stringData: + GITSYNC_USERNAME: ENC[AES256_GCM,data:6cZnsgZ53xsU,iv:j2qvZr+odimHPnlMKz9xTe8q+zXvyRDF9spH8TkcmBw=,tag:ik6DJ4cMfcNc5UHY5vRy3A==,type:str] + GITSYNC_PASSWORD: ENC[AES256_GCM,data:mjQJuOuH2YQ1i1JVO+lFZ7WYLmy4RjCi2xZRErU5hRNi2SdgRo0fwg==,iv:9pl3nVmlnynJ4jMke1Hy4ACBuMyM+65vmGR2kS705I0=,tag:K1dQ9YwVN0sZjieAOU3KWw==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYR3FFRWdPajVzbjZGem1N + OXA2aVhtSWRxQUpGcDFxLzBFQWkyMlh5L2pvCkgvM25IVTBqSFZ3QmJDaGxqSTJ2 + blQyOTQwaThRbkhzZ01uUmVoaXg5VHMKLS0tIHJJWlJoSmRXRi90M2VHSTlvdnBH + VjRWTHRSWUE0VFpqakwxUmx3aENqYUUK21JXxrt792sHoI2TCYU3aIQKKLL9f8oq + 1o+qkueVYSYmVetrqcjSWqfGE496BYi+pd4a0mmRwaLpLCZIIgtlvA== + -----END AGE ENCRYPTED FILE----- + recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc + encrypted_regex: ^(data|stringData|email)$ + lastmodified: "2026-05-11T19:21:18Z" + mac: ENC[AES256_GCM,data:/G7ttapGuQ942iVOeUGqIlCUm+yCFmmxbD4AWRJW/TCJkXI/krkGrDGPX8eQ2BLO9yWga93Ni+fm5tTwGd9mhg9+JkiMMrIoQHb8hm+HqZwFoW3KtxrcLPg0Gi973ecbXkrKszLWqBlBJPU8iuHg+6uicDsrE9TDKOUswc0HdQw=,iv:MISyqpFmGkVqldK1deix2auDXt6PPFXY+Z0A4q4iJwU=,tag:tn0XgaX9+/I89MRUqqcxDw==,type:str] + version: 3.13.0 diff --git a/kubernetes/app/blog/service.yaml b/kubernetes/app/blog/service.yaml new file mode 100644 index 0000000..0d41050 --- /dev/null +++ b/kubernetes/app/blog/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: blog + namespace: blog +spec: + selector: + app: blog + ports: + - port: 8080 + targetPort: 8080 + name: http diff --git a/kubernetes/config/cluster-vars.sops.yaml b/kubernetes/config/cluster-vars.sops.yaml index ef222dc..a1676a1 100644 --- a/kubernetes/config/cluster-vars.sops.yaml +++ b/kubernetes/config/cluster-vars.sops.yaml @@ -35,11 +35,11 @@ stringData: SEERR_HOST: ENC[AES256_GCM,data:l64ttp+rLNU8GfwIE4fhJROSMDEb,iv:1vHOw0LyGN9OMhYemhtRq9GE2fc4J2EprZU3bp/h4kk=,tag:WNV0Jh7816ra7IIOBMspBg==,type:str] FIREFLY_HOST: ENC[AES256_GCM,data:EQdrW33PVlD/brZCqh/sTkqLdPWW/bo=,iv:zbNnjsT1J6lpCEWtvNyL4A3bjWsusCjI5goWcZ8hajk=,tag:VwA6YA056bsyYIKB+X59bw==,type:str] GITEA_HOST: ENC[AES256_GCM,data:Ky3tXS9E9iadabpVzoK5zEbigmtn,iv:t6OdQeQ7Kxzb7qbi+KeoOkJd0XE2Dse5P1fFyiHM6tg=,tag:GpmHtVDr0IQVbpbALVx40g==,type:str] + BLOG_HOST: ENC[AES256_GCM,data:eADRFTla/PPFLVCoP9uKvPZ1YhY=,iv://xbCGLkZ6guGMDU0MnVtGR0adgMqNbe0zrCerj7w8k=,tag:HHXvN6y3YvJ4U4TkENkYVA==,type:str] BACKUP_LOCAL_HOST: ENC[AES256_GCM,data:ABaTI3NKkhF7K2FpPwvvrHA0l/tCxAi4Qek=,iv:34ixxSpKU1c12uoMdk4nz2Vo/+5A/npB7NWMsWFytIM=,tag:qjw2fQBebCKnqCnAPiBHaw==,type:str] sops: age: - - recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc - enc: | + - enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXekorS3lvckhHelE0NGVP Z0JSSmtTRk1yZEoxTVU4MnhaN2VjajU2cmhnCkEyWU0yandxUnZGZm5yWXJmQlM4 @@ -47,7 +47,8 @@ sops: MGJ6TFpwR0diNjlEN2syZkhNMFNwRDQK9pzmQGB0GQu6ogMIJW+kugvBNj3w+dxW bfEF9GAznIM/N5rPytF4wNgqwfoAF7GwumgA+iD43wprKtUJn+6dqw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-04-29T19:47:22Z" - mac: ENC[AES256_GCM,data:WgVeGY1tGl3Y97FRGNVEkM2J4WKeHil6ki2Jsww1QOsAdGyL7QaL0vPpfRfDc8FRHhyeahS7ShacUvBKnL5Lihxnrqhkd8wA0BgxONk0k/W8/WtU/tvhXpTwxTWgQ5VyPtX3QwwkAcGTARBu+Xrp2y9QGXU8NPO56eH6cUGPEW4=,iv:KjZbbRfhy4ZHtv38MHlX8jxK+9pp/U2aGaX+d8HRK3E=,tag:wcdQVdpYR9czzeYMi554oQ==,type:str] + recipient: age1zffnskvuezntkk703a0pyxsd5m8vx2hm33dr47wdfy8mn4fdw4sqgw0jgc encrypted_regex: ^(data|stringData|email)$ - version: 3.12.2 + lastmodified: "2026-05-11T19:20:10Z" + mac: ENC[AES256_GCM,data:eA4MCmIA7iuC6gelQqWfIWPBb5w+93BLfU2ZhqLMbIna+OIk0tOjogcVz89H+x72ryMB+39a1vD+cYzLcF16Zl0uVHB5UiK9UDssJ26nbt8SK18F9Ew0a0WLp+gCj2qNAB2Od8+rLOKccfmHm1x+C5RuvYnRE4A1U1VNu6sPx3Q=,iv:epN8ZWNF0taoWm94eR+Unx9TUvjSIlKMgacLfbyj94w=,tag:a9f+8K7SOQmFJaZZ2PjnMQ==,type:str] + version: 3.13.0