赞
踩
由于公司要做容器平台的升级旧平台需要迁移到新平台,所以觉得有那个时间不如迁移到部门的k8s集群中,因此就需要有一套相对完善的cicd。这里我只是想做一个v1.0版本,后期逐步的进化。
这里只是演示发布一个最简单的服务器。
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) } }
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无非就是从 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)
我这里是最近单的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看到,当然如果不成功根据错误自己改一下,也会加深你的记忆。
我把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作为真正的调用者来传递参数值
是不是有点感觉。。。
argocd的原理就是再k8s里面有一个控制器一直监听git的变化,然后对比集群中实际的状态,所以你也要先有一个git仓库,这里就是helm的仓库。
有了仓库就开始建立应用了
如果再一切顺利,你应该看到如下画面
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。