Docker 与 CI/CD
Docker 容器化技术和 CI/CD 持续集成/持续部署是现代 DevOps 的核心实践。掌握 Docker 基础、Dockerfile 编写、CI/CD 流程设计,是 Java 开发者向 DevOps 工程师进阶的必备技能。
面试高频考点:Docker 核心概念、Dockerfile 最佳实践、CI/CD 流程设计、容器化部署方案
一、Docker 核心概念
1.1 什么是 Docker
Docker 是一个开源的容器化平台,它允许开发者将应用程序及其依赖打包到一个轻量级、可移植的容器中,从而实现"一次构建,到处运行"。
┌─────────────────────────────────────────────────────────────┐
│ 传统虚拟机架构 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ App A │ │ App B │ │ App C │ │
│ ├─────────┤ ├─────────┤ ├─────────┤ │
│ │ Bin/Lib │ │ Bin/Lib │ │ Bin/Lib │ │
│ ├─────────┤ ├─────────┤ ├─────────┤ │
│ │Guest OS │ │Guest OS │ │Guest OS │ ← 每个虚拟机独立OS │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Hypervisor │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Host OS │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Physical Server │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Docker 容器架构 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ App A │ │ App B │ │ App C │ │
│ ├─────────┤ ├─────────┤ ├─────────┤ │
│ │ Bin/Lib │ │ Bin/Lib │ │ Bin/Lib │ ← 共享主机内核 │
│ └─────────┘ └─────────┘ └─────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Docker Engine │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Host OS │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ Physical Server │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘1.2 Docker vs 虚拟机
| 对比项 | Docker 容器 | 虚拟机 |
|---|---|---|
| 启动速度 | 秒级 | 分钟级 |
| 资源占用 | MB 级 | GB 级 |
| 性能 | 接近原生 | 有损耗 |
| 隔离性 | 进程级隔离 | 完全隔离 |
| 操作系统 | 共享主机内核 | 独立操作系统 |
| 移植性 | 极高 | 较低 |
1.3 核心组件
┌────────────────────────────────────────────────────────────┐
│ Docker 架构 │
├────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Client │ ──────► │ Docker Host │ │
│ │ (docker) │ │ │ │
│ └─────────────┘ │ ┌───────┐ ┌─────────┐ │ │
│ │ │daemon │ │ images │ │ │
│ │ └───┬───┘ └─────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌───────────────────┐ │ │
│ │ │ containers │ │ │
│ │ └───────────────────┘ │ │
│ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────┐ │
│ │ Registry │ │
│ │ (Docker Hub/私有仓库) │ │
│ └─────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────┘| 组件 | 说明 |
|---|---|
| Docker Client | 命令行工具(docker CLI)或 API 客户端 |
| Docker Daemon | 后台守护进程,负责构建、运行、分发容器 |
| Docker Image | 只读模板,包含创建容器的指令 |
| Docker Container | 镜像的运行实例,独立运行的进程 |
| Docker Registry | 镜像仓库,如 Docker Hub、Harbor |
二、Docker 基础命令
2.1 镜像管理
# 搜索镜像
docker search nginx
docker search openjdk:17
# 拉取镜像
docker pull nginx:latest
docker pull openjdk:17-jdk-slim
docker pull mysql:8.0
# 查看本地镜像
docker images
docker images | grep nginx
# 删除镜像
docker rmi nginx:latest
docker rmi -f $(docker images -q) # 删除所有镜像
# 镜像标签
docker tag nginx:latest myregistry.com/nginx:v1.0
# 推送镜像
docker push myregistry.com/nginx:v1.0
# 导出/导入镜像
docker save -o nginx.tar nginx:latest
docker load -i nginx.tar
# 查看镜像详情
docker inspect nginx:latest
# 查看镜像历史
docker history nginx:latest2.2 容器管理
# 创建并运行容器
docker run -d --name my-nginx -p 80:80 nginx:latest
docker run -d --name my-mysql \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=mydb \
-p 3306:3306 \
mysql:8.0
# 查看运行中的容器
docker ps
docker ps -a # 查看所有容器
# 启动/停止/重启容器
docker start my-nginx
docker stop my-nginx
docker restart my-nginx
# 进入容器
docker exec -it my-nginx /bin/bash
docker exec -it my-nginx sh
# 查看容器日志
docker logs my-nginx
docker logs -f --tail 100 my-nginx # 实时跟踪最新100行
# 查看容器详情
docker inspect my-nginx
# 查看容器资源使用
docker stats my-nginx
# 复制文件
docker cp app.jar my-nginx:/app/
docker cp my-nginx:/etc/nginx/nginx.conf ./
# 删除容器
docker rm my-nginx
docker rm -f $(docker ps -aq) # 强制删除所有容器2.3 常用参数说明
| 参数 | 说明 | 示例 |
|---|---|---|
-d | 后台运行 | docker run -d nginx |
-p | 端口映射 | -p 8080:80 |
-P | 随机端口映射 | docker run -P nginx |
-v | 挂载卷 | -v /host/path:/container/path |
-e | 环境变量 | -e MYSQL_ROOT_PASSWORD=root |
--name | 容器名称 | --name my-nginx |
--network | 网络 | --network mynet |
--restart | 重启策略 | --restart always |
-m | 内存限制 | -m 512m |
--cpus | CPU 限制 | --cpus 1.5 |
2.4 容器生命周期
┌──────────┐ docker create ┌──────────┐
│ Image │ ────────────────────► │ Created │
└──────────┘ └────┬─────┘
│ docker start
▼
┌──────────┐ docker stop ┌──────────┐
│ Stopped │ ◄────────────────────│ Running │
└────┬─────┘ └────┬─────┘
│ │
│ docker rm │ docker kill
▼ ▼
┌──────────┐ ┌──────────┐
│ Deleted │ │ Killed │
└──────────┘ └──────────┘
# docker run = docker create + docker start三、Dockerfile 编写
3.1 Dockerfile 基本结构
# 基础镜像
FROM openjdk:17-jdk-slim
# 维护者信息
LABEL maintainer="dev@example.com"
# 设置工作目录
WORKDIR /app
# 复制文件
COPY target/app.jar /app/app.jar
# 设置环境变量
ENV JAVA_OPTS="-Xms256m -Xmx512m"
ENV SPRING_PROFILES_ACTIVE="prod"
# 暴露端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/actuator/health || exit 1
# 启动命令
ENTRYPOINT ["java", "-jar", "/app/app.jar"]3.2 常用指令详解
| 指令 | 说明 | 示例 |
|---|---|---|
FROM | 指定基础镜像 | FROM openjdk:17-jdk-slim |
LABEL | 添加元数据标签 | LABEL version="1.0" |
ENV | 设置环境变量 | ENV APP_ENV=prod |
WORKDIR | 设置工作目录 | WORKDIR /app |
COPY | 复制文件 | COPY app.jar /app/ |
ADD | 复制文件(支持URL和解压) | ADD app.tar.gz /app/ |
RUN | 执行命令 | RUN apt-get update |
CMD | 容器启动默认命令 | CMD ["java", "-jar", "app.jar"] |
ENTRYPOINT | 容器启动入口点 | ENTRYPOINT ["java"] |
EXPOSE | 声明端口 | EXPOSE 8080 |
VOLUME | 创建挂载点 | VOLUME /data |
ARG | 构建参数 | ARG VERSION=1.0 |
HEALTHCHECK | 健康检查 | HEALTHCHECK CMD curl -f ... |
USER | 指定用户 | USER appuser |
3.3 CMD vs ENTRYPOINT
# CMD:可被 docker run 参数覆盖
FROM nginx
CMD ["nginx", "-g", "daemon off;"]
# docker run my-nginx nginx -g "daemon off;" → 覆盖 CMD
# ENTRYPOINT:docker run 参数追加
FROM nginx
ENTRYPOINT ["nginx", "-g"]
CMD ["daemon off;"]
# docker run my-nginx "daemon off;" → 追加到 ENTRYPOINT
# 组合使用(推荐)
FROM openjdk:17-jdk-slim
ENTRYPOINT ["java", "-jar"]
CMD ["app.jar"]
# docker run myapp → java -jar app.jar
# docker run myapp app-prod.jar → java -jar app-prod.jar3.4 多阶段构建
# 阶段1:构建
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package -DskipTests
# 阶段2:运行
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=builder /build/target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]多阶段构建优势:
- 构建环境和运行环境分离
- 最终镜像更小
- 安全性更高(不包含构建工具)
3.5 Dockerfile 最佳实践
# ✅ 推荐做法
# 1. 使用特定版本标签
FROM openjdk:17-jdk-slim # 而不是 openjdk:latest
# 2. 合并 RUN 命令减少层数
RUN apt-get update && apt-get install -y \
curl \
vim \
&& rm -rf /var/lib/apt/lists/*
# 3. 利用构建缓存,把不常变化的放前面
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY pom.xml ./
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn package
# 4. 使用 .dockerignore 排除不需要的文件
# .dockerignore 文件内容:
# target/
# *.log
# .git
# .idea
# 5. 使用非 root 用户运行
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser
# 6. 优化镜像大小
FROM eclipse-temurin:17-jre-alpine # 使用 Alpine 版本更小四、Docker Compose
4.1 什么是 Docker Compose
Docker Compose 是用于定义和运行多容器应用的工具。通过 YAML 文件配置应用的服务、网络和卷。
4.2 docker-compose.yml 基本结构
version: '3.8'
services:
# 应用服务
app:
build:
context: .
dockerfile: Dockerfile
container_name: myapp
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=db
depends_on:
- db
- redis
networks:
- app-network
restart: always
# 数据库服务
db:
image: mysql:8.0
container_name: mydb
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=mydb
volumes:
- db-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
networks:
- app-network
restart: always
# 缓存服务
redis:
image: redis:7-alpine
container_name: myredis
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- app-network
restart: always
# 网络定义
networks:
app-network:
driver: bridge
# 卷定义
volumes:
db-data:
redis-data:4.3 Docker Compose 命令
# 启动所有服务
docker-compose up -d
# 查看服务状态
docker-compose ps
# 查看日志
docker-compose logs -f app
# 停止所有服务
docker-compose down
# 停止并删除卷
docker-compose down -v
# 重新构建服务
docker-compose build app
# 重启服务
docker-compose restart app
# 进入容器
docker-compose exec app /bin/bash
# 扩展服务
docker-compose up -d --scale app=34.4 Java 微服务编排示例
version: '3.8'
services:
# 网关服务
gateway:
build: ./gateway
ports:
- "8080:8080"
environment:
- NACOS_SERVER_ADDR=nacos:8848
depends_on:
- nacos
networks:
- microservice-net
# 用户服务
user-service:
build: ./user-service
environment:
- NACOS_SERVER_ADDR=nacos:8848
- MYSQL_HOST=mysql
- REDIS_HOST=redis
depends_on:
- nacos
- mysql
- redis
networks:
- microservice-net
deploy:
replicas: 2
# 订单服务
order-service:
build: ./order-service
environment:
- NACOS_SERVER_ADDR=nacos:8848
- MYSQL_HOST=mysql
- REDIS_HOST=redis
depends_on:
- nacos
- mysql
- redis
networks:
- microservice-net
deploy:
replicas: 2
# Nacos 注册中心
nacos:
image: nacos/nacos-server:v2.2.0
environment:
- MODE=standalone
ports:
- "8848:8848"
networks:
- microservice-net
# MySQL 数据库
mysql:
image: mysql:8.0
environment:
- MYSQL_ROOT_PASSWORD=root
volumes:
- mysql-data:/var/lib/mysql
ports:
- "3306:3306"
networks:
- microservice-net
# Redis 缓存
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- microservice-net
networks:
microservice-net:
driver: bridge
volumes:
mysql-data:
redis-data:五、CI/CD 流程设计
5.1 CI/CD 概念
┌────────────────────────────────────────────────────────────────────┐
│ CI/CD 流水线 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 代码提交 │──►│ 代码检查 │──►│ 单元测试 │──►│ 构建 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │ │
│ Git Push SonarQube JUnit/Maven Docker Build │
│ │
│ ▼ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 镜像推送 │──►│ 部署测试 │──►│ 集成测试 │──►│ 部署生产 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │ │
│ Push to K8s Deploy E2E Test K8s Deploy │
│ Registry (Dev) (Prod) │
│ │
└────────────────────────────────────────────────────────────────────┘
CI (Continuous Integration):持续集成
- 代码合并
- 自动构建
- 自动测试
CD (Continuous Delivery/Deployment):持续交付/部署
- 自动部署到测试环境
- 自动部署到生产环境5.2 CI/CD 流程设计原则
| 原则 | 说明 |
|---|---|
| 自动化 | 所有步骤尽可能自动化 |
| 快速反馈 | 问题尽早发现、尽早修复 |
| 可重复 | 任何环境都能重复构建 |
| 版本控制 | 所有配置文件纳入版本控制 |
| 安全合规 | 代码扫描、安全检查 |
六、Jenkins CI/CD 配置
6.1 Jenkins Pipeline 语法
// Jenkinsfile (Declarative Pipeline)
pipeline {
agent any
// 工具配置
tools {
maven 'Maven 3.8'
jdk 'JDK 17'
}
// 环境变量
environment {
DOCKER_IMAGE = 'myregistry.com/myapp'
DOCKER_TAG = "${BUILD_NUMBER}"
}
// 触发条件
triggers {
gitlab(triggerOnPush: true, triggerOnMergeRequest: true)
}
stages {
// 阶段1:代码检出
stage('Checkout') {
steps {
checkout scm
}
}
// 阶段2:代码检查
stage('Code Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
}
// 阶段3:单元测试
stage('Unit Test') {
steps {
sh 'mvn test'
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
// 阶段4:构建
stage('Build') {
steps {
sh 'mvn package -DskipTests'
}
}
// 阶段5:构建镜像
stage('Build Image') {
steps {
script {
docker.withRegistry('https://myregistry.com', 'docker-credentials') {
def app = docker.build("${DOCKER_IMAGE}:${DOCKER_TAG}")
app.push()
app.push('latest')
}
}
}
}
// 阶段6:部署到开发环境
stage('Deploy Dev') {
steps {
sh """
kubectl set image deployment/myapp \
myapp=${DOCKER_IMAGE}:${DOCKER_TAG} \
-n dev
"""
}
}
// 阶段7:部署到生产环境(需手动确认)
stage('Deploy Prod') {
when {
branch 'main'
}
input {
message "Deploy to Production?"
ok "Deploy"
}
steps {
sh """
kubectl set image deployment/myapp \
myapp=${DOCKER_IMAGE}:${DOCKER_TAG} \
-n prod
"""
}
}
}
// 构建后操作
post {
success {
echo 'Build succeeded!'
slackSend(color: 'good', message: "Build ${BUILD_NUMBER} succeeded")
}
failure {
echo 'Build failed!'
slackSend(color: 'danger', message: "Build ${BUILD_NUMBER} failed")
}
always {
cleanWs()
}
}
}6.2 Jenkins 常用配置
// 并行执行
stage('Parallel Tests') {
parallel {
stage('Unit Tests') {
steps {
sh 'mvn test'
}
}
stage('Integration Tests') {
steps {
sh 'mvn verify -P integration-test'
}
}
}
}
// 条件执行
stage('Deploy') {
when {
anyOf {
branch 'main'
branch 'release/*'
}
}
steps {
// 部署逻辑
}
}
// 参数化构建
parameters {
choice(name: 'ENV', choices: ['dev', 'test', 'prod'], description: 'Environment')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: 'Skip tests')
}
// 使用参数
stage('Test') {
when {
expression { !params.SKIP_TESTS }
}
steps {
sh 'mvn test'
}
}七、GitLab CI 配置
7.1 GitLab CI 基本结构
# .gitlab-ci.yml
# 定义阶段
stages:
- build
- test
- package
- deploy
# 定义变量
variables:
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
DOCKER_IMAGE: "myregistry.com/myapp"
# 缓存配置
cache:
paths:
- .m2/repository/
# 构建阶段
build:
stage: build
image: maven:3.8-openjdk-17
script:
- mvn compile
artifacts:
paths:
- target/
expire_in: 1 hour
# 测试阶段
test:
stage: test
image: maven:3.8-openjdk-17
script:
- mvn test
coverage: '/Total.*?([0-9]{1,3})%/'
artifacts:
reports:
junit: target/surefire-reports/*.xml
# 打包阶段
package:
stage: package
image: maven:3.8-openjdk-17
script:
- mvn package -DskipTests
artifacts:
paths:
- target/*.jar
only:
- main
- develop
# 构建 Docker 镜像
docker-build:
stage: package
image: docker:latest
services:
- docker:dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $DOCKER_IMAGE:$CI_COMMIT_SHA .
- docker push $DOCKER_IMAGE:$CI_COMMIT_SHA
- docker push $DOCKER_IMAGE:latest
only:
- main
# 部署到开发环境
deploy-dev:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE:$CI_COMMIT_SHA -n dev
environment:
name: development
url: https://dev.example.com
only:
- develop
# 部署到生产环境
deploy-prod:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE:$CI_COMMIT_SHA -n prod
environment:
name: production
url: https://www.example.com
when: manual
only:
- main7.2 GitLab CI 高级特性
# 环境变量和密钥管理
deploy:
stage: deploy
script:
- kubectl apply -f k8s/
variables:
KUBECONFIG: $KUBE_CONFIG
only:
- main
# 手动部署和审批
deploy-prod:
stage: deploy
script:
- ./deploy.sh prod
when: manual
allow_failure: false
# 多环境部署
.deploy_template: &deploy_template
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE:$CI_COMMIT_SHA -n $ENV
deploy-dev:
<<: *deploy_template
stage: deploy
variables:
ENV: dev
only:
- develop
deploy-prod:
<<: *deploy_template
stage: deploy
variables:
ENV: prod
when: manual
only:
- main
# 触发下游项目
trigger-downstream:
stage: deploy
trigger:
project: group/downstream-project
branch: main八、容器化部署实践
8.1 Kubernetes 部署配置
# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: prod
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry.com/myapp:latest
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: db-host
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: app-secret
key: db-password
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
imagePullSecrets:
- name: registry-secret
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
namespace: prod
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: prod
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-service
port:
number: 808.2 部署策略
┌─────────────────────────────────────────────────────────────┐
│ 部署策略对比 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 滚动更新 (Rolling Update) │
│ ┌─────────┐ │
│ │ v1 v1 v1│ → ┌─────────┐ → ┌─────────┐ │
│ └─────────┘ │ v2 v1 v1│ │ v2 v2 v1│ │
│ └─────────┘ └─────────┘ │
│ → ┌─────────┐ │
│ │ v2 v2 v2│ │
│ └─────────┘ │
│ 优点:零停机、渐进式 │
│ 缺点:版本混合运行期间可能出现兼容性问题 │
│ │
│ 2. 蓝绿部署 (Blue-Green) │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Blue (v1) │ │ Green (v2) │ │
│ │ v1 v1 v1 │ │ v2 v2 v2 │ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │ Traffic │───────────┘ │
│ └─────────────┘ 切换流量 │
│ 优点:快速回滚、完整测试 │
│ 缺点:需要双倍资源 │
│ │
│ 3. 金丝雀发布 (Canary) │
│ ┌─────────────────────────────────┐ │
│ │ v1 v1 v1 v1 v2 │ │
│ └─────────────────────────────────┘ │
│ ↓ 监控 ↓ │
│ ┌─────────────────────────────────┐ │
│ │ v1 v1 v2 v2 v2 │ │
│ └─────────────────────────────────┘ │
│ 优点:风险可控、逐步验证 │
│ 缺点:需要完善的监控 │
│ │
└─────────────────────────────────────────────────────────────┘8.3 配置管理
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
application.yml: |
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://mysql:3306/mydb
username: root
logging:
level:
root: INFO
com.example: DEBUG
---
# Secret
apiVersion: v1
kind: Secret
metadata:
name: app-secret
type: Opaque
stringData:
db-password: mysecretpassword
api-key: xxxxxx九、常见面试题
Q1: Docker 镜像和容器的关系是什么?
A: 镜像是只读模板,容器是镜像的运行实例。类比:
- 镜像 = 类(Class)
- 容器 = 对象(Instance)
一个镜像可以创建多个容器,容器之间相互隔离。
Q2: Dockerfile 中 CMD 和 ENTRYPOINT 有什么区别?
A:
| 对比项 | CMD | ENTRYPOINT |
|---|---|---|
| 作用 | 默认启动命令 | 固定入口点 |
| 覆盖 | 可被 docker run 参数覆盖 | 不可覆盖,只能追加 |
| 场景 | 提供默认值 | 定义固定命令 |
# CMD 示例
CMD ["nginx", "-g", "daemon off;"]
# docker run my-image nginx -h # 覆盖整个 CMD
# ENTRYPOINT 示例
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
# docker run my-image -h # 等同于 nginx -hQ3: 如何优化 Docker 镜像大小?
A:
- 使用精简基础镜像(如
alpine) - 多阶段构建,分离构建和运行环境
- 合并
RUN命令减少层数 - 清理缓存和临时文件
- 使用
.dockerignore排除不需要的文件
# 优化前:可能 500MB+
FROM openjdk:17-jdk
COPY target/app.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
# 优化后:可能 200MB
FROM eclipse-temurin:17-jre-alpine
COPY target/app.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]Q4: 什么是 CI/CD?有什么好处?
A:
- CI(持续集成):代码提交后自动构建、测试
- CD(持续交付/部署):自动部署到各种环境
好处:
- 尽早发现和修复问题
- 减少手动操作错误
- 加快交付速度
- 提高代码质量
Q5: 如何实现零停机部署?
A:
- 滚动更新:逐步替换旧版本
- 蓝绿部署:先部署新版本,再切换流量
- 使用健康检查:确保新实例就绪后再接收流量
# Kubernetes 滚动更新配置
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多可以多创建1个Pod
maxUnavailable: 0 # 最少保证0个不可用Q6: Docker Compose 和 Kubernetes 有什么区别?
A:
| 对比项 | Docker Compose | Kubernetes |
|---|---|---|
| 定位 | 单机容器编排 | 分布式容器编排 |
| 规模 | 小型应用 | 大规模集群 |
| 功能 | 基本编排 | 自动伸缩、滚动更新、自愈 |
| 适用场景 | 开发、测试 | 生产环境 |
Q7: 解释 Docker 的存储驱动和容器存储?
A:
- 存储驱动:管理镜像层和容器层(如 overlay2)
- Volume:持久化存储,独立于容器生命周期
- Bind Mount:挂载主机目录
- tmpfs:临时存储在内存中
# Volume 使用
docker volume create mydata
docker run -v mydata:/app/data myapp
# Bind Mount 使用
docker run -v /host/path:/container/path myappQ8: 如何保证容器中 Java 应用的时间正确?
A:
- 设置时区环境变量
- 挂载主机时区文件
# 方式1:环境变量
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 方式2:JVM 参数
ENV JAVA_OPTS="-Duser.timezone=Asia/Shanghai"十、总结
| 知识点 | 核心内容 | 面试关键词 |
|---|---|---|
| Docker 基础 | 镜像、容器、仓库 | 与虚拟机区别、核心组件 |
| Dockerfile | 指令、最佳实践 | CMD vs ENTRYPOINT、多阶段构建 |
| Docker Compose | 多容器编排 | YAML 配置、服务依赖 |
| CI/CD | 持续集成/部署 | Jenkins Pipeline、GitLab CI |
| 容器部署 | K8s 部署、策略 | 滚动更新、蓝绿部署 |
最佳实践清单:
- 使用特定版本的基础镜像
- 多阶段构建减小镜像大小
- 使用非 root 用户运行容器
- 配置健康检查
- 合理设置资源限制
- 使用 Volume 持久化数据
- 自动化测试集成到 CI 流程
- 生产环境使用 Kubernetes
最后更新:2026年3月16日