-
Notifications
You must be signed in to change notification settings - Fork 6
DB Sync App/CronJob Charts #4258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dj-maisy
wants to merge
5
commits into
main
Choose a base branch
from
dj-maisy/db-sync-app-chart
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
8c34623
(WIP) Helm Chart for the new db-sync app script.
dj-maisy b0e8f79
Fleshing-out the DB Sync Chart.
dj-maisy a78d0b5
Add logic for URI building etc.
dj-maisy 49950dc
Stagger the DocDB backups by 2 minutes each
dj-maisy d476578
Various fixes - thanks GitHub Copilot?
dj-maisy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| apiVersion: v2 | ||
| name: db-sync | ||
| description: Helm chart for govuk-db-sync CronJobs (backup/restore for PostgreSQL, MySQL, DocumentDB) | ||
| type: application | ||
| version: 0.1.0 | ||
| appVersion: "0.1.0" | ||
| keywords: | ||
| - backup | ||
| - restore | ||
| - database | ||
| - cronjob | ||
| maintainers: | ||
| - name: GOV.UK Platform Engineering | ||
| email: govuk-platform-engineering@digital.cabinet-office.gov.uk |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| # db-sync Helm Chart | ||
|
|
||
| Helm chart for deploying `govuk-db-sync` CronJobs to manage database backups and restores across PostgreSQL, MySQL, and DocumentDB. | ||
|
|
||
| ## Installation | ||
|
|
||
| ```bash | ||
| helm install db-sync ./charts/db-sync \ | ||
| -f values-staging.yaml \ | ||
| -n database-sync | ||
| ``` | ||
|
|
||
| ## Configuration | ||
|
|
||
| ### Basic Job Configuration | ||
|
|
||
| Each job in `values.yaml` requires: | ||
|
|
||
| - **schedule**: Cron schedule (e.g., `"0 2 * * *"`) | ||
| - **operation**: `backup` or `restore` | ||
| - **dbType**: `postgres`, `mysql`, or `documentdb` | ||
| - **dbName**: Target database name | ||
| - **s3Bucket**: S3 bucket for backups (e.g., `s3://govuk-prod-backups`) | ||
| - **s3Path**: S3 path prefix (e.g., `myapp/db`) | ||
|
|
||
| ### Example Configuration | ||
|
|
||
| ```yaml | ||
| cronjobs: | ||
| production: | ||
| # PostgreSQL backup | ||
| account-api-postgres: | ||
| schedule: "37 23 * * *" | ||
| operation: backup | ||
| dbType: postgres | ||
| dbName: account-api_production | ||
| s3Bucket: s3://govuk-prod-database-backups | ||
| s3Path: account-api/db | ||
|
|
||
| # PostgreSQL restore with transformation | ||
| email-alert-api-postgres: | ||
| schedule: "54 3 * * 1" | ||
| operation: restore | ||
| dbType: postgres | ||
| dbName: email-alert-api_production | ||
| s3Bucket: s3://govuk-staging-database-backups | ||
| s3Path: email-alert-api/db | ||
| transformScript: email-alert-api.sql | ||
| extraEnv: | ||
| - name: DB_OWNER | ||
| value: email-alert-api | ||
|
|
||
| # MySQL backup | ||
| release-mysql: | ||
| schedule: "11 3 * * 1" | ||
| operation: backup | ||
| dbType: mysql | ||
| dbName: release_production | ||
| s3Bucket: s3://govuk-prod-database-backups | ||
| s3Path: release/db | ||
|
|
||
| # DocumentDB backup | ||
| publisher-documentdb: | ||
| schedule: "13 3 * * 1" | ||
| operation: backup | ||
| dbType: documentdb | ||
| dbName: publisher_production | ||
| s3Bucket: s3://govuk-prod-database-backups | ||
| s3Path: publisher/docdb | ||
| ``` | ||
|
|
||
| ### Optional Job Configuration | ||
|
|
||
| - **suspend**: Set to `true` to disable a job (default: `false`) | ||
| - **maxJobRuntimeSeconds**: Maximum job runtime in seconds (default: 43200 = 12 hours) | ||
| - **transformScript**: Name of a transform script from the `scripts/` ConfigMap | ||
| - **extraEnv**: Job-specific environment variables | ||
| - **resources**: Override default resource limits/requests | ||
|
|
||
| ## Secrets Management | ||
|
|
||
| This chart uses AWS Secrets Manager via the ExternalSecrets operator. | ||
|
|
||
| **Expected Secret Structure:** | ||
|
|
||
| Create a secret at `govuk/db-sync/passwd` with key/value pairs mapping job names to passwords: | ||
|
|
||
| ```json | ||
| { | ||
| "account-api-postgres": "password123", | ||
| "email-alert-api-postgres": "password456", | ||
| "release-mysql": "password789", | ||
| "publisher-documentdb": "mongodb-connection-string" | ||
| } | ||
| ``` | ||
|
|
||
| The chart loads these credentials into Kubernetes Secret `db-sync-passwd`, which is referenced by CronJobs. | ||
|
|
||
| ## IAM Permissions | ||
|
|
||
| The ServiceAccount uses AWS IAM Roles for Service Accounts (IRSA). Configure the annotation: | ||
|
|
||
| ```yaml | ||
| serviceAccount: | ||
| annotations: | ||
| eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/db-sync-role | ||
| ``` | ||
|
|
||
| **Required IAM Permissions:** | ||
| - S3: `s3:GetObject`, `s3:PutObject` on backup buckets | ||
| - RDS (for backups): Database instance access | ||
| - Secrets Manager: `secretsmanager:GetSecretValue` for `govuk/db-sync/passwd` | ||
| - CloudWatch (optional): Metrics publishing | ||
|
|
||
| ## Transform Scripts | ||
|
|
||
| Place SQL/JavaScript transformation scripts in the `scripts/` directory: | ||
|
|
||
| ``` | ||
| charts/db-sync/ | ||
| ├── scripts/ | ||
| │ ├── email-alert-api.sql | ||
| │ ├── content-store.sql | ||
| │ └── custom-transform.js | ||
| ``` | ||
|
|
||
| Reference them in job config: | ||
|
|
||
| ```yaml | ||
| transformScript: email-alert-api.sql | ||
| ``` | ||
|
|
||
| ## Environment-Specific Values | ||
|
|
||
| Create separate values files for each environment: | ||
|
|
||
| ```bash | ||
| values-production.yaml | ||
| values-staging.yaml | ||
| values-integration.yaml | ||
| ``` | ||
|
|
||
| Then deploy with: | ||
|
|
||
| ```bash | ||
| helm install db-sync ./charts/db-sync -f values-production.yaml | ||
| ``` | ||
|
|
||
| ## Monitoring | ||
|
|
||
| Each CronJob logs to stdout. Monitor failures via: | ||
| - Kubernetes Events: `kubectl describe cronjob db-sync-<job-name>` | ||
| - Pod Logs: `kubectl logs -l app.kubernetes.io/component=<job-name>` | ||
| - Failed Jobs: `kubectl get jobs --failed` | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| **Job not running:** | ||
| - Check if suspended: `kubectl get cronjob db-sync-<job-name>` | ||
| - Verify schedule is valid: https://crontab.guru/ | ||
|
|
||
| **Secret not found:** | ||
| - Ensure ExternalSecret is synced: `kubectl describe externalsecret db-sync-passwd` | ||
| - Check AWS Secrets Manager: `aws secretsmanager get-secret-value --secret-id govuk/db-sync/passwd` | ||
|
|
||
| **Pod crash:** | ||
| - Check pod logs: `kubectl logs <pod-name> -p` (for previous pod) | ||
| - Verify S3 bucket access and IAM permissions | ||
| - Check database connectivity and credentials | ||
|
|
||
| ## Values Reference | ||
|
|
||
| See `values.yaml` for all available configuration options. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| {{/* | ||
| Expand the name of the chart. | ||
| */}} | ||
| {{- define "db-sync.name" -}} | ||
| {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Create a default fully qualified app name. | ||
| */}} | ||
| {{- define "db-sync.fullname" -}} | ||
| {{- if .Values.fullnameOverride }} | ||
| {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} | ||
| {{- else }} | ||
| {{- $name := default .Chart.Name .Values.nameOverride }} | ||
| {{- if contains $name .Release.Name }} | ||
| {{- .Release.Name | trunc 63 | trimSuffix "-" }} | ||
| {{- else }} | ||
| {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Create chart name and version as used by the chart label. | ||
| */}} | ||
| {{- define "db-sync.chart" -}} | ||
| {{- printf "%s-%s" .Chart.Name .Chart.AppVersion | replace "+" "_" | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Common labels | ||
| */}} | ||
| {{- define "db-sync.labels" -}} | ||
| helm.sh/chart: {{ include "db-sync.chart" . }} | ||
| {{ include "db-sync.selectorLabels" . }} | ||
| {{- if .Chart.AppVersion }} | ||
| app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} | ||
| {{- end }} | ||
| app.kubernetes.io/managed-by: {{ .Release.Service }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Selector labels | ||
| */}} | ||
| {{- define "db-sync.selectorLabels" -}} | ||
| app.kubernetes.io/name: {{ include "db-sync.name" . }} | ||
| app.kubernetes.io/instance: {{ .Release.Name }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Create the name of the service account to use | ||
| */}} | ||
| {{- define "db-sync.serviceAccountName" -}} | ||
| {{- if .Values.serviceAccount.create }} | ||
| {{- default (include "db-sync.fullname" .) .Values.serviceAccount.name }} | ||
| {{- else }} | ||
| {{- default "default" .Values.serviceAccount.name }} | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Default DB port for a dbType. | ||
| Usage: include "db-sync.defaultDbPort" "postgres" | ||
| */}} | ||
| {{- define "db-sync.defaultDbPort" -}} | ||
| {{- if eq . "postgres" -}} | ||
| 5432 | ||
| {{- else if eq . "mysql" -}} | ||
| 3306 | ||
| {{- else if eq . "documentdb" -}} | ||
| 27017 | ||
| {{- end -}} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| URI scheme for a dbType. | ||
| Usage: include "db-sync.dbUriScheme" "postgres" | ||
| */}} | ||
| {{- define "db-sync.dbUriScheme" -}} | ||
| {{- if eq . "postgres" -}} | ||
| postgres | ||
| {{- else if eq . "mysql" -}} | ||
| mysql | ||
| {{- else if eq . "documentdb" -}} | ||
| mongodb | ||
| {{- end -}} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Transform DB user default by engine. | ||
| Expected dict keys: job | ||
| */}} | ||
| {{- define "db-sync.transformUser" -}} | ||
| {{- $job := .job -}} | ||
| {{- if eq $job.dbType "documentdb" -}} | ||
| {{- default "mongoadmin" $job.transformUsername -}} | ||
| {{- else if eq $job.dbType "mysql" -}} | ||
| {{- default "root" $job.transformUsername -}} | ||
| {{- else -}} | ||
| {{- default "postgres" $job.transformUsername -}} | ||
| {{- end -}} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Build source URI from components unless sourceUri is explicitly set. | ||
| Expected dict keys: root, job | ||
| */}} | ||
| {{- define "db-sync.sourceUri" -}} | ||
| {{- $root := .root -}} | ||
| {{- $job := .job -}} | ||
| {{- if $job.sourceUri -}} | ||
| {{- $job.sourceUri -}} | ||
| {{- else -}} | ||
| {{- $hostname := default $root.Values.defaultSourceDbHostname $job.sourceDbHostname -}} | ||
| {{- if $hostname -}} | ||
| {{- $port := default $root.Values.defaultSourceDbPort $job.sourceDbPort -}} | ||
| {{- if not $port -}} | ||
| {{- $port = include "db-sync.defaultDbPort" $job.dbType | trim -}} | ||
| {{- end -}} | ||
| {{- $username := default $root.Values.defaultDbUsername $job.sourceDbUsername -}} | ||
| {{- $scheme := include "db-sync.dbUriScheme" $job.dbType | trim -}} | ||
| {{- printf "%s://%s@%s:%s/%s" $scheme $username $hostname $port $job.dbName -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Build destination URI from components unless destUri is explicitly set. | ||
| Expected dict keys: root, job | ||
| */}} | ||
| {{- define "db-sync.destUri" -}} | ||
| {{- $root := .root -}} | ||
| {{- $job := .job -}} | ||
| {{- if $job.destUri -}} | ||
| {{- $job.destUri -}} | ||
| {{- else if ne $job.dbType "documentdb" -}} | ||
| {{- $hostname := default $root.Values.defaultDestDbHostname $job.destDbHostname -}} | ||
| {{- if $hostname -}} | ||
| {{- $port := default $root.Values.defaultDestDbPort $job.destDbPort -}} | ||
| {{- if not $port -}} | ||
| {{- $port = include "db-sync.defaultDbPort" $job.dbType | trim -}} | ||
| {{- end -}} | ||
| {{- $username := default $root.Values.defaultDbUsername $job.destDbUsername -}} | ||
| {{- $scheme := include "db-sync.dbUriScheme" $job.dbType | trim -}} | ||
| {{- printf "%s://%s@%s:%s/%s" $scheme $username $hostname $port $job.dbName -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Build transform URI from components unless transformUri is explicitly set. | ||
| Expected dict keys: job, transformUser | ||
| */}} | ||
| {{- define "db-sync.transformUri" -}} | ||
| {{- $job := .job -}} | ||
| {{- $transformUser := .transformUser -}} | ||
| {{- if $job.transformUri -}} | ||
| {{- $job.transformUri -}} | ||
| {{- else -}} | ||
| {{- $scheme := include "db-sync.dbUriScheme" $job.dbType | trim -}} | ||
| {{- $port := include "db-sync.defaultDbPort" $job.dbType | trim -}} | ||
| {{- $transformDbName := default $job.dbName $job.transformDbName -}} | ||
| {{- printf "%s://%s:password@127.0.0.1:%s/%s" $scheme $transformUser $port $transformDbName -}} | ||
| {{- end -}} | ||
|
dj-maisy marked this conversation as resolved.
|
||
| {{- end }} | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.