容器与编排¶
一、这页讲什么¶
把服务与依赖打成一个可重复运行的镜像,再用一份 compose.yml 描述它怎么跑。换一台机器,拉起的东西是一样的。
读完能做到四件事:跑起一个容器并确认它在监听端口;用 docker logs 与 docker exec 查容器内部;用 Compose 管理一组服务;判断数据到底存在宿主机还是容器可写层。
二、开始之前¶
| 项 | 内容 |
|---|---|
| 版本与日期 | Docker Engine 24 以上,Compose v2(docker compose);2026-10-21 核实 |
| 预计耗时 | 20 分钟 |
| 前置 | 有 sudo 或已加入 docker 组;一个空闲端口(示例用 8099) |
| 风险级别 | 中:会占端口、会写宿主机目录,误删容器可导致数据丢失 |
三、动手做¶
1. 确认 Docker 与 Compose 都在¶
$ docker version --format '{{.Server.Version}}'
26.1.4
$ docker compose version
Docker Compose version v2.27.1
看到这个就算成:两条命令都打印版本号。docker compose 中间是空格,docker-compose 是另一套已经停更的工具。
2. 起一个容器¶
$ docker run -d --name demo -p 8099:80 nginx:alpine
1f2c3b4a5d6e...
$ docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'
NAMES IMAGE STATUS PORTS
demo nginx:alpine Up 5 seconds 0.0.0.0:8099->80/tcp
看到这个就算成:docker ps 里状态是 Up,curl 返回 200。-p 8099:80 的意思是把宿主机的 8099 转到容器里的 80。
3. 看日志、进容器¶
$ docker logs --tail 5 demo
$ docker exec -it demo sh
/ # ls /usr/share/nginx/html
50x.html index.html
看到这个就算成:能进到容器内部执行命令,docker logs 有近期访问记录。
4. 用 Compose 管一组服务¶
新建一个目录,在里面写 compose.yml:
services:
web:
image: nginx:alpine
ports:
- "8099:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
cache:
image: redis:7-alpine
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
redis-data:
$ docker compose up -d
[+] Running 3/3
✔ Network demo_default Created
✔ Container demo-web-1 Started
✔ Container demo-cache-1 Started
$ docker compose ps
NAME IMAGE STATUS
demo-web-1 nginx:alpine Up 10 seconds
demo-cache-1 redis:7-alpine Up 10 seconds
看到这个就算成:docker compose ps 两个服务都是 Up。./html 是宿主机目录,redis-data 是命名卷,两者都不会随容器删除而消失。
5. 验证数据不在容器里¶
$ echo hello > html/index.html
$ curl -s http://127.0.0.1:8099/
hello
$ docker compose down
$ docker compose up -d
$ curl -s http://127.0.0.1:8099/
hello
看到这个就算成:down 删掉容器再 up 起来,hello 还在。数据留在宿主机目录里,这说明挂载生效。
6. 清理¶
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 8 3 4.2GB 2.1GB (50%)
$ docker image prune -f
看到这个就算成:docker system df 能看出谁占着磁盘;prune 只删悬空镜像,不动在用的。
四、做完怎么确认¶
docker ps与docker compose ps中服务状态都是Up。- 宿主机端口能访问到容器内服务。
docker compose down再up之后,挂载的数据还在。docker system df没有一路涨到把盘写满。
五、卡住了怎么排查¶
| 症状 | 多半是 | 先验证 | 处理 |
|---|---|---|---|
| 容器起来就退出 | 容器里的主进程不是常驻进程 | $ docker logs <容器> |
换镜像或改 command;容器活着的前提是主进程不退 |
| 端口访问不到 | 端口没映射,或映射到了别的地址 | $ docker port <容器>、$ sudo ss -tlnp \| grep 8099 |
核对 -p 宿主:容器;换宿主机端口 |
Address already in use |
宿主机端口被占;host 网络模式更容易撞 | $ sudo ss -tlnp \| grep :8099 |
换端口或停占用者;端口按内部台账登记 |
| 挂载目录权限不对 | 容器内 uid 与宿主机文件属主不一致 | $ docker exec <容器> ls -ln /挂载点 |
改宿主机属主,或在 compose.yml 里指定 user: |
| 删容器后数据没了 | 数据只写在容器可写层,没挂载 | $ docker inspect <容器> --format '{{json .Mounts}}' |
加 volumes;数据一律挂到宿主机目录或命名卷 |
| 镜像拉取很慢或失败 | 网络到 registry 不稳定 | $ docker pull nginx:alpine 单独试 |
配镜像代理;必要时先在能联网的机器上 docker save 再传 |
| 磁盘被 Docker 占满 | 悬空镜像、停止的容器、日志堆积 | $ docker system df -v |
docker image prune、限制日志大小(logging 选项) |
六、为什么这样能成¶
镜像分层,容器是可写层。 镜像由若干只读层拼起来,容器启动时在上面加一层可写的。删掉容器,可写层一起没,这就是「数据不挂载就等于没有」的原因。
网络模式决定端口长什么样。 默认的桥接网络里,容器有独立 IP,要靠 -p 映射到宿主机;host 模式直接共用宿主机网络栈,端口冲突的表现与裸机一致。
Compose 是声明式的。 写清要什么状态,up 负责让现实对齐配置。这也是一份 compose.yml 能当交接材料用的原因:它写的是「该怎么跑」,不是「我做过什么」。
重启策略接管了退出。 restart: unless-stopped 让服务被 OOM 或崩溃带走后自己回来,效果相当于 systemd 的 Restart=,只是作用在容器层面。
七、边界与替代¶
- 不做多机编排:Kubernetes、Swarm 一类不在本页范围。单机够用时不要引入集群。
- 硬件直通另看:需要显卡的容器要
--gpus,见 GPU 与 CUDA 环境。 - 不要用容器逃避托管:容器不是 systemd 的替代品,宿主机上仍然要有守护与巡检,见 Linux 服务器运维。
- 数据不能只存在容器里:可写层随容器销毁,卷和 bind mount 才是持久化位置。
- 端口是共享资源:容器对外占用的端口同样要登记,host 网络模式尤其要先查。
八、官方资料¶
- Docker Engine 安装(Ubuntu):https://docs.docker.com/engine/install/ubuntu/
- Compose 文件参考:https://docs.docker.com/compose/compose-file/
docker run参考:https://docs.docker.com/reference/cli/docker/container/run/- bind mount 说明:https://docs.docker.com/engine/storage/bind-mounts/
核实日期:2026-10-21。