当前位置:   article > 正文

第五天 从零开始构建基于Kubernetes的DevOps平台_搭建devops开发平台

搭建devops开发平台

基于Kubernetes的DevOps平台实践

持续集成工具:

  • Jenkins
  • gitlabci
  • Tekton

本章基于k8s集群部署gitlab、sonarQube、Jenkins等工具,并把上述工具集成到Jenkins中,以Django项目和SpringBoot项目为例,通过多分支流水线及Jenkinsfile实现项目代码提交到不同的仓库分支,实现自动代码扫描、单元测试、docker容器构建、k8s服务的自动部署。

  • DevOps、CI、CD介绍
  • Jenkins、sonarQube、gitlab的快速部署
  • Jenkins初体验
  • 流水线入门及Jenkinsfile使用
  • Jenkins与Kubernetes的集成
  • sonarQube代码扫描与Jenkins的集成
  • 实践Django项目的基于Jenkinsfile实现开发、测试环境的CI/CD
DevOps、CI、CD介绍

Continuous Integration (CI) / Continuous Delivery (CD)

软件交付流程

一个软件从零开始到最终交付,大概包括以下几个阶段:规划、编码、构建、测试、发布、部署和维护,基于这些阶段,我们的软件交付模型大致经历了几个阶段:

瀑布式流程

前期需求确立之后,软件开发人员花费数周和数月编写代码,把所有需求一次性开发完,然后将代码交给QA(质量保障)团队进行测试,然后将最终的发布版交给运维团队去部署。瀑布模型,简单来说,就是等一个阶段所有工作完成之后,再进入下一个阶段。这种模式的问题也很明显,产品迭代周期长,灵活性差。一个周期动辄几周几个月,适应不了当下产品需要快速迭代的场景。

敏捷开发

任务由大拆小,开发、测试协同工作,注重开发敏捷,不重视交付敏捷

DevOps

开发、测试、运维协同工作, 持续开发+持续交付。

我们是否可以认为DevOps = 提倡开发、测试、运维协同工作来实现持续开发、持续交付的一种软件交付模式?

大家想一下为什么最初的开发模式没有直接进入DevOps的时代?

原因是:沟通成本。

各角色人员去沟通协作的时候都是手动去做,交流靠嘴,靠人去指挥,很显然会出大问题。所以说不能认为DevOps就是一种交付模式,因为解决不了沟通协作成本,这种模式就不具备可落地性。

那DevOps时代如何解决角色之间的成本问题?DevOps的核心就是自动化。自动化的能力靠什么来支撑,工具和技术。

DevOps工具链

靠这些工具和技术,才实现了自动化流程,进而解决了协作成本,使得devops具备了可落地性。因此我们可以大致给devops一个定义:

devops = 提倡开发、测试、运维协同工作来实现持续开发、持续交付的一种软件交付模式 + 基于工具和技术支撑的自动化流程的落地实践。

因此devops不是某一个具体的技术,而是一种思想+自动化能力,来使得构建、测试、发布软件能够更加地便捷、频繁和可靠的落地实践。本次课程核心内容就是要教会大家如何利用工具和技术来实现完整的DevOps平台的建设。我们主要使用的工具有:

  1. gitlab,代码仓库,企业内部使用最多的代码版本管理工具。
  2. Jenkins, 一个可扩展的持续集成引擎,用于自动化各种任务,包括构建、测试和部署软件。
  3. robotFramework, 基于Python的自动化测试框架
  4. sonarqube,代码质量管理平台
  5. maven,java包构建管理工具
  6. Kubernetes
  7. Docker
Jenkins初体验
Kubernetes环境中部署jenkins

其他部署方式

注意点:

  1. 第一次启动很慢
  2. 因为后面Jenkins会与kubernetes集群进行集成,会需要调用kubernetes集群的api,因此安装的时候创建了ServiceAccount并赋予了cluster-admin的权限
  3. 默认部署到jenkins=true的节点
  4. 初始化容器来设置权限
  5. ingress来外部访问
  6. 数据存储通过hostpath挂载到宿主机中

jenkins/jenkins-all.yaml

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: Deployment
metadata:
  name: jenkins-master
  namespace: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      devops: jenkins-master
  template:
    metadata:
      labels:
        devops: jenkins-master
    spec:
      nodeSelector:
        jenkins: "true"
      serviceAccount: jenkins #Pod 需要使用的服务账号
      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:1.23.2
        imagePullPolicy: IfNotPresent
        ports:
        - name: http #Jenkins Master Web 服务端口
          containerPort: 8080
        - name: slavelistener #Jenkins Master 供未来 Slave 连接的端口
          containerPort: 50000
        volumeMounts:
        - name: jenkinshome
          mountPath: /var/jenkins_home
        env:
        - name: JAVA_OPTS
          value: "-Xms4096m -Xmx5120m -Duser.timezone=Asia/Shanghai -Dhudson.model.DirectoryBrowserSupport.CSP="
      volumes:
      - name: jenkinshome
        hostPath:
          path: /var/jenkins_home/
---
apiVersion: v1
kind: Service
metadata:
  name: jenkins
  namespace: jenkins
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  - name: slavelistener
    port: 50000
    targetPort: 50000
  type: ClusterIP
  selector:
    devops: jenkins-master
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: jenkins-web
  namespace: jenkins
spec:
  rules:
  - host: jenkins.luffy.com
    http:
      paths:
      - backend:
          serviceName: jenkins
          servicePort: 8080
        path: /
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102

创建服务:

## 为k8s-slave1打标签,将jenkins-master部署在k8s-slave1节点
$ kubectl label node k8s-slave1 jenkins=true
## 部署服务
$ kubectl create -f jenkins-all.yaml
## 查看服务
$ kubectl -n jenkins get po
NAME                              READY   STATUS    RESTARTS   AGE
jenkins-master-767df9b574-lgdr5   1/1     Running   0          20s

# 查看日志,第一次启动提示需要完成初始化设置
$ kubectl -n jenkins logs -f jenkins-master-767df9b574-lgdr5
......
*************************************************************

Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:

5396b4e1c395450f8360efd8ee641b18

This may also be found at: /var/jenkins_home/secrets/initialAdminPassword

*************************************************************

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

访问服务:

配置hosts解析,172.21.51.67 jenkins.luffy.com,然后使用浏览器域名访问服务。第一次访问需要大概几分钟的初始化时间。

使用jenkins启动日志中的密码,或者执行下面的命令获取解锁的管理员密码:

$ kubectl -n jenkins exec jenkins-master-767df9b574-lgdr5 bash 
/ # cat /var/jenkins_home/secrets/initialAdminPassword
35b083de1d25409eaef57255e0da481a

  • 1
  • 2
  • 3
  • 4

点击叉号,跳过选择安装推荐的插件环节,直接进入Jenkins。由于默认的插件地址安装非常慢,我们可以替换成国内清华的源,进入 jenkins 工作目录,目录下面有一个 updates 的目录,下面有一个 default.json 文件,我们执行下面的命令替换插件地址:

$ cd /var/jenkins_home/updates
$ sed -i 's/http:\/\/updates.jenkins-ci.org\/download/https:\/\/mirrors.tuna.tsinghua.edu.cn\/jenkins/g' default.json 
$ sed -i 's/http:\/\/www.google.com/https:\/\/www.baidu.com/g' default.json

  • 1
  • 2
  • 3
  • 4

暂时先不用重新启动pod,汉化后一起重启。

选择右上角admin->configure->password重新设置管理员密码,设置完后,会退出要求重新登录,使用admin/xxxxxx(新密码),登录即可。

安装汉化插件

Jenkins -> manage Jenkins -> Plugin Manager -> Avaliable,搜索 chinese关键字

选中后,选择[Install without restart],等待下载完成,然后点击[ Restart Jenkins when installation is complete and no jobs are running ],让Jenkins自动重启

启动后,界面默认变成中文。

Jenkins基本使用演示
演示目标
  • 代码提交gitlab,自动触发Jenkins任务
  • Jenkins任务完成后发送钉钉消息通知
演示准备

gitlab代码仓库搭建

https://github.com/sameersbn/docker-gitlab

## 全量部署的组件
$ gitlab-ctl status
run: alertmanager: (pid 1987) 27s; run: log: (pid 1986) 27s
run: gitaly: (pid 1950) 28s; run: log: (pid 1949) 28s
run: gitlab-exporter: (pid 1985) 27s; run: log: (pid 1984) 27s
run: gitlab-workhorse: (pid 1956) 28s; run: log: (pid 1955) 28s
run: logrotate: (pid 1960) 28s; run: log: (pid 1959) 28s
run: nginx: (pid 2439) 1s; run: log: (pid 1990) 27s
run: node-exporter: (pid 1963) 28s; run: log: (pid 1962) 28s
run: postgres-exporter: (pid 1989) 27s; run: log: (pid 1988) 27s
run: postgresql: (pid 1945) 28s; run: log: (pid 1944) 28s
run: prometheus: (pid 1973) 28s; run: log: (pid 1972) 28s
run: puma: (pid 1968) 28s; run: log: (pid 1966) 28s
run: redis: (pid 1952) 28s; run: log: (pid 1951) 28s
run: redis-exporter: (pid 1971) 28s; run: log: (pid 1964) 28s
run: sidekiq: (pid 1969) 28s; run: log: (pid 1967) 28s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

部署分析:

  1. 依赖postgres
  2. 依赖redis

使用k8s部署:

  1. 准备secret文件

    $ cat gitlab-secret.txt
    postgres.user.root=root
    postgres.pwd.root=1qaz2wsx
    
    $ kubectl -n jenkins create secret generic gitlab-secret --from-env-file=gitlab-secret.txt
    
    • 1
    • 2
    • 3
    • 4
    • 5
  2. 部署postgres

    注意点:

    • 使用secret来引用账户密码
    • 使用postgres=true来指定节点
    $ cat postgres.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: postgres
      labels:
        app: postgres
      namespace: jenkins
    spec:
      ports:
      - name: server
        port: 5432
        targetPort: 5432
        protocol: TCP
      selector:
        app: postgres
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: jenkins
      name: postgres
      labels:
        app: postgres
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: postgres
      template:
        metadata:
          labels:
            app: postgres
        spec:
          nodeSelector:
            postgres: "true"
          tolerations:
          - operator: "Exists"
          containers:
          - name: postgres
            image:  172.21.51.67:5000/postgres:11.4 #若本地没有启动该仓库,换成postgres:11.4
            imagePullPolicy: "IfNotPresent"
            ports:
            - containerPort: 5432
            env:
            - name: POSTGRES_USER           #PostgreSQL 用户名
              valueFrom:
                secretKeyRef:
                  name: gitlab-secret
                  key: postgres.user.root
            - name: POSTGRES_PASSWORD       #PostgreSQL 密码
              valueFrom:
                secretKeyRef:
                  name: gitlab-secret
                  key: postgres.pwd.root
            resources:
              limits:
                cpu: 1000m
                memory: 2048Mi
              requests:
                cpu: 50m
                memory: 100Mi
            volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: postgredb
          volumes:
          - name: postgredb
            hostPath:
              path: /var/lib/postgres/
              
    
    #部署到k8s-slave2节点
    $ kubectl label node k8s-slave2 postgres=true
    
    #创建postgres
    $ kubectl create -f postgres.yaml
    
    # 创建数据库gitlab,为后面部署gitlab组件使用
    $ kubectl -n jenkins exec -ti postgres-7ff9b49f4c-nt8zh bash
    root@postgres-7ff9b49f4c-nt8zh:/# psql
    root=# create database gitlab;
    CREATE DATABASE
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
  3. 部署redis

    $ cat redis.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: redis
      labels:
        app: redis
      namespace: jenkins
    spec:
      ports:
      - name: server
        port: 6379
        targetPort: 6379
        protocol: TCP
      selector:
        app: redis
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: jenkins
      name: redis
      labels:
        app: redis
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: redis
      template:
        metadata:
          labels:
            app: redis
        spec:
          tolerations:
          - operator: "Exists"
          containers:
          - name: redis
            image:  sameersbn/redis:4.0.9-2
            imagePullPolicy: "IfNotPresent"
            ports:
            - containerPort: 6379
            resources:
              limits:
                cpu: 1000m
                memory: 2048Mi
              requests:
                cpu: 50m
                memory: 100Mi
                
    # 创建
    $ kubectl create -f redis.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
  4. 部署gitlab

    注意点:

    • 使用ingress暴漏服务
    • 添加annotation,指定nginx端上传大小限制,否则推送代码时会默认被限制1m大小,相当于给nginx设置client_max_body_size的限制大小
    • 使用gitlab=true来选择节点
    • 使用服务发现地址来访问postgres和redis
    • 在secret中引用数据库账户和密码
    • 数据库名称为gitlab
    $ cat gitlab.yaml
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: gitlab
      namespace: jenkins
      annotations:
        nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    spec:
      rules:
      - host: gitlab.luffy.com
        http:
          paths:
          - backend:
              serviceName: gitlab
              servicePort: 80
            path: /
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: gitlab
      labels:
        app: gitlab
      namespace: jenkins
    spec:
      ports:
      - name: server
        port: 80
        targetPort: 80
        protocol: TCP
      selector:
        app: gitlab
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      namespace: jenkins
      name: gitlab
      labels:
        app: gitlab
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: gitlab
      template:
        metadata:
          labels:
            app: gitlab
        spec:
          nodeSelector:
            gitlab: "true"
          tolerations:
          - operator: "Exists"
          containers:
          - name: gitlab
            image:  sameersbn/gitlab:13.2.2
            imagePullPolicy: "IfNotPresent"
            env:
            - name: GITLAB_HOST
              value: "gitlab.luffy.com"
            - name: GITLAB_PORT
              value: "80"
            - name: GITLAB_SECRETS_DB_KEY_BASE
              value: "long-and-random-alpha-numeric-string"
            - name: GITLAB_SECRETS_DB_KEY_BASE
              value: "long-and-random-alpha-numeric-string"
            - name: GITLAB_SECRETS_SECRET_KEY_BASE
              value: "long-and-random-alpha-numeric-string"
            - name: GITLAB_SECRETS_OTP_KEY_BASE
              value: "long-and-random-alpha-numeric-string"
            - name: DB_HOST
              value: "postgres"
            - name: DB_NAME
              value: "gitlab"
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: gitlab-secret
                  key: postgres.user.root
            - name: DB_PASS
              valueFrom:
                secretKeyRef:
                  name: gitlab-secret
                  key: postgres.pwd.root
            - name: REDIS_HOST
              value: "redis"
            - name: REDIS_PORT
              value: "6379"
            ports:
            - containerPort: 80
            resources:
              limits:
                cpu: 2000m
                memory: 5048Mi
              requests:
                cpu: 100m
                memory: 500Mi
            volumeMounts:
            - mountPath: /home/git/data
              name: data
          volumes:
          - name: data
            hostPath:
              path: /var/lib/gitlab/
    
    #部署到k8s-slave2节点
    $ kubectl label node k8s-slave2 gitlab=true
    
    # 创建
    $ kubectl create -f gitlab.yaml
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

配置hosts解析:

172.21.51.67 gitlab.luffy.com
  • 1

设置root密码

访问http://gitlab.luffy.com,设置管理员密码

配置k8s-master节点的hosts

$ echo "172.21.51.67 gitlab.luffy.com" >>/etc/hosts
  • 1

myblog项目推送到gitlab

mkdir demo
cp -r myblog demo/
cd demo/myblog
git remote rename origin old-origin
git remote add origin http://gitlab.luffy.com/root/myblog.git
git push -u origin --all
git push -u origin --tags

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

钉钉推送

官方文档

  • 配置机器人

  • 试验发送消息

    $ curl 'https://oapi.dingtalk.com/robot/send?access_token=67e81175c6ebacb1307e83f62680f36fbcf4524e8f43971cf2fb2049bc58723d' \
       -H 'Content-Type: application/json' \
       -d '{"msgtype": "text", 
            "text": {
                 "content": "我就是我, 是不一样的烟火"
            }
          }'
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
演示过程

流程示意图:

  1. 安装gitlab plugin

    插件中心搜索并安装gitlab,直接安装即可

  2. 配置Gitlab

    系统管理->系统配置->Gitlab,其中的API Token,需要从下个步骤中获取

  3. 获取AccessToken

    登录gitlab,选择user->Settings->access tokens新建一个访问token

  4. 配置host解析

    由于我们的Jenkins和gitlab域名是本地解析,因此需要让gitlab和Jenkins服务可以解析到对方的域名。两种方式:

    • 在容器内配置hosts

    • 配置coredns的静态解析

              hosts {
                  172.21.51.67 jenkins.luffy.com  gitlab.luffy.com
                  fallthrough
              }
      
      • 1
      • 2
      • 3
      • 4
  5. 创建自由风格项目

    • gitlab connection 选择为刚创建的gitlab
    • 源码管理选择Git,填项项目地址
    • 新建一个 Credentials 认证,使用用户名密码方式,配置gitlab的用户和密码
    • 构建触发器选择 Build when a change is pushed to GitLab
    • 生成一个Secret token
    • 保存
  6. 到gitlab配置webhook

    • 进入项目下settings->Integrations
    • URL: http://jenkins.luffy.com/project/free
    • Secret Token 填入在Jenkins端生成的token
    • Add webhook
    • test push events,报错:Requests to the local network are not allowed
  7. 设置gitlab允许向本地网络发送webhook请求

    访问 Admin Aera -> Settings -> Network ,展开Outbound requests

    Collapse,勾选第一项即可。再次test push events,成功。

  8. 配置free项目,增加构建步骤,执行shell,将发送钉钉消息的shell保存

  9. 提交代码到gitlab仓库,查看构建是否自动执行

Master-Slaves(agent)模式

上面演示的任务,默认都是在master节点执行的,多个任务都在master节点执行,对master节点的性能会造成一定影响,如何将任务分散到不同的节点,做成多slave的方式?

  1. 添加slave节点

    • 系统管理 -> 节点管理 -> 新建节点
    • 比如添加172.21.51.68,选择固定节点,保存
    • 远程工作目录/opt/jenkins_jobs
    • 标签为任务选择节点的依据,如172.21.51.68
    • 启动方式选择通过java web启动代理,代理是运行jar包,通过JNLP(是一种允许客户端启动托管在远程Web服务器上的应用程序的协议 )启动连接到master节点服务中

  2. 执行java命令启动agent服务

    ## 登录172.21.51.68,下载agent.jar
    $ wget http://jenkins.luffy.com/jnlpJars/agent.jar
    ## 会提示找不到agent错误,因为没有配置地址解析,由于连接jenkins master会通过50000端口,直接使用cluster-ip
    $ kubectl -n jenkins get svc #在master节点执行查询cluster-ip地址
    NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)              AGE
    jenkins   ClusterIP   10.99.204.208   <none>        8080/TCP,50000/TCP   4h8m
    
    ## 再次回到68节点
    $ wget 10.99.204.208:8080/jnlpJars/agent.jar
    $ java -jar agent.jar -jnlpUrl http://10.99.204.208:8080/computer/172.21.51.68/slave-agent.jnlp -secret 4be4d164f861d2830835653567867a1e695b30c320d35eca2be9f5624f8712c8 -workDir "/opt/jenkins_jobs"
    ...
    INFO: Remoting server accepts the following protocols: [JNLP4-connect, Ping]
    Apr 01, 2020 7:03:51 PM hudson.remoting.jnlp.Main$CuiListener status
    INFO: Agent discovery successful
      Agent address: 10.99.204.208
      Agent port:    50000
      Identity:      e4:46:3a:de:86:24:8e:15:09:13:3d:a7:4e:07:04:37
    Apr 01, 2020 7:03:51 PM hudson.remoting.jnlp.Main$CuiListener status
    INFO: Handshaking
    Apr 01, 2020 7:03:51 PM hudson.remoting.jnlp.Main$CuiListener status
    INFO: Connecting to 10.99.204.208:50000
    Apr 01, 2020 7:03:51 PM hudson.remoting.jnlp.Main$CuiListener status
    INFO: Trying protocol: JNLP4-connect
    Apr 01, 2020 7:04:02 PM hudson.remoting.jnlp.Main$CuiListener status
    INFO: Remote identity confirmed: e4:46:3a:de:86:24:8e:15:09:13:3d:a7:4e:07:04:37
    Apr 01, 2020 7:04:03 PM hudson.remoting.jnlp.Main$CuiListener status
    INFO: Connected
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    若出现如下错误:

    SEVERE: http://jenkins.luffy.com/tcpSlaveAgentListener/ appears to be publishing an invalid X-Instance-Identity.
    java.io.IOException: http://jenkins.luffy.com/tcpSlaveAgentListener/ appears to be publishing an invalid X-Instance-Identity.
            at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:287)
            at hudson.remoting.Engine.innerRun(Engine.java:694)
            at hudson.remoting.Engine.run(Engine.java:519)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以选择: 配置从节点 -> 高级 -> Tunnel连接位置,参考下图进行设置:

  3. 查看Jenkins节点列表,新节点已经处于可用状态

  4. 测试使用新节点执行任务

    • 配置free项目

    • 限制项目的运行节点 ,标签表达式选择172.21.51.68

    • 立即构建

    • 查看构建日志

      Started by user admin
      Running as SYSTEM
      Building remotely on 172.21.51.68 in workspace /opt/jenkins_jobs/workspace/free-demo
      using credential gitlab-user
      Cloning the remote Git repository
      Cloning repository http://gitlab.luffy.com/root/myblog.git
       > git init /opt/jenkins_jobs/workspace/free-demo # timeout=10
       ...
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
Jenkins定制化容器

由于每次新部署Jenkins环境,均需要安装很多必要的插件,因此考虑把插件提前做到镜像中

Dockerfile

FROM jenkinsci/blueocean:1.23.2
LABEL maintainer="inspur_lyx@hotmail.com"

## 用最新的插件列表文件替换默认插件文件
COPY plugins.txt /usr/share/jenkins/ref/

## 执行插件安装
RUN /usr/local/bin/install-plugins.sh < /usr/share/jenkins/ref/plugins.txt

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

plugins.txt

ace-editor:1.1
allure-jenkins-plugin:2.28.1
ant:1.10
antisamy-markup-formatter:1.6
apache-httpcomponents-client-4-api:4.5.10-1.0
authentication-tokens:1.3
...

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

get_plugin.sh

admin:123456@localhost 需要替换成Jenkins的用户名、密码及访问地址

#!/usr/bin/env bash
curl -sSL  "http://admin:123456@localhost: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

  • 1
  • 2
  • 3
## 执行构建,定制jenkins容器
$ docker build . -t 172.21.51.67:5000/jenkins:v20200414 -f Dockerfile
$ docker push 172.21.51.67:5000/jenkins:v20200414

  • 1
  • 2
  • 3
  • 4

至此,我们可以使用定制化的镜像启动jenkins服务

## 删掉当前服务
$ kubectl delete -f jenkins-all.yaml

## 删掉已挂载的数据
$ rm -rf /var/jenkins_home

## 替换使用定制化镜像
$ sed -i 's#jenkinsci/blueocean#172.21.51.67:5000/jenkins:v20200404#g' jenkins-all.yaml

## 重新创建服务
$ kubectl create -f jenkins-all.yaml

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
本章小结

自由风格项目弊端:

  • 任务的完成需要在Jenkins端维护大量的配置
  • 没法做版本控制
  • 可读性、可移植性很差,不够优雅
流水线入门

官方文档

为什么叫做流水线,和工厂产品的生产线类似,pipeline是从源码到发布到线上环境。关于流水线,需要知道的几个点:

  • 重要的功能插件,帮助Jenkins定义了一套工作流框架;

  • Pipeline 的实现方式是一套 Groovy DSL( 领域专用语言 ),所有的发布流程都可以表述为一段 Groovy 脚本;

  • 将WebUI上需要定义的任务,以脚本代码的方式表述出来;

  • 帮助jenkins实现持续集成CI(Continue Integration)和持续部署CD(Continue Deliver)的重要手段;

流水线基础语法

官方文档

两种语法类型:

  • Scripted Pipeline,脚本式流水线,最初支持的类型
  • Declarative Pipeline,声明式流水线,为Pipeline plugin在2.5版本之后新增的一种脚本类型,后续Open Blue Ocean所支持的类型。与原先的Scripted Pipeline一样,都可以用来编写脚本。Declarative Pipeline 是后续Open Blue Ocean所支持的类型,写法简单,支持内嵌Scripted Pipeline代码

为与BlueOcean脚本编辑器兼容,通常建议使用Declarative Pipeline的方式进行编写,从jenkins社区的动向来看,很明显这种语法结构也会是未来的趋势。

脚本示例
pipeline { 
    agent {label '172.21.51.68'}
    environment { 
        PROJECT = 'myblog'
    }
    stages {
        stage('Checkout') { 
            steps { 
                checkout scm 
            }
        }
        stage('Build') { 
            steps { 
                sh 'make' 
            }
        }
        stage('Test'){
            steps {
                sh 'make check'
                junit 'reports/**/*.xml' 
            }
        }
        stage('Deploy') {
            steps {
                sh 'make publish'
            }
        }
    }
	post {
        success { 
            echo 'Congratulations!'
        }
		failure { 
            echo 'Oh no!'
        }
        always { 
            echo 'I will always say Hello again!'
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
脚本解释:
  • checkout步骤为检出代码; scm是一个特殊变量,指示checkout步骤克隆触发此Pipeline运行的特定修订

  • agent:指明使用哪个agent节点来执行任务,定义于pipeline顶层或者stage内部

    • any,可以使用任意可用的agent来执行

    • label,在提供了标签的 Jenkins 环境中可用的代理上执行流水线或阶段。 例如: agent { label 'my-defined-label' },最常见的使用方式

    • none,当在 pipeline 块的顶部没有全局代理, 该参数将会被分配到整个流水线的运行中并且每个 stage 部分都需要包含他自己的 agent 部分。比如: agent none

    • docker, 使用给定的容器执行流水线或阶段。 在指定的节点中,通过运行容器来执行任务

      agent {
          docker {
              image 'maven:3-alpine'
              label 'my-defined-label'
              args  '-v /tmp:/tmp'
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
  • options: 允许从流水线内部配置特定于流水线的选项。

    • buildDiscarder , 为最近的流水线运行的特定数量保存组件和控制台输出。例如: options { buildDiscarder(logRotator(numToKeepStr: '10')) }
    • disableConcurrentBuilds ,不允许同时执行流水线。 可被用来防止同时访问共享资源等。 例如: options { disableConcurrentBuilds() }
    • timeout ,设置流水线运行的超时时间, 在此之后,Jenkins将中止流水线。例如: options { timeout(time: 1, unit: 'HOURS') }
    • retry,在失败时, 重新尝试整个流水线的指定次数。 For example: options { retry(3) }
  • environment: 指令制定一个 键-值对序列,该序列将被定义为所有步骤的环境变量

  • stages: 包含一系列一个或多个 stage指令, stages 部分是流水线描述的大部分"work" 的位置。 建议 stages 至少包含一个 stage 指令用于连续交付过程的每个离散部分,比如构建, 测试, 和部署。

    pipeline {
        agent any
        stages { 
            stage('Example') {
                steps {
                    echo 'Hello World'
                }
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • steps: 在给定的 stage 指令中执行的定义了一系列的一个或多个steps

  • post: 定义一个或多个steps ,这些阶段根据流水线或阶段的完成情况而运行post 支持以下 post-condition 块中的其中之一: always, changed, failure, success, unstable, 和 aborted

    • always, 无论流水线或阶段的完成状态如何,都允许在 post 部分运行该步骤
    • changed, 当前流水线或阶段的完成状态与它之前的运行不同时,才允许在 post 部分运行该步骤
    • failure, 当前流水线或阶段的完成状态为"failure",才允许在 post 部分运行该步骤, 通常web UI是红色
    • success, 当前流水线或阶段的完成状态为"success",才允许在 post 部分运行该步骤, 通常web UI是蓝色或绿色
    • unstable, 当前流水线或阶段的完成状态为"unstable",才允许在 post 部分运行该步骤, 通常由于测试失败,代码违规等造成。通常web UI是黄色
    • aborted, 只有当前流水线或阶段的完成状态为"aborted",才允许在 post 部分运行该步骤, 通常由于流水线被手动的aborted。通常web UI是灰色

创建pipeline示意:

新建任务 -> 流水线

jenkins/pipelines/p1.yaml

pipeline {
   agent {label '172.21.51.68'}
   environment { 
      PROJECT = 'myblog'
   }
   stages {
      stage('printenv') {
         steps {
            echo 'Hello World'
            sh 'printenv'
         }
      }
      stage('check') {
         steps {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.luffy.com/root/myblog.git']]])
         }
      }
      stage('build-image') {
         steps {
            sh 'docker build . -t myblog:latest -f Dockerfile'
         }
      }
      stage('send-msg') {
         steps {
            sh """
            curl 'https://oapi.dingtalk.com/robot/send?access_token=67e81175c6ebacb1307e83f62680f36fbcf4524e8f43971cf2fb2049bc58723d' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text", 
        "text": {
             "content": "我就是我, 是不一样的烟火"
        }
      }'
      """
         }
      }
   }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

点击“立即构建”,同样的,我们可以配置触发器,使用webhook的方式接收项目的push事件,

  • 构建触发器选择 Build when a change is pushed to GitLab.
  • 生成 Secret token
  • 配置gitlab,创建webhook,发送test push events测试
Blue Ocean:

官方文档

我们需要知道的几点:

  • 是一个插件, 旨在为Pipeline提供丰富的体验 ;
  • 连续交付(CD)Pipeline的复杂可视化,允许快速和直观地了解Pipeline的状态;
  • 目前支持的类型仅针对于Pipeline,尚不能替代Jenkins 经典版UI

思考:

  1. 每个项目都把大量的pipeline脚本写在Jenkins端,对于谁去维护及维护成本是一个问题
  2. 没法做版本控制
Jenkinsflie

Jenkins Pipeline 提供了一套可扩展的工具,用于将“简单到复杂”的交付流程实现为“持续交付即代码”。Jenkins Pipeline 的定义通常被写入到一个文本文件(称为 Jenkinsfile )中,该文件可以被放入项目的源代码控制库中。

演示1:使用Jenkinsfile管理pipeline
  • 在项目中新建Jenkinsfile文件,拷贝已有script内容
  • 配置pipeline任务,流水线定义为Pipeline Script from SCM
  • 执行push 代码测试

Jenkinsfile:

jenkins/pipelines/p2.yaml

pipeline {
   agent { label '172.21.51.68'}

   stages {
      stage('printenv') {
         steps {
            echo 'Hello World'
            sh 'printenv'
         }
      }
      stage('check') {
         steps {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'gitlab-user', url: 'http://gitlab.luffy.com/root/myblog.git']]])
         }
      }
      stage('build-image') {
         steps {
            retry(2) { sh 'docker build . -t myblog:latest'}
         }
      }
      stage('send-msg') {
         steps {
            sh """
            curl 'https://oapi.dingtalk.com/robot/send?access_token=67e81175c6ebacb1307e83f62680f36fbcf4524e8f43971cf2fb2049bc58723d' \
   -H 'Content-Type: application/json' \
   -d '{"msgtype": "text", 
        "text": {
             "content": "我就是我, 是不一样的烟火"
        }
      }'
      """
         }
      }
   }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
演示2:优化及丰富流水线内容
  • 优化代码检出阶段

    由于目前已经配置了使用git仓库地址,且使用SCM来检测项目,因此代码检出阶段完全没有必要再去指定一次

  • 构建镜像的tag使用git的commit id

  • 增加post阶段的消息通知,丰富通知内容

  • 配置webhook,实现myblog代码推送后,触发Jenkinsfile任务执行

jenkins/pipelines/p3.yaml

pipeline {
    agent { label '172.21.51.68'}

    stages {
        stage('printenv') {
            steps {
            echo 'Hello World'
            sh 'printenv'
            }
        }
        stage('check') {
            steps {
                checkout scm
            }
        }
        stage('build-image') {
            steps {
            	retry(2) { sh 'docker build . -t myblog:${GIT_COMMIT}'}
            }
        }
    }
    post {
        success { 
            echo 'Congratulations!'
            sh """
                curl 'https://oapi.dingtalk.com/robot/send?access_token=67e81175c6ebacb1307e83f62680f36fbcf4524e8f43971cf2fb2049bc58723d' \
                    -H 'Content-Type: application/json' \
                    -d '{"msgtype": "text", 
                            "text": {
                                "content": "
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/639538
推荐阅读
相关标签