Update website - 0.5.0 release
This commit is contained in:
		
							parent
							
								
									fe81e5ab3d
								
							
						
					
					
						commit
						9f261169f0
					
				|  | @ -1,14 +1,14 @@ | ||||||
| --- | --- | ||||||
| title: "Latest (v0.4.x)" | title: "Latest (v0.5.x)" | ||||||
| linkTitle: "Latest (v0.4.x)" | linkTitle: "Latest (v0.5.x)" | ||||||
| weight: 10 | weight: 10 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   How to work with jenkins-operator latest version |   How to work with jenkins-operator latest version | ||||||
| --- | --- | ||||||
| 
 | 
 | ||||||
| {{% pageinfo %}} | {{% pageinfo %}} | ||||||
| This document describes a getting started guide for **Jenkins Operator** `v0.4.x` and an additional configuration. | This document describes a getting started guide for **Jenkins Operator** `v0.5.x` and an additional configuration. | ||||||
| {{% /pageinfo %}} | {{% /pageinfo %}} | ||||||
| 
 | 
 | ||||||
| ## First Steps | ## First Steps | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "AKS" | title: "AKS" | ||||||
| linkTitle: "AKS" | linkTitle: "AKS" | ||||||
| weight: 10 | weight: 10 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|     Additional configuration for Azure Kubernetes Service |     Additional configuration for Azure Kubernetes Service | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Configuration" | title: "Configuration" | ||||||
| linkTitle: "Configuration" | linkTitle: "Configuration" | ||||||
| weight: 2 | weight: 2 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   How to configure Jenkins with Operator |   How to configure Jenkins with Operator | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Configure backup and restore" | title: "Configure backup and restore" | ||||||
| linkTitle: "Configure backup and restore" | linkTitle: "Configure backup and restore" | ||||||
| weight: 10 | weight: 10 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   Prevent loss of job history |   Prevent loss of job history | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Custom Backup and Restore Providers" | title: "Custom Backup and Restore Providers" | ||||||
| linkTitle: "Custom Backup and Restore Providers" | linkTitle: "Custom Backup and Restore Providers" | ||||||
| weight: 10 | weight: 10 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   Custom backup and restore provider |   Custom backup and restore provider | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Customization" | title: "Customization" | ||||||
| linkTitle: "Customization" | linkTitle: "Customization" | ||||||
| weight: 3 | weight: 3 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   How to customize Jenkins |   How to customize Jenkins | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Deploy Jenkins" | title: "Deploy Jenkins" | ||||||
| linkTitle: "Deploy Jenkins" | linkTitle: "Deploy Jenkins" | ||||||
| weight: 1 | weight: 1 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   Deploy production ready Jenkins Operator manifest |   Deploy production ready Jenkins Operator manifest | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Diagnostics" | title: "Diagnostics" | ||||||
| linkTitle: "Diagnostics" | linkTitle: "Diagnostics" | ||||||
| weight: 40 | weight: 40 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   How to deal with Jenkins Operator problems |   How to deal with Jenkins Operator problems | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Notifications" | title: "Notifications" | ||||||
| linkTitle: "Notifications" | linkTitle: "Notifications" | ||||||
| weight: 10 | weight: 10 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|     How to setup operator notifications. |     How to setup operator notifications. | ||||||
| --- | --- | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| title: "Schema" | title: "Schema" | ||||||
| linkTitle: "Schema" | linkTitle: "Schema" | ||||||
| weight: 40 | weight: 40 | ||||||
| date: 2020-04-13 | date: 2021-01-18 | ||||||
| description: > | description: > | ||||||
|   API Schema definitions for Jenkins CRD |   API Schema definitions for Jenkins CRD | ||||||
| --- | --- | ||||||
|  | @ -1295,19 +1295,19 @@ More info: <a href="https://kubernetes.io/docs/concepts/storage/volumes">https:/ | ||||||
| <p>BasePlugins contains plugins required by operator | <p>BasePlugins contains plugins required by operator | ||||||
| Defaults to : | Defaults to : | ||||||
| - name: kubernetes | - name: kubernetes | ||||||
| version: 1.25.2 | version: “1.28.6” | ||||||
| - name: workflow-job | - name: workflow-job | ||||||
| version: “2.39” | version: “2.40” | ||||||
| - name: workflow-aggregator | - name: workflow-aggregator | ||||||
| version: “2.6” | version: “2.6” | ||||||
| - name: git | - name: git | ||||||
| version: 4.2.2 | version: “4.5.0” | ||||||
| - name: job-dsl | - name: job-dsl | ||||||
| version: “1.77” | version: “1.77” | ||||||
| - name: configuration-as-code | - name: configuration-as-code | ||||||
| version: “1.38” | version: “1.46” | ||||||
| - name: kubernetes-credentials-provider | - name: kubernetes-credentials-provider | ||||||
| version: 0.13</p> | version: “0.15”</p> | ||||||
| </td> | </td> | ||||||
| </tr> | </tr> | ||||||
| <tr> | <tr> | ||||||
|  | @ -2040,6 +2040,21 @@ Handler | ||||||
| </tr> | </tr> | ||||||
| <tr> | <tr> | ||||||
| <td> | <td> | ||||||
|  | <code>getLatestAction</code></br> | ||||||
|  | <em> | ||||||
|  | <a href="#github.com/jenkinsci/kubernetes-operator/pkg/apis/jenkins/v1alpha2.Handler"> | ||||||
|  | Handler | ||||||
|  | </a> | ||||||
|  | </em> | ||||||
|  | </td> | ||||||
|  | <td> | ||||||
|  | <em>(Optional)</em> | ||||||
|  | <p>GetLatestAction defines action which returns the latest backup number. If there is no backup “-1” should be | ||||||
|  | returned.</p> | ||||||
|  | </td> | ||||||
|  | </tr> | ||||||
|  | <tr> | ||||||
|  | <td> | ||||||
| <code>recoveryOnce</code></br> | <code>recoveryOnce</code></br> | ||||||
| <em> | <em> | ||||||
| uint64 | uint64 | ||||||
|  | @ -2623,5 +2638,5 @@ SecretKeySelector | ||||||
| <hr/> | <hr/> | ||||||
| <p><em> | <p><em> | ||||||
| Generated with <code>gen-crd-api-reference-docs</code> | Generated with <code>gen-crd-api-reference-docs</code> | ||||||
| on git commit <code>1c853e69</code>. | on git commit <code>fe81e5a</code>. | ||||||
| </em></p> | </em></p> | ||||||
|  | @ -0,0 +1,18 @@ | ||||||
|  | --- | ||||||
|  | title: "v0.4.x" | ||||||
|  | linkTitle: "v0.4.x" | ||||||
|  | weight: 10 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |   How to work with jenkins-operator latest version | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | {{% pageinfo %}} | ||||||
|  | This document describes a getting started guide for **Jenkins Operator** `v0.4.x` and an additional configuration. | ||||||
|  | {{% /pageinfo %}} | ||||||
|  | 
 | ||||||
|  | ## First Steps | ||||||
|  | 
 | ||||||
|  | Prepare your Kubernetes cluster and set up your `kubectl` access. | ||||||
|  | 
 | ||||||
|  | Once you have running Kubernetes cluster you can focus on installing **Jenkins Operator** according to the [Installation](/kubernetes-operator/docs/installation/) guide. | ||||||
|  | @ -0,0 +1,24 @@ | ||||||
|  | --- | ||||||
|  | title: "AKS" | ||||||
|  | linkTitle: "AKS" | ||||||
|  | weight: 10 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |     Additional configuration for Azure Kubernetes Service | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | Azure AKS managed Kubernetes service adds to every pod the following environment variables: | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | - name: KUBERNETES_PORT_443_TCP_ADDR | ||||||
|  |   value: | ||||||
|  | - name: KUBERNETES_PORT | ||||||
|  |   value: tcp:// | ||||||
|  | - name: KUBERNETES_PORT_443_TCP | ||||||
|  |   value: tcp:// | ||||||
|  | - name: KUBERNETES_SERVICE_HOST | ||||||
|  |   value: | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | The operator is aware of it and omits these environment variables when checking if a Jenkins pod environment has been changed. It prevents the  | ||||||
|  | restart of a Jenkins pod over and over again. | ||||||
|  | @ -0,0 +1,332 @@ | ||||||
|  | --- | ||||||
|  | title: "Configuration" | ||||||
|  | linkTitle: "Configuration" | ||||||
|  | weight: 2 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |   How to configure Jenkins with Operator | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | ## Configure Seed Jobs and Pipelines | ||||||
|  | 
 | ||||||
|  | Jenkins operator uses [job-dsl][job-dsl] and [kubernetes-credentials-provider][kubernetes-credentials-provider] plugins for configuring jobs | ||||||
|  | and deploy keys. | ||||||
|  | 
 | ||||||
|  | ## Prepare job definitions and pipelines | ||||||
|  | 
 | ||||||
|  | First you have to prepare pipelines and job definition in your GitHub repository using the following structure: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | cicd/ | ||||||
|  | ├── jobs | ||||||
|  | │   └── build.jenkins | ||||||
|  | └── pipelines | ||||||
|  |     └── build.jenkins | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | **`cicd/jobs/build.jenkins`** is a job definition: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | #!/usr/bin/env groovy | ||||||
|  | 
 | ||||||
|  | pipelineJob('build-jenkins-operator') { | ||||||
|  |     displayName('Build jenkins-operator') | ||||||
|  | 
 | ||||||
|  |     definition { | ||||||
|  |         cpsScm { | ||||||
|  |             scm { | ||||||
|  |                 git { | ||||||
|  |                     remote { | ||||||
|  |                         url('https://github.com/jenkinsci/kubernetes-operator.git') | ||||||
|  |                         credentials('jenkins-operator') | ||||||
|  |                     } | ||||||
|  |                     branches('*/master') | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             scriptPath('cicd/pipelines/build.jenkins') | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | **`cicd/pipelines/build.jenkins`** is an actual Jenkins pipeline: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | #!/usr/bin/env groovy | ||||||
|  | 
 | ||||||
|  | def label = "build-jenkins-operator-${UUID.randomUUID().toString()}" | ||||||
|  | def home = "/home/jenkins" | ||||||
|  | def workspace = "${home}/workspace/build-jenkins-operator" | ||||||
|  | def workdir = "${workspace}/src/github.com/jenkinsci/kubernetes-operator/" | ||||||
|  | 
 | ||||||
|  | podTemplate(label: label, | ||||||
|  |         containers: [ | ||||||
|  |                 containerTemplate(name: 'jnlp', image: 'jenkins/inbound-agent:alpine'), | ||||||
|  |                 containerTemplate(name: 'go', image: 'golang:1-alpine', command: 'cat', ttyEnabled: true), | ||||||
|  |         ], | ||||||
|  |         envVars: [ | ||||||
|  |                 envVar(key: 'GOPATH', value: workspace), | ||||||
|  |         ], | ||||||
|  |         ) { | ||||||
|  | 
 | ||||||
|  |     node(label) { | ||||||
|  |         dir(workdir) { | ||||||
|  |             stage('Init') { | ||||||
|  |                 timeout(time: 3, unit: 'MINUTES') { | ||||||
|  |                     checkout scm | ||||||
|  |                 } | ||||||
|  |                 container('go') { | ||||||
|  |                     sh 'apk --no-cache --update add make git gcc libc-dev' | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             stage('Dep') { | ||||||
|  |                 container('go') { | ||||||
|  |                     sh 'make dep' | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             stage('Test') { | ||||||
|  |                 container('go') { | ||||||
|  |                     sh 'make test' | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             stage('Build') { | ||||||
|  |                 container('go') { | ||||||
|  |                     sh 'make build' | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Configure Seed Jobs | ||||||
|  | 
 | ||||||
|  | Jenkins Seed Jobs are configured using `Jenkins.spec.seedJobs` section from your custom resource manifest: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   seedJobs: | ||||||
|  |   - id: jenkins-operator | ||||||
|  |     targets: "cicd/jobs/*.jenkins" | ||||||
|  |     description: "Jenkins Operator repository" | ||||||
|  |     repositoryBranch: master | ||||||
|  |     repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | **Jenkins Operator** will automatically discover and configure all the seed jobs. | ||||||
|  | 
 | ||||||
|  | You can verify if deploy keys were successfully configured in the Jenkins **Credentials** tab. | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | You can verify if your pipelines were successfully configured in the Jenkins Seed Job console output. | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | 
 | ||||||
|  | If your GitHub repository is **private** you have to configure SSH or username/password authentication. | ||||||
|  | 
 | ||||||
|  | ### SSH authentication | ||||||
|  | 
 | ||||||
|  | #### Generate SSH Keys | ||||||
|  | 
 | ||||||
|  | There are two methods of SSH private key generation: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | $ openssl genrsa -out <filename> 2048 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | or | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | $ ssh-keygen -t rsa -b 2048 | ||||||
|  | $ ssh-keygen -p -f <filename> -m pem | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Then copy content from generated file.  | ||||||
|  | 
 | ||||||
|  | #### Public key | ||||||
|  | 
 | ||||||
|  | If you want to upload your public key to your Git server you need to extract it. | ||||||
|  | 
 | ||||||
|  | If key was generated by `openssl` then you need to type this to extract public key: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | $ openssl rsa -in <filename> -pubout > <filename>.pub | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | If key was generated by `ssh-keygen` the public key content is located in <filename>.pub and there is no need to extract public key | ||||||
|  | 
 | ||||||
|  | #### Configure SSH authentication | ||||||
|  | 
 | ||||||
|  | Configure a seed job like this: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   seedJobs: | ||||||
|  |   - id: jenkins-operator-ssh | ||||||
|  |     credentialType: basicSSHUserPrivateKey | ||||||
|  |     credentialID: k8s-ssh | ||||||
|  |     targets: "cicd/jobs/*.jenkins" | ||||||
|  |     description: "Jenkins Operator repository" | ||||||
|  |     repositoryBranch: master | ||||||
|  |     repositoryUrl: ssh://git@github.com:jenkinsci/kubernetes-operator.git | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | and create a Kubernetes Secret (name of secret should be the same from `credentialID` field): | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: Secret | ||||||
|  | metadata: | ||||||
|  |   name: k8s-ssh | ||||||
|  |   labels: | ||||||
|  |     "jenkins.io/credentials-type": "basicSSHUserPrivateKey" | ||||||
|  |   annotations: | ||||||
|  |     "jenkins.io/credentials-description" : "ssh github.com:jenkinsci/kubernetes-operator" | ||||||
|  | stringData: | ||||||
|  |   privateKey: | | ||||||
|  |     -----BEGIN RSA PRIVATE KEY----- | ||||||
|  |     MIIJKAIBAAKCAgEAxxDpleJjMCN5nusfW/AtBAZhx8UVVlhhhIKXvQ+dFODQIdzO | ||||||
|  |     oDXybs1zVHWOj31zqbbJnsfsVZ9Uf3p9k6xpJ3WFY9b85WasqTDN1xmSd6swD4N8 | ||||||
|  |     ... | ||||||
|  |   username: github_user_name | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### Username & password authentication | ||||||
|  | 
 | ||||||
|  | Configure the seed job like: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   seedJobs: | ||||||
|  |   - id: jenkins-operator-user-pass | ||||||
|  |     credentialType: usernamePassword | ||||||
|  |     credentialID: k8s-user-pass | ||||||
|  |     targets: "cicd/jobs/*.jenkins" | ||||||
|  |     description: "Jenkins Operator repository" | ||||||
|  |     repositoryBranch: master | ||||||
|  |     repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | and create a Kubernetes Secret (name of secret should be the same from `credentialID` field): | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: Secret | ||||||
|  | metadata: | ||||||
|  |   name: k8s-user-pass | ||||||
|  | stringData: | ||||||
|  |   username: github_user_name | ||||||
|  |   password: password_or_token | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### External authentication | ||||||
|  | You can use `external` credential type if you want to configure authentication using Configuration As Code or Groovy Script. | ||||||
|  | 
 | ||||||
|  | ## HTTP Proxy for downloading plugins | ||||||
|  | 
 | ||||||
|  | To use forwarding proxy with an operator to download plugins you need to add the following environment variable to Jenkins Custom Resource (CR), e.g.: | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     containers: | ||||||
|  |       - name: jenkins-master | ||||||
|  |         env: | ||||||
|  |           - name: CURL_OPTIONS | ||||||
|  |             value: -L -x <proxy_url> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | In `CURL_OPTIONS` var you can set additional arguments to `curl` command. | ||||||
|  | 
 | ||||||
|  | ## Pulling Docker images from private repositories | ||||||
|  | 
 | ||||||
|  | To pull a Docker Image from private repository you can use `imagePullSecrets`. | ||||||
|  | 
 | ||||||
|  | Please follow the instructions on [creating a secret with a docker config](https://kubernetes.io/docs/concepts/containers/images/?origin_team=T42NTAGHM#creating-a-secret-with-a-docker-config). | ||||||
|  | 
 | ||||||
|  | ### Docker Hub Configuration | ||||||
|  | To use Docker Hub additional steps are required. | ||||||
|  | 
 | ||||||
|  | Edit the previously created secret: | ||||||
|  | ```bash | ||||||
|  | kubectl -n <namespace> edit secret <name> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | The `.dockerconfigjson` key's value needs to be replaced with a modified version. | ||||||
|  | 
 | ||||||
|  | After modifications, it needs to be encoded as a Base64 value before setting the `.dockerconfigjson` key. | ||||||
|  | 
 | ||||||
|  | Example config file to modify and use: | ||||||
|  | ``` | ||||||
|  | { | ||||||
|  |     "auths":{ | ||||||
|  |         "https://index.docker.io/v1/":{ | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         }, | ||||||
|  |         "auth.docker.io":{ | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         }, | ||||||
|  |         "registry.docker.io":{ | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         }, | ||||||
|  |         "docker.io":{ | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         }, | ||||||
|  |         "https://registry-1.docker.io/v2/": { | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         }, | ||||||
|  |         "registry-1.docker.io/v2/": { | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         }, | ||||||
|  |         "registry-1.docker.io": { | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         }, | ||||||
|  |         "https://registry-1.docker.io": { | ||||||
|  |             "username":"user", | ||||||
|  |             "password":"password", | ||||||
|  |             "email":"yourdockeremail@gmail.com", | ||||||
|  |             "auth":"base64 of string user:password" | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | [job-dsl]:https://github.com/jenkinsci/job-dsl-plugin | ||||||
|  | [kubernetes-credentials-provider]:https://jenkinsci.github.io/kubernetes-credentials-provider-plugin/ | ||||||
|  | @ -0,0 +1,86 @@ | ||||||
|  | --- | ||||||
|  | title: "Configure backup and restore" | ||||||
|  | linkTitle: "Configure backup and restore" | ||||||
|  | weight: 10 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |   Prevent loss of job history | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | Backup and restore is done by a container sidecar. | ||||||
|  | 
 | ||||||
|  | ### PVC | ||||||
|  | 
 | ||||||
|  | #### Create PVC | ||||||
|  | 
 | ||||||
|  | Save to the file named pvc.yaml: | ||||||
|  | ```yaml | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: PersistentVolumeClaim | ||||||
|  | metadata: | ||||||
|  |   name: <pvc_name> | ||||||
|  |   namespace: <namespace> | ||||||
|  | spec: | ||||||
|  |   accessModes: | ||||||
|  |   - ReadWriteOnce | ||||||
|  |   resources: | ||||||
|  |     requests: | ||||||
|  |       storage: 500Gi | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Run the following command: | ||||||
|  | ```bash | ||||||
|  | $ kubectl -n <namespace> create -f pvc.yaml | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | #### Configure Jenkins CR | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: <cr_name> | ||||||
|  |   namespace: <namespace> | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     securityContext: | ||||||
|  |       runAsUser: 1000 | ||||||
|  |       fsGroup: 1000 | ||||||
|  |     containers: | ||||||
|  |     - name: jenkins-master | ||||||
|  |       image: jenkins/jenkins:lts | ||||||
|  |     - name: backup # container responsible for the backup and restore | ||||||
|  |       env: | ||||||
|  |       - name: BACKUP_DIR | ||||||
|  |         value: /backup | ||||||
|  |       - name: JENKINS_HOME | ||||||
|  |         value: /jenkins-home | ||||||
|  |       - name: BACKUP_COUNT | ||||||
|  |         value: "3" # keep only the 2 most recent backups | ||||||
|  |       image: virtuslab/jenkins-operator-backup-pvc:v0.0.8 # look at backup/pvc directory | ||||||
|  |       imagePullPolicy: IfNotPresent | ||||||
|  |       volumeMounts: | ||||||
|  |       - mountPath: /jenkins-home # Jenkins home volume | ||||||
|  |         name: jenkins-home | ||||||
|  |       - mountPath: /backup # backup volume | ||||||
|  |         name: backup | ||||||
|  |     volumes: | ||||||
|  |     - name: backup # PVC volume where backups will be stored | ||||||
|  |       persistentVolumeClaim: | ||||||
|  |         claimName: <pvc_name> | ||||||
|  |   backup: | ||||||
|  |     containerName: backup # container name is responsible for backup | ||||||
|  |     action: | ||||||
|  |       exec: | ||||||
|  |         command: | ||||||
|  |         - /home/user/bin/backup.sh # this command is invoked on "backup" container to make backup, for example /home/user/bin/backup.sh <backup_number>, <backup_number> is passed by operator | ||||||
|  |     interval: 30 # how often make backup in seconds | ||||||
|  |     makeBackupBeforePodDeletion: true # make a backup before pod deletion | ||||||
|  |   restore: | ||||||
|  |     containerName: backup # container name is responsible for restore backup | ||||||
|  |     action: | ||||||
|  |       exec: | ||||||
|  |         command: | ||||||
|  |         - /home/user/bin/restore.sh # this command is invoked on "backup" container to make restore backup, for example /home/user/bin/restore.sh <backup_number>, <backup_number> is passed by operator | ||||||
|  |     #recoveryOnce: <backup_number> # if want to restore specific backup configure this field and then Jenkins will be restarted and desired backup will be restored | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,184 @@ | ||||||
|  | --- | ||||||
|  | title: "Custom Backup and Restore Providers" | ||||||
|  | linkTitle: "Custom Backup and Restore Providers" | ||||||
|  | weight: 10 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |   Custom backup and restore provider | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | 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/*/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 | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,202 @@ | ||||||
|  | --- | ||||||
|  | title: "Customization" | ||||||
|  | linkTitle: "Customization" | ||||||
|  | weight: 3 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |   How to customize Jenkins | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | ## How to customize Jenkins | ||||||
|  | Jenkins can be customized with plugins. | ||||||
|  | Plugin's configuration is applied as groovy scripts or the [configuration as code plugin](https://github.com/jenkinsci/configuration-as-code-plugin). | ||||||
|  | Any plugin working for Jenkins can be installed by the Jenkins Operator. | ||||||
|  |   | ||||||
|  | Pre-installed plugins:  | ||||||
|  | * configuration-as-code v1.38 | ||||||
|  | * git v4.2.2 | ||||||
|  | * job-dsl v1.77 | ||||||
|  | * kubernetes-credentials-provider v0.13 | ||||||
|  | * kubernetes v1.25.2 | ||||||
|  | * workflow-aggregator v2.6 | ||||||
|  | * workflow-job v2.38 | ||||||
|  | 
 | ||||||
|  | Rest of the plugins can be found in [plugins repository](https://plugins.jenkins.io/).  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #### Install plugins | ||||||
|  | 
 | ||||||
|  | Edit Custom Resource under `spec.master.plugins`: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |    plugins: | ||||||
|  |    - name: simple-theme-plugin | ||||||
|  |      version: 0.5.1 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Under `spec.master.basePlugins` you can find plugins for a valid **Jenkins Operator**: | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     basePlugins: | ||||||
|  |     - name: kubernetes | ||||||
|  |       version: 1.18.3 | ||||||
|  |     - name: workflow-job | ||||||
|  |       version: "2.34" | ||||||
|  |     - name: workflow-aggregator | ||||||
|  |       version: "2.6" | ||||||
|  |     - name: git | ||||||
|  |       version: 3.12.0 | ||||||
|  |     - name: job-dsl | ||||||
|  |       version: "1.76" | ||||||
|  |     - name: configuration-as-code | ||||||
|  |       version: "1.29" | ||||||
|  |     - name: kubernetes-credentials-provider | ||||||
|  |       version: 0.12.1 | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | You can change their versions. | ||||||
|  | 
 | ||||||
|  | The **Jenkins Operator** will then automatically install plugins after the Jenkins master pod restart. | ||||||
|  | 
 | ||||||
|  | #### Apply plugin's config | ||||||
|  | 
 | ||||||
|  | By using a [ConfigMap](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/) you can create your own **Jenkins** customized configuration. | ||||||
|  | Then you must reference the **`ConfigMap`** in the **Jenkins** pod customization file in `spec.groovyScripts` or `spec.configurationAsCode` | ||||||
|  | 
 | ||||||
|  | Create a **`ConfigMap`** with specific name (eg. `jenkins-operator-user-configuration`). Then, modify the **Jenkins** manifest: | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   configurationAsCode: | ||||||
|  |     configurations:  | ||||||
|  |     - name: jenkins-operator-user-configuration | ||||||
|  |   groovyScripts: | ||||||
|  |     configurations: | ||||||
|  |     - name: jenkins-operator-user-configuration | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Here is an example of `jenkins-operator-user-configuration`: | ||||||
|  | ```yaml | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: ConfigMap | ||||||
|  | metadata: | ||||||
|  |   name: jenkins-operator-user-configuration | ||||||
|  | data: | ||||||
|  |   1-configure-theme.groovy: |  | ||||||
|  |     import jenkins.* | ||||||
|  |     import jenkins.model.* | ||||||
|  |     import hudson.* | ||||||
|  |     import hudson.model.* | ||||||
|  |     import org.jenkinsci.plugins.simpletheme.ThemeElement | ||||||
|  |     import org.jenkinsci.plugins.simpletheme.CssTextThemeElement | ||||||
|  |     import org.jenkinsci.plugins.simpletheme.CssUrlThemeElement | ||||||
|  | 
 | ||||||
|  |     Jenkins jenkins = Jenkins.getInstance() | ||||||
|  | 
 | ||||||
|  |     def decorator = Jenkins.instance.getDescriptorByType(org.codefirst.SimpleThemeDecorator.class) | ||||||
|  | 
 | ||||||
|  |     List<ThemeElement> configElements = new ArrayList<>(); | ||||||
|  |     configElements.add(new CssTextThemeElement("DEFAULT")); | ||||||
|  |     configElements.add(new CssUrlThemeElement("https://cdn.rawgit.com/afonsof/jenkins-material-theme/gh-pages/dist/material-light-green.css")); | ||||||
|  |     decorator.setElements(configElements); | ||||||
|  |     decorator.save(); | ||||||
|  | 
 | ||||||
|  |     jenkins.save() | ||||||
|  |   1-system-message.yaml: | | ||||||
|  |     jenkins: | ||||||
|  |       systemMessage: "Configuration as Code integration works!!!" | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | * `*.groovy` is Groovy script configuration | ||||||
|  | * `*.yaml is` configuration as code | ||||||
|  | 
 | ||||||
|  | If you want to correct your configuration you can edit it while the **Jenkins Operator** is running.  | ||||||
|  | Jenkins will reconcile and apply the new configuration. | ||||||
|  | 
 | ||||||
|  | ## How to use secrets from a Groovy scripts | ||||||
|  | 
 | ||||||
|  | If you configured `spec.groovyScripts.secret.name`, then this secret is available to use from map Groovy scripts. | ||||||
|  | The secrets are loaded to `secrets` map. | ||||||
|  | 
 | ||||||
|  | Create a [secret](https://kubernetes.io/docs/concepts/configuration/secret/) with for example the name `jenkins-conf-secrets`. | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | kind: Secret | ||||||
|  | apiVersion: v1 | ||||||
|  | type: Opaque | ||||||
|  | metadata: | ||||||
|  |   name: jenkins-conf-secrets | ||||||
|  |   namespace: default | ||||||
|  | data: | ||||||
|  |   SYSTEM_MESSAGE: SGVsbG8gd29ybGQ= | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Then modify the **Jenkins** pod manifest by changing `spec.groovyScripts.secret.name` to `jenkins-conf-secrets`. | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   configurationAsCode: | ||||||
|  |     configurations:  | ||||||
|  |     - name: jenkins-operator-user-configuration | ||||||
|  |     secret: | ||||||
|  |       name: jenkins-conf-secrets | ||||||
|  |   groovyScripts: | ||||||
|  |     configurations: | ||||||
|  |     - name: jenkins-operator-user-configuration | ||||||
|  |     secret: | ||||||
|  |       name: jenkins-conf-secrets | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Now you can test that the secret is mounted by applying this `ConfigMap` for Groovy script: | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: ConfigMap | ||||||
|  | metadata: | ||||||
|  |   name: jenkins-operator-user-configuration | ||||||
|  | data: | ||||||
|  |   1-system-message.groovy: |  | ||||||
|  |     import jenkins.* | ||||||
|  |     import jenkins.model.* | ||||||
|  |     import hudson.* | ||||||
|  |     import hudson.model.* | ||||||
|  |     Jenkins jenkins = Jenkins.getInstance() | ||||||
|  |      | ||||||
|  |     jenkins.setSystemMessage(secrets["SYSTEM_MESSAGE"]) | ||||||
|  |     jenkins.save() | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Or by applying this configuration as code: | ||||||
|  | ```yaml | ||||||
|  | apiVersion: v1 | ||||||
|  | kind: ConfigMap | ||||||
|  | metadata: | ||||||
|  |   name: jenkins-operator-user-configuration | ||||||
|  | data: | ||||||
|  |   1-system-message.yaml: | | ||||||
|  |     jenkins: | ||||||
|  |       systemMessage: ${SYSTEM_MESSAGE} | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | After this, you should see the `Hello world` system message from the **Jenkins** homepage. | ||||||
|  | @ -0,0 +1,90 @@ | ||||||
|  | --- | ||||||
|  | title: "Deploy Jenkins" | ||||||
|  | linkTitle: "Deploy Jenkins" | ||||||
|  | weight: 1 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |   Deploy production ready Jenkins Operator manifest | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | Once Jenkins Operator is up and running let's deploy actual Jenkins instance. | ||||||
|  | Create manifest e.g. **`jenkins_instance.yaml`** with following data and save it on drive. | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   name: example | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     containers: | ||||||
|  |     - name: jenkins-master | ||||||
|  |       image: jenkins/jenkins:lts | ||||||
|  |       imagePullPolicy: Always | ||||||
|  |       livenessProbe: | ||||||
|  |         failureThreshold: 12 | ||||||
|  |         httpGet: | ||||||
|  |           path: /login | ||||||
|  |           port: http | ||||||
|  |           scheme: HTTP | ||||||
|  |         initialDelaySeconds: 80 | ||||||
|  |         periodSeconds: 10 | ||||||
|  |         successThreshold: 1 | ||||||
|  |         timeoutSeconds: 5 | ||||||
|  |       readinessProbe: | ||||||
|  |         failureThreshold: 3 | ||||||
|  |         httpGet: | ||||||
|  |           path: /login | ||||||
|  |           port: http | ||||||
|  |           scheme: HTTP | ||||||
|  |         initialDelaySeconds: 30 | ||||||
|  |         periodSeconds: 10 | ||||||
|  |         successThreshold: 1 | ||||||
|  |         timeoutSeconds: 1 | ||||||
|  |       resources: | ||||||
|  |         limits: | ||||||
|  |           cpu: 1500m | ||||||
|  |           memory: 3Gi | ||||||
|  |         requests: | ||||||
|  |           cpu: "1" | ||||||
|  |           memory: 500Mi | ||||||
|  |   seedJobs: | ||||||
|  |   - id: jenkins-operator | ||||||
|  |     targets: "cicd/jobs/*.jenkins" | ||||||
|  |     description: "Jenkins Operator repository" | ||||||
|  |     repositoryBranch: master | ||||||
|  |     repositoryUrl: https://github.com/jenkinsci/kubernetes-operator.git | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Deploy a Jenkins to Kubernetes: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl create -f jenkins_instance.yaml | ||||||
|  | ``` | ||||||
|  | Watch the Jenkins instance being created: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl get pods -w | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Get the Jenkins credentials: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl get secret jenkins-operator-credentials-<cr_name> -o 'jsonpath={.data.user}' | base64 -d | ||||||
|  | kubectl get secret jenkins-operator-credentials-<cr_name> -o 'jsonpath={.data.password}' | base64 -d | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Connect to the Jenkins instance (minikube): | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | minikube service jenkins-operator-http-<cr_name> --url | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Connect to the Jenkins instance (actual Kubernetes cluster): | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl port-forward jenkins-<cr_name> 8080:8080 | ||||||
|  | ``` | ||||||
|  | Then open browser with address `http://localhost:8080`. | ||||||
|  | 
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,42 @@ | ||||||
|  | --- | ||||||
|  | title: "Diagnostics" | ||||||
|  | linkTitle: "Diagnostics" | ||||||
|  | weight: 40 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |   How to deal with Jenkins Operator problems | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | Turn on debug in **Jenkins Operator** deployment: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | sed -i 's|\(args:\).*|\1\ ["--debug"\]|' deploy/operator.yaml | ||||||
|  | kubectl apply -f deploy/operator.yaml | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Watch Kubernetes events: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl get events --sort-by='{.lastTimestamp}' | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Verify Jenkins master logs: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl logs -f jenkins-<cr_name> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Verify the `jenkins-operator` logs: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl logs deployment/jenkins-operator | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Troubleshooting | ||||||
|  | 
 | ||||||
|  | Delete the Jenkins master pod and wait for the new one to come up: | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | kubectl delete pod jenkins-<cr_name> | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,114 @@ | ||||||
|  | --- | ||||||
|  | title: "Notifications" | ||||||
|  | linkTitle: "Notifications" | ||||||
|  | weight: 10 | ||||||
|  | date: 2020-04-13 | ||||||
|  | description: > | ||||||
|  |     How to setup operator notifications. | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | ## Slack | ||||||
|  | 
 | ||||||
|  | Please follow [this](https://api.slack.com/incoming-webhooks) instructions to get web hook URL. | ||||||
|  | 
 | ||||||
|  | Create web hook secret with name `jenkins-operator-notification-data`. Contains key `url` with provided web hook URL. | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | $ kubectl create secret generic jenkins-operator-notification-data --from-literal=url=<webhook_url> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Example configuration for Slack: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | kind: Jenkins | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     notifications: | ||||||
|  |     - level: info | ||||||
|  |       verbose: true | ||||||
|  |       name: <name> | ||||||
|  |       slack: | ||||||
|  |         webHookURLSecretKeySelector: | ||||||
|  |           secret: | ||||||
|  |             name: <secret_name> | ||||||
|  |           key: <key> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Microsoft Teams | ||||||
|  | 
 | ||||||
|  | Please follow [this](https://docs.microsoft.com/en-gb/outlook/actionable-messages/send-via-connectors) instructions to get web hook URL. | ||||||
|  | 
 | ||||||
|  | Example configuration for Microsoft Teams: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | kind: Jenkins | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     notifications: | ||||||
|  |     - level: info | ||||||
|  |       verbose: true | ||||||
|  |       name: <name> | ||||||
|  |       teams: | ||||||
|  |         webHookURLSecretKeySelector: | ||||||
|  |           secret: | ||||||
|  |             name: <secret_name> | ||||||
|  |           key: <key> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Mailgun | ||||||
|  | 
 | ||||||
|  | Example configuration for Mailgun: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | kind: Jenkins | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     notifications: | ||||||
|  |     - level: info | ||||||
|  |       verbose: true | ||||||
|  |       name: <name> | ||||||
|  |       mailgun: | ||||||
|  |         domain: <domain> | ||||||
|  |         apiKeySecretKeySelector: | ||||||
|  |           secret: | ||||||
|  |             name: <secret_name> | ||||||
|  |           key: <key> | ||||||
|  |         recipient: <your_email> | ||||||
|  |         from: <mailgun_email> | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Debug options | ||||||
|  | 
 | ||||||
|  | As you see there is two debugging options:  | ||||||
|  | 
 | ||||||
|  | * `level` (warning/info) - Set level of messages to send. | ||||||
|  | 
 | ||||||
|  | * `verbose` - Print stacktrace and additional error messages | ||||||
|  | 
 | ||||||
|  | ## Multiple providers | ||||||
|  | 
 | ||||||
|  | You can use multiple providers to send notification to another communication channels at the same time. | ||||||
|  | For example you will send notifications to Slack and Teams. | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | kind: Jenkins | ||||||
|  | spec: | ||||||
|  |   master: | ||||||
|  |     notifications: | ||||||
|  |     - level: info | ||||||
|  |       verbose: true | ||||||
|  |       name: nslack | ||||||
|  |       slack: | ||||||
|  |         webHookURLSecretKeySelector: | ||||||
|  |           secret: | ||||||
|  |             name: <secret_name> | ||||||
|  |           key: <key> | ||||||
|  |     - level: info | ||||||
|  |       verbose: true | ||||||
|  |       name: nteams | ||||||
|  |       teams: | ||||||
|  |         webHookURLSecretKeySelector: | ||||||
|  |           secret: | ||||||
|  |             name: <secret_name> | ||||||
|  |           key: <key> | ||||||
|  | ``` | ||||||
|  | @ -0,0 +1,104 @@ | ||||||
|  | --- | ||||||
|  | title: "OpenShift" | ||||||
|  | linkTitle: "OpenShift" | ||||||
|  | weight: 20 | ||||||
|  | date: 2020-04-29 | ||||||
|  | description: > | ||||||
|  |     Additional configuration for OpenShift | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | ## SecurityContext | ||||||
|  | 
 | ||||||
|  | OpenShift enforces Security Constraints Context (scc) when deploying an image. | ||||||
|  | By default, container images run in restricted scc which prevents from setting | ||||||
|  | a fixed user id to run with. You need to have ensure that you do not provide a | ||||||
|  | securityContext with a runAsUser and that your image does not use a hardcoded user. | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | securityContext: {} | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## OpenShift Jenkins image | ||||||
|  | 
 | ||||||
|  | OpenShift provides a pre-configured Jenkins image containing  3 openshift plugins for | ||||||
|  | jenkins (openshift-login-plugin, openshift-sync-plugin and openshift-client-plugin) | ||||||
|  | which allows better jenkins integration with kubernetes and OpenShift. | ||||||
|  | 
 | ||||||
|  | The OpenShift Jenkins image requires additional configuration to be fully enabled. | ||||||
|  | 
 | ||||||
|  | ### Sample OpenShift CR | ||||||
|  | The following Custom Resource can be used to create a Jenkins instance using the   | ||||||
|  | OpenShift Jenkins image and sets values for: | ||||||
|  | - `image: 'quay.io/openshift/origin-jenkins:latest' : This is the OpenShift Jenkins image. | ||||||
|  | 
 | ||||||
|  | - serviceAccount: to allow oauth authentication to work, the service account needs | ||||||
|  | a specific annotation pointing to the route exposing the jenkins service. Here, | ||||||
|  | the route is named `jenkins-route` | ||||||
|  | 
 | ||||||
|  | - `OPENSHIFT_ENABLE_OAUTH` environment variable for the master container is set to true. | ||||||
|  | 
 | ||||||
|  | Here is a complete Jenkins CR allowing the deployment of the Jenkins OpenShift image. | ||||||
|  | ```yaml | ||||||
|  | apiVersion: jenkins.io/v1alpha2 | ||||||
|  | kind: Jenkins | ||||||
|  | metadata: | ||||||
|  |   annotations: | ||||||
|  |     jenkins.io/openshift-mode: 'true' | ||||||
|  |   name: jenkins | ||||||
|  | spec: | ||||||
|  |   serviceAccount: | ||||||
|  |     annotations: | ||||||
|  |       serviceaccounts.openshift.io/oauth-redirectreference.jenkins: '{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"jenkins-route"}}' | ||||||
|  |   master: | ||||||
|  |     containers: | ||||||
|  |     - name: jenkins-master | ||||||
|  |       image: 'quay.io/openshift/origin-jenkins:latest' | ||||||
|  |       command: | ||||||
|  |       - /usr/bin/go-init | ||||||
|  |       - '-main' | ||||||
|  |       - /usr/libexec/s2i/run | ||||||
|  |       env: | ||||||
|  |       - name: OPENSHIFT_ENABLE_OAUTH | ||||||
|  |         value: 'true' | ||||||
|  |       - name: OPENSHIFT_ENABLE_REDIRECT_PROMPT | ||||||
|  |         value: 'true' | ||||||
|  |       - name: DISABLE_ADMINISTRATIVE_MONITORS | ||||||
|  |         value: 'false' | ||||||
|  |       - name: KUBERNETES_MASTER | ||||||
|  |         value: 'https://kubernetes.default:443' | ||||||
|  |       - name: KUBERNETES_TRUST_CERTIFICATES | ||||||
|  |         value: 'true' | ||||||
|  |       - name: JENKINS_SERVICE_NAME | ||||||
|  |         value: jenkins-operator-http-jenkins | ||||||
|  |       - name: JNLP_SERVICE_NAME | ||||||
|  |         value: jenkins-operator-slave-jenkins | ||||||
|  |       - name: JENKINS_UC_INSECURE | ||||||
|  |         value: 'false' | ||||||
|  |       - name: JENKINS_HOME | ||||||
|  |         value: /var/lib/jenkins | ||||||
|  |       - name: JAVA_OPTS | ||||||
|  |         value: >- | ||||||
|  |           -XX:+UnlockExperimentalVMOptions -XX:+UnlockExperimentalVMOptions | ||||||
|  |           -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 | ||||||
|  |           -Djenkins.install.runSetupWizard=false -Djava.awt.headless=true | ||||||
|  |       imagePullPolicy: Always | ||||||
|  |   service: | ||||||
|  |     port: 8080 | ||||||
|  |     type: ClusterIP | ||||||
|  |   slaveService: | ||||||
|  |     port: 50000 | ||||||
|  |     type: ClusterIP | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ### OpenShift OAuth integration | ||||||
|  | The creation of a Route is required for the integraiton of Jenkins with | ||||||
|  | OpenShift oauth authentication. By default, the jenkins http service is named | ||||||
|  | `jenkins-operator-http-${jenkins-cr-name}` | ||||||
|  | 
 | ||||||
|  | ```bash | ||||||
|  | oc create route edge jenkins-route --service=jenkins-operator-http-jenkins | ||||||
|  | ``` | ||||||
|  | Note: the route name (jenkins-route) must match the pointed route on the serviceaccount annotation. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | After the creation of the Route. It can be used to navigate to the Jenkins Login Page and login with your Openshift Credentials. | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
		Reference in New Issue