分类 PodMan 下的文章

Podman 使用传统的 fork/exec 模型(相对于客户端/服务器模型)来运行容器。

在进入本文的主要主题 Podman 和容器之前,我需要了解一点 Linux 审计功能的技术。

什么是审计?
Linux 内核有一个有趣的安全功能,叫做审计。它允许管理员在系统上监视安全事件,并将它们记录到audit.log 中,该文件可以本地存储或远程存储在另一台机器上,以防止黑客试图掩盖他的踪迹。

/etc/shadow 文件是一个经常要监控的安全文件,因为向其添加记录可能允许攻击者获得对系统的访问权限。管理员想知道是否有任何进程修改了该文件,你可以通过执行以下命令来执行此操作:

auditctl -w /etc/shadow

现在让我们看看当我修改了 /etc/shadow 文件会发生什么:

touch /etc/shadow

ausearch -f /etc/shadow -i -ts recent

type=PROCTITLE msg=audit(10/10/2018 09:46:03.042:4108) : proctitle=touch /etc/shadow type=SYSCALL msg=audit(10/10/2018 09:46:03.042:4108) : arch=x86_64 syscall=openat success=yes exit=3 a0=0xffffff9c a1=0x7ffdb17f6704 a2=O_WRONLY|O_CREAT|O_NOCTTY| O_NONBLOCK a3=0x1b6 items=2 ppid=2712 pid=3727 auid=dwalsh uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=pts1 ses=3 comm=touch exe=/usr/bin/touch subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)`
审计记录中有很多信息,但我重点注意到它记录了 root 修改了 /etc/shadow 文件,并且该进程的审计 UID(auid)的所有者是 dwalsh。

内核修改了这个文件了么?

跟踪登录 UID
登录 UID(loginuid),存储在 /proc/self/loginuid 中,它是系统上每个进程的 proc 结构的一部分。该字段只能设置一次;设置后,内核将不允许任何进程重置它。

当我登录系统时,登录程序会为我的登录过程设置 loginuid 字段。

我(dwalsh)的 UID 是 3267。

$ cat /proc/self/loginuid
3267
现在,即使我变成了 root,我的登录 UID 仍将保持不变。

$ sudo cat /proc/self/loginuid
3267
请注意,从初始登录过程 fork 并 exec 的每个进程都会自动继承 loginuid。这就是内核知道登录的人是 dwalsh 的方式。

容器
现在让我们来看看容器。

sudo podman run fedora cat /proc/self/loginuid
3267
甚至容器进程也保留了我的 loginuid。 现在让我们用 Docker 试试。

sudo docker run fedora cat /proc/self/loginuid
4294967295
为什么不一样?
Podman 对于容器使用传统的 fork/exec 模型,因此容器进程是 Podman 进程的后代。Docker 使用客户端/服务器模型。我执行的 docker 命令是 Docker 客户端工具,它通过客户端/服务器操作与 Docker 守护进程通信。然后 Docker 守护程序创建容器并处理 stdin/stdout 与 Docker 客户端工具的通信。

进程的默认 loginuid(在设置 loginuid 之前)是 4294967295(LCTT 译注:232 - 1)。由于容器是 Docker 守护程序的后代,而 Docker 守护程序是 init 系统的子代,所以,我们看到 systemd、Docker 守护程序和容器进程全部具有相同的 loginuid:4294967295,审计系统视其为未设置审计 UID。

cat /proc/1/loginuid
4294967295
怎么会被滥用?
让我们来看看如果 Docker 启动的容器进程修改 /etc/shadow 文件会发生什么。

$ sudo docker run --privileged -v /:/host fedora touch /host/etc/shadow
$ sudo ausearch -f /etc/shadow -i type=PROCTITLE msg=audit(10/10/2018 10:27:20.055:4569) : proctitle=/usr/bin/coreutils --coreutils-prog-shebang=touch /usr/bin/touch /host/etc/shadow type=SYSCALL msg=audit(10/10/2018 10:27:20.055:4569) : arch=x86_64 syscall=openat success=yes exit=3 a0=0xffffff9c a1=0x7ffdb6973f50 a2=O_WRONLY|O_CREAT|O_NOCTTY| O_NONBLOCK a3=0x1b6 items=2 ppid=11863 pid=11882 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset comm=touch exe=/usr/bin/coreutils subj=system_u:system_r:spc_t:s0 key=(null)
在 Docker 情形中,auid 是未设置的(4294967295);这意味着安全人员可能知道有进程修改了 /etc/shadow 文件但身份丢失了。

如果该攻击者随后删除了 Docker 容器,那么在系统上谁修改 /etc/shadow 文件将没有任何跟踪信息。

现在让我们看看相同的场景在 Podman 下的情况。

$ sudo podman run --privileged -v /:/host fedora touch /host/etc/shadow
$ sudo ausearch -f /etc/shadow -i type=PROCTITLE msg=audit(10/10/2018 10:23:41.659:4530) : proctitle=/usr/bin/coreutils --coreutils-prog-shebang=touch /usr/bin/touch /host/etc/shadow type=SYSCALL msg=audit(10/10/2018 10:23:41.659:4530) : arch=x86_64 syscall=openat success=yes exit=3 a0=0xffffff9c a1=0x7fffdffd0f34 a2=O_WRONLY|O_CREAT|O_NOCTTY| O_NONBLOCK a3=0x1b6 items=2 ppid=11671 pid=11683 auid=dwalsh uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=3 comm=touch exe=/usr/bin/coreutils subj=unconfined_u:system_r:spc_t:s0 key=(null)
由于它使用传统的 fork/exec 方式,因此 Podman 正确记录了所有内容。

这只是观察 /etc/shadow 文件的一个简单示例,但审计系统对于观察系统上的进程非常有用。使用 fork/exec 容器运行时(而不是客户端/服务器容器运行时)来启动容器允许你通过审计日志记录保持更好的安全性。

最后的想法
在启动容器时,与客户端/服务器模型相比,fork/exec 模型还有许多其他不错的功能。例如,systemd 功能包括:

SD_NOTIFY:如果将 Podman 命令放入 systemd 单元文件中,容器进程可以通过 Podman 返回通知,表明服务已准备好接收任务。这是在客户端/服务器模式下无法完成的事情。
套接字激活:你可以将连接的套接字从 systemd 传递到 Podman,并传递到容器进程以便使用它们。这在客户端/服务器模型中是不可能的。
在我看来,其最好的功能是作为非 root 用户运行 Podman 和容器。这意味着你永远不会在宿主机上授予用户 root 权限,而在客户端/服务器模型中(如 Docker 使用的),你必须打开以 root 身份运行的特权守护程序的套接字来启动容器。在那里,你将受到守护程序中实现的安全机制与宿主机操作系统中实现的安全机制的支配 —— 这是一个危险的主张。

via: https://opensource.com/article/18/10/podman-more-secure-way-run-containers

作者:Daniel J Walsh 选题:lujun9972 译者:wxy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

最近听说了两个技术 —— Podman 与 Buildah

本篇博文记录一下学习了解 Podman 的过程,以作备忘

Docker 是很流行的容器技术,它在运行的时候有一个守护进程,需要把服务启动起来,才能通过CLI管理容器,镜像,守护进程负责处理很多的事情,所以就可能有单点故障登风险,当Docker服务程序挂了,依赖它启动的容器就都不能使用了,另外 这篇文章 还列举可能存在的安全隐患。

Podman 是做什么的呢,它无需守护进程,可以用来管理容器,镜像,以下是它的一些特点

无需安装 Docker,安装 Podman 后就可以进行管理
Podman 的命令与 Docker 几乎相同
Docker 下的镜像 Podman 也可以使用
Podman 存储它的镜像和容器与 Docker 的位置不同(即通过 Podman 载入镜像后,用 Docker 查看镜像是看不到的)
Podman 一个很不错的特性是拥有 rootless 模式,非 root 用户也可以使用 Podman 来启动容器,用户和root用户的镜像/容器是互不影响的。

安装

这里是安装文档:Postman 安装

各个发行版都可以很方便的安装方式,比如 Fedora

$ sudo yum -y install podman

安装测试版本

$ sudo yum distro-sync --enablerepo=updates-testing podman
安装好后,应该有个 “Hello World” 来庆祝一下,别急,在国内需要了解一点先行知识

跟Docker一样,不设置源 pull 镜像会很慢,我了解得知有两个配置文件

/etc/containers/registries.conf
~/.config/containers/registries.conf
上边对应的是全局的配置文件,下边的是用户自己的配置文件,如果用户有自己的配置文件则会忽略全局配置文件

内容大概是这样的:

This is a system-wide configuration file used to

keep track of registries for various container backends.

It adheres to TOML format and does not support recursive

lists of registries.

[registries.search]
registries = ['docker.io', 'registry.fedoraproject.org', 'registry.access.redhat.com']

If you need to access insecure registries, add the registry's fully-qualified name.

An insecure registry is one that does not have a valid SSL certificate or only does HTTP.

[registries.insecure]
registries = ['localhost:5000']
我尝试使用国内Docker源来替换Podman的源,会报错,不清楚什么原因

然后我在下载镜像的时候指定源地址加速常用镜像的下载

$ podman pull daocloud.io/library/nginx
启动Nginx容器

$ podman run --name nginx -p 8080:80 -d nginx
打开浏览器访问 http://[服务器ip]:8080 就能看到 Nginx 的欢迎界面了

容器组——Pod

Podman 正如它的名字,其中的 pod 概念与 kubernetes 的 pod 相仿

可以创建一个容器组,然后在容器组里启动容器,可以统一的管理容器,下面使用一下这个功能

创建 Pod

因为 Podman 的容器组需要一个,后台运行的容器,用来保持容器组状态等。

这个小容器名为 “k8s.gcr.io/pause:3.1” 遗憾的是daocloud源里没有,我是到国外的服务器pull下来导出,下载回来再倒入进来的

如果运行了前边的 Nginx 容器,先删除一下

$ podman rm -f nginx

创建 Pod

$ podman pod create --name super_pod -p 8080:80 -p 6379:6379
此处创建了一个名为 super_pod 的容器组,初始化端口号绑定,这个绑定之后不能进行动态的添加

在 pod 里创建 Nginx

$ podman run --name nginx --pod super_pod -d nginx
在 pod 里创建 Redis

$ podman run -d --name redis --pod super_pod -v /etc/localtime:/etc/localtime:ro redis:latest redis-server --requirepass redis --notify-keyspace-events Ex
因为容器组已经绑定了端口号,那么在容器组里面启动的容器则不需要绑定端口

查看 pod 列表

$ podman pod list
查看运行种的 pod 容器信息

$ podman pod top super_pod
值得一提的是,Pod里面的容器,查看它们的 hostname,都为pod的名称

创建临时容器

$ podman run --rm -it --pod super_pod daocloud.io/library/alpine /bin/sh

查看容器的主机名

$ cat /etc/hostname

Out: super_pod

也就是说,容器组里面的容器可以指定容器名来互相访问

开启启动

相较于 Docker 守护进程指定 --restart 来启动容器,Podman 没有守护进程,该如何在系统启动的时候把容器启动呢?—— systemd

此处以启动一个 Redis 服务为例

创建文件及映射目录

$ sudo mkdir -p /opt/containers/var/lib/redis/data$ sudo mkdir -p /opt/containers/var/lib/redis/data
$ sudo chown 1001:1001 /opt/containers/var/lib/redis/data
$ sudo setfacl -m u:1001:-wx /opt/containers/var/lib/redis/data
创建配置文件 /etc/systemd/system/redis-service.service

[Unit]
Description=Redis Podman Container
After=network.target

[Service]
Type=simple
TimeoutStartSec=5m
ExecStartPre=-/usr/bin/podman rm "redis-service"

ExecStart=/usr/bin/podman run --name redis-service -v /opt/containers/var/lib/redis/data:/var/lib/redis/data:Z -e REDIS_PASSWORD=redis --net host daocloud.io/library/redis:latest

ExecReload=-/usr/bin/podman stop "redis-service"
ExecReload=-/usr/bin/podman rm "redis-service"
ExecStop=-/usr/bin/podman stop "redis-service"
Restart=always
RestartSec=30

[Install]
WantedBy=multi-user.target
防火墙开放访问权限

$ sudo firewall-cmd --zone=public --add-service=redis
启动 Redis

$ sudo systemctl start redis-service
设置开机启动

$ sudo systemctl enable redis-service
另外:

通过 --net host 启动的容器,其它容器访问这个 Redis,可以使用 127.0.0.1 进行连接
systemctl 启动的容器,切换到 root 用户,用 podman ps 可以查看到
参考:

floating-kwaaaay-with-podman-and-systemd

replacing-docker-with-podman-power-of-podman

podman 命令
镜像容器信息流程
ps 子命令 - 列表容器
描述:打印出关于容器的信息

语法参数

Usage:
podman ps [flags]

Flags:
-a, --all Show all the containers, default is only running containers
-f, --filter strings Filter output based on conditions given

  --format string    Pretty-print containers to JSON or using a Go template

-n, --last int Print the n last created containers (all states) (default -1)
-l, --latest Act on the latest container podman is aware of

                     Not supported with the "--remote" flag
  --no-trunc         Display the extended information
  --ns               Display namespace information

-p, --pod Print the ID and name of the pod the containers are associated with
-q, --quiet Print the numeric IDs of the containers only
-s, --size Display the total file sizes

  --sort choice      Sort output by: command, created, id, image, names, runningfor, size, status
  --sync             Sync container state with OCI runtime

-w, --watch uint Watch the ps output on an interval in seconds
Examples:

1.显示所有容器信息并显示容器所属的pod id

podman ps -ap

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES POD ID PODNAME

15e7d3797552 docker.io/mirrorgcrio/pause:3.2 25 hours ago Up 25 hours ago 73c5a062cb17-infra 73c5a062cb17 HelloWorld

2fd059b66fb6 docker.io/library/hello-world:latest /hello 25 hours ago Exited (0) 25 hours ago friendly_neumann 73c5a062cb17 HelloWorld

2.显示容器信息中指定的列

podman ps -ap --format "{{.ID}} {{.Image}} {{.Labels}} {{.Mounts}} {{.PodName}}"

15e7d3797552 docker.io/mirrorgcrio/pause:3.2 map[] [] HelloWorld

2fd059b66fb6 docker.io/library/hello-world:latest map[] [] HelloWorld

3.显示文件的总大小并根据名称进行排序

podman ps --size --sort names

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES SIZE

15e7d3797552 docker.io/mirrorgcrio/pause:3.2 25 hours ago Up 25 hours ago 73c5a062cb17-infra 0B (virtual 683kB)

top 子命令 - 显示容器正在运行的进程
描述:

基础语法:

podman top [options] container [format-descriptors]
podman container top [options] container [format-descriptors]

参数

--latest, -l # 显示通过Podman或以外的方式CRI-O运行容器 (The latest option is not supported on the remote client).

FORMAT DESCRIPTORS

args, capbnd, capeff, capinh, capprm, comm, etime, group, hgroup, hpid, huser, label, nice, pcpu, pgid,pid, ppid, rgroup, ruser, seccomp, state, time, tty, user, vsz

capbn:#Set of bounding capabilities. See capabilities (7) for more information.
capeff:#Set of effective capabilities. See capabilities (7) for more information.
capinh:#Set of inheritable capabilities. See capabilities (7) for more information.
capprm:#Set of permitted capabilities. See capabilities (7) for more information.
hgroup:#The corresponding effective group of a container process on the host.
hpid:#The corresponding host PID of a container process.
huser:#The corresponding effective user of a container process on the host.
label:#Current security attributes of the process.
seccomp:#Seccomp mode of the process (i.e., disabled, strict or filter). See seccomp (2) for more information.
state:#Process state codes (e.g, R for running, S for sleeping). See proc(5) for more information.
stime:#Process start time (e.g, "2019-12-09 10:50:36 +0100 CET).
基础实例:

1.默认情况下,podman-top打印的数据类似ps -ef:

$ sudo podman top 15e7d3797552
# USER PID PPID %CPU ELAPSED TTY TIME COMMAND
# 0 1 0 0.000 46h15m55.488413816s ? 0s /pause

2.可以通过在容器后面指定格式描述符作为参数来控制输出:

$ podman top -l pid seccomp args %C
# PID SECCOMP COMMAND %CPU
# 1 filter sh 0.000
# 8 filter vi /etc/ 0.000
$ podman top -l pid seccomp state args %C
# PID SECCOMP STATE COMMAND %CPU
# 1 filter S nginx: master process nginx -g daemon off; 0.000
# 30 filter S nginx: worker process 0.000
# 31 filter S nginx: worker process 0.000

3.如果指定了未知的描述符,Podman将退回到在容器中执行ps(1)。

$ podman top -l -- aux
USER PID PPID %CPU ELAPSED TTY TIME COMMAND
root 1 0 0.000 1h2m12.497061672s ? 0s sleep 100000
status 子命令 - 查看运行中容器资源使用情况
基础演示:

$podman stats nginx
ID NAME CPU % MEM USAGE / LIMIT MEM % NET IO BLOCK IO PIDS
19f105d5dc1e nginx -- 2.036MB / 1.893GB 0.11% 978B / 10.55kB -- / -- 2
log 子命令 - 查看容器运行的日志
基础演示:

podman logs nginx
inspect 子命令 - 镜像Image Layer信息
基础案例演示:

$ podman inspect nginx | grep -i "ipaddress"
"SecondaryIPAddresses": null,
"IPAddress": "10.88.0.110",
2.容器操作管理
pod 子命令 - 管理容器组的工具称为pods
描述:podman pod是一组管理pod或容器组的子命令。

SYNOPSIS(n. 概要,大纲)

podman pod subcommand

SUBCOMMANDS

┌────────┬───────────────────────┬─────────────────────────────┐
│Command │ Man Page │ Description │
├────────┼───────────────────────┼─────────────────────────────┤
│create │ podman-pod-create(1) │ Create a new pod. │
├────────┼───────────────────────┼─────────────────────────────┤
│exists │ podman-pod-exists(1) │ Check if a pod exists in local storage. │
├────────┼───────────────────────┼─────────────────────────────┤
│inspect │ podman-pod-inspect(1) │ Displays information describing a pod. │
├────────┼───────────────────────┼─────────────────────────────┤
│kill │ podman-pod-kill(1) │ Kill the main process of each container in one or more pods. │
├────────┼───────────────────────┼─────────────────────────────┤
│pause │ podman-pod-pause(1) │ Pause one or more pods. │
├────────┼───────────────────────┼─────────────────────────────┤
│prune │ podman-pod-prune(1) │ Remove all stopped pods and their containers. │
├────────┼───────────────────────┼─────────────────────────────┤
│ps │ podman-pod-ps(1) │ Prints out information about pods. │
├────────┼───────────────────────┼─────────────────────────────┤
│restart │ podman-pod-restart(1) │ Restart one or more pods. │
├────────┼───────────────────────┼─────────────────────────────┤
│rm │ podman-pod-rm(1) │ Remove one or more stopped pods and containers. │
├────────┼───────────────────────┼─────────────────────────────┤
│start │ podman-pod-start(1) │ Start one or more pods. │
├────────┼───────────────────────┼─────────────────────────────┤
│stats │ podman-pod-stats(1) │ Display a live stream of resource usage stats for containers in one or more pods. │
├────────┼───────────────────────┼─────────────────────────────┤
│stop │ podman-pod-stop(1) │ Stop one or more pods. │
├────────┼───────────────────────┼─────────────────────────────┤
│top │ podman-pod-top(1) │ Display the running processes of containers in a pod. │
├────────┼───────────────────────┼─────────────────────────────┤
│unpause │ podman-pod-unpause(1) │ Unpause one or more pods. │
└────────┴───────────────────────┴─────────────────────────────
基础实例:

创建名称为 HugoBlog 的 pod

podman pod create --name HugoBlog
# 63cb481fd2fd6a02d97355d8f950e765e06654c3b3a25111bc4431320fae897e

列出当前pod信息

podman pod ps
podman pod list
# POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID
# 63cb481fd2fd HugoBlog Running 40 minutes ago 2 6b317ba47f5b
# 73c5a062cb17 HelloWorld Running 47 hours ago 2 15e7d3797552

列出指定pod中容器信息

podman pod top 73c5a062cb17
# USER PID PPID %CPU ELAPSED TTY TIME COMMAND
# 0 1 0 0.000 47h27m58.166822731s ? 0s /pause
podman pod top HugoBlog
# USER PID PPID %CPU ELAPSED TTY TIME COMMAND
# 0 1 0 0.000 41m43.871974181s ? 0s /pause
# root 1 0 0.000 41m43.872432777s ? 0s nginx: master process nginx -g daemon off;
# nginx 30 1 0.000 41m43.872491657s ? 0s nginx: worker process
# nginx 31 1 0.000 41m43.87252457s ? 0s nginx: worker process

停止指定pod或者所有pod(其中的容器也随之停止)

podman pod stop HelloWorld
# 73c5a062cb17b5088072ec13c496c101b0b239f9aba1dcad93ba5d746cdfb12d
podman pod stop $(podman pod ps -q)
# 63cb481fd2fd6a02d97355d8f950e765e06654c3b3a25111bc4431320fae897e
# 73c5a062cb17b5088072ec13c496c101b0b239f9aba1dcad93ba5d746cdfb12d

删除指定Pod或者删除所有已经停止的pod

podman pod rm -f HugoBlog
podman pod rm $(podman pod ps -q)
run 子命令 - 容器运行与构建

在指定pod中运行容器

podman run -d --pod HugoBlog nginx:alpine
Trying to pull docker.io/library/nginx:alpine...
Getting image source signatures
Copying blob f682f0660e7a done
...
Copying config 6f715d38cf done
Writing manifest to image destination
Storing signatures
e2a645aa73da2bf98c8e7598a4a1368f92959d18f60b8500c1af70132fb778ed
将 pod 导出为声明式部署清单:

podman generate kube HugoBlog > HugoBlog.yaml
通过部署清单创建 pod:

podman play kube hugo.yaml
cat HugoBlog.yaml

Generation of Kubernetes YAML is still under development!

Save the output of this file and use kubectl create -f to import

it into Kubernetes.

Created with podman-2.0.6

apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2020-09-23T04:31:52Z"
labels:

app: HugoBlog

name: HugoBlog
spec:
containers:

  • command:

    • nginx
    • -g
    • daemon off;
      env:
    • name: PATH
      value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    • name: TERM
      value: xterm
    • name: NGINX_VERSION
      value: 1.19.2
    • name: NJS_VERSION
      value: 0.4.3
    • name: PKG_RELEASE
      value: "1"
    • name: container
      value: podman
    • name: HOSTNAME
      value: HugoBlog

    image: docker.io/library/nginx:alpine
    name: inspiringkare
    resources: {}
    securityContext:
    allowPrivilegeEscalation: true
    capabilities: {}
    privileged: false
    readOnlyRootFilesystem: false
    seLinuxOptions: {}
    workingDir: /
    status: {}


metadata:
creationTimestamp: null
spec: {}
status:
loadBalancer: {}
这是一个兼容 kubernetes 的 pod 定义,你可以直接通过 kubectl apply -f hugo.yaml 将其部署在 Kubernetes 集群中,也可以直接通过 podman 部署

如果通过声明式定义来创建 pod,还是无法解决服务发现的问题,除非换个支持静态 IP 的 CNI 插件,而支持静态 IP 的这些 CNI 插件又需要 etcd 作为数据库,我就这么点资源,可不想再加个 etcd,还是手撸命令行吧。

container 子命令 - 容器相关操作以及备份导出
基础演示:

Podman 支持将容器从一台机器迁移到另一台机器。

在源机器上对容器设置检查点并将容器打包到指定位置。

$ sudo podman container checkpoint <container_id> -e /tmp/checkpoint.tar.gz

在目标机器上使用源机器上传输过来的打包文件对容器进行恢复。

$ sudo podman container restore -i /tmp/checkpoint.tar.gz

缘起:

很多人可能遇到过开机重启时,由于Docker守护程序在占用多核CPU使用100%C使用的情况,导致所有容器都无法启动,服务都不能用的情况。很悲催的是这事儿虫虫也遇到了,之前文章中虫虫介绍过利用Docker重构WP博客的新架构。由于VPS机器不是很稳定,时常会重启,重启时候就会遇到这个事情,VPS负载很高,容器都没有起来,网站就无法访问了。这时候只能杀掉所有容器并重启守护进程,才能恢复。经过了解该问题是由于Docker守护进程引起,而且Docker守护进程是以root特权权限启动的,是一个安全问题,那么有什么方法解决呢?

为什么Docker需要一个守护进程呢?

Podman,Skopeo和Buildah

这三个工具都是符合OCI计划下的工具(github/containers)。主要是由RedHat推动的,他们配合可以完成Docker所有的功能,而且不需要守护程序或访问有root权限的组,更加安全可靠,是下一代容器容器工具。

Podman

Podman可以替换Docker中了大多数子命令(RUN,PUSH,PULL等)。Podman不需要守护进程,而是使用用户命名空间来模拟容器中的root,无需连接到具有root权限的套接字保证容器的体系安全。

Podman专注于维护和修改OCI镜像的所有命令和功能,例如拉动和标记。它还允许我们创建,运行和维护从这些图像创建的容器。

Buildah

Buildah用来构建OCI图像。虽然Podman也可以用户构建Docker镜像,但是构建速度超慢,并且默认情况下使用vfs存储驱动程序会耗尽大量磁盘空间。 buildah bud(使用Dockerfile构建)则会非常快,并使用覆盖存储驱动程序。

Buildah专注于构建OCI镜像。 Buildah的命令复制了Dockerfile中的所有命令。可以使用Dockerfiles构建镜像,并且不需要任何root权限。 Buildah的最终目标是提供更低级别的coreutils界面来构建图像。Buildah也支持非Dockerfiles构建镜像,可以允许将其他脚本语言集成到构建过程中。 Buildah遵循一个简单的fork-exec模型,不以守护进程运行,但它基于golang中的综合API,可以存储到其他工具中。

Skopeo

Skopeo是一个工具,允许我们通过推,拉和复制镜像来处理Docker和OC镜像。

Podman和Buildah对比

Buildah构建容器,Podman运行容器,Skopeo传输容器镜像。这些都是由Github容器组织维护的开源工具(github/containers)。这些工具都不需要运行守护进程,并且大多数情况下也不需要root访问权限。

Podman和Buildah之间的一个主要区别是他们的容器概念。 Podman允许用户创建"传统容器"。虽然Buildah容器实际上只是为了允许将内容添加回容器图像而创建的。一种简单方法是buildah run命令模拟Dockerfile中的RUN命令,而podman run命令模拟功能中的docker run命令。

简而言之,Buildah是创建OCI图像的有效方式,而Podman允许我们使用熟悉的容器cli命令在生产环境中管理和维护这些图像和容器。

容器迁移

安装

基本上各大发行版都提供了二进制安装包, 使用系统包管理就可以安装:

Fedora, CentOS:sudo yum -y install podman

Arch & Manjaro Linux: sudo pacman -S podman

Ubuntu安装:

sudo apt-get update -qq

sudo apt-get install -qq -y software-properties-common uidmap

sudo add-apt-repository -y ppa:projectatomic/ppa

sudo apt-get update -qq

sudo apt-get -qq -y install podman

迁移步骤

首先,用podman替换了cron和CI作业中的所有docker实例。

完成后第一步后使用sysdig来捕获对docker的引用,看看是否还有其他东西在调用docker:

sysdig | grep -w docker

如果您对性能敏感,这可能会大大降低系统速度。

现在就可以删除docker了:

sudo:yum remove docker

或者

apt remove -y docker-ce

清理配置文件:

删除/etc/apt/或者/etc/yum.repos.d/中指向Docker的源

删除/etc/docker/*,/etc/default/docker和/var/lib/ docker中的任何遗留文件

删除docker组:delgroup docker

总结

使用Podman,Skopeo和Buildah的新一代容器架构后,可以解决由于docker守护程序导致的启动和安全问题。使用新架构后除了"没有守护进程"和"不需要sudo访问"之外,没有发现很多不同之处。构建的容器都位于用户目录下(~/.local/containers中)而不是全局的(在/var/lib/docker中),即面向用户而不是面向守护进程。与Docker相比,podman pull会并行下载获取所有层。

podman初试-和docker对比
1,什么是docker?
Docker 是一个开源的应用容器引擎,属于 Linux 容器的一种封装,Docker 提供简单易用的容器使用接口,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上。容器是完全使用沙箱机制,相互之间不会有任何接口。

2,什么是Podman?
Podman 是一个开源的容器运行时项目,可在大多数 Linux 平台上使用。Podman 提供与 Docker 非常相似的功能。正如前面提到的那样,它不需要在你的系统上运行任何守护进程,并且它也可以在没有 root 权限的情况下运行。
Podman 可以管理和运行任何符合 OCI(Open Container Initiative)规范的容器和容器镜像。Podman 提供了一个与 Docker 兼容的命令行前端来管理 Docker 镜像。

Podman 官网地址:https://podman.io/
Podman 项目地址:https://github.com/containers/libpod
3,Podman 和docker不同之处?
docker 需要在我们的系统上运行一个守护进程(docker daemon),而podman 不需要
启动容器的方式不同:
docker cli 命令通过API跟 Docker Engine(引擎)交互告诉它我想创建一个container,然后docker Engine才会调用OCI container runtime(runc)来启动一个container。这代表container的process(进程)不会是Docker CLI的child process(子进程),而是Docker Engine的child process。
Podman是直接给OCI containner runtime(runc)进行交互来创建container的,所以container process直接是podman的child process。
因为docke有docker daemon,所以docker启动的容器支持--restart策略,但是podman不支持,如果在k8s中就不存在这个问题,我们可以设置pod的重启策略,在系统中我们可以采用编写systemd服务来完成自启动
docker需要使用root用户来创建容器,但是podman不需要
4,podman的安装
4.1,Arch Linux & Manjaro Linux
sudo pacman -S podman
4.2,Fedora,Centos
sudo yum -y install podman
4.3,Gentoo
sudo emerge app-emulation/libpod
4.4,MacOS
brew cask install podman
5,Podman CLI介绍
Podman CLI 里面87%的指令都和DOcker CLI 相同,官方给出了这么个例子alias docker=podman,所以说经常使用DOcker CLI的人使用podman上手非常快

运行一个容器
podman run -dt -p 80:80 --name nginx -v /data:/data -e NGINX_VERSION=1.16 nginx:1.16.0
列出当前所有的容器

podman ps -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19f105d5dc1e docker.io/library/nginx:1.16.0 nginx -g daemon o... 2 minutes ago Up 2 minutes ago 0.0.0.0:80->80/tcp nginx
查看一个镜像信息

podman inspect nginx | grep -i "ipaddress"

        "SecondaryIPAddresses": null,
        "IPAddress": "10.88.0.110",

查看容器运行的日志
podman logs nginx
查看运行中容器资源使用情况

podman top nginx

USER PID PPID %CPU ELAPSED TTY TIME COMMAND
root 1 0 0.000 5m26.420969043s pts/0 0s nginx: master process nginx -g daemon off;
nginx 6 1 0.000 5m26.421085502s pts/0 0s nginx: worker process

podman stats nginx

ID NAME CPU % MEM USAGE / LIMIT MEM % NET IO BLOCK IO PIDS
19f105d5dc1e nginx -- 2.036MB / 1.893GB 0.11% 978B / 10.55kB -- / -- 2
迁移容器
Podman 支持将容器从一台机器迁移到另一台机器。
首先,在源机器上对容器设置检查点,并将容器打包到指定位置。

$ sudo podman container checkpoint <container_id> -e /tmp/checkpoint.tar.gz
$ scp /tmp/checkpoint.tar.gz <destination_system>:/tmp
其次,在目标机器上使用源机器上传输过来的打包文件对容器进行恢复。

$ sudo podman container restore -i /tmp/checkpoint.tar.gz
podman的程序如何设置自启动
由于 Podman 不再使用守护进程管理服务,所以不能通过守护进程去实现自动重启容器的功能。那如果要实现开机自动重启容器,又该如何实现呢?
其实方法很简单,现在大多数系统都已经采用 Systemd 作为守护进程管理工具。这里我们就可以使用 Systemd 来实现 Podman 开机重启容器,这里我们以刚才启动的nginx为例。
建立一个 Systemd 服务配置文件。

$ vim /etc/systemd/system/nginx_podman.service

[Unit]
Description=Podman Nginx Service
After=network.target
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/podman start -a nginx
ExecStop=/usr/bin/podman stop -t 10 nginx
Restart=always

[Install]
WantedBy=multi-user.target
接下来,启用这个 Systemd 服务

$ sudo systemctl daemon-reload
$ sudo systemctl enable nginx_podman.service
$ sudo systemctl start nginx_podman.service
之后每次系统重启后 Systemd 都会自动启动这个服务所对应的容器,容器死亡之后也会启动这个容器,我们可以用下面的例子做测试
打一个sleep 30的docker包,这个容器运行起来一次只能坚持30s

$ vim Dockerfile
FROM busybox:latest
CMD ["sh","-c","sleep 30"]
然后按照上述方式设置启动自启动

演示下Podman 下启动的容器为Podman的子进程
我们刚才启动了一个nginx的podman 现在我们来看一下他的进程

ps -ef | grep [n]ginx

root 19368 19359 0 11:38 pts/0 00:00:00 nginx: master process nginx -g daemon off;
101 19381 19368 0 11:38 pts/0 00:00:00 nginx: worker process
然后查看这个nginx的父进程是那个

ps -ef | grep 19359

root 19359 1 0 11:38 ? 00:00:00 /usr/libexec/podman/conmon 。。。。
所以验证了我上面的说法