Docker for Beginners on Ubuntu 22.04: A Comprehensive Setup and Usage Guide
欢迎来到容器化的世界!如果你是一名开发者、运维工程师,或者仅仅是对新技术充满好奇的初学者,Docker 无疑是一个值得深入学习的强大工具。它改变了我们构建、打包和运行应用程序的方式,极大地提高了开发效率、部署一致性以及资源利用率。
本篇文章将带你一步步走完在流行的 Ubuntu 22.04 (Jammy Jellyfish) 操作系统上安装 Docker 的全过程,并介绍一些核心概念和基础命令,帮助你迈出容器化旅程的第一步。我们将确保每一步都详细解释,即使你是 Linux 环境的新手也能轻松跟进。
文章目录
- 引言:什么是 Docker 以及为什么你需要它?
- 传统部署的痛点
- 容器化概念 vs. 虚拟机
- Docker 的核心优势
- 准备工作:在 Ubuntu 22.04 上安装 Docker 前的准备
- 系统要求
- 用户权限
- 网络连接
- 在 Ubuntu 22.04 上安装 Docker Engine
- 推荐方法:使用 Docker 官方仓库 (获取最新版本)
- 更新系统包列表
- 安装必要的依赖项
- 添加 Docker 的官方 GPG 密钥
- 设置 Docker 稳定版仓库
- 安装 Docker Engine、CLI 和 Containerd
- 验证安装:运行 hello-world 容器
- 备用方法:使用 Ubuntu 默认仓库 (简单但版本可能较旧)
- 直接通过 apt 安装
- 验证安装
- 比较两种安装方法
- 推荐方法:使用 Docker 官方仓库 (获取最新版本)
- 安装后的配置:让 Docker 用起来更顺手
- 以非 root 用户管理 Docker (避免每次使用 sudo)
- 创建 docker 用户组
- 将当前用户添加到 docker 组
- 激活用户组变更
- 再次验证 (不使用 sudo)
- 管理 Docker 守护进程
- 检查服务状态
- 启动、停止和重启服务
- 设置开机自启
- 以非 root 用户管理 Docker (避免每次使用 sudo)
- Docker 核心概念初探:镜像、容器、仓库
- 镜像 (Images):应用的打包模板
- 容器 (Containers):镜像的运行实例
- 仓库 (Registries):存放镜像的地方 (Docker Hub)
- 你的第一个 Docker 容器:运行一个 Nginx Web 服务器
- 拉取 Nginx 镜像 (
docker pull
) - 运行 Nginx 容器 (
docker run
)-d
参数:后台运行-p
参数:端口映射
- 查看正在运行的容器 (
docker ps
) - 停止和移除容器 (
docker stop
,docker rm
) - 移除镜像 (
docker rmi
)
- 拉取 Nginx 镜像 (
- 管理 Docker 镜像:拉取、列表、删除
- 搜索镜像 (
docker search
) - 查看本地镜像 (
docker images
) - 删除本地镜像 (
docker rmi
) - 理解镜像标签 (tags)
- 搜索镜像 (
- 构建你自己的 Docker 镜像:Dockerfile 简介
- 什么是 Dockerfile?
- 基础 Dockerfile 指令 (FROM, RUN, COPY, CMD, EXPOSE)
- 构建一个简单的 Python 应用镜像 (示例)
- 创建应用文件和 Dockerfile
- 使用
docker build
构建镜像 - 运行自定义镜像容器
- 数据持久化:Docker 卷 (Volumes) 简介
- 为什么需要数据持久化?(容器是短暂的)
- 什么是 Docker 卷?
- 使用卷挂载数据 (
-v
参数) - 示例:挂载 Nginx 网页目录
- 清理 Docker 资源:释放磁盘空间
- 手动清理容器和镜像
- 使用
docker system prune
进行一键清理
- 常见问题与故障排除
- 权限问题 (“permission denied”)
- 容器启动后立即退出
- 端口冲突
- 镜像拉取失败
- 下一步:继续你的 Docker 之旅
- Docker Compose (多容器应用管理)
- Docker Hub (分享和获取镜像)
- 容器编排 (Swarm, Kubernetes)
- 结论
1. 引言:什么是 Docker 以及为什么你需要它?
在软件开发的早期,部署应用程序通常是一个令人头疼的过程。你可能写好了代码,在你的开发机器上运行完美,但当将其部署到测试环境或生产服务器时,却遇到了各种问题:依赖库版本不兼容、操作系统差异、配置错误等等。这就是臭名昭著的“在我的机器上可以运行”困境。
传统部署的痛点:
- 依赖冲突: 不同的应用程序可能需要同一库的不同版本,导致冲突。
- 环境差异: 开发、测试、生产环境可能操作系统、库、配置不同,导致部署失败。
- 部署复杂: 部署过程需要手动安装各种依赖和配置环境,费时且容易出错。
- 扩展困难: 随着用户量增长,扩展应用需要重复繁琐的部署过程。
- 资源浪费: 虚拟机虽然隔离性好,但资源开销大(需要独立的操作系统)。
容器化概念 vs. 虚拟机:
为了解决这些问题,人们开始寻求更好的解决方案。虚拟机(VM)提供了一种隔离环境,它模拟了一整台计算机,包括硬件和操作系统。虚拟机解决了环境差异问题,但它的缺点是体积庞大、启动慢,且每个虚拟机都需要独立的操作系统资源,效率不高。
容器化是另一种隔离技术。与虚拟机不同,容器共享宿主机的操作系统内核,但它们拥有自己独立的文件系统、进程空间、网络接口等。你可以将应用程序及其所有依赖项打包进一个轻量级的、可移植的单元——容器。
想象一下,虚拟机是一栋独立的房子,每栋房子都有自己的地基、墙壁、屋顶、水电系统(完整的操作系统)。而容器则像公寓楼里的一个单元,每个单元都有独立的门、房间、家具(应用程序及依赖),但它们共享同一个建筑结构(宿主机的操作系统内核)、公共走廊和基础水电供应。
Docker 的核心优势:
Docker 是目前最流行的容器化平台,它提供了一套工具来构建、分发和运行容器。使用 Docker,你可以获得以下优势:
- 一致的环境: 无论在开发、测试还是生产环境,容器都能提供一致的运行环境,极大地减少了“在我机器上可以运行”的问题。
- 快速部署: 容器启动非常快(通常秒级),部署应用程序变得简单高效。
- 隔离性: 容器之间相互隔离,互不影响。
- 轻量级: 相比虚拟机,容器占用的资源更少。
- 可移植性: Docker 容器可以在任何安装了 Docker 的机器上运行,无论是你的笔记本、服务器还是云平台。
- 模块化: 可以将复杂的应用分解成多个独立运行的容器(如 Web 服务器、数据库、缓存),方便管理和扩展。
- 版本控制: Docker 镜像可以像代码一样进行版本管理。
正是这些优势,让 Docker 成为了现代软件开发和运维的标准工具之一。现在,让我们开始在你的 Ubuntu 22.04 系统上安装 Docker 吧!
2. 准备工作:在 Ubuntu 22.04 上安装 Docker 前的准备
在开始安装之前,确保你的系统满足以下基本要求:
- 操作系统: 一个干净的或现有的 Ubuntu 22.04 (Jammy Jellyfish) 64位安装。本指南就是专门针对这个版本的。
- 用户权限: 你需要一个拥有
sudo
权限的非 root 用户。使用sudo
命令可以以管理员权限执行安装和配置命令。 - 网络连接: 安装过程需要从互联网下载 Docker 软件包,请确保你的机器连接正常。
大多数 Ubuntu 安装都会自动创建一个拥有 sudo 权限的用户。如果你当前使用的用户没有 sudo 权限,你需要切换到 root 用户或联系系统管理员来获取。
3. 在 Ubuntu 22.04 上安装 Docker Engine
有两种主要方法可以在 Ubuntu 上安装 Docker Engine:使用 Docker 的官方仓库或使用 Ubuntu 默认仓库。强烈推荐使用 Docker 官方仓库,因为它提供了最新版本的 Docker,并且更新更及时。
推荐方法:使用 Docker 官方仓库
这种方法涉及添加 Docker 的官方 GPG 密钥和软件源列表到你的系统中,这样你就可以使用 apt
包管理器直接安装和更新 Docker。
步骤 3.1:更新系统包列表
在安装任何新软件之前,最好先更新一下系统的包列表,确保你获取的是最新的软件包信息。
bash
sudo apt update
步骤 3.2:安装必要的依赖项
我们需要安装一些工具,以便通过 HTTPS 添加和管理软件仓库。
bash
sudo apt install ca-certificates curl gnupg lsb-release
* ca-certificates
: 允许系统检查 SSL/TLS 证书。
* curl
: 用于下载文件。
* gnupg
: 用于管理 GPG 密钥,验证下载的软件包的真实性。
* lsb-release
: 提供 LSB(Linux Standard Base)信息,有些脚本可能会依赖它。
步骤 3.3:添加 Docker 的官方 GPG 密钥
GPG 密钥用于验证你下载的 Docker 软件包确实来自 Docker 官方,而不是被篡改过的。我们将密钥下载下来,然后添加到系统的信任列表中。
bash
sudo mkdir -p /etc/apt/keyrings # 创建存储密钥的目录,如果不存在
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
* curl -fsSL ...
: 使用 curl 下载 GPG 密钥。-f
失败时不显示错误,-s
静默模式不显示进度,-S
失败时显示错误,-L
遵循重定向。
* |
: 将 curl 的输出通过管道传递给下一个命令。
* sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
: 使用 gpg 工具对下载的密钥进行解密(dearmor),然后以二进制格式保存到 /etc/apt/keyrings/docker.gpg
文件中。
设置密钥文件权限,确保其他用户无法写入:
bash
sudo chmod a+r /etc/apt/keyrings/docker.gpg
步骤 3.4:设置 Docker 稳定版仓库
现在我们将 Docker 的软件源添加到 APT 的源列表文件中。这将告诉你的系统从哪里下载 Docker 软件包。
bash
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
* echo ...
: 构建软件源字符串。
* deb
: 表示这是一个 Debian 格式的仓库。
* [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg]
: 指定仓库适用的体系结构(自动检测,如 amd64)以及用于验证软件包签名的 GPG 密钥文件路径。
* https://download.docker.com/linux/ubuntu
: Docker 仓库的 URL。
* $(lsb_release -cs)
: 自动检测你的 Ubuntu 版本代号(例如,对于 22.04 是 jammy
)。
* stable
: 指定使用 Docker 的稳定版仓库。你也可以选择 test
或 nightly
,但不推荐初学者使用。
* | sudo tee /etc/apt/sources.list.d/docker.list
: 将构建的字符串通过管道传递给 tee
命令,该命令会以 root 权限将内容写入 /etc/apt/sources.list.d/docker.list
文件。这将创建一个新的源列表文件,专门用于 Docker。
* > /dev/null
: 将 tee 的标准输出重定向到 /dev/null
,避免在终端上重复打印写入的内容。
添加完新的仓库后,需要再次更新 APT 包列表,以便系统知道新的软件源及其提供的软件包。
bash
sudo apt update
步骤 3.5:安装 Docker Engine、CLI 和 Containerd
现在,一切准备就绪,可以安装 Docker 的核心组件了。
bash
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
* docker-ce
: Docker Community Edition (社区版) Engine,是 Docker 的核心运行时。
* docker-ce-cli
: Docker Engine 的命令行工具。
* containerd.io
: 一个行业标准的容器运行时,Docker Engine 底层依赖它。
* docker-buildx-plugin
: Docker 的下一代构建工具,支持多平台构建。
* docker-compose-plugin
: Docker Compose 的插件,用于定义和运行多容器 Docker 应用(非常有用,后续会提及)。
步骤 3.6:验证安装:运行 hello-world 容器
安装完成后,Docker 服务应该已经自动启动了。你可以通过运行一个简单的测试容器来验证安装是否成功。
bash
sudo docker run hello-world
这个命令会执行以下操作:
1. 检查本地是否有 hello-world
镜像。
2. 如果本地没有,它会从 Docker Hub(默认的公共仓库)拉取 hello-world
镜像。
3. Docker Engine 会根据 hello-world
镜像创建一个新的容器。
4. 容器会运行一个简单的程序,打印一段欢迎信息,然后退出。
如果一切顺利,你将在终端看到类似下面的输出:
“`
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.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the “hello-world” image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To learn more, try the following commands:
docker run –rm -it ubuntu bash
docker image ls
docker container ls
docker container run –rm -it ubuntu bash
…
“`
看到 “Hello from Docker!” 这段信息,说明 Docker Engine 已经成功安装并可以正常工作了!
备用方法:使用 Ubuntu 默认仓库
Ubuntu 的官方仓库中也包含了 Docker 的软件包 (docker.io
)。这种方法安装更简单,但通常提供的 Docker 版本会比官方仓库中的旧。对于刚接触 Docker 并只需要基础功能的用户来说,这可能是一个快速入门的选择。
步骤:
bash
sudo apt update
sudo apt install docker.io
验证安装:
bash
sudo docker run hello-world
你同样会看到类似的 hello-world
输出。
比较两种安装方法:
特性 | Docker 官方仓库方法 | Ubuntu 默认仓库方法 |
---|---|---|
Docker 版本 | 最新稳定版 | 通常较旧版本 |
更新及时性 | Docker 官方发布新版本后很快就能更新 | 依赖 Ubuntu 仓库更新,较慢 |
安装步骤 | 稍多几步 (加密钥、加源) | 简单 (一步安装) |
推荐程度 | 强烈推荐 (获取最新特性和 bug 修复) | 适合极简需求,但不推荐长期使用 |
总结: 尽管默认仓库方法更简单,但为了获得最佳的 Docker 体验和最新的功能及安全性更新,请务必选择使用 Docker 官方仓库进行安装。本指南后续内容将基于通过官方仓库安装的 Docker 版本进行讲解。
4. 安装后的配置:让 Docker 用起来更顺手
安装完成后,有几个重要的配置步骤可以极大地提升你的使用体验。
以非 root 用户管理 Docker (避免每次使用 sudo)
默认情况下,只有 root 用户或 docker
用户组的成员才能执行 Docker 命令。为了避免每次输入 sudo
,建议将你的当前用户添加到 docker
用户组中。
步骤 4.1:创建 docker 用户组 (通常安装时已创建)
Docker 安装包通常会自动创建 docker
用户组。你可以通过以下命令检查它是否存在:
bash
grep docker /etc/group
如果输出了包含 docker
的行,说明用户组已存在。如果不存在,你可以手动创建:
bash
sudo groupadd docker
步骤 4.2:将当前用户添加到 docker 组
将你当前登录的用户添加到 docker
用户组中。将 <your_username>
替换为你的实际用户名。你可以使用 whoami
命令查看当前用户名。
bash
sudo usermod -aG docker $USER
* usermod
: 修改用户账户的命令。
* -aG
: -a
表示 append (添加),-G
表示 group (组)。组合使用表示将用户添加到指定的附加组中,而不是替换用户原有的附加组列表。
* docker
: 要添加的用户组。
* $USER
: 这是一个 shell 变量,代表当前登录用户的用户名。
步骤 4.3:激活用户组变更
用户组的变更需要重新登录或激活才能生效。最简单的方法是注销当前用户并重新登录。
如果你不想注销,也可以尝试使用 newgrp
命令来临时切换到新的用户组环境。注意:使用 newgrp docker
后,你当前终端会话的环境会改变,但可能不是所有 shell 功能都能完美工作,并且这只对当前会话有效。最稳妥的方式还是注销重登录。
“`bash
如果不想注销,可以在当前终端尝试运行此命令,但这并非总是完全可靠
newgrp docker
“`
步骤 4.4:再次验证 (不使用 sudo)
重新登录后,打开新的终端窗口,尝试再次运行 hello-world
容器,但这次不要使用 sudo
。
bash
docker run hello-world
如果命令成功执行并输出了欢迎信息,说明你现在已经可以通过非 root 用户执行 Docker 命令了!如果仍然出现 “permission denied” 错误,请确保你已经正确地将用户添加到 docker
组并重新登录了。
管理 Docker 守护进程
Docker Engine 作为后台服务运行,负责管理镜像、容器、卷等。你可以使用 systemctl
命令来管理这个服务。
检查服务状态:
bash
sudo systemctl status docker
输出会显示服务是否正在运行 (active (running)
),以及一些相关的日志信息。
启动 Docker 服务:
如果 Docker 服务没有运行,可以使用以下命令启动它:
bash
sudo systemctl start docker
停止 Docker 服务:
bash
sudo systemctl stop docker
重启 Docker 服务:
bash
sudo systemctl restart docker
设置开机自启:
Docker 服务通常在安装后会自动设置为开机自启。你可以使用以下命令确认或启用它:
bash
sudo systemctl enable docker
这将创建一个符号链接,确保系统启动时会自动启动 Docker 服务。
至此,你已经在 Ubuntu 22.04 上成功安装并配置好了 Docker 环境,并且可以使用非 root 用户方便地执行 Docker 命令了。接下来,我们将深入了解 Docker 的核心概念,并学习如何使用基础命令。
5. Docker 核心概念初探:镜像、容器、仓库
在使用 Docker 之前,理解几个核心概念至关重要:
-
镜像 (Images):
- Docker 镜像是一个轻量级、独立、可执行的软件包,包含运行某个应用所需的一切:代码、运行时、系统工具、库和设置。
- 可以将镜像理解为面向对象编程中的“类”或虚拟化中的“模板”。它是一个只读的模板。
- 镜像是通过一系列层(layers)构建的,每一层都代表一个文件系统的修改。这种分层结构使得镜像的构建、分发和更新非常高效。
- 你可以从公共仓库(如 Docker Hub)拉取现有的镜像,也可以根据 Dockerfile 文件构建自己的镜像。
-
容器 (Containers):
- Docker 容器是镜像的一个可运行实例。
- 可以将容器理解为面向对象编程中的“对象”或虚拟化中的“虚拟机实例”。它是镜像的运行时表现。
- 每个容器都在自己的独立环境中运行,拥有自己的文件系统、进程空间、网络接口等,与其他容器和宿主机隔离。
- 容器的状态是可以修改的(例如,在容器内创建文件、修改配置),这些修改会发生在容器的最顶层,这一层是可写的。
- 容器可以被创建、启动、停止、删除。它们是短暂的,如果删除了容器,除非使用了数据卷,否则容器内产生的数据也会丢失。
-
仓库 (Registries):
- Docker 仓库是集中存放 Docker 镜像的地方。
- 可以将仓库理解为代码版本控制系统(如 Git)的远程仓库。
- Docker Hub 是 Docker 官方提供的公共仓库,包含了大量常用的官方镜像和社区贡献的镜像。
- 你也可以搭建私有仓库来存放自己的镜像,或者使用云服务商提供的容器注册服务(如 AWS ECR, Google GCR, Azure ACR)。
- 使用
docker pull
命令从仓库拉取镜像,使用docker push
命令将自己的镜像上传到仓库。
简单来说:你从仓库获取一个镜像,然后用这个镜像创建一个或多个可运行的容器。
6. 你的第一个 Docker 容器:运行一个 Nginx Web 服务器
通过运行一个 Nginx Web 服务器来实践一下这些概念。Nginx 是一个高性能的 Web 服务器。
步骤 6.1:拉取 Nginx 镜像
首先,我们需要获取 Nginx 的 Docker 镜像。Docker 会默认从 Docker Hub 拉取镜像。
bash
docker pull nginx
这个命令会从 Docker Hub 拉取最新版本的 Nginx 官方镜像(如果没有指定标签,默认拉取 :latest
标签)。你会看到下载进度,因为 Nginx 镜像通常由多个层组成。
Using default tag: latest
latest: Pulling from library/nginx
... (一些下载层的信息) ...
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
最后一行表示镜像拉取成功。
步骤 6.2:运行 Nginx 容器
现在,我们使用拉取到的 Nginx 镜像创建一个并运行一个容器。
bash
docker run -d -p 80:80 nginx
让我们分解这个命令:
* docker run
: 这是运行一个新容器的命令。
* -d
: 这个参数表示在后台 (detached mode) 运行容器。如果不加 -d
,容器会在当前终端前台运行,占用你的终端。
* -p 80:80
: 这个参数设置端口映射。格式是 宿主机端口:容器内部端口
。这里我们将宿主机的 80 端口映射到容器内部 Nginx 默认监听的 80 端口。这样,你就可以通过访问宿主机的 80 端口来访问容器内的 Nginx 服务。
* nginx
: 指定用来创建容器的镜像名称(即我们刚刚拉取的 nginx:latest
镜像)。
命令执行后,如果成功,会输出一个长字符串,这是容器的唯一 ID。
a1b2c3d4e5f6... (容器 ID)
步骤 6.3:访问 Nginx Web 服务器
打开你的 Web 浏览器,访问 http://localhost
或 http://127.0.0.1
。你应该会看到 Nginx 的欢迎页面,显示 “Welcome to nginx!”。这证明你的 Nginx 容器正在后台成功运行,并且端口映射工作正常。
步骤 6.4:查看正在运行的容器
使用 docker ps
命令可以查看当前正在运行的容器列表。
bash
docker ps
输出会包含容器 ID、使用的镜像、命令、创建时间、状态、端口映射以及容器名称(如果没有指定,Docker 会随机生成一个名称)。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx "/docker-entrypoint.…" 20 seconds ago Up 19 seconds 0.0.0.0:80->80/tcp zealous_hawking
你可以看到刚才运行的 Nginx 容器的信息。
步骤 6.5:停止和移除容器
当你不再需要运行这个 Nginx 容器时,可以停止并移除它。你需要使用容器 ID 或容器名称。从 docker ps
的输出中获取容器 ID(例如 a1b2c3d4e5f6
)或名称(例如 zealous_hawking
)。
停止容器:
bash
docker stop zealous_hawking # 或者使用容器 ID: docker stop a1b2c3d4e5f6
容器停止后,它不再运行,但仍然存在于系统中。使用 docker ps
将看不到它。
查看所有容器(包括已停止的):
bash
docker ps -a
你会看到刚才停止的 Nginx 容器状态显示为 Exited
。
移除容器:
要完全清理掉容器,需要移除它。容器必须是停止状态才能被移除。
bash
docker rm zealous_hawking # 或者使用容器 ID: docker rm a1b2c3d4e5f6
命令执行后,会输出被移除的容器 ID 或名称。现在这个容器已经从系统中彻底删除了。
步骤 6.6:移除镜像 (可选)
如果确定以后不再需要 Nginx 镜像,也可以将其移除,以释放磁盘空间。
bash
docker rmi nginx # 或者使用镜像 ID
如果该镜像正在被某个容器使用(即使是停止状态),你需要先移除容器才能移除镜像。
通过运行 Nginx 容器的这个例子,你已经掌握了 Docker 最核心的几个操作:拉取镜像、运行容器、查看容器、停止容器和移除容器。
7. 管理 Docker 镜像:拉取、列表、删除
镜像管理是使用 Docker 的日常操作之一。
搜索镜像:
如果你不知道某个应用的镜像名称,或者想查找特定类型的镜像,可以使用 docker search
命令。例如,搜索与 “ubuntu” 相关的镜像:
bash
docker search ubuntu
输出会列出 Docker Hub 上包含 “ubuntu” 关键词的公共镜像,包括官方镜像、星级评价、描述等。
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating... 15380 [OK]
websphere-liberty WebSphere Liberty multi-architecture images 27 [OK]
...
OFFICIAL
列有 [OK]
的表示这是官方镜像,通常更可靠。
拉取镜像:
我们已经在 Nginx 例子中演示过了 docker pull nginx
。你也可以指定镜像的版本标签。例如,拉取 Ubuntu 22.04 (Jammy Jellyfish) 镜像:
bash
docker pull ubuntu:22.04
如果不指定标签,默认拉取 :latest
标签。
查看本地镜像:
使用 docker images
或 docker image ls
命令可以列出你本地系统中所有已经下载或构建的镜像。
bash
docker images
输出会显示镜像仓库名 (REPOSITORY)、标签 (TAG)、镜像 ID (IMAGE ID)、创建时间 (CREATED) 和大小 (SIZE)。
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest f652ca3780f0 4 days ago 187MB
ubuntu 22.04 272300ee266f 2 weeks ago 77.8MB
hello-world latest c0b22d77fcb3 2 months ago 13.3kB
镜像 ID 是镜像的唯一标识符。
删除本地镜像:
使用 docker rmi
命令删除本地镜像。可以使用镜像名称:标签 或 镜像 ID。
“`bash
docker rmi ubuntu:22.04
或者使用 ID
docker rmi 272300ee266f
“`
如果一个镜像被某个容器使用(即使是已停止状态),你是无法直接删除它的。需要先删除使用该镜像的所有容器。
8. 构建你自己的 Docker 镜像:Dockerfile 简介
虽然可以直接使用现有的公共镜像,但在实际开发中,你经常需要打包自己的应用程序。Dockerfile 就是用于定义如何构建一个自定义 Docker 镜像的文本文件。它包含了一系列指令,Docker 会按照这些指令一步步构建镜像。
什么是 Dockerfile?
Dockerfile 是一个纯文本文件,其中包含了构建镜像所需的全部指令。每一条指令都会在镜像中创建一个新的层。
基础 Dockerfile 指令:
FROM
: 指定构建镜像所基于的基础镜像。Dockerfile 的第一条指令通常是FROM
。例如:FROM ubuntu:22.04
或FROM python:3.9-slim
。RUN
: 在容器中执行命令。常用于安装软件包、配置环境等。例如:RUN apt update && apt install -y some-package
。COPY
: 将本地文件或目录复制到镜像中的指定路径。例如:COPY ./app /app
。ADD
: 类似于COPY
,但支持解压本地 tar 文件或从 URL 下载文件。通常推荐使用COPY
,因为它更透明。WORKDIR
: 设置后续指令的工作目录。例如:WORKDIR /app
。EXPOSE
: 声明容器在运行时监听的端口。这只是一个文档说明,并不会自动发布端口,需要在docker run
时使用-p
参数实际映射。例如:EXPOSE 80
。CMD
: 指定容器启动时要执行的默认命令。如果docker run
命令指定了其他命令,则会覆盖CMD
中指定的命令。Dockerfile 中通常只有一个CMD
指令。例如:CMD ["python", "app.py"]
。ENTRYPOINT
: 配置一个容器可执行的命令,该命令在容器启动时执行。ENTRYPOINT
和CMD
经常一起使用,CMD
为ENTRYPOINT
提供默认参数。ENV
: 设置环境变量。例如:ENV FLASK_APP=app.py
。
构建一个简单的 Python 应用镜像 (示例):
假设你有一个非常简单的 Python Web 应用,使用 Flask 框架。
-
创建项目目录:
bash
mkdir my-python-app
cd my-python-app -
创建应用文件
app.py
:
使用你喜欢的文本编辑器创建app.py
文件,内容如下:
“`python
from flask import Flask
import osapp = Flask(name)
@app.route(‘/’)
def hello_world():
return ‘Hello from Dockerized Python App on {}!’.format(os.uname().nodename)if name == ‘main‘:
# 监听所有网络接口 (‘0.0.0.0′),以便从外部访问
app.run(debug=True, host=’0.0.0.0’)
“` -
创建依赖文件
requirements.txt
:
记录应用所需的 Python 库。
Flask==2.2.2 # 使用一个特定版本,保证一致性
注意:在实际项目中,你可能需要根据你的 Flask 版本或其他库来调整版本号。 -
创建
Dockerfile
:
创建名为Dockerfile
(注意没有扩展名)的文本文件,内容如下:
“`dockerfile
# 使用官方 Python 镜像作为基础镜像,选择一个适合的版本
FROM python:3.9-slim-bullseye设置工作目录
WORKDIR /app
将本地的 requirements.txt 文件复制到镜像的 /app 目录
COPY requirements.txt .
在容器内安装 Python 依赖
RUN pip install –no-cache-dir -r requirements.txt
将本地应用代码复制到镜像的 /app 目录
COPY app.py .
声明容器会监听的端口
EXPOSE 5000
容器启动时执行的命令
CMD [“python”, “app.py”]
``
FROM python:3.9-slim-bullseye
*: 使用一个轻量级的 Python 官方镜像作为基础,基于 Debian Bullseye 系统。
slim版本不包含完整的开发工具,更小巧。
WORKDIR /app
*: 将容器内的当前工作目录设置为
/app。后续的
COPY和
RUN命令都会在这个目录下执行。
COPY requirements.txt .
*: 将本地的
requirements.txt文件复制到
/app目录(
.代表当前工作目录
/app)。
RUN pip install –no-cache-dir -r requirements.txt
*: 在镜像构建过程中执行此命令,安装
requirements.txt中列出的 Python 依赖。
–no-cache-dir可以减少镜像大小。
COPY app.py .
*: 将本地的
app.py文件复制到
/app目录。
EXPOSE 5000
*: 声明容器会监听 5000 端口。
CMD [“python”, “app.py”]
*: 容器启动后执行
python app.py` 命令来运行应用。 -
构建镜像:
在my-python-app
目录下(包含Dockerfile
和应用文件的目录)执行构建命令。
bash
docker build -t my-python-app .docker build
: 构建 Docker 镜像的命令。-t my-python-app
: 给构建的镜像命名并加上标签。这里我们将镜像命名为my-python-app
,由于没有指定标签,默认使用:latest
。.
: 指定 Dockerfile 所在的上下文路径。.
表示当前目录。Docker 会将当前目录下的所有文件(除了.dockerignore
指定的)发送给 Docker Engine 进行构建。
构建过程会显示每一步指令的执行情况,以及创建的中间层。
“`
[+] Building 15.4s (9/9) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 320B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/library/python:3.9-slim-bullseye 0.0s
=> [auth] library/python:pull token(s) for docker.io 0.0s
=> [1/4] FROM docker.io/library/python:3.9-slim-bullseye 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 78B 0.0s
=> [2/4] WORKDIR /app 0.0s
=> [3/4] COPY requirements.txt . 0.0s
=> [4/4] RUN pip install –no-cache-dir -r requirements.txt 14.6s
=> [5/4] COPY app.py . 0.0s
=> [6/4] EXPOSE 5000 0.0s
=> [7/4] CMD [“python”, “app.py”] 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:… 0.0s
=> => naming to docker.io/library/my-python-app:latest 0.0s
构建成功后,你可以使用 `docker images` 查看新创建的镜像:
bash
docker images
``
my-python-app` 的镜像。
你会看到一个名为
-
运行自定义镜像容器:
现在,使用你刚刚构建的镜像来运行一个容器。我们的 Flask 应用默认监听 5000 端口,所以需要将宿主机的某个端口映射到容器的 5000 端口。
bash
docker run -d -p 8000:5000 my-python-app
这里我们将宿主机的 8000 端口映射到容器的 5000 端口。 -
访问应用:
打开浏览器,访问http://localhost:8000
。你应该能看到你的 Flask 应用输出的 “Hello from Dockerized Python App on …” 消息。
通过这个例子,你已经学会了如何使用 Dockerfile 构建自己的镜像,这对于打包你的应用程序至关重要。
9. 数据持久化:Docker 卷 (Volumes) 简介
默认情况下,Docker 容器的文件系统是短暂的。这意味着当容器被删除时,容器内部产生的所有数据(如日志文件、数据库文件、用户上传的文件)都会丢失。这在大多数实际应用中是不可接受的。为了解决这个问题,Docker 提供了数据持久化的机制,其中最常用的是卷 (Volumes)。
为什么需要数据持久化?
* 数据不丢失: 确保容器删除后,重要数据仍然保留。
* 数据共享: 多个容器可以共享同一个卷来访问相同的数据。
* 分离数据与容器生命周期: 容器可以轻松升级、替换或重建,而数据不受影响。
什么是 Docker 卷?
Docker 卷是由 Docker 管理的,位于宿主机文件系统中的特殊目录。它是 Docker 存储数据的推荐方式。与直接将宿主机目录挂载到容器不同,Docker 卷不依赖于宿主机目录的具体路径,而是由 Docker Engine 进行管理。这提供了更好的可移植性。
使用卷挂载数据 (-v
参数):
在 docker run
命令中使用 -v
参数来挂载卷。-v
参数有两种主要形式:
-
绑定挂载 (Bind Mounts): 将宿主机文件系统中的特定目录或文件挂载到容器中的指定路径。格式:
-v /宿主机/路径:/容器/路径
- 简单直接,可以访问宿主机上的现有文件。
- 依赖于宿主机的文件系统结构,可移植性稍差。
-
命名卷 (Named Volumes): Docker 在宿主机上自动创建和管理卷。你可以给卷命名。格式:
-v 卷名称:/容器/路径
- 由 Docker 统一管理,路径由 Docker 决定。
- 更抽象,与宿主机具体路径解耦,可移植性更好。
- 推荐用于存储数据库文件、配置、日志等。
示例:挂载 Nginx 网页目录 (使用绑定挂载)
我们再次运行 Nginx 容器,但这次将宿主机上的一个目录挂载到容器内部存放网页文件的目录 (/usr/share/nginx/html
),这样我们就可以在宿主机上修改网页内容,容器内的 Nginx 会立即提供新的页面。
-
创建本地网页目录和文件:
在宿主机上创建一个目录,并在其中创建一个简单的 HTML 文件。
bash
mkdir ~/my-nginx-html
echo "<h1>Hello from your own Nginx container!</h1>" > ~/my-nginx-html/index.html -
运行 Nginx 容器并挂载目录:
停止并移除之前运行的 Nginx 容器(如果有的话):docker stop <nginx_container_name_or_id> && docker rm <nginx_container_name_or_id>
然后运行新的 Nginx 容器,使用-v
参数进行绑定挂载:
bash
docker run -d -p 80:80 -v ~/my-nginx-html:/usr/share/nginx/html nginx-v ~/my-nginx-html:/usr/share/nginx/html
: 将宿主机上的~/my-nginx-html
目录绑定挂载到容器内的/usr/share/nginx/html
目录。注意,~
在 shell 中代表当前用户的主目录,但在docker run
命令中直接使用~
可能无法正确解析,更稳妥的做法是使用绝对路径,或者像上面例子中直接使用,但在某些 shell 或环境下可能需要写成${HOME}/my-nginx-html
或使用$(pwd)/my-nginx-html
(如果你当前就在家目录下)。这里我们假设~/my-nginx-html
会被 shell 正确展开。为确保兼容性,使用绝对路径/home/your_username/my-nginx-html
或$(pwd)/my-nginx-html
更佳。 假设你在 home 目录下执行 mkdir 命令,那么可以使用$(pwd)/my-nginx-html
。
“`bash
假设你当前目录是你的home目录
docker run -d -p 80:80 -v $(pwd)/my-nginx-html:/usr/share/nginx/html nginx
“` -
访问 Nginx 并查看内容:
再次访问http://localhost
。你会看到~/my-nginx-html/index.html
文件中的内容:”Hello from your own Nginx container!
“。
-
修改文件并刷新:
在宿主机上修改~/my-nginx-html/index.html
文件,比如改成:
bash
echo "<h2>Content updated from host!</h2>" > ~/my-nginx-html/index.html
然后刷新浏览器,你会发现 Nginx 提供的页面内容立即更新了!这是因为容器直接读取的是宿主机上挂载目录中的文件。
这个例子展示了如何使用绑定挂载实现数据持久化和方便地与容器共享文件。对于更复杂的应用或数据库,使用命名卷是更推荐的方式,因为它由 Docker 管理,备份和迁移更方便。
10. 清理 Docker 资源:释放磁盘空间
随着你使用 Docker 的时间增长,系统中会累积很多不再使用的容器、镜像、卷和网络,它们会占用大量的磁盘空间。定期清理是非常必要的。
手动清理:
-
清理所有已停止的容器:
首先查看所有容器(包括已停止的):docker ps -a
移除所有已停止的容器:
bash
docker rm $(docker ps -aq)docker ps -aq
: 列出所有容器的 ID (安静模式-q
)。$(...)
: Shell 命令替换,将内部命令的输出作为外部命令的参数。
-
清理所有未被任何容器使用的镜像:
首先查看所有镜像:docker images
移除所有悬空镜像(悬空镜像
指的是没有被任何标签引用、也没有被任何容器使用的镜像):
bash
docker image prune
你也可以移除所有未被任何容器使用的镜像 (包括有标签但未使用的):
bash
docker image prune -a
注意:docker image prune -a
会移除所有未被 正在运行 的容器使用的镜像。如果某个镜像被 已停止 的容器使用,它也会被移除。 -
清理所有未被任何容器使用的卷:
首先查看所有卷:docker volume ls
移除所有未被任何容器使用的卷:
bash
docker volume prune
重要: 卷通常包含重要数据,执行docker volume prune
之前请确保你了解将要删除哪些卷,并且这些数据不再需要! -
清理所有未被任何容器使用的网络:
bash
docker network prune
使用 docker system prune
进行一键清理:
Docker 提供了一个非常方便的命令,可以一次性清理掉大部分不再使用的资源:
bash
docker system prune
这个命令会移除:
* 所有已停止的容器
* 所有没有被任何容器引用的网络
* 所有悬空镜像
* 所有构建缓存
执行该命令时,Docker 会提示你确认,并列出将要释放的空间。
如果要移除所有未被任何 正在运行 的容器使用的镜像(包括有标签的),可以加上 -a
参数:
bash
docker system prune -a
注意: docker system prune -a
会移除所有非运行容器使用的镜像和所有卷。这非常强大,但请谨慎使用,特别是 -a
参数,因为它可能会删除你认为仍然需要的镜像或卷。
定期运行 docker system prune
(不带 -a
)是一个保持 Docker 环境整洁的好习惯。
11. 常见问题与故障排除
作为初学者,你可能会遇到一些问题。以下是一些常见的 Docker 问题及其解决方案:
-
权限问题 (“permission denied while trying to connect to the Docker daemon…”)
- 原因: 当前用户没有访问 Docker 守护进程 socket 的权限。这通常是因为用户不在
docker
用户组中,或者用户组变更没有生效。 - 解决: 确保你的用户已经通过
sudo usermod -aG docker $USER
添加到docker
组,并且你已经注销并重新登录(或者在当前会话中尝试newgrp docker
,但不推荐作为永久解决方案)。运行id $USER
可以查看用户所属的组,确认docker
组在其中。
- 原因: 当前用户没有访问 Docker 守护进程 socket 的权限。这通常是因为用户不在
-
容器启动后立即退出 (Container exits immediately)
- 原因: 容器内的应用程序执行完毕或发生错误。很多容器设计的初衷是运行一个单进程应用,应用结束,容器也就退出了。常见原因包括:
- 应用程序配置错误或启动失败。
- 应用程序依赖的环境变量、文件等没有正确设置或挂载。
- CMD 或 ENTRYPOINT 指令不正确。
- 解决: 使用
docker ps -a
查看容器状态,确认其 ID。然后使用docker logs <container_id>
查看容器的日志输出,这通常会告诉你应用退出的原因。也可以尝试以交互模式运行容器(移除-d
参数),以便直接看到终端输出和错误信息。
- 原因: 容器内的应用程序执行完毕或发生错误。很多容器设计的初衷是运行一个单进程应用,应用结束,容器也就退出了。常见原因包括:
-
端口冲突 (Port is already allocated)
- 原因: 你尝试将容器端口映射到宿主机上已经被其他进程占用的端口。
- 解决: 使用
docker ps
查看是否有其他 Docker 容器正在使用该端口。使用sudo netstat -tulnp | grep :<port_number>
或sudo lsof -i :<port_number>
查看宿主机上是否有其他进程占用了该端口。更改docker run
命令中的宿主机端口映射,使用一个空闲的端口。
-
镜像拉取失败 (“unable to find image locally”, “Error response from daemon: pull access denied for …”)
- 原因: 可能是镜像名称或标签错误、网络问题、私有仓库需要认证等。
- 解决: 检查镜像名称和标签是否正确拼写。检查网络连接是否正常。如果需要从私有仓库拉取,确保你已经使用
docker login
进行了认证。
-
文件挂载问题 (Changes not reflected, Permission issues within container)
- 原因: 绑定挂载时,宿主机目录的权限问题,或者挂载路径错误。容器内部进程可能没有权限读写挂载的目录。
- 解决: 确保宿主机上挂载目录的权限允许容器内部的用户访问(容器内部的进程通常以 root 用户运行,但也可能切换到特定用户)。检查宿主机和容器内的路径是否正确匹配。使用
docker inspect <container_id>
查看容器的详细配置,包括卷挂载信息。
12. 下一步:继续你的 Docker 之旅
恭喜你!你已经成功在 Ubuntu 22.04 上安装了 Docker,并掌握了基础概念和核心命令。这为你深入学习 Docker 打下了坚实的基础。接下来,你可以探索更多高级特性:
- Docker Compose: 用于定义和运行多容器 Docker 应用。你可以使用一个 YAML 文件来配置应用的各个服务(如 Web 应用、数据库、缓存),然后使用一个命令启动整个应用栈。对于实际项目开发和部署非常有用。
- Docker Hub 和其他容器注册中心: 学习如何将自己构建的镜像上传到 Docker Hub 或私有仓库,以便在其他机器上使用或与团队成员分享。
- Docker 网络: 深入了解 Docker 的网络模型,学习如何连接不同的容器,如何将容器暴露给外部网络。
- Dockerfile 最佳实践: 学习如何编写高效、安全、体积小的 Dockerfile,如使用多阶段构建(multi-stage builds)。
- Docker Swarm 或 Kubernetes: 学习容器编排工具。当你的应用规模扩大,需要管理大量容器、实现高可用、负载均衡、弹性伸缩时,编排工具就变得必不可少。Kubernetes 是目前业界最流行的容器编排平台。
Docker 的生态系统非常庞大,涵盖了从开发到生产部署的各个环节。不断实践和学习,你将能更充分地利用 Docker 的强大能力。
13. 结论
在本指南中,我们详细介绍了在 Ubuntu 22.04 上为初学者安装 Docker 的过程,并重点讲解了使用 Docker 官方仓库的推荐方法。我们深入浅出地解释了 Docker 的核心概念——镜像、容器和仓库,并通过运行一个 Nginx Web 服务器、构建一个自定义 Python 应用镜像以及演示数据持久化等实际例子,让你亲手体验了 Docker 的强大功能。
你现在应该已经具备了在 Ubuntu 22.04 环境下开始使用 Docker 的基本能力。记住,实践是最好的老师。尝试将你的一个小项目容器化,或者运行一些有趣的公共镜像(如数据库、消息队列等),在实践中你会学到更多。
Docker 的世界充满机遇,它极大地简化了应用程序的部署和管理。祝你在容器化的旅程中一切顺利!
这篇文章详细介绍了 Docker 的安装、配置、核心概念、基础操作、自定义镜像构建、数据持久化以及清理和故障排除,内容涵盖了初学者入门所需的大部分关键知识点,并通过实际命令和示例进行了演示。总字数应该能够满足 3000 字左右的要求。