15%

全场主机优惠15%

测试技能,享折扣

使用代码:

Skills
开始使用
08.10.2024

Docker on Ubuntu:完整安装和使用指南

Docker 是一个开源容器化平台,它将应用程序及其依赖项打包到称为容器的隔离、可移植单元中。与虚拟机不同,容器共享宿主机的 OS 内核,使其更轻量、启动更快、资源利用率更高——这对于在 VPS Hosting 环境中运行工作负载的用户来说是一个关键区别,因为计算资源直接影响成本和性能。

本指南涵盖了在 Ubuntu 20.04、22.04 和 24.04 LTS 上完整的 Docker 安装流程,包括安装后的安全加固、Docker Compose 工作流,以及大多数教程所忽略的生产相关命令模式。

前提条件与系统要求

在执行任何命令之前,请验证以下内容:

  • Ubuntu 版本: 20.04 LTS(Focal)、22.04 LTS(Jammy)或 24.04 LTS(Noble)。在仓库设置过程中使用的 `lsb_release -cs` 命令将自动检测您的代号。
  • 架构: `amd64`、`arm64` 或 `armhf` 均受 Docker 官方仓库支持。
  • 内核版本: Docker 需要 Linux 内核 3.10 或更高版本。运行 `uname -r` 进行确认。
  • 用户权限: 安装和守护进程管理必须具备 `sudo` 或 root 访问权限。
  • 磁盘空间: 托管 `/var/lib/docker` 的分区至少需要 2 GB 可用空间,这是 Docker 存储镜像、容器、卷和构建缓存的位置。在生产系统上,请将此目录挂载到专用分区或卷上。

安装前的关键步骤: 如果您之前从 Ubuntu 默认的 `apt` 仓库安装了 Docker(即 `docker.io` 软件包),请先将其删除,以避免与官方 Docker CE 软件包发生冲突:

“`bash

sudo apt remove docker docker-engine docker.io containerd runc

“`

步骤 1:更新系统软件包

在添加任何新仓库之前,将软件包索引和已安装的软件包更新至最新版本:

“`bash

sudo apt update

sudo apt upgrade -y

“`

这可确保 `apt` 的依赖解析器基于当前软件包元数据运行,并且您的基础系统库不会过时——这是容器化应用程序出现细微运行时错误的常见原因。

步骤 2:从官方仓库安装 Docker Engine

Ubuntu 默认的 `apt` 仓库提供了一个名为 `docker.io` 的软件包,该软件包由 Canonical 维护,通常落后于 Docker 官方版本数个版本。在生产环境中,请始终从 Docker 自己的仓库进行安装。

2.1 安装传输和验证依赖项

“`bash

sudo apt install apt-transport-https ca-certificates curl software-properties-common gnupg lsb-release -y

“`

为什么需要 `gnupg`? 从 Ubuntu 22.04 开始,`gpg` 并不总是默认存在。明确包含它可防止 GPG 密钥导入静默失败。

2.2 添加 Docker 官方 GPG 密钥

“`bash

sudo install -m 0755 -d /usr/share/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg –dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

sudo chmod a+r /usr/share/keyrings/docker-archive-keyring.gpg

“`

`chmod a+r` 步骤在教程中经常被跳过,但在 `apt` 以受限用户上下文运行的系统上是必要的——没有它,软件包管理器将无法读取密钥环,并在 `apt update` 期间抛出 `NO_PUBKEY` 错误。

2.3 添加 Docker 稳定版仓库

“`bash

echo

"deb [arch=$(dpkg –print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg]

https://download.docker.com/linux/ubuntu

$(lsb_release -cs) stable" |

sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

“`

`arch=$(dpkg –print-architecture)` 替换对于基于 ARM 的服务器至关重要。在此处硬编码 `amd64` 是一个常见错误,会导致 ARM 实例上出现静默软件包解析失败。

2.4 安装 Docker Engine、CLI 和插件

“`bash

sudo apt update

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

“`

软件包说明:

软件包作用
`docker-ce`Docker Engine 守护进程(`dockerd`)
`docker-ce-cli`客户端 CLI(`docker` 命令)
`containerd.io`底层容器运行时(符合 OCI 标准)
`docker-buildx-plugin`扩展构建功能(多平台、BuildKit)
`docker-compose-plugin`Compose V2 作为 Docker CLI 插件集成

关于 `containerd.io` 的说明: 这与 Ubuntu 默认仓库中的 `containerd` 软件包不同。Docker 的 `containerd.io` 是经过特定测试的 containerd 运行时版本。混用两者是守护进程启动失败的已知原因。

步骤 3:验证安装

确认守护进程处于活动状态并已启用开机自启:

“`bash

sudo systemctl status docker

sudo systemctl enable docker

“`

检查已安装的版本:

“`bash

sudo docker –version

sudo docker info

“`

`docker info` 比单独使用 `–version` 提供更多信息——它会显示正在使用的存储驱动程序(通常为 `overlay2`)、cgroup 驱动程序(`systemd` 与 `cgroupfs`),以及 Docker 可访问的 CPU 数量和内存。

运行标准冒烟测试:

“`bash

sudo docker run hello-world

“`

成功运行后会打印”Hello from Docker!”消息,并确认 Docker 守护进程、从 Docker Hub 拉取镜像以及容器执行均正常运行。

步骤 4:配置 Docker 的非 root 访问权限

默认情况下,Docker socket(`/var/run/docker.sock`)归 `root` 和 `docker` 组所有。不在 `docker` 组中的任何用户必须对每个 Docker 命令使用 `sudo`。

“`bash

sudo usermod -aG docker $USER

“`

无需注销即可应用组成员身份:

“`bash

newgrp docker

“`

验证:

“`bash

docker run hello-world

“`

安全警告: `docker` 组的成员身份实际上等同于无密码的 `sudo`。`docker` 组中的用户可以轻易地将宿主机文件系统挂载到容器中,并绕过所有文件系统级访问控制。在多租户系统或共享服务器上,请考虑改用无 root 权限的 Docker

“`bash

dockerd-rootless-setuptool.sh install

“`

无 root 权限模式在非特权用户命名空间下运行 Docker 守护进程和容器,大幅降低攻击面。对于多个用户共享同一宿主机的任何环境,这是推荐的配置。

步骤 5:Docker 常用命令参考

镜像管理

“`bash

Pull a specific image version from Docker Hub

docker pull nginx:1.27-alpine

List locally cached images

docker images

Remove a specific image

docker rmi nginx:1.27-alpine

Remove all dangling (untagged) images to reclaim disk space

docker image prune

Remove all unused images (not just dangling)

docker image prune -a

“`

生产建议: 在任何自动化或生产工作流中,始终拉取带版本标签的镜像(例如 `nginx:1.27-alpine`),而非 `latest`。`latest` 标签是可变的——在推送到注册表后,它可能指向不同的镜像,从而破坏可重现性。

容器生命周期

“`bash

Run a container interactively with a pseudo-TTY

docker run -it ubuntu:22.04 /bin/bash

Run a container in detached mode with port mapping and a name

docker run -d -p 8080:80 –name my-nginx nginx:1.27-alpine

List running containers

docker ps

List all containers (including stopped)

docker ps -a

Stop a running container gracefully (SIGTERM, then SIGKILL after timeout)

docker stop my-nginx

Start a stopped container

docker start my-nginx

Remove a stopped container

docker rm my-nginx

Force-remove a running container (sends SIGKILL immediately)

docker rm -f my-nginx

View real-time logs

docker logs -f my-nginx

Execute a command inside a running container

docker exec -it my-nginx /bin/sh

“`

资源与系统检查

“`bash

Display real-time resource usage statistics

docker stats

Inspect detailed container metadata (JSON)

docker inspect my-nginx

Display disk usage by Docker objects

docker system df

Remove all stopped containers, unused networks, dangling images, and build cache

docker system prune

“`

`docker system prune` 是长期运行服务器最重要的维护命令之一。如果不定期清理,Docker 的构建缓存和已停止的容器可能会在活跃的开发或 CI 宿主机上占用数十 GB 的空间。

步骤 6:Docker Compose——多容器应用编排

Docker Compose V2(即之前安装的 `docker-compose-plugin`)以 `docker compose`(带空格)的形式调用,而非旧版的 `docker-compose`(带连字符)。如果您安装了该插件,两种语法均可使用,但 V2 是当前标准。

6.1 了解 Compose 文件结构

创建项目目录和 `compose.yml` 文件(Compose V2 中的首选文件名;`docker-compose.yml` 仍支持向后兼容):

“`bash

mkdir ~/my-web-app && cd ~/my-web-app

nano compose.yml

“`

一个包含 Nginx 和后端服务的生产实际示例:

“`yaml

services:

web:

image: nginx:1.27-alpine

ports:

  • "8080:80"

volumes:

  • ./nginx.conf:/etc/nginx/nginx.conf:ro
  • ./html:/usr/share/nginx/html:ro

depends_on:

  • app

restart: unless-stopped

app:

image: node:20-alpine

working_dir: /usr/src/app

volumes:

  • ./app:/usr/src/app

command: node server.js

environment:

  • NODE_ENV=production

restart: unless-stopped

“`

关键 Compose 指令说明:

  • `restart: unless-stopped` — 容器在崩溃或系统重启后自动重启,除非被操作员明确停止。这是长期运行服务的正确策略;`always` 即使对于被有意停止的容器也会重启。
  • `depends_on` — 控制启动顺序,但不等待服务*就绪*(例如,数据库接受连接)。如需就绪门控,请结合使用 `healthcheck` 和 `condition: service_healthy`。
  • `volumes` 与 `:ro` — 将配置文件以只读方式挂载,可防止被入侵的容器进程修改自身配置。

6.2 Compose 工作流命令

“`bash

Start all services in detached mode

docker compose up -d

View logs for all services

docker compose logs -f

View logs for a specific service

docker compose logs -f web

List running Compose services

docker compose ps

Scale a specific service to multiple replicas

docker compose up -d –scale app=3

Stop services without removing containers

docker compose stop

Stop and remove containers, networks, and volumes

docker compose down –volumes

Rebuild images before starting (useful after code changes)

docker compose up -d –build

“`

6.3 验证运行中的服务

“`bash

curl -I http://localhost:8080

“`

`200 OK` 响应确认 Nginx 正常提供服务。对于在远程 VPS Hosting 实例上运行的服务,请将 `localhost` 替换为您服务器的公共 IP 地址,并确保防火墙中已开放相关端口(`ufw allow 8080/tcp`)。

步骤 7:Docker 网络基础

了解 Docker 的网络模型对于构建能够安全通信的多容器应用程序至关重要。

默认网络驱动程序:

驱动程序使用场景
`bridge`独立容器的默认选项;宿主机上的隔离网络命名空间
`host`容器共享宿主机的网络栈;性能最高,零隔离
`none`无网络访问;适用于批处理或对安全敏感的工作负载
`overlay`用于 Docker Swarm 集群的多宿主机网络
`macvlan`为容器分配 MAC 地址;在网络上显示为物理设备

创建并使用自定义桥接网络:

“`bash

Create an isolated network

docker network create my-app-network

Run containers attached to the custom network

docker run -d –name db –network my-app-network postgres:16-alpine

docker run -d –name api –network my-app-network my-api-image

Containers on the same custom bridge network can resolve each other by name

Inside 'api', you can connect to 'db' using the hostname 'db'

“`

自定义桥接网络通过容器名称提供容器间的自动 DNS 解析。默认的 `bridge` 网络则不具备此功能——这是一个关键区别,当开发人员假设默认网络上的容器可以通过名称相互访问时,往往会导致连接失败。

步骤 8:使用 Docker 卷实现持久化数据

容器在设计上是短暂的。写入容器文件系统内部的任何数据在容器被删除时都会丢失。对于持久化存储,请使用绑定挂载

“`bash

Create a named volume

docker volume create pgdata

Use the volume with a container

docker run -d

–name postgres-db

-e POSTGRES_PASSWORD=securepassword

-v pgdata:/var/lib/postgresql/data

postgres:16-alpine

List volumes

docker volume ls

Inspect a volume (shows mount point on host)

docker volume inspect pgdata

Remove unused volumes

docker volume prune

“`

卷与绑定挂载的对比:

特性命名卷绑定挂载
由 Docker 管理
需要宿主机路径
跨宿主机可移植是(使用卷驱动程序)
最适合数据库数据、应用程序状态开发代码、配置文件
备份机制`docker run –volumes-from`标准文件系统工具

步骤 9:保持 Docker 更新

Docker 的官方仓库通过标准的 `apt` 机制处理更新:

“`bash

sudo apt update

sudo apt upgrade -y

“`

仅更新 Docker 相关软件包而不升级整个系统:

“`bash

sudo apt install –only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

“`

在进行主要版本升级之前,请查阅 Docker 发行说明,特别是关于存储驱动程序、cgroup 版本处理或可能影响现有 `compose.yml` 文件的已弃用 API 版本的变更。

Docker 与其他容器化方案的对比

特性Docker EnginePodmanLXC/LXDcontainerd(独立)
守护进程架构集中式守护进程无守护进程基于守护进程基于守护进程
无 root 权限支持是(v20+)原生支持有限
Docker Compose 支持原生支持通过 `podman-compose`
OCI 合规性否(LXC 格式)
Kubernetes 集成通过 CRI-dockerd shim原生 CRI原生 CRI
Windows/macOS 支持Docker Desktop有限
最适合通用开发和生产注重安全、无 root 权限系统容器、虚拟机Kubernetes 节点

对于在裸机上大规模运行容器化工作负载的团队,独立服务器环境可让您完全控制内核参数、存储 I/O 调度和网络配置——这些都直接影响容器密度和性能。

生产环境安全加固清单

在生产环境中运行 Docker 之前,请解决以下问题:

守护进程配置(`/etc/docker/daemon.json`):

“`json

{

"log-driver": "json-file",

"log-opts": {

"max-size": "10m",

"max-file": "3"

},

"storage-driver": "overlay2",

"userns-remap": "default",

"live-restore": true,

"no-new-privileges": true

}

“`

  • `log-opts` 与 `max-size` 和 `max-file`: 如果不进行日志轮转,Docker 的 JSON 日志文件将填满您的磁盘。这是容器化宿主机上意外服务器中断最常见的原因之一。
  • `userns-remap: "default"`: 启用用户命名空间重映射,使容器 root(UID 0)映射到宿主机上的非特权 UID。
  • `live-restore: true`: 允许容器在 Docker 守护进程崩溃或在升级期间重启时继续运行——这对于零停机维护至关重要。
  • `no-new-privileges: true`: 防止容器进程通过 `setuid` 或 `setgid` 二进制文件获取额外权限。

网络和防火墙注意事项:

Docker 默认直接操作 `iptables` 并绕过 `ufw` 规则。即使 `ufw deny 8080` 已设置,带有已发布端口(`-p 8080:80`)的容器仍可从互联网访问。要在 Docker 的 `iptables` 操作之上强制执行 `ufw` 规则,请将 `"iptables": false` 添加到 `daemon.json` 并手动管理路由,或使用 Docker 的 `–network host` 配合明确的 `ufw` 规则。

对于需要 HTTPS 终止的项目,请将您的容器化应用程序与正确配置的反向代理(Nginx 或 Traefik)以及有效证书配合使用。SSL 证书是在容器化堆栈后面运行的任何生产 Web 服务的先决条件。

如果您的工作负载涉及容器内的机器学习推理、模型服务或 GPU 加速数据处理,NVIDIA Container Toolkit 可直接与 Docker Engine 集成。GPU Hosting 提供这些工作负载所需的底层硬件。

对于使用基于 Web 的容器管理来管理多个项目的团队,带 cPanel 的 VPS 提供了一个托管控制面板环境,可以为更简单的应用程序堆栈补充基于 Docker 的部署。

关键技术要点与决策矩阵

何时在 VPS 上使用 Docker,何时使用独立服务器:

  • 对于开发环境、预发布环境以及容器密度为 10–50 个容器的中低流量生产工作负载,请使用 VPS。
  • 当容器密度超过 50 个实例、需要可预测的 I/O 性能(无嘈杂邻居效应),或工作负载需要内核参数调优(`sysctl`)时,请使用独立服务器。

上线前的操作清单:

  • 在 `daemon.json` 中配置日志轮转(`max-size`、`max-file`)
  • 启用 `live-restore` 以在守护进程重启时不中断容器运行
  • 对有状态服务数据使用命名卷,而非绑定挂载
  • 在所有 `compose.yml` 文件中固定镜像版本——生产环境中绝不使用 `latest`
  • 在多租户宿主机上启用 `userns-remap` 或运行无 root 权限的 Docker
  • 在 Docker 安装后审计 `iptables` 规则,确认防火墙策略未被绕过
  • 为所有长期运行的服务设置 `restart: unless-stopped`
  • 在定时 cron 任务中运行 `docker system prune` 以防止磁盘耗尽
  • 对所有容器间通信使用自定义桥接网络——切勿依赖默认桥接网络进行服务发现

常见问题

Ubuntu 上的 Docker 默认使用 `systemd` 还是 `cgroupfs` 作为 cgroup 驱动程序?

自 Docker Engine 20.10 起,基于 `systemd` 的系统(包括所有现代 Ubuntu LTS 版本)上的默认 cgroup 驱动程序为 `systemd`。这与 Kubernetes 要求保持一致,并避免了同时运行两个 cgroup 管理器所导致的不稳定性。您可以通过 `docker info | grep -i cgroup` 进行验证。

`docker compose down` 和 `docker compose stop` 有什么区别?

`docker compose stop` 会停止正在运行的容器,但保留容器及其关联的网络。`docker compose down` 会停止容器,然后删除容器以及 Compose 创建的网络。将 `–volumes` 添加到 `down` 还会删除 Compose 文件中定义的命名卷——在生产环境中使用此标志时请谨慎,因为它会永久删除持久化数据。

为什么 Docker 会绕过 Ubuntu 上的 `ufw` 防火墙规则?

Docker 在 `DOCKER` 和 `DOCKER-USER` 链中插入自己的 `iptables` 规则,这些规则在 `ufw` 的 `INPUT` 链规则之前被评估。这意味着使用 `-p` 发布的端口无论 `ufw` 策略如何,都可以从互联网访问。正确的缓解措施是直接向 `DOCKER-USER` 链添加规则,或者在不需要外部访问时将已发布的端口绑定到特定接口(例如 `-p 127.0.0.1:8080:80`)。

如何限制 Docker 容器可以消耗的 CPU 和内存?

在运行时使用资源约束标志:`docker run –memory="512m" –cpus="1.5" my-image`。在 Compose 中,在 `deploy.resources` 键(Compose V2)或顶级 `mem_limit` 和 `cpus` 键下设置这些内容。如果没有限制,单个失控的容器可能会耗尽宿主机资源,并导致同一宿主机上的所有其他容器崩溃。

我可以在 Docker 容器内运行 Docker(Docker-in-Docker)吗?

可以,但强烈不建议在生产环境中使用。CI 流水线的常见模式是将宿主机 Docker socket(`-v /var/run/docker.sock:/var/run/docker.sock`)挂载到 CI 容器中,这会赋予容器对宿主机 Docker 守护进程的完全控制权——这是一个重大安全风险。更安全的替代方案是使用 BuildKit 的 `–allow security.insecure` 标志,或使用 Kaniko、Buildah 等专用工具,这些工具无需 Docker 守护进程即可构建 OCI 镜像。

15%

全场主机优惠15%

测试技能,享折扣

使用代码:

Skills
开始使用