跳转至

容器与编排

一、这页讲什么

把服务与依赖打成一个可重复运行的镜像,再用一份 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
$ curl -I http://127.0.0.1:8099
HTTP/1.1 200 OK
Server: nginx/1.27.0

看到这个就算成: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 只删悬空镜像,不动在用的。

四、做完怎么确认

  1. docker ps 与 docker compose ps 中服务状态都是 Up。
  2. 宿主机端口能访问到容器内服务。
  3. docker compose down 再 up 之后,挂载的数据还在。
  4. 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 网络模式尤其要先查。

八、官方资料

核实日期:2026-10-21。