k8s 中安装 jenkins

这个系列是描述如何在 kubernetes 环境下创建 jenkins 动态 slave,那什么是 slave,又为什么要动态创建呢?首先,大家都使用 jenkins 进行构建以及发布等操作,但是一旦构建任务多了,一台 jenkins 可能忙不过来了。此时你就可以创建 slave,将一部分 job 放在 slave 上构建,达到减轻 master 压力的目的。

但是当构建任务实在太多时,你可能要创建多个 slave 才能满足需要。问题是可能很多构建任务只是集中在固定的时刻,多数时候 slave 都处于空闲状态,那么这时 slave 的资源就处于浪费状态了。

能不能 slave 在构建任务的时候才创建,构建完成之后自动删除呢?幸运的是,kubernetes 刚好能够做到这一点。这个系列的文章就是要实现这一功能,也就是所谓的动态 slave。

使用动态 slave 之后,所有的构建任务都不会在 master 上进行,你也不用为 job 分配 slave,而是只要有构建任务,kubernetes 就会为这个构建任务创建一个 slave,构建完成之后自动删除。

听起来不错对吗?那我们就开始吧。我们首先得安装 jenkins,在这之前你得确保你拥有一个 kubernetes 集群,因为 jenkins 只能安装在 kubernetes 集群中,如果没有的话就没有办法了。

jenkins 的安装可以使用 helm,也可以手动装。因为要挂载存储,手动装时只能使用 statefulset,而不能使用 deployment。

在这之前,我们要提供一个 nfs 用于存放 jenkins 数据。当然,如果你拥有其他的共享存储也行,nfs 只是一种最简单,最廉价的实现。

搭建 nfs

使用 nfs 最大的问题就是写权限,你可以 kubernetes 的 securityContext/runAsUser 指定 jenkins 容器中运行 jenkins 的用户 uid,以此来指定 nfs 目录的权限,让 jenkins 容器可写;也可以不限制,让所有用户都可以写。这里为了简单,就让所有用户可写了。

找一台主机,安装 nfs:

# yum install nfs-utils
# systemctl start nfs
# systemctl enable nfs
复制代码

创建共享目录,然后暴露出去:

# 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。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值