diff --git a/CHANGELOG.md b/CHANGELOG.md index c9e5316ca023815ed9172b155622f885a8bed915..53e6808fa131e78799f80f875e66b8fc4a002f0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ -## Unreleased - +## [0.3.0] - 2021-09-28 + +* Cron overhaul: + - Change the sidecar container to run a cron daemon instead of the backup + script with manual sleep. + - Put the backup script in a crontab. + - Remove the kubernetes CronJob that did the wordpress cron calling, + replacing it by a regular cronjob. + - Allow custom crontab entries, provided from a helm value. * Update mariadb chart to 9.6.0 NOTE: the mariadb chart does not provide backwards compatibility in this case, so manual action is required if you want to upgrade an existing diff --git a/Chart.yaml b/Chart.yaml index 418aaff423fcee679febde8ab062f21dba5e42b6..662d7f153c8ba6e961471940552d31c59dd8267e 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -5,7 +5,7 @@ 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.2.2 +version: 0.3.0 icon: https://make.wordpress.org/design/files/2016/09/WordPress-logotype-wmark.png dependencies: - name: mariadb diff --git a/README.md b/README.md index 5243d15476af4de8e2391b82eac1020ca0f7301a..e4324e519e77b746b7404a14ba5e9bdf58c2c3b8 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,9 @@ $ kubectl logs <pod> -c init-wordpress Helm will set up the kubernetes pods that are needed to run your website: 1. A WordPress pod that serves the site + - If you have `backup.enabled` or `wordpress.mu_cron.enabled`, or have a + non-empty `customCron`, this pod will also contain a sidecar container that + runs cron jobs. 2. Two MariaDB pods running the database (master-slave setup by default, unless you changed this in `values-local.yaml`) 3. If you configured Redis, a Redis pod is also set up diff --git a/templates/cron-schedule.yaml b/templates/cron-schedule.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a9ab91aa4cf405813ebd94d99ae8937578714071 --- /dev/null +++ b/templates/cron-schedule.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "wordpress.fullname" . }}-cron-schedule + labels: + app: {{ include "wordpress.name" . }} + chart: {{ include "wordpress.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + www-data: |- +{{- if .Values.wordpress.mu_cron.enabled }} + {{ .Values.wordpress.mu_cron.cronjob.schedule }} curl -s -w '%{http_code}' {{- if .Values.wordpress.mu_cron.cronjob.curlInsecure }} -k {{- end }} -L 'http://{{ include "wordpress.fullname" . }}:{{ .Values.service.port }}{{ .Values.wordpress.mu_cron.cronjob.path }}?doing_wp_cron&{{ required "Please set wordpress.mu_cron.secret to a random secret" .Values.wordpress.mu_cron.secret }}' +{{- end }} +{{- if .Values.backup.enabled }} + {{ .Values.backup.schedule }} cd /var/local/ansible && ansible-playbook backup.yml -e @secrets/secret-vars.yaml +{{- end }} +{{- range .Values.customCron }} + {{ .schedule }} {{ .command }} +{{- end }} diff --git a/templates/cronjob.yaml b/templates/cronjob.yaml deleted file mode 100644 index d344d56bcdc1e666dcfee8466b35299eb75c895a..0000000000000000000000000000000000000000 --- a/templates/cronjob.yaml +++ /dev/null @@ -1,94 +0,0 @@ -{{- if .Values.wordpress.mu_cron.enabled }} -apiVersion: batch/v1beta1 -kind: CronJob -metadata: - name: {{ template "wordpress.fullname" . }} - labels: - app: {{ include "wordpress.name" . }} - chart: {{ include "wordpress.chart" . }} - release: {{ .Release.Name }} - heritage: {{ .Release.Service }} - annotations: - {{- toYaml .Values.wordpress.mu_cron.cronjob.annotations | nindent 4 }} -spec: - schedule: "{{ .Values.wordpress.mu_cron.cronjob.schedule }}" - concurrencyPolicy: Forbid - {{- with .Values.wordpress.mu_cron.cronjob.failedJobsHistoryLimit }} - failedJobsHistoryLimit: {{ . }} - {{- end }} - {{- with .Values.wordpress.mu_cron.cronjob.successfulJobsHistoryLimit }} - successfulJobsHistoryLimit: {{ . }} - {{- end }} - jobTemplate: - metadata: - labels: - app.kubernetes.io/name: {{ include "wordpress.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - spec: - {{- with .Values.wordpress.mu_cron.cronjob.backoffLimit }} - backoffLimit: {{ . }} - {{- end }} - template: - metadata: - labels: - app.kubernetes.io/name: {{ include "wordpress.name" . }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - spec: - # Set a custom service account which has access to the WordPress - # statefulset's state - serviceAccountName: {{ include "wordpress.fullname" . }}-cron - restartPolicy: Never - {{- if (default .Values.image.pullSecrets .Values.wordpress.mu_cron.cronjob.image.pullSecrets) }} - imagePullSecrets: - {{- range (default .Values.image.pullSecrets .Values.wordpress.mu_cron.cronjob.image.pullSecrets) }} - - name: {{ . }} - {{- end }} - {{- end }} - containers: - - name: {{ .Chart.Name }}-cron-caller - image: "{{ default .Values.image.repository .Values.wordpress.mu_cron.cronjob.image.repository }}:{{ default .Values.image.tag .Values.wordpress.mu_cron.cronjob.image.tag }}" - imagePullPolicy: {{ default .Values.image.pullPolicy .Values.wordpress.mu_cron.cronjob.image.pullPolicy }} - command: [ "/bin/bash" ] - args: - - -c - - | - # NOTE: we use "{{` ... `}}" to make sure the curly braces are not templated by Helm. Returns <#readyReplicas>,<#replicasWanted> - equation=$(kubectl get statefulset {{ include "wordpress.fullname" . }} --template '{{ `{{.status.readyReplicas}},{{.status.replicas}}` }}') - # Make sure kubectl command did not fail - if [ $? -ne 0 ]; then - echo "Kubernetes command failed"; - exit 2; - fi - # Check if part before comma and after comma are equal - if [[ "${equation%,*}" == "${equation#*,}" ]]; then - output=$(curl -s -w '%{http_code}' {{- if .Values.wordpress.mu_cron.cronjob.curlInsecure }} -k {{- end }} -L 'http://{{ include "wordpress.fullname" . }}:{{ .Values.service.port }}{{ .Values.wordpress.mu_cron.cronjob.path }}?doing_wp_cron&{{ required "Please set wordpress.mu_cron.secret to a random secret" .Values.wordpress.mu_cron.secret }}') - # Note that if the output is 200invalid secret string, you - # need to provide the correct secret! - if [[ "$output" == "200" ]]; then - echo "success"; - exit 0 - else - echo "failed with output '$output'"; - exit 1 - fi - fi - # If we reach this point, the statefulset is not ready yet - echo "Service is not ready, doing nothing" - exit 0 - {{- with .Values.wordpress.mu_cron.cronjob.resources }} - resources: - {{ toYaml . | nindent 16 }} - {{- end }} - {{- with (default .Values.nodeSelector .Values.wordpress.mu_cron.cronjob.nodeSelector) }} - nodeSelector: -{{ toYaml . | indent 12 }} - {{- end }} - {{- with (default .Values.affinity .Values.wordpress.mu_cron.cronjob.affinity) }} - affinity: -{{ toYaml . | indent 12 }} - {{- end }} - {{- with (default .Values.tolerations .Values.wordpress.mu_cron.cronjob.tolerations) }} - tolerations: -{{ toYaml . | indent 12 }}: - {{- end }} -{{- end }} diff --git a/templates/rbac.yaml b/templates/rbac.yaml deleted file mode 100644 index 78467f25f3900348ba9dc14b8bc73bdbd5b7c92c..0000000000000000000000000000000000000000 --- a/templates/rbac.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- if .Values.wordpress.mu_cron.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: get-{{ include "wordpress.fullname" . }}-statefulset -rules: - - apiGroups: ["apps"] - resources: ["statefulsets"] - resourceNames: [{{ include "wordpress.fullname" . }}] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: read-{{ include "wordpress.fullname" . }}-statefulset -subjects: - - kind: ServiceAccount - name: {{ include "wordpress.fullname" . }}-cron - namespace: {{ .Release.Namespace }} -roleRef: - kind: Role - name: get-{{ include "wordpress.fullname" . }}-statefulset - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "wordpress.fullname" . }}-cron -{{- end }} diff --git a/templates/statefulset.yaml b/templates/statefulset.yaml index 8d5595585f5a1441fcd72d554d2f71a55b3268cc..02207ec25d2a55a81141b2cedd7663852cc83740 100644 --- a/templates/statefulset.yaml +++ b/templates/statefulset.yaml @@ -104,16 +104,22 @@ spec: subPath: .htaccess resources: {{ toYaml .Values.resources | indent 12 }} - {{- if .Values.backup.enabled }} - - name: {{ .Chart.Name }}-backup + {{- if or .Values.backup.enabled .Values.wordpress.mu_cron.enabled .Values.customCron }} + - name: {{ .Chart.Name }}-cron image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" imagePullPolicy: {{ .Values.initImage.pullPolicy }} command: - - "/var/local/ansible/scripts/backup.sh" - env: - - name: BACKUP_INTERVAL_SECONDS - # A day's worth of seconds. - value: {{ .Values.backup.intervalSeconds | quote }} + # Busybox's cron daemon. + - "crond" + # Run in foreground. + - "-f" + # Log to stderr, with level 6. + - "-d" + - "6" + # `crond` must be run as root, so we override the pod's security context. + securityContext: + runAsNonRoot: false + runAsUser: 0 volumeMounts: - name: {{ include "wordpress.name" . }}-wp-storage mountPath: /var/www/html @@ -126,6 +132,8 @@ spec: subPath: main.yml - name: ansible-secrets mountPath: /var/local/ansible/secrets + - name: cron-schedule + mountPath: /etc/crontabs/ {{- if .Values.backup.sshPrivateKey }} - name: ssh-private-key mountPath: /var/local/ssh-private-key @@ -186,3 +194,10 @@ spec: - name: htuploads configMap: name: {{ include "wordpress.fullname" . }}-htuploads + - name: cron-schedule + configMap: + name: {{ include "wordpress.fullname" . }}-cron-schedule + items: + - key: www-data + # This is the name of the user with id 33 in the cli container. + path: "xfs" diff --git a/values-local.yaml.example b/values-local.yaml.example index 4c78587c59daeae5dd5885f14b6cb225f7a303c9..d801919213211301a1697e1e49609fbb62fff450 100644 --- a/values-local.yaml.example +++ b/values-local.yaml.example @@ -175,8 +175,14 @@ redis: # # If isDate is set to false then backup names are a 2 week cycle of A(day number) or B(day number) # # A monthly database backup and monthly wordpress manifest are always made with monthnumber prefix # isDate: true -# # The interval at which backups occur. Defaults to 86400 seconds (24 hours) -# intervalSeconds: +# # The cron schedule that determines when backups are made. +# # Run at 3:37 every day. +# schedule: "37 3 * * *" + +# customCron: +# - schedule: "5 * * * *" +# command: "echo test" + # It's advisable to set resource limits to prevent your K8s cluster from # crashing # resources: diff --git a/values.yaml b/values.yaml index b826c68b7775384d9ecacd96329b66faa749c996..93ae529df36b3ffbe6e27b3cbe42927540d3e5ae 100644 --- a/values.yaml +++ b/values.yaml @@ -169,32 +169,11 @@ wordpress: slug: wp-cron-control version: cecdec276f086aafb6765ea77ce8d2ce0948e01c phpfile: wp-cron-control.php - # Optional annotations to add to the cronjob object cronjob: - image: - repository: bitnami/kubectl - tag: 1.18 - pullPolicy: IfNotPresent # Every 3 minutes schedule: "*/3 * * * *" # We use the internal DNS, so there is no TLS certificate curlInsecure: true - # resources: - # We usually recommend not to specify default resources and to leave this as a conscious - # choice for the user. This also increases chances charts run on environments with little - # resources, such as Minikube. If you do want to specify resources, uncomment the following - # lines, adjust them as necessary, and remove the curly braces after 'resources:'. - # limits: - # cpu: 100m - # memory: 128Mi - # requests: - # cpu: 100m - # memory: 128Mi - annotations: {} - failedJobsHistoryLimit: 3 - successfulJobsHistoryLimit: 1 - # Maximum number of times a failing Job is retried. - backoffLimit: 1 # Path to the cronjob PHP file (gets appended to the wordpress URL) path: /wp-cron.php # You can override this key for the cronjobs. If you don't change the @@ -241,13 +220,13 @@ ansibleVars: image: repository: open.greenhost.net:4567/openappstack/wordpress-helm/wordpress - tag: 0.2.2 + tag: 0.3.0 pullPolicy: Always pullSecrets: [] initImage: repository: open.greenhost.net:4567/openappstack/wordpress-helm/wordpress-cli-ansible - tag: 0.2.2 + tag: 0.3.0 pullPolicy: Always ingress: @@ -337,9 +316,12 @@ redis: backup: enabled: false - intervalSeconds: 86400 + # Daily at 2:00. + schedule: "0 2 * * *" isDate: true +customCron: [] + wpSalts: {} # Some of the variables configured above are put into a variable here, that's diff --git a/wp-cli-docker/scripts/backup.sh b/wp-cli-docker/scripts/backup.sh deleted file mode 100755 index 341c3218ba311ba264ab4ecbbfc4ab785959b6b6..0000000000000000000000000000000000000000 --- a/wp-cli-docker/scripts/backup.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -backupCommand="ansible-playbook backup.yml -e @secrets/secret-vars.yaml" - -while true -do - date - echo "Waiting for $BACKUP_INTERVAL_SECONDS seconds before starting next backup." - sleep $BACKUP_INTERVAL_SECONDS - $backupCommand - exitCode=$? - if [ $exitCode -ne 0 ] - then - echo "Backup failed, exiting!" - exit $exitCode - fi -done