kubernetes+Azure DevOps實現.Net Core項目的自動化部署&均衡負載

1. 前言

前前後後學習kubernetes也有一個來月了,關於kubernetes的部落格也寫了有十多篇。但是技術如果無法落地到實際的應用場景終歸是紙上談兵,所以就有了這一出:通過結合kubernetesazure devops實現項目的CI/CD以及均衡負載

寫完這篇後kubernetes的相關學習也暫時告一段落了,有種終於闖關成功了啊的感覺,當然這是題外話了。

以下只是以Net Core項目為例,實際運用場景中,只要是無狀態服務,除了dockfile的編寫有差別,剩下整個自動化部署鏈條中的技術也好,工具也好,都可以復用,與語言和語言框架本身無關。

以下場景需要用到的工具或者技術:

  • .Net Core

部署的應用本身

作為程式碼倉庫

  • kubernetes
    • docker
    • helm【kubernetes的包管理工具】
    • ingress【使用ingress綁定域名和https證書,實現域名訪問】
  • Azure DevOps

作為CI/CD的工具

:以下所有的相關部署程式碼,都在下面這個倉庫

  • 倉庫內容只是我自己用的一個小工具,當然具體是什麼內容不重要,這篇只是演示部署相關的

//github.com/lzw5399/TocGenerator


2. Net Core項目本身的準備

2.1 dockerfile

你需要一個dockerfile來構建一個docker image, 如果是.Net Core項目,vs提供了傻瓜式生成dockerfile的功能,可以免去初學時編寫dockerfile的煩惱

  • 本示例dockerfile路徑和內容

2.2 創建kubernetes用於helm的chart包

2.2.1 說明

這一部分需要有helm相關的知識,說白了就是將你的如果熟悉k8s但不熟悉helm,可以參照:

kubernetes系列(十六) – Helm安裝和入門

2.2.2 chart文件目錄和文件組成

自定義的chart包,位於以下路徑

//github.com/lzw5399/TocGenerator/tree/master/kubernetes

如上圖可以看出是一個很經典的自定義chart包的文件目錄,即:

.
├── Chart.yaml           【chart的name和version等資訊】
├── templates            【k8s的資源清單模板,可以引用values.yaml的變數】
|   ├── deployment.yaml
|   └── service.yaml
├── values.yaml          【定義變數,供template/下的yaml使用,實現動態替換yaml內容】

3. Azure Devops創建倉庫的pipeline

3.1 前言

Azure DevOps是微軟出品的DevOps平台,裡面包含了Pipelines工具鏈,對個人免費,可以用於項目的CI/CD

//dev.azure.com

3.2 使用azure devops準備操作

  • 如果之前使用過azure devops,這幾步可以視情況跳過。
  1. 進入azure devops註冊帳號
  2. 之後按照引導新建一個organization
  3. 再新建一個project
  4. 進入project

3.3 創建service connections

這裡要創建一個service connections,用於之後pipeline訪問k8s的master伺服器

  1. 點擊peject setting
  2. 這裡點擊service connections來創建一個連接,用於訪問k8s的master伺服器
  3. 然後填寫具體的憑證,之後的pipeline上需要

3.4 新建pipeline流水線

新建pipeline流水線用於自定義部署流程

  1. 點擊pipelines,然後點擊create pipelines,新建一條流水線來部署我們的應用
  2. 選擇程式碼倉庫位置,選github
  3. 然後會跳到github進行授權,授權完成後會顯示github的repo列表,選擇具體的倉庫
  4. 選擇完倉庫後,會自動按照你當前項目的語言,在github倉庫的根目錄生成一個默認的azure-pipelines.yml文件,
  5. 替換文件的內容,我們最終使用的yaml文件步驟大概如下
    • 第一步:構建docker鏡像
    • 第二步:將自定義的chart包拷貝到master伺服器上
    • 第三步:執行deploy.sh腳本,完成部署
# 哪條分支會觸發構建
trigger:
- master

resources:
- repo: self

# 定義變數
variables:
- name: appName
  value: tocgenerator

- name: tag
  value: $(Build.BuildNumber)

- name: imageNameWithoutTag
  value: $(dockerid)/$(appName)

- name: imageNameWithTag
  value: $(imageNameWithoutTag):$(tag)

- name: serverChartLocation
  value: /root/helm-chart-folder/toc

stages:
- stage: Build
  jobs:  
  - job: Build
    pool:
      vmImage: 'ubuntu-latest'
  
    # 這下面是每個我們要具體執行的任務
    steps:
    # build docker images並且push到倉庫
    - task: Docker@2
      displayName: docker build and push
      inputs:
        containerRegistry: 'my_docker_hub'
        repository: '$(imageNameWithoutTag)'
        command: 'buildAndPush'
        Dockerfile: '**/Dockerfile'
        buildContext: '.'
        tags: $(tag)
        addPipelineData: false

    # 將kubernetes文件夾,即chart包拷貝到k8s的master伺服器
    - task: CopyFilesOverSSH@0
      displayName: copy helm chart to server
      inputs:
        # 這個endpoint就是我們剛剛創建的service connection的名字
        sshEndpoint: 'my_server'
        sourceFolder: 'kubernetes'
        contents: '**'
        targetFolder: $(serverChartLocation)
        readyTimeout: '20000'
  
    # 在k8s的master伺服器上運行我們github倉庫的根目錄的deploy.sh,進行部署操作
    - task: SSH@0
      displayName: run deploy shell on server
      inputs:
        # 這個endpoint就是我們剛剛創建的service connection的名字
        sshEndpoint: 'my_server'
        runOptions: 'script'
        scriptPath: 'deploy.sh'
        args: '$(tag) $(serverChartLocation)'
        readyTimeout: '20000'

3.5 創建部署shell腳本

部署腳本的位置

//github.com/lzw5399/TocGenerator/blob/master/deploy.sh

幾點說明

  1. echo純粹是為了記錄log使用的,下面的示例把echo部分刪除了
  2. $1 and $2 代表外部傳入的參數
  3. $1是image的tag,$2是k8s的master伺服器上我們自定義的chart的目錄
  4. 移除沒有tag的懸掛docker image,純粹為了節省伺服器空間,為可選項
#!/bin/bash

# 出現錯誤退出腳本執行
set -o errexit

# $1 and $2 代表外部傳入的參數
# $1是image的tag,$2是k8s的master伺服器上我們自定義的chart的目錄
buildNumber=$1
serverChartLocation=$2
cd $serverChartLocation

# 安裝或者升級我們的helm release
# 即如果查詢到了有release存在就upgrade,沒有則install
if test -z "$(helm ls | grep toc-release)"; then
  helm install -f values.yaml --set env.buildnumber=$buildNumber --set image.tag=$buildNumber toc-release .
else
  helm upgrade -f values.yaml --set env.buildnumber=$buildNumber --set image.tag=$buildNumber toc-release .
fi

# 移除沒有tag的懸掛docker image(可選)
danglings=$(sudo docker images -f "dangling=true" -q)
if test -n "$danglings"; then
  sudo docker rmi $(sudo docker images -f "dangling=true" -q) >>/dev/null 2>&1
  if [[ $? != 0 ]]; then
    exit $?
  fi
fi

exit 0

4. 觸發pipeline部署流水線

這裡有兩種辦法,

  1. 點擊我們剛剛創建的pipeline手動run一個
  2. 通過push程式碼到倉庫的指定分支(我們設置的master)觸發構建

顯示構建成功之後就可以查看了!

5. 關於均衡負載

均衡負載是kubernetes自帶的基礎功能之一,這裡只是做了一個試驗可以更加直觀地感受到而已

如下

  1. 定義一個靜態的guid
  2. 在/version 路由下輸出guid

則如果有2個實例,且均衡負載成功的話,每次刷新這個介面,會隨機顯示這兩個guid

  • deployment的replicas實例數需要設置2以上

最後均衡負載試驗的地址,也是本次實例項目的線上地址

//toc.codepie.fun/version

  • 如下,會出現兩個不同的guid