Add an example of a custom backup/restore provider: AWS S3
- short guide with a simple example of how one can create a custom backup and restore provider
This commit is contained in:
		
							parent
							
								
									dbe8c1ae2e
								
							
						
					
					
						commit
						fc669bee2b
					
				| 
						 | 
					@ -0,0 +1,174 @@
 | 
				
			||||||
 | 
					# Custom Backup and Restore Providers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With enough effort one can create a custom backup and restore provider 
 | 
				
			||||||
 | 
					for the Jenkins Operator.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Requirements
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Two commands (e.g. scripts) are required:
 | 
				
			||||||
 | 
					- a backup command, e.g. `backup.sh` that takes one argument, a **backup number**
 | 
				
			||||||
 | 
					- a restore command, e.g. `backup.sh` that takes one argument, a **backup number**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Both scripts need to return an exit code of `0` on success and `1` or greater for failure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					One of those scripts (or the entry point of the container) needs to be responsible
 | 
				
			||||||
 | 
					for backup cleanup or rotation if required, or an external system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## How it works
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The mechanism relies on basic Kubernetes and UNIX functionalities.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The backup (and restore) container runs as a sidecar in the same 
 | 
				
			||||||
 | 
					Kubernetes pod as the Jenkins master.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Name of the backup and restore containers can be set as necessary using 
 | 
				
			||||||
 | 
					`spec.backup.containerName` and `spec.restore.containerName`. 
 | 
				
			||||||
 | 
					In most cases it will be the same container, but we allow for less common use cases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The operator will call a backup or restore commands inside a sidecar container when necessary:
 | 
				
			||||||
 | 
					- backup command (defined in `spec.backup.action.exec.command`) 
 | 
				
			||||||
 | 
					  will be called every `N` seconds configurable in: `spec.backup.interval`
 | 
				
			||||||
 | 
					  and on pod shutdown (if enabled in `spec.backup.makeBackupBeforePodDeletion`)
 | 
				
			||||||
 | 
					  with an integer representing the current backup number as first and only argument
 | 
				
			||||||
 | 
					- restore command (defined in `spec.restore.action.exec.command`) 
 | 
				
			||||||
 | 
					  will be called at Jenkins startup 
 | 
				
			||||||
 | 
					  with an integer representing the backup number to restore as first and only argument
 | 
				
			||||||
 | 
					  (can be overridden using `spec.restore.recoveryOnce`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Example AWS S3 backup using the CLI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This example shows abbreviated version of a simple AWS S3 backup implementation
 | 
				
			||||||
 | 
					using: `aws-cli`, `bash` and `kube2iam`. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In addition to your normal `Jenkins` `CustomResource` some additional settings 
 | 
				
			||||||
 | 
					for backup and restore are required, e.g.:
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					kind: Jenkins
 | 
				
			||||||
 | 
					apiVersion: jenkins.io/v1alpha1
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: example
 | 
				
			||||||
 | 
					  namespace: jenkins
 | 
				
			||||||
 | 
					spec:
 | 
				
			||||||
 | 
					  master:
 | 
				
			||||||
 | 
					    masterAnnotations:
 | 
				
			||||||
 | 
					      iam.amazonaws.com/role: "my-example-backup-role" # tell kube2iam where the AWS IAM role is
 | 
				
			||||||
 | 
					    containers:
 | 
				
			||||||
 | 
					      - name: jenkins-master
 | 
				
			||||||
 | 
					        ...
 | 
				
			||||||
 | 
					      - name: backup # container responsible for backup and restore
 | 
				
			||||||
 | 
					        image: quay.io/virtuslab/aws-cli:1.16.263-2
 | 
				
			||||||
 | 
					        workingDir: /home/user/bin/
 | 
				
			||||||
 | 
					        command: # our container entry point
 | 
				
			||||||
 | 
					          - sleep
 | 
				
			||||||
 | 
					          - infinity
 | 
				
			||||||
 | 
					        env:
 | 
				
			||||||
 | 
					          - name: BACKUP_BUCKET
 | 
				
			||||||
 | 
					            value: my-example-bucket # the S3 bucket name to use
 | 
				
			||||||
 | 
					          - name: BACKUP_PATH
 | 
				
			||||||
 | 
					            value: my-backup-path # the S3 bucket path prefix to use
 | 
				
			||||||
 | 
					          - name: JENKINS_HOME
 | 
				
			||||||
 | 
					            value: /jenkins-home # the path to mount jenkins home dir in the backup container
 | 
				
			||||||
 | 
					        volumeMounts:
 | 
				
			||||||
 | 
					          - mountPath: /jenkins-home # Jenkins home volume
 | 
				
			||||||
 | 
					            name: jenkins-home
 | 
				
			||||||
 | 
					          - mountPath: /home/user/bin/backup.sh
 | 
				
			||||||
 | 
					            name: backup-scripts
 | 
				
			||||||
 | 
					            subPath: backup.sh
 | 
				
			||||||
 | 
					            readOnly: true
 | 
				
			||||||
 | 
					          - mountPath: /home/user/bin/restore.sh
 | 
				
			||||||
 | 
					            name: backup-scripts
 | 
				
			||||||
 | 
					            subPath: restore.sh
 | 
				
			||||||
 | 
					            readOnly: true
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - name: backup-scripts
 | 
				
			||||||
 | 
					        configMap:
 | 
				
			||||||
 | 
					          defaultMode: 0754
 | 
				
			||||||
 | 
					          name: jenkins-operator-backup-s3
 | 
				
			||||||
 | 
					    securityContext: # make sure both containers use the same UID and GUID
 | 
				
			||||||
 | 
					      runAsUser: 1000
 | 
				
			||||||
 | 
					      fsGroup: 1000
 | 
				
			||||||
 | 
					  ...
 | 
				
			||||||
 | 
					  backup:
 | 
				
			||||||
 | 
					    containerName: backup # container name responsible for backup
 | 
				
			||||||
 | 
					    interval: 3600 # how often make a backup in seconds
 | 
				
			||||||
 | 
					    makeBackupBeforePodDeletion: true # trigger backup just before deleting the pod
 | 
				
			||||||
 | 
					    action:
 | 
				
			||||||
 | 
					      exec:
 | 
				
			||||||
 | 
					        command:
 | 
				
			||||||
 | 
					          # this command is invoked on "backup" container to create a backup,
 | 
				
			||||||
 | 
					          # <backup_number> is passed by operator,
 | 
				
			||||||
 | 
					          # for example /home/user/bin/backup.sh <backup_number>
 | 
				
			||||||
 | 
					          - /home/user/bin/backup.sh
 | 
				
			||||||
 | 
					  restore:
 | 
				
			||||||
 | 
					    containerName: backup # container name is responsible for restore backup
 | 
				
			||||||
 | 
					    action:
 | 
				
			||||||
 | 
					      exec:
 | 
				
			||||||
 | 
					        command:
 | 
				
			||||||
 | 
					          # this command is invoked on "backup" container to restore a backup,
 | 
				
			||||||
 | 
					          # <backup_number> is passed by operator
 | 
				
			||||||
 | 
					          # for example /home/user/bin/restore.sh <backup_number>
 | 
				
			||||||
 | 
					          - /home/user/bin/restore.sh
 | 
				
			||||||
 | 
					#    recoveryOnce: <backup_number> # if want to restore specific backup configure this field and then Jenkins will be restarted and desired backup will be restored
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The actual backup and restore scripts will be provided in a `ConfigMap`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					kind: ConfigMap
 | 
				
			||||||
 | 
					apiVersion: v1
 | 
				
			||||||
 | 
					metadata:
 | 
				
			||||||
 | 
					  name: jenkins-operator-backup-s3
 | 
				
			||||||
 | 
					  namespace: jenkins
 | 
				
			||||||
 | 
					  labels:
 | 
				
			||||||
 | 
					    app: jenkins-operator
 | 
				
			||||||
 | 
					data:
 | 
				
			||||||
 | 
					  backup.sh: |-
 | 
				
			||||||
 | 
					    #!/bin/bash -xeu
 | 
				
			||||||
 | 
					    [[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1;
 | 
				
			||||||
 | 
					    [[ -z "${BACKUP_BUCKET}" ]] && echo "Required 'BACKUP_BUCKET' env not set" && exit 1;
 | 
				
			||||||
 | 
					    [[ -z "${BACKUP_PATH}" ]] && echo "Required 'BACKUP_PATH' env not set" && exit 1;
 | 
				
			||||||
 | 
					    [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    backup_number=$1
 | 
				
			||||||
 | 
					    echo "Running backup #${backup_number}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BACKUP_TMP_DIR=$(mktemp -d)
 | 
				
			||||||
 | 
					    tar -C ${JENKINS_HOME} -czf "${BACKUP_TMP_DIR}/${backup_number}.tar.gz" --exclude jobs/*/config.xml --exclude jobs/*/workspace* -c jobs && \
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    aws s3 cp ${BACKUP_TMP_DIR}/${backup_number}.tar.gz s3://${BACKUP_BUCKET}/${BACKUP_PATH}/${backup_number}.tar.gz
 | 
				
			||||||
 | 
					    echo Done
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  restore.sh: |-
 | 
				
			||||||
 | 
					    #!/bin/bash -xeu
 | 
				
			||||||
 | 
					    [[ ! $# -eq 1 ]] && echo "Usage: $0 backup_number" && exit 1
 | 
				
			||||||
 | 
					    [[ -z "${BACKUP_BUCKET}" ]] && echo "Required 'BACKUP_BUCKET' env not set" && exit 1;
 | 
				
			||||||
 | 
					    [[ -z "${BACKUP_PATH}" ]] && echo "Required 'BACKUP_PATH' env not set" && exit 1;
 | 
				
			||||||
 | 
					    [[ -z "${JENKINS_HOME}" ]] && echo "Required 'JENKINS_HOME' env not set" && exit 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    backup_number=$1
 | 
				
			||||||
 | 
					    echo "Running restore #${backup_number}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BACKUP_TMP_DIR=$(mktemp -d)
 | 
				
			||||||
 | 
					    aws s3 cp s3://${BACKUP_BUCKET}/${BACKUP_PATH}/${backup_number}.tar.gz ${BACKUP_TMP_DIR}/${backup_number}.tar.gz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tar -C ${JENKINS_HOME} -zxf "${BACKUP_TMP_DIR}/${backup_number}.tar.gz"
 | 
				
			||||||
 | 
					    echo Done
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In our example we will use S3 bucket lifecycle policy to keep
 | 
				
			||||||
 | 
					the number of backups under control, e.g. Cloud Formation fragment:
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					    Type: AWS::S3::Bucket
 | 
				
			||||||
 | 
					    Properties:
 | 
				
			||||||
 | 
					      BucketName: my-example-bucket
 | 
				
			||||||
 | 
					      ...
 | 
				
			||||||
 | 
					      LifecycleConfiguration:
 | 
				
			||||||
 | 
					        Rules:
 | 
				
			||||||
 | 
					          - Id: BackupCleanup
 | 
				
			||||||
 | 
					            Status: Enabled
 | 
				
			||||||
 | 
					            Prefix: my-backup-path
 | 
				
			||||||
 | 
					            ExpirationInDays: 7
 | 
				
			||||||
 | 
					            NoncurrentVersionExpirationInDays: 14
 | 
				
			||||||
 | 
					            AbortIncompleteMultipartUpload:
 | 
				
			||||||
 | 
					              DaysAfterInitiation: 3
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
		Loading…
	
		Reference in New Issue