• Kubernetes(十四) CI/CD(2)


    本章节通过在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

  • 相关阅读:
    sql server该账户当前被锁定,所以用户'sa'登录失败。系统管理员无法将该账户解锁。(Microsoft SQL Server,错误:18486)
    windows server常用操作
    sql server2005直接会话运行成功,但在作业执行报错
    sql server xp_readerrorlog引起的CPU爆满100%
    sql server错误日志
    (13)python网络编程,python Socket
    tcp/ip
    (12)python异常处理,python中的 try except
    典型分布式系统分析:Dynamo
    c++ set与unordered set的区别
  • 原文地址:https://www.cnblogs.com/gaofeng-henu/p/12345182.html
Copyright © 2020-2023  润新知