【2022鐵人賽】初探YAML的範本(Template)設計

基本版的CI Pipeline建立完之後,又花了兩篇的篇幅快速的介紹了YAML的運算式、函數、變數以及參數,一切的一切就是為了後面進入YAML範本化的設計。

雖然每個系統建立一個Azure DevOps專案之後,我們仍然可以為每個專案依照前面的Pipeline設計方式替它們建立CI Pipeline,但是每個專案內的Pipeline應該幾乎是大同小異的,除了專案名稱與一些變數的設定值差異之外,依照前面流程規劃說明的內容來看,基本動作就是程式碼Commit之後,建立PR合併程式碼,接著編譯程式、上傳成品、建立Docker Image、佈署Dev環境,這些動作在每一個專案中的Pipeline都是一致的,所以最適合的方式就是建立YAML範本,讓每一個專案引用範本,傳入不同的設定值就行了。

要把現有的YAML內容改成template其實很簡單,把它們分拆成不同的YAML檔案就行。不過在這之前我先建立了一個新的Git Repository,名稱就叫作templates,使用預設的main分支,專門用來存放改為範本的YAML檔案。(建立Git Repo的部份忘了請參考先前的文章)

接著利用前面這篇最後完成的CI Pipeline YAML內容為例,我們將BuildCode這個Job中的內容抽離出來變成範本,在templates git repo中存放在jobs/buildCode.yaml。


jobs:
  - job: BuildCode
    steps:
      - checkout: sources
        clean: true
      - task: Bash@3
        displayName: Check folder exist or create
        inputs:
          targetType: 'inline'
          script: |
            if [ -d "$(Build.ArtifactStagingDirectory)/pipelineFiles/" ]; then echo "$(Build.ArtifactStagingDirectory)/pipelineFiles/ exist"; else mkdir -p "$(Build.ArtifactStagingDirectory)/pipelineFiles/"; fi
      - task: PowerShell@2
        displayName: 'Get git commit sha'
        inputs:
          targetType: 'inline'
          workingDirectory: $(Build.SourcesDirectory)
          pwsh: true
          script: |
            $gitFullCommitSHA = git rev-parse HEAD
            $gitCommitSHA = $gitFullCommitSHA.Substring(0,8)

            echo "git commit sha: $gitCommitSHA"
            echo "git full commit sha: $gitFullCommitSHA"

            echo "$gitFullCommitSHA" > $(Build.ArtifactStagingDirectory)/pipelineFiles/gitCommitSHA.txt
      - task: PublishBuildArtifacts@1
        inputs:
          PathtoPublish: '$(Build.ArtifactStagingDirectory)/pipelineFiles'
          ArtifactName: 'PipelineFiles'
          publishLocation: 'Container'
      - script: |
          export UID=$(id -u)
          export GID=$(id -g)
          docker run --user $UID:$GID --rm \
          -v $(Build.SourcesDirectory):/tmp/source \
          -v $(Build.BinariesDirectory):/tmp/publish \
          -e DOTNET_CLI_HOME=/tmp/.dotnet \
          mcr.microsoft.com/dotnet/sdk:6.0-alpine \
          dotnet publish /tmp/source/$(slnOrCsprojName) \
          -c release \
          -o /tmp/publish
        displayName: Dotnet Build
      - task: ArchiveFiles@2
        displayName: 壓縮成zip
        inputs:
          rootFolderOrFile: $(Build.BinariesDirectory)
          includeRootFolder: false
          archiveType: 'zip'
          archiveFile: '$(Build.ArtifactStagingDirectory)/zipFiles/$(buildResultZipName)'
          replaceExistingArchive: true
      - task: PublishBuildArtifacts@1
        displayName: 上傳到Pipeline Artifact
        inputs:
          PathtoPublish: '$(Build.ArtifactStagingDirectory)/zipFiles/$(buildResultZipName)'
          ArtifactName: '$(pipelineArtifact)'
          publishLocation: 'Container'

這邊要特別注意的地方就是最前面的jobs,雖然我們的目的是把BuildCode這個Job抽出來變成範本,但是範本並沒有限制只能有一個子項目,所以Job的範本必須以jobs為開頭。

把上面的內容存在templates git repo的jobs目錄下,檔名為buildCode.yaml,Commit到main分支之後,就可以將原本CI Pipeline的BuildCode部份移掉,改用template關鍵字來設定。不過在改用template關鍵字設定之前,還必須在resources底下加入對於templates git repo的資源設定:

resources:
  repositories:
  - repository: sources
    type: git
    name: ironman2022/NetApp
    ref: Develop
    trigger:
      branches:
        include:
          - Develop
  # 加入下面這段
  - repository: templates
    type: git
    name: ironman2022/templates
    ref: main

接著就把下面的BuildCode移除,改用下面這一行來取用範本內容,路徑是相對templates git repo的路徑,@符號後面的名稱是在resources定義repository的名稱,如果上面是設定為- repository: ABCD,那就是@ABCD

jobs:
    - template: jobs/buildCode.yaml@templates

完整的YAML內容如下:

trigger:
- none

resources:
  repositories:
  - repository: sources
    type: git
    name: ironman2022/NetApp
    ref: Develop
    trigger:
      branches:
        include:
          - Develop
  - repository: templates
    type: git
    name: ironman2022/templates
    ref: main


variables:
  pipelineArtifact: output
  buildResultZipName: buildResult.zip
  slnOrCsprojName: IronmanWeb.sln
  imgRepository: 'asia-east1-docker.pkg.dev/feisty-mechanic-363012/ironman2022/ironmanweb'
  buildDockerfile: 'Dockerfile'
  imgRegistryService: 'GCPArtifactRegistry'
  cloudRunServiceName: ironmanweb
  cloudRunPort: 8080
  cloudRunRegion: asia-east1
  cloudRunProjectId: feisty-mechanic-363012
  gcpAuthJsonFile: ironman2022-gcp-key.json


jobs:
  - template: jobs/buildCode.yaml@templates
  - job: BuildImage
    dependsOn: BuildCode
    steps:
      - task: DownloadBuildArtifacts@0
        displayName: 下載Pipeline Artifact
        inputs:
          buildType: 'current'
          cleanDestinationFolder: true
          downloadType: 'single'
          artifactName: '$(pipelineArtifact)'
          downloadPath: '$(System.ArtifactsDirectory)/'
      - task: ExtractFiles@1
        displayName: Unzip zip
        inputs:
          archiveFilePatterns: '$(System.ArtifactsDirectory)/$(pipelineArtifact)/$(buildResultZipName)'
          destinationFolder: '$(System.ArtifactsDirectory)/BuildImage'
          cleanDestinationFolder: true
          overwriteExistingFiles: true
      - task: Docker@2
        displayName: Build image
        inputs:
          repository: '$(imgRepository)'
          command: 'build'
          Dockerfile: $(buildDockerfile)
          buildContext: '$(System.ArtifactsDirectory)/BuildImage'
          arguments: '--no-cache'
          tags: |
            latest
      - task: Docker@2
        displayName: "Login to Container Registry"
        inputs:
          command: login
          containerRegistry: $(imgRegistryService)
      - task: Bash@3
        displayName: Push docker image
        inputs:
          targetType: 'inline'
          script: |
            docker push -a $(imgRepository)
  - job: DeployCloudRun
    dependsOn: BuildImage
    steps:
      - task: Bash@3
        displayName: Deploy docker image to cloudrun
        inputs:
          targetType: 'inline'
          script: |
            docker run --rm \
            -v $(Build.SourcesDirectory)/$(gcpAuthJsonFile):/gcp/cloudKey.json \
            asia.gcr.io/google.com/cloudsdktool/google-cloud-cli:latest \
            bash -c "gcloud auth login --cred-file=/gcp/cloudKey.json && gcloud run deploy $(cloudRunServiceName) --set-env-vars=Ironman=$(Build.BuildId) --image $(imgRepository) --region $(cloudRunRegion) --project $(cloudRunProjectId) --allow-unauthenticated"

修改之後再次執行Pipeline,是不是和原本的結果一樣呢?

發佈留言

%d 位部落客按了讚: