26

容器资源可视化隔离的实现方法

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzUxMTcwOTM4Mg%3D%3D&%3Bmid=2247486298&%3Bidx=1&%3Bsn=1310a2e114fbd74b4f8051973a81e3f1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

奇技指南

通常有一些业务已经习惯了在传统的物理机/虚拟机上使用top,free等命令来查看系统的资源使用情况,而在容器中使用这些命令看到的仍然是物理机/虚拟机上的数据。本文针对该问题,介绍了使用Lxcfs和kubernetes Admission Webhook来实现对容器的资源可视化隔离。

1

为什么对容器的资源进行可视化隔离?

容器技术提供了不同于传统虚拟机技术的环境隔离方式。通常的Linux容器对容器打包和启动进行了加速,但也降低了容器的隔离强度。其中Linux容器最为知名的问题就是资源视图隔离问题。

容器可以通过cgroup的方式对资源的使用情况进行限制,包括: 内存,CPU等。但是需要注意的是,如果容器内的一个进程使用一些常用的监控命令,如: free, top等命令其实看到还是物理机的数据,而非容器的数据。这是由于容器并没有做到对/proc,/sys等文件系统的隔离。

2

容器资源视图隔离有哪些应用场景?

1)在容器生产环境,通常有一些业务已经习惯了在传统的物理机,虚拟机上使用top,free等命令来查看系统的资源使用情况,但是容器没有做到资源视图隔离,那么在容器里面看到的数据还是物理机的数据。

2)在应用程序的视角来看,在容器里面运行进程和在物理机虚拟机上运行进程的运行环境是不同的。并且有些应用在容器里面运行进程会存在一些安全隐患:

  • 对于很多基于JVM的java程序,应用启动时会根据系统的资源上限来分配JVM的堆和栈的大小。而在容器里面运行运行JAVA应用由于JVM获取的内存数据还是物理机的数据,而容器分配的资源配额又小于JVM启动时需要的资源大小,就会导致程序启动不成功。并且在java应用里,一些java库也会根据资源视图分配堆和栈的大小,这同样会存在安全隐患。

  • 在CPU上也会存在问题,大多数的应用程序,比如nginx或者一些其它的中间件服务会根据其视图的cpuinfo文件信息设定默认的启动线程数。但是在容器内的进程总会从/proc/cpuinfo中获取到CPU的核数,而容器里面的/proc文件系统还是物理机的,从而会影响到运行在容器里面服务的性能。

3

容器资源视图没有完全解决的问题,如何做?

使用 lxcfs 和 kubernetes admission webhook机制来实现容器资源视图隔离的效果。

1)lxcfs官方介绍

LXCFS is a small FUSE filesystem written with the intention of making Linux containers feel more like a virtual machine. It started as a side-project of LXC but is useable by any runtime.

关于lxcfs的详细内容,请戳:https://github.com/lxc/lxcfs.

在应用Lxcfs在线上环境需要注意以下几点:

  1. 当前(lxcfs release 3.1.2)版本只对procfs文件系统进行了虚拟化,并没有对/sys/devices/system/cpu/online文件进行虚拟化,但是对/sys/devices/system/cpu/online的虚拟化工作已经被合并到master分支了,如果想要对其进行隔离操作的话,请单独对lxcfs进行编译再使用。

  2. 为什么需要对/sys/devices/system/cpu/online文件进行视图虚拟化?主要问题是由于一些语言在启动runtime时,会从该文件获取cpu的数量,来启动默认的线程数,比如: Java JVM或者Nginx。如果在容器里面获取的还是物理机的数据,会影响到应用程序的性能。

  3. lxcfs需要部署在k8s集群的各个Node节点上,当lxcfs服务重启或者crash时,之前已经挂载在容器/proc的挂载点会失效,导致在容器中执行free,top命令会失效。为了解决这个问题,现在的做法是使用systemd的方式在各个节点启动lxcfs服务,当lxcfs服务crash之后重启成功之后,会通过ExecStartPost的方式执行 /usr/local/bin/container_remount_lxcfs.sh脚本来对之前已经挂载过的容器进行重新挂载操作。

lxcfs.service的内容如下:

[Unit]

Description=FUSE filesystem for LXC

ConditionVirtualization=!container

Before=lxc.service

Documentation=man:lxcfs(1)


[Service]

ExecStart=/usr/bin/lxcfs -l /var/lib/lxc/lxcfs/

KillMode=process

Restart= always

Delegate=yes

ExecStopPost=-/bin/fusermount -u /var/lib/lxc/lxcfs

ExecReload=/bin/kill -USR1 $MAINPID


# add remount script

ExecStartPost=/usr/local/bin/container_remount_lxcfs.sh


[Install]

WantedBy=multi-user.target

lxcfs重启成功之后,执行的重新挂载脚本内容如下:

#! /bin/bash


PATH=$PATH:/bin

LXCFS="/var/lib/lxc/lxcfs"

LXCFS_ROOT_PATH="/var/lib/lxc"


containers=$(docker ps | grep -v pause | grep -v calico | awk '{print $1}' | grep -v CONTAINE)


for container in $containers;do

mountpoint=$(docker inspect --format '{{ range .Mounts }}{{ if eq .Destination "/var/lib/lxc" }}{{ .Source }}{{ end }}{{ end }}' $container)

if [ "$mountpoint" = "$LXCFS_ROOT_PATH" ];then

echo "remount $container"

PID=$(docker inspect --format '{{.State.Pid}}' $container)

# mount /proc

for file in meminfo cpuinfo loadavg stat diskstats swaps uptime;do

echo nsenter --target $PID --mount -- mount -B "$LXCFS/proc/$file" "/proc/$file"

nsenter --target $PID --mount -- mount -B "$LXCFS/proc/$file" "/proc/$file"

done

# mount /sys

for file in online;do

echo nsenter --target $PID --mount -- mount -B "$LXCFS/sys/devices/system/cpu/$file" "/sys/devices/system/cpu/$file"

nsenter --target $PID --mount -- mount -B "$LXCFS/sys/devices/system/cpu/$file" "/sys/devices/system/cpu/$file"

done

fi

done

2)基于kuernetes可扩展的admission webhook机制对发送创建POD的请求数据进行拦截,并对其请求的Body进行修改之后(在lxcfs中主要是对pod进行procfs文件系统的挂载操作),再放行到具体的handler中并持久化数据到etcd。

再配一张图更能体现adminssion webhook在请求中的哪个位置进行工作。

JnERZvI.jpg!web

作者基于开源的lxcfs-admission-webhook进行了修改,增加了对/sys/devices/system/cpu/online文件的虚拟化。详细的代码请戳: https://github.com/xigang/lxcfs-admission-webhook/tree/dev.

在使用lxcfs-admission-webhook服务时,需要注意的几点如下:

1. kubernetes的版本需要大于等于1.9。

2. 配置kube-apiserver配置文件,对–admission-control增加MutatingAdmissionWebhook,ValidatingAdmissionWebhook参数,使kubernetes开启admissionregistration.k8s.io/v1beta1 API。

3. 如果kubernetes master节点没有部署kube-proxy, 需要为kube-apiserver增加--enable-aggregator-routing=true的参数。

4. 如果使用的runc版本比较低,需要升级各个Node节点的runc,以允许容器对procfs文件系统的挂载操作。

最终体现容器资源可视化隔离的效果如下所示:

VBBrY3v.jpg!web

EJJRzyM.jpg!web

相关文章

  • https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/

  • https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/

  • https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/

qQVJN3Y.jpg!web

向所有一线抗疫勇士致敬!

360技术公众号

技术干货|一手资讯|精彩活动


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK