From 1bbb45e697eb4b58ebe18dc15f1a13fa3dacda2d Mon Sep 17 00:00:00 2001 From: lusyoe Date: Mon, 28 May 2018 14:45:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0jenkins=E5=92=8C=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide/jenkins.md | 166 +++++++++++++ manifests/jenkins/.helmignore | 21 ++ manifests/jenkins/Chart.yaml | 18 ++ manifests/jenkins/OWNERS | 6 + manifests/jenkins/README.md | 235 ++++++++++++++++++ manifests/jenkins/templates/NOTES.txt | 45 ++++ manifests/jenkins/templates/_helpers.tpl | 34 +++ manifests/jenkins/templates/config.yaml | 215 ++++++++++++++++ manifests/jenkins/templates/home-pvc.yaml | 28 +++ .../jenkins/templates/jenkins-agent-svc.yaml | 20 ++ .../templates/jenkins-master-deployment.yaml | 222 +++++++++++++++++ .../templates/jenkins-master-ingress.yaml | 22 ++ .../jenkins-master-networkpolicy.yaml | 33 +++ .../jenkins/templates/jenkins-master-svc.yaml | 31 +++ manifests/jenkins/templates/jenkins-test.yaml | 45 ++++ manifests/jenkins/templates/jobs.yaml | 8 + manifests/jenkins/templates/rbac.yaml | 20 ++ manifests/jenkins/templates/secret.yaml | 19 ++ .../jenkins/templates/service-account.yaml | 12 + manifests/jenkins/templates/test-config.yaml | 9 + manifests/jenkins/values.yaml | 194 +++++++++++++++ 21 files changed, 1403 insertions(+) create mode 100644 docs/guide/jenkins.md create mode 100644 manifests/jenkins/.helmignore create mode 100644 manifests/jenkins/Chart.yaml create mode 100644 manifests/jenkins/OWNERS create mode 100644 manifests/jenkins/README.md create mode 100644 manifests/jenkins/templates/NOTES.txt create mode 100644 manifests/jenkins/templates/_helpers.tpl create mode 100644 manifests/jenkins/templates/config.yaml create mode 100644 manifests/jenkins/templates/home-pvc.yaml create mode 100644 manifests/jenkins/templates/jenkins-agent-svc.yaml create mode 100644 manifests/jenkins/templates/jenkins-master-deployment.yaml create mode 100644 manifests/jenkins/templates/jenkins-master-ingress.yaml create mode 100644 manifests/jenkins/templates/jenkins-master-networkpolicy.yaml create mode 100644 manifests/jenkins/templates/jenkins-master-svc.yaml create mode 100644 manifests/jenkins/templates/jenkins-test.yaml create mode 100644 manifests/jenkins/templates/jobs.yaml create mode 100644 manifests/jenkins/templates/rbac.yaml create mode 100644 manifests/jenkins/templates/secret.yaml create mode 100644 manifests/jenkins/templates/service-account.yaml create mode 100644 manifests/jenkins/templates/test-config.yaml create mode 100644 manifests/jenkins/values.yaml diff --git a/docs/guide/jenkins.md b/docs/guide/jenkins.md new file mode 100644 index 000000000..cff29e82f --- /dev/null +++ b/docs/guide/jenkins.md @@ -0,0 +1,166 @@ +# Jenkins CI/CD + +## 前言 +本文档介绍如何快速通过K8s集群实现Jenkins 动态Slave CI/CD流程。 + +## 开始之前 +在开始之前需要准备以下环境: +- k8s dns组件 +参考文档:[kubedns](kubedns.md) +- helm +为了简化部署,通过helm来安装Jenkins,可参考文档:[helm](helm.md) +- 持久化存储 +这里使用**NFS**演示,参考文档:[nfs-client](nfs-client.md)。如果k8s集群是部署在公有云,也可使用厂商的NAS等存储方案,具体参考相关厂商文档 +- Ingress Controller(nginx-ingress/traefik) +默认是通过Ingress访问Jenkins,因此需要安装一种`Ingress Controller`。参考文档:[ingress](ingress.md) +- Gitlab 代码管理仓库。用于提交代码后自动触发CI, 目前项目中还没有相关内容,可[参考官网](https://about.gitlab.com/installation/)进行安装。 + +## 安装Jenkins +执行以下命令快速安装: +``` +helm install manifests/jenkins/ --name jenkins +``` +由于初始化过程中,默认安装指定的插件,所以启动较慢,大概5-10分钟左右就可以启动完成了。 + +部分默认配置说明: +**注**:以下配置都定义在`manifests/jenkins/values.yaml`文件中。 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
字段说明默认值
InstallPlugins初始化安装的插件 +
    +
  • kubernetes:1.6.3
  • +
  • workflow-aggregator:2.5
  • +
  • workflow-job:2.21
  • +
  • credentials-binding:1.16
  • +
  • git:3.9.0
  • +
  • gitlab:1.5.6
  • +
+
HostNameIngress访问入口jenkins.local.com
AdminPasswordadmin登录密码admin
UpdateCenter插件下载镜像地址https://mirrors.tuna.tsinghua.edu.cn/jenkins
StorageClass持久化存储SCnfs-dynamic-class
+ + +## 配置Kubernetes plugin +登录Jenkins,点击左边导航`系统管理`——>`系统设置`,拖动到最下面可以看到`云——>Kubernetes`配置,默认配置有以下字段: + +- Name:配置名称,后面运行测试的时候会用到,用于区别多个Kubernetes配置,默认为:kubernetes +- Kubernetes URL:集群访问url,可通过`kubectl cluster-info`查看,如果集群有部署**DNS**插件, 也可以直接填服务名称(自动解析),默认使用服务名称:https://kubernetes +- Jenkins URL:Jenkins访问地址,默认使用服务名称+端口号 + +在Jenkins初始化时,默认都已经配置好了,可以直接新建项目测试了。 + +## 简单测试 +点击左边:新建任务——>流水线(Pipeline) +任务名称可以随便起,这里为:k8s-test +配置——>流水线,选择`Pipeline script` +以下为测试脚本内容: +``` +podTemplate(label: 'jenkins-slave', cloud: 'kubernetes') +{ + node ('jenkins-slave') { + stage('test') { + echo "hello, world" + sleep 60 + } + } +} +``` + +- cloud:插件配置中的Name +- label:插件配置中的Images——>Kubernetes Pod Tempalte——>Labels +- node:与label一致即可 + +保存配置,点击立即构建,查看控制台输出,出现以下内容就表示运行成功了: +``` +Agent default-lsths is provisioned from template Kubernetes Pod Template +Agent specification [Kubernetes Pod Template] (jenkins-slave): +* [jnlp] jenkins/jnlp-slave:alpine(resourceRequestCpu: 200m, resourceRequestMemory: 256Mi, resourceLimitCpu: 200m, resourceLimitMemory: 256Mi) + +Running on default-lsths in /home/jenkins/workspace/k8s-test +[Pipeline] { +[Pipeline] stage +[Pipeline] { (test) +[Pipeline] echo +hello, world +[Pipeline] sleep +Sleeping for 1 min 0 sec +[Pipeline] } +[Pipeline] // stage +[Pipeline] } +[Pipeline] // node +[Pipeline] } +[Pipeline] // podTemplate +[Pipeline] End of Pipeline +Finished: SUCCESS +``` + + +## 配置自动触发CI + +- 配置Gitlab项目 +在`Gitlab`中创建一个测试项目,将上面测试的脚本内容写入到一个`Jenkinsfile`文件中,然后上传到该测试项目根路径下。 + +- 配置Jenkins项目 +点击项目`配置`——>`构建触发器`——>勾选`Build when a change is pushed to GitLab. GitLab webhook URL:http://jenkins.local.com/project/k8s-test`——>保存配置 + +- 配置Webhook +进入Gitlab测试项目的`Settings——>Integrations`,一般只需要填写`URL`即可,其他的可根据需求环境配置 +默认Jenkins配置不允许匿名用户触发构建,因此还需要添加用户和token。 +URL的格式为: +`http://[UserID]:[API Token]@jenkins.local.com/project/[ProjectName]` + +Jenkins 用户ID Token查看: +点击右上角的用户名——>设置——>API Token(点击Show API Token...) + +最终Webhook中的URL类似: +http://admin:a910b1492e39e9dd1ea48ea7f7638aaf@jenkins.local.com/project/k8s-test + +后面只需要我们一提交代码到Git仓库,就会自动触发Jenkins进行构建了。 + +## 项目应用 +这里我们以一个简单的Java项目为例,实战演示如何进行CI/CD。 +基本环境配置上面已经说过了,这里就不多介绍。 +示例项目:https://github.com/lusyoe/springboot-k8s-example + +结构说明: +- 镜像构建文件:`Dockerfile` +- k8s应用配置:`k8s-example.yaml` +- 项目源码:`src` +- Jenkins构建文件:`jenkins/Jenkinsfile` + +构建流程说明: +- 通过Jenkins kubernetes插件,定义构建过程中所需的3个docker容器:maven、docker、kubectl (这3个容器都在一个pod中) +- 挂载docker.sock和kubeconfig文件 +- 首先使用`maven`容器,检出代码,执行项目构建 +- 使用`docker`容器,构建镜像,推送到镜像参考 +- 使用`kubectl`容器,部署`k8s-example`应用(这里后面也可以使用helm) + +访问: +项目通过Ingress访问`k8s-example.com`,出现`hello, world`,就表示服务部署成功了。 \ No newline at end of file diff --git a/manifests/jenkins/.helmignore b/manifests/jenkins/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/manifests/jenkins/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/manifests/jenkins/Chart.yaml b/manifests/jenkins/Chart.yaml new file mode 100644 index 000000000..f0d539039 --- /dev/null +++ b/manifests/jenkins/Chart.yaml @@ -0,0 +1,18 @@ +name: jenkins +home: https://jenkins.io/ +version: 0.16.1 +appVersion: 2.107 +description: Open source continuous integration server. It supports multiple SCM tools + including CVS, Subversion and Git. It can execute Apache Ant and Apache Maven-based + projects as well as arbitrary scripts. +sources: +- https://github.com/jenkinsci/jenkins +- https://github.com/jenkinsci/docker-jnlp-slave +maintainers: +- name: lachie83 + email: lachlan.evenson@microsoft.com +- name: viglesiasce + email: viglesias@google.com +- name: lusyoe + email: lusyoe@163.com +icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png diff --git a/manifests/jenkins/OWNERS b/manifests/jenkins/OWNERS new file mode 100644 index 000000000..f5544ea12 --- /dev/null +++ b/manifests/jenkins/OWNERS @@ -0,0 +1,6 @@ +approvers: +- lachie83 +- viglesiasce +reviewers: +- lachie83 +- viglesiasce diff --git a/manifests/jenkins/README.md b/manifests/jenkins/README.md new file mode 100644 index 000000000..ce9b5e3d0 --- /dev/null +++ b/manifests/jenkins/README.md @@ -0,0 +1,235 @@ +# Jenkins Helm Chart + +Jenkins master and slave cluster utilizing the Jenkins Kubernetes plugin + +* https://wiki.jenkins-ci.org/display/JENKINS/Kubernetes+Plugin + +Inspired by the awesome work of Carlos Sanchez + +## Chart Details + +This chart will do the following: + +* 1 x Jenkins Master with port 8080 exposed on an external LoadBalancer +* All using Kubernetes Deployments + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```bash +$ helm install --name my-release stable/jenkins +``` + +## Configuration + +The following tables list the configurable parameters of the Jenkins chart and their default values. + +### Jenkins Master +| Parameter | Description | Default | +| --------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------- | +| `nameOverride` | Override the resource name prefix | `jenkins` | +| `fullnameOverride` | Override the full resource names | `jenkins-{release-name}` (or `jenkins` if release-name is `jenkins`) | +| `Master.Name` | Jenkins master name | `jenkins-master` | +| `Master.Image` | Master image name | `jenkinsci/jenkins` | +| `Master.ImageTag` | Master image tag | `lts` | +| `Master.ImagePullPolicy` | Master image pull policy | `IfNotPresent` | +| `Master.ImagePullSecret` | Master image pull secret | Not set | +| `Master.Component` | k8s selector key | `jenkins-master` | +| `Master.UseSecurity` | Use basic security | `true` | +| `Master.AdminUser` | Admin username (and password) created as a secret if useSecurity is true | `admin` | +| `Master.Cpu` | Master requested cpu | `200m` | +| `Master.Memory` | Master requested memory | `512Mi` | +| `Master.InitContainerEnv` | Environment variables for Init Container | Not set | +| `Master.ContainerEnv` | Environment variables for Jenkins Container | Not set | +| `Master.RunAsUser` | uid that jenkins runs with | `0` | +| `Master.FsGroup` | uid that will be used for persistent volume | `0` | +| `Master.ServiceAnnotations` | Service annotations | `{}` | +| `Master.ServiceType` | k8s service type | `ClusterIP` | +| `Master.ServicePort` | k8s service port | `8080` | +| `Master.NodePort` | k8s node port | Not set | +| `Master.HealthProbes` | Enable k8s liveness and readiness probes | `true` | +| `Master.HealthProbesTimeout` | Set the timeout for the liveness and readiness probes | `120` | +| `Master.HealthProbeLivenessFailureThreshold` | Set the failure threshold for the liveness probe | `12` | +| `Master.ContainerPort` | Master listening port | `8080` | +| `Master.SlaveListenerPort` | Listening port for agents | `50000` | +| `Master.DisabledAgentProtocols` | Disabled agent protocols | `JNLP-connect JNLP2-connect` | +| `Master.CSRF.DefaultCrumbIssuer.Enabled` | Enable the default CSRF Crumb issuer | `true` | +| `Master.CSRF.DefaultCrumbIssuer.ProxyCompatability` | Enable proxy compatibility | `true` | +| `Master.CLI` | Enable CLI over remoting | `false` | +| `Master.LoadBalancerSourceRanges` | Allowed inbound IP addresses | `0.0.0.0/0` | +| `Master.LoadBalancerIP` | Optional fixed external IP | Not set | +| `Master.JMXPort` | Open a port, for JMX stats | Not set | +| `Master.CustomConfigMap` | Use a custom ConfigMap | `false` | +| `Master.Ingress.Annotations` | Ingress annotations | `{}` | +| `Master.Ingress.TLS` | Ingress TLS configuration | `[]` | +| `Master.InitScripts` | List of Jenkins init scripts | Not set | +| `Master.CredentialsXmlSecret` | Kubernetes secret that contains a 'credentials.xml' file | Not set | +| `Master.SecretsFilesSecret` | Kubernetes secret that contains 'secrets' files | Not set | +| `Master.Jobs` | Jenkins XML job configs | Not set | +| `Master.InstallPlugins` | List of Jenkins plugins to install | `kubernetes:0.11 workflow-aggregator:2.5 credentials-binding:1.11 git:3.2.0` | +| `Master.ScriptApproval` | List of groovy functions to approve | Not set | +| `Master.NodeSelector` | Node labels for pod assignment | `{}` | +| `Master.Affinity` | Affinity settings | `{}` | +| `Master.Tolerations` | Toleration labels for pod assignment | `{}` | +| `NetworkPolicy.Enabled` | Enable creation of NetworkPolicy resources. | `false` | +| `NetworkPolicy.ApiVersion` | NetworkPolicy ApiVersion | `extensions/v1beta1` | +| `rbac.install` | Create service account and ClusterRoleBinding for Kubernetes plugin | `true` | +| `rbac.apiVersion` | RBAC API version | `v1` | +| `rbac.roleRef` | Cluster role name to bind to | `cluster-admin` | + +### Jenkins Agent + +| Parameter | Description | Default | +| ----------------------- | ----------------------------------------------- | ---------------------- | +| `Agent.AlwaysPullImage` | Always pull agent container image before build | `false` | +| `Agent.Enabled` | Enable Kubernetes plugin jnlp-agent podTemplate | `true` | +| `Agent.Image` | Agent image name | `jenkins/jnlp-slave` | +| `Agent.ImagePullSecret` | Agent image pull secret | Not set | +| `Agent.ImageTag` | Agent image tag | `latest` | +| `Agent.Privileged` | Agent privileged container | `false` | +| `Agent.Cpu` | Agent requested cpu | `200m` | +| `Agent.Memory` | Agent requested memory | `256Mi` | +| `Agent.volumes` | Additional volumes | `nil` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```bash +$ helm install --name my-release -f values.yaml stable/jenkins +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Mounting volumes into your Agent pods + +Your Jenkins Agents will run as pods, and it's possible to inject volumes where needed: + +```yaml +Agent: + volumes: + - type: Secret + secretName: jenkins-mysecrets + mountPath: /var/run/secrets/jenkins-mysecrets +``` + +The supported volume types are: `ConfigMap`, `EmptyDir`, `HostPath`, `Nfs`, `Pod`, `Secret`. Each type supports a different set of configurable attributes, defined by [the corresponding Java class](https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes). + +## NetworkPolicy + +To make use of the NetworkPolicy resources created by default, +install [a networking plugin that implements the Kubernetes +NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin). + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting +the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + + kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" + +Install helm chart with network policy enabled: + + $ helm install stable/jenkins --set NetworkPolicy.Enabled=true + +## Persistence + +The Jenkins image stores persistence under `/var/jenkins_home` path of the container. A dynamically managed Persistent Volume +Claim is used to keep the data across deployments, by default. This is known to work in GCE, AWS, and minikube. Alternatively, +a previously configured Persistent Volume Claim can be used. + +It is possible to mount several volumes using `Persistence.volumes` and `Persistence.mounts` parameters. + +### Persistence Values + +| Parameter | Description | Default | +| --------------------------- | ------------------------------- | --------------- | +| `Persistence.Enabled` | Enable the use of a Jenkins PVC | `true` | +| `Persistence.ExistingClaim` | Provide the name of a PVC | `nil` | +| `Persistence.AccessMode` | The PVC access mode | `ReadWriteOnce` | +| `Persistence.Size` | The size of the PVC | `8Gi` | +| `Persistence.volumes` | Additional volumes | `nil` | +| `Persistence.mounts` | Additional mounts | `nil` | +| `Persistence.StorageClass` | The PV Provisioner | `nfs-dynamic-class`| + +#### Existing PersistentVolumeClaim + +1. Create the PersistentVolume +1. Create the PersistentVolumeClaim +1. Install the chart + +```bash +$ helm install --name my-release --set Persistence.ExistingClaim=PVC_NAME stable/jenkins +``` + +## Custom ConfigMap + +When creating a new parent chart with this chart as a dependency, the `CustomConfigMap` parameter can be used to override the default config.xml provided. +It also allows for providing additional xml configuration files that will be copied into `/var/jenkins_home`. In the parent chart's values.yaml, +set the `jenkins.Master.CustomConfigMap` value to true like so + +```yaml +jenkins: + Master: + CustomConfigMap: true +``` + +and provide the file `templates/config.tpl` in your parent chart for your use case. You can start by copying the contents of `config.yaml` from this chart into your parent charts `templates/config.tpl` as a basis for customization. Finally, you'll need to wrap the contents of `templates/config.tpl` like so: + +```yaml +{{- define "override_config_map" }} + +{{ end }} +``` + +## RBAC + +If running upon a cluster with RBAC enabled you will need to do the following: + +* `helm install stable/jenkins --set rbac.install=true` +* Create a Jenkins credential of type Kubernetes service account with service account name provided in the `helm status` output. +* Under configure Jenkins -- Update the credentials config in the cloud section to use the service account credential you created in the step above. + +## Run Jenkins as non root user + +The default settings of this helm chart let Jenkins run as root user with uid `0`. +Due to security reasons you may want to run Jenkins as a non root user. +Fortunately the default jenkins docker image `jenkins/jenkins` contains a user `jenkins` with uid `1000` that can be used for this purpose. + +Simply use the following settings to run Jenkins as `jenkins` user with uid `1000`. + +```yaml +jenkins: + Master: + RunAsUser: 1000 + FsGroup: 1000 +``` + +Docs taken from https://github.com/jenkinsci/docker/blob/master/Dockerfile: +_Jenkins is run with user `jenkins`, uid = 1000. If you bind mount a volume from the host or a data container,ensure you use the same uid_ + +## Running behind a forward proxy + +The master pod uses an Init Container to install plugins etc. If you are behind a corporate proxy it may be useful to set `Master.InitContainerEnv` to add environment variables such as `http_proxy`, so that these can be downloaded. + +Additionally, you may want to add env vars for the Jenkins container, and the JVM (`Master.JavaOpts`). + +```yaml +Master: + InitContainerEnv: + - name: http_proxy + value: "http://192.168.64.1:3128" + - name: https_proxy + value: "http://192.168.64.1:3128" + - name: no_proxy + value: "" + ContainerEnv: + - name: http_proxy + value: "http://192.168.64.1:3128" + - name: https_proxy + value: "http://192.168.64.1:3128" + JavaOpts: >- + -Dhttp.proxyHost=192.168.64.1 + -Dhttp.proxyPort=3128 + -Dhttps.proxyHost=192.168.64.1 + -Dhttps.proxyPort=3128 +``` diff --git a/manifests/jenkins/templates/NOTES.txt b/manifests/jenkins/templates/NOTES.txt new file mode 100644 index 000000000..dc9e5e419 --- /dev/null +++ b/manifests/jenkins/templates/NOTES.txt @@ -0,0 +1,45 @@ +1. Get your '{{ .Values.Master.AdminUser }}' user password by running: + printf $(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "jenkins.fullname" . }} -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echo + +{{- if .Values.Master.HostName }} + +2. Visit http://{{ .Values.Master.HostName }} +{{- else }} +2. Get the Jenkins URL to visit by running these commands in the same shell: +{{- if contains "NodePort" .Values.Master.ServiceType }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "jenkins.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT/login + +{{- else if contains "LoadBalancer" .Values.Master.ServiceType }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "jenkins.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "jenkins.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}") + echo http://$SERVICE_IP:{{ .Values.Master.ServicePort }}/login + +{{- else if contains "ClusterIP" .Values.Master.ServiceType }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "component={{ template "jenkins.fullname" . }}-master" -o jsonpath="{.items[0].metadata.name}") + echo http://127.0.0.1:{{ .Values.Master.ServicePort }} + kubectl port-forward $POD_NAME {{ .Values.Master.ServicePort }}:{{ .Values.Master.ServicePort }} + +{{- end }} +{{- end }} + +3. Login with the password from step 1 and the username: {{ .Values.Master.AdminUser }} + +For more information on running Jenkins on Kubernetes, visit: +https://cloud.google.com/solutions/jenkins-on-container-engine + +{{- if .Values.Persistence.Enabled }} +{{- else }} +################################################################################# +###### WARNING: Persistence is disabled!!! You will lose your data when ##### +###### the Jenkins pod is terminated. ##### +################################################################################# +{{- end }} + +{{- if .Values.rbac.install }} +Configure the Kubernetes plugin in Jenkins to use the following Service Account name {{ template "jenkins.fullname" . }} using the following steps: + Create a Jenkins credential of type Kubernetes service account with service account name {{ template "jenkins.fullname" . }} + Under configure Jenkins -- Update the credentials config in the cloud section to use the service account credential you created in the step above. +{{- end }} diff --git a/manifests/jenkins/templates/_helpers.tpl b/manifests/jenkins/templates/_helpers.tpl new file mode 100644 index 000000000..eac695f6b --- /dev/null +++ b/manifests/jenkins/templates/_helpers.tpl @@ -0,0 +1,34 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "jenkins.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "jenkins.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 -}} + +{{- define "jenkins.kubernetes-version" -}} + {{- range .Values.Master.InstallPlugins -}} + {{ if hasPrefix "kubernetes:" . }} + {{- $split := splitList ":" . }} + {{- printf "%s" (index $split 1 ) -}} + {{- end -}} + {{- end -}} +{{- end -}} diff --git a/manifests/jenkins/templates/config.yaml b/manifests/jenkins/templates/config.yaml new file mode 100644 index 000000000..03d69fb65 --- /dev/null +++ b/manifests/jenkins/templates/config.yaml @@ -0,0 +1,215 @@ +{{- if not .Values.Master.CustomConfigMap }} + +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }} +data: + config.xml: |- + + + + {{ .Values.Master.ImageTag }} + 0 + NORMAL + {{ .Values.Master.UseSecurity }} + + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULLNAME} + ${ITEM_ROOTDIR}/builds + + + + + + + kubernetes + +{{- if .Values.Agent.Enabled }} + + + default + 2147483647 + 0 + + + {{- $local := dict "first" true }} + {{- range $key, $value := .Values.Agent.NodeSelector }} + {{- if not $local.first }},{{- end }} + {{- $key }}={{ $value }} + {{- $_ := set $local "first" false }} + {{- end }} + NORMAL + +{{- range $index, $volume := .Values.Agent.volumes }} + +{{- range $key, $value := $volume }}{{- if not (eq $key "type") }} + <{{ $key }}>{{ $value }} +{{- end }}{{- end }} + +{{- end }} + + + + jnlp + {{ .Values.Agent.Image }}:{{ .Values.Agent.ImageTag }} +{{- if .Values.Agent.Privileged }} + true +{{- else }} + false +{{- end }} + {{ .Values.Agent.AlwaysPullImage }} + /home/jenkins + + ${computer.jnlpmac} ${computer.name} + false + {{.Values.Agent.Cpu}} + {{.Values.Agent.Memory}} + {{.Values.Agent.Cpu}} + {{.Values.Agent.Memory}} + + + + +{{- if .Values.Agent.ImagePullSecret }} + + + {{ .Values.Agent.ImagePullSecret }} + + +{{- else }} + +{{- end }} + + +{{- end -}} + + https://kubernetes + false + {{ .Release.Namespace }} + http://{{ template "jenkins.fullname" . }}:{{.Values.Master.ServicePort}}{{ default "" .Values.Master.JenkinsUriPrefix }} + {{ template "jenkins.fullname" . }}-agent:50000 + 10 + 5 + 0 + 0 + + + 5 + 0 + + + + All + false + false + + + + All + 50000 + +{{- range .Values.Master.DisabledAgentProtocols }} + {{ . }} +{{- end }} + + +{{- if .Values.Master.CSRF.DefaultCrumbIssuer.Enabled }} + +{{- if .Values.Master.CSRF.DefaultCrumbIssuer.ProxyCompatability }} + true +{{- end }} + +{{- end }} + + + true + +{{- if .Values.Master.ScriptApproval }} + scriptapproval.xml: |- + + + + +{{- range $key, $val := .Values.Master.ScriptApproval }} + {{ $val }} +{{- end }} + + + + + + + +{{- end }} + jenkins.CLI.xml: |- + + +{{- if .Values.Master.CLI }} + true +{{- else }} + false +{{- end }} + + hudson.model.UpdateCenter.xml: |- + + + + default +{{- if .Values.Master.UpdateCenter }} + {{ .Values.Master.UpdateCenter }} +{{- else }} + https://updates.jenkins.io/update-center.json +{{- end }} + + + apply_config.sh: |- + mkdir -p /usr/share/jenkins/ref/secrets/; + echo "false" > /usr/share/jenkins/ref/secrets/slave-to-master-security-kill-switch; + cp -n /var/jenkins_config/config.xml /var/jenkins_home; + cp -n /var/jenkins_config/jenkins.CLI.xml /var/jenkins_home; + cp -n /var/jenkins_config/hudson.model.UpdateCenter.xml /var/jenkins_home; +{{- if .Values.Master.InstallPlugins }} + # Install missing plugins + cp /var/jenkins_config/plugins.txt /var/jenkins_home; + rm -rf /usr/share/jenkins/ref/plugins/*.lock + /usr/local/bin/install-plugins.sh `echo $(cat /var/jenkins_home/plugins.txt)`; + # Copy plugins to shared volume + cp -n /usr/share/jenkins/ref/plugins/* /var/jenkins_plugins; +{{- end }} +{{- if .Values.Master.ScriptApproval }} + cp -n /var/jenkins_config/scriptapproval.xml /var/jenkins_home/scriptApproval.xml; +{{- end }} +{{- if .Values.Master.InitScripts }} + mkdir -p /var/jenkins_home/init.groovy.d/; + cp -n /var/jenkins_config/*.groovy /var/jenkins_home/init.groovy.d/ +{{- end }} +{{- if .Values.Master.CredentialsXmlSecret }} + cp -n /var/jenkins_credentials/credentials.xml /var/jenkins_home; +{{- end }} +{{- if .Values.Master.SecretsFilesSecret }} + cp -n /var/jenkins_secrets/* /usr/share/jenkins/ref/secrets; +{{- end }} +{{- if .Values.Master.Jobs }} + for job in $(ls /var/jenkins_jobs); do + mkdir -p /var/jenkins_home/jobs/$job + cp -n /var/jenkins_jobs/$job /var/jenkins_home/jobs/$job/config.xml + done +{{- end }} +{{- range $key, $val := .Values.Master.InitScripts }} + init{{ $key }}.groovy: |- +{{ $val | indent 4 }} +{{- end }} + plugins.txt: |- +{{- if .Values.Master.InstallPlugins }} +{{- range $index, $val := .Values.Master.InstallPlugins }} +{{ $val | indent 4 }} +{{- end }} +{{- end }} +{{ else }} +{{ include "override_config_map" . }} +{{- end -}} diff --git a/manifests/jenkins/templates/home-pvc.yaml b/manifests/jenkins/templates/home-pvc.yaml new file mode 100644 index 000000000..d6e44f2f2 --- /dev/null +++ b/manifests/jenkins/templates/home-pvc.yaml @@ -0,0 +1,28 @@ +{{- if and .Values.Persistence.Enabled (not .Values.Persistence.ExistingClaim) -}} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: +{{- if .Values.Persistence.Annotations }} + annotations: +{{ toYaml .Values.Persistence.Annotations | indent 4 }} +{{- end }} + name: {{ template "jenkins.fullname" . }} + labels: + app: {{ template "jenkins.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +spec: + accessModes: + - {{ .Values.Persistence.AccessMode | quote }} + resources: + requests: + storage: {{ .Values.Persistence.Size | quote }} +{{- if .Values.Persistence.StorageClass }} +{{- if (eq "-" .Values.Persistence.StorageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.Persistence.StorageClass }}" +{{- end }} +{{- end }} +{{- end }} diff --git a/manifests/jenkins/templates/jenkins-agent-svc.yaml b/manifests/jenkins/templates/jenkins-agent-svc.yaml new file mode 100644 index 000000000..a9ad63ded --- /dev/null +++ b/manifests/jenkins/templates/jenkins-agent-svc.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "jenkins.fullname" . }}-agent + labels: + app: {{ template "jenkins.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" +{{- if .Values.Master.SlaveListenerServiceAnnotations }} + annotations: +{{ toYaml .Values.Master.SlaveListenerServiceAnnotations | indent 4 }} +{{- end }} +spec: + ports: + - port: {{ .Values.Master.SlaveListenerPort }} + targetPort: {{ .Values.Master.SlaveListenerPort }} + name: slavelistener + selector: + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + type: {{ .Values.Master.SlaveListenerServiceType }} diff --git a/manifests/jenkins/templates/jenkins-master-deployment.yaml b/manifests/jenkins/templates/jenkins-master-deployment.yaml new file mode 100644 index 000000000..798f415f4 --- /dev/null +++ b/manifests/jenkins/templates/jenkins-master-deployment.yaml @@ -0,0 +1,222 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "jenkins.fullname" . }} + labels: + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + component: "{{ .Release.Name }}-{{ .Values.Master.Name }}" +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + template: + metadata: + labels: + app: {{ template "jenkins.fullname" . }} + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + annotations: + checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }} + spec: + {{- if .Values.Master.NodeSelector }} + nodeSelector: +{{ toYaml .Values.Master.NodeSelector | indent 8 }} + {{- end }} + {{- if .Values.Master.Tolerations }} + tolerations: +{{ toYaml .Values.Master.Tolerations | indent 8 }} + {{- end }} + {{- if .Values.Master.Affinity }} + affinity: +{{ toYaml .Values.Master.Affinity | indent 8 }} + {{- end }} + securityContext: + runAsUser: {{ default 0 .Values.Master.RunAsUser }} +{{- if and (.Values.Master.RunAsUser) (.Values.Master.FsGroup) }} +{{- if not (eq .Values.Master.RunAsUser 0.0) }} + fsGroup: {{ .Values.Master.FsGroup }} +{{- end }} +{{- end }} + serviceAccountName: {{ if .Values.rbac.install }}{{ template "jenkins.fullname" . }}{{ else }}"{{ .Values.rbac.serviceAccountName }}"{{ end }} + initContainers: + - name: "copy-default-config" + image: "{{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }}" + imagePullPolicy: "{{ .Values.Master.ImagePullPolicy }}" + command: [ "sh", "/var/jenkins_config/apply_config.sh" ] + {{- if .Values.Master.InitContainerEnv }} + env: +{{ toYaml .Values.Master.InitContainerEnv | indent 12 }} + {{- end }} + volumeMounts: + - + mountPath: /var/jenkins_home + name: jenkins-home + - + mountPath: /var/jenkins_config + name: jenkins-config + {{- if .Values.Master.CredentialsXmlSecret }} + - + mountPath: /var/jenkins_credentials + name: jenkins-credentials + readOnly: true + {{- end }} + {{- if .Values.Master.SecretsFilesSecret }} + - + mountPath: /var/jenkins_secrets + name: jenkins-secrets + readOnly: true + {{- end }} + {{- if .Values.Master.Jobs }} + - + mountPath: /var/jenkins_jobs + name: jenkins-jobs + readOnly: true + {{- end }} + {{- if .Values.Master.InstallPlugins }} + - + mountPath: /var/jenkins_plugins + name: plugin-dir + {{- end }} + - + mountPath: /usr/share/jenkins/ref/secrets/ + name: secrets-dir + containers: + - name: {{ template "jenkins.fullname" . }} + image: "{{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }}" + imagePullPolicy: "{{ .Values.Master.ImagePullPolicy }}" + {{- if .Values.Master.UseSecurity }} + args: [ "--argumentsRealm.passwd.$(ADMIN_USER)=$(ADMIN_PASSWORD)", "--argumentsRealm.roles.$(ADMIN_USER)=admin"] + {{- end }} + env: + - name: JAVA_OPTS + value: "{{ default "" .Values.Master.JavaOpts}}" + - name: JENKINS_OPTS + value: "{{ if .Values.Master.JenkinsUriPrefix }}--prefix={{ .Values.Master.JenkinsUriPrefix }} {{ end }}{{ default "" .Values.Master.JenkinsOpts}}" + {{- if .Values.Master.UseSecurity }} + - name: ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "jenkins.fullname" . }} + key: jenkins-admin-password + - name: ADMIN_USER + valueFrom: + secretKeyRef: + name: {{ template "jenkins.fullname" . }} + key: jenkins-admin-user + {{- end }} + {{- if .Values.Master.ContainerEnv }} +{{ toYaml .Values.Master.ContainerEnv | indent 12 }} + {{- end }} + ports: + - containerPort: {{ .Values.Master.ContainerPort }} + name: http + - containerPort: {{ .Values.Master.SlaveListenerPort }} + name: slavelistener + {{- if .Values.Master.JMXPort }} + - containerPort: {{ .Values.Master.JMXPort }} + name: jmx + {{- end }} +{{- if .Values.Master.HealthProbes }} + livenessProbe: + httpGet: + path: /login + port: http + initialDelaySeconds: {{ .Values.Master.HealthProbesTimeout }} + timeoutSeconds: 5 + failureThreshold: {{ .Values.Master.HealthProbeLivenessFailureThreshold }} + readinessProbe: + httpGet: + path: /login + port: http + initialDelaySeconds: {{ .Values.Master.HealthProbesTimeout }} +{{- end }} + resources: + requests: + cpu: "{{ .Values.Master.Cpu }}" + memory: "{{ .Values.Master.Memory }}" + volumeMounts: +{{- if .Values.Persistence.mounts }} +{{ toYaml .Values.Persistence.mounts | indent 12 }} +{{- end }} + - + mountPath: /var/jenkins_home + name: jenkins-home + readOnly: false + - + mountPath: /var/jenkins_config + name: jenkins-config + readOnly: true + {{- if .Values.Master.CredentialsXmlSecret }} + - + mountPath: /var/jenkins_credentials + name: jenkins-credentials + readOnly: true + {{- end }} + {{- if .Values.Master.SecretsFilesSecret }} + - + mountPath: /var/jenkins_secrets + name: jenkins-secrets + readOnly: true + {{- end }} + {{- if .Values.Master.Jobs }} + - + mountPath: /var/jenkins_jobs + name: jenkins-jobs + readOnly: true + {{- end }} + {{- if .Values.Master.InstallPlugins }} + - + mountPath: /usr/share/jenkins/ref/plugins/ + name: plugin-dir + readOnly: false + {{- end }} + - + mountPath: /usr/share/jenkins/ref/secrets/ + name: secrets-dir + readOnly: false + volumes: +{{- if .Values.Persistence.volumes }} +{{ toYaml .Values.Persistence.volumes | indent 6 }} +{{- end }} + - name: jenkins-config + configMap: + name: {{ template "jenkins.fullname" . }} + {{- if .Values.Master.CredentialsXmlSecret }} + - name: jenkins-credentials + secret: + secretName: {{ .Values.Master.CredentialsXmlSecret }} + {{- end }} + {{- if .Values.Master.SecretsFilesSecret }} + - name: jenkins-secrets + secret: + secretName: {{ .Values.Master.SecretsFilesSecret }} + {{- end }} + {{- if .Values.Master.Jobs }} + - name: jenkins-jobs + configMap: + name: {{ template "jenkins.fullname" . }}-jobs + {{- end }} + {{- if .Values.Master.InstallPlugins }} + - name: plugin-dir + emptyDir: {} + {{- end }} + - name: secrets-dir + emptyDir: {} + - name: jenkins-home + {{- if .Values.Persistence.Enabled }} + persistentVolumeClaim: + claimName: {{ .Values.Persistence.ExistingClaim | default (include "jenkins.fullname" .) }} + {{- else }} + emptyDir: {} + {{- end -}} +{{- if .Values.Master.ImagePullSecret }} + imagePullSecrets: + - name: {{ .Values.Master.ImagePullSecret }} +{{- end -}} diff --git a/manifests/jenkins/templates/jenkins-master-ingress.yaml b/manifests/jenkins/templates/jenkins-master-ingress.yaml new file mode 100644 index 000000000..68fd535fe --- /dev/null +++ b/manifests/jenkins/templates/jenkins-master-ingress.yaml @@ -0,0 +1,22 @@ +{{- if .Values.Master.HostName }} +apiVersion: {{ .Values.Master.Ingress.ApiVersion }} +kind: Ingress +metadata: +{{- if .Values.Master.Ingress.Annotations }} + annotations: +{{ toYaml .Values.Master.Ingress.Annotations | indent 4 }} +{{- end }} + name: {{ template "jenkins.fullname" . }} +spec: + rules: + - host: {{ .Values.Master.HostName | quote }} + http: + paths: + - backend: + serviceName: {{ template "jenkins.fullname" . }} + servicePort: {{ .Values.Master.ServicePort }} +{{- if .Values.Master.Ingress.TLS }} + tls: +{{ toYaml .Values.Master.Ingress.TLS | indent 4 }} +{{- end -}} +{{- end }} diff --git a/manifests/jenkins/templates/jenkins-master-networkpolicy.yaml b/manifests/jenkins/templates/jenkins-master-networkpolicy.yaml new file mode 100644 index 000000000..6034c919f --- /dev/null +++ b/manifests/jenkins/templates/jenkins-master-networkpolicy.yaml @@ -0,0 +1,33 @@ +{{- if .Values.NetworkPolicy.Enabled }} +kind: NetworkPolicy +apiVersion: {{ .Values.NetworkPolicy.ApiVersion }} +metadata: + name: "{{ .Release.Name }}-{{ .Values.Master.Component }}" +spec: + podSelector: + matchLabels: + component: "{{ .Release.Name }}-{{ .Values.Master.Component }}" + ingress: + # Allow web access to the UI + - ports: + - port: {{ .Values.Master.ContainerPort }} + # Allow inbound connections from slave + - from: + - podSelector: + matchLabels: + "jenkins/{{ .Release.Name }}-{{ .Values.Agent.Component }}": "true" + ports: + - port: {{ .Values.Master.SlaveListenerPort }} +{{- if .Values.Agent.Enabled }} +--- +kind: NetworkPolicy +apiVersion: {{ .Values.NetworkPolicy.ApiVersion }} +metadata: + name: "{{ .Release.Name }}-{{ .Values.Agent.Component }}" +spec: + podSelector: + matchLabels: + # DefaultDeny + "jenkins/{{ .Release.Name }}-{{ .Values.Agent.Component }}": "true" +{{- end }} +{{- end }} diff --git a/manifests/jenkins/templates/jenkins-master-svc.yaml b/manifests/jenkins/templates/jenkins-master-svc.yaml new file mode 100644 index 000000000..b028744b8 --- /dev/null +++ b/manifests/jenkins/templates/jenkins-master-svc.yaml @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{template "jenkins.fullname" . }} + labels: + app: {{ template "jenkins.fullname" . }} + heritage: {{.Release.Service | quote }} + release: {{.Release.Name | quote }} + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + component: "{{.Release.Name}}-{{.Values.Master.Component}}" +{{- if .Values.Master.ServiceAnnotations }} + annotations: +{{ toYaml .Values.Master.ServiceAnnotations | indent 4 }} +{{- end }} +spec: + ports: + - port: {{.Values.Master.ServicePort}} + name: http + targetPort: {{.Values.Master.ContainerPort}} + {{if (and (eq .Values.Master.ServiceType "NodePort") (not (empty .Values.Master.NodePort)))}} + nodePort: {{.Values.Master.NodePort}} + {{end}} + selector: + component: "{{.Release.Name}}-{{.Values.Master.Component}}" + type: {{.Values.Master.ServiceType}} + {{if eq .Values.Master.ServiceType "LoadBalancer"}} + loadBalancerSourceRanges: {{.Values.Master.LoadBalancerSourceRanges}} + {{if .Values.Master.LoadBalancerIP}} + loadBalancerIP: {{.Values.Master.LoadBalancerIP}} + {{end}} + {{end}} diff --git a/manifests/jenkins/templates/jenkins-test.yaml b/manifests/jenkins/templates/jenkins-test.yaml new file mode 100644 index 000000000..73d061a4c --- /dev/null +++ b/manifests/jenkins/templates/jenkins-test.yaml @@ -0,0 +1,45 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ .Release.Name }}-ui-test-{{ randAlphaNum 5 | lower }}" + annotations: + "helm.sh/hook": test-success +spec: + {{- if .Values.Master.NodeSelector }} + nodeSelector: +{{ toYaml .Values.Master.NodeSelector | indent 4 }} + {{- end }} + {{- if .Values.Master.Tolerations }} + tolerations: +{{ toYaml .Values.Master.Tolerations | indent 4 }} + {{- end }} + initContainers: + - name: "test-framework" + image: "dduportal/bats:0.4.0" + command: + - "bash" + - "-c" + - | + set -ex + # copy bats to tools dir + cp -R /usr/local/libexec/ /tools/bats/ + volumeMounts: + - mountPath: /tools + name: tools + containers: + - name: {{ .Release.Name }}-ui-test + image: {{ .Values.Master.Image }}:{{ .Values.Master.ImageTag }} + command: ["/tools/bats/bats", "-t", "/tests/run.sh"] + volumeMounts: + - mountPath: /tests + name: tests + readOnly: true + - mountPath: /tools + name: tools + volumes: + - name: tests + configMap: + name: {{ template "jenkins.fullname" . }}-tests + - name: tools + emptyDir: {} + restartPolicy: Never diff --git a/manifests/jenkins/templates/jobs.yaml b/manifests/jenkins/templates/jobs.yaml new file mode 100644 index 000000000..c16813a7b --- /dev/null +++ b/manifests/jenkins/templates/jobs.yaml @@ -0,0 +1,8 @@ +{{- if .Values.Master.Jobs }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }}-jobs +data: +{{ .Values.Master.Jobs | indent 2 }} +{{- end -}} diff --git a/manifests/jenkins/templates/rbac.yaml b/manifests/jenkins/templates/rbac.yaml new file mode 100644 index 000000000..5a828aa76 --- /dev/null +++ b/manifests/jenkins/templates/rbac.yaml @@ -0,0 +1,20 @@ +{{ if .Values.rbac.install }} +{{- $serviceName := include "jenkins.fullname" . -}} +apiVersion: rbac.authorization.k8s.io/{{ required "A valid .Values.rbac.apiVersion entry required!" .Values.rbac.apiVersion }} +kind: ClusterRoleBinding +metadata: + name: {{ $serviceName }}-role-binding + labels: + app: {{ $serviceName }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Values.rbac.roleRef }} +subjects: +- kind: ServiceAccount + name: {{ $serviceName }} + namespace: {{ .Release.Namespace }} +{{ end }} \ No newline at end of file diff --git a/manifests/jenkins/templates/secret.yaml b/manifests/jenkins/templates/secret.yaml new file mode 100644 index 000000000..47cc2e056 --- /dev/null +++ b/manifests/jenkins/templates/secret.yaml @@ -0,0 +1,19 @@ +{{- if .Values.Master.UseSecurity }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "jenkins.fullname" . }} + labels: + app: {{ template "jenkins.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: + {{ if .Values.Master.AdminPassword }} + jenkins-admin-password: {{ .Values.Master.AdminPassword | b64enc | quote }} + {{ else }} + jenkins-admin-password: {{ randAlphaNum 10 | b64enc | quote }} + {{ end }} + jenkins-admin-user: {{ .Values.Master.AdminUser | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/manifests/jenkins/templates/service-account.yaml b/manifests/jenkins/templates/service-account.yaml new file mode 100644 index 000000000..cb0911cb5 --- /dev/null +++ b/manifests/jenkins/templates/service-account.yaml @@ -0,0 +1,12 @@ +{{ if .Values.rbac.install }} +{{- $serviceName := include "jenkins.fullname" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ $serviceName }} + labels: + app: {{ $serviceName }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +{{ end }} \ No newline at end of file diff --git a/manifests/jenkins/templates/test-config.yaml b/manifests/jenkins/templates/test-config.yaml new file mode 100644 index 000000000..00b8a66a1 --- /dev/null +++ b/manifests/jenkins/templates/test-config.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "jenkins.fullname" . }}-tests +data: + run.sh: |- + @test "Testing Jenkins UI is accessible" { + curl --retry 48 --retry-delay 10 {{ template "jenkins.fullname" . }}:{{ .Values.Master.ServicePort }}{{ default "" .Values.Master.JenkinsUriPrefix }}/login + } diff --git a/manifests/jenkins/values.yaml b/manifests/jenkins/values.yaml new file mode 100644 index 000000000..c2c6c6611 --- /dev/null +++ b/manifests/jenkins/values.yaml @@ -0,0 +1,194 @@ +# Default values for jenkins. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value + +## Overrides for generated resource names +# See templates/_helpers.tpl +# nameOverride: +# fullnameOverride: + +Master: + Name: jenkins-master + Image: "jenkins/jenkins" + ImageTag: "lts" + ImagePullPolicy: "IfNotPresent" +# ImagePullSecret: jenkins + Component: "jenkins-master" + UseSecurity: true + AdminUser: admin + AdminPassword: admin + Cpu: "200m" + Memory: "512Mi" + # Environment variables that get added to the init container (useful for e.g. http_proxy) + # InitContainerEnv: + # - name: http_proxy + # value: "http://192.168.64.1:3128" + # ContainerEnv: + # - name: http_proxy + # value: "http://192.168.64.1:3128" + # Set min/max heap here if needed with: + # JavaOpts: "-Xms512m -Xmx512m" + # JenkinsOpts: "" + # JenkinsUriPrefix: "/jenkins" + # Set RunAsUser to 1000 to let Jenkins run as non-root user 'jenkins' which exists in 'jenkins/jenkins' docker image. + # When setting RunAsUser to a different value than 0 also set FsGroup to the same value: + # RunAsUser: + # FsGroup: + ServicePort: 8080 + # For minikube, set this to NodePort, elsewhere use LoadBalancer + # Use ClusterIP if your setup includes ingress controller + ServiceType: ClusterIP + # Master Service annotations + ServiceAnnotations: {} + # service.beta.kubernetes.io/aws-load-balancer-backend-protocol: https + # Used to create Ingress record (should used with ServiceType: ClusterIP) + HostName: jenkins.local.com + # NodePort: + -Djava.awt.headless=true + -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai + -Dfile.encoding=UTF-8 + # -Dcom.sun.management.jmxremote.port=4000 + # -Dcom.sun.management.jmxremote.authenticate=false + # -Dcom.sun.management.jmxremote.ssl=false + # JMXPort: 4000 + + # 插件镜像地址 + UpdateCenter: https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/current/update-center.json + + # List of plugins to be install during Jenkins master start + InstallPlugins: + - kubernetes:1.6.3 + - workflow-aggregator:2.5 + - workflow-job:2.21 + - credentials-binding:1.16 + - git:3.9.0 + - gitlab:1.5.6 + # Used to approve a list of groovy functions in pipelines used the script-security plugin. Can be viewed under /scriptApproval + # ScriptApproval: + # - "method groovy.json.JsonSlurperClassic parseText java.lang.String" + # - "new groovy.json.JsonSlurperClassic" + # List of groovy init scripts to be executed during Jenkins master start + InitScripts: + # - | + # print 'adding global pipeline libraries, register properties, bootstrap jobs...' + # Kubernetes secret that contains a 'credentials.xml' for Jenkins + # CredentialsXmlSecret: jenkins-credentials + # Kubernetes secret that contains files to be put in the Jenkins 'secrets' directory, + # useful to manage encryption keys used for credentials.xml for instance (such as + # master.key and hudson.util.Secret) + # SecretsFilesSecret: jenkins-secrets + # Jenkins XML job configs to provision + # Jobs: |- + # test: |- + # <> + CustomConfigMap: false + # Node labels and tolerations for pod assignment + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + # ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + NodeSelector: {} + + Tolerations: {} + + Ingress: + ApiVersion: extensions/v1beta1 + Annotations: + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + + TLS: + # - secretName: jenkins.cluster.local + # hosts: + # - jenkins.cluster.local + +Agent: + Enabled: true + Image: jenkins/jnlp-slave + ImageTag: alpine +# ImagePullSecret: jenkins + Component: "jenkins-slave" + Privileged: false + Cpu: "200m" + Memory: "256Mi" + # You may want to change this to true while testing a new image + AlwaysPullImage: false + # You can define the volumes that you want to mount for this container + # Allowed types are: ConfigMap, EmptyDir, HostPath, Nfs, Pod, Secret + # Configure the attributes as they appear in the corresponding Java class for that type + # https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/main/java/org/csanchez/jenkins/plugins/kubernetes/volumes + volumes: + # - type: Secret + # secretName: mysecret + # mountPath: /var/myapp/mysecret + NodeSelector: {} + # Key Value selectors. Ex: + # jenkins-agent: v1 + +Persistence: + Enabled: true + ## A manually managed Persistent Volume and Claim + ## Requires Persistence.Enabled: true + ## If defined, PVC must be created manually before volume will be bound + # ExistingClaim: + + ## jenkins data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + StorageClass: "nfs-dynamic-class" + + Annotations: {} + AccessMode: ReadWriteOnce + Size: 8Gi + volumes: + # - name: nothing + # emptyDir: {} + mounts: + # - mountPath: /var/nothing + # name: nothing + # readOnly: true + +NetworkPolicy: + # Enable creation of NetworkPolicy resources. + Enabled: false + # For Kubernetes v1.4, v1.5 and v1.6, use 'extensions/v1beta1' + # For Kubernetes v1.7, use 'networking.k8s.io/v1' + ApiVersion: extensions/v1beta1 + +## Install Default RBAC roles and bindings +rbac: + install: true + serviceAccountName: default + # RBAC api version (currently either v1beta1 or v1alpha1 or v1) + apiVersion: v1 + # Cluster role reference + roleRef: cluster-admin