Golang交付至Kubernetes

0、前言

如果還不知道kubernetes下如何實現jenkins slave可以參考我的另一個博文Kubernetes Jenkins動態創建Slave

1、Go服務構建

我們需要通過自定義鏡像底包,能夠讓該底包擁有啟動go程式的基本功能和日誌重定向輸出到指定目錄下,方便日誌收集,編輯完相關文件後,我們需要通過docker命令去構建鏡像,構建完畢後將其推送到harbor base倉庫,該名為base的倉庫需要手動去harbor上創建。

1.1、製作Go服務鏡像底包

1.編寫Dockerfile以及相關的啟動腳本

  • Dcokerfile
FROM alpine
USER root
RUN apk add tzdata && /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&\
    echo 'Asia/Shanghai' >/etc/timezone &&\
    mkdir /opt/logs 
WORKDIR /opt/project_dir/
ADD entrypoint.sh /entrypoint.sh
CMD ["/entrypoint.sh"]
  • entrypoint.sh
#!/bin/sh
APP_NAME=${APP_NAME:-"app"}
exec ${APP_NAME} >> /opt/logs/stdout.log 2>&1

APP_NAME:啟動的二進位文件名,這個可以手動傳入或者默認為名為app

2.構建鏡像並上傳到harbor倉庫

$ docker build ./ -t harbor.od.com/base/go-run:1.14.2-1
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM alpine
 ---> a187dde48cd2
Step 2/6 : USER root
 ---> Using cache
 ---> f0673624a0e0
Step 3/6 : RUN apk add tzdata && /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&    echo 'Asia/Shanghai' >/etc/timezone &&    mkdir /opt/logs
 ---> Using cache
 ---> e91f2e8a4919
Step 4/6 : WORKDIR /opt/project_dir/
 ---> Using cache
 ---> 6afa8e9ddd43
Step 5/6 : ADD entrypoint.sh /entrypoint.sh
 ---> Using cache
 ---> 7a7efe533a5f
Step 6/6 : CMD ["/entrypoint.sh"]
 ---> Using cache
 ---> 17d85fd60af3
Successfully built 17d85fd60af3
Successfully tagged harbor.od.com/base/go-run:1.14.2-1

$ docker push harbor.od.com/base/go-run:1.14.2-1
The push refers to repository [harbor.od.com/base/go-run]
20c042b1ca31: Mounted from base/go-test 
98e20bd596b3: Mounted from base/go-test 
f4ee0579d5d8: Mounted from base/go-test 
beee9f30bc1f: Mounted from base/go-test 
1.14.2-1: digest: sha256:49dda47c11a3148fdfd60fe4850ada128e6550fb33c1a6284caadf0e6ea8016d size: 1153

1.2、製作slave基礎鏡像底包

我們是基於jenkins slave方式去構建項目,並且使用pipeline來實現流程化,我們都知道slave運行的是一個pod,那麼pod里可以有多個容器,而這些一個一個的容器就是執行我們對應操作的環境,所以我們需要通過製作一些鏡像來滿足我們的需求。

鏡像需求:
1、編譯golang應用:golang:v1.14.2
2、打包鏡像並推送到harbor:

1.2.1、Golang鏡像

該鏡像主要用於構建golang應用,我們是在基於golang v1.14.2環境下開發的golang程式,所以這裡鏡像版本就選擇:golang:v1.14.2

$ docker pull golang:v1.14.2
$ docker tag a1c86c0786 harbor.od.com/public/golang:v1.14.2
$ docker push harbor.od.com/public/golang:v1.14.2

1.2.2、Docker鏡像

該鏡像主要用於將編譯好的golang項目打包成鏡像並推送到harbor,但需要訂製化一下鏡像,需要將一台已經實現docker login 登錄到harbor倉庫所生成的配置文件,路徑為:/root/.docker/config.json,與原始Docker鏡像一起打包生成新的Docker鏡像並推送到本地倉庫。
1.準備鏡像文件

$ docker pull docker:19.03
$ docker tag e036013d6d10 harbor.od.com/public/docker:v19.03
$ docker push harbor.od.com/public/docker:v19.03

2.準備Dockerfile和config.json

  • Dockerfile
FROM harbor.od.com/public/docker:v19.03
USER root
ADD config.json /root/.docker/config.json

3.將/root/.docker/config.json文件拷貝到Dockerfile目錄下

{
	"auths": {
		"harbor.od.com": {
			"auth": "YWRtaW46SGFyYm9yMTIzNDU="
		}
	},
	"HttpHeaders": {
		"User-Agent": "Docker-Client/19.03.6 (linux)"
	}
}

4.製作並推送鏡像

$ docker build ./ -t harbor.od.com/public/docker:v19.03
$ docker push  harbor.od.com/public/docker:v19.0

2、Jenkins流水線

2.1、創建流水線

1.添加參數化構建

- Choice Parameterr:app_project
  Value:zj-skyquery
  Describe:項目名必須和git倉庫名一致

- Choice Parameterr:image_name
  Value:app/skyquery
  Describe:鏡像名稱:倉庫名/鏡像名

- String Parameterr:git_ver
  Value:zmaster
  Describe:Git倉庫分支或Commit ID

- String Parameterr:add_tag
  Value:
  Describe:打包鏡像tag,一般為日期時間

- Choice Parameterr:git_repo
  Value://gitee.com/jasonminghao/zj-skyquery.git
  Describe:程式碼倉庫地址

- Choice Parameterr:base_image
  Value:base/go-run:1.14.2-1
  Describe:基礎鏡像底包

2.pipeline程式碼

podTemplate(cloud:'kubernetes',containers: [
    containerTemplate(
       name: 'go-build', 
       envVars: [
         envVar(key: 'GO111MODULE', value: 'on'), 
         envVar(key: 'GOPROXY', value: '//goproxy.io'),
         envVar(key: 'CGO_ENABLED', value: '0'),
         envVar(key: 'GOOS', value: 'linux')
       ],
       image: 'harbor.od.com/infra/golang:v1.14.2', 
       ttyEnabled: true,
       command: 'cat'),
    containerTemplate(
       name: 'docker',
      ttyEnabled: true,
      image: 'harbor.od.com/public/docker:v19.03'),
    ],
    volumes: [
       nfsVolume(mountPath: '/go/pkg/mod', readOnly: false, serverAddress: 'hdss7-200.host.com', serverPath: '/data/nfs-volume/go-pkg/'),
       hostPathVolume(hostPath: '/run/docker.sock', mountPath: '/run/docker.sock')
            ]
 ){
    node(POD_LABEL) {
        stage('Get a Go project') {
        // 從git倉庫拉取程式碼
        checkout([$class: 'GitSCM', branches: [[name: "${params.git_ver}"]], browser: [$class: 'GitLab', repoUrl: ''], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'git', name: 'git', refspec: 'refs/changes/*:refs/changes/*', url: "${params.git_repo}"]]])

            container('go-build') {
                stage('Build a go project') {
                  // 將go應用構建名為app的可執行二進位文件
                  sh "go build -o app"
                }
            }
        }
        stage('Docker build') {
            container('docker') {
                stage('create dir'){
                  sh "mkdir -p /tmp/${params.app_project}/project_dir && mv ./app /tmp/${params.app_project}/project_dir"
                stage('build docker iamge'){
                 sh """
                   echo "FROM harbor.od.com/${params.base_image}" >/tmp/${params.app_project}/Dockerfile
                   echo "ADD ./project_dir /opt/project_dir" >>/tmp/${params.app_project}/Dockerfile
                  """
                  sh "cd /tmp/${params.app_project}/ && pwd && docker build ./ -t harbor.od.com/${params.image_name}:${params.git_ver}_${params.add_tag} && docker push harbor.od.com/${params.image_name}:${params.git_ver}_${params.add_tag} "                              

                 }
              }
            }
        }
    }
}

我們在podTemplate里需要導入以下幾個變數(pipeline已通過env實現)

// 如果運行的golang鏡像沒有以下變數會導致無法執行go build以及可執行文件無法在linux上運行
GO111MODULE =  on
GOPROXY = "//goproxy.io"
CGO_ENABLED = 0 
GOOS = linux 

之前我們遇到過一個問題,golang應用構建成功,也有二進位文件,但是我們將其通過deployment交付到k8s的時候容器會啟動失敗,並且報錯是 not found,但是把編譯的文件拿到linux上可正常運行,就是在docker里運行失敗,最後我們去網上查了一些資料,通過增加CGO_ENABLED=0 參數重新編譯即可解決該問題。

2.2、流水線構建

1.填寫參數構建

2.構建後的結果

3、golang資源配置清單

3.1、準備資源配置清單

1.deployment

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: sky-query
  namespace: app
  labels: 
    name: sky-query
spec:
  replicas: 1
  selector:
    matchLabels: 
      name: sky-query
  template:
    metadata:
      labels: 
        app: sky-query
        name: sky-query
    spec:
      containers:
      - name: sky-query
        image: harbor.od.com/app/skyquery:master_20200424_1725
        ports:
        - containerPort: 80
          protocol: TCP
        imagePullPolicy: IfNotPresent
      imagePullSecrets:
      - name: harbor
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
      securityContext: 
        runAsUser: 0
      schedulerName: default-scheduler
  strategy:
    type: RollingUpdate
    rollingUpdate: 
      maxUnavailable: 1
      maxSurge: 1
  revisionHistoryLimit: 7
  progressDeadlineSeconds: 600

2.svc

kind: Service
apiVersion: v1
metadata: 
  name: sky-query
  namespace: app
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  selector: 
    name: sky-query

3.ingress

kind: Ingress
apiVersion: extensions/v1beta1
metadata: 
  name: sky-query
  namespace: app
spec:
  rules:
  - host: sk-query.od.com
    http:
      paths:
      - path: /
        backend: 
          serviceName: sky-query
          servicePort: 80

3.2、應用資源配置清單

$ kubectl  apply -f ./
deployment.extensions/sky-query created
ingress.extensions/sky-query created
service/sky-query created

查看pod運行狀態

$ kubectl get pods -n app
NAME                         READY   STATUS    RESTARTS   AGE
sky-query-55474c768d-x8qj5   1/1     Running   0          2m10s

3.3、瀏覽器訪問golang業務

我們是通過ingress實現域名訪問golang業務,以為是實驗環境,我部署了內部自建dns,那我就直接將A記錄添加到dns中,那麼各位如果沒有自建dns,直接將解析添加到hosts文件中。

1.域名解析

$ vim /var/named/od.com.zone 
sk-query           A    10.4.7.10

$ systemctl restart named

2.瀏覽器訪問