返回文章列表
·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:/container | Docker 管理的虚拟磁盘,更适合生产 |
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,解决报错的过程就是最好的学习。