podman容器

podman容器

1 容器概念

1.1 容器技术介绍

  软件应用通常依赖于运行时环境(runtimeenvironment)提供的系统库、配置文件或服务。传统上,软件应用的运行时环境安装在物理主机或虚拟机上运行的操作系统中。然后,管理员在操作系统上安装应用依赖项

  在红帽企业Linux中,诸如RPM等打包系统可协助管理员管理应用依赖项。安装httpd软件包时,RPM系统会确保同时安装该软件包的正确库和其他依赖项

​ 传统方式部署软件应用的主要弊端是这些依赖项会受到运行时环境的束缚,应用需要的支持软件的版本可能比操作系统提供的软件更旧或更新。同一系统上的两个应用可能需要同一软件互不兼容的不同版本

​ 解决这些冲突的方式之一是将应用打包并作为容器进行部署
​ 容器是由一个或多个与系统其余部分隔离的进程组成的集合,软件容器是打包应用以简化其部署和管理的一种方式
​ 以实体集装箱为例。集装箱是打包和装运货物的标准方式。它作为一个箱子进行标记、装载、卸载,以及从一个位置运输到另一个位置。集装箱中的内容与其他集装箱的内容隔离,因此互不影响。这些基本原则也适用于软件容器

1.2 容器的核心技术

红帽企业Linux通过运用以下核心技术来支持容器:

1.用于资源管理的控制组(cgroups)

2.用于进程隔离的命名空间

3.加强安全边界的SELinux和Seccomp安全计算模式

1.3 容器和虚拟机的差异

1.容器提供许多与虚拟机相同的益处,如安全、存储和网络隔离等
2.这两种技术都将其应用库和运行时资源与主机操作系统或虚拟机监控程序隔离开,反之亦

3.容器和虚拟机以不同的方式与硬件和底层操作系统交互

** 4.虚拟机具有以下特征:

** 使多个操作系统能够同时在一个硬件平台上运行

** 使用虚拟机监控程序将硬件分为多个虚拟硬件系统

** 需要一个完整的操作系统环境来支持该应用

** 5.容器具有以下特征:

** 直接在操作系统上运行,从而跨系统上的所有容器共享资源

** 共享主机的内核,但它将应用进程与系统其余部分隔离开来

** 与虚拟机相比,它需要的硬件资源要少得多,因此容器的启动速度也更快

** 包括所有依赖项,如系统和编程依赖项,以及配置设置

1.4 Rootless和Rootful容器

在容器主机上,由特权用户运行的容器称为Rootful容器、由非特权用户运行的容器称为Rootless容器

**
Rootless容器不允许使用通常为特权用户保留的系统资源,例如访问受限目录,或在受限端口(1024以下的端口)上发布网络服务。此功能可防止潜在攻击者获取容器主机上的root特权

**
如有必要,可使用root用户身份直接运行容器,但如果有漏洞允许攻击者破坏容器,这样做会削弱系统的安全性

1.5 设计基于容器的架构

容器是重复利用托管应用并使其可以移植的有效方式

**
容器可以轻松地从一个环境迁移到另一个环境,如从开发环境迁移到生产环境

** 可以保存一个容器的多个版本,并根据需要快速访问每个版本

**
容器通常是临时的,可以将运行中容器所生成的数据永久保存到持久存储中,但容器本身通常会在需要时运行,然后停止并被删除,下次需要该特定容器时,将启动新的容器进程

**
可以在单个容器中安装含有多个服务的复杂软件应用。例如,Web服务器可能需要使用数据库和消息传递系统。不过,将一个容器用于多个服务会难以管理

**
更好的设计是在单独的容器中运行每个组件、Web服务器、数据库和消息传递系统。这样,更新和维护单个应用组件不会影响其他组件或应用堆栈

2 容器镜像和注册表

运行容器必须使用容器镜像:

**
容器镜像是包含编码步骤的静态文件,它充当创建容器的蓝图。容器镜像打包应用及其所有依赖项,如系统库、编程语言运行时和库以及其他配置设置**

**
容器镜像根据规范构建,如开放容器项目(OCI)镜像格式规范。这些规范定义容器镜像的格式,以及镜像支持的容器主机操作系统和硬件架构的元数据

**
容器注册表是用于存储和检索容器镜像的存储库。开发人员将容器镜像推送或上传到容器注册表中。可以从注册表中将这些容器镜像拉取或下载到本地系统,以用于运行容器

** 可使用包含第三方镜像的公共注册表,也可使用贵组织控制的私有注册表

容器镜像来源很重要。和任何其他软件包一样,必须知道是否可以信任容器镜像中的代码。对于是否及如何提供、评估和测试提交给它们的容器镜像,不同的注册表具有不同的策略

**
红帽通过两个主容器注册表分发认证容器镜像,可以使用红帽登录凭据来访问这两个注册表:

** 1.utility.redhat.io: 适用于基于官方红帽产品的容器

** 2.utilityconnect.redhat,com:适用于基于第三方产品的容器

**
3.红帽容器目录(https://access.redhat.com/containers)提供了一个基于Web的界面,通过它可以搜索这些注册表中的认证内容。

**
4.培训环境中添加了红帽Quay,官方为收费版,培训环境中为免费版utility.lab.example.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 环境中使用的镜像仓库浏览器访问为:
https://utility 账号是:admin 密码:redhat321
# 镜像对应的地址为:utility.lab.example.com

[kiosk@foundation0 ~]$ cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
### rht-vm-hosts file listing the entries to be appended to /etc/hosts

172.25.250.254 bastion.lab.example.com bastion
172.25.250.10 servera.lab.example.com servera
172.25.250.11 serverb.lab.example.com serverb
172.25.250.220 utility.lab.example.com utility
172.25.250.9 workstation.lab.example.com workstation
# 登录servera请使用ssh方式,不要使用su切换。
# 安装容器:
[root@foundation0 ~]# ssh root@servera
[root@servera ~]# ssh student@localhost
[student@servera ~]$ sudo dnf -y install container-tools # 安装podman容器
[student@servera ~]$ podman --version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 登录容器:
# 需要红帽开发人员账户才能从红帽注册表下载镜像。可以使用podman login命令对注册表进行身份验证。如果不向podman login命令提供注册表URL,它会向默认配置的注册表进行身份验证
$ podman login --help

# 登录方法一(交互):
$ podman login utility.lab.example.com
Username: admin
Password: redhat321
Login Succeeded!

# 登录方法二(非交互): # 推荐
$ podman login utility.lab.example.com -u admin -p redhat321 # 生产环境中是有https验证的
Login Succeeded!

[student@servera ~]$ podman login -u admin -p redhat321 utility.lab.example.com
Error: authenticating creds for "utility.lab.example.com": pinging container registry utility.lab.example.com: Get "https://utility.lab.example.com/v2/": tls: failed to verify certificate: x509: certificate is not valid for any names, but wanted to match utility.lab.example.com

# 如果出现以上报错,是要求https验证,需要通过选项--tls-verify进行手动关闭
# podman login utility.lab.example.com -u admin -p redhat321 --tls-verify=false

[student@servera ~]$ podman login --help
Description:
Log in to a container registry on a specified server.
Usage:
podman login [options] [REGISTRY]
Examples:
podman login quay.io
podman login --username ... --password ... quay.io
podman login --authfile dir/auth.json quay.io
Options:
--authfile string path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override
--cert-dir string use certificates at the specified path to access the registry
--get-login Return the current login user for the registry
-p, --password string Password for registry
--password-stdin Take the password from stdin
--tls-verify Require HTTPS and verify certificates when contacting registries
-u, --username string Username for registry
-v, --verbose Write more detailed information to stdout

[student@servera ~]$ podman login -u admin -p redhat321 utility.lab.example.com --tls-verify=false
Login Succeeded!

# 登录方法三(非交互):
# 使用podman login命令的--username和--password-sdtin选项,指定用于登录注册表的用户和密码
# --password-stdin选项从stdin读取密码
# 红帽建议不要使用--password选项直接提供密码,因为此选项会将密码存储在日志文件中
$ echo redhat321 | podman login -u admin --password-stdin utility.lab.example.com
Login Succeeded!

要验证您是否已登录到某一注册表,请使用 podman login命令
–get-login选项。及退出登录podman logout

1
2
3
4
5
6
7
8
$ podman login --get-login   # 查看登录的用户
admin

[student@servera ~]$ podman login utility.lab.example.com --get-login # 指定仓库地址,查看登录用户
admin

[student@servera ~]$ podman logout utility.lab.example.com # 登出
Removed login credentials for utility.lab.example.com

2.1 配置容器注册表

容器注册表的默认配置文件是 /etc/containers/registries.conf 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
[student@servera ~]$ sudo vim /etc/containers/registries.conf
[sudo] password for student: student

#第22行 指定可搜索的镜像仓库地址,如果使用完全合格域名,此处可以留空
unqualified-search-registries = ["utility.lab.example.com","registry.access.redhat.com","registry.redhat.io","docker.io"]

#第24行
[[utility]] 解除注释开启以下功能

#第37行
insecure = true #false/true 开启https安全验证/关闭安全验证

#第40行
blocked = false #需要过滤掉的镜像仓库地址

#第56行
location = "utility.lab.example.com" 指定容器注册表位置

$注意
~/.config/containers/registries.conf目录设置会覆盖/etc/containers/registries.conf

推荐:
【student】
$ mkdir -p ~/.config/containers
$ cp /etc/containers/registries.conf ~/.config/containers/registries.conf
$ vim ~/.config/containers/registries.conf
unqualified-search-registries = ["utility.lab.example.com"]
[[utility]]
insecure = true
blocked = false
location = "utility.lab.example.com"

# 登录容器注册表
[student@servera ~]$ podman login -u admin -p redhat321 utility.lab.example.com
Login Succeeded!

# 根据仓库地址搜索镜像
[student@servera ~]$ podman search utility.lab.example.com/
NAME DESCRIPTION
utility.lab.example.com/rhel8/mariadb-103
utility.lab.example.com/rhel9/mariadb-105
utility.lab.example.com/rhel9/httpd-24
utility.lab.example.com/library/nginx
utility.lab.example.com/ubi7/ubi
utility.lab.example.com/ubi9/ubi
utility.lab.example.com/ubi8/ubi
utility.lab.example.com/ubi9/python-312
utility.lab.example.com/rhel9/php-82

注意:如果只访问本地仓库,unqualified-search-registries = ["utility.lab.example.com"]默认即可,但要需要访问外网,需要用root用户修改vim /etc/resolv.conf文件内容添加nameserver 8.8.8.8 优先解析。
阿里容器
i2kldsde.mirror.aliyuncs.com

2.2 使用容器文件构建容器镜像


容器文件是一种文本文件,内含用于构建容器镜像的指令。

**
容器文件通常具有定义其文件和目录所在路径或URL的上下文。生成的容器镜像由只读层组成,每一层代表容器文件中的一条指令。**

**
以下输出是一个容器文件示例,它使用utility.access.redhat.com注册表中的UBI镜像,安装
python3 软件包,并将 hello 字符串打印到控制台。**

1
2
3
4
$ cat Containerfile
FROM utility.access.redhat.com/ubi8/ubi:latest
RUN dnf install -y python3
CMD["/bin/bash""-c""echo hello"]

创建容器文件及其使用说明超出了本课程的范畴。有关容器文件的更多信息,请参阅DO180课程。

2.3 规模化容器管理


新应用越来越多地使用容器来实施功能组件。这些容器提供应用的其他部分使用的服务。组织管理越来越多的容器,可能很快就会不堪重负。

在生产中大规模部署容器需要一个能够应对以下挑战的环境:

  • 平台必须确保提供必要服务的容器的可用性。
  • 环境必须通过增加或减少运行中的容器实例,并对流量进行负载平衡,从而应对应用的使用高峰。
  • 平台必须检测容器或主机的故障,并相应地作出反应。
  • 开发人员可能需要自动工作流,以便透明、安全地向客户交付新的应用版本。

Kubernetes是一项编排服务,可以使在容器主机集群中部署、管理和扩展基于容器的应用变得更加轻而易举。Kubernetes
通过负载平衡器将流量重定向到容器,以便您可以扩展提供服务的容器数量。Kubernetes
还支持用户定义的健康检查,以便监控您的容器,并在容器出现故障时将其重新启动。

红帽提供了一个名为红帽OpenShift 的kubernetes
发行版。红帽OpenShift是基于
Kubernetes基础架构构建的一组模块化组件和服务。它为开发人员提供的额外功能包括基于
Web
的远程管理、多租户、监控与审计、高级安全功能、应用生命周期管理和自助服务实例等。

红帽OpenShift不在本课程讨论范围之内,但您可以通过https://www.openshift.com了解更多相关信息。

3 部署容器

3.1Podman实用程序


**
Podman是来自container-tools元数据包的全功能容器引警,用于管理开放容器计划(0C)容器和镜像

**
podman实用程序的运作不使用守护进程,因此开发人员无需系统上的特权用户帐户来启动或停止容器

**
Podman提供多个子命令来与容器和镜像交互。以下列表显示了本节中使用的子命令:

** Podman的命令:

命令 描述


podman build 使用容器文件构建容器镜像
podman run 在新容器中运行命令
podman images 列出本地存储中的镜像
podman ps 打印有关容器的信息
podman inspect 显示容器、镜像、卷、网络或容器集的配置
podman pull 从注册表下载镜像
podman cp 在容器和本地文件系统之间复制文件或目录
podman exec 在运行中的容器内执行命令
podman rm 删除一个或多个容器
podman rmi 删除一个或多个本地存储的镜像
podman search 在注册表中搜索镜像

有关各个子命令使用帮助手册的更多信息,请将子命令附加到podman命令,并用连字符将两者分隔。例如,podman-build帮助手册介绍了podman
build子命令的用法。

** 为阐述本课中的主题,请想象以下情景:**

** 作为系统管理员,您被委任了一项任务,使用
python-38软件包运行基于名为python38的RHEL8UBI容器镜像的容器。您还有一项任务,从容器文件创建容器镜像,并从该容器镜像运行名为python36的容器。使用容器文件创建的容器镜像必须具有python36:1.0标签。识别两个容器之间的差异。另外,请确保容器中安装的python软件包与本地计算机中安装的Python版本不相冲突。**

3.2 安装容器实用工具


container-tools 软件包包含与容器和容器镜像交互所需的实用程序

** 若要在系统上下载、运行和比较容器,请使用dnf
install命令来安装container-tools 元软件包**

** 使用dnf info命令查看 container-tools 软件包的版本和内容**

1
2
3
[student@servera ~]$ sudo dnf -y install container-tools    # 安装podman容器 
[student@servera ~]$ podman --version
[student@servera ~]$ dnf info container-tools

container-tools元数据包提供所需的podman和skope
实用程序,用于完成分配的任务

3.3 从注册表下载容器镜像文件


1.确保podman实用程序已配置为从utility.lab.example.com注册表搜索和下载容器

** 2.podman
info命令显示podman实用程序的配置信息,包括其配置的注册表**

1
[student@servera ~]$ podman info

podman
search命令使用registries.conf文件中指定的注册表列表搜索匹配的名称镜像。默认情况下,Podman在所有非限定搜索注册表中执行搜索

3.4 让servera联网方法


1
2
3
1、f0宿主机上ens192是第二块网卡,vmware上选择nat模式-选择‘已连接’,再获取IP,保证可以上网
2、在f0宿主机上执行ssh root@172.25.254.254 rht-config-nat
3、servera上ping www.baidu.com测试

使用podman
search命令,显示包含python-38软件包的已配置注册表的镜像列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 在注册表中搜索镜像
[student@servera ~]$ podman search utility.lab.example.com/
NAME DESCRIPTION
utility.lab.example.com/rhel8/mariadb-103
utility.lab.example.com/rhel9/mariadb-105
utility.lab.example.com/rhel9/httpd-24
utility.lab.example.com/library/nginx
utility.lab.example.com/ubi7/ubi
utility.lab.example.com/ubi9/ubi
utility.lab.example.com/ubi8/ubi
utility.lab.example.com/ubi9/python-312
utility.lab.example.com/rhel9/php-82

# 从注册表中下载镜像
[student@servera ~]$ podman pull utility.lab.example.com/ubi7/ubi
[student@servera ~]$ podman pull utility.lab.example.com/rhel8/mariadb-103

# 列出本地存储中的镜像
[student@servera ~]$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
utility.lab.example.com/ubi7/ubi latest 87dd8ec61bbc 4 years ago 215 MB
utility.lab.example.com/rhel8/mariadb-103 latest 11a47e0fbed0 4 years ago 572 MB

# 镜像信息注解:
REPOSITORY :仓库地址
TAG :标记,latest最近版本
IMAGE ID :镜像ID,ID号唯一,保证镜像唯一性
CREATED :创建时间;
SIZE :镜像大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
练习:
以普通用户身份远程登录系统
ssh student@localhost
1.登录容器镜像仓库服务器utility.lab.example.com
$ sudo vim /etc/containers/registries.conf
$ podman login -u admin -p redhat321 utility.lab.example.com

2.搜索镜像服务器仓库utility.lab.example.com所有镜像
$ podman search utility.lab.example.com/

3.下载rhel7、8、9的ubi系统到本地
$ podman pull utility.lab.example.com/ubi7/ubi
$ podman pull utility.lab.example.com/ubi8/ubi
$ podman pull utility.lab.example.com/ubi9/ubi

4.查看本地镜像
$ podman images

5.通过rhel7的ubi运行一个交互式容器,然后退出
$ podman run -ti utility.lab.example.com/ubi7/ubi
$ exit #或者按ctrl+d

6.通过rhel8的ubi运行一个长期挂载到后台的容器
$ podman run -di utility.lab.example.com/ubi8/ubi

7.通过rhel9的ubi运行一个长期挂载到后台的容器,并且指定容器名称为rhel9
[student@servera ~]$ podman run -di --name rhel9 utility.lab.example.com/ubi9/ubi
b4b889bb5964acae14a6e7c2d138e8cc56e9ebf94edf9653f5341522ffc9ec76

8.进入rhel9容器进行交互
[student@servera ~]$ podman exec -ti rhel9 /bin/bash
[root@b4b889bb5964 /]# cat /etc/redhat-release
Red Hat Enterprise Linux release 9.3 (Plow)

9.使用httpd-24镜像运行一个web服务。并且访问测试
#本地测试,如果在其他主机上访问servera容器web服务,需要在servera防火墙允许主机端口8090

10.使用nginx镜像运行一个web服务。并且访问测试,nginx容器内端口为80

11.尝试运行一个mariadb-105的容器

3.5 从容器文件创建容器镜像


** 您获得了以下容器文件,用于在 python36-app目录中创建容器镜像**

1
2
3
4
5
$ cat Containerfile 
FROM utility.access.redhat.com/ubi8/ubi:latest
RUN dnf install -y python36
CMD["/bin/bash""-c""sleep infinity"]
# 此容器文件是教材中例子默认报错,可以使用下面的容器文件
1
2
3
4
5
[student@servera ~]$ vim Containerfile
FROM utility.lab.example.com/ubi9/ubi:latest
RUN echo -e '[rhel-9.3-for-x86_64-baseos-rpms]\nbaseurl = http://content.example.com/rhel9.3/x86_64/dvd/BaseOS\nenabled = true\ngpgcheck = false\nname = Red Hat Enterprise Linux 9.3 BaseOS (dvd)\n[rhel-9.3-for-x86_64-appstream-rpms]\nbaseurl = http://content.example.com/rhel9.3/x86_64/dvd/AppStream\nenabled = true\ngpgcheck = false\nname = Red Hat Enterprise Linux 9.3 Appstream (dvd)'>/etc/yum.repos.d/rhel_dvd.repo
RUN yum install --disablerepo=* --enablerepo=rhel-9.3-for-x86_64-baseos-rpms --enablerepo=rhel-9.3-for-x86_64-appstream-rpms -y python3
CMD ["/bin/bash", "-c", "sleep infinity"]
  • 以上容器文件使用utility.lab.example.com/ubi9/ubi:latest镜像作为基础镜像。容器文件而后将安装python36
    软件包,并运行sleep infinity bash命令来防止容器退出
  • 通常,容器运行一个进程,然后在该进程完成后退出。sleep
    nfinity命令可防止容器退出因为该进程永远不会完成。然后可以在容器内进行测试、开发和调试
  • 在检查容器文件后,可以使用podman build命令来构建镜像。podman
    build命令的语法如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ podman build -t NAME:TAG  DIR
[student@servera ~]$ podman build -t rhel7:2.0 .
[student@servera ~]$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/rhel7 2.0 98d0b6385a00 40 seconds ago 238 MB
utility.lab.example.com/ubi9/ubi latest 8d2a8803cfca 12 months ago 219 MB
utility.lab.example.com/ubi7/ubi latest 87dd8ec61bbc 4 years ago 215 MB
utility.lab.example.com/rhel8/mariadb-103 latest 11a47e0fbed0 4 years ago 572 MB
# 以上输出的最后一行显示了容器镜像ID。大多数Podman命令使用容器镜像ID的前12个字符来指代容器镜像,可以将此短ID或者容器或容器镜像的名称,作为大多数Podman命令的参数
# 注解:
-t,--tag name 生成镜像的名称
NAME:新镜像的名称
标签:新镜像的标签。如果未指定标签,则镜像自动标记为latest
DIR:工作目录路径。容器文件必须位于工作目录中。如果工作目录是当前目录,则可以用点(.)来指定它。使用-f标志指定与当前目录不同的目录

使用podman
inspect命令来查看容器镜像的低级别信息,并验证其内容是否符合容器要求:

1
2
(podman pull  先下载后再使用inspect)
[student@servera ~]$ podman inspect localhost/rhel7:2.0

** podman
inspect命令的输出显示reqistry.access.redhat.com/ubi8/ubi:latest基础镜像、用于安装
python36 软件包的 dnf命令,以及在运行时执行以防止容器退出的sleep
infinity bash命令**

4 运行容器


现在,您已拥有所需的容器镜像,可以使用它们来运行容器。容器可以处于以下状态之一!
Created 已创建好但尚未启动的容器。 运行中 与其进程一起运行的容器。
已停止 其进程已停止的容器。 Paused 其进程已暂停的容器。不支持 Rootless
容器。 Deleted 其进程处于已死状态的容器。

**podman ps命令列出系统上正在运行的容器。使用podman ps
-a来命令查看计算机中的所有容器 (已创建、已停止、已暂停或正在运行)。 **

** 可使用podman
create命令来创建容器,以便稍后运行。若要创建容器,请使用容器localhost/rhel7:2.0镜像的ID。也可以使用–name
选项设置名称来标识容器。此命令的输出是容器的长ID,如果不指定–name选项,会自动生成一个容器名称。**

1
$ podman create --name python36 dd6ca291f097

然后,可使用podman ps和podman ps
-a命令来验证容器是否已创建但尚未启动。您可以查看有关 python36
容器的信息,如容器的短ID、名称和状态,容器在启动时运行的命令,以及用于创建容器的镜像

1
2
$ podman ps 
$ podman ps -a

现在已验证容器已正确创建,决定启动容器,所以要运行 podman
start命令。可以使用名称或容器ID来启动容器。此命令的输出是容器的名称。

1
2
$ podman start python36
$ podman ps

4.1 从远程存储库运行容器

您可使用podman run命令,在一个步骤中创建并运行容器。podman run
命令在容器内运行进程,此进程将启动新容器。

您可以使用podman run
命令-d选项以分离模式运行容器,这将在后台运行容器,而不是在会话的前台运行

在python36容器的示例中,您不需要提供容器运行所提的命令,原因是为该容器创建镜像的容器文件中已提供了sleep
infinity 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
podman run 
-t 终端
-i 交互
-d 放在后台
--name 指定容器的名称,如果不指定,会自从产生名称

[student@servera ~]$ podman run -it utility.lab.example.com/ubi7/ubi
[student@servera ~]$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
537b1f1fbb6d utility.lab.example.com/ubi7/ubi:latest /bin/bash About a minute ago Up About a minute objective_antonelli

#实验前可以提前下载镜像至本地
#podman search utility.lab.example.com/
#podman pull utility.lab.example.com/ubi8/ubi
#podman images
[student@servera ~]$ podman run -it --name rhel9 utility.lab.example.com/ubi9/ubi
[student@servera ~]$ podman run -di --name rhel9-1 utility.lab.example.com/ubi9/ubi
[student@servera ~]$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
537b1f1fbb6d utility.lab.example.com/ubi7/ubi:latest /bin/bash 5 minutes ago Up 5 minutes objective_antonelli
fb45a06e2271 utility.lab.example.com/ubi9/ubi:latest /bin/bash 56 seconds ago Up 57 seconds rhel9-1

# ctrl+d退出后再查看容器的状态
[student@servera ~]$ podman exec -ti rhel9-1 /bin/bash
[student@servera ~]$ podman run -d --name rhel9-2 utility.lab.example.com/ubi9/ubi sleep infinity
[student@servera ~]$ podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fb45a06e2271 utility.lab.example.com/ubi9/ubi:latest /bin/bash 5 minutes ago Up 5 minutes rhel9-1
c4f7f4d72747 utility.lab.example.com/ubi9/ubi:latest sleep infinity 34 seconds ago Up 34 seconds rhel9-2

4.2 容器中的环境隔离

容器隔离应用的环境

**
每个容器都有自己的文件系统、网络和进程。查看ps命令的输出,并在主机和运行中容器之间进行比较,就会注意到隔离功能**

** 在本地计算机上运行ps
-ax命令,该命令将返回具有许多进程的预期结果**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[student@servera ~]$ ps -ax
[student@servera ~]$ podman run -di --name python36-db utility.lab.example.com/rhel8/mariadb-103
f4c3d26df7bd3614e6b4954ae6ed485046128afc89a95cba20c834b2ba0327ff
[student@servera ~]$ podman run -di --name python38 utility.lab.example.com/ubi9/ubi
8e7870d50daa32c768c4301911364285a659a5383b4cdadc96b8d3b7ff411c2d
[student@servera ~]$ podman run -id --name rhel7 utility.lab.example.com/ubi7/ubi
2d4b030f4141656f8b4b74d419c2b8de52669927a261953c92409bc995b35922
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
850618efbece utility.lab.example.com/ubi9/ubi:latest /bin/bash 9 hours ago Exited (0) 9 hours ago rhel9
fb45a06e2271 utility.lab.example.com/ubi9/ubi:latest /bin/bash 9 hours ago Up 9 hours rhel9-1
c4f7f4d72747 utility.lab.example.com/ubi9/ubi:latest sleep infinity 9 hours ago Up 9 hours rhel9-2
f4c3d26df7bd utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld 22 minutes ago Exited (1) 22 minutes ago python36-db
8e7870d50daa utility.lab.example.com/ubi9/ubi:latest /bin/bash 16 minutes ago Up 16 minutes python38
2d4b030f4141 utility.lab.example.com/ubi7/ubi:latest /bin/bash 12 seconds ago Up 13 seconds rhel7

podman exec命令可在运行中的容器内执行命令

**
该命令取容器的名称或ID作为第一个参数,并将下列参数作为要在容器内运行的命令**

** 使用podman exec命令查看rhel7容器中正在运行的进程。ps
aux命令的输出看起来有所不同,因为它运行与本地计算机不同的进程**

** 使用sh -c命令来封装要在容器中执行的命令**

** ps ax >
/tmp/process-data.log命令被解释为要在容器中执行的命令。如果不封装命令,则Podman可能会将大于号字符(>)解释为podman命令的一部分,而不是podman
exec选项的参数**

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#使用练习环境中的utility.lab.example.com/ubi7/ubi镜像,执行一些其他命令,比如ls /等
[student@servera ~]$ podman exec rhel7 ps -ax
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/bash
2 ? R 0:00 ps -ax
[student@servera ~]$ podman exec rhel7 sh -c 'ps -a > /tmp/process_data.log'
[student@servera ~]$ podman exec rhel7 sh -c 'echo China > /test.txt'
[student@servera ~]$ podman exec -ti rhel7 /bin/bash
[root@2d4b030f4141 /]# ls /
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys test.txt tmp usr var
[root@2d4b030f4141 /]# cat test.txt
China
[root@2d4b030f4141 /]# cat /tmp/process_data.log
PID TTY TIME CMD
[root@2d4b030f4141 /]# exit
exit
[student@servera ~]$

** 将主机系统上安装的python版本与容器上安装的python版本进行比较:**

1
2
3
4
5
6
[student@servera ~]$ podman exec rhel9-1 python3 --version
Python 3.9.18
[student@servera ~]$ python3 --version
Python 3.9.18
[student@servera ~]$ podman exec python38 python3 --version
Python 3.9.18

4.3 容器中的文件系统隔离


开发人员可以使用文件系统隔离功能,为不同版本的编程语言编写和测试应用,无需使用多个物理机或虚拟机。

您将在终端上的/tmp目录中创建一个显示hello world的简单bash 脚本。

1
2
3
4
5
6
7
8
[student@servera ~]$ echo "echo Hello China!" > /tmp/hello.sh
[student@servera tmp]$ cd /tmp;ll
total 8
-rw-r--r--. 1 student student 18 Mar 3 21:24 hello.sh
-rw-r--r--. 1 student student 30 Mar 3 21:01 process_data.log
drwx------. 3 root root 17 Mar 3 08:28 systemd-private-3f251aad6ce74edb86dddf89d56e8aed-chronyd.service-c0Zzhi
drwx------. 3 root root 17 Mar 3 08:28 systemd-private-3f251aad6ce74edb86dddf89d56e8aed-dbus-broker.service-QaiP7N
drwx------. 3 root root 17 Mar 3 08:28 systemd-private-3f251aad6ce74edb86dddf89d56e8aed-systemd-logind.service-iM6Zeq

/tmp/hello.sh
文件位于主机计算机上,而不存在于容器内的文件系统上。如果尝试使用podman
exec来执行脚本,则会出现错误,因为容器中不存在/tmp/hello.sh脚本

1
2
3
4
5
6
7
8
9
10
11
12
[student@servera tmp]$ stat /tmp/hello.sh
File: /tmp/hello.sh
Size: 18 Blocks: 8 IO Block: 4096 regular file
Device: fc04h/64516d Inode: 18159864 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ student) Gid: ( 1000/ student)
Context: unconfined_u:object_r:user_tmp_t:s0
Access: 2025-03-03 21:24:33.865950531 -0500
Modify: 2025-03-03 21:24:33.865950531 -0500
Change: 2025-03-03 21:24:33.865950531 -0500
Birth: 2025-03-03 21:24:33.865950531 -0500
[student@servera tmp]$ podman exec rhel7 stat /tmp/hello.sh
stat: cannot stat '/tmp/hello.sh': No such file or directory

podman cp命令在主机和容器文件系统之间复制文件和文件夹。您可以使用
podman cp 命令将/tmp/hello.sh文件复制到python38容器:

1
2
3
4
5
6
7
8
9
10
[student@servera ~]$ podman cp /tmp/hello.sh rhel7:/tmp/hello.sh
[student@servera ~]$ podman exec rhel7 stat /tmp/hello.sh
File: '/tmp/hello.sh'
Size: 18 Blocks: 8 IO Block: 4096 regular file
Device: 58h/88d Inode: 10261231 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2025-03-04 02:24:34.000000000 +0000
Modify: 2025-03-04 02:24:34.000000000 +0000
Change: 2025-03-04 02:28:24.720471184 +0000
Birth: -

脚本复制到容器文件系统后,即可从容器内执行:

1
2
[student@servera ~]$ podman exec rhel7 bash /tmp/hello.sh
Hello China!

4.4 删除容器和镜像


** 使用podman rm和podman rmi命令删除容器和镜像**

** 删除容器镜像之前,必须先从该镜像移除任何现有的运行中容器**

** 删除python38容器及其相关镜像:**

1
2
3
4
5
6
7
8
9
10
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
850618efbece utility.lab.example.com/ubi9/ubi:latest /bin/bash 10 hours ago Exited (0) 10 hours ago rhel9
fb45a06e2271 utility.lab.example.com/ubi9/ubi:latest /bin/bash 10 hours ago Up 10 hours rhel9-1
c4f7f4d72747 utility.lab.example.com/ubi9/ubi:latest sleep infinity 10 hours ago Up 10 hours rhel9-2
f4c3d26df7bd utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld About an hour ago Exited (1) About an hour ago python36-db
8e7870d50daa utility.lab.example.com/ubi9/ubi:latest /bin/bash 55 minutes ago Up 55 minutes python38
2d4b030f4141 utility.lab.example.com/ubi7/ubi:latest /bin/bash 39 minutes ago Up 39 minutes rhel7
student@servera ~]$ podman rmi utility.lab.example.com/ubi9/ubi:latest
Error: image used by 8e7870d50daa32c768c4301911364285a659a5383b4cdadc96b8d3b7ff411c2d: image is in use by a container: consider listing external containers and force-removing image

必须先停止容器,然后才能删除它。若要停止容器,请使用podman
stop命令

1
2
[student@servera ~]$ podman stop python38
python38

停止容器后,使用 podman rm 命令来删除容器

1
2
3
4
5
6
7
8
9
10
[student@servera ~]$ podman rm --help
[student@servera ~]$ podman rm python38
python38
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
850618efbece utility.lab.example.com/ubi9/ubi:latest /bin/bash 10 hours ago Exited (0) 10 hours ago rhel9
fb45a06e2271 utility.lab.example.com/ubi9/ubi:latest /bin/bash 10 hours ago Up 10 hours rhel9-1
c4f7f4d72747 utility.lab.example.com/ubi9/ubi:latest sleep infinity 10 hours ago Up 10 hours rhel9-2
f4c3d26df7bd utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld About an hour ago Exited (1) About an hour ago python36-db
2d4b030f4141 utility.lab.example.com/ubi7/ubi:latest /bin/bash 45 minutes ago Up 45 minutes rhel7

** 当容器不再存在时,可以使用podman rmi命令删除对应的镜像:**

1
2
3
4
5
6
7
8
9
10
11
12
13
[student@servera ~]$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/rhel7 2.0 98d0b6385a00 11 hours ago 238 MB
utility.lab.example.com/ubi9/ubi latest 8d2a8803cfca 12 months ago 219 MB
utility.lab.example.com/ubi7/ubi latest 87dd8ec61bbc 4 years ago 215 MB
utility.lab.example.com/rhel8/mariadb-103 latest 11a47e0fbed0 4 years ago 572 MB
[student@servera ~]$ podman rmi 8d2a8803cfca
Error: image used by c4f7f4d727471d590f6241cccf0be0b1ef2256cc43a710594642611fe6d0be47: image is in use by a container: consider listing external containers and force-removing image
[student@servera ~]$ podman rmi 98d0b6385a00
Untagged: localhost/rhel7:2.0
Deleted: 98d0b6385a005e09cfcee59a393cfce2fc46b56f09af6c4f87bd874f00966ed2
Deleted: 53c739e51f226903b6568038c9cf563de2007f756e0a0e86e5c00604cf474f3d
Deleted: 92b83aa1157f23b209f53480c6bbdf780c39490b37337bc4f4fcb1061b7c978

5 管理容器存储和网络资源

5.1 管理容器资源


可以使用容器来运行简单的进程,然后退出。

还可以配置容器以连续运行某一服务,如数据库服务器。如果您持续运行服务,您最终可能需要向容器添加更多资源,如持久存储或对其他网络的访问权限。

可以使用不同的策略为容器配置持久存储:

** 1.对于红帽OpenShift
等企业容器平台上的大型部署,您可以使用复杂的存储解决方案为容器提供存储,而无需了解底层基础架构**

**
2.对于单个容器主机上且无需扩展的小型部署,您可以通过在运行中的容器上创建要挂载的目录,从容器主机创建持久存储**

当Web服务器或数据库服务器等容器为容器主机外部的客户端提供内容时,必须为这些客户端设置通信通道,以访问容器的内容。

可以配置端口映射,以启用与容器的通信。通过端口映射,目的地为容器主机上端口的请求将被转发到容器内的端口。

设想必须执行以下任务:

  • 基于MariaDB,创建名为db01的容器化数据库。

  • 配置容器端口映射和主机防火墙,以允许端口3306/tcp 上的流量。

  • 配置db01容器,以使用具有适当SELinux 上下文的持久存储。

  • 添加适当的网络配置,以便client01容器可以使用DNS与db01容器通信。

5.2 容器的环境变量


容器镜像允许在创建时传递环境变量以自定义容器

可以使用环境变量为容器设置参数,以根据您的环境进行定制,无需创建自己的自定义镜像。通常,您不会修改容器镜像,因为这会向镜像添加层,或许更加难以维护。

使用podman run -d –name db01
utility.lab.example.com/rhel8/mariadb-103命令运行容器化数据库,但发现容器无法启动。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[student@servera ~]$ podman search utility.lab.example.com/
NAME DESCRIPTION
utility.lab.example.com/rhel8/mariadb-103
utility.lab.example.com/rhel9/mariadb-105
utility.lab.example.com/rhel9/httpd-24
utility.lab.example.com/library/nginx
utility.lab.example.com/ubi7/ubi
utility.lab.example.com/ubi9/ubi
utility.lab.example.com/ubi8/ubi
utility.lab.example.com/ubi9/python-312
utility.lab.example.com/rhel9/php-82

[student@servera ~]$ podman pull utility.lab.example.com/rhel8/mariadb-103

[student@servera ~]$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
utility.lab.example.com/ubi9/ubi latest 8d2a8803cfca 12 months ago 219 MB
utility.lab.example.com/ubi7/ubi latest 87dd8ec61bbc 4 years ago 215 MB
utility.lab.example.com/rhel8/mariadb-103 latest 11a47e0fbed0 4 years ago 572 MB
[student@servera ~]$ podman run -d --name db01 utility.lab.example.com/rhel8/mariadb-103
29decc6e48d62506e62e503a383943709138a8f789a32dd27d2fa1761bf3ea9f

# 发现容器无法启动
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
29decc6e48d6 utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld 2 minutes ago Exited (1) 2 minutes ago db01

使用podman container logs命令调查容器状态的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[student@servera ~]$ podman container logs db01
Warning: Can't detect memory limit from cgroups
Warning: Can't detect number of CPU cores from cgroups
Warning: Can't detect memory limit from cgroups
Warning: Can't detect number of CPU cores from cgroups
=> sourcing 20-validate-variables.sh ...
You must either specify the following environment variables:
MYSQL_USER (regex: '^[a-zA-Z0-9_]+$')
MYSQL_PASSWORD (regex: '^[a-zA-Z0-9_~!@#$%^&*()-=<>,.?;:|]+$')
MYSQL_DATABASE (regex: '^[a-zA-Z0-9_]+$')
Or the following environment variable:
MYSQL_ROOT_PASSWORD (regex: '^[a-zA-Z0-9_~!@#$%^&*()-=<>,.?;:|]+$')
Or both.
Optional Settings:
......
1
2
3
4
5
6
7
8
9
10
11
12
13
# 输出中的usage 标签提供了如何运行镜像的示例。url标签指向红帽容器目录中的一个Web页面,其中记录了环境变量以及有关如何使用容器镜像的其他信息。
# 此镜像的文档显示容器将3306端口用于数据库服务。文档中还显示了以下环境变量可用于配置数据库服务:
[student@servera ~]$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
utility.lab.example.com/ubi9/ubi latest 8d2a8803cfca 12 months ago 219 MB
utility.lab.example.com/ubi7/ubi latest 87dd8ec61bbc 4 years ago 215 MB
utility.lab.example.com/rhel8/mariadb-103 latest 11a47e0fbed0 4 years ago 572 MB
[student@servera ~]$ podman inspect utility.lab.example.com/rhel8/mariadb-103 | grep usage
"usage": "podman run -d -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db -p 3306:3306 rhel8/mariadb-103",
"usage": "podman run -d -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db -p 3306:3306 rhel8/mariadb-103",

$ skopeo inspect docker://utility.lab.example.com/rhel8/mariadb-105 | grep -B 1 Usage
#练习过程中可以将usage中提示的示例指令作为参考。

mariadb镜像的环境变量:

变量 描述


MYSQL_USER 要创建的MySQL帐户的用户名
MYSQL_PASSWORD 用户帐户的密码
MYSQL_DATABASE 数据库名称
MYSQL_ROOT_PASSWORD root用户的密码 (可选)

在检查了镜像的可用环境变量后,使用podman
run命令-e选项将环境变量传递给容器,并使用podman
ps命令来验证它是否正在运行。

1
2
3
4
5
6
7
8
9
10
11
12
13
[student@servera ~]$ podman rm -af
29decc6e48d62506e62e503a383943709138a8f789a32dd27d2fa1761bf3ea9f

[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

[student@servera ~]$ podman run -d --name db01 -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db utility.lab.example.com/rhel8/mariadb-103
41a62ff2efd7f268e52d6f6a0a9b503411824c5f4391b86dae5d3f01376cb896

# 容器启动成功
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
41a62ff2efd7 utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld 8 seconds ago Up 8 seconds db01

5.3 容器持久存储


** 默认情况下运行容器时,所有内容都使用基于容器的镜像**

**
鉴于容器镜像的寿命短,用户或应用写入的所有新数据在移除容器后都会丢失**

** 若要持久保留数据,可以将容器中的主机文件系统内容与–volume
(-v)选项搭配使用。在容器中使用此卷类型时,必须考虑文件系统级别的权限**

**
MariaDB容器镜像中,mysql用户必须拥有/var/lib/mysql目录,就如同MariaDB在主机上运行时一样**

**
打算挂载到容器中的目录必须具有mysql作为用户和组所有者(或mysql用户的UID/GID,如果主机上没有安装
MariaDB)**

** 如果以root 用户身份运行容器,则主机上的UID和GID
与容器内的UID和GID匹配**

** 可以使用podman
unshare命令在用户命名空间内运行命令。要获取用户命名空间的UID映射,请使用podman
unshare cat命令**

1
2
3
4
5
6
[student@servera ~]$ podman unshare cat /proc/self/uid_map
0 1000 1
1 100000 65536
[student@servera ~]$ podman unshare cat /proc/self/gid_map
0 1000 1
1 100000 65536

以上输出显示:

容器中的root用户 (UID和GID为0)映射到主机计算机上的用户(UID和GID为1000)

容器中的UID和GID1映射到主机计算机上的UID和GID 100000

1后的每个UID和GID以1增量递增。例如,容器内的UID和GID30映射到主机计算机上的UID和GID100029

可以使用podman
exec命令查看使用临时存储运行的容器内的mysql用户UID和GID:

1
2
[student@servera ~]$ podman exec -it db01 grep mysql /etc/passwd
mysql:x:27:27:MySQL Server:/var/lib/mysql:/sbin/nologin

将/home/user/db_data目录挂载到db01容器中,以在容器的/var/lib/mysql目录中提供持久存储

创建/home/user/db_data目录,并使用podman
unshare命令将27的用户命名空间UID和GID设置为该目录的所有者

1
2
3
4
5
6
7
8
9
10
[student@servera ~]$ mkdir /home/student/db_data
[student@servera ~]$ ll -d /home/student/db_data
drwxr-xr-x. 2 student student 6 Mar 4 00:43 /home/student/db_data

[student@servera ~]$ ll -d -n /home/student/db_data
drwxr-xr-x. 2 1000 1000 6 Mar 4 00:43 /home/student/db_data

[student@servera ~]$ podman unshare chown 27:27 /home/student/db_data/
[student@servera ~]$ ll -d /home/student/db_data
drwxr-xr-x. 2 100026 100026 6 Mar 4 00:43 /home/student/db_data

容器中的UID和GID 27映射到主机计算机上的UID和GID100026。可以使用ll
-d命令查看/home/student/db_data目录的所有权来验证映射。

现在已设置了正确的文件系统级权限,可使用podman run 命令-v选项来挂载目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[student@servera ~]$ podman rm -af
41a62ff2efd7f268e52d6f6a0a9b503411824c5f4391b86dae5d3f01376cb896

[student@servera ~]$ podman run -d --name db01 -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db -v /home/student/db_data/:/var/lib/mysql utility.lab.example.com/rhel8/mariadb-103
54279d1fff7f3b679dd9d4efb7bfa28a626f41f7d07bea50a445ec2392c8cb02

# db01容器未在运行
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
54279d1fff7f utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld 6 minutes ago Exited (1) 6 minutes ago db01

$ podman run -d --name db01 \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass \
-e MYSQL_DATABASE=db \
-e MYSQL_ROOT_PASSWORD=redhat \
-v /home/student/db_data/:/var/lib/mysql \
utility.lab.example.com/rhel8/mariadb-103

podman container logs命令显示/var/lib/mysql/data目录的权限错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[student@servera ~]$ podman container logs db01
Warning: Can't detect memory limit from cgroups
Warning: Can't detect number of CPU cores from cgroups
Warning: Can't detect memory limit from cgroups
Warning: Can't detect number of CPU cores from cgroups
=> sourcing 20-validate-variables.sh ...
=> sourcing 25-validate-replication-variables.sh ...
=> sourcing 30-base-config.sh ...
---> 05:58:29 Processing basic MySQL configuration files ...
=> sourcing 60-replication-config.sh ...
=> sourcing 70-s2i-config.sh ...
---> 05:58:29 Processing additional arbitrary MySQL configuration provided by s2i ...
=> sourcing 40-paas.cnf ...
=> sourcing 50-my-tuning.cnf ...
---> 05:58:29 Initializing database ...
---> 05:58:29 Running mysql_install_db ...
mkdir: cannot create directory '/var/lib/mysql/data': Permission denied
Fatal error Can't create database directory '/var/lib/mysql/data'
# 发生此错误的原因是,主机上/home/user/db\_data目录中设置的SELinux上下文不正确

5.4 容器存储的SELinux上下文


必须先设置container_file_t SELinux
上下文类型,然后才能将该目录作为持久存储挂载到容器

如果目录没有container_file_t SELinux 上下文,则容器无法访问该目录

可以将Z选项附加到podman
run命令-v选项的参数,以自动设置目录的SELinux上下文

当将/home/student/db_data
目录挂载为/var/lib/mysql目录的持久存储时,可以使用podman run -v
/home/student/db_data:/var/lib/mysql:Z 命令设置该目录的SELinux上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[student@servera ~]$ podman rm -af
54279d1fff7f3b679dd9d4efb7bfa28a626f41f7d07bea50a445ec2392c8cb02
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

[student@servera ~]$ podman run -d --name db01 -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db -v /home/student/db_data/:/var/lib/mysql:Z utility.lab.example.com/rhel8/mariadb-103
6195fc399b1f6ae8f5b9d3f436c02bb5d6b093b597949dc968b2a64f6e7d023c

[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6195fc399b1f utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld 23 seconds ago Up 24 seconds db01

[student@servera ~]$ ll -dZ /home/student/db_data/
drwxr-xr-x. 3 100026 100026 system_u:object_r:container_file_t:s0:c428,c988 36 Mar 4 01:26 /home/student/db_data/

$ podman run -d --name db01 \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass \
-e MYSQL_DATABASE=db \
-e MYSQL_ROOT_PASSWORD=redhat \
-v /home/student/db_data:/var/lib/mysql:Z \
utility.lab.example.com/rhel8/mariadb-103

5.5 分配端口映射到容器


要提供对容器的网络访问权限,客户端必须连接到容器主机上的端口,这些端口将网络流量传递到容器中的端口

将容器主机上的网络端口映射到容器中的端口时,容器将接收发送到主机网络端口的网络流量。

例如,可以将容器主机上的13306端口映射到容器上的3306端口,以便与
MariaDB容器通信。因此,发送到容器主机端口13306的流量将由容器中运行的MariaDB接收。

可以使用 podman run命令 -p
选项设置从容器主机上13306端口到db01容器上3306端口的端口映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[student@servera ~]$ podman rm -af
6195fc399b1f6ae8f5b9d3f436c02bb5d6b093b597949dc968b2a64f6e7d023c
[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

[student@servera ~]$ podman run -d --name db01 -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -e MYSQL_DATABASE=db -v /home/student/db_data/:/var/lib/mysql:Z -p 13306:3306 utility.lab.example.com/rhel8/mariadb-103
28202973f22d60ea4ccd096b8a321b30c7d57156a2c41d2502c1a0456c794ced

[student@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28202973f22d utility.lab.example.com/rhel8/mariadb-103:latest run-mysqld 20 seconds ago Up 20 seconds 0.0.0.0:13306->3306/tcp db01

$ podman run -d --name db01 \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass \
-e MYSQL_DATABASE=db \
-e MYSQL_ROOT_PASSWORD=redhat \
-p 13306:3306 \
-v /home/student/db_data:/var/lib/mysql:Z \
utility.lab.example.com/rhel8/mariadb-103

使用podman
port命令-a选项可显示正在使用的所有容器端口映射。还可以使用podmanport
db01命令显示 db01容器的映射端口。

1
2
3
4
[student@servera ~]$ podman port -a
28202973f22d 3306/tcp -> 0.0.0.0:13306
[student@servera ~]$ podman port db01
3306/tcp -> 0.0.0.0:13306

可以使用firewall-cmd命令允许端口13306流量传入容器主机,以便它可以重定向到容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Rootless(特权)容器无法打开主机上特权端口1024有 以下的端口。比如-p 80:8000 ,比必须使用root才可以对其进行调整
[student@servera ~]$ sudo firewall-cmd --permanent --add-port=13306/tcp
[sudo] password for student:
success
[student@servera ~]$ sudo firewall-cmd --reload
success
[student@servera ~]$ sudo firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports: 13306/tcp
protocols:
forward: yes
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
# firewall-cmd --add-port=13306/tcp --permanent
# firewall-cmd --reload

附加测试

通过主机mariadb数据库客户端软件访问容器数据库服务,需要安装mariadb软件后指定-u账号,-p密码(无空格),-h数据库服务器所在主机名或IP(不是容器名)及-P(大写)主机映射端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[kiosk@foundation0 ~]$ sudo dnf -y install mariadb
[kiosk@foundation0 ~]$ mysql -u user -ppass -h servera -P 13306 # -h 后面填写您当前实验的主机名或IP
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.3.17-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| db |
| information_schema |
| test |
+--------------------+
3 rows in set (0.012 sec)

MariaDB [(none)]> USE db;
Database changed
MariaDB [db]> SHOW TABLES;
Empty set (0.001 sec)

MariaDB [db]> exit
Bye

5.6 容器中的DNS配置

Podmanv4.0支持两种容器网络后端,即Netavark和CNI

自RHEL9起,系统默认使用Netavark。若要验证所用的网络后端,请运行以下podman
info命令。

将网络堆栈从 CNI 切换到 Netavark | Red Hat Product
Documentation

1
2
$ podman info --format {{.Host.NetworkBackend}}
netavark

主机上使用默认Podman网络的现有容器无法解析彼此的主机名,因为默认网络上未启用DNS。

使用podman network create命令创建一个支持DNS的网络。您可使用podman
network create命令创建名为db_net
的网络,并将子网指定为10.87.0.0/16,网关指定为10.87.0.1。

1
2
3
$ podman network create --gateway 10.87.0.1 --subnet 10.87.0.0/16 db_net

$ podman network ls #列出容器网络

如果不指定–gateway 或–subnet 选项,则会使用默认值创建它们。

podman network inspect 命令显示关于特定网络的信息。您可以使用podman
network inspect 命令验证网关和子网的设置是否正确,以及新的dbnet
网络是否启用了DNS。

1
$ podman network inspect db_net

您可以使用podman run命令–network选项将启用DNS的db_net
网络添加到新容器。您可以使用podman run命令–network选项创建连接到db_net
网络的db01和client01容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ podman run -d --name db01 \
--network db_net \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass \
-e MYSQL_DATABASE=db \
-e MYSQL_ROOT_PASSWORD=redhat \
-v /home/student/db_data:/var/lib/mysql:Z \
-p 13306:3306 \
utility.lab.example.com/rhel8/mariadb-105


$ podman run -d --name client01 \
--network db_net \
-v /etc/yum.repos.d:/etc/yum.repos.d/ \
utility.lab.example.com/ubi9-beta/ubi \
sleep infinity

$ podman ps -a

由于容器设计为仅具有所需的最少软件包,因此容器可能不具有测试通信所需的实用程序,如ping和ip命令。您可以使用
podman exec 命令在容器中安装这些实用程序。

1
2
$ podman exec client01 dnf install -y iputils iproute
..output omitted..

容器现在可以通过容器名称互相 ping。您可以使用podman exec
命令来测试DNS解析。名称解析到为db_net网络手动设置的子网内的IP。

1
2
3
4
5
6
$ podman exec client01 ping -c4 db01
PING db01.dns.podman (10.87.0.2) 56(84) bytes of data.
64 bytes from 10.87.0.2 (10.87.0.2): icmp_seq=1 ttl=64 time=1.08 ms
64 bytes from 10.87.0.2 (10.87.0.2): icmp_seq=2 ttl=64 time=0.082 ms
64 bytes from 10.87.0.2 (10.87.0.2): icmp_seq=3 ttl=64 time=0.063 ms
64 bytes from 10.87.0.2 (10.87.0.2): icmp_seq=4 ttl=64 time=0.070 ms

您可以使用 podman exec命令验证每个容器中的IP地址是否与DNS解析匹配。

1
2
$ podman exec client01 ip a | grep 10.8
inet 10.87.0.3/16 brd 10.87.255.255 scope global eth0

5.7 多个网络连接到单个容器

多个网络可以同时连接到一个容器,以帮助分隔不同类型的流量。 您可以使用
podman network create命令创建backend 网络

1
$ podman network create backend

然后,使用podman network ls 命令查看所有 Podman 网络。

1
2
3
4
5
$ podman network ls
NETWORK ID NAME DRIVER
a7fea510a6d1 backend bridge
fe680efc5276 db01 bridge
2f259bab93aa podman bridge

没有通过podman network create 命令的–gateway和–subnet
选项指定子网和网关。 使用podman network inspect 命令来获取backend
网络的IP信息。

1
$ podman network inspect backend

在容器运行时,您可以使用podman network connect
命令将其他网络连接到容器。您可以使用podman network
connect命令,将backend网络连接到db01和client01容器。

1
2
$ podman network connect backend db01
$ podman network connect backend client01

您可以使用podman inspect
命令验证两个网络是否都已连接到各个容器并显示IP信息。

1
2
3
4
$ podman inspect db01

$ podman inspect db01 | grep -A 34 Networks > db01
$ cat db01 #查看db01的两个网络IP为10.89.0.2 , 10.87.0.2

client01容器现在可以与两个网络上的db01容器通信。您可以使用podman exec
命令从cliento1容器pingdb01容器上的两个网络。

1
2
3
4
5
6
$ podman exec -ti client01 ping -c4 10.89.0.2
PING 10.89.0.2 (10.89.0.2) 56(84) bytes of data.
64 bytes from 10.89.0.2: icmp_seq=1 ttl=64 time=0.352 ms
$ podman exec -ti client01 ping -c4 10.87.0.2
PING 10.87.0.2 (10.87.0.2) 56(84) bytes of data.
64 bytes from 10.87.0.2: icmp_seq=1 ttl=64 time=0.594 ms

容器内安装mariadb客户端访问容器数据库

1
2
3
$ podman exec client01 dnf -y install mariadb
podman
$ podman exec -ti client01 mysql -u user -ppass -h db01

6 系统服务来管理容器

6.1 systemd管理小型容器

可以运行容器来完成系统任务,或获取一系列命令的输出

可能还希望运行无限期运行服务的容器,如Web 服务器或数据库

在传统环境中,特权用户通常将这些服务配置为在系统启动时运行,并使用
systemctl 命令进行管理

作为普通用户,可以创建systemd单元来配置您的Rootless容器。利用此配置,可以通过systemctl命令将容器作为常规系统服务进行管理

基于systemd单元管理容器主要用于不需要扩展的基本和小型部署

对于许多基于容器的应用和服务的更复杂扩展和编排,可以使用基于Kubernetes的企业编排平台,如红帽OpenShift容器平台

6.2 systemd用户服务要求

作为普通用户,可以使用systemctl命令来启用服务

该服务在打开会话(图形界面、文本控制台或SSH)时启动,并在关闭最后一个会话时停止。此行为与系统服务有所不同,系统服务是在系统启动时启动并在系统关机时停止。

默认情况下,当使用useradd
命令创建用户帐户时,系统将使用普通用户ID范围中的下一个可用ID

系统还在/etc/subuid文件中为用户的容器保留一系列ID

如果使用useradd命令–system选项创建用户帐户,则系统不会为用户容器保留范围。因此,无法使用系统帐户启动Rootless容器。

决定创建一个专门的用户帐户来管理容器。使用useradd
命令创建appdev-adm用户,并将redhat用作密码。

1
2
3
4
5
6
7
8
[student@servera ~]$ sudo useradd appdev-adm
[sudo] password for student:student
[student@servera ~]$ sudo passwd appdev-adm
Changing password for user appdev-adm.
New password:
BAD PASSWORD: The password is shorter than 8 characters
Retype new password:
passwd: all authentication tokens updated successfully.

然后,使用 su命令切换到appdev-adm用户,并使用podman命令来启动。

1
2
3
4
5
6
7
[student@servera ~]$ su appdev-adm
Password:redhat
[appdev-adm@servera student]$ podman info
ERRO[0000] XDG_RUNTIME_DIR directory "/run/user/1000" is not owned by the current user
[root@servera ~]# su appdev-adm
[appdev-adm@servera root]$ podman info
ERRO[0000] XDG_RUNTIME_DIR directory "/run/user/0" is not owned by the current user

Podman是一款无状态实用程序,需要完整的登录会话

Podman 必须在SSH会话中使用,不能在sudo或su shell中使用。因此,您将退出su
shell,并通过SSH登录计算机。

无状态应用:Stateless Application
是指并不会在会话中保存下次会话中去要的客户端数据。
每一个会话都像首次执行一样,不会依赖之前的数据进行响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[appdev-adm@servera root]$ exit
exit
[root@servera ~]# ssh appdev-adm@localhost
appdev-adm@localhost's password:
Register this system with Red Hat Insights: insights-client --register
Create an account or view all your systems at https://red.ht/insights-dashboard
Last login: Tue Mar 4 09:06:27 2025
[appdev-adm@servera ~]$ podman info
[appdev-adm@servera ~]$ podman login -u admin -p redhat321 utility.lab.example.com
Login Succeeded!
[appdev-adm@servera ~]$ podman search utility.lab.example.com/
NAME DESCRIPTION
utility.lab.example.com/rhel8/mariadb-103
utility.lab.example.com/rhel9/mariadb-105
utility.lab.example.com/rhel9/httpd-24
utility.lab.example.com/library/nginx
utility.lab.example.com/ubi7/ubi
utility.lab.example.com/ubi9/ubi
utility.lab.example.com/ubi8/ubi
utility.lab.example.com/ubi9/python-312
utility.lab.example.com/rhel9/php-82

然后,配置容器注册表并使用您的凭据进行身份验证。您可以使用以下命令运行
http 容器。

1
2
3
4
5
6
7
8
9
10
[appdev-adm@servera ~]$ mkdir /home/appdev-adm/nginx_web/
[appdev-adm@servera ~]$ echo nginx_web_page > /home/appdev-adm/nginx_web/index.html
[appdev-adm@servera ~]$ cat /home/appdev-adm/nginx_web/index.html
nginx_web_page
[appdev-adm@servera ~]$ podman run -d --name nginx -v /home/appdev-adm/nginx_web/:/usr/share/nginx/html/:Z -p 8080:80 utility.lab.example.com/library/nginx
[appdev-adm@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6f72fe3fc6d7 utility.lab.example.com/library/nginx:latest nginx -g daemon o... 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp nginx
[appdev-adm@servera ~]$ curl localhost:8080
nginx_web_page

为容器创建systemd用户文件

在~/.confiq/svstemd/user/目录中手动定义systemd服务

用户服务的文件语法与系统服务文件的相同。有关更多详细信息,请查看systemd.unit(5)和systemd.service(5)man手册

使用podman generate systemd 命令为现有容器生成systemd 服务文件podman
generate systemd 命令使用容器作为模型来创建配置文件

podman generate systemd命令–new选项指示podman实用程序对systemd
服务进行配置,以便在该服务启动时创建容器并在该服务停止时删除容器

可以使用podman generate
systemd命令和–name选项来显示为nginx容器建模的systemd服务文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[appdev-adm@servera ~]$ man systemd.unit | grep config.*user
~/.config/systemd/user.control/*
~/.config/systemd/user/*
│~/.config/systemd/user.control │ using the dbus API ($XDG_CONFIG_HOME is used if
$HOME/.config/systemd/user │ set, ~/.config otherwise) │
[appdev-adm@servera ~]$ man systemd.unit
[appdev-adm@servera ~]$ mkdir -p ~/.config/systemd/user/
[appdev-adm@servera ~]$ cd ~/.config/systemd/user/
[appdev-adm@servera ~]$ podman stop nginx # 生成单元文件之前先停止容器
nginx
[appdev-adm@servera user]$ podman generate systemd -n nginx -f
/home/appdev-adm/.config/systemd/user/container-nginx.service
[appdev-adm@servera user]$ ls
container-nginx.service
[appdev-adm@servera user]$ systemctl --user enable --now container-nginx.service
Created symlink /home/appdev-adm/.config/systemd/user/default.target.wants/container-nginx.service → /home/appdev-adm/.config/systemd/user/container-nginx.service.
[appdev-adm@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6f72fe3fc6d7 utility.lab.example.com/library/nginx:latest nginx -g daemon o... 26 minutes ago Up About a minute 0.0.0.0:8080->80/tcp nginx
[appdev-adm@servera user]$ systemctl --user stop container-nginx.service
[appdev-adm@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6f72fe3fc6d7 utility.lab.example.com/library/nginx:latest nginx -g daemon o... 27 minutes ago Exited (0) 16 seconds ago 0.0.0.0:8080->80/tcp nginx
[appdev-adm@servera user]$ systemctl --user restart container-nginx.service

$ podman generate systemd -n nginx
ExecStart=/usr/bin/podman start nginx
ExecStop=/usr/bin/podman stop -t 10 nginx

*$ podman generate systemd -n nginx -f
$ ls
container-nginx.service
  • 启动时,systemd守护进程执行podman start命令来启动现有容器
  • 停止时,systemd守护进程执行podman
    stop命令来停止容器。请注意,systemd守护进程不会删除该容器。

然后,使用上一命令并加上–new选项来比较systemd配置。

1
2
3
4
5
6
7
8
9
10
11
$ podman generate systemd -n nginx --new

ExecStartPre=/bin/rm -f %t/%n.ctr-id
ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon --replace -d --name nginx -p 8080:80 -v /home/appdev-adm/nginx_web/:/usr/share/nginx/html/:Z utility.lab.example.com/library/nginx
ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id

#通过--new选项创建用户单元文件的方法,仅供参考
$ cd ~/.config/systemd/user/
$ podman generate systemd -n nginx --new -f
/home/appdev-adm/.config/systemd/user/container-nginx.service
  • 启动时,systemd守护进程执行 podman run
    命令以创建并启动新容器。此操作使用podman run
    命令–rm选项,它将在停止时删除容器。
  • 停止时,systemd 执行 podman stop 命令以停止容器。
  • 在systemd停止容器后,systemd 将使用podman rm -f命令将其移除。

验证 podman generate systemd 命令的输出,并使用–files
选项运行上一命令,以在当前目录中创建systemd用户文件。由于nginx容器使用持久存储,因此您选择使用带有–new选项的podman
generate
systemd命令。然后,创建~/config/systemd/user/目录并将文件移到此位置上

为容器管理systemd用户文件

现在,您已创建了 systemd 用户文件,可以使用 systemctl命令 –user
选项来管理nginx容器。
首先,重新加载systemd守护进程,使systemctl命令知道新的用户文件。使用systemctl–user
start命令启动nginx容器。使用为容器生成的 systemd用户文件的名称。

1
2
3
4
5
6
$ systemctl --user enable --now container-nginx.service
#其他的管理方法,仅供参考
$ systemctl --user status container-nginx.service
$ systemctl --user stop container-nginx.service

#建议重启验证容器是否可以 开机自启动

下表总结了 systemd 系统和用户服务之间使用的不同目录和命令。


比较系统和用户服务


存储自定义单元文件 系统服务 /etc/systemd/system/unit.service

1
用户服务   \~/.config/systemd/user/unit.service

重新加载单元文件 系统服务 # systemctl daemon-reload

1
用户服务   \$ systemctl --user daemon-reload

启动和停止服务 系统服务 # systemctl start UNIT

1
2
3
\# systemctl stop UNIT
用户服务 \$ systemctl --user start UNIT
\$ systemctl --user stop UNIT

在计算机启动时启动服务 系统服务 # systemctl enable UNIT

1
2
用户服务   \$ loginctl enable-linger
\$ systemctl --user enable UNIT

容器配置为系统引导时启动

​ 此时,systemd服务配置已就绪,可以为给定的用户运行容器。但是,如果用户从系统注销,systemd服务会在特定时间后停止容器。出现此行为的原因是,systemd服务单元是使用.user选项创建的,它在用户登录时启动服务,并在用户注销时停止服务。 ​不过,您可以通过运行loginctl enable-linger命令来更改此默认行为,并强制已启用的服务在服务器启动时启动,并在服务器关闭期间停止。​可以使用loginctl命令将systemd用户服务配置为在所配置服务的最后一个用户会话关闭后保留

​ 使用 loginctl show-user 命令验证配置是否成功。

1
2
3
4
5
6
7
8
9
10
11
12
[appdev-adm@servera ~]$ loginctl show-user appdev-adm
Linger=no
[appdev-adm@servera ~]$ loginctl enable-linger
[appdev-adm@servera ~]$ loginctl show-user appdev-adm
Linger=yes

# 重启后进行测试
[kiosk@foundation0 ~]$ ssh root@servera
[root@servera ~]# ssh appdev-adm@localhost
[appdev-adm@servera ~]$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6f72fe3fc6d7 utility.lab.example.com/library/nginx:latest nginx -g daemon o... 40 minutes ago Up About a minute 0.0.0.0:8080->80/tcp nginx

Root使用Systemd管理容器

​ 将容器配置为以root身份运行,并使用systemd服务文件进行管理的一个优势是可以将这些服务文件配置为像常见systemd单元文件那样工作,而不是以特定用户身份来运行。

将服务文件设置为root的过程与前面概述的Rootless容器过程类似,但以下例外: ​

​ 1.不要创建专门的用户来管理容器。 ​
​ 2.服务文件必须在/etc/systemd/system目录中,而不是在~/config/systemd/user目录中 ​
​ 3.使用systemctl命令管理容器,但不使用 –user 选项 ​ 不要以root用户身份运行loginctl enable-linger命令

作者

罗宇

发布于

2025-04-22 13:50:42

更新于

2025-04-29 17:47:33

许可协议