mirror of
https://github.com/rubenhensen/k8scd.git
synced 2026-11-15 22:29:09 +01:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30540ea579 | ||
|
|
53d74768ad | ||
|
|
20bd3a4bca | ||
|
|
6cba1a9363 | ||
|
|
acfce6645e | ||
|
|
0de86dd65c | ||
|
|
d7ffdc05f6 | ||
|
|
c0f8034e14 | ||
|
|
a15a4f7c75 |
No files matched your search
@@ -3,7 +3,7 @@
|
|||||||
# k3s in Docker, to prove the backups can be restored. Nothing on the NAS
|
# k3s in Docker, to prove the backups can be restored. Nothing on the NAS
|
||||||
# changes:
|
# changes:
|
||||||
# - one backup (backups/<name>/) and the kopia repositories of the chosen
|
# - one backup (backups/<name>/) and the kopia repositories of the chosen
|
||||||
# namespaces (kopia/<namespace>/) are copied read-only from the NAS with
|
# namespaces (kopia/<namespace>/) are copied read-only from MinIO with
|
||||||
# rclone into a Docker volume. A second Velero on the real bucket could
|
# rclone into a Docker volume. A second Velero on the real bucket could
|
||||||
# run kopia maintenance on the same repositories as the cluster's own
|
# run kopia maintenance on the same repositories as the cluster's own
|
||||||
# - `rclone serve s3` serves that copy to a Velero 1.18.2 in k3s, with a
|
# - `rclone serve s3` serves that copy to a Velero 1.18.2 in k3s, with a
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
# Defaults: the newest Completed daily-backup, and ntfy freshrss
|
# Defaults: the newest Completed daily-backup, and ntfy freshrss
|
||||||
# actual-budget. Not gatus: a restored Gatus would start sending alerts.
|
# actual-budget. Not gatus: a restored Gatus would start sending alerts.
|
||||||
#
|
#
|
||||||
# It reads Velero's S3 key from the cluster (velero/velero-s3-credentials)
|
# It reads Velero's MinIO key from the cluster (velero/cloud-credentials)
|
||||||
# into a temporary directory, and the copied backup holds every Secret of
|
# into a temporary directory, and the copied backup holds every Secret of
|
||||||
# the cluster; all of it is deleted at the end. KEEP=1 keeps the test
|
# the cluster; all of it is deleted at the end. KEEP=1 keeps the test
|
||||||
# cluster (kubeconfig in the printed directory) until you run it with
|
# cluster (kubeconfig in the printed directory) until you run it with
|
||||||
@@ -71,14 +71,14 @@ wait_for() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Endpoint, region and bucket as Velero itself uses them. The NAS rejects a
|
# Endpoint, region and bucket as Velero itself uses them. MinIO answers a
|
||||||
# request signed for the wrong region (rclone defaults to us-east-1); MinIO
|
# request signed for the wrong region (rclone defaults to us-east-1) with a
|
||||||
# did so with a bare 400 on HEAD.
|
# bare 400 on HEAD.
|
||||||
if [[ -n ${FROM_VOLUME:-} ]]; then
|
if [[ -n ${FROM_VOLUME:-} ]]; then
|
||||||
BUCKET=${BUCKET:-velero-backups}
|
BUCKET=${BUCKET:-velero-backups}
|
||||||
else
|
else
|
||||||
bsl=$(pk -n velero get backupstoragelocations default -o json)
|
bsl=$(pk -n velero get backupstoragelocations default -o json)
|
||||||
S3URL=${S3URL:-$(jq -r '.spec.config.s3Url' <<<"$bsl")}
|
MINIO=${MINIO:-$(jq -r '.spec.config.s3Url' <<<"$bsl")}
|
||||||
REGION=${REGION:-$(jq -r '.spec.config.region // "us-east-1"' <<<"$bsl")}
|
REGION=${REGION:-$(jq -r '.spec.config.region // "us-east-1"' <<<"$bsl")}
|
||||||
BUCKET=${BUCKET:-$(jq -r '.spec.objectStorage.bucket' <<<"$bsl")}
|
BUCKET=${BUCKET:-$(jq -r '.spec.objectStorage.bucket' <<<"$bsl")}
|
||||||
fi
|
fi
|
||||||
@@ -101,10 +101,10 @@ if [[ -n ${FROM_VOLUME:-} ]]; then
|
|||||||
docker run --rm -v "$FROM_VOLUME:/from:ro" -v "$VOL:/data" alpine:3.22 cp -a /from/. /data/
|
docker run --rm -v "$FROM_VOLUME:/from:ro" -v "$VOL:/data" alpine:3.22 cp -a /from/. /data/
|
||||||
else
|
else
|
||||||
echo "== copy from the NAS (read-only)"
|
echo "== copy from the NAS (read-only)"
|
||||||
(umask 077; pk -n velero get secret velero-s3-credentials -o jsonpath='{.data.cloud}' | base64 -d |
|
(umask 077; pk -n velero get secret cloud-credentials -o jsonpath='{.data.cloud}' | base64 -d |
|
||||||
awk -F' *= *' '/aws_access_key_id/ {print "RCLONE_CONFIG_NAS_ACCESS_KEY_ID="$2}
|
awk -F' *= *' '/aws_access_key_id/ {print "RCLONE_CONFIG_NAS_ACCESS_KEY_ID="$2}
|
||||||
/aws_secret_access_key/ {print "RCLONE_CONFIG_NAS_SECRET_ACCESS_KEY="$2}' > "$WORK/nas.env"
|
/aws_secret_access_key/ {print "RCLONE_CONFIG_NAS_SECRET_ACCESS_KEY="$2}' > "$WORK/nas.env"
|
||||||
printf 'RCLONE_CONFIG_NAS_TYPE=s3\nRCLONE_CONFIG_NAS_PROVIDER=Other\nRCLONE_CONFIG_NAS_ENDPOINT=%s\nRCLONE_CONFIG_NAS_REGION=%s\n' "$S3URL" "$REGION" >> "$WORK/nas.env")
|
printf 'RCLONE_CONFIG_NAS_TYPE=s3\nRCLONE_CONFIG_NAS_PROVIDER=Minio\nRCLONE_CONFIG_NAS_ENDPOINT=%s\nRCLONE_CONFIG_NAS_REGION=%s\n' "$MINIO" "$REGION" >> "$WORK/nas.env")
|
||||||
copy() { docker run --rm --env-file "$WORK/nas.env" -v "$VOL:/data" "$RCLONE" copy --stats-one-line "nas:$BUCKET/$1" "/data/$BUCKET/$1"; }
|
copy() { docker run --rm --env-file "$WORK/nas.env" -v "$VOL:/data" "$RCLONE" copy --stats-one-line "nas:$BUCKET/$1" "/data/$BUCKET/$1"; }
|
||||||
copy "backups/$backup"
|
copy "backups/$backup"
|
||||||
for ns in "${namespaces[@]}"; do copy "kopia/$ns"; done
|
for ns in "${namespaces[@]}"; do copy "kopia/$ns"; done
|
||||||
|
|||||||
+10
-4
@@ -360,10 +360,16 @@ PR before Argo CD applies it.
|
|||||||
cover every app folder, a regex manager reads `imageName` in
|
cover every app folder, a regex manager reads `imageName` in
|
||||||
`*/postgresql-cluster*.yaml`, and major PostgreSQL updates are off
|
`*/postgresql-cluster*.yaml`, and major PostgreSQL updates are off
|
||||||
(those need pg_upgrade or a dump). Expect a batch of new PRs
|
(those need pg_upgrade or a dump). Expect a batch of new PRs
|
||||||
(2026-10-25; `renovate-config-validator` passes). They only come once
|
(2026-10-25). They only come once fewer than 10 Renovate PRs are open
|
||||||
fewer than 10 Renovate PRs are open (`config:recommended`'s
|
(`config:recommended`'s `prConcurrentLimit`); until then they wait
|
||||||
`prConcurrentLimit`); until then they wait under "Rate-Limited" on the
|
under "Rate-Limited" on the Dependency Dashboard (#2)
|
||||||
Dependency Dashboard (#2)
|
- The other folders were left out with a `(?!…)` lookahead. Renovate
|
||||||
|
compiles these patterns with RE2, which has no lookahead, so it
|
||||||
|
rejected the config and stopped opening PRs (#32). Each manager now
|
||||||
|
takes every folder and skips the others through its own
|
||||||
|
`ignorePaths`. Renovate 44.181.6 with RE2 loaded fails the old config
|
||||||
|
with #32's error and passes the new one, and its matcher picks the
|
||||||
|
same 166 files as the old pattern did
|
||||||
- [x] Merged 2026-10-25 and checked after the sync: #28 cert-manager
|
- [x] Merged 2026-10-25 and checked after the sync: #28 cert-manager
|
||||||
v1.21.3 (security release; all three pods on v1.21.3), #24
|
v1.21.3 (security release; all three pods on v1.21.3), #24
|
||||||
external-snapshotter v8.7.0 (its manifests ship the v8.6.0 image, as
|
external-snapshotter v8.7.0 (its manifests ship the v8.6.0 image, as
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# node001 passes TLS for the tunneled hosts through without opening it, so
|
||||||
|
# without this every request arrives from 46.224.26.65. Wordfence locks that
|
||||||
|
# address out after 20 failed logins (2026-10-26), and with it everyone who
|
||||||
|
# logs in to scoutingmbg.nl. node001 puts a PROXY header with the visitor's
|
||||||
|
# address in front of each connection; Envoy takes the address from there
|
||||||
|
# and adds it to X-Forwarded-For.
|
||||||
|
#
|
||||||
|
# Optional, because s2 and ntfy2 point straight at home and their clients
|
||||||
|
# send no header, and neither does node001's proxy on port 80. The price:
|
||||||
|
# whoever connects to the home address directly can send a header of their
|
||||||
|
# own and claim any address.
|
||||||
|
apiVersion: gateway.envoyproxy.io/v1alpha1
|
||||||
|
kind: ClientTrafficPolicy
|
||||||
|
metadata:
|
||||||
|
name: proxy-protocol
|
||||||
|
namespace: envoy-gateway-system
|
||||||
|
spec:
|
||||||
|
targetRefs:
|
||||||
|
- group: gateway.networking.k8s.io
|
||||||
|
kind: Gateway
|
||||||
|
name: homelab
|
||||||
|
proxyProtocol:
|
||||||
|
optional: true
|
||||||
+14
-3
@@ -127,6 +127,9 @@ spec:
|
|||||||
TRANSFORMERS_CACHE: /cache
|
TRANSFORMERS_CACHE: /cache
|
||||||
HF_XET_CACHE: /cache/huggingface-xet
|
HF_XET_CACHE: /cache/huggingface-xet
|
||||||
MPLCONFIGDIR: /cache/matplotlib-config
|
MPLCONFIGDIR: /cache/matplotlib-config
|
||||||
|
# The optimized models of v3.3.0: faster, less memory,
|
||||||
|
# and the same results, so no jobs to run again.
|
||||||
|
MACHINE_LEARNING_MODEL_REVISION: v2
|
||||||
# Trust store without the compromised eMudhra
|
# Trust store without the compromised eMudhra
|
||||||
# intermediates; requests would otherwise use certifi.
|
# intermediates; requests would otherwise use certifi.
|
||||||
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
|
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
|
||||||
@@ -139,11 +142,19 @@ spec:
|
|||||||
globalMounts:
|
globalMounts:
|
||||||
- path: /etc/ssl/certs
|
- path: /etc/ssl/certs
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
# The models and their one-off preparation, kept across restarts.
|
||||||
|
# Downloaded again if lost, so no backups: the no-backup group
|
||||||
|
# keeps it out of Longhorn's default one (backup-daily), and
|
||||||
|
# Velero skips that label too (velero/skip-emptydir-policy.yaml).
|
||||||
cache:
|
cache:
|
||||||
enabled: true
|
enabled: true
|
||||||
# Optional: to avoid downloading the ML models every start, use
|
type: persistentVolumeClaim
|
||||||
# a persistentVolumeClaim with size, accessMode and storageClass
|
storageClass: longhorn
|
||||||
type: emptyDir
|
accessMode: ReadWriteOnce
|
||||||
|
size: 10Gi
|
||||||
|
labels:
|
||||||
|
recurring-job.longhorn.io/source: enabled
|
||||||
|
recurring-job-group.longhorn.io/no-backup: enabled
|
||||||
|
|
||||||
destination:
|
destination:
|
||||||
server: https://kubernetes.default.svc
|
server: https://kubernetes.default.svc
|
||||||
|
|||||||
@@ -173,12 +173,18 @@ in
|
|||||||
# - mail.rubenhensen.nl (and anything else) → local stalwart on 8443
|
# - mail.rubenhensen.nl (and anything else) → local stalwart on 8443
|
||||||
# - tunneled hosts → home cluster ingress on 443
|
# - tunneled hosts → home cluster ingress on 443
|
||||||
# - s1 / ntfy1.rubenhensen.nl → local nginx on 8444 (Gatus, ntfy)
|
# - s1 / ntfy1.rubenhensen.nl → local nginx on 8444 (Gatus, ntfy)
|
||||||
|
#
|
||||||
|
# Each connection starts with a PROXY header that carries the visitor's
|
||||||
|
# address, so the home cluster sees who is asking instead of this box
|
||||||
|
# (gateway/proxy-protocol.yaml in k8scd). nginx sends the header to
|
||||||
|
# every upstream or none, so the local ones go through a hop on
|
||||||
|
# 8445/8446 that takes it off again.
|
||||||
streamConfig = ''
|
streamConfig = ''
|
||||||
map $ssl_preread_server_name $tunnel_upstream {
|
map $ssl_preread_server_name $tunnel_upstream {
|
||||||
${sniMapEntries}
|
${sniMapEntries}
|
||||||
s1.rubenhensen.nl 127.0.0.1:8444;
|
s1.rubenhensen.nl 127.0.0.1:8446;
|
||||||
ntfy1.rubenhensen.nl 127.0.0.1:8444;
|
ntfy1.rubenhensen.nl 127.0.0.1:8446;
|
||||||
default 127.0.0.1:8443;
|
default 127.0.0.1:8445;
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -186,6 +192,17 @@ ${sniMapEntries}
|
|||||||
listen [::]:443;
|
listen [::]:443;
|
||||||
proxy_pass $tunnel_upstream;
|
proxy_pass $tunnel_upstream;
|
||||||
ssl_preread on;
|
ssl_preread on;
|
||||||
|
proxy_protocol on;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:8445 proxy_protocol;
|
||||||
|
proxy_pass 127.0.0.1:8443;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:8446 proxy_protocol;
|
||||||
|
proxy_pass 127.0.0.1:8444;
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|||||||
+18
-2
@@ -5,8 +5,16 @@
|
|||||||
"customManagers:githubActionsVersions"
|
"customManagers:githubActionsVersions"
|
||||||
],
|
],
|
||||||
"argocd": {
|
"argocd": {
|
||||||
|
"description": "Every app folder; Renovate's regexes are RE2, which has no lookahead, so the other folders are skipped through ignorePaths",
|
||||||
"managerFilePatterns": [
|
"managerFilePatterns": [
|
||||||
"/^(?!(nix-infra-machine|backup-testing|docs|tools|\\.github)/)[^/]+/.+\\.yaml$/"
|
"/^[^/]+/.+\\.yaml$/"
|
||||||
|
],
|
||||||
|
"ignorePaths": [
|
||||||
|
"nix-infra-machine/**",
|
||||||
|
"backup-testing/**",
|
||||||
|
"docs/**",
|
||||||
|
"tools/**",
|
||||||
|
".github/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"helm-values": {
|
"helm-values": {
|
||||||
@@ -15,8 +23,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"kubernetes": {
|
"kubernetes": {
|
||||||
|
"description": "Every app folder, as for argocd",
|
||||||
"managerFilePatterns": [
|
"managerFilePatterns": [
|
||||||
"/^(?!(nix-infra-machine|backup-testing|docs|tools|\\.github)/)[^/]+/.+\\.yaml$/"
|
"/^[^/]+/.+\\.yaml$/"
|
||||||
|
],
|
||||||
|
"ignorePaths": [
|
||||||
|
"nix-infra-machine/**",
|
||||||
|
"backup-testing/**",
|
||||||
|
"docs/**",
|
||||||
|
"tools/**",
|
||||||
|
".github/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"customManagers": [
|
"customManagers": [
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: nas-minio-external
|
||||||
|
spec:
|
||||||
|
type: ExternalName
|
||||||
|
externalName: 192.168.1.99
|
||||||
|
ports:
|
||||||
|
- port: 9000
|
||||||
|
protocol: TCP
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
# Volume policy for the backup schedules. emptyDir volumes hold nothing
|
# Volume policy for the backup schedules. emptyDir volumes hold nothing
|
||||||
# worth keeping, and backing them up fails when the pod is gone before its
|
# worth keeping, and backing them up fails when the pod is gone before its
|
||||||
# volume is read, as happens with Velero's own repository maintenance jobs.
|
# volume is read, as happens with Velero's own repository maintenance jobs.
|
||||||
|
# PVCs in Longhorn's no-backup group (caches, such as immich's ML models)
|
||||||
|
# are skipped too.
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
metadata:
|
metadata:
|
||||||
@@ -14,3 +16,8 @@ data:
|
|||||||
- emptyDir
|
- emptyDir
|
||||||
action:
|
action:
|
||||||
type: skip
|
type: skip
|
||||||
|
- conditions:
|
||||||
|
pvcLabels:
|
||||||
|
recurring-job-group.longhorn.io/no-backup: enabled
|
||||||
|
action:
|
||||||
|
type: skip
|
||||||
@@ -27,22 +27,20 @@ spec:
|
|||||||
- mountPath: /target
|
- mountPath: /target
|
||||||
name: plugins
|
name: plugins
|
||||||
configuration:
|
configuration:
|
||||||
# Garage on the NAS (nas/garage). Until 2026-10-25 this was MinIO on
|
|
||||||
# the same NAS; the bucket was copied over with rclone, so the
|
|
||||||
# existing backups and kopia repositories carried on.
|
|
||||||
backupStorageLocation:
|
backupStorageLocation:
|
||||||
- name: default
|
- name: default
|
||||||
bucket: velero-backups
|
bucket: velero-backups
|
||||||
provider: aws
|
provider: aws
|
||||||
config:
|
config:
|
||||||
region: garage
|
region: minio
|
||||||
s3ForcePathStyle: true
|
s3ForcePathStyle: true
|
||||||
s3Url: http://192.168.1.99:3900
|
s3Url: http://192.168.1.99:9000
|
||||||
|
insecureSkipTLSVerify: "true"
|
||||||
volumeSnapshotLocation:
|
volumeSnapshotLocation:
|
||||||
- name: default
|
- name: default
|
||||||
provider: aws
|
provider: aws
|
||||||
config:
|
config:
|
||||||
region: garage
|
region: minio
|
||||||
features: EnableCSI
|
features: EnableCSI
|
||||||
# Kopia maintenance ran every hour for each of the 15 repositories,
|
# Kopia maintenance ran every hour for each of the 15 repositories,
|
||||||
# and once a day as a full run of several minutes that writes its
|
# and once a day as a full run of several minutes that writes its
|
||||||
@@ -61,8 +59,11 @@ spec:
|
|||||||
memoryRequest: 256Mi
|
memoryRequest: 256Mi
|
||||||
memoryLimit: 2Gi
|
memoryLimit: 2Gi
|
||||||
credentials:
|
credentials:
|
||||||
# From Vault, velero/external-secret.yaml
|
# Use existing secret created by External Secrets Operator
|
||||||
existingSecret: velero-s3-credentials
|
# existingSecret: velero-minio-credentials
|
||||||
|
# extraEnvVars:
|
||||||
|
# AWS_SHARED_CREDENTIALS_FILE: /credentials/cloud
|
||||||
|
existingSecret: cloud-credentials
|
||||||
deployNodeAgent: true
|
deployNodeAgent: true
|
||||||
nodeAgent:
|
nodeAgent:
|
||||||
containerSecurityContext:
|
containerSecurityContext:
|
||||||
|
|||||||
Reference in new issue
Block a user