这篇文章是一份面向个人工程实践的 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 用户态程序,例如 shjavals
  • 不拥有独立内核,而是共享宿主机内核
  • 更适合描述为“进程级隔离环境”,而不是真正意义上的虚拟机

一句话理解:

容器负责隔离运行环境,镜像负责定义运行环境。

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.conf
  • docker-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