返回文章列表
·29 分钟阅读

Docker 学习手册(WSL Ubuntu 实践版)

零基础边看边练——安装、镜像、容器、网络、卷、Compose、排障全流程。

DockerWSLUbuntu

适用环境:Windows 11/10 + WSL Ubuntu
目标读者:零基础小白,边看边练
最后更新:2026-06-23


1. Docker 是什么

一句话:Docker 把应用和它的依赖打包成一个标准化的"集装箱",在哪台机器上都能一模一样地运行。

1.1 架构图解

1.2 核心概念(三句话)

概念类比说明
镜像 (Image)类 / 模板只读的打包文件,比如一个 Ubuntu + JDK 的包
容器 (Container)对象 / 实例从镜像启动的运行中的进程,可读可写,隔离
卷 (Volume)移动硬盘容器删了数据还在的地方,用于持久化

1.3 为什么 Docker 比虚拟机轻量?

  • 虚拟机:每个 App 都要装一套完整的 Guest OS,几个 G 起步,启动慢。
  • Docker:所有容器共享宿主机内核,启动是秒级的,占资源极小。

2. 在 WSL Ubuntu 上安装 Docker

2.1 前置检查

先确保你的 WSL Ubuntu 是最新的:

# 更新包索引
sudo apt update

# 升级已有包
sudo apt upgrade -y

2.2 一键安装(官方脚本)

# 1. 下载并运行官方安装脚本
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# 2. 把当前用户加入 docker 组(免 sudo)
sudo usermod -aG docker $USER

# 3. 重启 WSL 终端(或执行 newgrp docker)
newgrp docker

# 4. 验证
docker version
docker run hello-world

看到 Hello from Docker! 就成功了。

2.3 如果脚本失败(国内网络问题)

参考 常见问题排障 的国内镜像方案。


3. 核心概念速查表

关键参数记忆法:

  • docker = 客户端命令
  • run = 创建并启动一个容器
  • -d = detached(后台运行,detach)
  • -p = 端口映射(宿主机:容器)
  • -v = 挂载卷(共享目录)
  • --name = 给容器起个别名
  • -it = interactive + tty(交互式,常用于调试 shell)

4. 镜像操作

4.1 查看本地镜像

[root@kuangshen /]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
hello-world  latest    bf756fb1ae65   4 months ago  13.3kB

解释

  • REPOSITORY — 镜像的仓库源
  • TAG — 镜像的标签
  • IMAGE ID — 镜像的 id
  • CREATED — 镜像的创建时间
  • SIZE — 镜像的大小

可选项

  • -a, --all — 列出所有镜像
  • -q, --quiet — 只显示镜像的 id
# 精简显示(只看 ID 和名字)
docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"

# 查看镜像详情
docker inspect ubuntu:22.04

4.2 搜索与拉取

[root@kuangshen /]# docker search mysql
NAME                           DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                          MySQL is a widely used relational database…   15000     [OK]
mariadb                        MariaDB is a community-developed fork of My…   7000      [OK]

解释

  • NAME — 镜像名称
  • DESCRIPTION — 镜像描述
  • STARS — 收藏/星标数量
  • OFFICIAL — 官方维护的镜像
  • AUTOMATED — 自动构建

可选项

  • --filter STARS=3000 — 按星标筛选
# 拉取指定版本(推荐指定版本,避免拉 latest 导致不兼容)
docker pull mysql:8.0
docker pull openjdk:21-jdk

# 查看下载进度
docker images                           # 能看到 SIZE 变化

4.3 删除镜像

[root@kuangshen /]# docker rmi hello-world
Untagged: hello-world:latest
Untagged: hello-world@sha256:...
Deleted: sha256:bf756fb1ae65...
Deleted: sha256:...

解释

  • Untagged — 移除镜像标签(镜像层还在)
  • Deleted — 真正从本地删除镜像层

可选项

  • -f, --force — 强制删除正在被容器使用的镜像
# 一次性删除所有悬挂镜像(无标签的)
docker image prune

4.4 查看镜像分层

# 镜像由多层 Layer 叠加而成(共享层很省空间)
docker history nginx:latest

4.5 构建自己的镜像(Dockerfile)

# 目录结构
mkdir -p ~/docker-demo/my-java-app
cd ~/docker-demo/my-java-app

# 编写 Dockerfile
cat > Dockerfile << 'EOF'
# 基础镜像
FROM openjdk:21-jdk-slim

# 作者信息
LABEL maintainer="yourname"

# 工作目录(后续命令都在这个目录下执行)
WORKDIR /app

# 复制文件(先复制构建依赖,利用缓存层)
COPY pom.xml .
COPY src ./src

# 构建项目(如果有 Maven 镜像,可选)
# RUN mvn package -DskipTests

# 暴露端口(只是声明,实际映射靠 -p)
EXPOSE 8080

# 启动命令
CMD ["java", "-jar", "target/app.jar"]
EOF

# 构建镜像
docker build -t my-java-app:1.0 .

# 验证
docker images | grep my-java-app

5. 容器生命周期

5.1 创建并启动容器(run)

[root@kuangshen /]# docker run -d -p 8080:80 --name my-nginx nginx:alpine
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1

解释

  • -d — 后台运行(Detached)
  • -p 8080:80 — 宿主机 8080 端口映射到容器 80 端口
  • --name my-nginx — 给容器起个别名,否则随机生成哈希名
  • nginx:alpine — 镜像名 + 标签
# Java 示例:运行 Spring Boot 项目
docker run -d \
  -p 8080:8080 \
  -v ~/my-app/logs:/app/logs \
  --name my-app \
  my-java-app:1.0

# 常用参数详解:
# -d          后台运行(Detached)
# -p 8080:80  宿主机8080端口 → 容器80端口
# -v /主机:/容器  挂载目录(双向同步)
# --name      容器别名(否则随机生成哈希名)

5.2 运行流程图

5.3 查看容器状态

[root@kuangshen /]# docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                  NAMES
a1b2c3d4e5f6   nginx:alpine   "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   0.0.0.0:8080->80/tcp   my-nginx

解释

  • CONTAINER ID — 容器 ID
  • IMAGE — 使用的镜像
  • COMMAND — 容器启动时执行的命令
  • CREATED — 创建时间
  • STATUS — 当前状态(Up/Running、Exited 等)
  • PORTS — 端口映射
  • NAMES — 容器名称

可选项

  • -a, --all — 列出所有容器(包括已停止的)
  • -q, --quiet — 只显示容器 ID
  • -l, --last — 查看最后一次运行的容器
# 查看所有容器(包括已停止的)
docker ps -a

# 只显示容器 ID
docker ps -q

# 查看最后一次运行的容器
docker ps -l

5.4 容器启停与重启

[root@kuangshen /]# docker stop my-nginx
my-nginx

解释

  • my-nginx — 成功停止的容器名称

可选项

没有额外的可选项,直接跟容器名或 ID 即可。

# 启动已停止的容器(返回容器 ID)
docker start my-nginx

# 强制杀死(相当于 kill -9)
docker kill my-nginx
docker kill --signal=SIGKILL my-nginx

# 重启
docker restart my-nginx

# 暂停(冻结进程,保留内存状态)
docker pause my-nginx

# 恢复
docker unpause my-nginx

5.5 进入容器内部

[root@kuangshen /]# docker exec -it my-nginx bash
root@a1b2c3d4e5f6:/#

解释

  • exec — 在运行中的容器里执行新命令
  • -it — 交互式终端
  • bash — 要执行的命令

可选项

可以用 sh 替代 bash(有些镜像里没有 bash)。

# 进去后 exit 退出,不停止容器
exit

# 启动新容器直接进去(用于快速实验)
docker run -it --rm ubuntu:22.04 bash
# --rm 表示退出后自动删除容器(不留垃圾)

5.6 查看日志

[root@kuangshen /]# docker logs -f my-app
2026-06-23 10:18:12,345 INFO  [main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http)
2026-06-23 10:18:12,789 INFO  [main] com.example.demo.DemoApplication        : Started DemoApplication in 3.45 seconds

解释

  • -f, --follow — 实时跟踪日志输出
  • 日志按容器启动顺序追加,ctrl+c 退出跟踪

可选项

  • --tail N — 查看最后 N 行,默认显示全部
  • -t, --timestamps — 在每行加时间戳
# 查看最近 100 行
docker logs --tail 100 my-app

# 带时间戳
docker logs -t my-app

5.7 删除容器

[root@kuangshen /]# docker rm my-nginx
my-nginx

解释

  • my-nginx — 被删除的容器名称

可选项

  • -f, --force — 强行删除运行中的容器(先 stop,再 rm)
# 启动中 → 强行删(等价 stop + rm)
docker rm -f my-nginx

# 一次删所有已停止的容器(大扫除)
docker container prune

6. 网络

6.1 Docker 网络类型

模式说明
bridge(默认)容器有独立 IP,同一 bridge 内可通,宿主机也可通过端口访问
host容器直接共享宿主机网络栈,无隔离,端口不冲突
none无网卡,完全隔离
container复用另一个容器的网络

6.2 常用操作

# 创建自定义桥接网络(推荐,比默认的 bridge 更灵活)
docker network create my-net

# 让两个容器在同一个网络里(可通过容器名互相访问)
docker run -d --name mysql --network my-net \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

docker run -d --name my-app --network my-net \
  -p 8080:8080 \
  my-java-app:1.0

# 这时 my-app 里连接数据库直接用:
# jdbc:mysql://mysql:3306/mydb
# 注意:是容器名 mysql,不是 IP

6.3 网络拓扑


7. 数据持久化

7.1 为什么需要持久化?

容器内的文件是临时的,容器删了,写的文件全没了。

7.2 两种持久化方式

方式声明方式特点
Bind Mount-v ~/host:/container直接映射主机目录,开发时调试方便
Volume-v my-volume:/containerDocker 管理的虚拟磁盘,更适合生产

7.3 实战对比

# 方式一:Bind Mount(绑定主机目录)
# 适合开发:改了主机代码,容器里立即可见(热部署)
docker run -d \
  -v "$(pwd)":/app \
  -p 8080:8080 \
  my-java-app:1.0

# 方式二:Volume(Docker 管理的卷)
# 适合数据库等:MySQL 数据存在卷里,容器随便删数据不丢
docker run -d \
  -v mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  mysql:8.0

# 查看所有卷
docker volume ls

# 查看卷详情
docker volume inspect mysql-data

7.4 架构示意


8. Docker Compose

当你需要同时跑多个容器(比如 Spring Boot + MySQL + Redis),逐个敲 docker run 太痛苦。Docker Compose 用 YAML 一键编排。

8.1 安装 Compose

现代 Docker 已经自带 docker compose(注意空格):

# 确认版本(V2 才有 docker compose 带空格)
docker compose version

8.2 实战:Spring Boot + MySQL

mkdir -p ~/docker-demo/compose-demo
cd ~/docker-demo/compose-demo

创建 docker-compose.yml:

version: '3.8'

services:
  # 服务1:MySQL
  mysql:
    image: mysql:8.0
    container_name: compose-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: mydb
    ports:
      - "3306:3306"
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - app-net

  # 服务2:Java App(假设你已经 build 好镜像)
  app:
    image: my-java-app:1.0
    container_name: compose-app
    restart: always
    ports:
      - "8080:8080"
    depends_on:
      - mysql
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/mydb
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: secret
    networks:
      - app-net

# 定义卷(重复使用)
volumes:
  mysql-data:

# 定义网络
networks:
  app-net:

常用命令:

# 启动所有服务(后台)
docker compose up -d

# 查看状态
docker compose ps

# 查看日志(跟着服务名看)
docker compose logs -f app

# 停止所有服务(容器保留)
docker compose stop

# 停止并删除容器
docker compose down

# 停止、删容器、删卷(数据也清空!慎用)
docker compose down -v

# 重新构建镜像后重启
docker compose up -d --build

8.3 Compose 工作流


9. 常用组合命令速查

9.1 一键清理

# 删除所有已停止的容器 + 悬挂镜像 + 未使用的卷
docker system prune -a --volumes

9.2 查看磁盘占用

docker system df

9.3 复制文件到 / 从容器

# 从容器拷出
docker cp my-app:/app/logs/app.log ./local-log.log

# 拷入容器
docker cp ./config.yaml my-app:/app/config.yaml

9.4 修改已停止容器的配置再启动

# 从旧容器创建新镜像
docker commit my-app my-app:modified

# 用新镜像启动
docker run -d -p 8080:8080 my-app:modified

9.5 查看容器资源占用

# 实时
docker stats

# 只看一下 my-app
docker stats my-app

9.6 快捷脚本(放 ~/.bashrc)

# 加到 ~/.bashrc 末尾
alias d='docker'
alias dps='docker ps'
alias dpa='docker ps -a'
alias di='docker images'
alias dstop='docker stop $(docker ps -q)'
alias drm='docker rm $(docker ps -aq)'
alias dprune='docker system prune -a'

然后 source ~/.bashrc 生效。以后敲 dps 就是 docker ps。


10. 常见问题排障

10.1 Docker 启动失败

# 查看状态
sudo systemctl status docker

# 手动启动
sudo systemctl start docker

10.2 权限不足(Cannot connect)

# 忘了加 docker 组
sudo usermod -aG docker $USER
newgrp docker
# 或者直接重启终端

10.3 WSL 里无法访问 localhost(Windows 浏览器打不开)

# 现象:容器在跑,但 Windows 浏览器访问 http://localhost:8080 报错

# 1. 确认 WSL 监听在 0.0.0.0
# 容器内不要 bind 127.0.0.1,否则 WSL 外部访问不到

# 2. 用 WSL IP 访问
# 在 WSL 终端里看 IP
hostname -I | awk '{print $1}'
# 比如得到 172.20.5.123
# Windows 浏览器访问 http://172.20.5.123:8080

# 3. 或者直接用 docker run 的 -p 指定后,
# 很多情况下 localhost 直接可用,不行就用 WSL IP

10.4 国内镜像加速

Docker Hub 在国内拉取经常超时。解决方案:

# 方法1:使用国内镜像站点
# 编辑 /etc/docker/daemon.json
sudo tee /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "https://dockerhub.azk8s.cn",
    "https://hub-mirror.c.163.com"
  ]
}
EOF

# 重启 Docker
sudo systemctl restart docker

# 验证
docker info | grep -i mirror
# 方法2:拉取时直接指定国内镜像站的地址
# 比如 pull nginx 时用阿里云镜像
docker pull registry.cn-hangzhou.aliyuncs.com/nginx:alpine

# 先 tag 成标准名字方便后续使用
docker tag registry.cn-hangzhou.aliyuncs.com/nginx:alpine nginx:alpine

10.5 容器启动后立刻退出(Exited)

# 原因:前台进程结束后,容器认为任务完成就退出

# 排查
docker logs <container_id>

# 常见原因:
# 1. CMD 写错了,比如 java 命令找不到
# 2. 没有保持前台进程,比如写了 service nginx start(后台化了)
# 3. Java 应用启动失败直接 crash

# 修复:确保 Dockerfile 的 CMD 前台运行
# 正确:
CMD ["java", "-jar", "app.jar"]
# 错误:
# CMD service nginx start   ← 后台启动,容器会退出
# CMD nginx                 ← 前台启动才对(nginx -g 'daemon off;')

附录:每日一句 Cheat Sheet

# 镜像
docker images                          # 列出镜像
docker pull xxx                        # 下载镜像
docker build -t name .                 # 构建镜像

# 容器(run 是一键创建+启动,最常用)
docker run -d -p 8080:80 --name web nginx:alpine

# 容器管理
docker ps                              # 看运行中的
docker ps -a                           # 看所有的
docker start/stop/restart <id>         # 启停重启
docker exec -it <id> bash              # 进容器内部
docker logs -f <id>                    # 实时日志
docker rm -f <id>                      # 删除容器

# Compose
docker compose up -d                   # 启动一组服务
docker compose logs -f <service>       # 看日志
docker compose down                    # 停掉服务

# 清理
docker system prune -a --volumes       # 大扫除

学习建议:不要光看,每个命令都亲手敲一遍。遇到报错直接复制去搜,或看 docker logs,解决报错的过程就是最好的学习。

Docker 学习手册(WSL Ubuntu 实践版) — Aohs