游戏 DevOps 最佳实践:构建高效研发运维体系
深入探讨游戏研发的DevOps最佳实践,包括容器化部署、自动化运维、监控告警等
深入探讨游戏研发的DevOps最佳实践,包括容器化部署、自动化运维、监控告警等
深入探讨游戏研发的CI/CD流水线设计,包括自动化构建、测试、发布等全流程
深入探讨CI/CD流水线设计的核心原则、性能优化策略和安全实践,帮助你构建高效可靠的生产级持续集成交付系统。
详细对比Jenkins和GitLab CI的架构差异、功能特性、使用场景和性能表现,帮助你做出正确的技术选型决策。
深入讲解GitHub Actions的核心概念、工作流语法和最佳实践,帮助你构建高效的自动化CI/CD流水线。
引言 云原生架构是现代应用部署的标准模式。本文将深入探讨如何使用Kubernetes、服务网格和DevOps实践构建弹性、可扩展的云原生应用。 一、Kubernetes核心概念 1.1 Pod与Deployment 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 # ========== Pod配置 ========== apiVersion: v1 kind: Pod metadata: name: nginx-pod labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5 --- # ========== Deployment配置 ========== apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 env: - name: ENVIRONMENT value: "production" volumeMounts: - name: config-volume mountPath: /etc/nginx/config.d volumes: - name: config-volume configMap: name: nginx-config 1.2 Service与Ingress 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 # ========== Service配置 ========== apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: ClusterIP selector: app: nginx ports: - port: 80 targetPort: 80 protocol: TCP --- # ========== Ingress配置 ========== apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nginx-ingress annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: tls: - hosts: - app.example.com secretName: app-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: nginx-service port: number: 80 二、服务网格 2.1 Istio配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 # ========== Istio VirtualService ========== apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - match: - headers: end-user: exact: jason fault: delay: percentage: value: 100 fixedDelay: 3s route: - destination: host: reviews subset: v2 - route: - destination: host: reviews subset: v1 --- # ========== Istio DestinationRule ========== apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: reviews spec: host: reviews trafficPolicy: loadBalancer: simple: LEAST_CONN subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 trafficPolicy: loadBalancer: simple: RANDOM 三、云原生最佳实践 3.1 健康检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 livenessProbe: httpGet: path: /health/live port: 8080 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /health/ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 startupProbe: httpGet: path: /health/startup port: 8080 initialDelaySeconds: 0 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 30 3.2 资源限制 1 2 3 4 5 6 7 resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" 总结 云原生架构是现代应用部署的最佳实践。通过Kubernetes、服务网格和DevOps的结合,可以构建弹性、可扩展的应用系统。 ...
引言 在当今快速迭代的软件开发环境中,DevOps实践和CI/CD流水线已成为团队提高交付效率、保证软件质量的关键。本文将深入探讨如何构建完整的DevOps体系,从代码提交到生产部署的全流程自动化实践。 一、DevOps核心理念 1.1 DevOps的CALMS模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 # ========== DevOps CALMS模型 ========== """ C - Culture (文化) ├── 协作精神 ├── 持续改进 ├── 容错心态 └── 透明沟通 A - Automation (自动化) ├── 构建自动化 ├── 测试自动化 ├── 部署自动化 └── 监控自动化 L - Lean (精益) ├── 消除浪费 ├── 持续交付 ├── 快速反馈 └── 价值流优化 M - Measurement (度量) ├── 关键指标追踪 ├── 数据驱动决策 ├── 持续监控 └── 效果评估 S - Sharing (分享) ├── 知识共享 ├── 最佳实践传播 ├── 工具共享 └── 经验总结 """ # DevOps成熟度评估模型 DEVOPS_MATURITY_LEVELS = { "Level 1 - 初始级": { "characteristics": [ "手动操作为主", "缺乏标准化流程", "开发和运维分离" ], "practices": [], "improvements": [ "建立基础自动化流程", "制定标准化规范" ] }, "Level 2 - 可重复级": { "characteristics": [ "有基本的CI流程", "环境配置初步标准化", "文档化流程" ], "practices": [ "版本控制", "单元测试", "基础自动化构建" ], "improvements": [ "扩展自动化范围", "增加测试覆盖率" ] }, "Level 3 - 已定义级": { "characteristics": [ "完整的CI/CD流水线", "基础设施即代码", "自动化测试体系" ], "practices": [ "持续集成", "持续部署", "自动化测试", "配置管理" ], "improvements": [ "优化部署流程", "增强监控能力" ] }, "Level 4 - 可管理级": { "characteristics": [ "全链路自动化", "完善的监控体系", "快速故障恢复" ], "practices": [ "自动化运维", "监控告警", "故障自愈", "性能优化" ], "improvements": [ "持续优化", "成本控制" ] }, "Level 5 - 优化级": { "characteristics": [ "智能运维", "预测性维护", "持续创新" ], "practices": [ "AI辅助决策", "混沌工程", "自动化优化" ], "improvements": [ "持续演进" ] } } 1.2 DevOps指标体系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 # ========== DORA指标 ========== class DORAMetrics: """DORA (DevOps Research and Assessment) 核心指标""" @staticmethod def deployment_frequency(deployments: int, days: int) -> float: """ 部署频率 - 精英级:按需部署 (每天多次) - 高绩效:每周1-6个月 - 中等绩效:每月1-6个月 - 低绩效:少于每6个月1次 """ return deployments / days @staticmethod def lead_time_for_changes(commit_time: str, deploy_time: str) -> float: """ 变更前置时间 从代码提交到成功部署的时间 - 精英级:小于1小时 - 高绩效:小于1天 - 中等绩效:1周-1个月 - 低绩效:超过1个月 """ from datetime import datetime commit = datetime.fromisoformat(commit_time) deploy = datetime.fromisoformat(deploy_time) return (deploy - commit).total_seconds() / 3600 # 小时 @staticmethod def time_to_restore_service(incident_time: str, restore_time: str) -> float: """ 服务恢复时间 - 精英级:小于1小时 - 高绩效:小于1天 - 中等绩效:1天-1周 - 低绩效:超过1周 """ from datetime import datetime incident = datetime.fromisoformat(incident_time) restore = datetime.fromisoformat(restore_time) return (restore - incident).total_seconds() / 3600 # 小时 @staticmethod def change_failure_rate(total_deployments: int, failed_deployments: int) -> float: """ 变更失败率 - 精英级:0-15% - 高绩效:15-30% - 中等绩效:30-60% - 低绩效:超过60% """ return (failed_deployments / total_deployments) * 100 @classmethod def evaluate_performance(cls, metrics: dict) -> str: """评估团队DevOps绩效等级""" score = 0 if metrics['deployment_frequency'] >= 1: # 每天至少1次 score += 1 if metrics['lead_time'] <= 1: # 小于1小时 score += 1 if metrics['restore_time'] <= 1: # 小于1小时 score += 1 if metrics['failure_rate'] <= 15: # 小于15% score += 1 levels = { 4: "精英级", 3: "高绩效", 2: "中等绩效", 1: "低绩效", 0: "低绩效" } return levels[score] # ========== 自定义DevOps指标 ========== class DevOpsMetricsCollector: """DevOps指标收集器""" def __init__(self): self.metrics = { 'builds': [], 'deployments': [], 'incidents': [], 'tests': [] } def record_build(self, build_info: dict): """记录构建信息""" self.metrics['builds'].append({ 'timestamp': build_info['timestamp'], 'branch': build_info['branch'], 'commit': build_info['commit'], 'status': build_info['status'], 'duration': build_info['duration'], 'triggered_by': build_info['triggered_by'] }) def record_deployment(self, deployment_info: dict): """记录部署信息""" self.metrics['deployments'].append({ 'timestamp': deployment_info['timestamp'], 'environment': deployment_info['environment'], 'version': deployment_info['version'], 'status': deployment_info['status'], 'duration': deployment_info['duration'], 'deployed_by': deployment_info['deployed_by'] }) def record_incident(self, incident_info: dict): """记录故障信息""" self.metrics['incidents'].append({ 'detected_at': incident_info['detected_at'], 'resolved_at': incident_info.get('resolved_at'), 'severity': incident_info['severity'], 'affected_services': incident_info['affected_services'], 'root_cause': incident_info.get('root_cause') }) def calculate_metrics(self, days: int = 30) -> dict: """计算DevOps指标""" from datetime import datetime, timedelta cutoff_time = datetime.now() - timedelta(days=days) # 筛选时间范围内的数据 recent_builds = [ b for b in self.metrics['builds'] if datetime.fromisoformat(b['timestamp']) > cutoff_time ] recent_deployments = [ d for d in self.metrics['deployments'] if datetime.fromisoformat(d['timestamp']) > cutoff_time ] recent_incidents = [ i for i in self.metrics['incidents'] if datetime.fromisoformat(i['detected_at']) > cutoff_time ] # 计算指标 total_builds = len(recent_builds) successful_builds = len([b for b in recent_builds if b['status'] == 'success']) build_success_rate = (successful_builds / total_builds * 100) if total_builds > 0 else 0 total_deployments = len(recent_deployments) successful_deployments = len([d for d in recent_deployments if d['status'] == 'success']) deployment_success_rate = (successful_deployments / total_deployments * 100) if total_deployments > 0 else 0 deployment_frequency = total_deployments / days total_incidents = len(recent_incidents) resolved_incidents = len([i for i in recent_incidents if i.get('resolved_at')]) avg_resolution_time = 0 if resolved_incidents > 0: resolution_times = [] for incident in recent_incidents: if incident.get('resolved_at'): detected = datetime.fromisoformat(incident['detected_at']) resolved = datetime.fromisoformat(incident['resolved_at']) resolution_times.append((resolved - detected).total_seconds() / 3600) avg_resolution_time = sum(resolution_times) / len(resolution_times) return { 'build_metrics': { 'total_builds': total_builds, 'success_rate': round(build_success_rate, 2), 'avg_duration': round( sum(b['duration'] for b in recent_builds) / total_builds if total_builds > 0 else 0, 2 ) }, 'deployment_metrics': { 'total_deployments': total_deployments, 'success_rate': round(deployment_success_rate, 2), 'frequency_per_day': round(deployment_frequency, 2) }, 'incident_metrics': { 'total_incidents': total_incidents, 'resolved_incidents': resolved_incidents, 'avg_resolution_time_hours': round(avg_resolution_time, 2) } } 二、持续集成(CI)实践 2.1 Git工作流 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 # ========== Git工作流模型 ========== class GitWorkflow: """Git工作流管理""" BRANCH_STRATEGIES = { "Git Flow": { "branches": { "main": "生产分支,只接受来自release的合并", "develop": "开发主分支", "feature": "功能开发分支,从develop创建", "release": "发布准备分支,从develop创建", "hotfix": "紧急修复分支,从main创建" }, "workflow": """ 1. 从develop创建feature分支 2. 完成开发后合并回develop 3. 准备发布时创建release分支 4. 测试通过后合并到main和develop 5. 紧急修复从main创建hotfix分支 6. 修复后合并到main和develop """ }, "GitHub Flow": { "branches": { "main": "始终可部署的生产分支", "feature": "功能分支,从main创建" }, "workflow": """ 1. 从main创建feature分支 2. 提交并推送到远程 3. 创建Pull Request 4. Code Review和讨论 5. 合并到main 6. 立即部署 """ }, "GitLab Flow": { "branches": { "main": "主分支", "feature": "功能分支", "environment": "环境分支(staging, production)" }, "workflow": """ 1. 从main创建feature分支 2. 完成后合并回main 3. main分支触发CI/CD 4. 通过测试后部署到staging 5. 人工验证后部署到production """ }, "Trunk Based Development": { "branches": { "trunk/main": "主分支,所有开发在此进行" }, "workflow": """ 1. 开发者直接在主分支提交 2. 使用Feature Flags控制功能发布 3. 频繁集成到主分支 4. 持续部署 """ } } class BranchProtectionRules: """分支保护规则""" def __init__(self): self.rules = {} def add_protection(self, branch: str, rules: dict): """添加分支保护规则""" self.rules[branch] = { 'require_pull_request': rules.get('require_pull_request', True), 'require_approvals': rules.get('require_approvals', 1), 'dismiss_stale_reviews': rules.get('dismiss_stale_reviews', True), 'require_status_checks': rules.get('require_status_checks', True), 'required_status_checks': rules.get('required_status_checks', []), 'require_linear_history': rules.get('require_linear_history', True), 'allow_force_pushes': rules.get('allow_force_pushes', False), 'allow_deletions': rules.get('allow_deletions', False) } def check_protection(self, branch: str) -> dict: """检查分支保护状态""" return self.rules.get(branch, {}) 2.2 CI流水线配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 # ========== Jenkins Pipeline ========== # Jenkinsfile pipeline { agent any tools { maven 'Maven 3.8' jdk 'JDK 11' } environment { APP_NAME = 'my-application' VERSION = "${env.BUILD_ID}" REGISTRY = 'registry.example.com' IMAGE_NAME = "${REGISTRY}/${APP_NAME}:${VERSION}" } stages { stage('Checkout') { steps { checkout scm } } stage('Setup') { steps { sh ''' echo "Setting up build environment..." python3 -m venv venv . venv/bin/activate pip install -r requirements.txt ''' } } stage('Code Quality') { parallel { stage('Lint') { steps { sh ''' . venv/bin/activate flake8 src/ --max-line-length=120 ''' } } stage('Security Scan') { steps { sh ''' . venv/bin/activate bandit -r src/ ''' } } } } stage('Unit Tests') { steps { sh ''' . venv/bin/activate pytest tests/unit/ --cov=src --cov-report=xml --cov-report=html ''' } post { always { junit 'test-results/*.xml' publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: 'htmlcov', reportFiles: 'index.html', reportName: 'Coverage Report' ]) } } } stage('Integration Tests') { steps { sh ''' docker-compose -f docker-compose.test.yml up -d sleep 10 . venv/bin/activate pytest tests/integration/ docker-compose -f docker-compose.test.yml down ''' } } stage('Build') { steps { sh ''' . venv/bin/activate python setup.py sdist bdist_wheel ''' } } stage('Docker Build') { steps { script { docker.build(image: IMAGE_NAME) } } } stage('Security Scan Image') { steps { sh ''' trivy image ${IMAGE_NAME} --severity HIGH,CRITICAL ''' } } stage('Push to Registry') { when { branch 'main' } steps { script { docker.withRegistry("https://${REGISTRY}", 'docker-registry-credentials') { docker.image(IMAGE_NAME).push() docker.image(IMAGE_NAME).push('latest') } } } } stage('Deploy to Staging') { when { branch 'main' } steps { sh ''' kubectl set image deployment/${APP_NAME} \ ${APP_NAME}=${IMAGE_NAME} \ --namespace=staging kubectl rollout status deployment/${APP_NAME} \ --namespace=staging ''' } } stage('Smoke Tests') { when { branch 'main' } steps { sh ''' . venv/bin/activate pytest tests/smoke/ --base-url=https://staging.example.com ''' } } stage('Deploy to Production') { when { branch 'main' approval { input 'Deploy to Production?' } } steps { sh ''' kubectl set image deployment/${APP_NAME} \ ${APP_NAME}=${IMAGE_NAME} \ --namespace=production kubectl rollout status deployment/${APP_NAME} \ --namespace=production ''' } } } post { success { echo "Pipeline succeeded!" cleanWs() } failure { echo "Pipeline failed!" mail to: 'team@example.com', subject: "Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}", body: "Check console output at ${env.BUILD_URL}" } always { sh ''' docker system prune -f ''' } } } # ========== GitHub Actions Workflow ========== # .github/workflows/ci-cd.yml name: CI/CD Pipeline on: push: branches: [main, develop] pull_request: branches: [main, develop] env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: lint: name: Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.9' cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install flake8 black isort - name: Run flake8 run: | flake8 src/ tests/ --count --select=E9,F63,F7,F82 --show-source --statistics - name: Check code formatting run: | black --check src/ tests/ - name: Check import ordering run: | isort --check-only src/ tests/ test: name: Test runs-on: ubuntu-latest strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install -r requirements-dev.txt - name: Run tests run: | pytest tests/ --cov=src --cov-report=xml --cov-report=html - name: Upload coverage uses: codecov/codecov-action@v3 with: file: ./coverage.xml flags: unittests name: codecov-umbrella security: name: Security Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run Bandit run: | pip install bandit[toml] bandit -r src/ - name: Run Safety run: | pip install safety safety check --file requirements.txt - name: Run Trivy uses: aquasecurity/trivy-action@master with: scan-type: 'fs' scan-ref: '.' format: 'sarif' output: 'trivy-results.sarif' - name: Upload Trivy results uses: github/codeql-action/upload-sarif@v2 with: sarif_file: 'trivy-results.sarif' build: name: Build Docker Image runs-on: ubuntu-latest needs: [lint, test, security] if: github.event_name == 'push' && github.ref == 'refs/heads/main' permissions: contents: read packages: write steps: - uses: actions/checkout@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Log in to Container Registry uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v4 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix={{branch}}- - name: Build and push uses: docker/build-push-action@v4 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max deploy: name: Deploy runs-on: ubuntu-latest needs: build if: github.event_name == 'push' && github.ref == 'refs/heads/main' environment: name: production url: https://example.com steps: - uses: actions/checkout@v3 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v2 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: us-east-1 - name: Update kubeconfig run: | aws eks update-kubeconfig --name production-cluster --region us-east-1 - name: Deploy to Kubernetes run: | kubectl set image deployment/app \ app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \ --namespace=production - name: Verify deployment run: | kubectl rollout status deployment/app --namespace=production 2.3 测试自动化策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 # ========== 测试金字塔 ========== class TestAutomationStrategy: """测试自动化策略""" TEST_PYRAMID = { "Unit Tests (70%)": { "scope": "单个函数/类", "speed": "毫秒级", "cost": "低", "isolation": "完全隔离", "examples": [ "函数返回值验证", "边界条件测试", "异常处理测试" ] }, "Integration Tests (20%)": { "scope": "多个组件协作", "speed": "秒级", "cost": "中", "isolation": "部分隔离", "examples": [ "API集成测试", "数据库集成测试", "服务间通信测试" ] }, "E2E Tests (10%)": { "scope": "完整用户流程", "speed": "分钟级", "cost": "高", "isolation": "无隔离", "examples": [ "用户注册流程", "购物车完整流程", "支付完整流程" ] } } class AutomatedTestSuite: """自动化测试套件""" def __init__(self): self.unit_tests = [] self.integration_tests = [] self.e2e_tests = [] def add_unit_test(self, test_func): """添加单元测试""" self.unit_tests.append(test_func) def add_integration_test(self, test_func): """添加集成测试""" self.integration_tests.append(test_func) def add_e2e_test(self, test_func): """添加E2E测试""" self.e2e_tests.append(test_func) def run_all_tests(self): """运行所有测试""" results = { 'unit': self._run_tests(self.unit_tests), 'integration': self._run_tests(self.integration_tests), 'e2e': self._run_tests(self.e2e_tests) } return results def _run_tests(self, tests): """运行测试""" passed = 0 failed = 0 for test in tests: try: test() passed += 1 except AssertionError as e: failed += 1 print(f"Test failed: {e}") return { 'total': len(tests), 'passed': passed, 'failed': failed } # ========== 测试驱动开发(TDD)工作流 ========== class TDDWorkflow: """TDD工作流""" @staticmethod def red_green_refactor(feature_name: str): """ Red-Green-Refactor循环 Red: 编写失败的测试 Green: 编写最少代码使测试通过 Refactor: 重构代码 """ print(f"\n=== TDD Workflow for {feature_name} ===") # Red: 编写测试 print("\n[RED] Writing failing test...") test_code = f""" def test_{feature_name}(): result = {feature_name}() assert result is not None """ print(test_code) # Green: 实现功能 print("\n[GREEN] Implementing minimum code to pass...") impl_code = f""" def {feature_name}(): return True """ print(impl_code) # Refactor: 重构 print("\n[REFACTOR] Improving code quality...") print("Optimizing implementation...") return True # ========== 行为驱动开发(BDD) ========== class BDDScenario: """BDD场景定义""" def __init__(self, name: str): self.name = name self.given_steps = [] self.when_steps = [] self.then_steps = [] def given(self, description: str, action=None): """Given步骤""" self.given_steps.append({ 'description': description, 'action': action }) return self def when(self, description: str, action=None): """When步骤""" self.when_steps.append({ 'description': description, 'action': action }) return self def then(self, description: str, assertion=None): """Then步骤""" self.then_steps.append({ 'description': description, 'assertion': assertion }) return self def execute(self): """执行场景""" print(f"\nScenario: {self.name}") # 执行Given步骤 print("\nGiven:") context = {} for step in self.given_steps: print(f" {step['description']}") if step['action']: context.update(step['action'](context) or {}) # 执行When步骤 print("\nWhen:") for step in self.when_steps: print(f" {step['description']}") if step['action']: context.update(step['action'](context) or {}) # 执行Then步骤 print("\nThen:") for step in self.then_steps: print(f" {step['description']}") if step['assertion']: step['assertion'](context) return context # 使用示例 def user_login_scenario(): """用户登录场景""" scenario = BDDScenario("User successfully logs in") scenario.given("a registered user with email 'user@example.com' and password 'password123'") .when("the user submits valid credentials") .then("the user should be authenticated") .then("the user should receive an authentication token") # 实际执行时会实现具体的action和assertion return scenario 三、持续部署(CD)实践 3.1 部署策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 # ========== 部署策略 ========== class DeploymentStrategy: """部署策略""" @staticmethod def rolling_update(deployment_config: dict): """ 滚动更新 逐步替换旧实例,确保服务始终可用 """ total_replicas = deployment_config['replicas'] max_unavailable = deployment_config.get('max_unavailable', 1) max_surge = deployment_config.get('max_surge', 1) print(f"Starting rolling update...") print(f"Total replicas: {total_replicas}") print(f"Max unavailable: {max_unavailable}") print(f"Max surge: {max_surge}") for i in range(total_replicas): print(f"Updating replica {i+1}/{total_replicas}...") # 部署新实例 # 等待健康检查通过 # 终止旧实例 print("Rolling update completed!") @staticmethod def blue_green_deployment(deployment_config: dict): """ 蓝绿部署 并行维护两套环境,通过切换流量实现零停机部署 """ print("Starting blue-green deployment...") # 当前环境 current_env = deployment_config['current_environment'] # 'blue' or 'green' # 目标环境 target_env = 'green' if current_env == 'blue' else 'blue' print(f"Current environment: {current_env}") print(f"Target environment: {target_env}") # 1. 在目标环境部署新版本 print(f"\n1. Deploying new version to {target_env} environment...") # 部署到目标环境 # 2. 等待目标环境健康检查 print(f"\n2. Waiting for {target_env} to be healthy...") # 健康检查 # 3. 切换流量 print(f"\n3. Switching traffic from {current_env} to {target_env}...") # 切换负载均衡器 # 4. 验证新版本 print(f"\n4. Validating {target_env}...") # 运行烟雾测试 # 5. 保留旧环境(可选回滚) print(f"\n5. Keeping {current_env} as rollback option...") print("Blue-green deployment completed!") @staticmethod def canary_deployment(deployment_config: dict): """ 金丝雀部署 逐步将流量引导到新版本,监控问题并快速回滚 """ print("Starting canary deployment...") canary_steps = deployment_config.get('canary_steps', [ {'percentage': 5, 'duration': 300}, # 5% 流量,5分钟 {'percentage': 25, 'duration': 600}, # 25% 流量,10分钟 {'percentage': 50, 'duration': 600}, # 50% 流量,10分钟 {'percentage': 100, 'duration': 0} # 100% 流量 ]) for step in canary_steps: percentage = step['percentage'] duration = step['duration'] print(f"\nRouting {percentage}% of traffic to canary...") # 调整流量分配 # 监控关键指标 # 如果检测到问题,立即回滚 if duration > 0: print(f"Monitoring for {duration} seconds...") # 等待并监控 print("\nAll traffic routed to new version!") print("Canary deployment completed!") @staticmethod def a_b_testing_deployment(deployment_config: dict): """ A/B测试部署 同时运行多个版本,按特定规则分配流量 """ print("Starting A/B testing deployment...") variants = deployment_config['variants'] # [{'name': 'A', 'percentage': 50}, ...] total_percentage = sum(v['percentage'] for v in variants) if total_percentage != 100: raise ValueError(f"Total percentage must be 100%, got {total_percentage}%") print("Traffic allocation:") for variant in variants: print(f" {variant['name']}: {variant['percentage']}%") # 配置流量分配规则 # 设置指标收集 # 配置自动回滚规则 print("A/B testing deployment completed!") # ========== 部署回滚 ========== class DeploymentRollback: """部署回滚""" def __init__(self, deployment_manager): self.deployment_manager = deployment_manager self.rollback_history = [] def execute_rollback(self, target_version: str, reason: str): """执行回滚""" print(f"\nInitiating rollback to version {target_version}") print(f"Reason: {reason}") # 1. 记录回滚 rollback_record = { 'timestamp': datetime.now().isoformat(), 'from_version': self.deployment_manager.get_current_version(), 'to_version': target_version, 'reason': reason } self.rollback_history.append(rollback_record) # 2. 执行回滚 try: # 停止当前版本 print("Stopping current version...") # 部署目标版本 print(f"Deploying version {target_version}...") # 验证回滚 print("Verifying rollback...") print(f"Rollback to version {target_version} completed successfully!") except Exception as e: print(f"Rollback failed: {e}") raise def get_rollback_history(self): """获取回滚历史""" return self.rollback_history 3.2 GitOps实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 # ========== GitOps with ArgoCD ========== # 应用部署清单 # apps/application.yaml apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: my-application namespace: argocd spec: project: default source: repoURL: https://github.com/org/infrastructure.git targetRevision: main path: apps/my-application helm: valueFiles: - values-prod.yaml destination: server: https://kubernetes.default.svc namespace: production syncPolicy: automated: prune: true selfHeal: true syncOptions: - CreateNamespace=true # Kubernetes部署清单 # apps/my-application/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-application labels: app: my-application spec: replicas: 3 selector: matchLabels: app: my-application template: metadata: labels: app: my-application spec: containers: - name: app image: registry.example.com/my-application:{{ .Values.image.tag }} ports: - containerPort: 8080 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 --- apiVersion: v1 kind: Service metadata: name: my-application spec: selector: app: my-application ports: - port: 80 targetPort: 8080 type: ClusterIP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-application annotations: cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: ingressClassName: nginx tls: - hosts: - app.example.com secretName: my-application-tls rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-application port: number: 80 # Helm Values # apps/my-application/values-prod.yaml image: tag: "1.2.3" replicas: 3 resources: requests: memory: "256Mi" cpu: "200m" limits: memory: "512Mi" cpu: "500m" autoscaling: enabled: true minReplicas: 3 maxReplicas: 10 targetCPUUtilizationPercentage: 70 targetMemoryUtilizationPercentage: 80 database: host: postgres-production.example.com port: 5432 name: app_production sslMode: require 四、基础设施即代码(IaC) 4.1 Terraform配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 # ========== Terraform配置示例 ========== # 主配置文件 # main.tf terraform { required_version = ">= 1.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } backend "s3" { bucket = "terraform-state-prod" key = "production/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "terraform-locks" } } provider "aws" { region = var.aws_region default_tags { tags = { Environment = var.environment Project = var.project_name ManagedBy = "Terraform" } } } # VPC配置 # modules/vpc/main.tf resource "aws_vpc" "main" { cidr_block = var.vpc_cidr enable_dns_hostnames = true enable_dns_support = true tags = { Name = "${var.project_name}-vpc" } } resource "aws_internet_gateway" "main" { vpc_id = aws_vpc.main.id tags = { Name = "${var.project_name}-igw" } } resource "aws_subnet" "public" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index) availability_zone = var.availability_zones[count.index] map_public_ip_on_launch = true tags = { Name = "${var.project_name}-public-${count.index}" Type = "Public" } } resource "aws_subnet" "private" { count = length(var.availability_zones) vpc_id = aws_vpc.main.id cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index + length(var.availability_zones)) availability_zone = var.availability_zones[count.index] tags = { Name = "${var.project_name}-private-${count.index}" Type = "Private" } } resource "aws_eip" "nat" { count = length(var.availability_zones) domain = "vpc" tags = { Name = "${var.project_name}-nat-${count.index}" } depends_on = [aws_internet_gateway.main] } resource "aws_nat_gateway" "main" { count = length(var.availability_zones) allocation_id = aws_eip.nat[count.index].id subnet_id = aws_subnet.public[count.index].id tags = { Name = "${var.project_name}-nat-${count.index}" } depends_on = [aws_internet_gateway.main] } # EKS集群配置 # modules/eks/main.tf resource "aws_eks_cluster" "main" { name = "${var.project_name}-cluster" role_arn = aws_iam_role.cluster.arn version = var.kubernetes_version vpc_config { subnet_ids = concat( aws_subnet.private[*].id, aws_subnet.public[*].id ) endpoint_private_access = true endpoint_public_access = true public_access_cidrs = var.allowed_public_access_cidrs } enabled_cluster_log_types = var.cluster_log_types depends_on = [ aws_iam_role_policy_attachment.cluster_amazon_eks_cluster_policy, aws_iam_role_policy_attachment.cluster_amazon_eks_vpc_resource_controller, ] tags = { Name = "${var.project_name}-eks" } } resource "aws_eks_node_group" "main" { cluster_name = aws_eks_cluster.main.name node_group_name = "${var.project_name}-node-group" node_role_arn = aws_iam_role.node.arn subnet_ids = aws_subnet.private[*].id scaling_config { desired_size = var.node_group_desired_size max_size = var.node_group_max_size min_size = var.node_group_min_size } instance_types = var.node_instance_types remote_access { ec2_ssh_key = var.ssh_key_name source_security_group_ids = [aws_security_group.node.id] } labels = { Environment = var.environment Project = var.project_name } tags = { Name = "${var.project_name}-node" } depends_on = [ aws_iam_role_policy_attachment.node_amazon_eks_worker_node_policy, aws_iam_role_policy_attachment.node_amazon_eks_cni_policy, aws_iam_role_policy_attachment.node_amazon_ec2_container_registry_read_only, ] } # 变量定义 # variables.tf variable "aws_region" { description = "AWS region" type = string default = "us-east-1" } variable "environment" { description = "Environment name" type = string validation { condition = contains(["development", "staging", "production"], var.environment) error_message = "Environment must be development, staging, or production." } } variable "project_name" { description = "Project name" type = string } variable "vpc_cidr" { description = "CIDR block for VPC" type = string default = "10.0.0.0/16" } variable "availability_zones" { description = "List of availability zones" type = list(string) default = ["us-east-1a", "us-east-1b", "us-east-1c"] } # 输出定义 # outputs.tf output "cluster_id" { description = "EKS cluster ID" value = aws_eks_cluster.main.id } output "cluster_endpoint" { description = "EKS cluster endpoint" value = aws_eks_cluster.main.endpoint } output "cluster_security_group_id" { description = "Security group ID attached to the EKS cluster" value = aws_eks_cluster.main.vpc_config[0].cluster_security_group_id } output "cluster_certificate_authority_data" { description = "Base64 encoded certificate data required to communicate with the cluster" value = aws_eks_cluster.main.certificate_authority[0].data } 4.2 Docker与Kubernetes配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 # ========== Docker配置 ========== # Dockerfile FROM python:3.11-slim # 设置工作目录 WORKDIR /app # 设置环境变量 ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 # 安装系统依赖 RUN apt-get update && \ apt-get install -y --no-install-recommends \ gcc \ libc6-dev \ && rm -rf /var/lib/apt/lists/* # 复制依赖文件 COPY requirements.txt . # 安装Python依赖 RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY src/ ./src/ COPY config/ ./config/ # 创建非root用户 RUN useradd -m -u 1000 appuser && \ chown -R appuser:appuser /app USER appuser # 暴露端口 EXPOSE 8080 # 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080/health || exit 1 # 启动应用 CMD ["gunicorn", "src.app:app", "--bind", "0.0.0.0:8080", "--workers", "4"] # docker-compose.yml version: '3.8' services: app: build: context: . dockerfile: Dockerfile image: my-application:latest ports: - "8080:8080" environment: - DATABASE_URL=postgresql://user:password@db:5432/app - REDIS_URL=redis://redis:6379 depends_on: db: condition: service_healthy redis: condition: service_started healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 3 start_period: 40s restart: unless-stopped db: image: postgres:15-alpine environment: - POSTGRES_DB=app - POSTGRES_USER=user - POSTGRES_PASSWORD=password volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U user -d app"] interval: 10s timeout: 5s retries: 5 restart: unless-stopped redis: image: redis:7-alpine volumes: - redis_data:/data restart: unless-stopped nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/ssl:/etc/nginx/ssl:ro depends_on: - app restart: unless-stopped volumes: postgres_data: redis_data: # ========== Kubernetes配置 ========== # deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-application labels: app: my-application spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: my-application template: metadata: labels: app: my-application spec: serviceAccountName: my-application securityContext: runAsNonRoot: true runAsUser: 1000 fsGroup: 1000 containers: - name: app image: registry.example.com/my-application:1.0.0 ports: - name: http containerPort: 8080 protocol: TCP env: - name: DATABASE_URL valueFrom: secretKeyRef: name: app-secrets key: database-url - name: REDIS_URL valueFrom: configMapKeyRef: name: app-config key: redis-url resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" livenessProbe: httpGet: path: /health port: http initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: http initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 3 lifecycle: preStop: exec: command: - sh - -c - sleep 15 terminationGracePeriodSeconds: 30 # service.yaml apiVersion: v1 kind: Service metadata: name: my-application spec: type: ClusterIP selector: app: my-application ports: - name: http port: 80 targetPort: http protocol: TCP # hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: my-application spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: my-application 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: 60 selectPolicy: Max 总结 构建高效的DevOps和CI/CD流水线需要: ...
引言 容器化技术彻底改变了应用的部署和运维方式。Docker提供了轻量级的容器化解决方案,而Kubernetes(K8s)则提供了强大的容器编排能力。本文将从零开始,深入讲解如何使用Docker和Kubernetes构建生产级的容器化系统。 一、Docker基础 1.1 Docker核心概念 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # Docker三大核心概念 # 1. 镜像(Image):应用的只读模板 # 2. 容器(Container):镜像的运行实例 # 3. 仓库(Registry):存储和分发镜像 # 查看Docker版本 docker --version docker info # 运行第一个容器 docker run hello-world # 交互式运行容器 docker run -it ubuntu bash 1.2 Dockerfile最佳实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 # 1. 选择合适的基础镜像 # 生产环境推荐使用alpine或distroless FROM node:20-alpine AS builder # 2. 设置工作目录 WORKDIR /app # 3. 优化依赖安装(利用Docker缓存) # 先复制package文件,安装依赖后再复制源代码 COPY package*.json ./ RUN npm ci --only=production # 4. 复制源代码 COPY . . # 5. 构建应用 RUN npm run build # 6. 生产镜像(多阶段构建) FROM node:20-alpine # 7. 创建非root用户 RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 WORKDIR /app # 8. 只复制必要文件 COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules COPY --from=builder --chown=nodejs:nodejs /app/package.json ./ # 9. 切换到非root用户 USER nodejs # 10. 暴露端口 EXPOSE 3000 # 11. 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD node healthcheck.js || exit 1 # 12. 启动命令 CMD ["node", "dist/index.js"] 1.3 Docker Compose本地开发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 # docker-compose.yml version: '3.8' services: # 前端应用 frontend: build: context: ./frontend dockerfile: Dockerfile target: development ports: - "3000:3000" volumes: - ./frontend:/app - /app/node_modules environment: - NODE_ENV=development - API_URL=http://api:4000 depends_on: - api # 后端API api: build: context: ./backend dockerfile: Dockerfile ports: - "4000:4000" environment: - NODE_ENV=development - DATABASE_URL=postgresql://postgres:password@db:5432/myapp - REDIS_URL=redis://redis:6379 depends_on: - db - redis volumes: - ./backend:/app - /app/node_modules # PostgreSQL数据库 db: image: postgres:16-alpine ports: - "5432:5432" environment: - POSTGRES_USER=postgres - POSTGRES_PASSWORD=password - POSTGRES_DB=myapp volumes: - postgres_data:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql # Redis缓存 redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data command: redis-server --appendonly yes # Nginx反向代理 nginx: image: nginx:alpine ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/ssl:/etc/nginx/ssl:ro depends_on: - frontend - api volumes: postgres_data: redis_data: 二、Kubernetes核心概念 2.1 Pod与容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # pod.yaml - 最小部署单元 apiVersion: v1 kind: Pod metadata: name: nginx-pod labels: app: nginx spec: containers: - name: nginx image: nginx:1.25-alpine ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5 volumeMounts: - name: config mountPath: /etc/nginx/conf.d volumes: - name: config configMap: name: nginx-config 2.2 Deployment部署 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 # deployment.yaml - 声明式部署 apiVersion: apps/v1 kind: Deployment metadata: name: web-app labels: app: web spec: # 期望的副本数 replicas: 3 # 选择器 selector: matchLabels: app: web # Pod模板 template: metadata: labels: app: web spec: containers: - name: web image: myregistry/web-app:v1.0.0 ports: - containerPort: 3000 env: - name: NODE_ENV value: "production" - name: DATABASE_URL valueFrom: secretKeyRef: name: db-secret key: url resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 3000 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 3000 initialDelaySeconds: 5 periodSeconds: 5 # 更新策略 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # 最多多1个Pod maxUnavailable: 0 # 不允许不可用Pod # 历史版本限制 revisionHistoryLimit: 10 2.3 Service服务发现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # service.yaml - 服务暴露 apiVersion: v1 kind: Service metadata: name: web-service spec: # ClusterIP: 集群内部访问(默认) # NodePort: 通过节点IP:端口访问 # LoadBalancer: 云服务商负载均衡器 type: ClusterIP # 选择器 selector: app: web # 端口配置 ports: - name: http protocol: TCP port: 80 # Service端口 targetPort: 3000 # Pod端口 # Session保持(可选) sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 # ingress.yaml - HTTP路由 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: web-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/ssl-redirect: "true" cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: ingressClassName: nginx tls: - hosts: - example.com secretName: web-tls rules: - host: example.com http: paths: - path: / pathType: Prefix backend: service: name: web-service port: number: 80 三、Kubernetes高级配置 3.1 ConfigMap配置管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: app-config data: # 键值对配置 NODE_ENV: "production" LOG_LEVEL: "info" # 文件配置 app.json: | { "port": 3000, "database": { "host": "postgres.default.svc.cluster.local", "port": 5432 } } nginx.conf: | server { listen 80; location / { proxy_pass http://web-service:3000; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # 使用ConfigMap apiVersion: v1 kind: Pod metadata: name: app-pod spec: containers: - name: app image: myapp:latest env: # 从ConfigMap读取环境变量 - name: NODE_ENV valueFrom: configMapKeyRef: name: app-config key: NODE_ENV volumeMounts: # 挂载文件 - name: config mountPath: /etc/app readOnly: true volumes: - name: config configMap: name: app-config items: - key: app.json path: config.json - key: nginx.conf path: nginx.conf 3.2 Secret密钥管理 1 2 3 4 5 6 7 8 9 10 11 # 创建Secret apiVersion: v1 kind: Secret metadata: name: db-secret type: Opaque data: # Base64编码的值 username: cG9zdGdyZXM= password: cGFzc3dvcmQ= url: cG9zdGdyZXNxbDovL3Bvc3RncmVzOnBhc3N3b3JkQGRiOjU0MzIvbXlhcHA= 1 2 3 4 5 6 7 8 9 10 # 从命令行创建Secret kubectl create secret generic db-secret \ --from-literal=username=postgres \ --from-literal=password=password \ --from-file=cert=./tls.crt # 从文件创建 kubectl create secret tls web-tls \ --cert=path/to/tls.cert \ --key=path/to/tls.key 3.3 PersistentVolume持久化存储 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # persistent-volume.yaml apiVersion: v1 kind: PersistentVolume metadata: name: pv-data spec: capacity: storage: 10Gi accessModes: - ReadWriteOnce # RWO, RWX, ROX persistentVolumeReclaimPolicy: Retain # Retain, Delete, Recycle storageClassName: standard hostPath: path: /data/volume 1 2 3 4 5 6 7 8 9 10 11 12 # persistent-volume-claim.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-data spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: standard 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # 使用PVC apiVersion: v1 kind: Pod metadata: name: app-pod spec: containers: - name: app image: myapp:latest volumeMounts: - name: data mountPath: /app/data volumes: - name: data persistentVolumeClaim: claimName: pvc-data 四、自动扩缩容 4.1 Horizontal Pod Autoscaler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 # hpa.yaml - 水平Pod自动扩缩容 apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: web-hpa spec: # 扩缩容目标 scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: web-app # 最小和最大副本数 minReplicas: 2 maxReplicas: 10 # 指标配置 metrics: # CPU使用率 - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 # 内存使用量 - type: Resource resource: name: memory target: type: AverageValue averageValue: 512Mi # 自定义指标 - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: "100" # 行为配置 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 50 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 0 policies: - type: Percent value: 100 periodSeconds: 15 - type: Pods value: 2 periodSeconds: 15 selectPolicy: Max 4.2 Vertical Pod Autoscaler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 # vpa.yaml - 垂直Pod自动扩缩容 apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: web-vpa spec: targetRef: apiVersion: apps/v1 kind: Deployment name: web-app updatePolicy: updateMode: Auto # Off, Initial, Recreate, Auto resourcePolicy: containerPolicies: - containerName: '*' minAllowed: cpu: 100m memory: 128Mi maxAllowed: cpu: 1 memory: 1Gi controlledResources: ["cpu", "memory"] controlledValues: RequestsAndLimits 五、CI/CD集成 5.1 GitHub Actions CI/CD 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 # .github/workflows/deploy-k8s.yml name: Build and Deploy to Kubernetes on: push: branches: [main] pull_request: branches: [main] env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=branch type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix={{branch}}- - name: Build and push Docker image 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 - name: Install kubectl uses: azure/setup-kubectl@v3 with: version: 'latest' - name: Configure kubectl run: | echo "${{ secrets.KUBE_CONFIG }}" | base64 -d > kubeconfig export KUBECONFIG=kubeconfig - name: Deploy to Kubernetes run: | # 更新deployment中的镜像 kubectl set image deployment/web-app \ web=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} # 等待部署完成 kubectl rollout status deployment/web-app # 验证部署 kubectl get pods -l app=web 5.2 Helm Charts管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 # Helm Chart values.yaml replicaCount: 3 image: repository: ghcr.io/myorg/myapp pullPolicy: IfNotPresent tag: "v1.0.0" imagePullSecrets: [] service: type: ClusterIP port: 80 targetPort: 3000 ingress: enabled: true className: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod hosts: - host: app.example.com paths: - path: / pathType: Prefix tls: - secretName: app-tls hosts: - app.example.com resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" autoscaling: enabled: true minReplicas: 2 maxReplicas: 10 targetCPUUtilizationPercentage: 70 targetMemoryUtilizationPercentage: 80 database: host: postgres.default.svc.cluster.local port: 5432 name: myapp existingSecret: db-secret 1 2 3 4 5 6 7 8 # 部署Helm Chart helm upgrade --install myapp ./chart \ --namespace production \ --create-namespace \ --values values.yaml \ --set image.tag=${IMAGE_TAG} \ --wait \ --timeout 5m 六、监控与日志 6.1 Prometheus监控 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # ServiceMonitor for Prometheus Operator apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: web-app labels: app: web spec: selector: matchLabels: app: web endpoints: - port: http path: /metrics interval: 30s scrapeTimeout: 10s 6.2 日志收集 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # Fluentd ConfigMap apiVersion: v1 kind: ConfigMap metadata: name: fluentd-config data: fluent.conf: | <source> @type tail path /var/log/containers/*.log pos_file /var/log/fluentd-containers.log.pos tag kubernetes.* read_from_head true <parse> @type json </parse> </source> <filter kubernetes.**> @type kubernetes_metadata </filter> <match **> @type elasticsearch host elasticsearch.logging.svc.cluster.local port 9200 logstash_format true logstash_prefix k8s </match> 总结 Docker和Kubernetes构建了现代容器化部署的基础: ...
深入探讨2025年云原生部署的最新技术和最佳实践,包括Kubernetes、Serverless、微服务架构、DevOps流水线、可观测性和自动化运维,帮助企业构建高效、可扩展的云原生应用。
全面介绍现代服务器运维的核心技术和最佳实践,包括自动化部署、监控告警、性能优化、故障排除和安全加固,帮助运维团队构建高效、可靠的服务器管理体系。