現代軟體開發環境中,應用程式的快速迭代與可靠交付已成為企業競爭力的關鍵指標。雲端原生應用程式自動化佈署技術的成熟,為開發團隊帶來了前所未有的效率提升與風險降低。從基礎設施即程式碼的宣告式管理,到持續整合與持續佈署的自動化流程,再到容器化技術與容器協調工具的完美結合,這些技術共同構建了現代化應用程式交付的完整生態系統。本文將深入探討這些核心技術的原理與實踐,透過詳細的程式碼範例與流程圖解析,協助工程團隊掌握自動化佈署的精髓,建立穩定可靠的軟體交付流程。
雲端原生自動化佈署的核心挑戰
在雲端運算環境中佈署應用程式是一個涉及多層次技術堆疊的複雜工程。現代微服務架構的應用程式往往包含數十個甚至上百個相互依賴的服務元件,這些元件需要在不同的環境中保持一致性,同時還要滿足高可用性、可擴展性與安全性的要求。傳統的手動佈署方式在面對這樣的複雜度時,不僅效率低下,更容易因人為失誤導致嚴重的生產環境問題。
佈署過程中的環境一致性問題是最常見的挑戰之一。開發環境、測試環境與生產環境之間的細微差異,往往導致「在我的電腦上可以運作」這類難以除錯的問題。這些差異可能來自作業系統版本、函式庫依賴、環境變數設定或網路配置等多個層面。確保跨環境的一致性需要系統化的方法與工具支援。
依賴關係的管理同樣充滿挑戰。一個典型的應用程式可能依賴資料庫、快取系統、訊息佇列、外部 API 等多種服務。這些依賴項之間的啟動順序、版本相容性、配置參數都需要精確控制。當依賴關係變得複雜時,手動管理這些細節幾乎不可能做到零失誤。
佈署過程的可回復性與錯誤處理機制也是關鍵考量。當新版本的應用程式出現問題時,需要能夠快速回復到上一個穩定版本,將對使用者的影響降到最低。這要求佈署系統具備完善的版本管理、狀態追蹤與自動回復能力。同時,佈署過程中的每個步驟都應該有清晰的錯誤處理邏輯,避免系統陷入不一致的狀態。
效能與資源最佳化是另一個不容忽視的面向。在雲端環境中,計算資源的使用直接關聯到成本。如何在保證服務品質的前提下,透過自動擴展、資源排程與負載平衡來最佳化資源使用,需要精心設計的佈署策略與監控機制。
安全性與合規性要求在自動化佈署中同樣重要。從程式碼儲存庫到生產環境的整個交付鏈路上,都需要嚴格的存取控制、機密資訊管理與稽核記錄。特別是在處理敏感資料或面對監管要求時,自動化佈署系統必須內建安全最佳實踐。
Infrastructure as Code 基礎設施管理
基礎設施即程式碼是現代雲端運算的基石技術,它將傳統上透過圖形介面或命令列手動操作的基礎設施管理工作,轉化為可版本控制、可複製、可測試的程式碼。這種轉變帶來了革命性的改變,使得基礎設施的建立、修改與銷毀變得像軟體開發一樣可靠與高效。
Terraform 作為 Infrastructure as Code 領域的領導工具,透過宣告式的配置語言 HCL 讓工程師能夠清晰地描述所需的基礎設施狀態。Terraform 的強大之處在於它的雲端無關特性,支援 AWS、Azure、GCP 等主流雲端平台,以及眾多第三方服務提供者。這使得團隊可以使用統一的工具和語法管理多雲環境的基礎設施。
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "terraform-state-bucket"
key = "production/infrastructure.tfstate"
region = "ap-northeast-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
}
}
}
variable "aws_region" {
description = "AWS 部署區域"
type = string
default = "ap-northeast-1"
}
variable "environment" {
description = "環境名稱"
type = string
validation {
condition = contains(["development", "staging", "production"], var.environment)
error_message = "環境必須是 development、staging 或 production"
}
}
variable "instance_type" {
description = "EC2 執行個體類型"
type = string
default = "t3.medium"
}
variable "project_name" {
description = "專案名稱"
type = string
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"]
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc-${var.environment}"
}
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = "10.0.${count.index + 1}.0/24"
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-subnet-${count.index + 1}-${var.environment}"
Type = "Public"
}
}
data "aws_availability_zones" "available" {
state = "available"
}
resource "aws_security_group" "web" {
name_prefix = "${var.project_name}-web-sg-"
description = "安全群組用於 Web 伺服器"
vpc_id = aws_vpc.main.id
ingress {
description = "允許 HTTPS 流量"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "允許 HTTP 流量"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
description = "允許所有對外流量"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-web-sg-${var.environment}"
}
lifecycle {
create_before_destroy = true
}
}
resource "aws_instance" "web" {
count = var.environment == "production" ? 3 : 1
ami = data.aws_ami.ubuntu.id
instance_type = var.instance_type
subnet_id = aws_subnet.public[count.index % length(aws_subnet.public)].id
vpc_security_group_ids = [aws_security_group.web.id]
user_data = templatefile("${path.module}/scripts/init.sh", {
environment = var.environment
project_name = var.project_name
})
root_block_device {
volume_type = "gp3"
volume_size = 30
delete_on_termination = true
encrypted = true
}
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}
tags = {
Name = "${var.project_name}-web-${count.index + 1}-${var.environment}"
Role = "WebServer"
}
lifecycle {
ignore_changes = [ami]
}
}
output "instance_ids" {
description = "Web 伺服器執行個體 ID"
value = aws_instance.web[*].id
}
output "instance_public_ips" {
description = "Web 伺服器公開 IP 位址"
value = aws_instance.web[*].public_ip
}
output "vpc_id" {
description = "VPC ID"
value = aws_vpc.main.id
}
這段 Terraform 配置展示了一個完整的雲端基礎設施定義。首先定義了 Terraform 的版本要求與提供者配置,並設定了遠端狀態儲存於 S3,搭配 DynamoDB 實現狀態鎖定機制以避免並行修改衝突。透過變數系統,配置具備良好的可重用性與彈性,能夠支援不同環境的需求。
資料來源區塊用於查詢最新的 Ubuntu AMI 映像,確保使用的是經過驗證的官方映像。VPC 與子網路的配置建立了網路隔離的基礎,使用多個可用區域來提高可用性。安全群組的定義遵循最小權限原則,只開放必要的連接埠,並啟用加密與安全的 metadata 服務配置。
執行個體的數量根據環境動態調整,生產環境使用三個執行個體以提供高可用性,而開發環境僅使用單一執行個體以節省成本。user_data 腳本可以在執行個體啟動時自動執行初始化任務,實現完全自動化的伺服器配置。
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 120
start
:編寫 Terraform 配置檔案;
:執行 terraform init;
note right
初始化工作目錄
下載必要的提供者外掛
配置後端狀態儲存
end note
:執行 terraform plan;
note right
生成執行計畫
顯示將要建立的資源
驗證配置語法
end note
if (計畫檢查通過?) then (是)
:執行 terraform apply;
note right
建立或更新基礎設施
按照依賴順序執行
記錄狀態變更
end note
:基礎設施佈署完成;
:輸出資源資訊;
else (否)
:修正配置錯誤;
note right
檢查語法錯誤
驗證資源配置
確認權限設定
end note
:重新執行 plan;
endif
stop
@endumlCI/CD 持續整合與持續佈署
持續整合與持續佈署是現代軟體開發的核心實踐,透過自動化的建置、測試與佈署流程,大幅縮短從程式碼提交到生產環境上線的時間。GitHub Actions 作為整合於 GitHub 平台的 CI/CD 工具,提供了強大而靈活的工作流程定義能力,讓開發團隊能夠輕鬆建立複雜的自動化流程。
一個完善的 CI/CD 流程應該涵蓋程式碼品質檢查、自動化測試、安全掃描、映像建置、映像推送與自動佈署等多個階段。每個階段都應該有明確的成功與失敗標準,並在出現問題時立即中斷流程並通知相關人員。
name: 雲端原生應用程式 CI/CD 流程
on:
push:
branches:
- main
- develop
- 'release/**'
tags:
- 'v*'
pull_request:
branches:
- main
- develop
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
NODE_VERSION: '20'
TERRAFORM_VERSION: '1.6.0'
jobs:
code-quality:
name: 程式碼品質檢查
runs-on: ubuntu-latest
steps:
- name: 檢出程式碼
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 設定 Node.js 環境
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: 安裝依賴套件
run: npm ci
- name: 執行 ESLint 程式碼檢查
run: npm run lint
- name: 執行 Prettier 格式檢查
run: npm run format:check
- name: 執行型別檢查
run: npm run type-check
security-scan:
name: 安全性掃描
runs-on: ubuntu-latest
steps:
- name: 檢出程式碼
uses: actions/checkout@v4
- name: 執行 npm audit 檢查
run: npm audit --production
continue-on-error: true
- name: 執行 Snyk 安全掃描
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
continue-on-error: true
- name: 執行 Trivy 漏洞掃描
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: 上傳掃描結果
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
test:
name: 自動化測試
runs-on: ubuntu-latest
needs: [code-quality]
strategy:
matrix:
node-version: [18, 20]
steps:
- name: 檢出程式碼
uses: actions/checkout@v4
- name: 設定 Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: 安裝依賴套件
run: npm ci
- name: 執行單元測試
run: npm run test:unit
- name: 執行整合測試
run: npm run test:integration
- name: 生成測試覆蓋率報告
run: npm run test:coverage
- name: 上傳覆蓋率報告到 Codecov
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/coverage-final.json
flags: unittests
name: codecov-${{ matrix.node-version }}
build:
name: 建置 Docker 映像
runs-on: ubuntu-latest
needs: [test, security-scan]
permissions:
contents: read
packages: write
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
image-digest: ${{ steps.build.outputs.digest }}
steps:
- name: 檢出程式碼
uses: actions/checkout@v4
- name: 設定 Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 登入容器登錄庫
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 提取 Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
- name: 建置並推送 Docker 映像
id: build
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
NODE_VERSION=${{ env.NODE_VERSION }}
BUILD_DATE=${{ github.event.head_commit.timestamp }}
VCS_REF=${{ github.sha }}
deploy-staging:
name: 佈署到測試環境
runs-on: ubuntu-latest
needs: [build]
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://staging.example.com
steps:
- name: 檢出程式碼
uses: actions/checkout@v4
- name: 設定 kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: 配置 Kubernetes 認證
run: |
echo "${{ secrets.KUBE_CONFIG_STAGING }}" | base64 -d > kubeconfig
export KUBECONFIG=./kubeconfig
- name: 更新 Kubernetes 佈署
run: |
kubectl set image deployment/webapp \
webapp=${{ needs.build.outputs.image-tag }} \
-n staging
kubectl rollout status deployment/webapp -n staging --timeout=5m
- name: 執行煙霧測試
run: |
sleep 30
curl -f https://staging.example.com/health || exit 1
deploy-production:
name: 佈署到生產環境
runs-on: ubuntu-latest
needs: [build]
if: startsWith(github.ref, 'refs/tags/v')
environment:
name: production
url: https://example.com
steps:
- name: 檢出程式碼
uses: actions/checkout@v4
- name: 設定 kubectl
uses: azure/setup-kubectl@v3
with:
version: 'v1.28.0'
- name: 配置 Kubernetes 認證
run: |
echo "${{ secrets.KUBE_CONFIG_PRODUCTION }}" | base64 -d > kubeconfig
export KUBECONFIG=./kubeconfig
- name: 執行金絲雀佈署
run: |
kubectl set image deployment/webapp-canary \
webapp=${{ needs.build.outputs.image-tag }} \
-n production
kubectl rollout status deployment/webapp-canary -n production --timeout=5m
- name: 監控金絲雀指標
run: |
sleep 300
./scripts/check-canary-metrics.sh
- name: 完整佈署到生產環境
run: |
kubectl set image deployment/webapp \
webapp=${{ needs.build.outputs.image-tag }} \
-n production
kubectl rollout status deployment/webapp -n production --timeout=10m
- name: 執行生產環境健康檢查
run: |
sleep 30
curl -f https://example.com/health || exit 1
- name: 發送佈署通知
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: '生產環境佈署完成'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
if: always()
這個 GitHub Actions 工作流程展示了一個企業級 CI/CD 流程的完整實作。流程從程式碼品質檢查開始,透過 ESLint、Prettier 與型別檢查確保程式碼符合團隊標準。安全性掃描階段整合了多個工具,從依賴套件漏洞到檔案系統安全進行全面檢查。
自動化測試階段使用矩陣策略在多個 Node.js 版本上執行測試,確保應用程式的相容性。測試覆蓋率報告自動上傳到 Codecov,提供可視化的品質追蹤。建置階段使用 Docker Buildx 的快取機制,大幅提升建置速度,同時生成豐富的 metadata 用於映像標記。
佈署階段根據分支與標籤自動決定目標環境。測試環境使用簡單的滾動更新策略,而生產環境採用金絲雀佈署模式,先在小範圍內驗證新版本,確認穩定後再全面推出。整個流程具備完善的錯誤處理與通知機制,確保團隊能及時掌握佈署狀態。
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 120
start
:開發者推送程式碼;
fork
:程式碼品質檢查;
note right
ESLint 語法檢查
Prettier 格式驗證
TypeScript 型別檢查
end note
fork again
:安全性掃描;
note right
依賴套件漏洞檢查
原始碼安全分析
容器映像掃描
end note
end fork
if (品質與安全檢查通過?) then (是)
:執行自動化測試;
fork
:單元測試;
fork again
:整合測試;
fork again
:端對端測試;
end fork
if (所有測試通過?) then (是)
:建置 Docker 映像;
:推送映像到登錄庫;
if (目標分支為 develop?) then (是)
:佈署到測試環境;
:執行煙霧測試;
elseif (存在版本標籤?) then (是)
:金絲雀佈署;
:監控金絲雀指標;
if (指標正常?) then (是)
:完整佈署到生產環境;
:執行健康檢查;
:發送成功通知;
else (否)
:回復金絲雀佈署;
:發送失敗警報;
stop
endif
endif
else (否)
:標記建置失敗;
:發送失敗通知;
stop
endif
else (否)
:標記檢查失敗;
:發送失敗通知;
stop
endif
:佈署流程完成;
stop
@endumlDocker 容器化技術實踐
容器化技術徹底改變了應用程式的封裝與佈署方式。Docker 透過將應用程式及其所有依賴項打包成獨立的容器映像,解決了「在我的電腦上可以運作」的經典問題。容器提供了一致的執行環境,無論是在開發者的筆記型電腦、測試伺服器還是生產環境的叢集中,應用程式都能以相同的方式運作。
一個優秀的 Dockerfile 應該遵循多項最佳實踐。使用多階段建置可以大幅縮小最終映像的大小,將建置時依賴與執行時依賴分離。適當的層快取策略能夠加快建置速度,將不常變動的內容放在較早的層中。安全性考量包括使用非特權使用者執行應用程式、掃描已知漏洞,以及最小化攻擊面。
# 多階段建置 - 建置階段
FROM node:20-alpine AS builder
# 設定工作目錄
WORKDIR /app
# 複製依賴套件定義檔案
COPY package*.json ./
COPY tsconfig.json ./
# 安裝建置依賴
RUN npm ci --only=production && \
npm cache clean --force
# 複製原始碼
COPY src ./src
COPY public ./public
# 建置應用程式
RUN npm run build
# 執行時階段
FROM node:20-alpine AS runtime
# 設定環境變數
ENV NODE_ENV=production \
PORT=3000 \
TZ=Asia/Taipei
# 安裝 dumb-init 用於訊號處理
RUN apk add --no-cache dumb-init
# 建立非特權使用者
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# 設定工作目錄
WORKDIR /app
# 從建置階段複製檔案
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
# 切換到非特權使用者
USER nodejs
# 暴露應用程式連接埠
EXPOSE 3000
# 健康檢查
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# 使用 dumb-init 作為進程管理器
ENTRYPOINT ["dumb-init", "--"]
# 啟動應用程式
CMD ["node", "dist/server.js"]
這個 Dockerfile 展示了現代容器映像建置的最佳實踐。多階段建置將建置過程與最終執行映像分離,確保生產映像只包含執行所需的檔案,大幅減小映像大小並提升安全性。使用 Alpine Linux 作為基礎映像進一步縮小了體積,同時保持了完整的功能性。
建置階段明確地分離了依賴安裝與應用程式建置步驟,利用 Docker 的層快取機制優化建置時間。當原始碼變更但依賴未變時,可以重用快取的依賴層,顯著加快建置速度。執行階段建立了專用的非特權使用者,遵循最小權限原則,降低容器被攻破後的安全風險。
健康檢查定義確保容器編排系統能夠正確監控應用程式狀態,在應用程式無回應時自動重啟容器。dumb-init 的使用解決了容器中訊號處理的常見問題,確保應用程式能夠正確接收並處理終止訊號,實現優雅關閉。
對於更複雜的應用程式,可能需要配置檔案的動態載入、機密資訊的安全處理,以及與外部服務的整合。Docker Compose 提供了在本地環境中組織多容器應用程式的便捷方式。
version: '3.9'
services:
webapp:
build:
context: .
dockerfile: Dockerfile
args:
NODE_VERSION: '20'
image: webapp:latest
container_name: webapp
restart: unless-stopped
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@postgres:5432/webapp
- REDIS_URL=redis://redis:6379
- LOG_LEVEL=info
env_file:
- .env
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- app-network
volumes:
- ./logs:/app/logs
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
postgres:
image: postgres:16-alpine
container_name: postgres
restart: unless-stopped
environment:
- POSTGRES_DB=webapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass password
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:alpine
container_name: nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
depends_on:
- webapp
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
postgres-data:
driver: local
redis-data:
driver: local
這個 Docker Compose 配置定義了一個完整的應用程式堆疊,包括 Web 應用程式、PostgreSQL 資料庫、Redis 快取與 Nginx 反向代理。透過依賴關係與健康檢查的配置,確保服務按正確順序啟動,並在所有依賴服務就緒後才開始處理請求。
資料持久化透過具名卷宗實現,確保資料在容器重啟後不會遺失。網路隔離提供了基本的安全性,所有服務在同一個自定義網路中通訊,可以透過服務名稱進行互相尋址。環境變數與配置檔案的分離使得相同的映像可以在不同環境中使用,只需調整配置即可。
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 120
package "容器映像建置流程" {
start
:編寫 Dockerfile;
:執行 docker build;
partition "建置階段" {
:載入基礎映像;
:安裝建置依賴;
:複製原始碼;
:編譯應用程式;
}
partition "執行時階段" {
:載入執行時基礎映像;
:建立非特權使用者;
:複製編譯產物;
:設定環境變數;
:定義健康檢查;
:指定啟動命令;
}
:生成容器映像;
:標記映像版本;
:執行安全掃描;
if (掃描通過?) then (是)
:推送映像到登錄庫;
:映像可供佈署使用;
else (否)
:修正安全問題;
:重新建置映像;
endif
stop
}
@endumlKubernetes 容器協調管理
Kubernetes 作為雲端原生應用程式的事實標準容器協調平台,提供了強大的自動化佈署、擴展與管理能力。它將基礎設施抽象化,讓開發者能夠專注於應用程式邏輯,而由 Kubernetes 負責處理底層的資源排程、負載平衡、故障恢復等複雜任務。
一個完整的 Kubernetes 佈署通常包含多個資源物件,每個物件負責應用程式的不同面向。Deployment 管理應用程式的副本集與滾動更新策略,Service 提供穩定的網路端點,ConfigMap 與 Secret 分別管理配置與敏感資訊,而 Ingress 則處理外部流量的路由。
apiVersion: v1
kind: Namespace
metadata:
name: webapp-production
labels:
environment: production
app: webapp
---
apiVersion: v1
kind: ConfigMap
metadata:
name: webapp-config
namespace: webapp-production
data:
NODE_ENV: "production"
LOG_LEVEL: "info"
API_TIMEOUT: "30000"
MAX_CONNECTIONS: "100"
---
apiVersion: v1
kind: Secret
metadata:
name: webapp-secrets
namespace: webapp-production
type: Opaque
stringData:
database-url: "postgresql://user:password@postgres.database:5432/webapp"
redis-url: "redis://redis.cache:6379"
jwt-secret: "your-jwt-secret-key"
api-key: "your-external-api-key"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
namespace: webapp-production
labels:
app: webapp
version: v1
spec:
replicas: 3
revisionHistoryLimit: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
spec:
serviceAccountName: webapp
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
containers:
- name: webapp
image: ghcr.io/organization/webapp:v1.2.3
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 3000
protocol: TCP
env:
- name: NODE_ENV
valueFrom:
configMapKeyRef:
name: webapp-config
key: NODE_ENV
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: webapp-config
key: LOG_LEVEL
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: webapp-secrets
key: database-url
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: webapp-secrets
key: redis-url
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"
livenessProbe:
httpGet:
path: /health/liveness
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/readiness
port: http
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
startupProbe:
httpGet:
path: /health/startup
port: http
initialDelaySeconds: 0
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 30
volumeMounts:
- name: config
mountPath: /app/config
readOnly: true
- name: logs
mountPath: /app/logs
volumes:
- name: config
configMap:
name: webapp-config
- name: logs
emptyDir: {}
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- webapp
topologyKey: kubernetes.io/hostname
---
apiVersion: v1
kind: Service
metadata:
name: webapp
namespace: webapp-production
labels:
app: webapp
spec:
type: ClusterIP
sessionAffinity: None
ports:
- name: http
port: 80
targetPort: http
protocol: TCP
selector:
app: webapp
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: webapp-hpa
namespace: webapp-production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: webapp
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 30
- type: Pods
value: 2
periodSeconds: 30
selectPolicy: Max
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
namespace: webapp-production
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
tls:
- hosts:
- webapp.example.com
secretName: webapp-tls
rules:
- host: webapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp
port:
number: 80
這個完整的 Kubernetes 配置展示了生產環境應用程式佈署的各個面向。Deployment 定義了三個副本以提供高可用性,採用滾動更新策略確保零停機時間佈署。資源請求與限制的設定確保每個 Pod 獲得足夠的資源,同時防止資源耗盡影響其他應用程式。
健康檢查機制包含三種探針,分別用於不同目的。啟動探針給予應用程式足夠的時間完成初始化,存活探針檢測應用程式是否仍在運作,就緒探針確認應用程式是否準備好接收流量。這種多層次的健康檢查確保 Kubernetes 能夠準確判斷 Pod 的狀態並做出正確的調度決策。
HorizontalPodAutoscaler 根據 CPU 與記憶體使用率自動調整副本數量,在流量高峰時快速擴展,在流量下降時逐步縮減。擴展行為的精細控制避免了過於頻繁的擴縮容操作,提供了穩定的服務品質。Ingress 配置處理外部流量路由,整合 cert-manager 自動管理 TLS 憑證,確保安全的 HTTPS 連線。
@startuml
!define PLANTUML_FORMAT svg
!theme _none_
skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 140
package "Kubernetes 叢集" {
rectangle "Master 節點" {
component "API Server" as api
component "Scheduler" as sched
component "Controller Manager" as ctrl
component "etcd" as etcd
}
package "Worker 節點 1" {
component "kubelet" as kb1
component "kube-proxy" as kp1
rectangle "Pod 1" {
component "Container A" as c1a
component "Container B" as c1b
}
}
package "Worker 節點 2" {
component "kubelet" as kb2
component "kube-proxy" as kp2
rectangle "Pod 2" {
component "Container A" as c2a
component "Container B" as c2b
}
}
package "Worker 節點 3" {
component "kubelet" as kb3
component "kube-proxy" as kp3
rectangle "Pod 3" {
component "Container A" as c3a
component "Container B" as c3b
}
}
}
api --> etcd
api --> sched
api --> ctrl
api --> kb1
api --> kb2
api --> kb3
kb1 --> c1a
kb1 --> c1b
kb2 --> c2a
kb2 --> c2b
kb3 --> c3a
kb3 --> c3b
@enduml隨著雲端原生技術的持續成熟,自動化佈署已從錦上添花的進階功能,轉變為現代軟體開發不可或缺的基礎能力。從 Infrastructure as Code 的基礎設施管理、CI/CD 的流程自動化、Docker 的容器封裝到 Kubernetes 的編排調度,每個環節都在各自的領域解決了特定的挑戰,而它們的有機結合則構成了一個完整的應用程式交付體系。
玄貓認為,掌握這些技術不僅僅是學會使用工具,更重要的是理解背後的設計理念與最佳實踐。Infrastructure as Code 教會我們用程式碼思維管理基礎設施,CI/CD 強調快速反饋與持續改進,容器化解決了環境一致性問題,而 Kubernetes 則提供了大規模應用程式管理的標準化方案。
在實際應用中,團隊需要根據自身的技術棧、團隊規模與業務需求,選擇適合的工具組合與實施策略。對於小型團隊或初創專案,可以從簡單的 Docker Compose 與 GitHub Actions 開始,逐步累積經驗。當應用規模擴大後,再引入 Kubernetes 與 Terraform 等更強大的工具。重要的是建立正確的思維模式與工作流程,而非盲目追求技術的先進性。
未來的發展趨勢將聚焦於更高層次的抽象與更智慧的自動化。Serverless 技術持續降低應用程式佈署的複雜度,Service Mesh 提供了更精細的服務治理能力,GitOps 將宣告式配置與版本控制深度整合。同時,AI 與機器學習技術也開始應用於佈署流程的優化,例如智慧化的資源調度、異常檢測與自動修復。
對於工程團隊而言,投資於自動化佈署能力的建設將帶來長期的回報。不僅能夠提升交付速度與品質,更能釋放工程師的時間去關注更有價值的創新工作。在快速變化的技術環境中,建立可靠、高效的自動化佈署體系,是保持競爭力的關鍵所在。