主頁 > 軟體設計 > 在容器上構建持續部署及最佳實踐初探

在容器上構建持續部署及最佳實踐初探

2020-09-15 00:23:23 軟體設計

要想理解持續集成和持續部署,先要了解它的部分組成,以及各個組成部分之間的關系,下面這張圖是我見過的最簡潔、清晰的持續部署和集成的關系圖,

file

圖片來源

持續部署:

如圖所示,開發的流程是這樣的:
程式員從原始碼庫(Source Control)中下載源代碼,撰寫程式,完成后提交代碼到原始碼庫,持續集成(Continuous Integration)工具從原始碼庫中下載源代碼,編譯源代碼,然后提交到運行庫(Repository),然后持續交付(Continuous Delivery)工具從運行庫(Repository)中下載代碼,生成發布版本,并發布到不同的運行環境(例如DEV,QA,UAT, PROD),

圖中,左邊的部分是持續集成,它主要跟開發和程式員有關;右邊的部分是持續部署,它主要跟測驗和運維有關,持續交付(Continuous Delivery)又叫持續部署(Continuous Deployment),它們如果細分的話還是有一點區別的,但我們這里不分得那么細,統稱為持續部署,本文側重講解持續部署,

持續集成和部署有下面幾個主要參與者:

  • 源代碼庫:負責存盤源代碼,常用的有Git和SVN,
  • 持續集成與部署工具:負責自動編譯和打包以及把可運行程式存盤到可運行庫,比較流行的有Jenkins,GitLab,Travis CI,CircleCI 等
  • 庫管理器(Repository Manager):也就是圖中的Repository,我們又叫運行庫,負責管理程式組件,最常用的是Nexus,它是一個私有庫,它的作用是管理程式組件,

庫管理器有兩個職能:

  • 管理第三方庫:應用程式常常要用到很多第三方庫,并且不同的技術堆疊需要的庫不同,它們經常是存放在第三方公共庫里,管理起來不是很方便,一般公司會建立一個私有管理庫,來集中統一管理各種第三方軟體,例如它既可以做為Maven庫(Java),也可以做為鏡像庫(Docker),還可以做為NPM庫(JavaScript),來保證公司軟體的規范性,
  • 管理內部程式的交付:所有公司在各種環境(例如DEV,QA,UAT, PROD)發布的程式都由它來管理,并賦予統一的版本號,這樣任何交付都有據可查,同時便利于程式回滾,

持續部署步驟:

各個公司對持續部署(Continuous Deployment)的要求不同,它的步驟也不相同,但主要包括下面幾個步驟:

  • 下載原始碼:從源代碼庫(例如github)中下載源代碼,
  • 編譯代碼:編譯語言都需要有這一步
  • 測驗:對程式進行測驗,
  • 生成鏡像:這里包含兩個步驟,一個是創建鏡像,另一個是存盤鏡像到鏡像庫,
  • 部署鏡像: 把生成的鏡像部署到容器上

上面的流程是廣義的持續部署流程,狹義的流程是從庫管理器中檢索可運行程式,這樣就省去了下載原始碼和編譯代碼環節,改由直接從庫管理器中下載可執行程式,但由于并不是每個公司都有單獨的庫管理器,這里就采用了廣義的持續部署流程,這樣對每個公司都適用,

持續部署實體:

下面我們通過一個具體的實體來展示如何完成持續部署,我們用Jenkins來做為持續部署工具,用它部署一個Go程式到k8s環境,

我們的流程基本是上面講的狹義流程,但由于沒有Nexus,我們稍微變通了一下,改由從原始碼庫直接下載源程式,步驟如下:

  • 下載原始碼:從github下載源代碼到Jenkins的運行環境
  • 測驗:這一步暫時沒有實際內容
  • 生成鏡像:創建鏡像,并上傳到Docker hub,
  • 部署鏡像: 將生成的鏡像部署到k8s

在創建Jenkins專案之前,先要做些準備作業:

建立Docker Hub賬戶

需要在Docker Hub上創建賬戶和鏡像庫,這樣才能上傳鏡像,具體程序這里就不詳細講解了,請查閱相關資料,

在Jenkins上創建憑證(Credentials)

需要設定訪問Docker hub的用戶和口令,以后在Jenkins腳本里可以通過變數的方式進行參考,這樣口令就不會以明碼的方式出現在程式里,

用管理員賬戶登錄 Jenkins主頁面后,找到 Manage Jenkins-》Credentials-》System -》Global Credentials -》Add Credentials,如下圖所示輸入你的Docker Hub的用戶名和口令,“ID”是后面你要在腳本里參考的,

file

創建預裝Docker和k8s的Jenkins鏡像

Jenkins的默認容器里面沒有Docker和k8s,因此我們需要在Jenkins鏡像的基礎上重新創建新的鏡像,后面還會詳細講解,
下面是鏡像檔案(Dockerfile-modified-jenkins)

FROM jenkins/jenkins:lts

USER root

ENV DOCKERVERSION=19.03.4

RUN curl -fsSLO https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKERVERSION}.tgz \
  && tar xzvf docker-${DOCKERVERSION}.tgz --strip 1 \
                 -C /usr/local/bin docker/docker \
  && rm docker-${DOCKERVERSION}.tgz

RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl \
    && chmod +x ./kubectl \
    && mv ./kubectl /usr/local/bin/kubectl

上面的鏡像在“jenkins/jenkins:lts”的基礎上又安裝了Docker和kubectl,這樣就支持這兩個軟體了,鏡像里使用的是docker的19.03.4版本,這里裝的只是“Docker CLI”,沒有Docker引擎,用的時候還是要把虛擬機的卷掛載到容器上,使用虛機的Docker引擎,因此最好保證容器里的Docker版本和虛機的Docker版本一致,

使用如下命令查看Docker版本:

vagrant@ubuntu-xenial:/$ docker version

詳細情況請參見Configure a CI/CD pipeline with Jenkins on Kubernetes

準備作業已經完成,現在要正式創建Jenkins專案:

Jenkins腳本:

專案的創建是在Jenkins的主頁上來完成,它的名字是“jenkins-k8sdemo”,它的最主要部分是腳本代碼,它也跟Go程式存放在相同的原始碼庫中,檔案的名字也是“jenkins-k8sdemo”,專案的腳本頁面如下圖所示,

file

如果你不熟悉安裝和創建Jenkins專案,請參閱在k8s上安裝Jenkins及常見問題

下面就是jenkins-k8sdemo腳本檔案:

def POD_LABEL = "k8sdemopod-${UUID.randomUUID().toString()}"
podTemplate(label: POD_LABEL, cloud: 'kubernetes', containers: [
    containerTemplate(name: 'modified-jenkins', image: 'jfeng45/modified-jenkins:1.0', ttyEnabled: true, command: 'cat')
  ],
  volumes: [
     hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
  ]) {

    node(POD_LABEL) {
       def kubBackendDirectory = "/script/kubernetes/backend"
       stage('Checkout') {
            container('modified-jenkins') {
                sh 'echo get source from github'
                git 'https://github.com/jfeng45/k8sdemo'
            }
          }
       stage('Build image') {
            def imageName = "jfeng45/jenkins-k8sdemo:${env.BUILD_NUMBER}"
            def dockerDirectory = "${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"
             container('modified-jenkins') {
               withCredentials([[$class: 'UsernamePasswordMultiBinding',
                 credentialsId: 'dockerhub',
                 usernameVariable: 'DOCKER_HUB_USER',
                 passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
                 sh """
                   docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD}
                   docker build -f ${WORKSPACE}${dockerDirectory} -t ${imageName} .
                   docker push ${imageName}
                   """
               }
             }
           }
       stage('Deploy') {
           container('modified-jenkins') {
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-service.yaml"
             }
       }
    }
}



我們逐段看一下代碼:

設定容器鏡像:

podTemplate(label: POD_LABEL, cloud: 'kubernetes', containers: [
    containerTemplate(name: 'modified-jenkins', image: 'jfeng45/modified-jenkins:1.0', ttyEnabled: true, command: 'cat')
  ],
  volumes: [
     hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock')
  ])

這里設定Jenkins子節點Pod的容器鏡像,用的是“jfeng45/modified-jenkins:1.0”,也就是我們在上個步驟創建的,所有的腳本里的步驟(stage)都用的是這個鏡像,“volumes:”用來掛載卷到Jenkins容器中,這樣Jenkins子節點就可以使用虛機的Docker引擎,

關于Jenkins腳本命令和設定掛載卷請參閱jenkinsci/kubernetes-plugin

創建鏡像:

下面的代碼生成Go程式的Docker鏡像檔案,這里我們沒有用Docker插件,而是直接呼叫Docker命令,它的好處后面會講到,它參考了我們前面設定的“Docker hub”的憑證去訪問Docker庫,在腳本里,我們先登錄到“Docker hub”,然后使用上一步從GitHub下載的源代碼來創建鏡像,最后上傳鏡像到“Docker hub”,其中“${WORKSPACE}”是Jenkins預定義變數,從GitHub下載的源代碼就存放在“${WORKSPACE}”里,

stage('Build image') {

            def imageName = "jfeng45/jenkins-k8sdemo:${env.BUILD_NUMBER}"
            def dockerDirectory = "${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"
             container('modified-jenkins') {
               withCredentials([[$class: 'UsernamePasswordMultiBinding',
                 credentialsId: 'dockerhub',
                 usernameVariable: 'DOCKER_HUB_USER',
                 passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
                 sh """
                   docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD}
                   docker build -f ${WORKSPACE}${dockerDirectory} -t ${imageName} .
                   docker push ${imageName}
                   """
               }
             }
           }

如果你想了解Jenkins命令詳情,請參閱Set Up a Jenkins CI/CD Pipeline with Kubernetes

我們這里并沒有重新生成Go程式的鏡像檔案,而是復用了以前就有的k8s創建Go程式的鏡像檔案,Go程式的鏡像檔案路徑是“\script\kubernetes\backend\docker\Dockerfile-k8sdemo-backend”,
它的代碼如下,后面還會講到這樣做的好處,

# vagrant@ubuntu-xenial:~/app/k8sdemo/script/kubernetes/backend$
# docker build -t k8sdemo-backend .

FROM golang:latest as builder

# Set the Current Working Directory inside the container
WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

COPY . .

WORKDIR /app/cmd

# Build the Go app
#RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main.exe

RUN go build -o main.exe

######## Start a new stage from scratch #######
FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/

RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2

# Copy the Pre-built binary file from the previous stage
COPY --from=builder /app/cmd/main.exe .

# Command to run the executable
# CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"
CMD 

關于Go鏡像檔案詳情,請參閱創建優化的Go鏡像檔案以及踩過的坑

部署鏡像:

下面部署Go程式到k8s上,這里也沒有用kubectl插件,而是直接用kubectl命令呼叫已經存在的k8s的部署和服務組態檔(檔案里會參考生成的Go鏡像),它的好處后面也會講到,

 stage('Deploy') {
           container('modified-jenkins') {
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"
               sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-service.yaml"
             }
       }

關于k8s的部署和服務組態檔詳情,請參閱把應用程式遷移到k8s需要修改什么?

為什么沒用Declarative?

用腳本來寫Pipeline有兩種方法,“Scripted Pipleline”和“Declarative Pipleline”,這里用的是第一種方法, “Declarative Pipleline”是新的方法,之所以沒用它,是因為開始用的是Declarative模式但沒調出來,然后就改用“Scripted Pipleline”,結果成功了,后來才發現設定Declarative的方法,特別是如何掛載卷,但看了一下,比起“Scripted Pipleline”要復雜不少,就偷了一下懶,沒有再改,

如果你想知道怎樣在Declarative模式下設定掛載卷,請參閱Jenkins Pipeline Kubernetes Agent shared Volumes

自動執行專案:

現在的Jenkins中的專案需要手動啟動,如果你需要自動啟動專案的話就要創建webhook,GitHub和dockerhub都支持webhook,在它們的頁面上都有設定選項,“webhook”是一個反向呼叫的URL,每當有新的代碼或鏡像提交時,GitHub和dockerhub都會呼叫這個URL,URL被設定成Jenkins的專案地址,這樣相關的專案就會自動啟動,

檢驗結果:

現在Jenkins的專案就完全配置好了,需要運行專案,檢驗結果,啟動專案后,
查看“Console Output”,下面是部分輸出(全部輸出太長,請看附錄),說明部署成功,

,,,
+ kubectl apply -f /home/jenkins/workspace/test1/script/kubernetes/backend/backend-deployment.yaml
deployment.apps/k8sdemo-backend-deployment created
[Pipeline] sh+ kubectl apply -f /home/jenkins/workspace/test1/script/kubernetes/backend/backend-service.yaml
service/k8sdemo-backend-service created
[Pipeline] }
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS

查看運行結果:
獲得Pod名字:

vagrant@ubuntu-xenial:/home$ kubectl get pod
NAME                                           READY   STATUS    RESTARTS   AGE
envar-demo                                     1/1     Running   15         32d
k8sdemo-backend-deployment-6b99dc6b8c-8kxt9    1/1     Running   0          50s
k8sdemo-database-deployment-578fc88c88-mm6x8   1/1     Running   9          20d
k8sdemo-jenkins-deployment-675dd574cb-r57sb    1/1     Running   0          2d23h

登錄Pod并運行程式:

vagrant@ubuntu-xenial:/home$ kubectl exec -ti k8sdemo-backend-deployment-6b99dc6b8c-8kxt9 -- /bin/sh
~ # ./main.exe
DEBU[0000] connect to database
DEBU[0000] dataSourceName:dbuser:dbuser@tcp(k8sdemo-database-service:3306)/service_config?charset=utf8
DEBU[0000] FindAll()
DEBU[0000] created=2019-10-21
DEBU[0000] find user:{1 Tony IT 2019-10-21}
DEBU[0000] find user list:[{1 Tony IT 2019-10-21}]
DEBU[0000] user lst:[{1 Tony IT 2019-10-21}]

結果正確,

Jenkins原理

實體部分已經結束,下面來探討最佳實踐,在這之前,先要搞清楚Jenkins的原理,

可執行命令

我一直有一個問題就是那些命令是Jenkins可以通過shell執行的?Jenkins和Docker、k8s不同,后者有自己的一套命令,只要把它們學會了就行了,而Jenkins是通過與別的系統集成來作業的,因此它的可執行命令與其他系統有關,這導致了你很難知道那些命令是可以執行的,那些不行,你需要弄懂它的原理,才能得到答案,當Jenkins執行腳本時,主節點會自動生成一個子節點(Docker容器),所有的Jenkins命令都是在這個容器里執行的,所以能執行的命令與容器密切相關,一般來講,你可以通過shell來運行Linux命令,那下面的問題就來了:

  1. 為什么我不能用Bash?

    因為你使用的子節點的容器可能使用的是精簡版的Linux,例如Alpine,它是沒有Bash的,

  2. 為什么我不能運行Docker命令或Kubectl?

    因為它的默認容器是jenkinsci/jnlp-slave,而它里面沒有預裝Docker或kubectl,你可以不使用默認容器,而是指定你自己的容器,并在其中預裝上述軟體,那么就可以執行這些命令了,

如何共享檔案

一個Jenkins專案通常要分成幾個步驟(stage)來完成,例如你下載的原始碼要在幾個步驟之間共享,那怎么共享呢?Jenkins為每個專案分配了一個WORKSPACE(磁盤空間), 里面存盤了所有從原始碼庫和其他地方下載的檔案,不同stage之間可以通過WORKSPACE來共享檔案,

關于WORKSPACE詳情,請參閱Jenkins Project Artifacts and Workspace

最佳實踐

要總結最佳實踐就要理解持續部署在整個開發流程中的作用和位置,它主要起一個串接各個環節的作用,而程式的部署是由k8s和Docker來完成的,因此程式部署的腳本也都在k8s中,并由k8s來維護,我們不想在Jenkins里再維護一套類似的腳本,因此最好的辦法是把Jenkins的腳本壓縮到最小,盡可能多地直接呼叫k8s的腳本,

另外能寫代碼就不要在頁面上配置,只有代碼是可以重復執行并保證穩定結果的,頁面配置不能移植,而且不能保證每次配置都產生一樣的結果,

盡量少使用插件

Jenkins有許多插件,基本上你想要完成什么功能都有相應的插件,例如你需要使用Docker功能,就有“Docker Pipeline”插件,你要使用k8s功能就有“kubectl”插件,但它會帶來很多的問題,

  • 第一,每個插件都有他自己的設定方式(一般要在Jenkins插件頁面進行設定),但這種設定是與其他持續部署工具不兼容的,如果以后你要遷移到其他持續部署工具,這些設定都需要廢棄,
  • 第二,每個插件都有自己的命令格式,因此你需要另外學習一套新的命令,
  • 第三,這些插件往往只支持部分功能,使你能做的事情受到了限制,

例如,你需要創建一個Docker鏡像檔案,命令如下,它將創建一個名為"jfeng45/jenkins-k8sdemo"的鏡像,鏡像的默認檔案是在專案的根目錄下的Dockerfile,

app = docker.build("jfeng45/jenkins-k8sdemo")

但創建Docker鏡像檔案命令有許多引數選項,例如,你的鏡像檔案名不是Dockerfile,并且目錄不是在專案根目錄下,應如何寫呢?這在以前的版本是不支持的,后來的版本支持了,但畢竟不太方便,還要學新的命令,最好的辦法是能直接使用Docker命令,這樣就完美的解決了上面說的三個問題,答案就在前面講的Jenkins原理里,其實絕大多數插件都是不需要的,你只要自己創建一個Jenkins子節點容器,并安裝相應的軟體就能圓滿解決,

下面是使用插件的腳本和不使用的對比,不使用的看起來更長,那時因為使用插件的腳本和Jenkins里的憑證設定有更好的集成,而不使用的腳本沒有,但除了這個小缺點,其他方面不使用的腳本都要遠遠優于使用插件的,

使用插件的腳本(用插件命令):

stage('Create Docker images') {
  container('docker') {
      app = docker.build("jfeng45/codedemo", "-f ${WORKSPACE}/script/kubernetes/backend/docker/Dockerfile-k8sdemo-test .")
      docker.withRegistry('', 'dockerhub') {
          // Push image and tag it with our build number for versioning purposes.
          app.push("${env.BUILD_NUMBER}")
      }
    }
  }

不使用插件的腳本(直接用Docker命令):

stage('Create a d ocker image') {
     def imageName = "jfeng45/codedemo:${env.BUILD_NUMBER}"
     def dockerDirectory = "${kubBackendDirectory}/docker/Dockerfile-k8sdemo-backend"
      container('modified-jenkins') {
        withCredentials([[$class: 'UsernamePasswordMultiBinding',
          credentialsId: 'dockerhub',
          usernameVariable: 'DOCKER_HUB_USER',
          passwordVariable: 'DOCKER_HUB_PASSWORD']]) {
          sh """
            docker login -u ${DOCKER_HUB_USER} -p ${DOCKER_HUB_PASSWORD}
            docker build -f ${WORKSPACE}${dockerDirectory} -t ${imageName} .
            docker push ${imageName}
            """
        }
      }
    }

盡量多使用k8s和Dcoker

例如我們要創建一個應用程式的鏡像,我們可以寫一個Docker檔案,并在Jenkins腳本里呼叫這個Docker檔案來創建,也可以寫一個Jenkins腳本,在腳本里來創建鏡像,比較好的方法是前者,因為Docker和k8s都是事實上的標準,移植起來很方便,

Jenkins腳本的代碼越少越好

如果你認同前面兩個原則,那么這一條就是順理成章的,原因也和上面是一樣的,

常見問題:

1.變數要放在雙引號里
Jenkins的腳本即可以使用單引號也可以使用雙引號,但如果你在引號里參考了變數,那么就要使用雙引號,

正確的命令:

sh "kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml"

錯誤的命令:

sh 'kubectl apply -f ${WORKSPACE}${kubBackendDirectory}/backend-deployment.yaml'

2.docker not found

如果Jenkins的容器里沒有Docker,但你又呼叫了Docker命令,那么“Console Output”里就會有如下錯誤:

+ docker inspect -f . k8sdemo-backend:latest
/var/jenkins_home/workspace/k8sdec@2@tmp/durable-01e26997/script.sh: 1:     /var/jenkins_home/workspace/k8sdec@2@tmp/durable-01e26997/script.sh: docker:     not found

3.Jenkins宕機了

在除錯Jenkins時,我新創建了一個鏡像檔案并上傳到“Docker hub”之后就發現Jenkins宕機了,檢查了Pod,發現了問題,k8s找不到Jenkins的鏡像檔案了(鏡像檔案從磁盤上消失了),因為Jenkins的部署檔案的設定是“imagePullPolicy: Never”,所以一旦鏡像沒有了,它不會自動重新下載,后來找到了原因,Vagrant的默認磁盤大小是10G,如果空間不夠,它會自動從磁盤上洗掉其他鏡像檔案,騰出空間,結果就把Jenkins的鏡像檔案給刪了,解決方案是擴充Vagrant的磁盤大小,

下面是修改之后的Vagrantfile,把磁盤空間改成了16G,

Vagrant.configure(2) do |config|
     ,,,
     config.vm.box = "ubuntu/xenial64"
     config.disksize.size = '16GB'
     ,,,
end

詳情請見How can I increase disk size on a Vagrant VM?

原始碼:

完整原始碼的github鏈接

下面是專案中與本文有關的部分:

file

索引

  1. Nexus Platform Overview
  2. Configure a CI/CD pipeline with Jenkins on Kubernetes
  3. 在k8s上安裝Jenkins及常見問題
  4. jenkinsci/kubernetes-plugin
  5. Jenkins Pipeline Kubernetes Agent shared Volumes
  6. Set Up a Jenkins CI/CD Pipeline with Kubernetes
  7. 創建優化的Go鏡像檔案以及踩過的坑
  8. 把應用程式遷移到k8s需要修改什么?
  9. Jenkins Pipeline Kubernetes Agent shared Volumes
  10. Jenkins Project Artifacts and Workspace
  11. How can I increase disk size on a Vagrant VM?

附錄:

下面是Jenkins專案運行后的完整的“Console Output”:

Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] podTemplate
[Pipeline] {
[Pipeline] node
Still waiting to schedule task
‘k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa-twb8s-k9pn3’ is offline
Agent k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa-twb8s-k9pn3 is provisioned from template Kubernetes Pod Template
Agent specification [Kubernetes Pod Template] (k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa): 
* [modified-jenkins] jfeng45/modified-jenkins:1.0

Running on k8sdemopod-030ed100-cb28-4770-b6de-c491970e5baa-twb8s-k9pn3 in /home/jenkins/workspace/jenkins-k8sdemo
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Checkout)
[Pipeline] container
[Pipeline] {
[Pipeline] sh
+ echo get source from github
get source from github
[Pipeline] git
No credentials specified
Cloning the remote Git repository
Cloning repository https://github.com/jfeng45/k8sdemo
 > git init /home/jenkins/workspace/jenkins-k8sdemo # timeout=10
Fetching upstream changes from https://github.com/jfeng45/k8sdemo
 > git --version # timeout=10
 > git fetch --tags --force --progress -- https://github.com/jfeng45/k8sdemo +refs/heads/*:refs/remotes/origin/*
 > git config remote.origin.url https://github.com/jfeng45/k8sdemo # timeout=10
 > git config --add remote.origin.fetch +refs/heads/*:refs/remotes/origin/* # timeout=10
 > git config remote.origin.url https://github.com/jfeng45/k8sdemo # timeout=10
Fetching upstream changes from https://github.com/jfeng45/k8sdemo
 > git fetch --tags --force --progress -- https://github.com/jfeng45/k8sdemo +refs/heads/*:refs/remotes/origin/*
Checking out Revision 90c57dcd8ff362d01631a54125129090b503364b (refs/remotes/origin/master)
 > git rev-parse refs/remotes/origin/master^{commit} # timeout=10
 > git rev-parse refs/remotes/origin/origin/master^{commit} # timeout=10
 > git config core.sparsecheckout # timeout=10
 > git checkout -f 90c57dcd8ff362d01631a54125129090b503364b
 > git branch -a -v --no-abbrev # timeout=10
 > git checkout -b master 90c57dcd8ff362d01631a54125129090b503364b
Commit message: "added jenkins continous deployment files"
[Pipeline] }
 > git rev-list --no-walk 90c57dcd8ff362d01631a54125129090b503364b # timeout=10
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Build image)
[Pipeline] container
[Pipeline] {
[Pipeline] withCredentials
Masking supported pattern matches of $DOCKER_HUB_USER or $DOCKER_HUB_PASSWORD
[Pipeline] {
[Pipeline] sh
+ docker login -u **** -p ****
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /home/jenkins/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
+ docker build -f /home/jenkins/workspace/jenkins-k8sdemo/script/kubernetes/backend/docker/Dockerfile-k8sdemo-backend -t ****/jenkins-k8sdemo:7 .
Sending build context to Docker daemon  218.6kB

Step 1/13 : FROM golang:latest as builder
 ---> dc7582e06f8e
Step 2/13 : WORKDIR /app
 ---> Running in c5770704333e
Removing intermediate container c5770704333e
 ---> 73445078c82d
Step 3/13 : COPY go.mod go.sum ./
 ---> 6762344c7bc8
Step 4/13 : RUN go mod download
 ---> Running in 56a1f253c3f5
go: finding github.com/davecgh/go-spew v1.1.1
go: finding github.com/go-sql-driver/mysql v1.4.1
go: finding github.com/konsorten/go-windows-terminal-sequences v1.0.1
go: finding github.com/pkg/errors v0.8.1
go: finding github.com/pmezard/go-difflib v1.0.0
go: finding github.com/sirupsen/logrus v1.4.2
go: finding github.com/stretchr/objx v0.1.1
go: finding github.com/stretchr/testify v1.2.2
go: finding golang.org/x/sys v0.0.0-20190422165155-953cdadca894
Removing intermediate container 56a1f253c3f5
 ---> 455ef98244eb
Step 5/13 : COPY . .
 ---> 092444c8a5ef
Step 6/13 : WORKDIR /app/cmd
 ---> Running in 558240a3dcb1
Removing intermediate container 558240a3dcb1
 ---> 044e01b8184b
Step 7/13 : RUN go build -o main.exe
 ---> Running in 648899ba522c
Removing intermediate container 648899ba522c
 ---> 69f6652bc706
Step 8/13 : FROM alpine:latest
 ---> 965ea09ff2eb
Step 9/13 : RUN apk --no-cache add ca-certificates
 ---> Using cache
 ---> a27265887a1e
Step 10/13 : WORKDIR /root/
 ---> Using cache
 ---> b9c048c97f07
Step 11/13 : RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
 ---> Using cache
 ---> 95a2b77e3e0a
Step 12/13 : COPY --from=builder /app/cmd/main.exe .
 ---> Using cache
 ---> c5dc6dfdf037
Step 13/13 : CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"
 ---> Using cache
 ---> b141558cb0f3
Successfully built b141558cb0f3
Successfully tagged ****/jenkins-k8sdemo:7
+ docker push ****/jenkins-k8sdemo:7
The push refers to repository [docker.io/****/jenkins-k8sdemo]
0e5809dd35f7: Preparing
8861feb71103: Preparing
5b63d4bd63b4: Preparing
77cae8ab23bf: Preparing
77cae8ab23bf: Mounted from ****/codedemo
8861feb71103: Mounted from ****/codedemo
5b63d4bd63b4: Mounted from ****/codedemo
0e5809dd35f7: Mounted from ****/codedemo
7: digest: sha256:95c780bb08793712cd2af668c9d4529e17c99e58dfb05ffe8df6a762f245ce10 size: 1156
[Pipeline] }
[Pipeline] // withCredentials
[Pipeline] }
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] stage
[Pipeline] { (Deploy)
[Pipeline] container
[Pipeline] {
[Pipeline] sh
+ kubectl apply -f /home/jenkins/workspace/jenkins-k8sdemo/script/kubernetes/backend/backend-deployment.yaml
deployment.apps/k8sdemo-backend-deployment created
[Pipeline] sh
+ kubectl apply -f /home/jenkins/workspace/jenkins-k8sdemo/script/kubernetes/backend/backend-service.yaml
service/k8sdemo-backend-service created
[Pipeline] }
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Finished: SUCCESS

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/39857.html

標籤:架構設計

上一篇:Nginx配置實體-反向代理實體:根據訪問的路徑跳轉到不同埠的服務中

下一篇:重構改善既有代碼

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more