在这个针对初学者和老手的 podman 教程中,一步步的从开始使用 podman 到探索所有 Podman 功能。
当我们谈论容器时,我们首先想到的默认工具就是Docker。但自从 Docker 诞生以来,容器领域已经有了很多发展,特别是在容器安全方面。Podman 是解决安全问题的项目之一。
以下是您将从本指南中学到的内容。
什么是PodMan?
Podman是一款符合OCI标准的容器管理工具,提供与 Docker 类似的容器管理功能。
podman 的最佳功能之一是它能够运行无根容器。无根容器是在没有根权限(普通用户)的情况下运行和管理容器的概念。从安全角度来看,无根容器即使在容器受到攻击者破坏的情况下也不允许根访问,从而增加了额外的安全层。您可以在此处了解无根容器的优点。
注意: Docker 还支持无根模式,但有一些限制。您可以在这里阅读相关内容。
Podman 也是无守护进程的(与 docker 不同),这意味着它没有守护进程并直接与 runc 交互(运行基于 OCI 规范的容器)。在文章的最后,我补充了Docker 和 Podman 之间的区别。
另外,假设 Linux 中有两个用户。用户 a和用户 b。用户 a使用 podman创建的容器不能被用户 b修改,反之亦然。
podman 的另一个有趣且高级的功能是在 Pod 中运行容器。与Kubernetes Pod类似,您可以使用 Podman 在本地创建多容器 Pod。您可以将 podman pod 导出为 Kubernetes 清单,并使用 Kubernetes pod 清单来部署 podman pod。
安装 Podman
前往官方podman 安装文档。在这里您可以找到 Windows、MAC 和 Linux 版本的所有安装命令。
注意:对于 Windows 和 Mac,podman 需要虚拟机来部署容器。
我使用 Ubuntu 20.10 进行测试。对于Debian 11 和 ubuntu 20.10 或更高版本,您可以使用以下步骤。
sudo apt-get install runc -y
sudo apt-get -y install podman
对于 CentOS 7,
sudo yum -y install podman
对于 MAC,安装 Podaman,然后 使用 podman machine 命令初始化基于QEMU的虚拟机。
brew install podman
podman machine init
podman machine start
安装后,使用以下命令验证安装。
podman version
Podman 容器注册表配置
默认情况下,Podman 配置有两个容器注册表。
您可以在以下文件中找到默认的 Podman 容器注册表配置。
/etc/containers/registries.conf
您可以将自定义或私有容器注册表添加到此配置。例如,Google容器注册表、AWS ECR、自托管私有注册表等。
如果您想使用registry中的其他私有容器镜像,可以使用命令登录registry podman
。例如,要登录 docker hub,
podman login docker.io
登录后,您将能够使用podman
命令从 docker hub 中提取容器镜像
如果您希望为特定用户拥有不同的注册表配置,您可以registries.conf
在用户目录中创建单独的容器注册表信息。
$HOME/.config/containers/registries.conf
Podman 容器存储
每个系统用户都有自己的容器存储。这意味着,如果您尝试从不同的用户登录中提取映像,它将从远程注册表而不是本地映像中提取映像。
例如,
/var/lib/containers/storage
目录中$HOME/.local/share/containers/storage/
目录 中使用 Podman 管理容器
您可以像使用 docker 一样管理容器。但是,我们将使用 podman 作为带有类似于 docker 标志的命令,而不是 docker 命令。此外,您可以使用任何没有 sudo 权限的用户运行 podman 命令。
首先,让我们尝试拉取图像。默认情况下,podman 首先在quay.io中搜索镜像,然后在docker.io中搜索。如果quay.io中不存在映像,podman 会在 docker.io 中搜索并拉取该映像。因此,最好指定注册表端点的完整映像名称。例如,
podman pull docker.io/nginx
podman pull quay.io/quay/busybox
让我们从 dockerhub 注册表运行 Nginx 容器。以下命令运行具有主机端口映射的 Nginx 容器8080
。
podman run --name docker-nginx -p 8080:80 docker.io/nginx
如果您看到的话,上面的 podman 命令相当于 docker 命令和标志。
在无根模式(普通用户模式)下不能使用低于 1024 的端口。因为普通用户容器命名空间确实具有映射这些端口的权限。如果您想使用 podman 映射小于 1024 的主机端口,您应该以 root 用户或使用 sudo 权限运行 podman,如下所示。
sudo podman run --name docker-nginx -p 80:80 docker.io/nginx
您可以使用以下命令检查映射的端口。-l
flat 返回最新容器的详细信息。
podman port -l
您可以使用检查命令检查容器。
podman inspect -l
其他停止、移除和删除容器的命令与 docker 命令的工作方式相同。
举几个例子,
podman images
podman ps
podman ps -a
podman stop
podman rm
运行 help 命令以了解所有可用的 podman 命令。
podman --help
使用 Podman 构建容器镜像
让我们尝试使用自定义 HTML 文件构建 Nginx 的容器映像。我在 GitHub 存储库中有 Dockerfile 和 HTML 文件。
让我们将 repo 和 cd 克隆到 repo nginx-image目录中。
git clone https://github.com/scriptcamp/podman.git
cd podman/nginx-image
在nginx-image
文件夹内,您将看到一个Dockerfile
和index.html
文件。
让我们使用 podman 构建容器镜像。该命令与 docker 命令类似。
podman build -t scriptcamp/nginx .
现在,让我们将映像推送到容器注册表。确保您已登录容器注册表以推送映像。这里我使用的是dockerhub。
podman push scriptcamp/nginx
使用 Podman 创建 Pod
在本节中,我们将学习如何使用 Podman 创建 pod。Podaman 的高级功能之一是它能够创建类似于 Kubernetes Pod 的 Pod。Pod 是一个可以拥有一个或多个容器的单元。
这是您可以做的。
现在让我们看一下示例。
要了解所有可用的 podman pod 命令,只需运行 help 命令即可。
podman pod --help
创建一个空 Pod
让我们创建一个空的 Pod。如果您不指定该--name
标志,podman 将创建一个具有随机名称的 pod。
podman pod create --name demo-pod
让我们列出创建的 Pod。
podman pod ls
以下命令列出 pod 中的所有容器。对于空 Pod,将k8s.gcr.io/pause
添加一个容器。
podman ps -a --pod
将容器添加到 Podman Pod
让我们向空 pod 添加一个 Nginx 容器。如果运行以下命令后列出容器,您将看到 Nginx 容器添加到demo-pod
podman run -dt --pod demo-pod nginx
启动、停止和删除 Podman Pod 内的容器
您可以使用与删除具有 ID 的容器相同的命令来启动、停止和从 podman pod 中删除容器。
首先使用 podman 命令列出 pod 中的容器,并使用以下命令和容器 ID。
podman start
podman stop
podman rm
使用容器创建 Pod
我们可以使用单个命令创建一个 Pod 并添加容器。让我们使用 Nginx 容器创建一个 pod,该容器具有8080
.
podman run -dt --pod new:frontend -p 8080:80 nginx
如果您访问VM IP上的8080端口,您应该能够看到Nginx主页。
启动、停止和删除 Pod
您可以使用容器 ID/名称选择并停止 pod 内的各个容器,也可以使用以下命令立即停止所有容器。
podman pod stop
podman pod start
要删除 pod,首先停止 pod 中的所有容器,然后运行,
podman pod rm
-f
或者,您可以使用flag强行删除 pod,而不停止容器
podman pod rm -f
多容器应用程序堆栈
您可以在单个 podman pod 中拥有多容器应用程序堆栈。这有助于在本地开发和测试应用程序。
例如,如果您有应用程序和数据库,则可以在同一个 pod 中拥有应用程序和数据库容器。您可以在需要时随时销毁和调出堆栈。类似于 docker-compose 的东西。
应用程序和数据库容器都可以使用本地主机相互通信。我将在博客中单独介绍这个实现。
从 Podman Pod 定义生成 Kubernetes YAML
Podman 的另一个有趣的功能是您可以从 podman pod 生成 Kubernetes pod YAML。这是一个工作功能,但仍在开发中。
首先,让我们使用 Nginx 容器部署一个名为 webserver 的 pod。
podman run -dt --pod new:webserver -p 8080:80 nginx
现在,要为 podman pod 生成 Kubernetes YAML,我们将使用generate kube
带有 pod 名称的标志,如下所示。
podman generate kube webserver
您可以通过重定向将生成的 YAML 定向到文件。
podman generate kube webserver >> webserver.yaml
从 YAML 创建 Podman Pod
如果您有 pod YAML 文件,则可以使用该play kube
标志将其导入并作为 Podman pod 运行。
例如,我们将使用上一节中生成的来运行 pod。首先,我们需要删除现有的 Webserver pod。webserver.yaml
podman pod rm -f webserver
让我们使用以下命令导入 podwebserver.yaml
podman play kube webserver.yaml
现在,让我们尝试导入 Kubernetes YAML。将以下 Kubernetes 清单保存为nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: webserver
image: docker.io/nginx:latest
ports:
- containerPort: 80
让我们将其导入为 podman pod。
podman play kube nginx.yaml
现在,如果列出 Pod,您可以看到正在运行的 Nginx Pod。
Podman 桌面
Podman Desktop 是一个用于管理 podman 容器的 GUI 工具。它适用于 Windows、MAC 和 Linux 系统。
您可以从此处下载并安装 Podman Desktop。
Docker 与 Podman
如果您想知道 Podman 与 docker 有何不同,下表可以帮助您了解一些关键差异。
PodMan | Docker |
---|---|
Podman 是无守护进程的 | Docker 有一个守护进程(containerd)。docker CLI 与守护进程交互来管理容器。 |
Podman直接通过runc与Linux内核交互 | Docker守护进程拥有运行容器的所有子进程 |
Podman 可以部署具有多个容器的 Pod。相同的 pod 清单可以在 Kubernetes 中使用。此外,您还可以将 Kubernetes Pod 清单部署为 Podman Pod。 | Docker中没有pod的概念 |
无需任何额外配置即可运行无根容器。您可以使用 root 或非特权用户运行容器。 | Docker 无根模式需要额外的配置。 |
Podman VS Buildah
当您开始学习或研究 podman 时,您将阅读有关Buildah 的内容。这两个项目最初都是由 Redhat 创建的。
您可能会对 Podman 和 Buildah 项目感到困惑。Podman 在幕后使用 Buildah 进行运行和构建操作。阅读这个官方 podman 博客,其中解释了Podman 和 Buildah之间的区别。
Podman 错误和问题
以下是我在使用 Podman 进行实践时遇到的一些错误和问题。
端口映射错误
如果您尝试映射特权端口,您可能会收到以下错误。如果您想以非 root 用户身份运行 Podman,请始终使用非特权端口。
Error: error from slirp4netns while setting up port redirection: map[desc:bad request: add_hostfwd: slirp_add_hostfwd failed]
如果您没有runc
安装,可能会出现以下错误。
Error: default OCI runtime "runc" not found: invalid argument
图像错误
如果您没有指定正确的镜像名称,Podman 将抛出以下错误。
Trying to pull quay.io/busybox...
error parsing HTTP 404 response body: invalid character '