在 Serverless 架構下,Lambda 函式的自動化佈署至關重要。本文詳細說明如何利用 Jenkins Pipeline 建立 CI/CD 流程,實作 Lambda 函式的自動化建置、測試、佈署及版本管理。流程涵蓋程式碼簽出、建置 Docker 映像、封裝 Node.js 相依套件、上傳至 S3 儲存桶、更新 Lambda 函式程式碼,並利用版本控制和別名機制實作多環境佈署策略。同時整合 API Gateway 與 Lambda 函式,並使用 Terraform 進行基礎設施佈署,最後加入電子郵件通知功能,提升佈署流程的可靠性和可控性。
使用 Jenkins 實作 Lambda 函式的 CI/CD 流程
在開發 Serverless 架構的應用程式時,如何有效地管理和佈署 Lambda 函式是一個重要的課題。本文將介紹如何使用 Jenkins 實作 Lambda 函式的持續整合和持續佈署(CI/CD)流程。
建立 Jenkinsfile
首先,我們需要在專案中建立一個 Jenkinsfile,用於定義 CI/CD 流程。以下是一個範例 Jenkinsfile:
def imageName = 'mlabouardy/movies-store'
node('workers'){
try {
stage('Checkout'){
checkout scm
notifySlack('STARTED')
}
def imageTest= docker.build("${imageName}-test", "-f Dockerfile.test .")
stage('Tests'){
parallel(
'Quality Tests': {
sh "docker run --rm ${imageName}-test npm run lint"
},
'Unit Tests': {
sh "docker run --rm ${imageName}-test npm run test"
},
'Coverage Reports': {
sh "docker run --rm -v $PWD/coverage:/app/coverage ${imageName}-test npm run coverage"
publishHTML (target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: "$PWD/coverage",
reportFiles: "index.html",
reportName: "Coverage Report"
])
}
)
}
// ...
內容解密:
此 Jenkinsfile 定義了一個 CI/CD 流程,包括簽出程式碼、執行測試、產生測試報告等階段。其中,stage('Checkout') 階段負責簽出程式碼,stage('Tests') 階段負責執行測試,包括程式碼品品檢查、單元測試和測試報告生成。
建立 Node.js 型 Lambda 函式的佈署套件
接下來,我們需要建立一個 Node.js 型 Lambda 函式的佈署套件。以下是一個範例:
stage('Build'){
sh """
docker build -t ${imageName} .
containerName=\$(docker run -d ${imageName})
docker cp \$containerName:/app/node_modules node_modules
docker rm -f \$containerName
zip -r ${commitID()}.zip node_modules src
"""
}
內容解密:
此階段負責建立 Node.js 型 Lambda 函式的佈署套件。首先,使用 Docker 建置映像檔,然後從容器中複製 node_modules 資料夾,最後將 node_modules 和 src 資料夾壓縮成一個 zip 檔案。
將佈署套件上傳到 S3
接下來,我們需要將佈署套件上傳到 S3。以下是一個範例:
stage('Push'){
def fileName = commitID()
def parallelStagesMap = functions.collectEntries {
["${it}" : {
stage("Lambda: ${it}") {
sh "aws s3 cp ${fileName}.zip s3://${bucket}/${it}/"
}
}]
}
parallel parallelStagesMap
}
內容解密:
此階段負責將佈署套件上傳到 S3。首先,定義了一個 parallelStagesMap,用於平行上傳佈署套件到不同的 S3 資料夾。然後,使用 parallel 指令平行執行上傳任務。
更新 Lambda 函式程式碼
最後,我們需要更新 Lambda 函式程式碼。以下是一個範例:
stage('Deploy'){
sh "aws lambda update-function-code --function-name ${functionName} --s3-bucket ${bucket} --s3-key ${functionName}/${commitID()}.zip --region ${region}"
}
內容解密:
此階段負責更新 Lambda 函式程式碼。使用 AWS CLI 的 update-function-code 指令更新 Lambda 函式程式碼。
自動化佈署與管理 AWS Lambda 函式的多環境策略
在開發無伺服器應用程式時,管理多個環境對於測試新變更而不影響生產環境至關重要。本章節將探討如何利用 Jenkins 實作 AWS Lambda 函式的持續整合和持續佈署(CI/CD),並探討如何維護多個 Lambda 環境。
使用 Jenkins 自動佈署 Lambda 函式
Jenkins 可以與 AWS Lambda 無縫整合,以自動化佈署流程。首先,我們需要在 Jenkinsfile 中定義佈署階段,利用 AWS CLI 更新 Lambda 函式程式碼。
更新 Lambda 函式程式碼
stage('Deploy'){
functions.each { function ->
sh "aws lambda update-function-code --function-name ${function} --s3-bucket ${bucket} --s3-key ${function}/${commitID()}.zip --region ${region}"
}
}
詳細解說:
- 更新函式程式碼:使用
aws lambda update-function-code命令更新指定的 Lambda 函式。 - S3 儲存桶:從指定的 S3 儲存桶和鍵讀取程式碼包。
- 版本控制:利用 Git commit ID 作為版本標識,確保每次佈署都有唯一的版本。
載入資料到 DynamoDB 表
在自動化佈署市場應用之前,需要將一些資料載入到 DynamoDB 表中。可以透過觸發 MoviesLoader 函式來實作這一點。
呼叫 MoviesLoader 函式
aws lambda invoke --function-name MoviesLoader --payload '{}' response.json
詳細解說:
- 呼叫函式:使用
aws lambda invoke命令呼叫指定的 Lambda 函式。 - 處理結果:將函式的輸出儲存到
response.json檔案中。 - IAM 許可權:確保執行 AWS CLI 的 IAM 使用者具有
AWSLambda_FullAccess策略。
將靜態網站佈署到 S3
市場應用是一個使用 Angular 框架編寫的單頁應用(SPA)。我們可以利用 S3 的網站託管功能來佈署靜態內容。
Jenkinsfile 中的佈署階段
stage('Push'){
sh "aws s3 cp --recursive dist/ s3://${bucket}/"
}
詳細解說:
- 複製檔案:使用
aws s3 cp命令遞迴地將本地檔案複製到 S3 儲存桶。 - 網站託管:確保 S3 儲存桶已啟用網站託管功能。
維護多個 Lambda 環境
AWS Lambda 允許發布版本,以代表函式程式碼和組態在某一時間點的狀態。預設情況下,每個 Lambda 函式都有 $LATEST 版本,指向最新的變更。
發布新版本
stage('Deploy'){
sh "aws lambda update-function-code --function-name ${functionName} --s3-bucket ${bucket} --s3-key ${functionName}/${commitID()}.zip --region ${region}"
sh "aws lambda publish-version --function-name ${functionName} --description ${commitID()} --region ${region}"
}
詳細解說:
- 更新函式程式碼:首先更新 Lambda 函式的程式碼。
- 發布新版本:使用
aws lambda publish-version命令發布新版本,並使用 Git commit ID 作為版本描述。
無伺服器函式的多環境管理
在現代化的軟體開發流程中,使用Lambda-based serverless functions已成為主流趨勢。為了有效管理不同環境下的Lambda函式,開發團隊需要建立一套完善的CI/CD流程,以確保程式碼的版本控制、發布和佈署能夠順暢進行。
Lambda函式版本控制與發布
當開發人員將程式碼推播到Git儲存函式庫後,Jenkins會自動觸發CI/CD流程。在佈署階段,Jenkins會執行預先定義的命令,將Lambda函式的程式碼更新並發布新的版本。圖12.24展示了佈署階段的執行日誌。
值得注意的是,Lambda函式的版本是不可變的,一旦建立就無法更新其程式碼或設定。因此,每次發布新版本時,都會建立一個新的版本。
使用Lambda別名管理多環境
為了方便管理不同環境下的Lambda函式,可以使用Lambda別名。別名是一個指向特定版本的指標,可以用來將函式從一個環境提升到另一個環境,例如從測試環境提升到生產環境。與版本不同,別名是可變的。
建立Lambda別名
可以使用AWS CLI命令建立Lambda別名:
aws lambda create-alias --function-name MoviesStoreViewFavorites --name sandbox --version 1
建立後,新的別名會出現在Lambda函式的Aliases列表中。
在Jenkinsfile中更新Lambda別名
為了自動化更新Lambda別名,可以在Jenkinsfile中新增相關程式碼。在佈署階段,Jenkins會根據目前的Git分支,更新對應的Lambda別名,使其指向最新發布的版本。
sh "aws lambda update-function-code --function-name ${it} --s3-bucket ${bucket} --s3-key ${it}/${fileName}.zip --region ${region}"
def version = sh(
script: "aws lambda publish-version --function-name ${it} --description ${fileName} --region ${region} | jq -r '.Version'",
returnStdout: true
).trim()
if (env.BRANCH_NAME in ['master','preprod','develop']){
sh "aws lambda update-alias --function-name ${it} --name ${environments[env.BRANCH_NAME]} --function-version ${version} --region ${region}"
}
內容解密:
- 更新Lambda函式程式碼:使用
aws lambda update-function-code命令更新Lambda函式的程式碼。 - 發布新版本:使用
aws lambda publish-version命令發布新版本,並使用jq命令解析輸出的JSON結果,取得新版本的版本號。 - 更新Lambda別名:根據目前的Git分支,更新對應的Lambda別名,使其指向最新發布的版本。
組態API Gateway使用Lambda別名
為了讓API Gateway能夠使用Lambda別名,需要在API Gateway的組態中,將Lambda函式的版本替換為stage variable。
設定Stage Variable
- 在API Gateway控制檯中,導航到Movies API,點選GET方法。
- 更新目標Lambda函式,使用stage variable
${stageVariables.environment}。 - 儲存組態後,API Gateway會提示授予呼叫Lambda函式別名的許可權。
使用Terraform佈署API
可以使用Terraform程式碼佈署API,並設定stage variable。
resource "aws_api_gateway_deployment" "sandbox" {
depends_on = [
module.GetMovies,
module.GetOneMovie,
module.GetFavorites,
module.PostFavorites
]
variables = {
"environment" = "sandbox"
}
rest_api_id = aws_api_gateway_rest_api.api.id
stage_name = "sandbox"
}
圖表翻譯:
此圖示展示了API Gateway佈署的流程,包括建立新的佈署階段、設定stage variable等步驟。
佈署到不同環境
建立不同的佈署階段(sandbox、staging、production),並使用對應的Lambda別名。可以透過建立pull request,將程式碼從develop分支合併到preprod分支,觸發Jenkins自動構建和佈署。
最終,使用者可以透過不同的API URL存取不同環境下的Lambda函式,例如:https://id.execute-api.region.amazonaws.com/sandbox/movies。
佈署多環境的Lambda無伺服器函式
為了在多個環境中佈署市場,我們將根據目前的分支名稱注入環境名稱。請參考以下程式碼。
stage('Build'){
sh """
docker build -t ${imageName} \
--build-arg ENVIRONMENT=${environments[env.BRANCH_NAME]} .
containerName=\$(docker run -d ${imageName})
docker cp \$containerName:/app/dist dist
docker rm -f \$containerName
"""
}
內容解密:
這段程式碼定義了一個名為Build的階段,主要功能是建立Docker映像並將靜態檔案複製到本地目錄。首先,它使用docker build命令建立映像,並傳入ENVIRONMENT建構引數,該引數的值根據目前分支名稱從environments對映中取得。接著,它執行一個容器,將容器內的/app/dist目錄複製到本地的dist目錄,最後刪除該容器。
更新S3上傳指令
接下來,我們更新aws s3 cp指令,將靜態檔案推播到S3儲存桶中以環境名稱命名的資料夾下。
if (env.BRANCH_NAME in ['master','preprod','develop']){
stage('Push'){
sh "aws s3 cp --recursive dist/ s3://${bucket}/${environments[env.BRANCH_NAME]}/"
}
}
內容解密:
這段程式碼檢查目前分支是否為master、preprod或develop。如果是,則執行一個名為Push的階段,將本地dist目錄下的檔案遞迴上傳到S3儲存桶中對應環境名稱的資料夾內。
維護多個Lambda環境
將變更推播到功能分支,並發起一個合併請求以合併到develop分支。當合併發生時,將執行新的Pipeline。
佈署Lambda函式到生產環境
要將Lambda函式佈署到生產環境,需要將preprod分支合併到master分支。
if(env.BRANCH_NAME == 'master'){
timeout(time: 2, unit: "HOURS") {
input message: "Deploy to production?", ok: "Yes"
}
sh "aws lambda update-alias --function-name ${it} \
--name ${environments[env.BRANCH_NAME]} \
--function-version ${version} \
--region ${region}"
}
內容解密:
這段程式碼檢查目前分支是否為master。如果是,則在佈署到生產環境之前彈出一個輸入對話方塊,要求確認是否佈署。如果確認,則更新Lambda函式的別名指向新佈署的版本。
設定Jenkins的電子郵件通知
要在Jenkins中設定電子郵件通知,需要安裝Email Extension外掛程式。進入「管理Jenkins」>「設定系統」,向下捲動到「Extended E-mail Notification」部分,輸入SMTP伺服器的相關資訊。
def sendEmail(String buildStatus){
buildStatus = buildStatus ?: 'SUCCESSFUL'
emailext body: "More info at: ${env.BUILD_URL}",
subject: "Name: '${env.JOB_NAME}' Status: ${buildStatus}",
to: '$DEFAULT_RECIPIENTS'
}
內容解密:
這段程式碼定義了一個名為sendEmail的函式,用於傳送電子郵件通知。函式根據構建狀態傳送自訂的電子郵件,包含構建任務的名稱、狀態和詳細資訊連結。
在Jenkinsfile中呼叫sendEmail函式
node('workers'){
try {
stage('Checkout'){...}
stage('Tests'){...}
stage('Build'){...}
stage('Push'){...}
stage('Deploy'){...}
} catch(e){
currentBuild.result = 'FAILED'
throw e
} finally {
notifySlack(currentBuild.result)
if (env.BRANCH_NAME == 'master'){
sendEmail(currentBuild.result)
}
}
}
內容解密:
這段程式碼在Jenkinsfile的finally區塊中呼叫了sendEmail函式,以便在Pipeline完成後傳送電子郵件通知。只有當目前分支為master時,才會傳送電子郵件,以避免向開發人員傳送不必要的通知。