NOTE · Engineering Systems

Docker 镜像、容器与 Compose 操作备忘

整理 Docker 镜像、容器、存储、网络与 Compose 的日常操作。

Docker 基本命令

  • 查看 Docker 版本:

    docker --version
    
  • 查看正在运行的容器:

    sudo docker ps
    
  • 查看所有容器(包括停止的):

    sudo docker ps -a
    
  • 重启容器(包括停止的):

    sudo docker restart 容器ID或名称
    
  • 查看下载的镜像:

    sudo docker images
    
  • 查看卷:

    sudo docker volume ls
    
  • 停止容器:

    sudo docker stop 容器ID或名称
    
  • 删除容器:

    sudo docker rm 容器ID或名称
    

说明 docker rm 删除的是容器,不会删除命名卷。docker volume rm 接受的是卷名,不是容器 ID 或容器名。只有确认卷内数据不再需要时才应单独删除;docker rm -v <容器>只会一并删除该容器关联的匿名卷,命名卷仍会保留。

  • 删除镜像:

    sudo docker rmi 镜像ID或名称
    

    删除仅有<none>标签的镜像(又称dangling images):

    sudo docker image prune
    

    删除所有不再使用的镜像(即使有名称或标签,也会被删除):

    sudo docker image prune -a
    

    -a 会删除所有未被任何容器引用的镜像,不只是 <none> 镜像。先用 docker system dfdocker imagesdocker ps -a 确认范围;以后还要使用的镜像可能需要重新下载。

  • 删除卷

    sudo docker volume rm 卷的名称
    

    删除所有不再使用(悬空)的匿名卷

    sudo docker volume prune
    

    删除所有不再使用的卷:

    sudo docker volume prune -a
    

    数据删除警告:卷可能保存数据库、配置或上传文件。volume prune 默认只清理未被容器引用的匿名卷;加 -a 后也会清理未被引用的命名卷。先执行 docker volume lsdocker volume inspect <卷名>,并确认备份可恢复。

  • 彻底删除:

    sudo docker system prune -a --volumes
    

    这条命令将会移除

    • 所有停止的容器
    • 没有被任何容器使用的网络
    • 没有被任何容器使用的匿名卷(--volumes
    • 没有被任何容器关联的镜像(-a
    • 未使用的构建缓存

高风险命令docker system prune -a --volumes 会扩大镜像删除范围,并额外删除未使用的匿名卷,不适合作为日常“顺手清理”。它不会替代 docker volume prune -a 去删除命名卷。先用 docker system df -v 盘点,并分别检查容器、镜像和卷;重要数据先做可恢复性验证。

创建和运行 Docker 容器

运行第一个容器

  1. 拉取镜像:(可选)

    可以从 Docker Hub 拉取官方的 Nginx 镜像:

    sudo docker pull nginx
    
  2. 运行容器:

    sudo docker run -d -p 80:80 --name mynginx nginx
    
    • -d:以守护进程(后台)模式运行容器。
    • -p 80:80:将主机的 80 端口映射到容器的 80 端口。
    • --name mynginx:为容器指定名称为 mynginx
    • nginx:使用的镜像名称。
  3. 访问服务:

    在浏览器中访问 http://localhost,应该能看到 Nginx 的默认欢迎页面。

  4. 停止和删除容器:

    sudo docker stop mynginx
    sudo docker rm mynginx
    

运行带交互模式的容器

运行一个 Ubuntu 容器,并进入交互式终端:

docker run -it ubuntu /bin/bash
  • -it:组合了 -i(交互式)和 -t(分配一个伪终端)。
  • ubuntu:镜像名称。
  • /bin/bash:要运行的命令,这里是 Bash 终端。

在容器内,可以执行各种命令。输入 exit 或按 Ctrl+D 可以退出容器。

Docker 运行 Home Assistant 容器示例

现有以下示例代码

sudo docker run -d \
  --name homeassistant \
  --restart=unless-stopped \
  -e TZ=Asia/Shanghai \
  -v /PATH_TO_YOUR_CONFIG:/config \
  -v /etc/localtime:/etc/localtime:ro \
  --network=host \
  ghcr.io/home-assistant/home-assistant:stable
  • docker run -d
    这是运行容器的命令,-d 参数表示容器将以 后台模式 运行

  • --name homeassistant
    为容器指定名称,方便以后引用。在这里,容器的名称是 homeassistant,可通过这个名称来管理容器(如停止、删除容器)

  • --restart=unless-stopped
    这个选项定义了容器的重启策略。在这里,unless-stopped 表示当容器退出时,Docker 会自动重启容器,除非用户手动停止容器。这有助于确保容器在意外崩溃后能自动恢复

  • -e TZ=Asia/Shanghai
    这个选项为容器内的环境设置一个名为TZ的环境变量,其值为Asia/Shanghai,即设置容器的时区为 上海时区,这样容器内的时间和宿主机保持一致

  • -v /PATH_TO_YOUR_CONFIG:/config
    这是一个 绑定挂载 (bind mount) 命令。

    • /PATH_TO_YOUR_CONFIG:替换为宿主机上用于保存 Home Assistant 配置和数据的绝对路径。
    • /config:这是容器内部应用程序的配置目录,Home Assistant 会在这里存储所有的配置和数据库文件。
      通过这个挂载,宿主机上的 /docker/appdata/homeassistant 目录将与容器内的 /config 目录同步。即使容器被删除或重启,所有配置和数据仍然会保留在宿主机上
  • --network=host
    使用宿主机的网络模式。容器将直接使用宿主机的 IP 地址和端口,这样容器内的服务就能直接通过宿主机的网络进行访问。通常,使用 host 网络模式可以提高网络性能,但会减少容器的隔离性

  • ghcr.io/home-assistant/home-assistant:stable
    Home Assistant 官方文档使用的稳定版容器镜像。需要访问 USB 等设备时,应按设备精确添加 --device 等参数;不要无条件授予 --privileged

使用 Dockerfile 构建镜像

Dockerfile 是用于构建 Docker 镜像的脚本,包含了构建镜像的所有指令。

创建一个简单的应用

  1. 创建工作目录:

    mkdir myapp
    cd myapp
    
  2. 创建应用文件:

    创建一个简单的 Python 应用,文件名为 app.py

    print("Hello, Docker!")
    
  3. 编写 Dockerfile:

    myapp 目录下创建一个名为 Dockerfile 的文件,内容如下:

    # 使用官方 Python 3 镜像作为基础镜像
    FROM python:3
    
    # 将当前目录的内容复制到容器的 /app 目录
    COPY . /app
    
    # 设置工作目录为 /app
    WORKDIR /app
    
    # 指定容器启动时运行的命令
    CMD ["python", "app.py"]
    

构建镜像

myapp 目录下运行:

docker build -t mypythonapp .
  • -t mypythonapp:为镜像指定名称为 mypythonapp
  • .:Dockerfile 所在的目录。

运行镜像

docker run mypythonapp

应该会在终端看到输出:

Hello, Docker!

Docker 更新镜像

手工重建容器前,必须保留原容器的挂载、端口、环境变量、网络、设备和重启策略。仅重新执行一个简化的 docker run 会让这些配置丢失。优先把配置写入 Compose;如果暂时只能手工管理,至少先检查并留存当前配置:

sudo docker inspect 容器ID或名称
sudo docker pull 镜像名:<tag>

# 确认数据已备份、且能够用完整的原始参数重建后再执行:
sudo docker stop 容器ID或名称
sudo docker rm 容器ID或名称

# 必须复用原有的 -v/--mount、-p、-e、--network、--device、--restart 等参数
sudo docker run -d --name 新容器名 [原有全部运行参数] 镜像名:<tag>

升级后先检查:

sudo docker ps --filter name=新容器名
sudo docker logs --tail 100 新容器名

确认新容器工作正常且回滚镜像不再需要后,再考虑删除旧镜像。docker inspect 可能含环境变量中的敏感值,不要把输出直接发布到公网。

Docker Compose 更新镜像

当前 Compose CLI 使用 docker compose(中间为空格)。Compose 文件顶层的 version 字段已经过时,不必填写。以下 compose.yaml 使用明确镜像标签:

services:
  web:
    image: nginx:PINNED_TAG
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html

PINNED_TAG 替换为已经核对并计划使用的明确版本标签。修改并保存目标标签后,先备份持久化数据,再让 Compose 拉取并重建服务:

sudo docker compose config
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail 100

docker compose up -d 会按 Compose 文件保留声明过的挂载、端口、环境和网络配置。不要依赖隐式的 latest:固定并主动更新标签更便于复现和回滚。docker compose config 可能展开环境变量或其他敏感配置,不要把输出直接发布。删除卷的 docker compose down -v 会清除 Compose 声明的命名卷与匿名卷,除非明确要删数据,否则不要加 -v

Docker 容器管理

  • 查看容器日志

    sudo docker logs 容器ID或名称
    
  • 进入正在运行的容器

    sudo docker exec -it 容器ID或名称 /bin/bash
    
  • 重启容器

    sudo docker restart 容器ID或名称
    
  • 查看容器资源使用情况

    sudo docker stats
    

Docker 的重启策略

  • no

    默认值。容器退出后不会自动重启

  • on-failure

    仅当容器以非零状态码退出(意味着发生错误)时重启;若容器手动docker stop或容器以正常状态码退出,则不会重启

  • always

    • 无论什么原因导致容器退出,都始终尝试重启。如果 Docker 或宿主机重启后,容器也会随 Docker 一起启动
    • 只有在停止容器(如docker stop)且 Docker 没有重启的情况下,容器才会保持停止状态
  • unless-stopped

    • always很相似,但当容器是被手动停止之后,如果宿主机/ Docker 再重启,就不会自动把这个容器再拉起来
    • 换句话说,如果容器是“运行时崩溃”导致退出,或宿主机/docker 重启会自动重启该容器;但如果手动docker stop过它,下次 Docker daemon 重启时它就不会被自动拉起

容器、镜像与卷的关系

  • 镜像是只读模板;
  • 容器是在镜像基础上增加可写层和运行配置;
  • 绑定挂载直接引用宿主机路径;
  • 命名卷由 Docker 管理并以卷名引用;
  • 删除容器不会自动等于删除绑定目录或命名卷。

先查看容器实际挂载:

sudo docker inspect --format '{{json .Mounts}}' 容器ID或名称
sudo docker volume ls
sudo docker volume inspect 卷名

docker inspect 的完整输出可能含环境变量、内部地址和凭据,公开排错时只提取需要字段。重要数据的“备份”应包含一致性策略和恢复测试:数据库运行期间直接 cp -r 数据目录未必得到可恢复快照。

端口、环境与设备映射

sudo docker run --rm \
  -p 127.0.0.1:8080:80 \
  -e APP_MODE=development \
  --mount type=bind,src=/HOST_DATA,dst=/app/data,readonly \
  IMAGE_NAME:PINNED_TAG
  • 127.0.0.1:8080:80 只在宿主机回环地址监听;省略 IP 通常会扩大监听范围;
  • 环境变量可能出现在 inspect、进程环境或部署配置中,不适合承载长期密钥;
  • readonly 只限制该挂载在容器中的写入,不会让整个容器只读;
  • /HOST_DATA、镜像和标签都是占位符。

硬件访问优先授予具体设备:

sudo docker run --device /dev/DEVICE_NODE:/dev/DEVICE_NODE IMAGE_NAME:PINNED_TAG

仍要结合设备权限、用户组和容器内用户检查。--privileged 会显著扩大权限,不作为“设备打不开”的通用修复。

Compose v1 历史命令对照

Docker Compose v1 使用独立的 docker-compose 可执行文件;当前 Compose v2 已集成到 Docker CLI:

历史写法当前写法
docker-compose configdocker compose config
docker-compose pulldocker compose pull
docker-compose up -ddocker compose up -d
docker-compose logsdocker compose logs

维护老主机时先执行 docker compose versiondocker-compose version 查看实际安装,不要在同一项目里混用两套命令。顶层 version: 字段属于历史 Compose 文件格式标记;现代 Compose Specification 不再需要它。

清理前盘点

sudo docker system df -v
sudo docker ps -a --size
sudo docker images --digests
sudo docker volume ls

先分别判断停止容器、缓存、未引用镜像和卷是否仍需回滚/恢复,再选择最小范围的删除命令。不要把 system prune -a --volumes 当作固定维护步骤。

其他

在多数基于 systemd 的 Linux 发行版上,可使用以下命令来重启 Docker 服务 (Docker Daemon):

sudo systemctl restart docker