前面的BuildCode、BuildImage兩個Job都已經拆解成job template了,最後就剩下DeployCloudRun這個Job,不過裡面只有一個task,倒是也沒什麼好拆解的,而且我們是利用script的方式跑docker container在裡面執行gcloud sdk的指令,所以也沒什麼再拆細成為step template的必要。
雖然沒有要拆細到steps的範本,不過job template還是ok的。
先前我們在gcloud指令中設定了一個環境變數為Ironman,不過若是有多個環境變數會讓指令變得很長,如果寫在檔案裡面就會方便許多,這部份在官方文件的可用參數列表當中可以查得到。我們原本使用的是–-set-env-vars來設定環境變數,但是如果是要改用檔案的方式來設定的話,則是改成使用–-env-vars-file來給它檔案路徑。
說到檔案,就必須好好正視這個問題了。
我們將Pipeline的YAML檔案拆解為不同的範本,其實很大一個目的是希望讓多個不同的專案共用範本的YAML檔案,也就是說範本的檔案可能會存在名為「DevOps」這個Azure DevOps專案中的「templates」Git Repository,之後其它的A、B、C專案可能都會有自己的一個「Pipelines」Git Repository,用來存放各別專案的Pipeline YAML檔案,以及一些專案使用的設定檔。
那麼,我們前面使用到的檔案像是GCP的身份驗證json檔,或是上面提到的CloudRun的環境變數設定檔共通的部份,這些比較好的放置地就是「templates」這個Git Repository,其它專案可能也會需要設定特有的CloudRun環境變數,這時候檔案應該是放在各別專案內的「Pipelines」Git Repository。
所以,有一些Job裡面的內容可能就會需要針對不同的情況取得「templates」和「Pipelines」以及Source code存放的Git Repository裡面的檔案,這時候就需要在Job裡面使用checkout關鍵字來分別取得不同Repository的檔案,而這些Repository就是定義在resources底下的內容(前面用過囉)。
說了那麼多,還是直接看YAML內容會比較容易理解吧?同樣把修改前和修改後的內容都貼上來方便對照。
- 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"
parameters:
- name: cloudRunProjectId
type: string
- name: imgRepository
type: string
- name: cloudRunRegion
type: string
- name: cloudRunServiceName
type: string
- name: templateResourceName
type: string
default: templates
- name: pipelineResourceName
type: string
default: pipelines
jobs:
- job: DeployCloudRun
dependsOn: BuildImage
steps:
- script: |
echo '${{ convertToJson(parameters) }}' >> parameters.json
cat parameters.json
displayName: Print template parameters
- checkout: ${{ parameters.templateResourceName }}
path: ${{ parameters.templateResourceName }}
clean: true
- checkout: ${{ parameters.pipelineResourceName }}
path: ${{ parameters.pipelineResourceName }}
clean: true
- template: ../steps/merge-cloudRun-envVars.yaml
parameters:
templateResourceName: ${{ parameters.templateResourceName }}
pipelineResourceName: ${{ parameters.pipelineResourceName }}
projectEnvVarsFile: variables/cloudrun-envVars.yaml
sharedEnvVarsFile: variables/cloudrun-envVars.yaml
mergedNewFileFullPath: $(Agent.BuildDirectory)/mergedEnvVars.yaml
- task: Bash@3
displayName: Deploy docker image to cloudrun
inputs:
targetType: 'inline'
script: |
docker run --rm \
-v $(Agent.BuildDirectory)/${{ parameters.templateResourceName }}/ironman2022-gcp-key.json:/gcp/cloudKey.json \
-v $(Agent.BuildDirectory)/mergedEnvVars.yaml:/gcp/env-vars.yaml \
asia.gcr.io/google.com/cloudsdktool/google-cloud-cli:latest \
bash -c "gcloud auth login --cred-file=/gcp/cloudKey.json && gcloud run deploy ${{ parameters.cloudRunServiceName }} --env-vars-file /gcp/env-vars.yaml --image ${{ parameters.imgRepository }} --region ${{ parameters.cloudRunRegion }} --project ${{ parameters.cloudRunProjectId }} --allow-unauthenticated"
從修改後的YAML內容來看,是不是好像比預期的內容多了一些?
前面幾個參數是deploy cloudrun使用的,後面的templateResourceName、pipelineResourceName則是希望使用到範本的Pipeline可以告知在Pipeline YAML的resources底下的repository定義的名稱是什麼,這樣才可以在範本裡面正確的checkout,並且這個名稱也會被用在path的部份。
在修改後的YAML內容中我增加了一個script,在前面文章的內容都沒有出現過,但是其實它對於在設計YAML範本是一個滿有用的小技巧,就是在Pipeline執行時可以在Logs輸出我們所設定的參數內容,方便查看該次執行的時候使用的參數內容是什麼。(參考官方文件)
除了上面多增加的script之外,其實在這裡雖然沒有將使用gcloud的script改為step範本,但是多做了一個合併環境變數檔案內容的step範本,YAML內容如下:
# steps/merge-cloudRun-envVars.yaml
parameters:
- name: templateResourceName
type: string
- name: pipelineResourceName
type: string
- name: projectEnvVarsFile
type: string
- name: sharedEnvVarsFile
type: string
- name: mergedNewFileFullPath
type: string
steps:
- script: |
if [ -f "$(Agent.BuildDirectory)/${{ parameters.pipelineResourceName }}/${{ parameters.projectEnvVarsFile }}" ]; then
cat \
$(Agent.BuildDirectory)/${{ parameters.pipelineResourceName }}/${{ parameters.projectEnvVarsFile }} \
$(Agent.BuildDirectory)/${{ parameters.templateResourceName }}/${{ parameters.sharedEnvVarsFile }} >> \
${{ parameters.mergedNewFileFullPath }};
else
cat \
$(Agent.BuildDirectory)/${{ parameters.templateResourceName }}/${{ parameters.sharedEnvVarsFile }} >> \
${{ parameters.mergedNewFileFullPath }};
fi
displayName: merge env vars files
這邊的設計是假設在template和專案的pipelines兩個Git Repository都有在固定的位置放指定檔名的檔案(variables/cloudrun-envVars.yaml),合併之後將它mapping到container裡面提供給gcloud設定環境變數檔案路徑使用,這樣我們就可以將共用的設定內容放在template的Git Repository裡面,專案額外要設定的則放在它自己pipelines的Git Repository裡面。
最後就是GCP用的身份驗證檔案,因為已經改放在template的Git Repository裡面,所以就不需要再從外部傳入檔名,所以直接把檔名寫在YAML內容中。
最後,下面這個是CI Pipeline將三個Job都改用template之後的內容:
trigger:
- none
resources:
repositories:
- repository: sources
type: git
name: ironman2022/NetApp
ref: Develop
trigger:
branches:
include:
- Develop
- repository: pipelines
type: git
name: ironman2022/Pipelines
ref: main
- 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
cloudRunRegion: asia-east1
cloudRunProjectId: feisty-mechanic-363012
gcpAuthJsonFile: ironman2022-gcp-key.json
jobs:
- template: jobs/buildCode.yaml@templates
parameters:
sourcePath: $(Build.SourcesDirectory)
slnOrCsprojName: $(slnOrCsprojName)
- template: jobs/buildImage.yaml@templates
parameters:
artifactName: '$(pipelineArtifact)'
unzip: true
zipFileName: buildResult.zip
unzipToFolderPath: $(System.ArtifactsDirectory)/buildImage
imgRepository: $(imgRepository)
imgTags: |
latest
buildDockerfile: $(buildDockerfile)
buildContext: $(System.ArtifactsDirectory)/buildImage
containerRegistry: $(imgRegistryService)
- template: jobs/deployCloudRun.yaml@templates
parameters:
cloudRunProjectId: $(cloudRunProjectId)
imgRepository: $(imgRepository)
cloudRunRegion: $(cloudRunRegion)
cloudRunServiceName: $(cloudRunServiceName)
templateResourceName: templates
pipelineResourceName: pipelines
下面的截圖是convertToJson印出參數的範例:
對了,CloudRun的環境變數檔案內容的格式是每一行一個環境變數設定,key跟value之間使用一個半形冒號和一個空白字元相隔,跟YAML的設定方式一樣。