Docker 基础:镜像、容器、卷与网络

Docker 解决的核心问题是“环境一致性”:镜像把代码、运行时、系统库一起打包,服务器上不再需要装 Python 或 JDK。但容器默认是易失的——删掉容器,里面的数据也一起消失。所以真正要建立直觉的是四件事:镜像与容器的关系、docker run 的关键参数、数据怎么落到卷上、容器之间怎么互相找到。

镜像、容器、仓库的关系

概念是什么类比生命周期
镜像 Image只读的分层文件模板类 / 安装包长期存在,可跨机器分发
容器 Container镜像之上的一层可写运行实例对象 / 进程随时可删,删除即丢可写层
仓库 Registry存放与分发镜像的服务应用商店长期存在
卷 Volume由 Docker 管理的数据目录外接硬盘删除容器不影响
网络 Network容器之间的虚拟网络交换机删除容器不影响

一句话概括链路:docker build 产出镜像并 push 到仓库,服务器 pullcompose up 后变成容器,容器接上卷存数据、接上网络互相访问。

安装 Docker 与国内镜像加速

# 官方脚本安装,--mirror Aliyun 走国内源
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
sudo systemctl enable --now docker

# 让普通用户直接用 docker(等价于给了 root 权限,谨慎)
sudo usermod -aG docker $USER
// /etc/docker/daemon.json —— 镜像加速与日志轮转
{
  "registry-mirrors": ["https://docker.m.daocloud.io"],
  "log-driver": "json-file",
  "log-opts": { "max-size": "50m", "max-file": "5" },
  "data-root": "/data/docker",
  "live-restore": true
}
sudo systemctl daemon-reload && sudo systemctl restart docker
docker info | grep -A3 'Registry Mirrors'

log-opts 一定要配:默认容器日志不轮转,跑几个月就能把系统盘写满。

docker run 关键参数

参数作用生产建议
-d后台运行部署必加
--name指定容器名有语义的名字,便于看日志与 exec
-p 127.0.0.1:8000:8000端口映射绑 127.0.0.1,公网入口交给 Nginx
-v /data/x:/app/data挂载数据数据必须落卷,别写在容器里
-e KEY=VALUE注入环境变量配置与密钥走这里,不要打进镜像
--restart unless-stopped重启策略长驻服务必加
--network appnet加入自定义网络多服务互访的前提
docker run -d \
  --name myapi \
  --restart unless-stopped \
  --network appnet \
  -p 127.0.0.1:8000:8000 \
  -e TZ=Asia/Shanghai -e APP_ENV=prod \
  -e DATABASE_URL='mysql://app:pass@db:3306/appdb' \
  -v /data/myapi/uploads:/app/uploads \
  --log-opt max-size=50m \
  myapi:1.4.0
重启策略行为适用
no(默认)不自动重启一次性任务
on-failure[:N]非 0 退出时重启,最多 N 次批处理、迁移脚本
always总是重启,手动 stop 后也会被拉起需谨慎
unless-stopped除手动 stop 外都重启长驻服务的推荐值

数据卷与绑定挂载

维度数据卷 -v volname:/path绑定挂载 -v /host/path:/path
存储位置data-root 下的 volumes/宿主机任意目录
可移植性高,可用 docker volume 迁移低,依赖宿主机目录结构
权限控制Docker 管理,较省心宿主机 uid/gid 需自己对齐
备份docker run --rm -v vol:/d -v $PWD:/b alpine tar czf /b/v.tgz -C /d .直接 tar 宿主机目录
适合数据库数据配置文件、代码目录、外挂日志

取舍建议:数据库数据用命名卷,配置文件与需要人工查看的日志用绑定挂载。把宿主机空目录绑到镜像里有内容的路径,会出现“文件被盖住”的现象,这是新手最容易懵的一类问题。

自定义网络与按服务名互访

docker network create appnet

# 同一网络内的容器可直接用容器名当主机名
docker run -d --name db --network appnet -e MYSQL_ROOT_PASSWORD=secret mysql:8.0
docker run -d --name myapi --network appnet -p 127.0.0.1:8000:8000 myapi:1.4.0

# 在应用容器里验证解析与连通性
docker exec -it myapi sh -c 'getent hosts db'
docker exec -it myapi sh -c 'nc -vz db 3306'

默认的 bridge 网络不支持按容器名解析,必须用自定义网络;容器名就是 DNS 名,重建后 IP 会变,所以连接配置里永远写服务名而不是 IP。

常用诊断与清理命令

docker ps -a                                  # 含已退出容器,看退出码
docker logs -f --tail 200 myapi               # 跟随日志
docker logs --since 30m myapi                 # 只看最近 30 分钟
docker exec -it myapi sh                      # 进容器排查(无 sh 用 bash)
docker inspect myapi --format '{{.State.Status}} {{.State.ExitCode}}'
docker stats --no-stream                      # 资源占用快照

docker system df                              # 先看占用,别盲删
docker image prune                            # 清理悬空镜像(<none>)
docker image prune -a --filter "until=168h"   # 清理一周前未使用的镜像
docker volume prune                           # 清理未使用的卷(会删数据,先确认)

生产固定策略:保留最近 3 个镜像 tag,docker system prune --volumes 当危险操作对待。

验证方法

docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
sudo ss -lntp | grep 8000              # 期望 127.0.0.1:8000
docker exec -it myapi sh -c 'nc -vz db 3306'   # 依赖是否真连通

# 删容器重建后数据仍在,才算卷配对了
docker rm -f myapi && docker run -d --name myapi -v mydata:/app/data myapi:1.4.0
docker exec -it myapi ls -l /app/data

常见坑

现象做法
数据写在容器里重建容器数据全丢用命名卷或绑定挂载
-p 3306:3306数据库暴露到公网且绕过 ufw-p 127.0.0.1:3306:3306 或不映射
默认 bridge 网络容器间用名字连不上docker network create 建自定义网络
容器日志不轮转磁盘被写满daemon.jsonlog-opts
容器里没有 shdocker exec 失败,无法排查选带 shell 的基础镜像
prune 顺手带 --volumes数据卷被删,数据库没了生产禁用该参数

小结:镜像是只读模板、容器是可写实例,凡是需要留存的东西必须落到卷上;docker run 记住 -d/--name/-p 127.0.0.1/-v/-e/--restart/--network 七个参数就够覆盖绝大多数场景;自定义网络让容器按服务名互访;最后把日志轮转、镜像保留策略和数据卷备份定成规矩,Docker 才不会变成新的运维债。

笔记加载中…