iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
IT Operation

迎接 AI 開發爆發期:告別手動部署,帶領企業團隊從 Git 規範到 CI/CD 實戰系列 第 24

Day 24 MyDeploy.jenkinsfile (上) — 建置打包與配置渲染實務

  • 分享至 

  • xImage
  •  

在 CI/CD 的世界裡,有一條黃金法則:一次編譯,到處部署。本篇將介紹 MyDeploy.jenkinsfile 的前半段實作,重點在於如何將原始碼轉化為「Immutable Artifacts」,並動態注入各環境所需的設定值。

配置渲染 (Config Rendering) 的核心技術

為什麼不直接將 appsettings.json 寫死在專案中?

  1. 安全性:生產環境的密碼不應出現在 Git 歷史紀錄中。
  2. 環境一致性:同一套二進位檔應能套用於測試與生產環境,差別僅在於配置參數。

我們的策略是:在專案中保留一份 appsettings.template.json,敏感欄位使用佔位符(如 {{DB_PASSWORD}})。部署時,由一個輕量級腳本從 Vault 抓取數值並替換。

實作:配置渲染腳本 (render_config.py)

我們使用 Python 結合 Jinja2 模板引擎來實作。這能確保 JSON 格式在替換過程中保持正確。

# scripts/render_config.py (核心邏輯精簡版)
import jinja2
import argparse
import hvac # HashiCorp Vault Client

def render(template_path, output_path, env_name, vault_addr, token):
    # 1. 初始化 Vault 用戶端
    client = hvac.Client(url=vault_addr, token=token)
    
    # 2. 從 Vault 抓取該環境的秘密資訊
    # 假設路徑格式為: secret/data/app/config/<env_name>
    secret_response = client.secrets.kv.v2.read_secret_version(
        path='app/config', mount_point=env_name
    )
    secrets = secret_response['data']['data']

    # 3. 載入模板並渲染
    with open(template_path, 'r') as f:
        template = jinja2.Template(f.read())
    
    rendered_content = template.render(secrets)

    # 4. 產出最終設定檔
    with open(output_path, 'w') as f:
        f.write(rendered_content)

if __name__ == "__main__":
    # 解析參數: --template, --output, --env ...
    # 執行 render()
    pass

MyDeploy.jenkinsfile:建置階段實作

@Library('common-pipeline-library') _

pipeline {
    agent { label 'dotnet' }
    
    parameters {
        string(name: 'PROJECT_ID', defaultValue: 'user-service')
        choice(name: 'DEPLOY_ENV', choices: ['Test', 'Prod'], description: '部署目標環境')
    }

    stages {
        stage('Render Configuration') {
            steps {
                script {
                    // 從 Vault 取得 Token 並執行渲染
                    withVault(configuration: vaultConfig, secrets: vaultSecrets) {
                        sh "python scripts/render_config.py --template appsettings.template.json --output appsettings.json --env ${params.DEPLOY_ENV}"
                    }
                }
            }
        }

        stage('Build & Publish') {
            steps {
                // 產出執行所需的所有依賴檔案
                sh "dotnet publish -c Release -o ./publish"
            }
        }

        stage('Archive Artifacts') {
            steps {
                script {
                    def timestamp = new Date().format("yyyyMMdd-HHmm")
                    env.ZIP_NAME = "${params.PROJECT_ID}-${timestamp}.zip"
                    
                    // 打包 publish 目錄,包含剛才渲染好的配置
                    sh "zip -r ${env.ZIP_NAME} ./publish"
                    
                    // 產生部署元數據 (Metadata),供目標主機讀取
                    def deployParams = """{
                        "AppName": "${params.PROJECT_ID}",
                        "ZipFileName": "${env.ZIP_NAME}",
                        "TargetEnv": "${params.DEPLOY_ENV}"
                    }"""
                    writeFile file: 'deploy_params.json', text: deployParams
                }
            }
        }
    }
}

關鍵設計說明

  1. 部署描述檔 (deploy_params.json):這是 Jenkins (Linux) 與目標主機 (Windows) 之間的「合約」。遠端腳本讀取此檔後,才知道應該處理哪個 Zip 檔案。
  2. dotnet publish:確保產出物是「自包含 (Self-contained)」的,包含所有必要的二進位檔,這正是不可變產出物的精隨。

結語

今天我們完成了「建置」與「配置渲染」。我們現在手上有一份包含環境配置的 Zip 壓縮檔。明天,我們將實作後半段:如何跨越網路邊界,將產出物送達 Windows 伺服器並自動安裝。


上一篇
Day 23 實戰演練:sonarqube.jenkinsfile — 自動化品質掃描管線實作
下一篇
Day 25 MyDeploy.jenkinsfile (下) — 遠端部署與 PR 即時反饋
系列文
迎接 AI 開發爆發期:告別手動部署,帶領企業團隊從 Git 規範到 CI/CD 實戰30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言