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。