1. 概述

上一篇已经把 Docker 的核心概念讲清楚了:镜像是交付物,容器是运行实例,仓库负责分发,Docker Engine 通过 containerd、runc、Namespace、Cgroup 和 OverlayFS 把应用隔离起来运行。本文进入真正的操作层:在一台 Linux 主机上安装 Docker Engine,完成基本验证,并掌握日常最常用的容器、镜像、日志、网络、数据卷和清理命令。

这篇文章的目标不是把 docker --help 逐项翻译一遍,而是建立一套可执行的最小工作流。读完之后,应该能够完成下面几件事:

  1. 在 Ubuntu 服务器上使用 Docker 官方 apt 仓库安装 Docker Engine。
  2. 判断 Docker Desktop、Docker Engine 和发行版自带 docker.io 包之间的区别。
  3. docker rundocker psdocker logsdocker exec 管理容器生命周期。
  4. docker pulldocker imagesdocker inspectdocker rmi 管理镜像。
  5. 用端口映射、环境变量、命名卷和 bind mount 启动一个可验证的 Nginx 容器。
  6. 在故障发生时快速判断是 daemon、权限、镜像、网络还是端口冲突问题。

本文以 Ubuntu 24.04 LTS / 22.04 LTS 服务器为主要环境,因为这是企业内网、云主机、测试环境最常见的 Docker Engine 落地点。macOS 和 Windows 更适合安装 Docker Desktop,本质上是在本机上运行一个轻量 Linux VM,再把 Docker CLI、Desktop UI 和虚拟机内的 Docker Engine 串起来。

2. 理论基础

2.1 Docker Engine 与 Docker Desktop 的区别

很多初学者会把 Docker 当成一个单一软件,但实际落地时至少要区分三类安装形态。

安装形态 典型平台 适用场景 需要注意
Docker Engine Ubuntu、Debian、RHEL、CentOS、Rocky Linux 等 Linux 服务器 生产服务器、测试服务器、CI Runner、云主机 直接管理宿主机容器运行时,需要关注 systemd、iptables、内核参数和磁盘空间
Docker Desktop macOS、Windows、开发者 Linux 桌面 本地开发、调试、学习、单机演示 macOS/Windows 容器实际运行在 Linux VM 中,网络和文件挂载行为与纯 Linux 有差异
发行版自带包 Ubuntu 的 docker.io、旧版第三方源 快速试验或受控内网仓库 版本可能落后,包名和插件组合可能不同,不适合作为标准生产安装基线

如果是服务器,优先采用 Docker 官方文档中的仓库安装方式;如果是个人电脑,优先采用 Docker Desktop。不要在同一台机器上混装多个来源的 Docker 包,否则后续排障会变得非常混乱。

2.2 Docker 安装后到底装了什么

使用官方仓库安装 Docker Engine 时,常见核心包包括:

  • docker-ce:Docker Community Edition 的 daemon 与核心组件。
  • docker-ce-clidocker 命令行客户端。
  • containerd.io:容器运行时服务,负责镜像、快照和容器生命周期的底层管理。
  • docker-buildx-plugin:Buildx 构建插件,支持 BuildKit、多平台构建等能力。
  • docker-compose-plugin:Compose v2 插件,命令形式是 docker compose

它们之间的调用关系可以简化理解为:

平时我们输入的 docker run nginx 看起来是一条命令,背后实际发生了多步动作:CLI 把请求发给 Docker daemon;daemon 检查本地有没有镜像;没有就从 registry 拉取;然后通过 containerd 创建容器快照;最后由 runc 创建隔离进程。

2.3 安装前需要想清楚的几件事

Docker 很容易装上,但生产环境真正容易出问题的是前置规划。

规划项 建议
操作系统 优先使用仍在维护期内的 LTS 发行版,例如 Ubuntu 24.04 LTS 或 22.04 LTS
磁盘路径 默认数据目录是 /var/lib/docker,镜像和容器日志增长很快,建议单独规划磁盘或 LVM
网络 Docker 会创建 docker0 bridge,并可能修改 iptables/nftables 规则;生产主机需提前确认防火墙策略
权限 默认只有 root 或 docker 组用户能访问 Docker socket;把用户加入 docker 组等同于授予高权限
镜像源 内网环境通常需要配置 registry mirror 或私有仓库
日志 默认 json-file 日志如果不限制大小,长期运行容器可能撑满磁盘
版本管理 生产环境应记录 Docker Engine、containerd、Compose 插件版本,升级前先在测试环境验证

特别要注意 Docker socket 权限。/var/run/docker.sock 能控制宿主机上的容器,具备很高权限。不要为了方便把它随意挂进业务容器,也不要把普通用户无脑加入 docker 组。开发机可以这样做,生产服务器则应该经过权限评估。

3. 环境规划

本文采用下面的验证环境作为命令基线:

项目 说明
操作系统 Ubuntu 24.04 LTS 或 Ubuntu 22.04 LTS
CPU 架构 amd64 或 arm64
安装来源 Docker 官方 apt repository
主要组件 Docker Engine、Docker CLI、containerd、Buildx、Compose v2
网络要求 能访问 download.docker.com、Docker Hub 或企业镜像代理
用户权限 具备 sudo 权限

如果你的服务器在公司内网,无法直接访问外部网络,需要提前准备三类代理:

  1. apt 仓库代理或离线 deb 包仓库。
  2. Docker Hub 镜像代理或私有镜像仓库。
  3. HTTPS 代理环境变量,例如 HTTP_PROXYHTTPS_PROXYNO_PROXY

本文命令默认直接访问 Docker 官方仓库。内网离线安装可以沿用相同验证步骤,但包来源需要替换为企业内部仓库。

4. 实战操作

Docker 安装与基础命令路径

这个实操路径把本文步骤串成一条可复验流程:先清理冲突包,再完成仓库、安装、验证、权限、端口发布和数据持久化。后续命令都围绕这条路径展开。

4.1 卸载旧版本或冲突包

如果机器上曾经安装过 Ubuntu 自带的 docker.io,或者历史脚本装过旧包,先清理冲突包。官方文档建议在安装 Docker Engine 前移除旧版或非官方包名。

bash
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
  sudo apt-get remove -y "$pkg"
done

这一步不会删除 /var/lib/docker/ 中已有镜像、容器和卷数据。如果是生产主机,执行前仍然建议先确认是否存在历史容器:

bash
sudo ls -lah /var/lib/docker 2>/dev/null || true
sudo systemctl status docker --no-pager || true

如果这台机器已经承载业务容器,不要直接清理或重装,应该先导出容器清单、镜像清单、Compose 文件和数据卷位置。

4.2 配置 Docker 官方 apt 仓库

安装依赖工具:

bash
sudo apt-get update
sudo apt-get install -y ca-certificates curl

创建 keyrings 目录并导入 Docker 官方 GPG key:

bash
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

添加 apt source:

bash
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

更新索引:

bash
sudo apt-get update

如果这里报 NO_PUBKEYCould not resolve hostConnection timed out,先不要继续安装,应该回到网络、DNS、代理或 GPG key 步骤排查。

4.3 安装 Docker Engine 与插件

安装当前仓库中的稳定版本:

bash
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

安装完成后检查服务状态:

bash
sudo systemctl status docker --no-pager
sudo systemctl is-enabled docker

如果服务没有自动启动,可以手动启用并启动:

bash
sudo systemctl enable --now docker

查看版本:

bash
docker version
docker compose version
docker buildx version

这里不要在文章或运维手册里写死某个版本输出,因为 Docker Engine 和插件版本会随时间更新。更稳妥的做法是记录实际执行结果,例如在变更单或 CMDB 中记录:

bash
docker version --format 'Client={{.Client.Version}} Server={{.Server.Version}}'
docker compose version

4.4 运行 hello-world 验证

执行最小验证:

bash
sudo docker run hello-world

这条命令会完成四件事:

  1. Docker CLI 连接本机 Docker daemon。
  2. daemon 发现本地没有 hello-world 镜像。
  3. daemon 从默认 registry 拉取镜像。
  4. daemon 创建并运行一个短生命周期容器,容器输出说明后退出。

查看退出后的容器:

bash
sudo docker ps -a

清理测试容器:

bash
sudo docker container prune

hello-world 适合验证安装链路,但不适合验证端口映射、日志、exec、数据卷等日常能力。下一步我们用 Nginx 做一个更接近真实业务的验证。

4.5 配置非 root 用户使用 Docker

开发机或测试机可以把当前用户加入 docker 组,避免每条命令都写 sudo

bash
sudo usermod -aG docker "$USER"
newgrp docker

重新登录后验证:

bash
docker ps

生产环境要谨慎执行这一步。能访问 Docker socket 的用户基本可以通过挂载宿主机目录、启动特权容器等方式获得宿主机高权限。服务器上更推荐通过 sudoers、堡垒机、CI/CD 服务账号和审计流程控制 Docker 操作。

4.6 启动一个 Nginx 容器

拉取镜像:

bash
docker pull nginx:alpine

后台启动容器,把宿主机 8080 端口映射到容器 80 端口:

bash
docker run -d \
  --name web-demo \
  -p 8080:80 \
  --restart unless-stopped \
  nginx:alpine

检查容器状态:

bash
docker ps

访问验证:

bash
curl -I http://127.0.0.1:8080

查看日志:

bash
docker logs web-demo
docker logs -f --tail=50 web-demo

进入容器执行命令:

bash
docker exec -it web-demo sh

在容器内可以检查 Nginx 进程和配置目录:

bash
ps aux
nginx -T | head
exit

停止、启动、重启容器:

bash
docker stop web-demo
docker start web-demo
docker restart web-demo

删除容器:

bash
docker rm -f web-demo

这里的 -f 会强制停止并删除容器。生产环境不要习惯性使用 rm -f,除非已经确认容器没有承载业务,或者数据已经通过卷、数据库、对象存储等方式持久化。

4.7 使用 bind mount 发布自定义页面

创建本地目录和测试页面:

bash
mkdir -p ~/docker-lab/nginx/html
cat > ~/docker-lab/nginx/html/index.html <<'EOF'
<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>Docker Nginx Demo</title>
</head>
<body>
  <h1>Hello Docker</h1>
  <p>这个页面来自宿主机 bind mount。</p>
</body>
</html>
EOF

启动容器并挂载目录:

bash
docker run -d \
  --name nginx-bind \
  -p 8081:80 \
  -v "$HOME/docker-lab/nginx/html:/usr/share/nginx/html:ro" \
  nginx:alpine

验证页面:

bash
curl http://127.0.0.1:8081

这里的 :ro 表示容器内只读挂载。对于配置文件、静态页面、证书等场景,如果容器不需要写入,优先使用只读挂载,减少误改和安全风险。

4.8 使用命名卷保存数据

bind mount 适合把宿主机某个明确路径挂进容器;命名卷更适合由 Docker 管理生命周期。以 Nginx 日志目录为例:

bash
docker volume create nginx-logs

docker run -d \
  --name nginx-volume \
  -p 8082:80 \
  -v nginx-logs:/var/log/nginx \
  nginx:alpine

查看卷信息:

bash
docker volume ls
docker volume inspect nginx-logs

删除容器后,卷仍然保留:

bash
docker rm -f nginx-volume
docker volume ls

删除卷:

bash
docker volume rm nginx-logs

注意:容器删除不等于数据删除,数据卷删除才会真正清掉 Docker 管理的卷数据。反过来,如果没有规划卷,容器可写层里的业务数据会随容器删除而丢失。

5. 配置详解

5.1 docker run 常用参数

docker run 是初学阶段最重要的命令。它不是单纯的“运行”,而是“基于镜像创建一个新容器并启动”。

参数 作用 示例
-d 后台运行容器 docker run -d nginx
--name 指定容器名称 --name web-demo
-p 端口映射 -p 8080:80
-e 注入环境变量 -e TZ=Asia/Shanghai
-v 挂载卷或目录 -v app-data:/data
--restart 设置重启策略 --restart unless-stopped
--network 指定网络 --network bridge
--memory 限制内存 --memory 512m
--cpus 限制 CPU --cpus 1.5
--user 指定容器内运行用户 --user 1000:1000
--rm 容器退出后自动删除 docker run --rm alpine uname -a

一个稍完整的例子:

bash
docker run -d \
  --name app-nginx \
  --restart unless-stopped \
  -p 8080:80 \
  -e TZ=Asia/Shanghai \
  -v "$PWD/html:/usr/share/nginx/html:ro" \
  --memory 256m \
  --cpus 0.5 \
  nginx:alpine

这个命令已经包含了生产演练中最常见的几个要素:固定名称、自动重启、端口映射、时区、只读挂载和资源限制。

5.2 容器状态管理命令

命令 作用
docker ps 查看正在运行的容器
docker ps -a 查看所有容器,包括已退出容器
docker start <container> 启动已存在容器
docker stop <container> 优雅停止容器
docker restart <container> 重启容器
docker rm <container> 删除已停止容器
docker rm -f <container> 强制停止并删除容器
docker inspect <container> 查看容器详细 JSON 信息
docker stats 查看容器资源使用
docker top <container> 查看容器内进程

排障时不要只看 docker ps。如果容器启动后立即退出,docker ps 看不到,需要用:

bash
docker ps -a
docker logs <container>
docker inspect <container>

5.3 镜像管理命令

命令 作用
docker pull <image> 拉取镜像
docker images 查看本地镜像
docker image ls 查看本地镜像,等同于新式子命令
docker rmi <image> 删除镜像
docker image prune 清理悬空镜像
docker history <image> 查看镜像层历史
docker inspect <image> 查看镜像元数据

查看镜像占用:

bash
docker system df

只清理悬空镜像:

bash
docker image prune

清理所有未被容器使用的镜像:

bash
docker image prune -a

prune -a 在开发机上很方便,在生产服务器上要谨慎。它不会删除正在被容器使用的镜像,但会删除暂时没有容器引用的镜像,可能导致下次回滚或启动时需要重新拉取。

5.4 日志查看与限制

查看日志:

bash
docker logs web-demo
docker logs --tail=100 web-demo
docker logs -f --since=30m web-demo

默认日志驱动常见为 json-file。长期运行容器建议配置日志轮转,否则 /var/lib/docker/containers/<container-id>/ 下的日志文件可能持续增长。

可以在 /etc/docker/daemon.json 中配置:

json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  }
}

重启 Docker daemon:

bash
sudo systemctl restart docker

注意:daemon 级别配置通常只影响新建容器。已经存在的容器是否生效,需要按实际版本和配置验证,稳妥做法是重建容器。

5.5 Docker daemon 基础配置

常见 daemon 配置文件路径:

bash
/etc/docker/daemon.json

一个适合测试环境的示例:

json
{
  "data-root": "/var/lib/docker",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"
  },
  "default-address-pools": [
    {
      "base": "172.30.0.0/16",
      "size": 24
    }
  ]
}

配置前先检查 JSON 格式:

bash
python3 -m json.tool /etc/docker/daemon.json

重启并验证:

bash
sudo systemctl restart docker
sudo systemctl status docker --no-pager
docker info

如果重启失败,优先查看:

bash
journalctl -u docker -n 100 --no-pager

6. 验证与测试

6.1 基础健康检查

安装后建议固定执行这组检查:

bash
docker version
docker info
docker compose version
docker buildx version
docker run --rm hello-world

判断成功的关键不是背某段输出,而是确认:

  1. docker version 同时能显示 Client 和 Server 信息。
  2. docker info 能正常返回 daemon、storage driver、cgroup driver、registry 等信息。
  3. hello-world 能拉取镜像并运行。
  4. docker compose version 能显示 Compose v2 插件版本。
  5. 普通用户是否能执行 Docker 命令符合你的权限规划。

6.2 端口映射验证

启动 Nginx:

bash
docker run -d --name port-test -p 8088:80 nginx:alpine

验证本机访问:

bash
curl -I http://127.0.0.1:8088

验证监听端口:

bash
ss -lntp | grep 8088 || true

验证容器端口配置:

bash
docker port port-test
docker inspect port-test --format '{{json .NetworkSettings.Ports}}'

如果本机能访问、远端不能访问,问题通常不在 Docker 容器,而在云安全组、服务器防火墙或上游网络策略。

6.3 数据持久化验证

创建命名卷并写入文件:

bash
docker volume create persist-test

docker run --rm \
  -v persist-test:/data \
  alpine sh -c 'date > /data/created-at.txt && cat /data/created-at.txt'

再次启动新容器读取:

bash
docker run --rm \
  -v persist-test:/data \
  alpine cat /data/created-at.txt

如果第二次能读到文件,说明数据保存在命名卷中,而不是临时容器可写层中。

6.4 资源限制验证

启动一个带资源限制的容器:

bash
docker run -d \
  --name limited-nginx \
  --memory 128m \
  --cpus 0.5 \
  nginx:alpine

查看限制和实时资源:

bash
docker inspect limited-nginx --format 'Memory={{.HostConfig.Memory}} NanoCPUs={{.HostConfig.NanoCpus}}'
docker stats limited-nginx

这里的数值单位不一定直观:内存通常是字节,CPU 可能以 NanoCPUs 表示。运维手册中应写清楚人类可读参数,例如 --memory 128m--cpus 0.5,不要只记录 inspect 里的底层值。

6.5 清理实验环境

清理本文创建的测试容器:

bash
docker rm -f web-demo nginx-bind nginx-volume port-test limited-nginx 2>/dev/null || true

清理测试卷:

bash
docker volume rm nginx-logs persist-test 2>/dev/null || true

按需清理未使用资源:

bash
docker system df
docker container prune
docker image prune
docker volume prune

不要在生产服务器上随手执行 docker system prune -a --volumes。这条命令会清理未使用镜像、容器、网络和卷,影响面很大,必须先确认数据备份和业务状态。

7. 故障排查

Docker 基础排障矩阵

这张矩阵把安装后最常见的问题按证据入口归类:服务状态、权限、镜像拉取、容器日志、端口映射、磁盘空间和 daemon 配置。排障时先收集证据,再决定是否改配置或清理资源。

7.1 Cannot connect to the Docker daemon

典型现象:

bash
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

排查顺序:

bash
sudo systemctl status docker --no-pager
sudo journalctl -u docker -n 100 --no-pager
ls -l /var/run/docker.sock

常见原因:

  • Docker 服务没有启动。
  • /etc/docker/daemon.json JSON 格式错误。
  • Docker 数据目录权限或磁盘异常。
  • 当前用户没有访问 Docker socket 的权限。
  • 系统升级后 containerd 或依赖服务异常。

7.2 普通用户执行 Docker 提示 permission denied

检查当前用户组:

bash
id
ls -l /var/run/docker.sock

如果是开发机,可以加入 docker 组:

bash
sudo usermod -aG docker "$USER"
newgrp docker

如果是生产服务器,不建议简单扩大用户权限。应明确谁可以操作 Docker、操作是否需要 sudo、命令是否有审计记录。

7.3 镜像拉取失败

常见现象包括 DNS 失败、TLS 失败、连接超时、认证失败、rate limit。

排查命令:

bash
curl -I https://registry-1.docker.io/v2/
docker pull nginx:alpine
docker login
docker info | grep -i proxy -A3

处理方向:

  • 检查 DNS、默认路由、防火墙和代理。
  • 内网环境配置 registry mirror 或私有仓库。
  • 私有镜像确认 docker login 的 registry 地址是否正确。
  • CI 环境避免匿名频繁拉取公共镜像,尽量使用认证账号或内部缓存。

7.4 容器启动后立即退出

先查看退出状态:

bash
docker ps -a
docker logs <container>
docker inspect <container> --format 'ExitCode={{.State.ExitCode}} Error={{.State.Error}}'

常见原因:

  • 容器主进程执行完成,所以正常退出,例如 alpine echo hello
  • 启动命令写错,entrypoint 或 command 不存在。
  • 配置文件挂载路径错误。
  • 端口、权限、依赖服务、环境变量缺失。
  • 应用自身启动失败。

容器不是虚拟机,容器内的主进程结束,容器就会退出。想要容器长期运行,镜像必须以前台进程作为主进程,例如 Nginx 的 daemon off 模式。

7.5 端口映射失败或访问不通

如果启动时报端口占用:

bash
docker run -d --name web -p 8080:80 nginx:alpine

排查:

bash
ss -lntp | grep 8080 || true
docker ps --format 'table {{.Names}}\t{{.Ports}}'

如果容器已启动但访问不通:

bash
docker logs web
docker exec web nginx -T
docker port web
curl -I http://127.0.0.1:8080

再从外部访问时,需要检查云安全组、主机防火墙、NAT、负载均衡和上游 ACL。

7.6 磁盘空间被 Docker 占满

查看 Docker 占用:

bash
docker system df
sudo du -sh /var/lib/docker

查看大日志文件:

bash
sudo find /var/lib/docker/containers -name '*-json.log' -size +100M -ls

处理建议:

  • 给 Docker 配置日志轮转。
  • 清理无用容器、悬空镜像、构建缓存。
  • 对生产环境先确认容器、镜像和卷的用途,再清理。
  • 长期运行环境把 /var/lib/docker 放在可监控、可扩容的磁盘上。

构建缓存清理:

bash
docker builder prune

7.7 daemon 配置后 Docker 无法启动

如果修改 /etc/docker/daemon.json 后 Docker 起不来,先检查 JSON:

bash
sudo python3 -m json.tool /etc/docker/daemon.json
sudo journalctl -u docker -n 100 --no-pager

常见问题:

  • JSON 多了尾随逗号。
  • data-root 路径不存在或权限不对。
  • default-address-pools 和现有网络冲突。
  • 配置项拼写错误。
  • 代理配置写在了错误位置。

修改 daemon 配置前建议备份:

bash
sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.$(date +%F-%H%M%S).bak

8. 运维建议

8.1 建立最小标准安装手册

企业内部不要让每个工程师从搜索引擎复制不同安装命令。建议维护一份内部标准,至少包括:

  • 支持的操作系统版本。
  • Docker Engine 和插件安装来源。
  • 是否允许普通用户加入 docker 组。
  • /var/lib/docker 是否独立挂载。
  • daemon 日志轮转配置。
  • 镜像仓库和代理地址。
  • 基础验证命令。
  • 升级和回滚方式。

这份标准可以很短,但必须可复制、可审计、可回滚。

8.2 生产容器不要只靠 docker run

docker run 适合学习、临时验证和排障。生产环境至少应该把参数固化到 Compose 文件、systemd unit、Ansible playbook 或平台化发布系统中。否则一旦主机重装或容器被删除,很难准确复原当时的启动参数。

短期可以用下面命令反查容器配置:

bash
docker inspect <container>

inspect 是结果,不是期望状态。真正可维护的方式是把配置写进版本库。

8.3 给容器日志和数据划边界

不要把业务数据写在容器可写层。推荐原则:

  • 数据库、上传文件、状态数据放到命名卷、bind mount 或外部存储。
  • 容器日志设置大小限制,并接入集中日志系统。
  • 配置文件用只读挂载。
  • 临时缓存可以留在容器可写层,但要能承受容器重建后丢失。

8.4 升级前先做兼容性验证

Docker Engine、containerd、Buildx、Compose 插件都可能影响构建、网络、日志和运行行为。升级前至少验证:

bash
docker version
docker compose version
docker run --rm hello-world
docker run --rm -p 18080:80 nginx:alpine
docker compose config
docker compose up -d

如果生产环境依赖特定 storage driver、特殊网络、GPU、私有 registry 或老版本 Compose 文件,更要先在测试环境复现。

8.5 把 Docker 主机纳入基础设施监控

建议监控以下指标:

  • Docker daemon 是否运行。
  • /var/lib/docker 所在磁盘使用率。
  • 容器数量、重启次数、退出状态。
  • 容器 CPU、内存、网络和块设备 I/O。
  • 大日志文件增长速度。
  • 镜像拉取失败、容器启动失败等事件。

Docker 是应用交付工具,也是宿主机上的高权限运行时。只监控应用进程、不监控 Docker 主机,会在磁盘、网络、日志和权限问题上留下盲区。

9. 安装前的决策清单

安装 Docker 之前,先确认这台机器的角色。开发电脑、测试服务器、CI 构建机、单机生产服务器和 Kubernetes 节点,对 Docker 的安装方式、权限边界和升级节奏并不一样。开发电脑可以优先使用 Docker Desktop,因为它集成了图形界面、Compose、Kubernetes 可选能力和本地体验;Linux 服务器通常优先安装 Docker Engine,因为运行路径更清晰,也更适合纳入系统服务、监控和配置管理。

第二个决策是数据目录放在哪里。Docker 默认数据目录通常位于 /var/lib/docker,镜像、容器层、卷和构建缓存都会消耗这里的磁盘。如果根分区很小,安装后很快会遇到磁盘不足。生产服务器应提前规划独立磁盘或分区,并把 Docker 数据目录容量纳入监控。

第三个决策是普通用户是否加入 docker 组。加入后可以免 sudo 执行 Docker 命令,但这基本等价于授予该用户控制宿主机 Docker 的高权限。开发机可以按团队规则配置;生产服务器上应谨慎,只给受控运维账号或自动化账号,并保留审计。

第四个决策是镜像来源。默认访问 Docker Hub 适合学习,但企业网络可能需要代理、镜像加速、私有 registry 或云厂商镜像仓库。安装时就要确认能否稳定拉取基础镜像,不能等上线时才发现网络访问受限。

第五个决策是版本和升级策略。Docker Engine、containerd、Buildx、Compose 插件都会影响构建和运行行为。生产环境不建议无计划自动升级,应先在测试环境验证核心工作负载、网络、卷、日志和镜像构建,再安排维护窗口升级。

10. 基础命令的学习顺序

学习 Docker 基础命令时,不建议一次性背完整 CLI。更有效的顺序是围绕容器生命周期展开。

第一组命令回答“镜像从哪里来”:

bash
docker pull nginx:alpine
docker images
docker image inspect nginx:alpine

第二组命令回答“容器如何启动和停止”:

bash
docker run --name web -d -p 8080:80 nginx:alpine
docker ps
docker stop web
docker start web
docker rm -f web

第三组命令回答“容器内部发生了什么”:

bash
docker logs web
docker exec -it web sh
docker inspect web
docker stats web

第四组命令回答“网络和数据如何接入”:

bash
docker network ls
docker volume ls
docker run --name web -d -p 8080:80 -v webdata:/usr/share/nginx/html nginx:alpine

第五组命令回答“如何清理实验环境”:

bash
docker ps -a
docker rm -f web
docker image prune
docker volume prune

清理命令要格外谨慎。学习环境可以 prune,生产环境必须先确认镜像、容器和卷的业务归属。尤其是 docker volume prune,它会删除未被容器引用的卷,但“未被引用”不等于“不再需要”。如果业务容器临时停止,数据卷可能被误删。

11. 安装后的基线配置

安装完成后,建议建立一份 Docker 主机基线。基线不需要复杂,但要覆盖运行时、日志、权限、网络和数据。

日志方面,至少要限制容器日志大小。否则一个异常循环输出日志的容器,就可能把 /var/lib/docker 打满。可以通过 daemon 配置设置默认日志驱动和大小,例如 json-filemax-sizemax-file。如果已经有集中日志平台,可以把容器日志接入 Fluent Bit、Vector、Promtail 或其他采集器。

权限方面,要明确谁能执行 Docker 命令。Docker socket 是高权限入口,任何能访问 /var/run/docker.sock 的用户或容器,都可能创建特权容器、挂载宿主机路径或读取敏感数据。不要把 Docker socket 随意挂进业务容器,也不要把所有开发者都加入生产服务器的 docker 组。

网络方面,要记录默认 bridge、自定义网络、端口发布和宿主机防火墙之间的关系。Docker 会操作 iptables/nftables 规则,不同发行版和安全策略下行为可能不同。安装后应测试端口发布、容器出网、容器间通信和防火墙规则是否符合预期。

数据方面,要确认 /var/lib/docker 所在磁盘、备份策略和清理策略。镜像层、容器可写层、命名卷、构建缓存都会增长。没有容量监控的 Docker 主机,迟早会因为磁盘打满影响所有容器。

12. 一台 Docker 主机的交付验收

如果把 Docker 安装到团队共享服务器或生产服务器,建议按下面清单验收:

类别 验收项 证据
版本 Docker Engine、containerd、Buildx、Compose docker versiondocker compose version
服务 daemon 开机启动、状态正常 systemctl status docker
镜像 能拉取官方测试镜像或私有仓库镜像 docker pull hello-world
容器 能启动、停止、删除测试容器 docker run --rm hello-world
端口 端口发布可从预期来源访问 Nginx 测试容器和浏览器/curl
数据 命名卷重建后数据保留 删除容器后重建验证
日志 日志大小限制或采集生效 daemon 配置和日志平台
权限 docker 组成员受控 getent group docker
磁盘 Docker 数据目录容量可监控 docker system df、系统监控
备份 配置、Compose 文件、数据卷策略明确 文档和备份记录

这张验收表可以避免“安装成功但不可运维”。尤其是共享服务器,谁可以启动容器、容器能暴露哪些端口、数据放在哪里、日志是否可控,都需要在安装后立即明确。

13. 升级与回滚

Docker 升级前要先确认影响面。Docker Engine 升级可能影响 daemon 行为、网络规则、日志驱动、storage driver、BuildKit、Compose 插件和镜像构建。普通学习环境可以直接按官方文档升级;生产环境要先在测试主机验证核心容器。

升级前建议记录:

bash
docker version
docker info
docker compose version
docker system df
docker network ls
docker volume ls
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'

升级后再执行同样命令对比。还要验证至少一个端口发布容器、一个带命名卷的容器、一个 Compose 应用和一次镜像构建。如果团队依赖 GPU、私有 registry、代理、特殊 storage driver 或老版本 Compose 语法,这些都要单独验证。

回滚策略取决于安装方式。通过官方 apt/yum 仓库安装时,可以保留旧版本包信息,必要时降级;通过 Docker Desktop 时,要关注桌面端版本和虚拟机数据;通过自动化脚本安装时,要保留脚本版本和包源。无论哪种方式,真正关键的是业务数据不能只存在容器可写层,否则回滚 Docker 版本也无法恢复数据。

14. 从单机命令走向声明式配置

本文展示了大量 docker run 命令,但它们更适合学习和临时验证,不适合作为长期交付方式。原因很简单:命令历史不可审计,参数容易遗漏,团队无法清楚知道期望状态。

当一个容器开始包含端口、卷、环境变量、网络、重启策略和资源限制时,就应该把它写成 Compose 文件或其他声明式配置。这样配置可以进入版本库,可以评审,可以回滚,也可以被 CI/CD 或自动化平台调用。后续 Compose 文章会继续展开这一点。

基础命令仍然重要,因为排障时经常需要 docker pslogsinspectexecstats 来看真实状态。但运行状态和期望配置要分开:CLI 用于观察和临时操作,Compose/脚本/平台用于表达长期期望状态。

15. Docker 主机的常见故障复盘

15.1 Docker daemon 无法启动

安装后最常见的问题是 daemon 起不来。不要只反复执行 systemctl restart docker,要先看日志和配置。常见原因包括 daemon.json JSON 格式错误、配置了不存在的 registry mirror、数据目录权限异常、storage driver 与内核不兼容、iptables/nftables 规则冲突、磁盘已满。

排查顺序可以这样:

bash
systemctl status docker --no-pager
journalctl -u docker -n 100 --no-pager
sudo dockerd --validate --config-file /etc/docker/daemon.json
df -h
docker info

如果是 daemon 配置错误,优先回滚最近修改的 /etc/docker/daemon.json,再重启服务。不要为了让 Docker 启动而删除整个 /var/lib/docker,那会影响镜像、容器和卷,可能直接造成数据丢失。

15.2 容器端口发布后访问不通

端口访问不通要分层检查。第一层看容器是否运行,第二层看应用是否在容器内监听正确地址,第三层看端口是否发布到宿主机,第四层看宿主机防火墙和云安全组,第五层看客户端访问路径。

bash
docker ps
docker logs <container>
docker exec <container> ss -lntp
docker port <container>
ss -lntp | grep 8080
curl -v http://127.0.0.1:8080

一个典型错误是应用只监听 127.0.0.1。在容器内监听本地回环地址,宿主机即使发布端口,也不一定能访问应用。更稳妥的方式是让容器内服务监听 0.0.0.0,再通过 Docker 端口发布和防火墙控制外部访问。

15.3 磁盘被镜像、日志和卷占满

Docker 主机磁盘打满后,容器可能无法启动、日志无法写入、数据库写入失败,甚至 daemon 本身异常。排查时先看整体占用:

bash
docker system df
docker ps --size
du -sh /var/lib/docker/* 2>/dev/null

清理时要区分对象。镜像可以按未使用状态清理,容器可以清理退出状态,构建缓存可以按时间清理,但卷必须确认业务归属。很多数据事故来自把 volume 当成缓存清掉。生产环境建议先导出清理候选清单,由业务或运维确认后再执行。

16. 安装文档如何沉淀

完成一次 Docker 安装后,应该把结果沉淀成团队文档,而不是只留下命令历史。文档至少包含:操作系统版本、Docker 安装来源、Docker Engine 版本、Compose 版本、daemon 配置、数据目录、日志策略、普通用户权限、registry 配置、代理配置、验证命令、回滚方式。

如果团队有多台 Docker 主机,文档还应该变成自动化脚本或配置管理任务。手工安装可以作为第一次探索,但长期应该用 Ansible、Shell 脚本、云初始化、镜像模板或平台能力统一。否则每台主机的 Docker 版本、日志限制、registry、权限和数据目录都会慢慢漂移。

安装文档还要标注“不适用范围”。比如本文以 Ubuntu 和 Docker 官方仓库为主,如果是 RHEL、Debian、Windows、macOS、离线环境、国产操作系统或云厂商托管容器服务,安装路径都需要单独复核官方文档。把范围写清楚,比给出一个看似万能的脚本更可靠。

17. 学完本篇后的练习

建议完成三个练习。第一,安装 Docker 后运行 hello-world 和 Nginx,记录完整验证命令。第二,创建一个命名卷,写入文件,删除容器后重建,确认数据保留。第三,故意启动一个端口冲突容器,观察错误日志并写出排查过程。

这三个练习分别覆盖 daemon、端口和数据。它们比单纯背命令更有价值,因为生产问题往往就发生在这些位置。后续进入 Dockerfile、网络和数据卷时,这些基础命令会反复用到。

18. 多环境安装差异

不同环境安装 Docker 的关注点不同。开发电脑更关注体验,例如 Docker Desktop、代理、文件共享、资源配额和本地 Kubernetes;测试服务器更关注和生产一致,例如 Docker Engine 版本、Compose 插件、私有镜像仓库和自动化部署;生产服务器更关注安全、审计、容量、日志和升级窗口。

离线环境还要单独处理。不能访问公网时,需要提前准备 Docker Engine 安装包、依赖包、镜像 tar 包或私有 registry。离线安装文档要写清包版本、校验方式、导入镜像命令和升级路径。不要在离线现场临时找包,否则很容易出现版本不一致或来源不可追踪。

云服务器也有特殊点。安全组、云防火墙、弹性网卡、云盘挂载和云监控都会影响 Docker 使用。端口发布后外部不能访问,不一定是 Docker 问题,也可能是安全组没放行;数据卷写入慢,不一定是容器问题,也可能是云盘性能规格不够。安装验证时要同时检查宿主机、Docker 和云平台三层。

19. 基础命令和生产操作的边界

初学者常用 docker run -it --rm 做实验,这是好习惯,因为环境干净、容易清理。但生产服务通常不能这样运行。生产容器需要重启策略、日志策略、数据卷、网络、健康检查、资源限制和配置管理。命令越长,越应该沉淀成 Compose 或自动化配置。

同样,docker exec 适合排障,不适合长期变更。进入容器里手工改配置,容器重建后就会丢失,也无法审计。正确做法是把配置写到镜像、挂载文件或声明式配置中,再重建容器。容器化的核心价值就是让运行状态可重建,而不是让容器变成另一台被手工维护的服务器。

安装和命令学习的最终目标,是让 Docker 主机进入可重复管理状态。也就是说,换一台新主机时,团队能按同一份文档或脚本完成安装;容器异常时,能用同一组命令定位 daemon、镜像、容器、网络和卷;升级失败时,能按记录回滚。做到这一点,Docker 才从个人工具变成团队基础设施。

如果只会在一台机器上手工安装,却无法解释包源、版本、数据目录、日志限制、权限和回滚方式,就还不能把这台 Docker 主机交给团队长期使用。安装完成后的交付记录,和安装命令本身同样重要。

后续所有容器实践都建立在这台主机的可靠性之上;主机基线不稳,镜像、网络和数据卷都会受到影响。

所以安装完成后要先验主机,再验容器,最后再验应用,不要把应用异常直接归因于 Docker 本身。

这条顺序也适用于后续所有 Docker 故障处理。

先确认基础,再判断应用,最后沉淀记录并持续复验。

20. 总结

本文完成了 Docker 容器化实战系列的第二步:从安装 Docker Engine 到掌握基础命令。核心结论如下:

  1. 服务器优先使用 Docker 官方仓库安装 Docker Engine,开发电脑优先使用 Docker Desktop。
  2. 安装 Docker 不只是装一个 CLI,还包括 daemon、containerd、Buildx、Compose 等组件。
  3. docker run 是学习 Docker 的入口,但生产配置应该沉淀为 Compose、脚本或自动化平台。
  4. 排障要围绕 daemon 状态、用户权限、镜像拉取、容器日志、端口映射和数据卷展开。
  5. Docker 数据目录、日志轮转、socket 权限和镜像仓库,是从学习环境走向生产环境必须提前规划的部分。

下一篇会进入镜像构建与 Dockerfile。到那一步,重点会从“如何运行别人做好的镜像”转向“如何把自己的应用做成可交付、可复用、可缓存、可安全扫描的镜像”。

参考资料