云原生时代必学:Docker + Kubernetes 容器化部署实战指南
容器化已经不再是"可选项",而是现代开发的"必选项"。本文带你从零开始,掌握 Docker 和 Kubernetes 的核心技能,完成一次完整的应用容器化部署。
云原生时代必学:Docker + Kubernetes 容器化部署实战指南
容器化已经不再是"可选项",而是现代开发的"必选项"。本文带你从零开始,掌握 Docker 和 Kubernetes 的核心技能,完成一次完整的应用容器化部署。
引子:为什么容器化如此重要?
2026 年的今天,如果你还在问"我的项目需要容器化吗",那答案只有一个:需要。
原因很简单:
- 环境一致性:开发、测试、生产环境完全一致,彻底告别"在我机器上能跑"的尴尬
- 快速部署:从代码提交到服务上线,分钟级完成
- 弹性伸缩:流量来了自动扩容,流量走了自动缩容,成本最优
- 故障隔离:一个容器挂了不影响其他服务,系统更稳定
但很多开发者对容器化的理解还停留在"会用 Docker 跑个 MySQL"的层面。今天,我们就来一次完整的实战,从 Docker 基础到 Kubernetes 部署,走完整个流程。
第一部分:Docker 核心概念与实战
1.1 Docker 是什么?
简单说,Docker 就是一个打包工具。它把你的应用和所有依赖(代码、运行时、系统工具、库文件)打包成一个镜像(Image),然后可以在任何支持 Docker 的环境中运行这个镜像,生成容器(Container)。
类比一下:
- 镜像 = 光盘(只读的模板)
- 容器 = 用光盘安装出来的系统(可运行的实例)
1.2 编写第一个 Dockerfile
假设我们有一个简单的 Node.js 应用:
// 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:
{
"name": "docker-demo",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
现在编写 INLINE_CODE_1:
# 使用官方 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"]
关键点解析:
- INLINE_CODE_2:使用 Alpine 版本的 Node.js 镜像,体积更小(约 180MB vs 900MB)
- INLINE_CODE_3:设置容器内的工作目录
- INLINE_CODE_4:先复制依赖文件,利用 Docker 缓存层
- INLINE_CODE_5:INLINE_CODE_6 比 INLINE_CODE_7 更快更可靠,INLINE_CODE_8 排除开发依赖
- INLINE_CODE_9:复制应用代码(放在依赖安装之后,避免每次代码变更都重新安装依赖)
- INLINE_CODE_10:声明容器运行时监听的端口(只是声明,不自动映射)
- INLINE_CODE_11:容器启动时执行的命令
1.3 构建与运行
# 构建镜像
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:
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
运行:
# 启动所有服务
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 的核心概念:
| 概念 | 说明 |
|---|---|
| Pod | K8s 最小部署单元,包含一个或多个容器 |
| Deployment | 管理 Pod 的副本数、更新策略 |
| Service | 定义访问 Pod 的网络入口 |
| ConfigMap | 存储配置信息 |
| Secret | 存储敏感信息(密码、密钥) |
| Namespace | 资源隔离(开发/测试/生产环境) |
2.2 编写 Kubernetes 配置文件
创建 INLINE_CODE_13:
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:
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!):
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
database-url: "postgresql://user:password@db:5432/mydb"
2.3 部署到 Kubernetes
# 创建命名空间(可选)
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 镜像优化
多阶段构建,减小镜像体积:
# 构建阶段
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 健康检查
在应用中加入健康检查端点:
// 存活探针(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 收集
// 使用结构化日志
const log = (level, message, meta = {}) => {
console.log(JSON.stringify({
level,
message,
timestamp: new Date().toISOString(),
...meta
}));
};
log('info', 'Server started', { port: PORT });
监控:集成 Prometheus + Grafana
# Prometheus 抓取配置
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
3.4 CI/CD 集成
GitHub Actions 示例:
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_17 或
package.json,容器只需要一个主进程
Q2: 容器无法访问外部网络
原因:DNS 配置问题或网络策略限制
解决:
# 测试 DNS
docker run --rm alpine nslookup google.com
# 检查网络策略
kubectl get networkpolicy
Q3: 数据持久化
错误做法:数据存在容器内
正确做法:
- 使用 Volume(Docker)
- 使用 PersistentVolume(K8s)
# K8s 持久化存储示例
volumes:
- name: data-volume
persistentVolumeClaim:
claimName: my-pvc
Q4: 资源限制
必须设置,避免单个容器耗尽节点资源:
resources:
requests:
memory: "128Mi" # 保证至少这么多
cpu: "100m" # 100 毫核
limits:
memory: "256Mi" # 最多用这么多
cpu: "500m" # 500 毫核
结语:下一步学习路径
容器化只是起点,接下来你可以:
- 服务网格:Istio、Linkerd(服务间通信、熔断、限流)
- GitOps:ArgoCD、Flux(用 Git 管理 K8s 配置)
- Serverless 容器:AWS Fargate、Google Cloud Run(无需管理节点)
- 边缘计算:K3s、KubeEdge(轻量级 K8s)
最后提醒:
不要为了容器化而容器化。如果你的应用很简单,单机部署就够了。容器化是为了解决特定问题而存在的工具,不是银弹。
但当你需要规模化、高可用、快速迭代时,Docker + Kubernetes 绝对是 2026 年开发者必备的技能栈。
参考资源:
欢迎在评论区分享你的容器化经验或遇到的问题!