From 29c31421748f9974ca70de0cc4b612511e66690f Mon Sep 17 00:00:00 2001 From: Maarten de Waard <maarten@greenhost.nl> Date: Tue, 9 Jun 2020 10:50:30 +0200 Subject: [PATCH] write backups to remote storage --- Chart.yaml | 2 +- RELEASING.md | 6 +- templates/secrets.yaml | 4 ++ templates/statefulset.yaml | 70 ++++++++----------- values-local.yaml.example | 23 +++--- values.yaml | 40 +++-------- wp-cli-docker/Dockerfile | 13 +++- .../roles/wordpress-backup/tasks/main.yml | 17 +++-- wp-cli-docker/scripts/backup.sh | 6 +- 9 files changed, 82 insertions(+), 99 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index b07f4f1..5778010 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -4,5 +4,5 @@ description: WordPress with a replicated MariaDB backend name: wordpress # Please only change the chart version as part of the release procedure: see # RELEASING.md -version: 0.1.1 +version: 0.1.0 icon: https://make.wordpress.org/design/files/2016/09/WordPress-logotype-wmark.png diff --git a/RELEASING.md b/RELEASING.md index 46da487..8438232 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,9 +4,9 @@ When releasing a new version of the wordpress-helm chart, please remember to do the following: * change the chart version in `Chart.yaml`; * change the default `image.tag` and `initImage.tag` in `values.yaml` to the new - version (e.g., "0.1.2"); -* create a git tag for the new version (e.g., "0.1.2") and push it to Gitlab + version (e.g., "0.1.0"); +* create a git tag for the new version (e.g., "0.1.0") and push it to Gitlab (any branch will do); the CI will create and push docker images tagged by that same version string. (You can push all git tags using `git push --tags`, or this specific one using - `git push origin 0.1.2`.) + `git push origin 0.1.0`.) diff --git a/templates/secrets.yaml b/templates/secrets.yaml index ff5625f..d124688 100644 --- a/templates/secrets.yaml +++ b/templates/secrets.yaml @@ -10,3 +10,7 @@ metadata: type: Opaque data: secret-vars.yaml: {{ tpl .Values.ansibleSecrets . | b64enc }} + {{- if .Values.backup.sshPrivateKey }} + ssh-private-key: {{ .Values.backup.sshPrivateKey | b64enc }} + ssh-known-hosts: {{ .Values.backup.sshKnownHosts | b64enc }} + {{- end }} diff --git a/templates/statefulset.yaml b/templates/statefulset.yaml index 2967e4a..3976adf 100644 --- a/templates/statefulset.yaml +++ b/templates/statefulset.yaml @@ -22,9 +22,6 @@ spec: # gets re-run. checksum/config: {{ include (print $.Template.BasePath "/ansible-vars.yaml") . | sha256sum }} checksum/config: {{ include (print $.Template.BasePath "/secrets.yaml") . | sha256sum }} - {{- if .Values.podAnnotations }} - {{- toYaml .Values.podAnnotations | nindent 8 }} - {{- end }} spec: imagePullSecrets: - name: {{ .Values.initImage.imagePullSecretName }} @@ -62,9 +59,6 @@ spec: value: {{ .Values.database.db.name }} - name: WORDPRESS_TABLE_PREFIX value: {{ .Values.wordPressTablePrefix }} - # TODO: Remove this debug - - name: WORDPRESS_DEBUG - value: "true" containers: - name: {{ .Chart.Name }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" @@ -74,28 +68,11 @@ spec: containerPort: 80 - name: https containerPort: 443 - {{- if .Values.livenessProbe.enabled }} - livenessProbe: - httpGet: - path: {{ .Values.wordpress.site.probe_path }} - port: http - initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} - successThreshold: {{ .Values.livenessProbe.successThreshold }} - failureThreshold: {{ .Values.livenessProbe.failureThreshold }} - {{- end }} - {{- if .Values.readinessProbe.enabled }} - readinessProbe: - httpGet: - path: {{ .Values.wordpress.site.probe_path }} - port: http - initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} - successThreshold: {{ .Values.readinessProbe.successThreshold }} - failureThreshold: {{ .Values.readinessProbe.failureThreshold }} - {{- end }} + # livenessProbe: + # httpGet: + # path: /wp-login.php + # port: http + # timeoutSeconds: 2 env: - name: WORDPRESS_DB_HOST value: {{ .Release.Name }}-database @@ -110,9 +87,6 @@ spec: value: {{ .Values.database.db.name }} - name: WORDPRESS_TABLE_PREFIX value: {{ .Values.wordPressTablePrefix }} - # TODO: Remove this debug - - name: WORDPRESS_DEBUG - value: "true" # readinessProbe: # httpGet: # path: / @@ -152,8 +126,9 @@ spec: value: {{ .Values.database.db.name }} - name: WORDPRESS_TABLE_PREFIX value: {{ .Values.wordPressTablePrefix }} - - name: BACKUP_NAME - value: {{ .Release.Name }} + - name: BACKUP_INTERVAL_SECONDS + # A day's worth of seconds. + value: "86400" volumeMounts: - name: {{ include "wordpress.name" . }}-wp-storage mountPath: /var/www/html @@ -166,8 +141,13 @@ spec: subPath: main.yml - name: ansible-secrets mountPath: /var/local/ansible/secrets - - name: {{ include "wordpress.name" . }}-backups - mountPath: /mnt/backups + {{- if .Values.backup.sshPrivateKey }} + - name: ssh-private-key + mountPath: /var/local/ssh-private-key + - name: ssh-known-hosts + mountPath: /etc/ssh/ssh_known_hosts + subPath: ssh_known_hosts + {{- end }} {{- end }} {{- with .Values.nodeSelector }} nodeSelector: @@ -196,18 +176,26 @@ spec: - name: ansible-secrets secret: secretName: {{ include "wordpress.fullname" . }}-ansible-secrets - # TODO: this might not be necessary items: - key: secret-vars.yaml path: secret-vars.yaml + {{- if .Values.backup.sshPrivateKey }} + - name: ssh-private-key + secret: + secretName: {{ include "wordpress.fullname" . }}-ansible-secrets + items: + - key: ssh-private-key + path: ssh-private-key + - name: ssh-known-hosts + secret: + secretName: {{ include "wordpress.fullname" . }}-ansible-secrets + items: + - key: ssh-known-hosts + path: ssh_known_hosts + {{- end }} - name: ansible-vars configMap: name: {{ include "wordpress.fullname" . }}-ansible-vars - name: htuploads configMap: name: {{ include "wordpress.fullname" . }}-htuploads - {{- if .Values.backup.enabled }} - - name: {{ include "wordpress.name" . }}-backups - persistentVolumeClaim: - claimName: {{ .Values.backup.pvcName }} - {{- end }} diff --git a/values-local.yaml.example b/values-local.yaml.example index 9277203..924aac2 100644 --- a/values-local.yaml.example +++ b/values-local.yaml.example @@ -28,8 +28,7 @@ wordpress: theme: twentynineteen # NOTE: Make sure you use underscore and that the localisation is in full caps locale: en_US - # NOTE: Optionally set a Wordpress version number to override the default version: in values.yaml - # version: LOCAL-WORDPRESS-VERSION-NUMBER-OR-DELETE-THIS-LINE + version: 5.2.3 # NOTE: This is the URL that points to your WordPress installation. If this # URL is set incorrectly your site will most likely not work. You can not # change it after you have run helm install once because WordPress saves the @@ -43,10 +42,6 @@ wordpress: alt_enabled: false # alt_config: PATH-SETTING-IN-OPTIONS-TABLE # alt_path: SOME-LOGIN-PATH - # # Path used by the liveness and readiness probes to see if the site runs - # # correctly. Defaults to `/wp-login.php`. Be sure to make this the same as - # # alt_path if you use it! - # probe_path: /wp-login.php wp_content: # The directory to mount the files placed in wp-content. You shouldn't have to # change this. @@ -125,12 +120,20 @@ database: # This will add a cronjob that performs a daily backup of the wordpress # database, copying an sql file created by `wp db export` to the given PVC. -# The persistent volume is not created by this chart, because we don't want it -# to be managed by helm, so you will need to create it via some other means. # backup: # enabled: true -# pvcName: wordpress-backups - +# # The target location of the backup. This can be a local directory (not +# # advisable) or a remote directory reachable over SSH. backup command uses +# # this value as the second argument for `rsync` +# target: <username@server.example.org:backup-dir/> +# # If `backup.target` is an SSH address, use this private key: +# sshPrivateKey: | +# -----BEGIN OPENSSH PRIVATE KEY----- +# ... +# -----END OPENSSH PRIVATE KEY----- +# # Mounted to /etc/ssh/known_hosts +# sshKnownHosts: | +# hostname keytype key # It's advisable to set resource limits to prevent your K8s cluster from # crashing # resources: diff --git a/values.yaml b/values.yaml index a54ce24..c1fd126 100644 --- a/values.yaml +++ b/values.yaml @@ -87,17 +87,14 @@ wordpress: theme_fallback: twentytwenty # NOTE: Make sure you use underscore and that the localisation is in full caps locale: en_US - version: 5.4.1 + version: 5.2.3 url: "http://localhost" title: "Wordpress Helm" ## If including a plugin to alias wp login then set an alt_path and set the alt_config # NOTE: A value for alt_enabled must be set. Select either true or false - alt_enabled: false + alt_enabled: false # alt_config: PATH-SETTING-IN-OPTIONS-TABLE # alt_path: SOME-LOGIN-PATH - # Path used by the liveness and readiness probes to see if the site runs - # correctly. Defaults to `/wp-login.php` - probe_path: /wp-login.php wp_content: ## The directory to mount the files placed in wp-content. You shouldn't have to ## change this. @@ -169,14 +166,15 @@ ansibleVars: wpSalts: {} image: - repository: open.greenhost.net:4567/open/wordpress-helm/wordpress - tag: 0.1.1 + repository: docker.greenhost.net/open/wordpress-helm/wordpress + tag: 0.1.0 pullPolicy: Always initImage: - repository: open.greenhost.net:4567/open/wordpress-helm/wordpress-cli-ansible - tag: 0.1.1 + repository: docker.greenhost.net/open/wordpress-helm/wordpress-cli-ansible + tag: 0.1.0 pullPolicy: Always + imagePullSecretName: greenhost-registry-pull ingress: enabled: false @@ -191,9 +189,6 @@ nodeSelector: {} tolerations: [] affinity: {} - -podAnnotations: {} - database: db: user: wordpress @@ -266,6 +261,8 @@ backup: # completely stored as a b64encoded secret in Kubernetes. If you're not a # developer, never change this variable, only change the variables it points to. ansibleSecrets: | + BACKUP_NAME: {{ .Release.Name }} + BACKUP_TARGET: {{ .Values.backup.target }} DB_HOST: {{ .Release.Name }}-database DB_NAME: {{ .Values.database.db.name }} DB_PASS: {{ .Values.database.db.password }} @@ -336,22 +333,3 @@ ansibleSecrets: | SECURE_AUTH_SALT: {{ .Values.wpSalts.SECURE_AUTH_SALT | default ( randAlphaNum 32) }} WP_CACHE_KEY_SALT: {{ .Values.wpSalts.WP_CACHE_KEY_SALT | default ( randAlphaNum 32) }} WP_CRON_CONTROL_SECRET: {{ .Values.wpSalts.WP_CRON_CONTROL_SECRET | default ( randAlphaNum 32 ) }} - -## Liveness and readiness probe values -## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes -## -livenessProbe: - enabled: true - initialDelaySeconds: 60 - periodSeconds: 15 - timeoutSeconds: 5 - failureThreshold: 3 - successThreshold: 1 - -readinessProbe: - enabled: true - initialDelaySeconds: 10 - periodSeconds: 15 - timeoutSeconds: 5 - failureThreshold: 3 - successThreshold: 1 diff --git a/wp-cli-docker/Dockerfile b/wp-cli-docker/Dockerfile index ea50924..3d136ee 100644 --- a/wp-cli-docker/Dockerfile +++ b/wp-cli-docker/Dockerfile @@ -1,11 +1,20 @@ -FROM wordpress:cli-2.4-php7.3 +FROM wordpress:cli-2.1.0-php7.3 USER root -RUN apk add ansible git rsync +RUN apk add ansible git rsync openssh ADD . /var/local/ansible ENV ANSIBLE_CONFIG /var/local/ansible/ansible.cfg +# This is the homedir of user 33 in the alpine container and needs to exist and +# be writable for ssh to work. +RUN mkdir -p /etc/X11/fs && chown -R 33:33 /etc/X11/fs + +# SSH Identity file location for backup playbook. It's in /etc/X11/fs which is +# the default home directory for the user with ID 33, which is the UID of the +# www-data user in the Debian container that runs the site... +RUN mkdir -p /etc/ssh/ && echo "IdentityFile /var/local/ssh-private-key/ssh-private-key" > /etc/ssh/ssh_config + # Chown the files to the Debian www-data user, because that's the only WP # container that runs Apache too. RUN chown -R 33:33 /var/local/ansible; \ diff --git a/wp-cli-docker/roles/wordpress-backup/tasks/main.yml b/wp-cli-docker/roles/wordpress-backup/tasks/main.yml index 0793499..9ffe7a5 100644 --- a/wp-cli-docker/roles/wordpress-backup/tasks/main.yml +++ b/wp-cli-docker/roles/wordpress-backup/tasks/main.yml @@ -7,16 +7,19 @@ changed_when: false - block: - - name: Create backups directory - file: - path: "{{ backup_dir }}" + - name: Create temporary backups directory + tempfile: state: directory + suffix: backup + register: backup_dir - name: Export WordPress database to file shell: - wp {{ cli_args }} db export "{{ backup_dir }}/{{ backup_filename }}" + wp {{ cli_args }} db export "{{ backup_dir.path }}/{{ backup_filename }}" # Never overwrite an existing file. args: - creates: "{{ backup_dir }}/{{ backup_filename }}" + creates: "{{ backup_dir.path }}/{{ backup_filename }}" + - name: Copy export to backup location + shell: + rsync -a "{{ backup_dir.path }}/{{ backup_filename }}" "{{ BACKUP_TARGET }}" vars: - backup_dir: "/mnt/backups/wp-db/{{ lookup('env', 'BACKUP_NAME') }}" - backup_filename: "wp-db-{{ lookup('env', 'BACKUP_NAME') }}-{{ date.stdout }}.sql" + backup_filename: "wp-db-{{ BACKUP_NAME }}-{{ date.stdout }}.sql" diff --git a/wp-cli-docker/scripts/backup.sh b/wp-cli-docker/scripts/backup.sh index 078335f..341c321 100755 --- a/wp-cli-docker/scripts/backup.sh +++ b/wp-cli-docker/scripts/backup.sh @@ -1,14 +1,12 @@ #!/bin/bash backupCommand="ansible-playbook backup.yml -e @secrets/secret-vars.yaml" -# A day's worth of seconds. -interval=86400 while true do date - echo "Waiting for $interval seconds before starting next backup." - sleep $interval + echo "Waiting for $BACKUP_INTERVAL_SECONDS seconds before starting next backup." + sleep $BACKUP_INTERVAL_SECONDS $backupCommand exitCode=$? if [ $exitCode -ne 0 ] -- GitLab