k8s 中安裝 jenkins

這個系列是描述如何在 kubernetes 環境下建立 jenkins 動態 slave,那什麼是 slave,又爲何要動態建立呢?首先,你們都使用 jenkins 進行構建以及發佈等操做,可是一旦構建任務多了,一臺 jenkins 可能忙不過來了。此時你就能夠建立 slave,將一部分 job 放在 slave 上構建,達到減輕 master 壓力的目的。node

可是當構建任務實在太多時,你可能要建立多個 slave 才能知足須要。問題是可能不少構建任務只是集中在固定的時刻,多數時候 slave 都處於空閒狀態,那麼這時 slave 的資源就處於浪費狀態了。nginx

能不能 slave 在構建任務的時候才建立,構建完成以後自動刪除呢?幸運的是,kubernetes 恰好可以作到這一點。這個系列的文章就是要實現這一功能,也就是所謂的動態 slave。web

使用動態 slave 以後,全部的構建任務都不會在 master 上進行,你也不用爲 job 分配 slave,而是隻要有構建任務,kubernetes 就會爲這個構建任務建立一個 slave,構建完成以後自動刪除。docker

聽起來不錯對嗎?那咱們就開始吧。咱們首先得安裝 jenkins,在這以前你得確保你擁有一個 kubernetes 集羣,由於 jenkins 只能安裝在 kubernetes 集羣中,若是沒有的話就沒有辦法了。vim

jenkins 的安裝可使用 helm,也能夠手動裝。由於要掛載存儲,手動裝時只能使用 statefulset,而不能使用 deployment。api

在這以前,咱們要提供一個 nfs 用於存放 jenkins 數據。固然,若是你擁有其餘的共享存儲也行,nfs 只是一種最簡單,最廉價的實現。瀏覽器

搭建 nfs

使用 nfs 最大的問題就是寫權限,你能夠 kubernetes 的 securityContext/runAsUser 指定 jenkins 容器中運行 jenkins 的用戶 uid,以此來指定 nfs 目錄的權限,讓 jenkins 容器可寫;也能夠不限制,讓全部用戶均可以寫。這裏爲了簡單,就讓全部用戶可寫了。markdown

找一臺主機,安裝 nfs:app

# yum install nfs-utils
# systemctl start nfs
# systemctl enable nfs
複製代碼

建立共享目錄,而後暴露出去:oop

# mkdir -p /opt/nfs/jenkins-data
# vim /etc/exports
/opt/nfs/jenkins-data 10.20.3.0/24(rw,all_squash)
複製代碼

這裏的 ip 使用 kubernetes node 節點的 ip 範圍,後面的 all_squash 選項會將全部訪問的用戶都映射成 nfsnobody 用戶,無論你是什麼用戶訪問,最終都會壓縮成 nfsnobody,因此你只要將 /opt/nfs/jenkins-data 的屬主改成 nfsnobody,那麼不管什麼用戶來訪問都具備寫權限。

這個選項在不少機器上因爲用戶 uid 不規範致使啓動進程的用戶不一樣,可是同時要對一個共享目錄具備寫權限時頗有效。

chown -R nfsnobody. /opt/nfs/jenkins-data/
systemctl reload nfs
複製代碼

而後在任意一個 node 節點上進行驗證:

# showmount -e NFS_IP
複製代碼

若是可以看到 /opt/nfs/jenkins-data 就表示 ok 了。

建立 pv

jenkins 其實只要加載對應的目錄就能夠讀取以前的數據,可是因爲 deployment 沒法定義存儲卷,所以咱們只能使用 StatefulSet。

首先建立 pv,pv 是給 StatefulSet 使用的,每次 StatefulSet 啓動都會經過 volumeClaimTemplates 這個模板去建立 pvc,所以你必須得有 pv,才能供 pvc 綁定。

# cd /usr/local/kuberneters/manifests/jenkins
# vim pv.yml
---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: jenkins
spec:
 nfs:
 path: /opt/nfs/jenkins-data
 server: 10.20.5.1
 accessModes: ["ReadWriteOnce"]
 capacity:
 storage: 1Ti
複製代碼

我這裏給了 1t,你看着辦。

建立 serviceAccount

接着是 service account,由於 jenkins 後面須要可以動態建立 slave,所以它必須具有一些權限。

# vim service-account.yml
---
apiVersion: v1
kind: ServiceAccount
metadata:
 name: jenkins

---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
 name: jenkins
rules:
 - apiGroups: [""]
 resources: ["pods"]
 verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
 - apiGroups: [""]
 resources: ["pods/exec"]
 verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
 - apiGroups: [""]
 resources: ["pods/log"]
 verbs: ["get", "list", "watch"]
 - apiGroups: [""]
 resources: ["secrets"]
 verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
 name: jenkins
roleRef:
 apiGroup: rbac.authorization.k8s.io
 kind: Role
 name: jenkins
subjects:
 - kind: ServiceAccount
 name: jenkins
複製代碼

建立了一個 RoleBinding 和一個 ServiceAccount,而且將 RoleBinding 的權限綁定到這個用戶上。因此,jenkins 容器必須使用這個 ServiceAccount 運行才行,否則 RoleBinding 的權限它將不具有。

RoleBinding 的權限很容易就看懂了,由於 jenkins 須要建立和刪除 slave,因此才須要上面這些權限。至於 secrets 權限,則是 https 證書。

部署 jenkins

我這裏使用了私有的 registry 來下載公網的 jenkins 鏡像,我以前的文章介紹了它的實現,有興趣能夠看看。由於私有鏡像須要認證,因此這裏也提供了 nexus-pull 這個 secret 進行認證,它的建立很簡單。

kubectl create secret docker-registry nexus-pull --docker-username=admin --docker-password="admin123" --docker-server="registry.ntpstat.com:2222"
複製代碼

jenkins 部署時須要注意它的副本數,你的副本數有多少就要有多少個 pv,一樣,存儲會有多倍消耗。這裏我只使用了一個副本,所以前面也只建立了一個 pv。

# vim statefulset.yml
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
 name: jenkins
 labels:
 name: jenkins
spec:
 serviceName: jenkins
 replicas: 1
 updateStrategy:
 type: RollingUpdate
 template:
 metadata:
 name: jenkins
 labels:
 name: jenkins
 spec:
 terminationGracePeriodSeconds: 10
 serviceAccountName: jenkins
 imagePullSecrets:
 - name: nexus-pull
 containers:
 - name: jenkins
 image: registry.ntpstat.com:2222/jenkins/jenkins:lts
          # 時刻保持鏡像最新
 imagePullPolicy: Always
 ports:
 - containerPort: 8080
 - containerPort: 50000
 resources:
 limits:
 cpu: 4
 memory: 4Gi
 requests:
 cpu: 4
 memory: 4Gi
 env:
 - name: LIMITS_MEMORY
 valueFrom:
 resourceFieldRef:
 resource: limits.memory
 divisor: 1Mi
 - name: JAVA_OPTS
              # value: -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
 value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
 volumeMounts:
 - name: jenkins-home
 mountPath: /var/jenkins_home
 livenessProbe:
 httpGet:
 path: /login
 port: 8080
 initialDelaySeconds: 60
 timeoutSeconds: 5
 failureThreshold: 12 # ~2 minutes
 readinessProbe:
 httpGet:
 path: /login
 port: 8080
 initialDelaySeconds: 60
 timeoutSeconds: 5
 failureThreshold: 12 # ~2 minutes
  # pvc 模板,對應以前的 pv
 volumeClaimTemplates:
 - metadata:
 name: jenkins-home
 spec:
 accessModes: ["ReadWriteOnce"]
 resources:
 requests:
 storage: 1Ti
複製代碼

建立 service

爲 ingress 建立 service。

# vim service.yml
---
apiVersion: v1
kind: Service
metadata:
 name: jenkins
spec:
  # type: LoadBalancer
 selector:
 name: jenkins
  # ensure the client ip is propagated to avoid the invalid crumb issue when using LoadBalancer (k8s >=1.7)
  #externalTrafficPolicy: Local
 ports:
 - name: http
 port: 80
 targetPort: 8080
 protocol: TCP
 - name: agent
 port: 50000
 protocol: TCP
複製代碼

安裝 ingress

jenkins 的 web 界面須要從集羣外訪問,這裏咱們選擇的是使用 ingress,ingress controller 的實現有多種,我選擇 traefik。關於 traefik 的安裝和使用能夠查看我以前的文章

traefik 安裝好後,咱們須要定義 ingress,也就是所謂的 nginx 虛擬主機的定義。由於咱們已經有了證書,因此這裏一樣爲 jenkins 的訪問開啓 https。

# vim ingress.yml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: jenkins
spec:
 rules:
 - http:
 paths:
 - path: /
 backend:
 serviceName: jenkins
 servicePort: 80
 host: jenkins.ntpstat.com
 tls:
 - hosts:
 - jenkins.ntpstat.com
 secretName: ntpstat.com
複製代碼

此時你的目錄下一共存在 5 個文件,能夠直接一條命令直接部署:

kubectl apply -f .
複製代碼

經過 kubectl get pods 查看是否運行成功,一旦出錯,能夠經過 kubectl describe/log 來查看哪裏出現問題。

訪問 jenkins

安裝成功後,你就能夠直接訪問了。這裏要注意你的 traefik 安裝在了哪些節點上,若是像我同樣只安裝在了某些節點而不是全部節點的話,你的 jenkins 的域名應該解析到 traefik 所在的節點。全部 dns 記錄的直接寫 hosts,可是最好仍是使用 dns。

我這裏寫好 hosts 以後,直接在瀏覽器上訪問 jenkins.ntpstat.com 就 ok 了,它會直接跳轉到 https。

而後在 nfs 存儲的目錄下找到 secrets/initialAdminPassword,接着下載它推薦的插件便可。若是不能直接訪問外網下載,jenkins 能夠直接配置代理,關於代理的配置,我這篇文件講的很清楚。

好了,這篇文章就到這裏了,下篇就會提到如何實現動態 slave。