一、为什么要给Podman容器做资源配额?

很多人刚开始用Podman的时候,只会简单敲个podman run就启动容器了,觉得容器能跑就行。但时间长了就会遇到各种麻烦:比如某个容器突然占满了服务器的CPU,导致其他容器甚至服务器本身卡得动不了;或者某个容器偷偷吃了好几十G内存,把其他容器的运行空间挤没了。

举个真实的例子:我之前帮一个小团队排查线上问题,他们的电商系统有两个容器,一个是商品搜索服务,一个是订单处理服务。有一次大促,搜索服务突然占了服务器100%的CPU,导致订单服务完全没法处理,最后丢了好几百笔订单。后来查原因,是搜索服务的代码有个死循环bug,但更要命的是,当时根本没给这个容器设CPU上限——要是设了,最多只会让搜索服务变慢,不会连累订单服务。

资源配额的核心作用,就是给每个容器画好“安全圈”:你能用到多少CPU、多少内存、多少磁盘空间,都提前定死,不会让一个容器把整个服务器的资源吃光。这就像家里分蛋糕,每个人的份额提前说好,不会有人抢别人的那份。

二、Podman资源配额的核心配置项

要给容器做精细化配额,首先得搞懂Podman能控制哪些资源。这些配置项都是围绕容器的运行资源来的,我们一个个说清楚。

2.1 CPU配额:控制容器能用到的CPU算力

CPU配额是最常用的配置项,也是最容易出问题的。Podman的CPU配置有两个核心参数:--cpus和--cpuset-cpus。

  • --cpus:控制容器最多能用到多少个CPU核心。比如设成--cpus=2,就表示这个容器最多能用2个CPU核心的算力,哪怕服务器有16核,它也只能用2核。
  • --cpuset-cpus:控制容器只能用指定的CPU核心。比如设成--cpuset-cpus=0-3,就表示这个容器只能用服务器的0、1、2、3这四个CPU核心,不能用其他核心。这个配置一般用在需要隔离的场景,比如给某个核心服务单独分配专属CPU,避免和其他服务抢核心。

2.2 内存配额:控制容器能用到的内存大小

内存配额的配置相对复杂一点,因为内存不够用的时候,容器会直接被杀死,影响服务可用性。Podman的内存配置主要有三个参数:

  • --memory:控制容器最多能用到的内存大小。比如设成--memory=1g,就表示这个容器最多只能用1G内存,超过的话,Podman会直接杀死容器。
  • --memory-swap:控制容器能用到的内存加交换分区的总大小。比如--memory=1g --memory-swap=2g,就表示容器最多能用1G内存,加上1G的交换分区,总共2G。如果设成--memory-swap=-1,就表示容器可以无限制使用交换分区。
  • --memory-swappiness:控制容器使用交换分区的倾向。值的范围是0到100,值越大,容器越倾向于用交换分区;值越小,越倾向于用内存。一般来说,我们会把这个值设得低一点,因为交换分区的速度比内存慢很多,会影响容器性能。

2.3 磁盘配额:控制容器能用到的磁盘空间

很多人会忽略磁盘配额,但这个配置也很重要。比如某个日志容器,要是没设磁盘配额,它会一直写日志,直到把服务器的磁盘占满,导致整个服务器没法运行。Podman的磁盘配置主要有两个参数:

  • --storage-opt size:控制容器的根文件系统最多能用到的磁盘空间。比如设成--storage-opt size=10g,就表示这个容器的根文件系统最多只能用10G磁盘空间。
  • --volume:如果容器挂载了宿主机的目录,也可以给这个目录单独设磁盘配额。比如--volume /data/logs:/logs:size=5g,就表示容器挂载的/logs目录最多只能用5G磁盘空间。

三、精细化配置的完整示例

接下来我们通过一个完整的示例,把上面的配置项都串起来。这个示例模拟一个电商系统的两个核心服务:商品搜索服务和订单处理服务,我们给这两个服务分别做精细化的资源配额配置。

技术栈:Podman 4.0+(这个版本的资源配额配置最稳定,支持所有我们用到的参数)

3.1 配置商品搜索服务

商品搜索服务的特点是CPU占用高,但内存占用相对稳定,所以我们要给它设CPU上限,同时给内存设合理的配额。

# 启动商品搜索服务容器
podman run -d \
  --name search-service \  # 容器名称
  --cpus=2 \  # 最多使用2个CPU核心
  --cpuset-cpus=0-1 \  # 只能使用服务器的0、1号CPU核心
  --memory=1g \  # 最多使用1G内存
  --memory-swap=2g \  # 最多使用1G内存+1G交换分区,总共2G
  --memory-swappiness=10 \  # 尽量少用交换分区
  --storage-opt size=10g \  # 根文件系统最多使用10G磁盘
  --volume /data/search-logs:/logs:size=5g \  # 挂载日志目录,最多使用5G磁盘
  -p 8080:8080 \  # 端口映射
  docker.io/library/search-service:latest  # 镜像地址

3.2 配置订单处理服务

订单处理服务的特点是内存占用高,CPU占用相对稳定,所以我们要给它设内存上限,同时给CPU设合理的配额。

# 启动订单处理服务容器
podman run -d \
  --name order-service \  # 容器名称
  --cpus=1 \  # 最多使用1个CPU核心
  --cpuset-cpus=2 \  # 只能使用服务器的2号CPU核心
  --memory=2g \  # 最多使用2G内存
  --memory-swap=3g \  # 最多使用2G内存+1G交换分区,总共3G
  --memory-swappiness=5 \  # 尽量少用交换分区
  --storage-opt size=5g \  # 根文件系统最多使用5G磁盘
  --volume /data/order-logs:/logs:size=3g \  # 挂载日志目录,最多使用3G磁盘
  -p 8081:8081 \  # 端口映射
  docker.io/library/order-service:latest  # 镜像地址

3.3 验证配置是否生效

配置完之后,我们需要验证一下这些配额是不是真的生效了。Podman提供了podman inspect命令可以查看容器的详细配置,同时也可以用podman stats命令实时查看容器的资源使用情况。

# 查看搜索服务容器的CPU配额配置
podman inspect search-service | grep -i cpus
# 输出结果应该包含"cpus": 2,"cpuset_cpus": "0-1"

# 查看搜索服务容器的内存配额配置
podman inspect search-service | grep -i memory
# 输出结果应该包含"memory": 1073741824(1G的字节数),"memory_swap": 2147483648(2G的字节数)

# 实时查看两个容器的资源使用情况
podman stats search-service order-service
# 输出结果会显示每个容器的CPU使用率、内存使用率、磁盘使用率等

四、应用场景分析

Podman容器资源配额的精细化配置,适合很多场景,我们举几个最常见的:

4.1 多租户服务器场景

如果一台服务器同时给多个团队或者多个客户使用,就必须给每个团队的容器设资源配额。比如A团队的容器不能占用超过30%的CPU,B团队的容器不能占用超过20%的内存,这样就不会出现一个团队把服务器资源吃光,影响其他团队的情况。

4.2 核心服务隔离场景

对于一些核心服务,比如订单服务、支付服务,我们需要给它们单独分配专属的CPU和内存,避免和其他非核心服务抢资源。比如我们给订单服务分配专属的CPU核心,哪怕其他服务的CPU使用率很高,订单服务也能保证足够的算力。

4.3 测试环境场景

在测试环境中,我们经常会测试一些有问题的代码,比如死循环、内存泄漏的代码。给测试容器设资源配额,就可以避免测试的时候把整个测试服务器搞挂,影响其他测试的进行。

4.4 日志服务场景

日志服务的特点是会一直写日志,很容易占满磁盘。给日志服务的磁盘设配额,就可以避免日志占满整个服务器的磁盘空间。

五、技术优缺点分析

任何技术都有优缺点,Podman的资源配额也不例外,我们来分析一下:

5.1 优点

  • 简单易用:配置命令很简单,不需要复杂的配置文件,直接在启动容器的时候加参数就可以。
  • 轻量高效:Podman本身是轻量的容器引擎,资源配额的配置不会给容器带来额外的性能开销。
  • 灵活可控:可以针对每个容器单独配置不同的配额,也可以给容器组配置统一的配额,灵活性很高。
  • 兼容K8s:Podman的资源配额配置和Kubernetes的资源配额配置很相似,熟悉Podman的配置之后,再学K8s的配置会很容易。

5.2 缺点

  • 配置复杂:如果容器的数量很多,每个容器的配额都不一样,配置和管理起来会很麻烦。
  • 容易误配置:比如把内存配额设得太小,会导致容器频繁被杀死;把CPU配额设得太大,又起不到配额的作用。
  • 没有全局监控:Podman本身没有自带的全局监控工具,要监控所有容器的资源使用情况,需要配合其他工具,比如Prometheus、Grafana。

六、注意事项

在做Podman容器资源配额的精细化配置时,有几个注意事项一定要记住:

6.1 先测再用

配置配额之前,一定要先对容器的实际资源使用情况做测试。比如先让容器在没有配额的情况下运行一段时间,统计它的CPU、内存、磁盘的峰值使用情况,然后再根据这个峰值来设配额。如果把配额设得比实际峰值还小,会导致容器频繁被杀死,影响服务可用性。

6.2 不要过度隔离

--cpuset-cpus这个配置虽然能隔离CPU,但也会带来一些问题。比如服务器的CPU核心数量是固定的,如果给太多容器分配专属的CPU核心,会导致有些核心闲置,有些核心过载,反而浪费了服务器的资源。所以这个配置一般只给核心服务用,其他服务尽量用--cpus来控制CPU配额。

6.3 注意内存交换分区的影响

内存交换分区的速度比内存慢很多,所以尽量不要让容器用到交换分区。--memory-swappiness的值尽量设得低一点,最好设成0,这样容器就不会用到交换分区。但如果容器的内存配额设得太小,又没有交换分区,容器很容易被杀死,所以需要平衡好内存配额和交换分区的配置。

6.4 注意磁盘配额的限制

--storage-opt size这个配置是针对容器的根文件系统的,而如果容器挂载了宿主机的目录,这个配置是不起作用的,需要单独给挂载的目录设配额。另外,磁盘配额的配置需要宿主机的文件系统支持,比如ext4、xfs文件系统都支持磁盘配额,而fat32文件系统不支持。

七、文章总结

Podman容器资源配额的精细化配置,是保证服务器稳定运行的重要手段。通过给每个容器配置合理的CPU、内存、磁盘配额,可以避免一个容器把整个服务器的资源吃光,保证各个服务之间的隔离和稳定。

在实际应用中,我们需要根据容器的实际资源使用情况,结合具体的应用场景,来配置合理的配额。同时,也要注意配额配置的优缺点和注意事项,避免因为配置不当导致服务出现问题。

对于容器数量很多的场景,我们可以配合容器编排工具,比如Kubernetes,来统一管理容器的资源配额,这样可以大大降低配置和管理的难度。