Docker完整学习笔记
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还能正常访问的
{ "registry-mirrors": [ "https://docker.1ms.run", "https://docker.xuanyuan.me", "https://docker.m.daocloud.io" ] } 3. Docker 基本操作
常用命令
# ========== 镜像管理 ========== # 列出本地镜像 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容器备份恢复
将一台机器上的容器备份恢复到另一台机器
# ========== 场景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/healthDockerFile和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/是 Vuenpm 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_filescompose 自定义网络 → 服务间用容器名互访,不用写 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容器是镜像运行起来之后的实例。
特点:
- 一个镜像可以启动多个容器。
- 容器之间默认相互隔离。
- 容器删除后,容器可写层中的数据默认会丢失。
- 持久化数据应使用 volume 或 bind mount。
示例:
docker run -d --name my-nginx -p 8080:80 nginx docker ps docker stop my-nginx docker rm my-nginx镜像仓库用于保存和分发镜像。
常见仓库:
- 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.0AI写代码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.repoyum install -y docker-ce docker-ce-cli containerd.io5.4 启动 Docker
systemctl enable docker.service --now docker --version systemctl status docker5.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 info6. Docker 常用命令
6.1 镜像命令
# 查看本地镜像 docker images # 搜索镜像 docker search nginx # 拉取镜像 docker pull nginx:latest # 删除镜像 docker rmi nginx:latest # 查看镜像详细信息 docker inspect nginx # 查看镜像构建历史 docker history nginx6.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 web6.3 进入容器
docker exec -it web bash如果容器中没有 bash,可以使用 sh:
docker exec -it web sh6.4 查看日志
docker logs web docker logs -f web docker logs --tail 100 web6.5 文件复制
# 从宿主机复制文件到容器 docker cp ./index.html web:/usr/share/nginx/html/index.html # 从容器复制文件到宿主机 docker cp web:/etc/nginx/nginx.conf ./nginx.conf6.6 查看资源占用
docker stats6.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.08. 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 nginx8.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 redis9. 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.09.3 bind mount 目录挂载
bind mount 是把宿主机目录挂载到容器中,适合挂载配置文件、项目代码。
docker run -d \ --name web \ -p 8080:80 \ -v /opt/nginx/html:/usr/share/nginx/html \ nginx9.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.010.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=test10.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 用于定义和运行多个容器。适合本地开发、测试环境和小规模部署。
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 start11.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 -d12. Docker 日志与排错
12.1 常见排错命令
# 查看容器状态 docker ps -a # 查看容器日志 docker logs 容器名 # 查看容器详细信息 docker inspect 容器名 # 进入容器检查 docker exec -it 容器名 sh # 查看容器资源占用 docker stats # 查看端口映射 docker port 容器名 # 查看 Docker 服务日志 journalctl -u docker -f12.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 down15. 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 容器名 sh15. 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 \ nginx19. 容器内应用为什么要前台运行?
容器的生命周期由主进程决定。如果主进程退出,容器也会退出。因此容器中的主应用一般要以前台方式运行。
20. Docker 适合运行有状态服务吗?
可以运行,但要正确处理数据持久化、备份、恢复、网络和资源限制。数据库这类有状态服务在生产环境中要更加谨慎,通常需要结合专业运维方案。
16. 学习路线建议
- 先掌握镜像、容器、仓库三个核心概念。
- 熟练使用
docker run、docker ps、docker logs、docker exec。 - 学会网络和数据卷,理解容器间通信和数据持久化。
- 学会编写 Dockerfile,把自己的应用打包成镜像。
- 学会 Docker Compose,能启动一套完整开发环境。
- 最后再学习 Kubernetes、CI/CD、镜像仓库和生产环境部署。