背景

由于公司要做容器平台的升级旧平台需要迁移到新平台,所以觉得有那个时间不如迁移到部门的k8s集群中,因此就需要有一套相对完善的cicd。这里我只是想做一个v1.0版本,后期逐步的进化。

golang项目

这里只是演示发布一个最简单的服务器。

package main

import (
	"fmt"
	"log"
	"net/http"
)

// 处理主页请求
func index(w http.ResponseWriter, r *http.Request) {
	// 向客户端写入内容
	fmt.Fprintf(w, "mother  fucker!")
}

func main() {
	http.HandleFunc("/", index)              //设置访问的路由
	err := http.ListenAndServe(":9090", nil) //设置监听的端口
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

Dockerfile
FROM golang:1.14-alpine AS development
WORKDIR $GOPATH/src
COPY . .
RUN go build -o golang-demo ./main.go

FROM alpine:latest AS production
WORKDIR /root/
COPY --from=development /go/src/golang-demo .
EXPOSE 9090
ENTRYPOINT ["./golang-demo"]

CI

ci无非就是从 gitlab拉取代码,然后进行测试,编译,打包,然后将镜像推送到镜像仓库,最后修改helm的values.yaml。

所以这里就需要gitlab的账号密码,harbor的账号密码。

gitlab-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: gitlab-secret
  namespace: golang-demo-pipeline
  annotations:
    tekton.dev/git-0: 你的代码仓库
type: kubernetes.io/basic-auth
stringData:
  username: 账号
  password: 密码

harbor-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: harbor-secret
  namespace: golang-demo-pipeline
  annotations:
    tekton.dev/docker-0: https://镜像仓库地址
type: kubernetes.io/basic-auth
stringData:
  username: 用户名
  password: 密码

sa.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: cicd
  namespace: golang-demo-pipeline
secrets:
  - name: gitlab-secret
  - name: harbor-secret

由于tekton里面git 和 image都是resource,因此需要创建git-resource 和 image-resource
git-resource.yaml

apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: gitlab-pipeline-resource
  namespace: golang-demo-pipeline
spec:
  type: git
  params:
    - name: revision
      value: main
    - name: url
      value: 代码库地址

image-resource.yaml

apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: harbor-image
  namespace: golang-demo-pipeline
spec:
  type: image
  params:
    - name: url
      value: 镜像仓库地址/项目名/golang-demo-pipeline 

好了、前期工作准备好了开始写task
task-test.yaml
这里我只是echo了一句,后面的版本再完善

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: test
  namespace: golang-demo-pipeline
spec:
  resources:
    inputs:
      - name: repo
        type: git
  steps:
    - name: run-test
      image: golang:1.14-alpine
      workingDir: /workspace/repo
      command: ['echo']
      args: ['this is a test task']

task-generate-build-id.yaml
每次进行docker build -tag都需要有一个版本号,这里是根据基础版本+时间生成版本号

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: generate-build-id
  namespace: golang-demo-pipeline
spec:
  description: >-
    Given a base version, this task generates a unique build id by appending
    the base-version to the current timestamp.
  params:
    - name: base-version
      description: Base product version
      type: string
      default: "1.0"
  results:
    - name: timestamp
      description: Current timestamp
    - name: build-id
      description: ID of the current build
  steps:
    - name: get-timestamp
      image: bash:5.0.18
      script: |
        #!/usr/bin/env bash
        ts=`date "+%Y%m%d-%H%M%S"`
        echo "Current Timestamp: ${ts}"
        echo ${ts} | tr -d "\n" | tee $(results.timestamp.path)
    - name: get-buildid
      image: bash:5.0.18
      script: |
        #!/usr/bin/env bash
        ts=`cat $(results.timestamp.path)`
        buildId=$(inputs.params.base-version)-${ts}
        echo ${buildId} | tr -d "\n" | tee $(results.build-id.path)

task-build-and-push.yaml

我这里是最近单的docker in docker的方式打包镜像,正确的方式可以使用sidecar的方式或者kaniko的方式后面去实现,这里直接build完推送到镜像仓库。

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-and-push
  namespace: golang-demo-pipeline
spec:
  resources:
    inputs: # 定义输入资源
      - name: repo #输入资源,就是github的那个仓库
        type: git
    outputs: # 定义输出资源
      - name: builtImage # 输出镜像名字
        type: image
  params:
    - name: pathToDockerfile #指明 dockerfile 在仓库中的哪个位置
      type: string
      default: /workspace/repo/Dockerfile # repo资源的路径
      description: dockerfile path
    - name: pathToContext #指明 dockerfile 在仓库中的哪个位置
      type: string
      default: /workspace/repo  # repo资源的路径
      description: the build context used by docker daemon
    - name: imageTag
      type: string
      default: "v0.0.0"
      description: the docker image tag
  steps:
    - name: build-and-push
      image: docker:stable
      script: |
        #!/usr/bin/env sh
        docker login 镜像仓库地址
        docker build -t $(resources.outputs.builtImage.url):$(params.imageTag) -f $(params.pathToDockerfile) $(params.pathToContext)
        docker push $(resources.outputs.builtImage.url):$(params.imageTag)  # 这边的参数都是在 input 和 output 中定义的
      volumeMounts:
        - name: dockersock #将docker.sock文件挂载进来,使用宿主机docker daemon 构建镜像
          mountPath: /var/run/docker.sock
  volumes:
    - name: dockersock
      hostPath:
        path: /var/run/docker.sock

task-helm-values.yaml
我这里选择使用helm来部署应用,因此需要修改helm的values.yaml,主要就是修改镜像的版本号。先把helm chart clone下来,然后修改image.tag最后push回去,就这么简单

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: helm-values
  namespace: golang-demo-pipeline
spec:
  params:
    - name: git_url
      description: Git repository containing manifest files to update
    - name: git_email
      default: 你的git email
    - name: git_name
      default: 你的 git username
    - name: git_manifest_dir
      description: Manifests files dir
    - name: tool_image
      default: cnych/helm-kubectl-curl-git-jq-yq
    - name: image-tag
      description: Deploy docker image tag
  steps:
    - name: git-push
      image: $(params.tool_image)
      env:
        - name: GIT_USERNAME
          valueFrom:
            secretKeyRef:
              name: gitlab-secret
              key: username
              optional: true
        - name: GIT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: gitlab-secret
              key: password
              optional: true
      command: ["/bin/bash"]
      args:
        - -c
        - |
          set -eu
          echo Load environment variables from previous steps
          #source /workspace/env-config
          git config --global user.email "$(params.git_email)"
          git config --global user.name "$(params.git_name)"
          git clone --branch master --depth 1 http://${GIT_USERNAME}:${GIT_PASSWORD}@$(params.git_url) repo
          cd "repo/$(params.git_manifest_dir)"
          ls -l
          echo old value:
          cat values.yaml | yq r - 'image.tag'
          echo replacing with new value:
          echo $(params.image-tag)
          yq w --inplace values.yaml 'image.tag' "$(params.image-tag)"
          echo verifying new value
          yq r values.yaml 'image.tag'
          if ! git diff-index --quiet HEAD --; then
            git status
            git add .
            git commit -m "helm values updated by tekton pipeline in change-manifests task"
            git push
          else
              echo "no changes, git repository is up to date"
          fi

有了task后,要开始写pipeline和pipeline run了,把task组合起来并运行起来
pipeline.yaml

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: golang-demo
  namespace: golang-demo-pipeline
spec:
  resources:
  - name: repo
    type: git
  - name: builtImage
    type: builtImage
  params:
  - name: image-tag
    type: string
  - name: git_url
    type: string
  - name: git_manifest_dir
    type: string
  tasks:
  - name: test
    taskRef:
      name: test
    resources:
      inputs:
      - name: repo      # Task 输入名称
        resource: repo  # Pipeline 资源名称
  - name: get-build-id
    taskRef:
      name: generate-build-id
    params:
    - name: base-version
      value: $(params.image-tag)
  - name: build-and-push
    taskRef:
      name: build-and-push
    runAfter:
    - test              # 测试任务执行之后
    resources:
      inputs:
      - name: repo      # Task 输入名称
        resource: repo  # Pipeline 资源名称
      outputs:
      - name: builtImage
        resource: builtImage
    params:
    - name: imageTag
      value: "$(tasks.get-build-id.results.build-id)" # 使用generate-build-id生成tag
  - name: helm-values
    taskRef:
      name: helm-values
    runAfter:
    - build-and-push
    params:
    - name: git_url
      value: $(params.git_url)
    - name: git_manifest_dir
      value: $(params.git_manifest_dir)
    - name: image-tag
      value:  "$(tasks.get-build-id.results.build-id)"  # 使用generate-build-id生成tag

pipeline-run.yaml

apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
  name: golang-demo
  namespace: golang-demo-pipeline
spec:
  serviceAccountName: cicd
  pipelineRef:
    name: golang-demo
  resources:
  - name: repo
    resourceRef:
      name: gitlab-pipeline-resource
  - name: builtImage
    resourceRef:
      name: harbor-image
  params:
  - name: image-tag
    value: "v0.1.0"
  - name: git_url
    value: 你的helm git仓库地址
  - name: git_manifest_dir
    value: helm-golang-demo

最后执行以下apply -f . 如果一些正常可以从tekton ui看到,当然如果不成功根据错误自己改一下,也会加深你的记忆。
在这里插入图片描述

对tekton的理解

我把task比喻成一个个function

func taskA(paramA string){}
func taskB(paramB string){}
func taskC(paramC string){}

pipeline就是一个调用A、B、C的函数

func pipeline(paramA,paramB,paramC string){
	taskA(paramA)
	taskC(paramB)
	taskC(paramC)
}

pipelineRun 就是真正的调用者来调用pipeline

pipeline("a","b","c")

如下图 task需要参数git_manifest_dir
在这里插入图片描述
pipeline调用tasl也需要参数git_manifest_dir
在这里插入图片描述
pipelineRun作为真正的调用者来传递参数值
在这里插入图片描述
是不是有点感觉。。。

CD

argocd的原理就是再k8s里面有一个控制器一直监听git的变化,然后对比集群中实际的状态,所以你也要先有一个git仓库,这里就是helm的仓库。
在这里插入图片描述

有了仓库就开始建立应用了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果再一切顺利,你应该看到如下画面
在这里插入图片描述

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐