kubernetes+Jenkins持续集成

320 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

在安装 Jenkins 之前,我们先装一个 kubernetes 的可视化管理工具,尽管 kubernetes 官方提供了 dashboard UI 界面,但是目前市面上的第三方 UI 库提供的界面更友好,功能更强大,下面我们要介绍的 Kuboard 就是一款比较优秀的工具,这是由 Jumpserver 团队开发的。

安装 Kuboard

在 k8s 上,很多软件都提供了一键式安装方式,前提是有网, Kuboard 也支持一键安装:

kubectl apply -f https://kuboard.cn/install-script/kuboard.yaml

如果没有网的话,可以找地方下载 kuboard.yaml 这个文件,然后再执行,当然里面的镜像也要提前下载好,导入内网。这里有一个不够好的地方就是 kuboard.yaml 默认使用的是 kube-system 命名空间,建议大家创建一个新的命名空间,修改文件内部使用的命名空间。

安装完后,默认使用 Service 对外提供服务,如果我们安装好了 Ingress,也可以使用 Ingress 对外提供服务,默认的服务端口是32567。

kubernetes+Jenkins持续集成

执行以下命令获取 Token:

echo $(kubectl -n kube-system get secret $(kubectl -n kube-system get secret | grep kuboard-user | awk '{print $1}') -o go-template='{{.data.token}}' | base64 -d)

注意里面的命名空间,如果我们自己修改了命名空间,这条命令里面也要修改一下

eyJhbGciOiJSUzI1NiIsImtpZCI6ImZVbXM5dkI4a0ZtZEtSeDhtcUlob2Exelh4cjNWVTgyTUpnMVFWSlk4cHMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrdWJvYXJkLXVzZXItdG9rZW4tbWN4c3MiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhzZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiM2FjZWFjNDYtNTVmOS00OTExLWFkZDYtZDA3NjgyY2UzYWJmIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmt1Ym9hcmQtdXNlciJ9.ZNTlqYZ2Wp-_ZX8eV7P56tlYOB-viMwZl9g04Hn8PcBPOVVqA5Udsf5xajVjXX-v1x0Gek3hQxZc1ad0MvbFLnQdKeRpbSmbOqg1JPFjB_PybxlaIh15MVrXaEYnociM3YchCE32DpKziHFovJDN2t7mOUlgT5KuJpXydGwuyHrfwbaqRSmOM_spnEonEJrhHh0-1yl48QIARFjHqSOZ868rtm913V9xYPfjJoTZH0yhuSEwwwgsDZABOhw7Lm0lc-Y52I4kWszE1CVkdkTIjAszT_Ir7RCh9WChrz3jsRucJMs9o_RNbZkr3lRQujo4WQpOMNmliL26fSn69qRAqQ

输入 Token 以后登录

kubernetes+Jenkins持续集成

k8s 部署 Jenkins

apiVersion: v1
kind: Namespace
metadata:
  name: jenkins
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins
  namespace: jenkins
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: jenkins-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: jenkins
  namespace: jenkins
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: jenkins-master
  namespace: jenkins
spec:
  replicas: 1
  serviceName: jenkins
  selector:
    matchLabels:
      devops: jenkins-master
  template:
    metadata:
      labels:
        devops: jenkins-master
    spec:
      tolerations:
      - operator: Exists
      serviceAccount: jenkins
      initContainers:
      - name: fix-permissions
        image: busybox
        command: ["sh", "-c", "chown -R 1000:1000 /var/jenkins_home"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: jenkinshome
          mountPath: /var/jenkins_home
      containers:
      - name: jenkins
        image: jenkinsci/blueocean:latest
        imagePullPolicy: IfNotPresent
        ports:
        - name: http #Jenkins Master Web 服务端口
          containerPort: 8080
        - name: slavelistener #Jenkins Master 供未来 Slave 连接的端口
          containerPort: 50000
        volumeMounts:
        - name: jenkinshome
          mountPath: /var/jenkins_home
      volumes:
      - name: jenkinshome
        hostPath:
          path: /usr1/jenkins
---
apiVersion: v1
kind: Service
metadata:
  name: jenkins
  namespace: jenkins
spec:
  type: NodePort
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  - name: slavelistener
    port: 50000
    targetPort: 50000
  selector:
    devops: jenkins-master
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: jenkins-web
  namespace: jenkins
  annotations:
    Kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: www.jenkins.com
    http:
      paths:
      - path: /
        backend:
          serviceName: jenkins
          servicePort: 8080

简单描述一下:

  1. 创建命名空间,并增加账户管理
  2. 通过 StatefulSet 创建 Jenkins 服务容器,绑定了持久化存储卷
  3. 创建 Service 和 Ingress,通过 Ingress 向外提供服务,Service 内的 NodePort 定义可以去掉

安装完成后,如果配置了 Ingress,我们在当前机器上配置好 hosts,就可以通过 www.jenkins.com 去访问了;如果没有配置 Ingress,可以通过节点和端口去访问,执行以下命令查询 NodePort 端口:

kubectl get service -n jenkins

kubernetes+Jenkins持续集成

访问登录页面

kubernetes+Jenkins持续集成

然后通过以下命令查询管理员密码:

kubectl get pod -n jenkins
kubectl exec jenkins-master-0 -n jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword

登录以后可以按照这篇文章进行配置:Jenkins安装配置

Jenkins 集成 kubernetes (重点)

在使用 Jenkins 的过程中,我们不可能只使用一个 master 节点,必定是要使用多个节点组成集群,传统的 Jenkins 集群需要固定节点,而 kubernetes 的使用将使我们的集群更加灵活,可以即用即走,只有 master 节点需要长期存在,其他节点使用时创建,使用完销毁,合理的利用了资源,又不浪费资源。

安装 kubernetes 插件

在 Jenkins 中安装插件 kubernetes,只安装这一个,其他的就附带安装了。

安装完以后,我们在添加节点的地方使用。

配置地址信息

  • kubernetes 地址:这里的地址我们可以通过以下命令去查询
kubectl get nodes -v=7

kubernetes+Jenkins持续集成

这里就是我们 kubernetes 的地址,集群内部和外部都可以使用。但是因为我们的 Jenkins 是部署在集群内部的, 所以我们可以使用 kubernetes 的服务发现机制来配置。在 kubernetes 中对外提供 API 服务的是:

kubectl get svc

# kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 40d

在 default 命名空间下,有一个 kubernetes 的 Service,因此在集群内部根据 kubernetes 的路由规则可以采用 kubernetes.default 的方式访问 API,所以这里的配置可以是:
kubernetes.default 和 https://192.168.143.130:6443

  • Kubernetes 命名空间:jenkins
  • 服务证书:安装 Jenkins 的时候已经指定过 serviceaccount,所以这里不用填
  • Jenkins 地址:http://jenkins:8080
  • Jenkins 通道:jenkins:5000

kubernetes+Jenkins持续集成

配置 Pod Template

  • 名称: jnlp-slave
  • 命名空间:jenkins
  • 标签列表:jnlp-slave,作为 agent 的label 选择使用
  • 连接 jenkins 的超时时间(秒):300
  • 节点选择器:agent=true,选择指定标签的机器调度
  • 工作空间卷:可以使用共享存储,也可以使用临时的

这里我们没有选择镜像,默认会使用 jnlp-slave 的镜像,当我们设置镜像后,默认镜像就不使用了,必须填写

kubernetes+Jenkins持续集成

制作工具容器

我们在使用 Pod 作为容器执行机,实际是就是使用 Docker 容器作为 Jenkins 的节点,因为很多时候我们在 Jenkins 的任务中是要制作镜像的,所以需要制作一个工具集合的容器。

FROM alpine
USER root

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && apk update && apk add --no-cache openrc docker git curl tar gcc g++ make bash shadow openjdk8 py-pip python3-dev openssl-dev libffi-dev libstdc++ harfbuzz nss freetype ttf-freefont && mkdir -p /root/.kube && usermod -a -G docker root

COPY requirements.txt /
COPY config /root/.kube/

RUN pip install -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com -r /requirements.txt

RUN rm -rf /var/cache/apk/* && rm -rf ~/.cache/pip
#----安装 kubectl----
COPY kubectl /usr/local/bin/
RUN chmod -x /usr/local/bin/kubectl

创建一个目录,用来存放制作文件

mkdir tools

cd tools

# 拷贝 kubectl 文件和配置文件到当前目录
cp 'which kubectl' .
cp ~/.kube/config .

执行镜像制作命令

docker build -t 192.168.143.130:5000/jnlp-tools:latest .
docker push 192.168.143.130:5000/jnlp-tools:latest

然后我们在节点配置里,把镜像配置上

kubernetes+Jenkins持续集成

kubernetes+Jenkins持续集成

kubernetes+Jenkins持续集成

在 tools 容器中我们可能要执行 docker 命令,所以要挂载本机的 /var/run/docker.sock 文件

kubernetes 搭建 sonarqube 扫描环境

apiVersion: v1
kind: Service
metadata:
  name: sonarqube
  namespace: jenkins
  labels:
    app: sonarqube
spec:
  ports:
  - name: sonarqube
    port: 9000
    targetPort: 9000
    protocol: TCP
  selector:
    app: sonarqube
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: jenkins
  name: sonarqube
  labels:
    app: sonarqube
spce:
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      nodeSelector:
        sonar: true
      initContainers:
      - name: elasticsearch-logging-init
        image: alpine: 3.6
        command:
          - /sbin/sysctl
          - -w
          - vm.max_map_count=262144
        resources: {}
        securityContext:
          privileged: true
      containers:
      - name: sonarqube
        image: sonarqube: 7.9.5-community
        ports:
        - containerPort: 9000
        env:
        - name: SONARQUBE_JDBC_USERNAME
          value: root
        - name: SONARQUBE_JDBC_PASSWORD
          value: 123456
        - name: SONARQUBE_JDBC_URL
          value: "jdbc: mysql://ip:3306/sonar?useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useConfigs=maxPerformance&useSSL=false"
        livenessProbe:
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60
          periodSeconds: 30
          failureThreshold: 6
        resources:
          limits:
            cpu: 2000m
            memory: 4096Mi
          requests:
            cpu: 300m
            memory: 512Mi
        volumes:
        - name: sonarqube-data
          hostPath:
            path: /opt/sonarqube/data
        - name: sonarqube-logs
          hostPath:
            path: /opt/sonarqube/logs
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: sonarqube
  namespace: jenkins
spec:
  rules:
  - host: www.sonar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: sonarqube
          servicePort: 9000

小技巧

  1. 导出 Jenkins 已安装插件列表
# 账号:admin,密码:123456,Jenkins-master地址:xx.xx.xx.xx
curl -sSL "http://admin:123456@xx.xx.xx.xx:8080/pluginManager/api/xml?depth=1&xpath=/*/*/shortName|/*/*/version&wrapper=plugins" | perl -pe 's/.*?<shortName>([\w-]+).*?<version>([^<]+)()(</\w+>)+/\1:\2\n/g'|sed 's/ /:/' > plugins.txt

然后在其他安装了 Jenkins 的机器上执行以下命令,就可以安装好指定插件了

/usr/local/bin/install-plugins.sh < plugins.txt