From a784b17138b5e21aa070da334a6c260797c3e34d Mon Sep 17 00:00:00 2001 From: Vic Iglesias Date: Mon, 12 Sep 2016 12:01:29 -0700 Subject: [PATCH] Jenkins (#61) * initial commit * Jenkins chart * Move jenkins to incubator * Update chart metadata * Optimize image layers * Update Jenkins readme * Use fullname template where possible * Templatize configmap name * Add icon to chart metadata * Add configurable JAVA_OPTS * Add NOTES.txt explaining next steps * Update to support namespaces * Update tests * Update test image --- incubator/jenkins/.helmignore | 21 +++++ incubator/jenkins/Chart.yaml | 13 +++ incubator/jenkins/README.md | 77 +++++++++++++++ incubator/jenkins/master-image/Dockerfile | 4 + incubator/jenkins/templates/NOTES.txt | 36 +++++++ incubator/jenkins/templates/_helpers.tpl | 16 ++++ incubator/jenkins/templates/config.yaml | 77 +++++++++++++++ incubator/jenkins/templates/home-pvc.yaml | 15 +++ .../templates/jenkins-master-deployment.yaml | 93 +++++++++++++++++++ .../jenkins/templates/jenkins-master-svc.yaml | 22 +++++ incubator/jenkins/templates/secret.yaml | 17 ++++ incubator/jenkins/values.yaml | 34 +++++++ test/Dockerfile | 6 +- test/e2e.sh | 2 +- test/publish-docker.sh | 2 +- 15 files changed, 430 insertions(+), 5 deletions(-) create mode 100644 incubator/jenkins/.helmignore create mode 100755 incubator/jenkins/Chart.yaml create mode 100644 incubator/jenkins/README.md create mode 100644 incubator/jenkins/master-image/Dockerfile create mode 100644 incubator/jenkins/templates/NOTES.txt create mode 100644 incubator/jenkins/templates/_helpers.tpl create mode 100644 incubator/jenkins/templates/config.yaml create mode 100644 incubator/jenkins/templates/home-pvc.yaml create mode 100644 incubator/jenkins/templates/jenkins-master-deployment.yaml create mode 100644 incubator/jenkins/templates/jenkins-master-svc.yaml create mode 100644 incubator/jenkins/templates/secret.yaml create mode 100644 incubator/jenkins/values.yaml diff --git a/incubator/jenkins/.helmignore b/incubator/jenkins/.helmignore new file mode 100644 index 0000000000..f0c1319444 --- /dev/null +++ b/incubator/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/incubator/jenkins/Chart.yaml b/incubator/jenkins/Chart.yaml new file mode 100755 index 0000000000..f03888fc8c --- /dev/null +++ b/incubator/jenkins/Chart.yaml @@ -0,0 +1,13 @@ +name: jenkins +home: https://jenkins.io/ +version: 0.1.0 +description: A Jenkins Helm chart for Kubernetes. +sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave +maintainers: + - name: Lachlan Evenson + email: lachlan@deis.com + - name: Vic Iglesias + email: viglesias@google.com +icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png \ No newline at end of file diff --git a/incubator/jenkins/README.md b/incubator/jenkins/README.md new file mode 100644 index 0000000000..a1eff8ba39 --- /dev/null +++ b/incubator/jenkins/README.md @@ -0,0 +1,77 @@ +# 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 + +## Get this chart + +Download the latest release of the chart from the [releases](../../../releases) page. + +Alternatively, clone the repo if you wish to use the development snapshot: + +```bash +$ git clone https://github.com/kubernetes/charts.git +``` + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```bash +$ helm install --name my-release jenkins-x.x.x.tgz +``` + +## Configuration + +The following tables lists the configurable parameters of the Jenkins chart and their default values. + +### Jenkins Master + +| Parameter | Description | Default | +|-----------------------|----------------------------------|----------------------------------------------------------| +| `Master.Name` | Jenkins master name | `jenkins-master` | +| `Master.Image` | Master image name | `gcr.io/kubernetes-charts-ci/jenkins-master-k8s` | +| `Master.ImageTag` | Master image tag | `v0.1.0` | +| `Master.ImagePullPolicy` | Master image pull policy | `Always` | +| `Master.Component` | k8s selector key | `jenkins-master` | +| `Master.Cpu` | Master requested cpu | `200m` | +| `Master.Memory` | Master requested memory | `256Mi` | +| `Master.ServicePort` | k8s service port | `8080` | +| `Master.ContainerPort`| Master listening port | `8080` | +| `Master.SlaveListenerPort`| Listening port for agents | `50000` | + +### Jenkins Agent + +| Parameter | Description | Default | +|-----------------------|----------------------------------|----------------------------------------------------------| +| `Agent.Image` | Agent image name | `jenkinsci/jnlp-slave` | +| `Agent.ImageTag` | Agent image tag | `2.52` | | +| `Agent.Cpu` | Agent requested cpu | `200m` | +| `Agent.Memory` | Agent requested memory | `256Mi` | + +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 jenkins-x.x.x.tgz +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Persistence + +The Jenkins image stores persistence under `/var/jenkins_home` path of the container. A Persistent Volume +Claim is used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. + +# Todo +* Enable Docker-in-Docker or Docker-on-Docker support on the Jenkins agents diff --git a/incubator/jenkins/master-image/Dockerfile b/incubator/jenkins/master-image/Dockerfile new file mode 100644 index 0000000000..5d31b9ab40 --- /dev/null +++ b/incubator/jenkins/master-image/Dockerfile @@ -0,0 +1,4 @@ +FROM jenkins:2.7.3 +RUN /usr/local/bin/install-plugins.sh kubernetes:0.8 workflow-aggregator:2.3 \ + && mkdir -p /usr/share/jenkins/ref/secrets/ \ + && echo "false" > /usr/share/jenkins/ref/secrets/slave-to-master-security-kill-switch \ No newline at end of file diff --git a/incubator/jenkins/templates/NOTES.txt b/incubator/jenkins/templates/NOTES.txt new file mode 100644 index 0000000000..06381dacd9 --- /dev/null +++ b/incubator/jenkins/templates/NOTES.txt @@ -0,0 +1,36 @@ +Getting Started: + +1. Get your '{{ .Values.Master.AdminUser }}' user password by running: + printf $(printf '\%o' `kubectl get secret --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath="{.data.jenkins-admin-password[*]}"`);echo + +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 "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 -w {{ template "fullname" . }}' **** + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + 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 "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 }} + +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 }} + diff --git a/incubator/jenkins/templates/_helpers.tpl b/incubator/jenkins/templates/_helpers.tpl new file mode 100644 index 0000000000..da95264bcc --- /dev/null +++ b/incubator/jenkins/templates/_helpers.tpl @@ -0,0 +1,16 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 24 -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 24 -}} +{{- end -}} \ No newline at end of file diff --git a/incubator/jenkins/templates/config.yaml b/incubator/jenkins/templates/config.yaml new file mode 100644 index 0000000000..3df28e59be --- /dev/null +++ b/incubator/jenkins/templates/config.yaml @@ -0,0 +1,77 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "fullname" . }} +data: + config.xml: |- + + + + 2.7.3 + 0 + NORMAL + true + + true + + + false + + ${JENKINS_HOME}/workspace/${ITEM_FULLNAME} + ${ITEM_ROOTDIR}/builds + + + + + + + default + + + default + {{ .Values.Agent.Image }}:{{ .Values.Agent.ImageTag }} + false + false + + + /home/jenkins + 2147483647 + + + {{.Values.Agent.Cpu}} + {{.Values.Agent.Memory}} + {{.Values.Agent.Cpu}} + {{.Values.Agent.Memory}} + + + + + + + https://kubernetes.default + false + {{ .Release.Namespace }} + http://{{ template "fullname" . }}:8080 + {{ template "fullname" . }}:50000 + 10 + 5 + + + 5 + 0 + + + + All + false + false + + + + All + 50000 + + + + true + \ No newline at end of file diff --git a/incubator/jenkins/templates/home-pvc.yaml b/incubator/jenkins/templates/home-pvc.yaml new file mode 100644 index 0000000000..aca548155e --- /dev/null +++ b/incubator/jenkins/templates/home-pvc.yaml @@ -0,0 +1,15 @@ +{{- if .Values.Persistence.Enabled }} +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ template "fullname" . }} + annotations: + volume.alpha.kubernetes.io/storage-class: {{ .Values.Persistence.StorageClass | quote }} +spec: + accessModes: + - {{ .Values.Persistence.AccessMode | quote }} + resources: + requests: + storage: {{ .Values.Persistence.Size | quote }} +{{- end }} \ No newline at end of file diff --git a/incubator/jenkins/templates/jenkins-master-deployment.yaml b/incubator/jenkins/templates/jenkins-master-deployment.yaml new file mode 100644 index 0000000000..24a6824d6e --- /dev/null +++ b/incubator/jenkins/templates/jenkins-master-deployment.yaml @@ -0,0 +1,93 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: {{ template "fullname" . }} + labels: + heritage: {{.Release.Service | quote }} + release: {{.Release.Name | quote }} + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + component: "{{.Release.Name}}-{{.Values.Master.Name}}" + annotations: + "helm.sh/created": {{.Release.Time.Seconds | quote }} +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + component: "{{.Release.Name}}-{{.Values.Master.Component}}" + template: + metadata: + labels: + heritage: {{.Release.Service | quote }} + release: {{.Release.Name | quote }} + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + component: "{{.Release.Name}}-{{.Values.Master.Component}}" + annotations: + pod.alpha.kubernetes.io/init-containers: '[ + { + "name": "copy-default-config", + "image": "{{.Values.Master.Image}}:{{.Values.Master.ImageTag}}", + "command": ["cp", "-n", "/var/jenkins_config/config.xml", "/var/jenkins_home"], + "volumeMounts": [ + { + "name": "jenkins-config", + "mountPath": "/var/jenkins_config" + }, + { + "name": "jenkins-home", + "mountPath": "/var/jenkins_home" + } + ] + } + ]' + spec: + securityContext: + runAsUser: 0 + containers: + - name: {{ template "fullname" . }} + image: "{{.Values.Master.Image}}:{{.Values.Master.ImageTag}}" + imagePullPolicy: "{{.Values.Master.ImagePullPolicy}}" + args: [ "--argumentsRealm.passwd.$(ADMIN_USER)=$(ADMIN_PASSWORD)", "--argumentsRealm.roles.$(ADMIN_USER)=admin"] + env: + - name: JAVA_OPTS + value: "{{ default "" .Values.Master.JavaOpts}}" + - name: ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "fullname" . }} + key: jenkins-admin-password + - name: ADMIN_USER + valueFrom: + secretKeyRef: + name: {{ template "fullname" . }} + key: jenkins-admin-user + ports: + - containerPort: {{.Values.Master.ContainerPort}} + name: http + - containerPort: {{.Values.Master.SlaveListenerPort}} + name: slavelistener + resources: + requests: + cpu: "{{.Values.Master.Cpu}}" + memory: "{{.Values.Master.Memory}}" + volumeMounts: + - + mountPath: /var/jenkins_home + name: jenkins-home + readOnly: false + - + mountPath: /var/jenkins_config + name: jenkins-config + readOnly: true + volumes: + - name: jenkins-config + configMap: + name: {{ template "fullname" . }} + - name: jenkins-home + {{- if .Values.Persistence.Enabled }} + persistentVolumeClaim: + claimName: {{ template "fullname" . }} + {{- else }} + emptyDir: {} + {{- end -}} \ No newline at end of file diff --git a/incubator/jenkins/templates/jenkins-master-svc.yaml b/incubator/jenkins/templates/jenkins-master-svc.yaml new file mode 100644 index 0000000000..6cf4c43429 --- /dev/null +++ b/incubator/jenkins/templates/jenkins-master-svc.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{template "fullname" . }} + labels: + heritage: {{.Release.Service | quote }} + release: {{.Release.Name | quote }} + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + component: "{{.Release.Name}}-{{.Values.Master.Component}}" + annotations: + "helm.sh/created": {{.Release.Time.Seconds | quote }} +spec: + ports: + - port: {{.Values.Master.ServicePort}} + name: http + targetPort: {{.Values.Master.ContainerPort}} + - port: {{.Values.Master.SlaveListenerPort}} + targetPort: {{.Values.Master.SlaveListenerPort}} + name: slavelistener + selector: + component: "{{.Release.Name}}-{{.Values.Master.Component}}" + type: {{.Values.Master.ServiceType}} diff --git a/incubator/jenkins/templates/secret.yaml b/incubator/jenkins/templates/secret.yaml new file mode 100644 index 0000000000..29f85c22a6 --- /dev/null +++ b/incubator/jenkins/templates/secret.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "fullname" . }} + labels: + app: {{ template "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 }} diff --git a/incubator/jenkins/values.yaml b/incubator/jenkins/values.yaml new file mode 100644 index 0000000000..207b34a52c --- /dev/null +++ b/incubator/jenkins/values.yaml @@ -0,0 +1,34 @@ +# Default values for jenkins. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value + +Master: + Name: jenkins-master + Image: "gcr.io/kubernetes-charts-ci/jenkins-master-k8s" + ImageTag: "v0.1.0" + ImagePullPolicy: "Always" + Component: "jenkins-master" + AdminUser: admin +# AdminPassword: + Cpu: "200m" + Memory: "256Mi" +# Set min/max heap here if needed with: +# JavaOpts: "-Xms512m -Xmx512m" + ServicePort: 8080 +# For minikube, set this to NodePort, elsewhere use LoadBalancer + ServiceType: LoadBalancer + ContainerPort: 8080 + SlaveListenerPort: 50000 + +Agent: + Image: jenkinsci/jnlp-slave + ImageTag: 2.52 + Cpu: "200m" + Memory: "256Mi" + +Persistence: + Enabled: true + StorageClass: generic + AccessMode: ReadWriteOnce + Size: 8Gi \ No newline at end of file diff --git a/test/Dockerfile b/test/Dockerfile index 5a63de032c..6b87951bc9 100644 --- a/test/Dockerfile +++ b/test/Dockerfile @@ -24,15 +24,15 @@ ENV CLOUDSDK_CORE_DISABLE_PROMPTS=1 \ RUN set -x \ && cd /opt \ && echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \ - && apt-get update -y \ + && apt-get update \ && apt-get install --no-install-recommends -y jq wget python git localepurge ca-certificates \ && wget -q https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-${GOOGLE_CLOUD_SDK_VERSION}-linux-x86_64.tar.gz \ - && tar xfv google-cloud-sdk-${GOOGLE_CLOUD_SDK_VERSION}-linux-x86_64.tar.gz \ + && tar zxfv google-cloud-sdk-${GOOGLE_CLOUD_SDK_VERSION}-linux-x86_64.tar.gz \ && ./google-cloud-sdk/install.sh \ && gcloud components install kubectl \ && gcloud config set project ${GOOGLE_PROJECT} RUN cd /opt && wget -q ${HELM_URL} \ - && tar xzfv helm-${HELM_VERSION}-linux-amd64.tar.gz \ + && tar xzfv helm-${HELM_VERSION}-linux-amd64.tar.gz RUN apt-get -y purge wget localepurge \ && apt-get clean \ && cd / \ diff --git a/test/e2e.sh b/test/e2e.sh index 03639b5b1b..5cbd9f2af2 100755 --- a/test/e2e.sh +++ b/test/e2e.sh @@ -18,7 +18,7 @@ set -o nounset set -o pipefail # TODO should we inject this. This is creating problems bumping the Docker version -IMAGE_VERSION="test-image:v1.7" +IMAGE_VERSION="test-image:v1.9" CHART_ROOT=${CHART_ROOT:-$(git rev-parse --show-toplevel)} IMAGE_NAME=${IMAGE_NAME:-"gcr.io/kubernetes-charts-ci/${IMAGE_VERSION}"} diff --git a/test/publish-docker.sh b/test/publish-docker.sh index 6bc6a03429..88895fbaf6 100755 --- a/test/publish-docker.sh +++ b/test/publish-docker.sh @@ -12,7 +12,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -VERSION=v1.7 +VERSION=v1.9 CONTAINER_NAME=test-image usage() {