podman容器


1 容器的概念

1.1 容器技术介绍

  软件应用通常依赖于运行时环境(runtimeenvironment)提供的系统库、配置文件或服务

  传统软件应用的运行时环境安装在物理主机或虚拟机上运行的操作系统中。然后,管理员在操作系统上安装应用依赖项

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

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

  容器是由一个或多个与系统其余部分隔离的进程组成的集合,软件容器是打包应用以简化其部署和管理的一种方式

  以实体集装箱为例。集装箱是打包和装运货物的标准方式。作为一个箱子进行标记、装载、卸载,以及从一个位置运输到另一个位置。集装箱中的内容与其他集装箱的内容隔离,因此互不影响。这些基本原则也适用于软件容器

1.2 容器技术的核心

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

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

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

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

1.3 容器和虚拟机的差异

  1.容器提供许多与虚拟机相同的益处,如安全、存储和网络隔离等

  2.这两种技术都将其应用库和运行时资源与主机操作系统或虚拟机监控程序隔离开

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

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

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

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

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

  5.容器具有以下特征:

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

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

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

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

1.4 Rootless和Rootful容器

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

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

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

1.5 设计基于容器的架构

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

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

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

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

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

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

2 容器镜像和注册表

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

  1.容器镜像是包含编码步骤的静态文件,充当创建容器的蓝图

  2.容器镜像打包应用及其所有依赖项,如系统库、编程语言运行时和库以及其他配置设置

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

  4.容器注册表是用于存储和检索容器镜像的存储库。开发人员将容器镜像推送或上传到容器注册表中,可以从注册表中将这些容器镜像拉取或下载到本地系统,以用于运行容器。可使用包含第三方镜像的公共注册表,也可使用贵组织控制的私有注册表

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

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

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

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

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

2.1 安装容器

  使用的镜像仓库浏览器访问为: 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

2.2 登录容器

  需要红帽开发人员账户才能从红帽注册表下载镜像。可以使用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

[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!

2.3 验证容器的登录

  要验证是否已登录到某一注册表,请使用 podman login命令的–get-login选项

  退出登录:podman logout

$ 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.4 配置容器注册表

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

[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"]

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

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

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

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

# $注意 ~/.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/

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

2.5 容器文件构建容器镜像

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

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

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

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

2.6 规模化容器管理

  新应用越来越多地使用容器来实施功能组件,这些容器提供应用的其他部分使用的服务

  组织管理越来越多的容器,可能很快就会不堪重负

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

   1.平台必须确保提供必要服务的容器的可用性

   2.环境必须通过增加或减少运行中的容器实例,并对流量进行负载平衡,从而应对应用的使用高峰

   3.平台必须检测容器或主机的故障,并相应地作出反应

   4.开发人员可能需要自动工作流,以便透明、安全地向客户交付新的应用版本

  Kubernetes是一项编排服务,可以使在容器主机集群中部署、管理和扩展基于容器的应用变得更加轻而易举

  Kubernetes通过负载平衡器将流量重定向到容器,以便可以扩展提供服务的容器数量

  Kubernetes支持用户定义的健康检查,以便监控您的容器,并在容器出现故障时将其重新启动

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

3 部署容器

3.1 Podman实用程序

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

  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子命令的用法

3.2 安装容器实用工具

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

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

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

[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实用程序的配置信息,包括其配置的注册表

[student@servera ~]$ podman info

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

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

# 在注册表中搜索镜像
[student@servera ~]$ podman search utility.lab.example.com/

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

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

# 镜像信息注解:
1.REPOSITORY    仓库地址
2.TAG           标记,latest最近版本
3.IMAGE ID      镜像ID,ID号唯一,保证镜像唯一性
4.CREATED       创建时间;
5.SIZE          镜像大小

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

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

$ cat Containerfile 
FROM utility.access.redhat.com/ubi8/ubi:latest
RUN dnf install -y python36
CMD["/bin/bash""-c""sleep infinity"]
# 此容器文件是教材中例子默认报错,可以使用下面的容器文件

[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 infinity命令可防止容器退出因为该进程永远不会完成,然后可以在容器内进行测试、开发和调试

  在检查容器文件后,可以使用podman build命令来构建镜像。podman build命令的语法如下所示:

$ 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命令来查看容器镜像的低级别信息,并验证其内容是否符合容器要求:

[student@servera ~]$ podman inspect localhost/rhel7:2.0

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

4 运行容器

  现在已拥有所需的容器镜像,可以使用它们来运行容器。容器可以处于以下状态之一!

   1.Created:已创建好但尚未启动的容器

   2.运行中:与其进程一起运行的容器

   3.已停止:其进程已停止的容器

   4.Paused:其进程已暂停的容器,不支持 Rootless容器

   5.Deleted:其进程处于已死状态的容器

  podman ps命令列出系统上正在运行的容器

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

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

$ podman create --name python36 dd6ca291f097

# 使用podman ps和podman ps -a命令来验证容器是否已创建但尚未启动
$ podman ps -a
$ podman ps 

# 运行podman start命令。可以使用名称或容器ID来启动容器。此命令的输出是容器的名称
$ podman start python36
$ podman ps

4.1 从远程存储库运行容器

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

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

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

# 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命令,该命令将返回具有许多进程的预期结果

[student@servera ~]$ ps -ax
[student@servera ~]$ podman run -di --name python36-db utility.lab.example.com/rhel8/mariadb-103
f4c3d26df7bd3614e6b4954ae6ed485046128afc89a95cba20c834b2ba0327ff
[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

4.3 容器内执行命令

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

  使用podman exec命令查看rhel7容器中正在运行的进程

  ps aux命令的输出看起来有所不同,因为它运行与本地计算机不同的进程

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

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

[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版本进行比较
[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.4 容器中的文件系统隔离

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

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

[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文件位于主机计算机上,而不存在于容器内的文件系统上。如果尝试使用podmanexec来执行脚本,则会出现错误,因为容器中不存在/tmp/hello.sh脚本

[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容器:

[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: -

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

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

4.5 删除容器和镜像

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

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

[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
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命令

[student@servera ~]$ podman stop python38
python38

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

[student@servera ~]$ podman rm --help
[student@servera ~]$ podman rm python38
python38

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

[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服务器或数据库服务器等容器为容器主机外部的客户端提供内容时,必须为这些客户端设置通信通道,以访问容器的内容

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

5.2 容器的环境变量

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

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

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

[student@servera ~]$ podman search utility.lab.example.com/
     
[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/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命令调查容器状态的原因

[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:
......

  容器信息展示

# 输出中的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

  mariadb镜像的环境变量

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

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

[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命令

[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

  以上输出显示:

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

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

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

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

[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目录,并使用podmanunshare命令将27的用户命名空间UID和GID设置为该目录的所有者

[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选项来挂载目录:

[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目录的权限错误:

[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上下文

[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 ~]$ 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端口的端口映射

[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选项可显示正在使用的所有容器端口映射。还可以使用podman port db01命令显示 db01容器的映射端口

[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流量传入容器主机,以便它可以重定向到容器:

# 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

5.6 容器的网络后端

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

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

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

$ 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

$ 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

$ podman network inspect db_net

  podman run命令–network选项将启用DNS的db_net网络添加到新容器:

   可使用podman run命令–network选项创建连接到db_net网络的db01和client01容器

$ 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 命令在容器中安装这些实用程序

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

  容器现在可以通过容器名称互相ping

  可以使用podman exec命令来测试DNS解析,名称解析到为db_net网络手动设置的子网内的IP

$ 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解析匹配

$ 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网络

$ podman network create backend

  使用podman network ls 命令查看所有Podman网络

$ 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信息

$ podman network inspect backend

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

  可以使用podman network connect命令,将backend网络连接到db01和client01容器

$ podman network connect backend db01
$ podman network connect backend client01

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

$ 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容器上的两个网络

$ 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客户端访问容器数据库

$ 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用作密码

[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命令来启动

[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指不会在会话中保存下次会话中去要的客户端数据,每个会话都像首次执行一样,不会依赖之前的数据进行响应

[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/

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

[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

6.2.1 创建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服务文件

[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配置

$ 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/目录并将文件移到此位置上

6.2.2 管理systemd用户文件

  现在已创建了systemd用户文件,可以使用systemctl命令的–user选项来管理nginx容器

  首先,重新加载systemd守护进程,使systemctl命令知道新的用户文件:

   使用systemctl–user start命令启动nginx容器

   使用为容器生成的systemd用户文件的名称

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

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

  systemd 系统和用户服务之间使用的不同目录和命令:

1.存储自定义单元文件     系统服务   /etc/systemd/system/unit.service
                       用户服务   \~/.config/systemd/user/unit.service
                         
2.重新加载单元文件       系统服务   \# systemctl daemon-reload
                       用户服务   \$ systemctl --user daemon-reload
                       
3.启动和停止服务         系统服务   \# systemctl start UNIT    
                                 \# systemctl stop UNIT
                       用户服务   \$ systemctl --user start UNIT   
                                 \$ systemctl --user stop UNIT
                     
4.在计算机启动时启动服务  系统服务   \# systemctl enable UNIT
                       用户服务   \$ loginctl enable-linger  
                                 \$ systemctl --user enable UNIT                    

6.2.3 配置为系统引导时启动

  此时systemd服务配置已就绪,可以为给定的用户运行容器。但是,如果用户从系统注销,systemd服务会在特定时间后停止容器。出现此行为的原因是,systemd服务单元是使用.user选项创建的,它在用户登录时启动服务,并在用户注销时停止服务

  可以通过运行loginctl enable-linger命令来更改此默认行为,并强制已启用的服务在服务器启动时启动,并在服务器关闭期间停止。使用loginctl命令将systemd用户服务配置为在所配置服务的最后一个用户会话关闭后保留

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

[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

6.2.4 Root使用Systemd管理容器

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

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

   1.不要创建专门的用户来管理容器

   2.服务文件必须在/etc/systemd/system目录中,而不是在~/config/systemd/user目录中

   3.使用systemctl命令管理容器,但不使用–user选项,不要以root用户身份运行loginctl enable-linger命令


文章作者: 罗宇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 罗宇 !
 上一篇
Kubernetes网络系统原理 Kubernetes网络系统原理
Kubernetes网络采用的是CNI标准,CNI的基本思想是:在创建容器时,先创建好网络命名空间,然后调用CNI插件为这个命名空间配置网络,最后再启动容器内的进程
2025-05-30
下一篇 
Docker容器&Kubernetes Docker容器&Kubernetes
容器编排是指自动化地部署、扩展、管理和协调容器化应用的过程,它涉及到多个方面,如容器的调度、资源分配、容器之间的通信、故障恢复等
2025-05-28
  目录