Docker 与 Debian:新手快速上手指南
前言:拥抱容器化浪潮
在当今快速发展的软件开发和部署领域,效率、一致性与可移植性成为了衡量一个技术栈是否优秀的关键指标。虚拟机(VM)在过去扮演了重要角色,但它们往往资源消耗大,启动慢。正是在这样的背景下,容器化技术应运而生,并以前所未有的速度普及开来。
Docker 是目前最流行、最具影响力的容器化平台之一。它极大地简化了应用程序的打包、分发和运行过程。通过 Docker,开发者可以将应用程序及其依赖项一起打包到一个称为“镜像”(Image)的标准单元中,然后从这个镜像创建出在任何支持 Docker 的环境中都能一致运行的“容器”(Container)。
Debian 作为一个稳定、可靠、社区驱动的 Linux 发行版,因其强大的包管理系统(APT)和广泛的软件支持,一直是许多开发者和系统管理员的首选操作系统,无论是用于服务器还是开发环境。
将 Docker 与 Debian 结合,能够充分利用两者的优势:在稳定的 Debian 环境中,享受 Docker 带来的便捷开发、测试和部署体验。
本文旨在为刚接触 Docker 的 Debian 用户提供一份详尽的入门指南。我们将从 Docker 的核心概念开始,一步步指导你如何在 Debian 系统上安装 Docker Engine,掌握 Docker 的基本操作,并通过实际示例加深理解。无论你是开发者想构建一致的开发环境,还是系统管理员想简化应用部署,本文都将助你快速踏入 Docker 世界。
第一部分:理解 Docker 及其核心概念
在动手安装之前,花点时间理解 Docker 的基本原理至关重要。
1.1 Docker 是什么?为什么使用它?
简单来说,Docker 是一个开源的应用容器化平台。它允许你将应用程序及其所有依赖项(库、系统工具、代码、运行时环境等)打包到一个独立的、可移植的容器中。
为什么使用 Docker?
- 一致性: “在我机器上能跑!” 这句话是许多开发者和运维人员的痛点。Docker 通过将环境与应用一起打包,确保无论在开发、测试还是生产环境,应用运行所需的环境都完全一致,消除了“环境差异”带来的各种问题。
- 可移植性: Docker 容器可以在任何安装了 Docker Engine 的机器上运行,无论是你的笔记本电脑、物理服务器、虚拟机,还是云平台。这极大地简化了应用的迁移和扩展。
- 效率: 容器共享宿主机的操作系统内核,相比虚拟机,它们启动更快、占用的系统资源更少。这使得在同一台机器上运行更多应用实例成为可能。
- 隔离性: 每个 Docker 容器都有自己的文件系统、网络接口和进程空间,它们之间相互隔离,互不影响。这提高了安全性,也避免了不同应用之间的依赖冲突。
- 简化部署: 使用 Docker,应用的部署过程变成了拉取镜像、运行容器的标准流程,极大地简化了持续集成/持续部署(CI/CD)流程。
- 版本控制: Docker 镜像可以通过标签(tag)进行版本管理,方便回滚到旧版本或管理不同的发布分支。
1.2 虚拟机 vs 容器:有什么不同?
这是初学者常常感到困惑的地方。
-
虚拟机 (VM): 虚拟机通过 Hypervisor (如 VMware, VirtualBox, KVM) 模拟一套完整的硬件,并在其上安装一个完整的操作系统(包含内核)。每个 VM 都有自己的 OS 内核和用户空间。
- 优点: 提供完全的硬件和 OS 隔离,适用于运行不同操作系统或需要高度隔离的场景。
- 缺点: 资源消耗大(每个 VM 都需要独立的 OS 副本),启动慢。
-
容器 (Container): 容器利用宿主机的操作系统内核,通过命名空间 (Namespaces) 和控制组 (Control Groups, cgroups) 等 Linux 内核技术,在进程级别实现隔离。容器只包含应用及其依赖,不包含完整的操作系统。
- 优点: 资源消耗小(共享内核),启动快,体积小,可移植性好。
- 缺点: 依赖宿主机的 OS 内核(不能在 Linux 宿主机上直接运行 Windows 容器),隔离性相比 VM 稍弱(但对于大多数应用场景足够)。
可以将虚拟机想象成独立的房屋,每栋房屋有自己独立的基础设施(水、电、暖气)。而容器则像是在同一栋公寓楼里的不同单元,它们共享了建筑的基础设施(地基、墙体、管道),但每个单元内部是独立的。
Docker 容器是基于 Linux 内核的隔离技术构建的,因此在 Debian 这样的 Linux 发行版上运行 Docker 具有天然的优势。
1.3 Docker 核心概念
理解以下几个概念对于学习 Docker 至关重要:
- 镜像 (Image): Docker 镜像是一个轻量级、独立的可执行软件包,包含运行某个软件所需的一切:代码、运行时环境、系统工具、库和设置。镜像是一个只读的模板。你可以将其想象成一个虚拟机的快照,但更轻量。镜像通过 Dockerfile 构建。
- 容器 (Container): 容器是镜像的运行实例。当你运行一个镜像时,就会创建一个容器。容器是可读写的,它在镜像的基础上创建了一个可写层。你可以启动、停止、删除、进入容器。容器之间相互隔离。
- 仓库 (Registry): Docker 仓库是存放 Docker 镜像的地方。最著名的公共仓库是 Docker Hub。你也可以搭建私有仓库。通过仓库,你可以方便地分享、存储和管理镜像。
- Dockerfile: Dockerfile 是一个文本文件,包含了一系列构建 Docker 镜像的指令。通过执行 Dockerfile 中的指令,Docker 会自动构建出一个镜像。它是实现“基础设施即代码”的重要工具。
- 数据卷 (Volume): 容器默认是无状态的,其内部数据在容器被删除后会丢失。数据卷用于在宿主机和容器之间,或者容器之间共享和持久化数据。数据卷独立于容器的生命周期,保证了数据的持久性。
- 网络 (Network): Docker 提供了多种网络模式,允许容器之间以及容器与外部世界进行通信。默认的网络模式通常能够满足基本需求,但在更复杂的应用场景中,需要进行更精细的网络配置。
现在,我们对 Docker 有了初步认识,是时候在我们的 Debian 系统上安装它了。
第二部分:在 Debian 系统上安装 Docker Engine
在 Debian 上安装 Docker 有几种方法,但官方推荐使用 Docker 官方提供的仓库进行安装。这样做的好处是能够获取最新版本的 Docker,并能方便地进行后续更新。Debian 默认仓库中的 Docker 版本可能较旧。
本指南将详细介绍如何通过官方仓库安装 Docker Engine。
2.1 系统要求
- 一个运行中的 Debian 系统。推荐使用 Debian 10 (Buster), Debian 11 (Bullseye) 或更新版本。
- 一个具有
sudo
权限的非 root 用户(推荐)。
2.2 清理旧版本(如果存在)
如果你之前通过 Debian 默认仓库或其他方式安装过旧版本的 Docker,建议先完全卸载它们,以避免冲突。
打开终端,执行以下命令:
bash
sudo apt-get remove docker docker-engine docker.io containerd runc
这个命令会尝试卸载可能存在的旧版本包。即使没有安装过,执行它也不会有什么副作用。
2.3 安装 Docker Engine 的步骤
官方推荐的安装步骤包括:更新 APT 包索引,安装必要的软件包,添加 Docker 的官方 GPG 密钥,设置 Docker 仓库,最后安装 Docker Engine。
步骤 1:更新 APT 包索引并安装必要软件包
这些软件包用于允许 APT 通过 HTTPS 使用仓库。
bash
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
ca-certificates
: 允许 APT 通过 SSL/TLS 验证远程服务器。curl
: 用于下载 GPG 密钥。gnupg
: 用于管理 GPG 密钥。lsb-release
: 用于查找 Debian 版本信息,方便设置正确的仓库源。
步骤 2:添加 Docker 的官方 GPG 密钥
APT 使用 GPG 密钥来验证下载的软件包的完整性和来源。
bash
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo mkdir -p /etc/apt/keyrings
: 创建存放 GPG 密钥的目录,-p
确保父目录也一并创建,即使目录已存在也不会报错。curl -fsSL https://download.docker.com/linux/debian/gpg
: 下载 Docker 的 GPG 密钥文件。-f
: 在 HTTP 错误时静默失败。-s
: 静默模式,不显示进度条和错误信息。-S
: 当-s
启用时,显示错误信息。-L
: 如果服务器返回重定向,跟随重定向。
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
: 将下载的密钥文件进行解密(dearmor)并保存到/etc/apt/keyrings/docker.gpg
文件中。
步骤 3:设置 Docker APT 仓库
将 Docker 的仓库信息添加到 APT 的源列表中。
bash
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
echo "..."
: 打印仓库信息字符串。deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg]
: 指定这是一个 deb 仓库,限制安装的架构 ($(dpkg --print-architecture)
会自动检测你的系统架构,如amd64
),并指定用于签名的 GPG 密钥文件路径。https://download.docker.com/linux/debian
: Docker 仓库的基础 URL。$(lsb_release -cs)
: 会输出你的 Debian 版本代号(如bullseye
或bookworm
)。这确保你添加的是对应你 Debian 版本的仓库。stable
: 指定使用稳定版仓库。你也可以替换为test
或nightly
获取测试版或每夜构建版(不推荐新手使用)。
| sudo tee /etc/apt/sources.list.d/docker.list
: 将echo
的输出内容通过管道传递给tee
命令。tee
命令以 root 权限将内容写入/etc/apt/sources.list.d/docker.list
文件。> /dev/null
: 将tee
的标准输出重定向到/dev/null
,避免在终端重复显示刚刚写入的内容。
步骤 4:再次更新 APT 包索引
添加新仓库后,需要再次更新 APT 包索引,以便识别 Docker 仓库中的软件包。
bash
sudo apt-get update
步骤 5:安装 Docker Engine, containerd 和 Docker Compose (可选)
现在可以安装 Docker 的核心组件了。
bash
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
docker-ce
: Docker 社区版 (Community Edition)。docker-ce-cli
: Docker 命令行客户端。containerd.io
: 一个核心容器运行时,Docker Engine 构建在其之上。docker-buildx-plugin
: 用于构建多平台镜像的插件。docker-compose-plugin
: Docker Compose 的插件版本。Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具,非常实用(强烈推荐安装)。
2.4 验证安装
安装完成后,可以通过运行一个测试容器来验证 Docker 是否正确安装并正在运行。
bash
sudo docker run hello-world
这个命令会:
1. 尝试在本地查找 hello-world
镜像。
2. 如果找不到,则从 Docker Hub 拉取 hello-world
镜像。
3. 从镜像创建一个新的容器,并在其中运行一个简单的程序,该程序会打印一条欢迎消息,然后退出。
如果你看到类似以下内容的输出,说明 Docker 安装成功:
“`
Unable to find image ‘hello-world:latest’ locally
latest: Pulling from library/hello-world
… (下载过程) …
Digest: sha256:…
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
… (其他说明信息) …
“`
2.5 避免每次执行 Docker 命令时使用 sudo
默认情况下,只有 root
用户和 docker
组的成员才能执行 Docker 命令。为了方便,避免每次输入 sudo
,可以将你的当前用户添加到 docker
组。
重要: 将用户添加到 docker
组会赋予该用户与 root 用户几乎等同的权限(因为可以通过 Docker 容器访问宿主机文件系统等),请谨慎操作。
bash
sudo usermod -aG docker $USER
sudo usermod
: 修改用户账户。-aG docker
: 将用户添加到docker
组 (-a
是 append,-G
是 group)。$USER
: 这是一个环境变量,代表当前登录的用户名。
添加用户到 docker
组后,你需要完全退出当前终端会话(或注销并重新登录),更改才会生效。
重新登录后,你可以再次验证:
bash
docker run hello-world
这次你应该无需使用 sudo
即可成功运行命令。如果仍然需要 sudo
,请确保你已经完全重新登录。
至此,你已经在 Debian 系统上成功安装了 Docker Engine,并完成了基本的配置。接下来,我们将学习如何使用 Docker 进行基本操作。
第三部分:Docker 基本操作:从镜像到容器
Docker 的核心在于镜像和容器。本节将介绍如何查找、拉取、查看、运行、停止和删除镜像与容器。
3.1 查找镜像 (Optional)
虽然直接从 Docker Hub 或其他注册中心拉取镜像更常见,但你也可以使用 docker search
命令来搜索公共仓库中的镜像(这个命令不如直接在 Docker Hub 网站上搜索好用,但了解一下无妨)。
例如,搜索关于 Ubuntu 的镜像:
bash
docker search ubuntu
输出通常包括镜像名称、描述、星级(流行度)、官方/自动化构建标记等信息。
3.2 拉取镜像 (docker pull
)
拉取镜像是使用 Docker 的第一步。docker pull
命令从仓库下载镜像到本地。
语法:docker pull <镜像名称>[:<标签>]
<镜像名称>
:通常是仓库名/镜像名
的格式,如果省略仓库名,则默认为library
,即 Docker Hub 的官方镜像。<标签>
:用于指定镜像的版本,如latest
(最新版,默认)、20.04
等。如果不指定标签,默认为latest
。
示例:
拉取 Ubuntu 官方镜像的最新版:
bash
docker pull ubuntu
拉取具体版本的 Ubuntu 镜像 (例如 22.04):
bash
docker pull ubuntu:22.04
拉取一个非官方的 Nginx 镜像 (来自某个用户或组织的仓库):
bash
docker pull someuser/someimage:sometag # 这是一个示例,实际使用时替换为实际的仓库/镜像/标签
3.3 查看本地镜像 (docker images
)
使用 docker images
或 docker image ls
命令可以列出所有已经下载到本地的镜像。
bash
docker images
输出示例:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 272300ee265e 2 weeks ago 77.9MB
ubuntu 22.04 272300ee265e 2 weeks ago 77.9MB
hello-world latest d2c94e258dcb 2 months ago 13.3kB
nginx latest a2b5d75f342b 4 weeks ago 142MB
REPOSITORY
: 镜像所在的仓库名。TAG
: 镜像的标签/版本。IMAGE ID
: 镜像的唯一标识符 (ID)。CREATED
: 镜像创建的时间。SIZE
: 镜像的大小。
你会注意到 ubuntu:latest
和 ubuntu:22.04
可能有相同的 IMAGE ID
。这是因为不同的标签可以指向同一个镜像层,Docker 的镜像是由一系列层构成的。
3.4 运行容器 (docker run
)
docker run
是 Docker 中最常用的命令之一,它结合了创建容器和启动容器两个步骤。如果本地没有指定的镜像,它会先尝试从仓库拉取。
语法:docker run [选项] <镜像名称>[:<标签>] [命令] [参数]
[选项]
: 控制容器的运行方式,非常重要。<镜像名称>[:<标签>]
: 指定用于创建容器的镜像。[命令] [参数]
: 在容器中执行的命令,如果镜像有默认命令 (CMD
或ENTRYPOINT
),则这里的命令会覆盖默认命令。
常用选项:
-d, --detach
: 后台运行容器,并打印容器 ID。-it
: 交互式运行容器。-i
保持标准输入流开放,-t
分配一个伪终端 (TTY)。常用于需要与容器交互的场景(如进入容器命令行)。-p <宿主机端口>:<容器端口>
,--publish
: 将宿主机端口映射到容器内部的端口,使得外部可以访问容器提供的服务。--name <容器名称>
: 为容器指定一个人类可读的名称。如果不指定,Docker 会随机生成一个。指定名称方便后续操作。-v <宿主机路径>:<容器路径>
,--volume
: 挂载数据卷,实现数据持久化或共享。--rm
: 容器退出后自动删除容器。--env <变量名>=<值>
,-e
: 设置容器内的环境变量。
示例:
示例 1:运行一个简单的 hello-world 容器 (非交互式,运行完即退出)
bash
docker run hello-world
这个命令会像之前验证安装时一样,运行 hello-world
镜像中的程序并退出。
示例 2:运行一个 Ubuntu 容器并在其中执行命令
bash
docker run ubuntu echo "Hello from inside the container!"
这会创建一个 Ubuntu 容器,在其中执行 echo "Hello from inside the container!"
命令,然后容器退出。
示例 3:交互式运行一个 Ubuntu 容器,进入其 bash shell
bash
docker run -it ubuntu bash
* -it
: 启用交互模式并分配伪终端。
* ubuntu
: 使用 Ubuntu 镜像。
* bash
: 在容器中执行 bash
命令,这样你就可以在容器的命令行中进行操作了。
进入容器后,你会看到命令提示符变为容器内部的提示符(通常是 root@<容器ID>:/# 或类似)。你可以在这里执行命令,体验容器内部的环境。输入 exit
即可退出容器(容器也会随之停止,因为 bash 进程结束了)。
示例 4:以后台模式运行一个 Nginx Web 服务器 (稍后在实战部分详细介绍)
bash
docker run -d -p 8080:80 --name my-nginx nginx
* -d
: 后台运行。
* -p 8080:80
: 将宿主机的 8080 端口映射到容器内部的 80 端口(Nginx 默认监听 80 端口)。
* --name my-nginx
: 给容器命名为 my-nginx
。
* nginx
: 使用官方 Nginx 镜像。
3.5 查看正在运行的容器 (docker ps
)
使用 docker ps
命令可以列出当前正在运行的容器。
bash
docker ps
输出示例 (对应上面的 Nginx 示例):
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
abcdef123456 nginx "nginx -g 'daemon of…" 10 seconds ago Up 9 seconds 0.0.0.0:8080->80/tcp my-nginx
CONTAINER ID
: 容器的唯一标识符 (ID)。IMAGE
: 容器使用的镜像。COMMAND
: 容器启动时执行的命令。CREATED
: 容器创建的时间。STATUS
: 容器的状态 (如 Up, Exited, Created)。PORTS
: 端口映射信息。NAMES
: 容器的名称。
3.6 查看所有容器 (包括已停止的) (docker ps -a
)
使用 docker ps -a
或 docker container ls -a
命令可以列出所有容器,无论它们是否正在运行。
bash
docker ps -a
这会显示包括已经退出的 hello-world
和之前交互式运行后退出的 ubuntu
容器。
3.7 停止容器 (docker stop
)
使用 docker stop <容器ID或名称>
命令可以停止一个正在运行的容器。Docker 会向容器内的进程发送一个 SIGTERM 信号,等待一段默认时间(通常是 10 秒),如果进程没有退出,则发送 SIGKILL 信号强制终止。
bash
docker stop my-nginx # 按名称停止
docker stop abcdef123456 # 按 ID 停止 (使用容器 ID 的前几位即可)
3.8 启动已停止的容器 (docker start
)
使用 docker start <容器ID或名称>
命令可以启动一个已经停止的容器。
bash
docker start my-nginx
3.9 重启容器 (docker restart
)
使用 docker restart <容器ID或名称>
命令可以重启一个容器。
bash
docker restart my-nginx
3.10 删除容器 (docker rm
)
使用 docker rm <容器ID或名称>
命令可以删除一个已停止的容器。
bash
docker rm my-nginx
如果要删除正在运行的容器,需要加上 -f
或 --force
选项(但通常不推荐这样做,最好先正常停止):
bash
docker rm -f my-nginx
一次删除多个容器:
bash
docker rm container1 container2 container3
删除所有已停止的容器(非常有用):
bash
docker container prune
这个命令会提示你确认是否删除所有未被任何容器引用的停止状态的容器。
3.11 删除镜像 (docker rmi
)
使用 docker rmi <镜像ID或名称>[:<标签>]
命令可以删除本地的一个镜像。
bash
docker rmi ubuntu:latest
docker rmi abcdef123456 # 按镜像 ID 删除 (使用镜像 ID 的前几位即可)
注意: 只有在没有任何容器使用该镜像时,才能成功删除镜像。如果想强制删除,即使有容器使用(这些容器会被标记为 <none>
且无法启动),可以使用 -f
或 --force
选项(同样不推荐新手这样做)。
删除所有未被使用的镜像(悬挂镜像,dangling images):
bash
docker image prune
这个命令会删除没有标签且没有被任何容器引用的镜像。
删除所有未被使用的镜像(包括没有标签和有标签但没有被任何容器引用的镜像):
bash
docker image prune -a
这个命令会提示你确认,因为会删除更多内容。
3.12 进入正在运行的容器 (docker exec
)
docker exec
命令用于在正在运行的容器中执行命令。这对于调试、查看容器内部状态非常有用。
语法:docker exec [选项] <容器ID或名称> <命令> [参数]
示例:
在 my-nginx
容器中执行 ls -l /usr/share/nginx/html
命令:
bash
docker exec my-nginx ls -l /usr/share/nginx/html
进入 my-nginx
容器的 bash shell 进行交互操作:
bash
docker exec -it my-nginx bash
-it
选项是关键,它让你能够与容器内的 bash 会话进行交互。退出容器时,输入 exit
。注意: 使用 docker exec
进入容器并退出时,容器本身不会停止。
第四部分:实战演练:运行一个 Nginx Web 服务器
现在,让我们通过一个实际的例子来巩固前面学到的命令:在 Docker 中运行一个 Nginx Web 服务器。
目标: 运行一个 Nginx 容器,并将宿主机的 80 端口映射到容器的 80 端口,使得我们可以通过浏览器访问宿主机的 80 端口来查看 Nginx 的欢迎页面。
步骤 1:拉取 Nginx 镜像
首先,确保你有 Nginx 镜像在本地。
bash
docker pull nginx
这将拉取官方最新的 Nginx 镜像。
步骤 2:运行 Nginx 容器
使用 docker run
命令以后台模式运行容器,并进行端口映射。
bash
docker run -d -p 80:80 --name my-nginx-web nginx
命令解释:
* -d
: 使容器在后台守护进程模式运行。
* -p 80:80
: 将宿主机的 80 端口 (宿主机端口
) 映射到容器的 80 端口 (容器端口
)。你可以根据需要更改宿主机端口,例如 8080:80
。
* --name my-nginx-web
: 给这个容器起一个有意义的名字,方便管理。
* nginx
: 指定使用的镜像名称。
运行成功后,会输出容器的完整 ID。
步骤 3:验证容器是否正在运行
使用 docker ps
命令查看正在运行的容器。
bash
docker ps
你应该能看到名为 my-nginx-web
的容器,状态显示为 Up ...
。
步骤 4:通过浏览器访问 Nginx
打开你的 Web 浏览器,访问 http://localhost
或 http://你的宿主机IP地址
。如果你将端口映射为 80:80
,则无需指定端口号。如果你映射到其他端口(如 8080:80
),则需要访问 http://localhost:8080
。
如果一切顺利,你应该能看到 Nginx 的欢迎页面,上面写着 “Welcome to nginx!”。
步骤 5:查看容器日志 (可选)
如果你想查看 Nginx 容器的运行日志,可以使用 docker logs
命令。
bash
docker logs my-nginx-web
这会显示 Nginx 输出到标准输出和标准错误流的日志信息。
步骤 6:停止并删除容器 (可选)
当你不再需要这个 Nginx 容器时,可以停止并删除它。
bash
docker stop my-nginx-web
docker rm my-nginx-web
第五部分:数据持久化:Docker Volume 简介
前面提到,容器是无状态的。如果你在容器内创建或修改了文件,当容器被删除时,这些更改也会随之丢失。这对于需要保存数据(如数据库数据、日志文件、用户上传的文件等)的应用来说是不可接受的。
Docker Volume 就是解决这个问题的标准方案,它提供了一种独立于容器生命周期的方式来持久化数据。Volume 可以存储在宿主机文件系统的某个位置,由 Docker 进行管理。
5.1 为什么不用 bind mount (绑定挂载)?
另一种持久化数据的方式是 Bind Mount,直接将宿主机文件系统的某个目录挂载到容器内的某个目录。虽然 Bind Mount 也很常用,特别是在开发过程中(例如挂载项目代码到容器中进行开发/测试),但对于生产环境下的数据持久化,Volume 通常是更好的选择,因为它:
- 由 Docker 管理,更易于备份、迁移和管理。
- 可以更方便地与 Docker Swarm 或 Kubernetes 等编排工具集成。
- 默认的 Volume 创建在宿主机 Docker 数据目录
/var/lib/docker/volumes/
下,与宿主机文件系统的其他部分隔离,不影响宿主机文件系统的结构。
5.2 创建和使用 Volume
Volume 可以由 Docker 自动创建,也可以预先创建。
方法 1:在 docker run
时匿名创建 Volume
这是最简单的使用方式,Docker 会自动创建一个 Volume,并将其挂载到指定的容器路径。Volume 的名称是 Docker 随机生成的 ID。
bash
docker run -d -v /app/data --name my-app-anon some-app-image
* -v /app/data
: 在容器内部的 /app/data
路径上挂载一个匿名 Volume。
匿名 Volume 不方便管理和引用,通常用于临时数据存储或当你不需要显式引用 Volume 时。
方法 2:在 docker run
时使用命名 Volume
你可以指定 Volume 的名称,这样更容易管理和在不同容器间共享。
bash
docker run -d -v my-data-volume:/app/data --name my-app-named some-app-image
* -v my-data-volume:/app/data
: 将名为 my-data-volume
的 Volume 挂载到容器内部的 /app/data
路径。如果 my-data-volume
不存在,Docker 会自动创建它。
方法 3:预先创建 Volume
你可以使用 docker volume create
命令预先创建 Volume。
bash
docker volume create my-db-volume
然后在使用容器时引用它:
bash
docker run -d -v my-db-volume:/var/lib/mysql --name my-mysql mysql
这将把名为 my-db-volume
的 Volume 挂载到 MySQL 容器存放数据的地方,确保数据库数据不会丢失。
5.3 查看和管理 Volume
- 列出所有 Volume:
docker volume ls
- 查看 Volume 详细信息:
docker volume inspect <Volume名称>
- 删除不再使用的 Volume:
docker volume rm <Volume名称>
- 删除所有未被容器使用的 Volume:
docker volume prune
实战举例:使用 Volume 持久化 Nginx 的网站文件
假设你希望通过 Volume 挂载宿主机的一个目录,作为 Nginx 的网站根目录,这样你修改宿主机的文件,Nginx 容器就能立即提供更新后的内容。这通常通过 Bind Mount 更常用,但为了演示命名 Volume,我们也可以先将文件复制到命名 Volume 中。更标准的 Web 内容持久化会使用 Bind Mount。
但这里我们演示一个更符合 Volume 用途的例子:让 Nginx 将日志输出到 Volume。
“`bash
创建一个命名 Volume 用于存放日志
docker volume create nginx-logs
以后台模式运行 Nginx,并将容器的日志目录 /var/log/nginx 映射到 nginx-logs 这个 Volume
docker run -d -p 80:80 \
–name my-nginx-with-logs \
-v nginx-logs:/var/log/nginx \
nginx
访问 Nginx 几次(例如 http://localhost)以生成日志
停止容器
docker stop my-nginx-with-logs
查看 Volume 的位置
docker volume inspect nginx-logs
``
docker volume inspect的输出会包含一个
Mountpoint字段,显示 Volume 在宿主机文件系统中的实际路径(通常在
/var/lib/docker/volumes/nginx-logs/_data下)。你可以去那个目录查看 Nginx 生成的日志文件。即使你删除了
my-nginx-with-logs容器,
nginx-logs` 这个 Volume 及其里面的数据依然存在,可以被新的容器再次挂载。
第六部分:构建自定义镜像:初识 Dockerfile
到目前为止,我们都只是使用了现成的 Docker Hub 镜像。但在实际应用中,你经常需要为自己的应用程序或特定的环境定制镜像。Dockerfile 就是实现这一目标的工具。
Dockerfile 是一个包含一系列指令的文本文件,Docker 根据这些指令一步步构建出镜像。
6.1 Dockerfile 基本指令
以下是一些最常用的 Dockerfile 指令:
FROM <镜像>[:<标签>]
: 指定构建新镜像的基础镜像。Dockerfile 的第一条指令必须是 FROM。例如:FROM ubuntu:22.04
。RUN <命令>
: 在当前镜像层之上执行命令,并在新的镜像层中提交结果。常用于安装软件包、配置环境等。例如:RUN apt-get update && apt-get install -y nginx
。COPY <宿主机路径> <容器路径>
: 从宿主机将文件或目录复制到镜像的文件系统中。例如:COPY ./app /app
。ADD <源路径> <容器路径>
: 类似于 COPY,但源路径可以是 URL,并且如果源是压缩文件,ADD 会自动解压。通常推荐使用 COPY,因为其行为更明确。WORKDIR <路径>
: 设置容器内部工作目录。之后所有RUN
,CMD
,ENTRYPOINT
,COPY
,ADD
指令都会在这个目录下执行。例如:WORKDIR /app
。EXPOSE <端口> [<端口>...]
: 声明容器在运行时监听的网络端口。这只是一个文档作用,并不会实际发布端口,端口发布需要在使用docker run
时加上-p
选项。例如:EXPOSE 80
。ENV <键>=<值>
: 设置环境变量。例如:ENV NODE_ENV=production
。CMD <命令> [<参数>...]
: 指定容器启动时要执行的默认命令。如果docker run
命令指定了其他命令,则此处的 CMD 会被忽略。一个 Dockerfile 只能有一个 CMD。ENTRYPOINT <命令> [<参数>...]
: 指定容器启动时要执行的可执行文件。ENTRYPOINT 和 CMD 经常一起使用,ENTRYPOINT 指定执行的命令,CMD 提供默认参数。
6.2 编写一个简单的 Dockerfile
让我们编写一个简单的 Dockerfile,基于 Ubuntu 创建一个包含 Nginx 并将自定义 HTML 文件放入其中的镜像。
- 创建一个新的目录(例如
my-nginx-image
)。 - 在该目录下创建一个名为
Dockerfile
的文件(注意大小写,无后缀)。 - 在该目录下创建一个名为
html
的子目录。 -
在
html
目录中创建一个名为index.html
的文件,内容如下:html
<!DOCTYPE html>
<html>
<head>
<title>Hello from Docker!</title>
</head>
<body>
<h1>Welcome to my custom Nginx container!</h1>
<p>This page is served from a Docker image built using a Dockerfile on Debian.</p>
</body>
</html> -
编辑
Dockerfile
文件,添加以下内容:“`dockerfile
指定基础镜像
FROM ubuntu:latest
设置环境变量,避免交互式安装时的提示
ENV DEBIAN_FRONTEND=noninteractive
更新apt并安装nginx
RUN apt-get update && \
apt-get install -y nginx && \
rm -rf /var/lib/apt/lists/*复制自定义的html文件到nginx默认网页目录
COPY html/ /var/www/html/
暴露容器的80端口
EXPOSE 80
定义容器启动时运行的命令
使用 exec 形式运行 nginx,使其成为容器的主进程
CMD [“nginx”, “-g”, “daemon off;”]
“`
6.3 构建镜像 (docker build
)
在包含 Dockerfile
文件的目录中打开终端,执行 docker build
命令来构建镜像。
语法:docker build [选项] <路径>
[选项]
: 常用的有-t <镜像名称>[:<标签>]
用于给构建的镜像命名和打标签。<路径>
: 指定构建上下文 (build context) 的路径,通常是当前目录 (.
)。Docker Daemon 会将此路径下的所有文件发送给构建进程。
bash
docker build -t my-custom-nginx:1.0 .
命令解释:
* docker build
: 执行构建命令。
* -t my-custom-nginx:1.0
: 给构建的镜像命名为 my-custom-nginx
,标签为 1.0
。
* .
: 指定构建上下文为当前目录。Docker 会查找当前目录下的 Dockerfile
。
构建过程会一步步执行 Dockerfile 中的指令,并为每一步创建一个新的镜像层。第一次构建可能会花一些时间,因为需要下载 Ubuntu 基础镜像和安装 Nginx。后续如果只修改 html
文件,由于 Docker 的分层存储和构建缓存机制,重新构建会非常快。
构建成功后,你可以使用 docker images
命令查看新构建的镜像:
bash
docker images
你应该能看到 my-custom-nginx
镜像。
6.4 运行自定义镜像
现在,你可以像运行其他镜像一样运行你自定义的镜像。
bash
docker run -d -p 8080:80 --name my-custom-nginx-container my-custom-nginx:1.0
然后通过浏览器访问 http://localhost:8080
,你应该能看到你在 index.html
中编写的自定义内容。
第七部分:新手常见问题与排查
在使用 Docker 的过程中,新手可能会遇到一些常见问题。
7.1 权限问题 (permission denied
或 Got permission denied while trying to connect to the Docker daemon socket
)
这是最常见的问题。原因在于当前用户没有足够的权限访问 Docker Daemon 的 Socket 文件 (/var/run/docker.sock
)。
解决方法:
- 确保你的用户已经加入了
docker
用户组:
bash
sudo usermod -aG docker $USER - 重要: 添加用户组后,必须完全退出当前终端会话或注销并重新登录,更改才会生效。
- 如果不想加入用户组,可以在每次执行 Docker 命令前加上
sudo
(不推荐)。
7.2 端口冲突 (port is already allocated
或 Bind for 0.0.0.0:80 failed: port is already in use
)
这通常发生在使用 -p
进行端口映射时,宿主机上指定的端口已经被其他进程占用。
解决方法:
- 更改
docker run
命令中的宿主机端口,使用一个未被占用的端口(例如-p 8081:80
)。 - 查找并停止占用该端口的其他进程(可以使用
sudo netstat -tulnp | grep <端口号>
或sudo ss -tulnp | grep <端口号>
)。
7.3 容器启动后立即退出 (docker ps -a
显示 Exited 状态)
有很多原因可能导致容器启动后立即退出:
- 容器内的命令执行完毕: 例如,如果你的容器只是运行一个脚本,脚本执行完成后容器的主进程退出,容器也就停止了。
- 容器内的命令出错: 检查容器的日志 (
docker logs <容器ID或名称>
) 查看错误信息。 - ** ENTRYPOINT 或 CMD 配置错误:** 确保你的 Dockerfile 或
docker run
命令指定的启动命令是正确的,并且能在容器环境中正常执行。 - 依赖缺失或配置问题: 检查容器日志,看是否有应用程序启动失败的错误。
排查方法:
- 使用
docker ps -a
查看容器的退出状态码和启动命令。 - 使用
docker logs <容器ID或名称>
查看容器的详细日志。 - 尝试不使用
-d
(后台模式) 运行容器,这样你可以在前台看到容器的输出和错误信息。 - 使用
docker run -it <镜像名称> <命令,例如 bash 或 sh>
进入容器内部进行调试,手动执行启动命令,查看报错信息。
7.4 磁盘空间不足
Docker 镜像和容器会占用磁盘空间,特别是当下载了很多大型镜像或运行了大量容器后。
解决方法:
- 定期清理不再使用的容器:
docker container prune
- 定期清理不再使用的镜像:
docker image prune -a
(会删除所有未被使用的镜像,包括有标签的) - 清理悬挂 Volume:
docker volume prune
- 清理悬挂网络:
docker network prune
- 执行 Docker 系统清理(最彻底,会删除所有停止的容器、未使用的网络、悬挂镜像和 Build Cache):
docker system prune
或docker system prune -a
(会删除所有未使用的镜像,包括有标签的)
重要: docker system prune -a
是非常彻底的清理命令,执行前请确认不再需要那些未被当前运行容器使用的镜像。
7.5 容器网络无法访问外部或互相访问
默认的 bridge
网络模式通常能够满足容器访问外部网络的需求,容器也可以通过名称互相访问(如果使用了自定义 bridge 网络)。
排查方法:
- 检查防火墙规则:宿主机上的防火墙(如
ufw
或iptables
)可能会阻止流量进出 Docker 容器。确保 Docker 使用的端口和网络范围是开放的。Docker 通常会创建自己的 iptables 规则,但有时会与宿主机规则冲突。 - 检查容器网络配置:使用
docker inspect <容器ID或名称>
查看容器的网络设置。 - 尝试使用
docker exec <容器ID或名称> ping <目标IP或域名>
在容器内部测试网络连通性。
第八部分:更进一步:探索 Docker 的世界
恭喜你!通过前面的步骤,你已经掌握了 Docker 在 Debian 上的安装和基本使用。这只是 Docker 世界的冰山一角,还有很多强大的功能值得探索:
- Docker Compose: 用于定义和运行多容器 Docker 应用。通过一个 YAML 文件描述整个应用栈(例如一个 Web 应用、一个数据库和一个缓存),然后使用一个命令启动、停止和管理整个应用。这对于开发和部署复杂应用非常有用。
- Docker Network Modes: 除了默认的
bridge
模式,还有host
(与宿主机共享网络栈,较少隔离)、none
(无网络)、overlay
(用于跨多个 Docker Daemon 主机的集群网络) 等模式,适用于不同的网络需求。 - Bind Mounts: 除了 Volume,Bind Mounts 也是常用的持久化方式,特别适用于开发过程中将宿主机代码直接映射到容器内部进行实时修改和测试。
- Dockerfile 进阶: 学习更多 Dockerfile 指令(如
ARG
,ONBUILD
,HEALTHCHECK
等),以及如何优化 Dockerfile 来减小镜像体积和加快构建速度(多阶段构建 Multi-stage Builds)。 - Docker Swarm 或 Kubernetes: Docker Swarm 是 Docker 官方提供的容器编排工具,而 Kubernetes 是目前最流行的容器编排系统。当你需要在多台服务器上管理大量容器时,容器编排工具是必不可少的。
- Docker Registry: 学习如何使用私有 Docker Registry 来存储和分发自己的镜像。
- Docker 安全性: 了解如何构建更安全的 Docker 镜像和运行更安全的容器。
结论
Docker 已经成为现代软件开发和部署不可或缺的工具。通过本文的详细指南,你已经成功地在稳定可靠的 Debian 系统上安装了 Docker Engine,并掌握了镜像、容器、Volume、Dockerfile 等核心概念及基本操作。
从拉取和运行现成的镜像,到构建和运行自己的自定义镜像,你已经具备了使用 Docker 简化开发和部署的基础能力。将你学到的知识应用到实际项目中,不断实践和探索 Docker 的更多高级功能,你将能够显著提升工作效率,构建更健壮、更可移植的应用程序。
容器化的大门已经为你打开,尽情享受 Docker 带来的便利吧!祝你在容器化的旅程中一切顺利!