Docker 学习笔记
这篇文章是一份面向个人工程实践的 Docker 入门整理,重点记录 Docker、Dockerfile、Docker Compose,以及它们在 Windows + WSL2 环境下的实际工作方式。
文章定位#
这不是一篇面向生产环境的完整 Docker 手册,而是一份偏向个人工程实践的入门梳理,目标是把几个最常见但最容易混淆的问题讲清楚:
- Docker 到底是什么
- Dockerfile 和 Docker Compose 分别解决什么问题
- Windows + WSL2 下 Docker 到底运行在哪里
- 一个前后端项目如何通过 Compose 完成部署
参考资料#
Docker 是什么#
Docker 是基于 Linux 内核能力实现的容器技术,核心依赖包括命名空间(Namespaces)和控制组(cgroups)。它的本质不是“轻量虚拟机”,而是运行在宿主机 Linux 内核之上的隔离用户态环境。
因此,Docker 容器具备这些特征:
- 有自己的文件系统视图,例如
/bin、/etc、/usr、/app - 可以运行 Linux 用户态程序,例如
sh、java、ls - 不拥有独立内核,而是共享宿主机内核
- 更适合描述为“进程级隔离环境”,而不是真正意义上的虚拟机
一句话理解:
容器负责隔离运行环境,镜像负责定义运行环境。
Windows + WSL2 下的 Docker 运行方式#
在 Windows 环境中,Docker 并不是“直接跑在 Windows 内核上”。Docker Desktop 实际依赖的是 WSL2 提供的 Linux 内核环境。
你可以这样理解:
- Windows 是宿主操作系统
- WSL2 提供真实的 Linux 内核能力
- Docker Desktop 运行并管理 Docker 引擎
- 你在 WSL 终端中执行
docker ps,本质上是在调用 Docker Desktop 暴露给 WSL 的接口
所以,当前这类开发方式最适合的原则通常是:
- 代码放在 WSL 的 Linux 文件系统中,例如
/home/lst041/... - 容器、镜像和图形化管理交给 Docker Desktop
- 命令操作在 WSL 或 PowerShell 中完成,但要清楚引擎本身由 Docker Desktop 托管
Dockerfile 与 Docker Compose 的区别#
这两个概念经常被混用,但职责完全不同。
Dockerfile:定义“单个镜像如何构建”#
Dockerfile 用来描述一个镜像的构建过程,例如:
- 使用哪个基础镜像
- 工作目录是什么
- 需要复制哪些文件
- 暴露哪个端口
- 容器启动时执行什么命令
一个最常见的 Spring Boot 示例:
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Docker Compose:定义“多个服务如何协同运行”#
Docker Compose 面向的是系统级编排,而不是单个镜像构建。它主要解决的问题包括:
- 多个容器如何一起启动
- 服务之间如何通信
- 哪些端口要暴露给宿主机
- 哪些目录需要挂载
- 数据如何持久化
如果把 Dockerfile 看成“做一台机器”,那 Docker Compose 更像是“把多台机器和网络一起搭起来”。
一个基础的 Dockerfile 使用流程#
1. 准备应用产物#
以 Java 项目为例,先确保已经打出 JAR 包,并且 Dockerfile 能够正确访问它:
Dockerfile一般放在项目根目录target/目录中需要存在可执行 JAR- JDK 版本要与项目构建产物匹配
2. 构建镜像#
docker build -t my-app .
说明:
-t表示给镜像打标签(tag).表示当前目录作为构建上下文
3. 运行容器#
docker run -d -p 8080:8080 --name app my-app
说明:
-d表示后台运行-p 8080:8080表示宿主机端口映射到容器端口--name app为容器指定名称
常用 Docker 命令速查#
容器与镜像查看#
docker ps
docker ps -a
docker images
docker --version
docker info
拉取、运行与进入容器#
docker pull nginx:alpine
docker run -d --name mynginx -p 80:80 nginx:alpine
docker logs mynginx
docker exec -it mynginx sh
停止、启动与删除#
docker stop mynginx
docker start mynginx
docker restart mynginx
docker rm mynginx
docker rmi nginx:alpine
清理命令#
docker image prune
docker system prune
构建与调试#
docker build -t my-springboot-app .
docker run -it xxx sh
docker exec -it xxx sh
Docker Compose 部署示例#
这里用一个典型的前后端项目部署流程来说明 Compose 的使用方式。
1. 构建后端镜像#
FROM eclipse-temurin:8-jdk-alpine
WORKDIR /app
COPY target/*.jar app.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
构建命令:
docker build -t springboot .
docker images
2. 编写 Nginx 配置#
这个配置主要负责两件事:
- 托管前端静态资源
- 将
/api请求反向代理到后端容器
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://backend:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
3. 编写 docker-compose.yml#
version: "3.8"
services:
db:
image: mysql:8.0
container_name: library-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: Llsstt2324
MYSQL_DATABASE: library
TZ: Asia/Shanghai
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/init:/docker-entrypoint-initdb.d
networks:
- library-net
backend:
image: springboot:latest
container_name: library-backend
restart: always
depends_on:
- db
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/library?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: Llsstt2324
networks:
- library-net
nginx:
image: nginx:alpine
container_name: library-nginx
restart: always
ports:
- "80:80"
volumes:
- ./Library-frontend/dist:/usr/share/nginx/html
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- backend
networks:
- library-net
networks:
library-net:
driver: bridge
4. 启动整套服务#
docker compose up -d
访问地址:
http://localhost
常用配套命令:
docker compose up -d --build backend
docker compose ps
docker logs -f library-backend
Docker Compose 常用命令#
生命周期管理#
| 命令 | 作用 |
|---|---|
docker compose up -d | 后台启动所有服务 |
docker compose down | 停止并删除容器和网络 |
docker compose stop | 停止服务但保留容器 |
docker compose start | 启动已停止的服务 |
docker compose restart | 重启所有服务 |
状态检查与排错#
| 命令 | 作用 |
|---|---|
docker compose ps | 查看容器状态 |
docker compose logs -f | 查看全部服务日志 |
docker compose logs -f backend | 查看指定服务日志 |
docker compose top | 查看进程与资源占用 |
docker compose config | 校验并展开配置 |
重新构建#
| 命令 | 作用 |
|---|---|
docker compose up -d --build | 重新构建并启动变更服务 |
docker compose up -d --build backend | 只更新指定服务 |
docker compose build --no-cache | 强制无缓存构建 |
两种常见的 Compose 使用模式#
模式一:先手动构建,再交给 Compose 运行#
docker build -t springboot:latest .
docker compose up -d
对应 Compose 配置:
backend:
image: springboot:latest
适用场景:
- 学习阶段
- 镜像需要复用
- 镜像来源于 Docker Hub
模式二:由 Compose 负责构建#
backend:
build:
context: .
dockerfile: docker/backend/Dockerfile
启动命令:
docker compose up -d --build
这个模式更接近真实工程实践,因为镜像构建与服务编排绑定得更紧。
端口流向应该怎么理解#
以 Spring Boot API 为例,常见有两条访问路径。
方式一:直接访问后端容器#
浏览器 -> 宿主机端口 9090 -> 容器端口 8080 -> Spring Boot 应用
方式二:通过 Nginx 反向代理#
浏览器 -> 宿主机端口 80 -> Nginx 容器 -> 后端容器 8080
无论走哪条路径,真正提供服务的都是容器内部的应用端口。
把镜像推送到 Docker Hub#
1. 给镜像打标签#
docker tag springboot:latest yourname/springboot-library:v1.0
2. 推送镜像#
docker push yourname/springboot-library:v1.0
3. 修改 Compose 配置#
backend:
image: yourname/springboot-library:v1.0
container_name: library-backend
restart: always
从 Docker Hub 拉镜像并运行#
1. 拉取镜像#
docker pull authorname/springboot-library:v1.0
2. 创建目录结构#
mkdir -p library-app/nginx library-app/mysql/init library-app/Library-frontend/dist
3. 准备配置文件#
nginx/default.confdocker-compose.yml
4. 准备初始化 SQL 与前端产物#
cp /mnt/e/Vue-Springboot-Library/mysql/init/springboot-vue.sql /home/lst041/library-app/mysql/init/
cp -r /mnt/e/Vue-Springboot-Library/Library-frontend/dist /home/lst041/library-app/Library-frontend/
5. 启动#
cd ~/library-app
docker compose up -d
一个更标准的项目目录结构#
project/
├── docker-compose.yml
├── backend/
│ └── Dockerfile
├── frontend/
│ └── dist/
└── mysql/
└── init/
└── init.sql
多行命令在不同终端中的写法#
| 终端 | 换行符 |
|---|---|
| Ubuntu / WSL / Bash | \ |
| Windows CMD | ^ |
| PowerShell | ` |
如果没有强需求,我通常更推荐直接写成单行命令,可读性和兼容性都更稳定:
docker run -d -p 8080:8080 --name app springboot