折腾侠
技术教程

云原生时代必学:Docker + Kubernetes 容器化部署实战指南

容器化已经不再是"可选项",而是现代开发的"必选项"。本文带你从零开始,掌握 Docker 和 Kubernetes 的核心技能,完成一次完整的应用容器化部署。

折腾侠
2026/03/16 发布
24约 8 分钟1334 字 / 996 词00

云原生时代必学:Docker + Kubernetes 容器化部署实战指南

容器化已经不再是"可选项",而是现代开发的"必选项"。本文带你从零开始,掌握 Docker 和 Kubernetes 的核心技能,完成一次完整的应用容器化部署。

引子:为什么容器化如此重要?

2026 年的今天,如果你还在问"我的项目需要容器化吗",那答案只有一个:需要

原因很简单:

  • 环境一致性:开发、测试、生产环境完全一致,彻底告别"在我机器上能跑"的尴尬
  • 快速部署:从代码提交到服务上线,分钟级完成
  • 弹性伸缩:流量来了自动扩容,流量走了自动缩容,成本最优
  • 故障隔离:一个容器挂了不影响其他服务,系统更稳定

但很多开发者对容器化的理解还停留在"会用 Docker 跑个 MySQL"的层面。今天,我们就来一次完整的实战,从 Docker 基础到 Kubernetes 部署,走完整个流程。


第一部分:Docker 核心概念与实战

1.1 Docker 是什么?

简单说,Docker 就是一个打包工具。它把你的应用和所有依赖(代码、运行时、系统工具、库文件)打包成一个镜像(Image),然后可以在任何支持 Docker 的环境中运行这个镜像,生成容器(Container)

类比一下:

  • 镜像 = 光盘(只读的模板)
  • 容器 = 用光盘安装出来的系统(可运行的实例)

1.2 编写第一个 Dockerfile

假设我们有一个简单的 Node.js 应用:

JavaScript
// app.js
const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req, res) => {
  res.json({ message: 'Hello from Docker!', timestamp: new Date() });
});

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

对应的 INLINE_CODE_0

JSON
{
  "name": "docker-demo",
  "version": "1.0.0",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.18.2"
  }
}

现在编写 INLINE_CODE_1

Dockerfile
# 使用官方 Node.js 镜像作为基础镜像
FROM node:20-alpine

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 package-lock.json(如果有)
COPY package*.json ./

# 安装依赖
RUN npm ci --only=production

# 复制应用代码
COPY . .

# 暴露端口
EXPOSE 3000

# 启动命令
CMD ["npm", "start"]

关键点解析

  1. INLINE_CODE_2:使用 Alpine 版本的 Node.js 镜像,体积更小(约 180MB vs 900MB)
  2. INLINE_CODE_3:设置容器内的工作目录
  3. INLINE_CODE_4:先复制依赖文件,利用 Docker 缓存层
  4. INLINE_CODE_5INLINE_CODE_6INLINE_CODE_7 更快更可靠,INLINE_CODE_8 排除开发依赖
  5. INLINE_CODE_9:复制应用代码(放在依赖安装之后,避免每次代码变更都重新安装依赖)
  6. INLINE_CODE_10:声明容器运行时监听的端口(只是声明,不自动映射)
  7. INLINE_CODE_11:容器启动时执行的命令

1.3 构建与运行

Bash
# 构建镜像
docker build -t my-node-app:1.0 .

# 查看镜像
docker images

# 运行容器
docker run -d -p 3000:3000 --name my-app my-node-app:1.0

# 查看运行日志
docker logs my-app

# 访问服务
curl http://localhost:3000

# 停止并删除容器
docker stop my-app
docker rm my-app

1.4 Docker Compose:多容器编排

真实应用很少只有一个容器。通常你需要:应用 + 数据库 + Redis + ... 这时就需要 Docker Compose

创建 INLINE_CODE_12

YAML
version: '3.8'

services:
  # 应用服务
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://user:password@db:5432/mydb
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
    restart: unless-stopped
    networks:
      - app-network

  # PostgreSQL 数据库
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network
    restart: unless-stopped

  # Redis 缓存
  redis:
    image: redis:7-alpine
    networks:
      - app-network
    restart: unless-stopped

volumes:
  postgres-data:

networks:
  app-network:
    driver: bridge

运行:

Bash
# 启动所有服务
docker compose up -d

# 查看服务状态
docker compose ps

# 查看日志
docker compose logs -f app

# 停止所有服务
docker compose down

# 停止并删除数据卷(慎用!)
docker compose down -v

第二部分:Kubernetes 入门与部署

2.1 为什么需要 Kubernetes?

Docker Compose 适合单机环境,但当你需要:

  • 在多台服务器上部署应用
  • 自动处理容器故障(挂了自动重启)
  • 根据负载自动扩缩容
  • 零停机更新(滚动发布)

这时就需要 Kubernetes(K8s)

K8s 的核心概念:

概念说明
PodK8s 最小部署单元,包含一个或多个容器
Deployment管理 Pod 的副本数、更新策略
Service定义访问 Pod 的网络入口
ConfigMap存储配置信息
Secret存储敏感信息(密码、密钥)
Namespace资源隔离(开发/测试/生产环境)

2.2 编写 Kubernetes 配置文件

创建 INLINE_CODE_13

YAML
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
  labels:
    app: my-app
spec:
  replicas: 3  # 运行 3 个副本
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: my-node-app:1.0
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: "production"
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: database-url
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

创建 INLINE_CODE_14

YAML
apiVersion: v1
kind: Service
metadata:
  name: my-app-service
spec:
  selector:
    app: my-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000
  type: LoadBalancer  # 云厂商会自动创建负载均衡器

创建 INLINE_CODE_15(不要提交到 Git!):

YAML
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  database-url: "postgresql://user:password@db:5432/mydb"

2.3 部署到 Kubernetes

Bash
# 创建命名空间(可选)
kubectl create namespace production

# 创建 Secret
kubectl apply -f k8s/secrets.yaml -n production

# 部署应用
kubectl apply -f k8s/deployment.yaml -n production

# 创建服务
kubectl apply -f k8s/service.yaml -n production

# 查看部署状态
kubectl get deployments -n production
kubectl get pods -n production
kubectl get services -n production

# 查看 Pod 日志
kubectl logs -f deployment/my-app-deployment -n production

# 扩容到 5 个副本
kubectl scale deployment my-app-deployment --replicas=5 -n production

# 滚动更新镜像
kubectl set image deployment/my-app-deployment my-app=my-node-app:2.0 -n production

# 查看更新状态
kubectl rollout status deployment/my-app-deployment -n production

# 回滚到上一个版本
kubectl rollout undo deployment/my-app-deployment -n production

第三部分:生产环境最佳实践

3.1 镜像优化

多阶段构建,减小镜像体积:

Dockerfile
# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 生产阶段
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "dist/index.js"]

镜像体积对比

  • 单阶段:~500MB
  • 多阶段:~180MB(减少 64%)

3.2 健康检查

在应用中加入健康检查端点:

JavaScript
// 存活探针(livenessProbe)- 应用是否活着
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date() });
});

// 就绪探针(readinessProbe)- 应用是否可以接收流量
app.get('/ready', (req, res) => {
  if (dbConnected && cacheConnected) {
    res.json({ status: 'ready' });
  } else {
    res.status(503).json({ status: 'not ready' });
  }
});

3.3 日志与监控

日志:输出到 stdout/stderr,让 K8s 收集

JavaScript
// 使用结构化日志
const log = (level, message, meta = {}) => {
  console.log(JSON.stringify({
    level,
    message,
    timestamp: new Date().toISOString(),
    ...meta
  }));
};

log('info', 'Server started', { port: PORT });

监控:集成 Prometheus + Grafana

YAML
# Prometheus 抓取配置
annotations:
  prometheus.io/scrape: "true"
  prometheus.io/port: "3000"
  prometheus.io/path: "/metrics"

3.4 CI/CD 集成

GitHub Actions 示例:

YAML
name: Deploy to Kubernetes

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    
    - name: Build and push Docker image
      uses: docker/build-push-action@v5
      with:
        push: true
        tags: registry.example.com/my-app:${{ github.sha }}
    
    - name: Deploy to Kubernetes
      uses: azure/k8s-deploy@v4
      with:
        manifests: |
          k8s/deployment.yaml
          k8s/service.yaml
        images: |
          registry.example.com/my-app:${{ github.sha }}
        namespace: production

第四部分:常见问题与解决方案

Q1: 容器启动后立即退出

原因:容器主进程结束

解决

  • 检查 INLINE_CODE_16 是否是前台运行的命令
  • 不要用 INLINE_CODE_17package.json,容器只需要一个主进程

Q2: 容器无法访问外部网络

原因:DNS 配置问题或网络策略限制

解决

Bash
# 测试 DNS
docker run --rm alpine nslookup google.com

# 检查网络策略
kubectl get networkpolicy

Q3: 数据持久化

错误做法:数据存在容器内

正确做法

  • 使用 Volume(Docker)
  • 使用 PersistentVolume(K8s)
YAML
# K8s 持久化存储示例
volumes:
- name: data-volume
  persistentVolumeClaim:
    claimName: my-pvc

Q4: 资源限制

必须设置,避免单个容器耗尽节点资源:

YAML
resources:
  requests:
    memory: "128Mi"  # 保证至少这么多
    cpu: "100m"      # 100 毫核
  limits:
    memory: "256Mi"  # 最多用这么多
    cpu: "500m"      # 500 毫核

结语:下一步学习路径

容器化只是起点,接下来你可以:

  1. 服务网格:Istio、Linkerd(服务间通信、熔断、限流)
  2. GitOps:ArgoCD、Flux(用 Git 管理 K8s 配置)
  3. Serverless 容器:AWS Fargate、Google Cloud Run(无需管理节点)
  4. 边缘计算:K3s、KubeEdge(轻量级 K8s)

最后提醒

不要为了容器化而容器化。如果你的应用很简单,单机部署就够了。容器化是为了解决特定问题而存在的工具,不是银弹。

但当你需要规模化、高可用、快速迭代时,Docker + Kubernetes 绝对是 2026 年开发者必备的技能栈。


参考资源

欢迎在评论区分享你的容器化经验或遇到的问题!

分享到:

如果这篇文章对你有帮助,欢迎请作者喝杯咖啡 ☕

加载评论中...