1. 初识 Docker
核心考点:Docker 解决啥、和虚拟机的区别、架构三件套
-
解决啥:大型项目组件多(MySQL/Redis/RabbitMQ...),依赖版本冲突 + 操作系统差异 → Docker 把"应用+依赖+运行环境"打包成镜像,一次构建到处跑
-
和虚拟机区别(面试高频):
-
虚拟机:Hypervisor + 完整 Guest OS,重、启动慢
-
Docker:共享宿主机内核,只隔离进程,轻量,秒级启动
-
-
架构三件套:
-
镜像 Image:只读模板(类比"类")
-
容器 Container:镜像跑起来的实例(类比"对象")
-
Registry:DockerHub / 阿里云 ACR / 私有 harbor,存镜像的地方
-
💡 面试被问"Docker 为啥比 VM 轻",答共享内核 + 无 Guest OS 就够,别扯 cgroup/namespace 太细,除非对方追问。
2. 镜像加速
镜像加速本质改 /etc/docker/daemon.json的 registry-mirrors
2026还能正常访问的
XML
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me",
"https://docker.m.daocloud.io"
]
}
3. Docker 基本操作
常用命令
bash
# ========== 镜像管理 ==========
# 列出本地镜像
docker images
# 拉取镜像
docker pull nginx:1.20
# 删除镜像
docker rmi nginx:1.20
# 导出镜像为tar文件
docker save -o nginx.tar nginx:1.20
# 从tar文件导入镜像
docker load -i nginx.tar
# 查看镜像历史分层
docker history nginx:1.20
# 标记镜像(打标签)
docker tag nginx:1.20 myrepo/nginx:1.20
# 推送镜像到仓库
docker push myrepo/nginx:1.20
# ========== 容器生命周期 ==========
# 创建并启动容器(前台)
docker run -it --name ubuntu ubuntu:22.04 /bin/bash
# 创建并启动容器(后台)
docker run -d --name nginx -p 80:80 nginx:1.20
# 启动已有容器
docker start nginx
# 停止容器
docker stop nginx
# 重启容器
docker restart nginx
# 强制停止容器
docker kill nginx
# 暂停容器
docker pause nginx
# 恢复暂停的容器
docker unpause nginx
# 删除容器(运行中需加-f)
docker rm nginx
# 强制删除运行中的容器
docker rm -f nginx
# 删除所有停止的容器
docker container prune
# ========== 容器操作 ==========
# 查看运行中的容器
docker ps
# 查看所有容器(含停止)
docker ps -a
# 查看容器详细信息(JSON)
docker inspect nginx
# 查看容器日志(实时跟踪)
docker logs -f nginx
# 进入容器执行命令(交互式)
docker exec -it nginx /bin/bash
# 在容器内执行单条命令
docker exec nginx ls /etc
# 从宿主机复制文件到容器
docker cp /path/host.txt nginx:/container/path
# 从容器复制文件到宿主机
docker cp nginx:/container/path /path/host.txt
# 查看容器内进程
docker top nginx
# 查看容器资源占用
docker stats nginx
# 查看容器端口映射
docker port nginx
# 重命名容器
docker rename old-name new-name
# ========== 数据卷 ==========
# 创建命名卷
docker volume create my-volume
# 列出所有卷
docker volume ls
# 查看卷详情
docker volume inspect my-volume
# 删除卷
docker volume rm my-volume
# 删除未使用的卷
docker volume prune
# 挂载命名卷运行容器
docker run -d --name mysql -v my-volume:/var/lib/mysql mysql:8.0
# 挂载宿主机目录(bind mount)
docker run -d --name nginx -v /host/html:/usr/share/nginx/html nginx:1.20
# ========== 网络 ==========
# 列出网络
docker network ls
# 创建桥接网络
docker network create my-net
# 查看网络详情
docker network inspect my-net
# 连接容器到网络
docker network connect my-net nginx
# 断开容器与网络
docker network disconnect my-net nginx
# 删除网络
docker network rm my-net
# 删除未使用的网络
docker network prune
# 指定网络运行容器
docker run -d --name app --network my-net my-app
# ========== Docker Compose ==========
# 启动服务(后台)
docker-compose up -d
# 启动服务并重新构建镜像
docker-compose up -d --build
# 停止服务
docker-compose stop
# 停止并删除容器、网络(默认不删卷)
docker-compose down
# 停止并删除容器、网络、卷(危险)
docker-compose down -v
# 查看服务状态
docker-compose ps
# 查看服务日志
docker-compose logs -f app
# 重启服务
docker-compose restart
# 拉取最新镜像
docker-compose pull
# 执行服务中的命令
docker-compose exec app /bin/bash
# ========== 清理与系统信息 ==========
# 查看Docker版本
docker version
# 查看Docker系统信息
docker info
# 查看磁盘使用情况
docker system df
# 清理所有未使用的资源(容器、镜像、网络、卷)
docker system prune -a --volumes
# 清理未使用的镜像
docker image prune -a
# 清理未使用的容器
docker container prune
# 清理未使用的网络
docker network prune
# 清理未使用的卷
docker volume prune
# 查看事件流(实时监控)
docker events
# 查看历史命令
docker history nginx:1.20
容器备份恢复
将一台机器上的容器备份恢复到另一台机器
bash
# ========== 场景A:无状态容器(如 Spring Boot 镜像)→ save/load ==========
# 【原机器】1. 提交容器为镜像(如果当初是 docker run 起的、没 save 过镜像)
docker commit sb-container sb-app:1.0
# 【原机器】2. 导出镜像为 tar
docker save -o sb-app.tar sb-app:1.0
# 【原机器】3. 连同 docker-compose.yml 一起拷到新机器
scp sb-app.tar user@new-host:/opt/
scp docker-compose.yml user@new-host:/opt/
# 【新机器】4. 导入镜像
docker load -i /opt/sb-app.tar
# 【新机器】5. 启动(前提:MySQL 等其他依赖已就位)
docker-compose up -d
# ========== 场景B:有状态容器(MySQL 数据卷)→ alpine tar 法 ==========
# 【原机器】1. 停 MySQL 保一致性
docker stop mysql
# 【原机器】2. alpine 临时容器把命名卷打成 tar
docker run --rm \
-v mysql-data:/volume \
-v /tmp:/backup \
alpine \
tar -czf /backup/mysql-data.tar.gz -C /volume .
# 【原机器】3. 拷到新机器
scp /tmp/mysql-data.tar.gz user@new-host:/tmp/
# 【新机器】4. 用同名 compose 先把卷创建出来(只起 mysql 让它初始化一次然后停)
docker-compose up -d mysql
docker-compose stop mysql
# 【新机器】5. alpine 临时容器恢复卷数据
docker run --rm \
-v myapp_mysql-data:/volume \
-v /tmp:/backup \
alpine \
sh -c "rm -rf /volume/* && tar -xzf /backup/mysql-data.tar.gz -C /volume"
# 【新机器】6. 起全套
docker-compose up -d
# ========== 场景C:MySQL 跨版本/最稳 → mysqldump SQL 法 ==========
# 【原机器】1. dump(--single-transaction 保证 InnoDB 一致性)
docker exec mysql mysqldump -uroot -p${MYSQL_ROOT_PASSWORD} \
--single-transaction --routines --triggers --all-databases > /tmp/backup.sql
# 【原机器】2. 拷 SQL + 镜像 + compose
scp /tmp/backup.sql user@new-host:/tmp/
scp sb-app.tar user@new-host:/opt/
# 【新机器】3. load 镜像 + 起空 MySQL
docker load -i /opt/sb-app.tar
docker-compose up -d mysql # 此时库是空的
# 【新机器】4. 导入 SQL
docker cp /tmp/backup.sql mysql:/tmp/
docker exec -i mysql mysql -uroot -p${MYSQL_ROOT_PASSWORD} < /tmp/backup.sql
# 【新机器】5. 起应用
docker-compose up -d app
# 【新机器】6. 验证
docker-compose ps
curl http://localhost:8080/actuator/health
DockerFile和docker-compose.yml的用法案例
现在有个前后端分离项目:app.jar、dist(前端代码文件夹)、还用到了mysql、redis、nginx;
项目拆一下:5 样东西 ≠ 1 个 Dockerfile,Dockerfile 一次只构建一个镜像。正确拆法是:
-
app.jar → 自己写 Dockerfile 打 Spring Boot 镜像
-
dist + nginx → 写一个 Dockerfile(把 dist 拷进 nginx)
-
mysql / redis → 直接用官方镜像,不用自己写 Dockerfile,compose 里配就行
一、后端 Dockerfile(app.jar)
如果用jdk8,将FROM eclipse-temurin:21-jre-alpine改为FROM eclipse-temurin:8-jre-alpine
# ========== 多阶段构建 ==========
# 阶段1:构建(如果你的 CI 已经 mvn package 了,这段可以省,直接用阶段2)
FROM maven:3.9-eclipse-temurin:21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
# 阶段2:运行
FROM eclipse-temurin:21-jre-alpine
LABEL maintainer="you@example.com"
# 时区(面试常问:为啥容器里时间不对)
ENV TZ=Asia/Shanghai
WORKDIR /app
# 从构建阶段拷 jar(CI 已经打好就用下面这句替代上面阶段1)
# COPY target/app.jar /app.jar
COPY --from=build /app/target/*.jar app.jar
# JVM 调优(8年经验简历能写"JVM 参数调优")
# -Xms -Xmx 按容器内存来,比如容器 1G 就设 512m
# UseContainerSupport 让 JVM 识别容器内存上限(JDK 8u191+ 默认开,21 肯定开)
ENTRYPOINT ["java", \
"-Xms256m", "-Xmx512m", \
"-XX:+UseG1GC", \
"-Duser.timezone=Asia/Shanghai", \
"-jar", "/app.jar"]
💡 如果公司 CI 已经
mvn package了,阶段1删掉,只留阶段2 +COPY target/app.jar /app.jar,镜像从 700MB 降到 80MB 级(alpine jre)。
二、 前端 Dockerfile(dist + nginx)
项目里 dist/是 Vue npm run build出来的,跟 nginx 放一起:
# ========== 多阶段构建 ==========
# 阶段1:Node 编 dist(如果 dist 已经本地打好了,这段也可以省)
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci --registry=https://registry.npmmirror.com
COPY . .
RUN npm run build
# 阶段2:Nginx 跑 dist
FROM nginx:1.26-alpine
# 删掉默认配置
RUN rm /etc/nginx/conf.d/default.conf
# 拷自定义 nginx.conf(反向代理后端 api 用,下面给)
COPY nginx.conf /etc/nginx/conf.d/
# 拷 dist(本地已有 dist 就 COPY dist /usr/share/nginx/html,不用阶段1)
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
配套 nginx.conf(解决前端路由 history 模式 + 反向代理 /api到 Spring Boot):
server {
listen 80;
server_name localhost;
# 前端静态资源
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html; # Vue Router history 模式必配
}
# 反向代理后端接口
location /api/ {
proxy_pass http://app:8080/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
注意
proxy_pass http://app:8080里的app是 compose service 名,靠自定义网络 DNS 解析,不是 localhost。
三、docker-compose.yml 把 5 样串起来(重点交付物)
version: '3.8'
services:
# ===== 后端 =====
app:
build:
context: ./backend # 下放 app.jar + Dockerfile
dockerfile: Dockerfile
container_name: app
ports:
- "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: prod
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/appdb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: root123
SPRING_DATA_REDIS_HOST: redis
SPRING_DATA_REDIS_PORT: 6379
volumes:
- ./logs:/app/logs # SB 日志挂出来
depends_on:
- mysql
- redis
restart: always
networks:
- app-net
# ===== 前端 + nginx =====
nginx:
build:
context: ./frontend # 下放 dist + nginx.conf + Dockerfile
dockerfile: Dockerfile
container_name: nginx
ports:
- "80:80"
depends_on:
- app
restart: always
networks:
- app-net
# ===== MySQL =====
mysql:
image: mysql:8.0
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: appdb
TZ: Asia/Shanghai
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
# 首次启动自动执行建表/初始化SQL(可选)
# - ./init.sql:/docker-entrypoint-initdb.d/init.sql
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--default-authentication-plugin=mysql_native_password
restart: always
networks:
- app-net
# ===== Redis =====
redis:
image: redis:7-alpine
container_name: redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
# 自定义配置(可选,关保护模式/设密码)
# - ./redis.conf:/etc/redis/redis.conf
command: redis-server --appendonly yes
restart: always
networks:
- app-net
networks:
app-net:
driver: bridge
volumes:
mysql-data:
redis-data:
四、目录结构参考(照着摆)
project/
├── backend/
│ ├── Dockerfile # 后端的
│ ├── app.jar # mvn package 出来的
│ └── logs/ # 运行时挂出来
├── frontend/
│ ├── Dockerfile # 前端的
│ ├── dist/ # npm run build 出来的
│ └── nginx.conf
├── docker-compose.yml
└── init.sql # (可选) MySQL 初始化
五、启动 & 验证
# 构建 + 启动(--build 强制重打镜像,改了 Dockerfile 必加)
docker-compose up -d --build
# 看状态
docker-compose ps
# 看后端日志
docker-compose logs -f app
# 浏览器 http://服务器IP ,nginx 80 端口进,/api 反向代理到 app:8080
-
多阶段构建压镜像体积:Maven 3.9 + JDK21 编,Alpine JRE21 跑,最终镜像 80MB 级
-
镜像分层优化 :pom.xml 单独 COPY 先
dependency:go-offline,依赖层缓存 -
nginx 反向代理 解决跨域 + Vue history 模式
try_files -
compose 自定义网络 → 服务间用容器名互访,不用写 IP
-
有状态服务挂卷 (mysql-data / redis-data),
restart: always
⚠️ 一个坑:生产别把 mysql/redis 的
ports暴露成"3306:3306",云服务器公网 3306 被扫很正常,改成"127.0.0.1:3306:3306"让 nginx→app→mysql 走内网就行。
4. Docker 核心概念
4.1 镜像 Image
镜像是容器运行的模板,里面包含应用程序、依赖库、配置文件、运行命令等。
特点:
- 镜像是只读的。
- 镜像是分层的。
- 镜像可以基于另一个镜像构建。
- 镜像可以上传到仓库,也可以从仓库拉取。
示例:
docker pull nginx:latest
docker images
4.2 容器 Container
容器是镜像运行起来之后的实例。
特点:
- 一个镜像可以启动多个容器。
- 容器之间默认相互隔离。
- 容器删除后,容器可写层中的数据默认会丢失。
- 持久化数据应使用 volume 或 bind mount。
示例:
docker run -d --name my-nginx -p 8080:80 nginx
docker ps
docker stop my-nginx
docker rm my-nginx
4.3 仓库 Registry
镜像仓库用于保存和分发镜像。
常见仓库:
- Docker Hub。
- 阿里云容器镜像服务。
- 腾讯云镜像仓库。
- Harbor 私有仓库。
常用命令:
docker login
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0
docker pull username/myapp:1.0
AI写代码bash
- 1
- 2
- 3
- 4
5. Docker 安装
以下以 CentOS Stream 8 为例。
5.1 准备环境
实验环境:
- 容器管理工具:Docker Engine。
- 容器运行时:containerd、runc。
- 操作系统:CentOS Stream 8。
5.2 配置 Docker 软件源
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
5.3 安装 Docker
yum install -y docker-ce docker-ce-cli containerd.io
5.4 启动 Docker
systemctl enable docker.service --now
docker --version
systemctl status docker
5.5 配置镜像加速
mkdir -p /etc/docker
vim /etc/docker/daemon.json
示例配置:
{
"registry-mirrors": [
"https://054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com"
]
}
重启 Docker:
systemctl daemon-reload
systemctl restart docker
docker info
6. Docker 常用命令
6.1 镜像命令
# 查看本地镜像
docker images
# 搜索镜像
docker search nginx
# 拉取镜像
docker pull nginx:latest
# 删除镜像
docker rmi nginx:latest
# 查看镜像详细信息
docker inspect nginx
# 查看镜像构建历史
docker history nginx
6.2 容器命令
# 创建并启动容器
docker run -d --name web -p 8080:80 nginx
# 查看正在运行的容器
docker ps
# 查看所有容器
docker ps -a
# 停止容器
docker stop web
# 启动已停止容器
docker start web
# 重启容器
docker restart web
# 删除容器
docker rm web
# 强制删除运行中的容器
docker rm -f web
6.3 进入容器
docker exec -it web bash
如果容器中没有 bash,可以使用 sh:
docker exec -it web sh
6.4 查看日志
docker logs web
docker logs -f web
docker logs --tail 100 web
6.5 文件复制
# 从宿主机复制文件到容器
docker cp ./index.html web:/usr/share/nginx/html/index.html
# 从容器复制文件到宿主机
docker cp web:/etc/nginx/nginx.conf ./nginx.conf
6.6 查看资源占用
docker stats
6.7 清理资源
# 删除停止的容器
docker container prune
# 删除无用镜像
docker image prune
# 删除无用网络
docker network prune
# 删除无用 volume
docker volume prune
# 清理所有无用资源
docker system prune
注意:docker system prune -a 会删除所有未被容器使用的镜像,生产环境慎用。
7. docker run 常用参数
docker run [OPTIONS] IMAGE [COMMAND]
常用参数:
| 参数 | 作用 |
|---|---|
-d |
后台运行 |
-it |
交互式终端 |
--name |
指定容器名称 |
-p |
端口映射 |
-v |
挂载目录或数据卷 |
-e |
设置环境变量 |
--network |
指定网络 |
--restart |
设置重启策略 |
--rm |
容器退出后自动删除 |
--privileged |
开启特权模式 |
--memory |
限制内存 |
--cpus |
限制 CPU |
示例:
docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql-data:/var/lib/mysql \
--restart=always \
mysql:8.0
8. Docker 网络
8.1 网络模式
Docker 常见网络模式:
| 网络模式 | 说明 |
|---|---|
| bridge | 默认模式,容器通过 docker0 网桥通信 |
| host | 容器直接使用宿主机网络 |
| none | 容器没有网络 |
| container | 与另一个容器共享网络命名空间 |
| 自定义 bridge | 推荐用于多个容器互相通信 |
8.2 常用网络命令
# 查看网络
docker network ls
# 查看网络详情
docker network inspect bridge
# 创建自定义网络
docker network create app-net
# 使用指定网络启动容器
docker run -d --name nginx --network app-net nginx
# 将容器连接到网络
docker network connect app-net nginx
# 将容器移出网络
docker network disconnect app-net nginx
8.3 容器之间通信
推荐使用自定义网络。处于同一个自定义网络中的容器,可以通过容器名互相访问。
示例:
docker network create app-net
docker run -d --name redis --network app-net redis:7
docker run -it --rm --network app-net redis:7 redis-cli -h redis
9. Docker 存储
9.1 容器数据为什么会丢失
容器运行后会在镜像只读层上增加一层可写层。如果删除容器,这一层也会被删除。因此数据库、上传文件、日志等需要持久化的数据不能只放在容器内部。
9.2 volume 数据卷
volume 是 Docker 管理的数据卷,适合保存数据库数据。
# 创建数据卷
docker volume create mysql-data
# 查看数据卷
docker volume ls
# 查看数据卷详情
docker volume inspect mysql-data
# 使用数据卷
docker run -d \
--name mysql8 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql-data:/var/lib/mysql \
mysql:8.0
9.3 bind mount 目录挂载
bind mount 是把宿主机目录挂载到容器中,适合挂载配置文件、项目代码。
docker run -d \
--name web \
-p 8080:80 \
-v /opt/nginx/html:/usr/share/nginx/html \
nginx
9.4 volume 与 bind mount 对比
| 对比项 | volume | bind mount |
|---|---|---|
| 管理方 | Docker 管理 | 用户自己管理 |
| 路径 | Docker 默认路径 | 用户指定路径 |
| 适用场景 | 数据库、持久化数据 | 配置文件、代码目录 |
| 可移植性 | 较好 | 依赖宿主机目录结构 |
10. Dockerfile
Dockerfile 是用来构建镜像的文本文件,里面定义了基础镜像、依赖安装、文件复制、启动命令等步骤。
10.1 常用指令
| 指令 | 作用 |
|---|---|
FROM |
指定基础镜像 |
LABEL |
添加镜像元数据 |
WORKDIR |
指定工作目录 |
COPY |
复制文件到镜像 |
ADD |
复制文件,支持自动解压和 URL |
RUN |
构建镜像时执行命令 |
CMD |
容器启动时默认命令 |
ENTRYPOINT |
容器启动入口 |
ENV |
设置环境变量 |
ARG |
构建参数 |
EXPOSE |
声明容器端口 |
VOLUME |
声明挂载点 |
USER |
指定运行用户 |
10.2 Dockerfile 示例:Nginx 静态站点
FROM nginx:1.25-alpine
COPY ./dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建和运行:
docker build -t my-nginx-site:1.0 .
docker run -d --name site -p 8080:80 my-nginx-site:1.0
10.3 Dockerfile 示例:Java 应用
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/app.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
10.4 CMD 与 ENTRYPOINT 区别
CMD 提供默认启动命令,容易被 docker run 后面的命令覆盖。
ENTRYPOINT 定义容器固定入口,更适合作为主程序入口。
示例:
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
CMD ["--spring.profiles.active=prod"]
运行时可以覆盖 CMD 参数:
docker run myapp:1.0 --spring.profiles.active=test
10.5 镜像构建优化
- 选择更小的基础镜像,例如 alpine、slim。
- 合并相关
RUN指令,减少镜像层数。 - 使用
.dockerignore排除无关文件。 - 利用构建缓存,把不常变化的步骤放前面。
- 使用多阶段构建减少最终镜像体积。
- 不把密码、密钥写入镜像。
- 尽量使用非 root 用户运行应用。
10.6 多阶段构建示例
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /build/target/app.jar /app/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
- Docker Compose
Docker Compose 用于定义和运行多个容器。适合本地开发、测试环境和小规模部署。
11.1 Compose 常用命令
# 启动服务
docker compose up -d
# 查看服务
docker compose ps
# 查看日志
docker compose logs -f
# 停止并删除服务
docker compose down
# 重新构建镜像并启动
docker compose up -d --build
# 停止服务但不删除容器
docker compose stop
# 启动已停止服务
docker compose start
11.2 Compose 示例:Web + MySQL + Redis
services:
app:
build: .
container_name: demo-app
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: 123456
SPRING_REDIS_HOST: redis
depends_on:
- mysql
- redis
networks:
- app-net
mysql:
image: mysql:8.0
container_name: demo-mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: demo
volumes:
- mysql-data:/var/lib/mysql
networks:
- app-net
redis:
image: redis:7
container_name: demo-redis
ports:
- "6379:6379"
networks:
- app-net
networks:
app-net:
volumes:
mysql-data:
启动:
docker compose up -d
12. Docker 日志与排错
12.1 常见排错命令
# 查看容器状态
docker ps -a
# 查看容器日志
docker logs 容器名
# 查看容器详细信息
docker inspect 容器名
# 进入容器检查
docker exec -it 容器名 sh
# 查看容器资源占用
docker stats
# 查看端口映射
docker port 容器名
# 查看 Docker 服务日志
journalctl -u docker -f
12.2 容器启动失败排查思路
- 用
docker ps -a查看容器是否退出。 - 用
docker logs查看应用错误日志。 - 检查端口是否被占用。
- 检查环境变量是否配置正确。
- 检查挂载路径是否存在、权限是否正确。
- 检查镜像架构是否与服务器架构匹配。
- 检查容器启动命令是否正确。
12.3 端口访问不了排查思路
- 容器是否正在运行。
- 是否配置了
-p 宿主机端口:容器端口。 - 应用是否监听
0.0.0.0,而不是只监听127.0.0.1。 - 宿主机防火墙或安全组是否放行。
- 容器内部服务是否正常。
13. Docker 安全建议
- 不在镜像中写入密码、Token、私钥。
- 尽量不要使用
--privileged。 - 生产环境尽量使用固定版本标签,不使用
latest。 - 使用非 root 用户运行应用。
- 只暴露必要端口。
- 定期更新基础镜像。
- 对镜像进行漏洞扫描。
- 限制容器 CPU 和内存资源。
- 对重要数据使用 volume 持久化,并定期备份。
14. Docker 实践项目
项目 1:使用 Docker 部署 Nginx 静态网站
目标:
- 使用 Nginx 容器运行一个静态页面。
- 将宿主机目录挂载到容器。
- 通过浏览器访问页面。
步骤:
mkdir -p /opt/docker-demo/html
echo "Hello Docker" > /opt/docker-demo/html/index.html
docker run -d \
--name nginx-demo \
-p 8080:80 \
-v /opt/docker-demo/html:/usr/share/nginx/html \
nginx:1.25
访问:
http://服务器IP:8080
项目 2:使用 Docker 部署 MySQL
目标:
- 启动 MySQL 8 容器。
- 使用 volume 持久化数据。
- 使用客户端连接数据库。
步骤:
docker volume create mysql-data
docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=demo \
-v mysql-data:/var/lib/mysql \
--restart=always \
mysql:8.0
进入 MySQL:
docker exec -it mysql8 mysql -uroot -p123456
项目 3:使用 Dockerfile 打包 Spring Boot 项目
目标:
- 将 Spring Boot 项目打包成 jar。
- 编写 Dockerfile。
- 构建镜像并运行容器。
Dockerfile:
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
构建运行:
mvn clean package -DskipTests
docker build -t springboot-demo:1.0 .
docker run -d \
--name springboot-demo \
-p 8080:8080 \
springboot-demo:1.0
项目 4:使用 Docker Compose 部署完整后端环境
目标:
- 使用 Compose 同时启动应用、MySQL、Redis。
- 应用通过服务名访问 MySQL 和 Redis。
- 使用 volume 保存 MySQL 数据。
文件:docker-compose.yml
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: demo
volumes:
- mysql-data:/var/lib/mysql
networks:
- app-net
redis:
image: redis:7
networks:
- app-net
app:
build: .
ports:
- "8080:8080"
environment:
MYSQL_HOST: mysql
REDIS_HOST: redis
depends_on:
- mysql
- redis
networks:
- app-net
networks:
app-net:
volumes:
mysql-data:
启动:
docker compose up -d --build
停止:
docker compose down
15. Docker 常见面试题
1. Docker 和虚拟机有什么区别?
Docker 是进程级隔离,共享宿主机内核,启动快、资源占用少;虚拟机是硬件级虚拟化,每台虚拟机都有完整操作系统,隔离性更强但资源占用更大。
2. Docker 镜像和容器有什么区别?
镜像是只读模板,容器是镜像运行后的实例。一个镜像可以创建多个容器,容器有自己的可写层。
3. Docker 为什么启动快?
容器不需要启动完整操作系统,而是共享宿主机内核,本质上是启动一个隔离的进程,所以启动速度很快。
4. Docker 底层隔离原理是什么?
主要依赖 Linux Namespace 和 Cgroups。Namespace 负责隔离进程、网络、文件系统等资源;Cgroups 负责限制和统计 CPU、内存、I/O 等资源。
5. Docker 镜像为什么是分层的?
镜像分层可以复用已有层,减少存储空间和网络传输成本。构建镜像时,如果某一层没有变化,可以直接使用 缓存 。
6. Dockerfile 中 CMD 和 ENTRYPOINT 有什么区别?
CMD 是默认命令,容易被 docker run 后面的参数覆盖;ENTRYPOINT 是容器启动入口,通常用于固定启动主程序。二者可以配合使用,ENTRYPOINT 定义程序,CMD 提供默认参数。
7. COPY 和 ADD 有什么区别?
COPY 只负责复制文件或目录,语义更清晰,推荐优先使用。ADD 除了复制,还支持自动解压 tar 文件和从 URL 添加文件。
8. volume 和 bind mount 有什么区别?
volume 由 Docker 管理,适合数据库等持久化数据;bind mount 使用宿主机指定路径,适合挂载配置文件或代码目录。
9. 容器之间如何通信?
推荐创建自定义 bridge 网络,然后让容器加入同一个网络。这样容器之间可以通过容器名或服务名访问。
10. Docker Compose 是什么?
Docker Compose 是用于定义和管理多容器应用的工具,通过 docker-compose.yml 描述服务、网络、数据卷等配置,再用一条命令启动整个应用。
11. 如何减少 Docker 镜像体积?
可以选择更小的基础镜像,使用多阶段构建,合并 RUN 指令,清理安装缓存,使用 .dockerignore 排除无关文件,并避免把源码、测试文件、构建工具放入最终镜像。
12. 容器退出后数据会不会丢失?
如果数据只保存在容器可写层,删除容器后会丢失。需要持久化的数据应保存到 volume 或 bind mount 中。
13. 如何查看容器日志?
docker logs 容器名
docker logs -f 容器名
docker logs --tail 100 容器名
14. 如何进入正在运行的容器?
docker exec -it 容器名 bash
如果没有 bash:
docker exec -it 容器名 sh
15. Docker 容器访问不了外部端口怎么办?
排查方向:
- 容器是否运行。
- 是否正确配置
-p端口映射。 - 应用是否监听
0.0.0.0。 - 防火墙或云服务器安全组是否放行。
- 容器日志是否报错。
16. Docker 中 latest 标签有什么问题?
latest 不代表最新稳定版本,只是一个普通标签。生产环境使用 latest 可能导致版本不可控,建议使用明确版本号。
17. 什么是 Dockerfile 构建缓存?
Docker 构建镜像时会按指令逐层执行。如果某一层指令和上下文没有变化,Docker 会复用之前的缓存,从而加快构建速度。
18. 如何限制容器资源?
docker run -d \
--name app \
--memory=512m \
--cpus=1 \
nginx
19. 容器内应用为什么要前台运行?
容器的生命周期由主进程决定。如果主进程退出,容器也会退出。因此容器中的主应用一般要以前台方式运行。
20. Docker 适合运行有状态服务吗?
可以运行,但要正确处理数据持久化、备份、恢复、网络和资源限制。数据库这类有状态服务在生产环境中要更加谨慎,通常需要结合专业运维方案。
16. 学习路线建议
- 先掌握镜像、容器、仓库三个核心概念。
- 熟练使用
docker run、docker ps、docker logs、docker exec。 - 学会网络和数据卷,理解容器间通信和数据持久化。
- 学会编写 Dockerfile,把自己的应用打包成镜像。
- 学会 Docker Compose,能启动一套完整开发环境。
- 最后再学习 Kubernetes、CI/CD、镜像仓库和生产环境部署。