Docker 学习笔记
1. Docker 是什么
Docker 是一个开源的容器化平台,用来把应用程序及其运行依赖打包成镜像,并通过容器的方式运行。容器可以理解为一个轻量级、可移植、隔离的应用运行环境。
与传统虚拟机相比,Docker 容器不需要为每个应用启动完整操作系统,而是共享宿主机内核,因此启动更快、资源占用更少、部署更方便。
常见使用场景:
- 开发环境统一:避免"我电脑能跑,你电脑不能跑"。
- 应用快速部署:镜像构建一次,可以在多台机器运行。
- 微服务部署:每个服务独立容器化。
- CI/CD 自动化发布:构建、测试、发布流程标准化。
- 本地快速启动中间件:MySQL、Redis、Nginx、RabbitMQ 等。
2. Docker 与虚拟机对比
| 对比项 | Docker 容器 | 虚拟机 |
|---|---|---|
| 隔离方式 | 进程级隔离,共享宿主机内核 | 硬件级虚拟化,独立操作系统 |
| 启动速度 | 秒级甚至毫秒级 | 通常较慢 |
| 资源占用 | 较少 | 较多 |
| 镜像体积 | 通常较小 | 通常较大 |
| 可移植性 | 很强 | 较强 |
| 隔离强度 | 弱于虚拟机 | 更强 |
一句话记忆:虚拟机像"装了一台新电脑",容器像"启动了一个隔离的应用进程"。
3. 容器相关技术体系
3.1 容器核心技术

Docker 的底层依赖 Linux 内核提供的能力:
- Namespace:命名空间,用于资源隔离。
- PID Namespace:进程隔离。
- NET Namespace:网络隔离。
- MNT Namespace:文件系统挂载隔离。
- IPC Namespace:进程间通信隔离。
- UTS Namespace:主机名和域名隔离。
- USER Namespace:用户和用户组隔离。
- Cgroups:控制组,用于资源限制。
- 限制 CPU 使用。
- 限制内存使用。
- 限制磁盘 I/O。
- 限制进程数量。
- UnionFS:联合文件系统,用于镜像分层。
- Docker 镜像由多层只读层组成。
- 容器启动后会在镜像层之上增加一层可写层。
- 多个镜像可以复用相同层,节省空间。
3.2 容器平台技术

容器平台通常包括:
- 镜像构建:Dockerfile、BuildKit。
- 镜像仓库:Docker Hub、Harbor、阿里云镜像仓库等。
- 容器运行:Docker Engine、containerd、runc。
- 容器编排:Docker Compose、Kubernetes。
- 日志监控:docker logs、ELK、Prometheus、Grafana。
3.3 容器支持技术

容器运行离不开网络、存储、安全、监控等支持能力:
- 网络:bridge、host、none、自定义网络。
- 存储:volume、bind mount、tmpfs。
- 安全:权限控制、只读文件系统、非 root 用户运行。
- 监控:容器状态、CPU、内存、网络、磁盘。
- 编排:服务发现、滚动更新、自动扩缩容。
4. Docker 核心概念
4.1 镜像 Image
镜像是容器运行的模板,里面包含应用程序、依赖库、配置文件、运行命令等。
特点:
- 镜像是只读的。
- 镜像是分层的。
- 镜像可以基于另一个镜像构建。
- 镜像可以上传到仓库,也可以从仓库拉取。
示例:
bash
docker pull nginx:latest
docker images
4.2 容器 Container
容器是镜像运行起来之后的实例。
特点:
- 一个镜像可以启动多个容器。
- 容器之间默认相互隔离。
- 容器删除后,容器可写层中的数据默认会丢失。
- 持久化数据应使用 volume 或 bind mount。
示例:
bash
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 私有仓库。
常用命令:
bash
docker login
docker tag myapp:1.0 username/myapp:1.0
docker push username/myapp:1.0
docker pull username/myapp:1.0
5. Docker 安装
以下以 CentOS Stream 8 为例。
5.1 准备环境
实验环境:
- 容器管理工具:Docker Engine。
- 容器运行时:containerd、runc。
- 操作系统:CentOS Stream 8。
5.2 配置 Docker 软件源
bash
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
bash
yum install -y docker-ce docker-ce-cli containerd.io
5.4 启动 Docker
bash
systemctl enable docker.service --now
docker --version
systemctl status docker
5.5 配置镜像加速
bash
mkdir -p /etc/docker
vim /etc/docker/daemon.json
示例配置:
json
{
"registry-mirrors": [
"https://054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com"
]
}
重启 Docker:
bash
systemctl daemon-reload
systemctl restart docker
docker info
6. Docker 常用命令
6.1 镜像命令
bash
# 查看本地镜像
docker images
# 搜索镜像
docker search nginx
# 拉取镜像
docker pull nginx:latest
# 删除镜像
docker rmi nginx:latest
# 查看镜像详细信息
docker inspect nginx
# 查看镜像构建历史
docker history nginx
6.2 容器命令
bash
# 创建并启动容器
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 进入容器
bash
docker exec -it web bash
如果容器中没有 bash,可以使用 sh:
bash
docker exec -it web sh
6.4 查看日志
bash
docker logs web
docker logs -f web
docker logs --tail 100 web
6.5 文件复制
bash
# 从宿主机复制文件到容器
docker cp ./index.html web:/usr/share/nginx/html/index.html
# 从容器复制文件到宿主机
docker cp web:/etc/nginx/nginx.conf ./nginx.conf
6.6 查看资源占用
bash
docker stats
6.7 清理资源
bash
# 删除停止的容器
docker container prune
# 删除无用镜像
docker image prune
# 删除无用网络
docker network prune
# 删除无用 volume
docker volume prune
# 清理所有无用资源
docker system prune
注意:docker system prune -a 会删除所有未被容器使用的镜像,生产环境慎用。
7. docker run 常用参数
bash
docker run [OPTIONS] IMAGE [COMMAND]
常用参数:
| 参数 | 作用 |
|---|---|
-d |
后台运行 |
-it |
交互式终端 |
--name |
指定容器名称 |
-p |
端口映射 |
-v |
挂载目录或数据卷 |
-e |
设置环境变量 |
--network |
指定网络 |
--restart |
设置重启策略 |
--rm |
容器退出后自动删除 |
--privileged |
开启特权模式 |
--memory |
限制内存 |
--cpus |
限制 CPU |
示例:
bash
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 常用网络命令
bash
# 查看网络
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 容器之间通信
推荐使用自定义网络。处于同一个自定义网络中的容器,可以通过容器名互相访问。
示例:
bash
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 管理的数据卷,适合保存数据库数据。
bash
# 创建数据卷
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 是把宿主机目录挂载到容器中,适合挂载配置文件、项目代码。
bash
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 静态站点
dockerfile
FROM nginx:1.25-alpine
COPY ./dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
构建和运行:
bash
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 应用
dockerfile
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 定义容器固定入口,更适合作为主程序入口。
示例:
dockerfile
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
CMD ["--spring.profiles.active=prod"]
运行时可以覆盖 CMD 参数:
bash
docker run myapp:1.0 --spring.profiles.active=test
10.5 镜像构建优化
- 选择更小的基础镜像,例如 alpine、slim。
- 合并相关
RUN指令,减少镜像层数。 - 使用
.dockerignore排除无关文件。 - 利用构建缓存,把不常变化的步骤放前面。
- 使用多阶段构建减少最终镜像体积。
- 不把密码、密钥写入镜像。
- 尽量使用非 root 用户运行应用。
10.6 多阶段构建示例
dockerfile
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"]
11. Docker Compose
Docker Compose 用于定义和运行多个容器。适合本地开发、测试环境和小规模部署。
11.1 Compose 常用命令
bash
# 启动服务
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
yaml
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:
启动:
bash
docker compose up -d
12. Docker 日志与排错
12.1 常见排错命令
bash
# 查看容器状态
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 容器运行一个静态页面。
- 将宿主机目录挂载到容器。
- 通过浏览器访问页面。
步骤:
bash
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
访问:
text
http://服务器IP:8080
项目 2:使用 Docker 部署 MySQL
目标:
- 启动 MySQL 8 容器。
- 使用 volume 持久化数据。
- 使用客户端连接数据库。
步骤:
bash
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:
bash
docker exec -it mysql8 mysql -uroot -p123456
项目 3:使用 Dockerfile 打包 Spring Boot 项目
目标:
- 将 Spring Boot 项目打包成 jar。
- 编写 Dockerfile。
- 构建镜像并运行容器。
Dockerfile:
dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
构建运行:
bash
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
yaml
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:
启动:
bash
docker compose up -d --build
停止:
bash
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. 如何查看容器日志?
bash
docker logs 容器名
docker logs -f 容器名
docker logs --tail 100 容器名
14. 如何进入正在运行的容器?
bash
docker exec -it 容器名 bash
如果没有 bash:
bash
docker exec -it 容器名 sh
15. Docker 容器访问不了外部端口怎么办?
排查方向:
- 容器是否运行。
- 是否正确配置
-p端口映射。 - 应用是否监听
0.0.0.0。 - 防火墙或云服务器安全组是否放行。
- 容器日志是否报错。
16. Docker 中 latest 标签有什么问题?
latest 不代表最新稳定版本,只是一个普通标签。生产环境使用 latest 可能导致版本不可控,建议使用明确版本号。
17. 什么是 Dockerfile 构建缓存?
Docker 构建镜像时会按指令逐层执行。如果某一层指令和上下文没有变化,Docker 会复用之前的缓存,从而加快构建速度。
18. 如何限制容器资源?
bash
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、镜像仓库和生产环境部署。