本章节通过在Jenkins创建一个kubernetes云环境,动态的在kubernetes集群中创建pod完成pipeline的构建流程,关于直接在宿主机上搭建Jenkins集群的可参照Kubernetes CI/CD(1)

部署Jenkins

  1. 下载Jenkins对应的镜像

    docker pull jenkins/jenkins:2.221
    
    
  2. 将jenkins镜像上传到自己的私有镜像仓库中

    docker tag jenkins/jenkins:2.221 192.168.0.107/k8s/jenkins:2.221
    
    docker push 	192.168.0.107/k8s/jenkins:2.221
    
    
  3. 编写启动Jenkins的yml文件

    cat > jenkins.yml << EOF
    kind: PersistentVolume
    apiVersion: v1
    metadata:
    name: jenkins
    labels:
    type: local
    app: jenkins
    spec:
    capacity:
    storage: 10Gi
    accessModes:
    - ReadWriteOnce
    hostPath:
    path: /opt/k8s/yml/jenkins/data
    --- kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
    name: jenkins-claim
    spec:
    accessModes:
    - ReadWriteOnce
    resources:
    requests:
    storage: 10Gi
    --- apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: jenkins
    namespace: default
    automountServiceAccountToken: true
    --- apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: Jenkins-cluster-admin
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: cluster-admin
    subjects:
    - kind: ServiceAccount
    name: jenkins
    namespace: default
    --- apiVersion: v1
    kind: Service
    metadata:
    name: jenkins
    labels:
    app: jenkins
    spec:
    ports:
    - port: 80
    targetPort: 8080
    nodePort: 8888
    name: jenkins
    - port: 50000
    targetPort: 50000
    nodePort: 50000
    name: agent
    selector:
    app: jenkins
    tier: jenkins
    type: NodePort
    --- apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: jenkins
    labels:
    app: jenkins
    spec:
    strategy:
    type: Recreate
    selector:
    matchLabels:
    app: jenkins
    tier: jenkins
    template:
    metadata:
    labels:
    app: jenkins
    tier: jenkins
    spec:
    serviceAccountName: jenkins
    containers:
    - image: 192.168.0.107/k8s/jenkins:2.221
    imagePullPolicy: IfNotPresent
    name: jenkins
    securityContext:
    privileged: true
    runAsUser: 0
    volumeMounts:
    - name: kubeconfig
    mountPath: /var/jenkins_home/.kube
    - name: docker
    mountPath: /var/run/docker.sock
    - name: docker-bin
    mountPath: /usr/bin/docker
    - name: jenkins-persistent-storage
    mountPath: /var/jenkins_home
    ports:
    - containerPort: 8080
    name: jenkins
    - containerPort: 50000
    name: agent
    volumes:
    - name: kubeconfig
    emptyDir: {}
    - name: docker
    hostPath:
    path: /var/run/docker.sock
    - name: docker-bin
    hostPath:
    path: /opt/k8s/bin/docker
    - name: jenkins-persistent-storage
    persistentVolumeClaim:
    claimName: jenkins-claim
    EOF

安装 kubernetes相关插件

kubernetes-cd
kubernetes-client-api
kubernetes-credentials
kubernetes

配置kubernetes云(配置详情官方网站kubernetes-plugin)

  1. 新加一个Cloud

    在Jenkins界面执行

    Manage Jenkins -> Manage Nodes and Clouds -> Configure Clouds -> Add a new Cloud

  2. 配置cloud,点击Kubernetes Cloud details

    • Kubernetes 地址:指定要连接的k8s集群API地址,因为我们master是在k8s集群中启动的,所以此处可以直接用https://kubernetes.default.svc.cluster.local,其中kubernetes是k8s集群给我们启动的一个service,内部会把对他的访问转发给API server,如果Jenkins不在k8s集群中,或者想要启动的构建pod和master不是一个集群,这个地方就需要相应的k8s集群地址
    • 凭据:访问k8s集群的认证凭证,我们启动Jenkins集群时同时创建了Service account,并赋给了Jenkins容器,所以这个地方可以直接创建一个service count类型的凭据,如果是访问其他集群,需要用服务证书key来配置

    配置好后点击:连接测试,正常的话会出现Connection test successful的提示

  3. 配置Jenkins相关信息(主要是agent和master通信的信息)

    • Jenkins 地址: 连接jenkins master的地址,因为我们jenkins对应的service启动节点是80,所以这个地方就去掉了端口号,并且service也启动了50000端口映射到容器的50000,所以直接配置成http://jenkins

构建一个简单的流水线验证cloud的构建功能

  1. 在Jenkins界面新建一个item,名称hello-pipeline-cloud, 类型选择:流水线(pipeline)

  2. 编辑pipeline部分

    podTemplate(cloud: "kubernetes") {
    node(POD_LABEL) { stage('Run shell') {
    sh 'echo hello world'
    } }
    }
    • cloud: "kubernetes",指定执行的云环境,默认是kubernetes,所以这个地方可以省略,当有多个云环境或者我们创建的cloud名称不是kubernetes则需要明确指定
    • POD_LABEL 是在1.17.0版本后引入的一个新特性,可以自动对创建的pod进行打标签
  3. 执行构建

    首先我们可以在要执行的k8s集群上执行如下命令观察执行构建过程中k8s云给我做了什么事

    kubectl get pod -w
    
    

    在Jenkins界面选择刚创建的工程,点击 Build Now

    对应的集群的输出

    root@master:/opt/k8s/yml/jenkins# kubectl get pod -w
    NAME READY STATUS RESTARTS AGE
    jenkins-68d8b54c45-gshvp 1/1 Running 0 60m
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 0/1 Pending 0 0s
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 0/1 Pending 0 0s
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 0/1 ContainerCreating 0 0s
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 1/1 Running 0 1s
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 1/1 Terminating 0 7s
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 0/1 Terminating 0 8s
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 0/1 Terminating 0 9s
    hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v 0/1 Terminating 0 9s

    可以看到k8s集群给我们创建了一个新的pod:hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v,构建完成后会自动把这个pod停掉

    Jenkins构建的日志

    Started by user admin
    Running in Durability level: MAX_SURVIVABILITY
    [Pipeline] Start of Pipeline
    [Pipeline] podTemplate
    [Pipeline] {
    [Pipeline] node
    Still waiting to schedule task
    All nodes of label ‘hello-pipeline-cloud_9-7n3c4’ are offline
    Created Pod: hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v in namespace default
    Agent hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v is provisioned from template hello-pipeline-cloud_9-7n3c4-8stzj
    ---
    apiVersion: "v1"
    kind: "Pod"
    metadata:
    annotations:
    buildUrl: "http://jenkins/job/hello-pipeline-cloud/9/"
    runUrl: "job/hello-pipeline-cloud/9/"
    labels:
    jenkins: "slave"
    jenkins/label: "hello-pipeline-cloud_9-7n3c4"
    name: "hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v"
    spec:
    containers:
    - env:
    - name: "JENKINS_SECRET"
    value: "********"
    - name: "JENKINS_AGENT_NAME"
    value: "hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v"
    - name: "JENKINS_NAME"
    value: "hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v"
    - name: "JENKINS_AGENT_WORKDIR"
    value: "/home/jenkins/agent"
    - name: "JENKINS_URL"
    value: "http://jenkins/"
    image: "jenkins/jnlp-slave:3.35-5-alpine"
    name: "jnlp"
    volumeMounts:
    - mountPath: "/home/jenkins/agent"
    name: "workspace-volume"
    readOnly: false
    nodeSelector:
    beta.kubernetes.io/os: "linux"
    restartPolicy: "Never"
    securityContext: {}
    volumes:
    - emptyDir:
    medium: ""
    name: "workspace-volume" Running on hello-pipeline-cloud-9-7n3c4-8stzj-n8g6v in /home/jenkins/agent/workspace/hello-pipeline-cloud
    [Pipeline] {
    [Pipeline] stage
    [Pipeline] { (Run shell)
    [Pipeline] sh
    + echo hello world
    hello world
    [Pipeline] }
    [Pipeline] // stage
    [Pipeline] }
    [Pipeline] // node
    [Pipeline] }
    [Pipeline] // podTemplate
    [Pipeline] End of Pipeline
    Finished: SUCCESS

    从日志中可以看到k8s集群采用默认的pod模版给我启动了一个pod,并且把我们的构建内容sh 'echo hello world'在pod对应的容器中执行了

    如果默认的模版不能满足我们的要求(比如在内网工作,对应的image想用我们自己私有仓库中的镜像可以重写name是jnlp的容器模版)后面一个例子,我们会重写这个模版,并在我们自定义的容器中执行我们的构建

构建自定义podtemplage

  1. 在Jenkins界面新建一个item,名称hello-pipeline-selfpodtemplate, 类型选择:流水线(pipeline)

  2. 编辑pipeline部分

    podTemplate(yaml: """
    apiVersion: v1
    kind: Pod
    metadata:
    labels:
    app: busybox
    spec:
    containers:
    - name: busybox
    image: 192.168.0.107/k8s/busybox:latest
    command:
    - cat
    tty: true
    """
    ,containers: [containerTemplate(name: 'jnlp', image: '192.168.0.107/jenkins/jnlp-slave:3.35-5-alpine')]
    ,cloud: "kubernetes") {
    node(POD_LABEL) {
    container('busybox') {
    sh "hostname"
    }
    }
    }
    • yaml 通过这个字段,我们定义了一个自己的podtemplate,容器名称是busybox,并在后面使用
    • containers的containerTemplate,我们重写了jnlp容器启动的镜像名称。jnlp镜像必须启动,他要和jenkins master通信,来告知构建状态,并且不能配置其它容器模版的名称为jnlp,否则会造成构建一直不会结束
    • 具体的构建流程,我们通过container('busybox')指定了构建要执行的容器,这个地方我们可以根据要构建的类型,配置maven、gradle、docker等各种构建环境来配置不同的podtemplate以此满足不同的构建需求,这也是用云环境进行构建的优势

    同样,我们看下k8s集群的pod创建情况


    root@master:/opt/k8s/yml/jenkins# kubectl get pod -w
    NAME READY STATUS RESTARTS AGE
    jenkins-68d8b54c45-gshvp 1/1 Running 0 83m hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw 0/2 Pending 0 0s
    hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw 0/2 Pending 0 0s
    hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw 0/2 ContainerCreating 0 0s
    hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw 2/2 Running 0 2s
    hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw 2/2 Terminating 0 7s

    看到这次k8s集群创建的pod包含了两个容器,如果我们describe这个pod可以看到里面是我们指定的容器

    Jenkins日志

    Started by user admin
    Running in Durability level: MAX_SURVIVABILITY
    [Pipeline] Start of Pipeline
    [Pipeline] podTemplate
    [Pipeline] {
    [Pipeline] node
    Created Pod: hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw in namespace default
    Still waiting to schedule task
    Waiting for next available executor on ‘hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw’
    Agent hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw is provisioned from template hello-pipeline-selfpodtemplate_6-ch89k-0v48m
    ---
    apiVersion: "v1"
    kind: "Pod"
    metadata:
    annotations:
    buildUrl: "http://jenkins/job/hello-pipeline-selfpodtemplate/6/"
    runUrl: "job/hello-pipeline-selfpodtemplate/6/"
    labels:
    app: "busybox"
    jenkins: "slave"
    jenkins/label: "hello-pipeline-selfpodtemplate_6-ch89k"
    name: "hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw"
    spec:
    containers:
    - command:
    - "cat"
    image: "192.168.0.107/k8s/busybox:latest"
    name: "busybox"
    tty: true
    volumeMounts:
    - mountPath: "/home/jenkins/agent"
    name: "workspace-volume"
    readOnly: false
    - env:
    - name: "JENKINS_SECRET"
    value: "********"
    - name: "JENKINS_AGENT_NAME"
    value: "hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw"
    - name: "JENKINS_NAME"
    value: "hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw"
    - name: "JENKINS_AGENT_WORKDIR"
    value: "/home/jenkins/agent"
    - name: "JENKINS_URL"
    value: "http://jenkins/"
    image: "192.168.0.107/jenkins/jnlp-slave:3.35-5-alpine"
    imagePullPolicy: "IfNotPresent"
    name: "jnlp"
    resources:
    limits: {}
    requests: {}
    securityContext:
    privileged: false
    tty: false
    volumeMounts:
    - mountPath: "/home/jenkins/agent"
    name: "workspace-volume"
    readOnly: false
    nodeSelector:
    beta.kubernetes.io/os: "linux"
    restartPolicy: "Never"
    securityContext: {}
    volumes:
    - emptyDir:
    medium: ""
    name: "workspace-volume" Running on hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw in /home/jenkins/agent/workspace/hello-pipeline-selfpodtemplate
    [Pipeline] {
    [Pipeline] container
    [Pipeline] {
    [Pipeline] sh
    + hostname
    hello-pipeline-selfpodtemplate-6-ch89k-0v48m-xxsqw
    [Pipeline] }
    [Pipeline] // container
    [Pipeline] }
    [Pipeline] // node
    [Pipeline] }
    [Pipeline] // podTemplate
    [Pipeline] End of Pipeline
    Finished: SUCCESS
    • 可以看到jnlp对应的镜像名称变成我们指定的镜像
    • pod template 的内容默认是打印出来的,可以通过配置不显示 podTemplate(showRawYaml:false,...)

遇到问题

  1. 追加kubernetes service account凭据时报错

    HTTP ERROR 403 No valid crumb was included in the request
    
    

    简单解决方法,在Configure Global Security配置页面去掉CSRF Protection

Kubernetes CI/CD(2)的更多相关文章

  1. Kubernetes CI/CD(1)

    本文通过在kubernetes上启动Jenkins服务,并将宿主机上的docker.docker.sock挂载到Jenkins容器中,实现在Jenkins容器中直接打镜像的形式实现CI功能. Kube ...

  2. Kubernetes学习之路(二)之ETCD集群二进制部署

    ETCD集群部署 所有持久化的状态信息以KV的形式存储在ETCD中.类似zookeeper,提供分布式协调服务.之所以说kubenetes各个组件是无状态的,就是因为其中把数据都存放在ETCD中.由于 ...

  3. Kubernetes学习之路(十)之资源清单定义

    一.Kubernetes常用资源 以下列举的内容都是 kubernetes 中的 Object,这些对象都可以在 yaml 文件中作为一种 API 类型来配置. 类别 名称 工作负载型资源对象 Pod ...

  4. Kubernetes 部署Web UI (Dashboard)

    Kubernetes 部署Web UI (Dashboard) 项目下载地址:https://github.com/kubernetes/kubernetes/tree/master/cluster/ ...

  5. CI框架浅析(二)

    该文延续上篇文章: CI框架浅析(一) 在CI框架的核心库中,CodeIgniter.php负责加载所有需要的类库,第一个加载的是公共库 core/Common.php Common.php 负责加载 ...

  6. kubernetes集群搭建(2):docker私有仓库

    kubernetes集群搭建(1):环境准备 中各节点已经安装好了docker,请确认docker已启动并正常运行 1.通过命令启动私库 docker run -d -p 5000:5000 --pr ...

  7. 浅谈 kubernetes service 那些事(上篇)

    一.问题 首先,我们思考这样一个问题: 访问k8s集群中的pod, 客户端需要知道pod地址,需要感知pod的状态.那如何获取各个pod的地址?若某一node上的pod故障,客户端如何感知? 二.k8 ...

  8. 浅谈 kubernetes service 那些事 (下篇)

    欢迎访问网易云社区,了解更多网易技术产品运营经验. 五.K8s 1.8 新特性--ipvs ipvs与iptables的性能差异 随着服务的数量增长,IPTables 规则则会成倍增长,这样带来的问题 ...

  9. Kubernetes学习之路(一)之概念和架构解析和证书创建和分发

    1.Kubernetes的重要概念 转自:CloudMan老师公众号<每天5分钟玩转Kubernetes>https://item.jd.com/26225745440.html Clus ...

随机推荐

  1. c#数字图像处理(十)图像缩放

    图像几何变换(缩放.旋转)中的常用的插值算法 在图像几何变换的过程中,常用的插值方法有最邻近插值(近邻取样法).双线性内插值和三次卷积法. 最邻近插值: 这是一种最为简单的插值方法,在图像中最小的单位 ...

  2. http轮询,长轮询

    轮询,长轮询 轮询 轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接. 优点:后端程序编写比较容易. 缺点:请求中有大半是无用,浪费带宽和服务器资源. 实例:适于小 ...

  3. Docker基础内容之镜像构建

    前言 Docker可以通过读取Dockerfile中的指令来自动构建图像.Dockerfile是一个文本文档,包含用户可以在命令行上调用的所有命令来组装一个图像.使用docker构建用户可以创建一个自 ...

  4. Spring实战:第一个spring mvc项目

    我自己看的pdf书中有几个小错误导致项目一直起不来,具体错误是: 此处的名称不一致导致的,此外对于映射@RequestMapping("/"),需要删除创建框架时自带的index. ...

  5. kubernetes secret 和 serviceaccount删除

    背景 今天通过配置创建了一个serviceaccounts和secret,后面由于某种原因想再次创建发现已存在一个serviceaccounts和rolebindings.rbac.authoriza ...

  6. 三、Django学习之单表查询接口

    查询接口 all() 查询所有结果,结果是queryset类型 filter(**kwargs) and条件关系:参数用逗号分割表示and关系 models.Student.objects.filte ...

  7. 编写一个函数,输入n为偶数时,调用方法求1/2+1/4+...+1/n,当输入n为奇数时,调用函数1/1+1/3+...+1/n

    需求:编写一个函数,输入n为偶数时,调用方法求1/2+1/4+...+1/n,当输入n为奇数时,调用函数1/1+1/3+...+1/n package com.Summer_0511.cn; impo ...

  8. 死磕dtd(1)

    看到安卓开发里大量的xml文件和layout里的Android UI开始复习一下xml xml的校验规则依据dtd dtd里面大小写敏感.....查找了好久才发现这个问题 <?xml versi ...

  9. Java并发读书笔记:线程安全与互斥同步

    目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...

  10. 再谈typedef(重点为函数指针)

    有种很方便的写法. typedef int *p: p pointer: 这时直接把pointer带入原式中,取代p然后去掉typedef,得到的结果就是int * pointer: 哈哈,这样直接替 ...