Kubernetes容器隔离问题实例分析
这篇文章主要讲解了“Kubernetes容器隔离问题实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Kubernetes容器隔离问题实例分析”吧!
成都创新互联公司成立与2013年,是专业互联网技术服务公司,拥有项目成都网站制作、成都做网站网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元赤城做网站,已为上家服务,为赤城各地企业和个人服务,联系电话:13518219792
背景
容器中看到的/proc伪文件系统的信息是宿主的/proc,没有隔离/proc 意味着获取不到容器中进程相关的proc信息。另外,一些需要读取proc信息的应用,会获取到错误的数据。
/proc/meminfo,/proc/cpuinfo, /proc/stat, /proc/uptime, /proc/loadavg
等用户UID/GID的映射,导致容器中的进程具有宿主上相同uid/gid用户的权限
需求分析
方案调研
Docker社区里有讨论过类似的问题, https://github.com/docker/docker/issues/8427, 可以通过kernel patch或者bind mount /proc 来实现。 淘宝团队在几年前曾经发过一个kernel patch https://github.com/alibaba/taobao-kernel/blob/master/patches.taobao/overlayfs-0005-vfs-introduce-clone_private_mount.patch
业界讨论主要有以下几种方案:
## 方案一、 - 直接修改proc文件系统 - https://lkml.org/lkml/2012/5/28/299 - mount -t proc -o meminfo-from-cgroup none /path/to/container/proc 缺点:不可能要求并入内核 ## 方案二、 - Procg 方案: 把文件系统挂载进容器,替代原本的proc文件系统 - 通过读cgroup指定的信息替代原本的/proc/meminfo信息 - https://github.com/fabiokung/procg/ 缺点:内核的cgroup中没有公开读取内存数据函数接口 ## 方案三、 基于lxcfs的docker容器procps软件包升级方案 ——修改free、top、uptime等源码 缺点:不是一个广泛接受的方案,不说自己修改命令可能产生的bug 和成本。不同的linux版本,可能会需要不同的补丁。
解决方案
为LXC准备的FUSE文件系统, 提供了如下的特性:
* a cgroupfs compatible view for unprivileged containers * a set of cgroup-aware files: * cpuinfo * meminfo * stat * uptime
用户空间文件系统 (Filesystem in Userspace, FUSE)
用户空间文件系统 是操作系统中的概念,指完全在用户态实现的文件系统。
目前Linux通过内核模块对此进行支持。一些文件系统如ZFS,glusterfs使用FUSE实现。
FUSE的工作原理如上图所示。假设基于FUSE的用户态文件系统hello挂载在/tmp/fuse目录下。当应用层程序要访问/tmp/fuse下的文件时,通过glibc中的函数进行系统调用,处理这些系统调用的VFS中的函数会调用FUSE在内核中的文件系统;内核中的FUSE文件系统将用户的请求,发送给用户态文件系统hello;用户态文件系统收到请求后,进行处理,将结果返回给内核中的FUSE文件系统;最后,内核中的FUSE文件系统将数据返回给用户态程序。
Linux内核从
2.6.14
支持通过FUSE模块
在
用户空间
实现文件系统libfuse: 用户空间的fuse库, 非特权用户可访问。
LXCFS - 基于FUSE实现的用户空间文件系统
站在文件系统的角度: 通过调用
libfuse库
和内核的FUSE模块
交互实现两个基本功能
让每个容器有自身的cgroup文件系统视图
,类似 Cgroup Namespace提供容器内部虚拟的proc文件系统
LXCFS 视角
从main函数可以看出,初始化的过程包括:
将运行时工作目录/run/lxcfs/controllers/ 挂载到 tmpfs文件系统
将当前系统的各个group子系统重新挂载到 /run/lxcfs/controllers/ 目录
调用libfuse库的主函数 fuse_main(), 指定用户态文件系统的目标目录- /var/lib/lxcfs/
使用struct fuse_operations 的ops方法, 与内核中的FUSE模块交互。 lxcfs.c:701
容器视角
把虚拟proc文件系统挂载到docker容器
用户在容器中读取/proc/meminfo ,cpuinfo 等信息
在 proc_meminfo_read 操作中实现 读取meminfo
过程: 拿到 meminfo进程的pid传给 lxcfs --> 拿到pid的cgroup分组 --> host的/cgroup目录对应进程的cgroup子系统信息
存在的问题以及如何解决
LXCFS的部署问题以及带来的影响和成本? 见如下补充的 LxcFS - k8s实践
故障恢复,如何自动remount? 如果lxcfs进程重启了,那么容器里的/proc/cpuinfo等等都会报
transport connected failed
这个是因为/var/lib/lxcfs会删除再重建,inode变了。所以参考豆瓣的做法,共享mount事件,重新给容器挂载
https://github.com/lxc/lxcfs/issues/193
https://github.com/alibaba/pouch/issues/140
User Namespace
解决什么问题?
用户UID/GID的映射,导致容器中的进程具有宿主上相同uid/gid用户的权限
docker 从1.10版本 开始支持user namespace 隔离。使用参数:DOCKER_OPTS="--userns-remap=default"
相关文档链接:
基于LXCFS增强docker容器隔离性的分析 2015.12.9
docker容器显示问题及修复 2017-03-23
lxc-1.0.9 lxcfs-2.0.0 fuse-2.8.7源码详细注释分析
Kubernetes之路 2 - 利用LXCFS提升容器资源可见性
Kubernetes Initializers
看看大阿里pouch怎么解决remount这个问题
lxcfs 的 Kubernetes实践
注:以下内容来自文档链接4,内容有稍作修改
首先我们要在集群节点上安装并启动lxcfs,我们将用Kubernetes的方式,用利用容器和DaemonSet方式来运行 lxcfs FUSE文件系统。
本文所有示例代码可以通过以下地址从Github上获得
git clone https://github.com/denverdino/lxcfs-initializer cd lxcfs-initializer
其manifest文件如下
apiVersion: apps/v1beta2 kind: DaemonSet metadata: name: lxcfs labels: app: lxcfs spec: selector: matchLabels: app: lxcfs template: metadata: labels: app: lxcfs spec: hostPID: true tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule containers: - name: lxcfs image: dockerhub.nie.netease.com/whale/lxcfs:2.0.8 imagePullPolicy: Always securityContext: privileged: true volumeMounts: - name: rootfs mountPath: /host volumes: - name: rootfs hostPath: path: /
注: 由于 lxcfs FUSE需要共享系统的PID名空间以及需要特权模式,所有我们配置了相应的容器启动参数。
可以通过如下命令在所有集群节点上自动安装、部署完成 lxcfs,是不是很简单?:-)
kubectl create -f lxcfs-daemonset.yaml
那么如何在Kubernetes中使用 lxcfs 呢?和上文一样,我们可以在Pod的定义中添加对 /proc 下面文件的 volume(文件卷)和对 volumeMounts(文件卷挂载)定义。然而这就让K8S的应用部署文件变得比较复杂,有没有办法让系统自动完成相应文件的挂载呢?
Kubernetes提供了 Initializer 扩展机制,可以用于对资源创建进行拦截和注入处理,我们可以借助它优雅地完成对lxcfs文件的自动化挂载。
其 manifest 文件如下
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: lxcfs-initializer-default namespace: kube-system rules: - apiGroups: ["*"] resources: ["deployments"] verbs: ["initialize", "patch", "watch", "list"] --- apiVersion: v1 kind: ServiceAccount metadata: name: lxcfs-initializer-service-account namespace: kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: lxcfs-initializer-role-binding subjects: - kind: ServiceAccount name: lxcfs-initializer-service-account namespace: kube-system roleRef: kind: ClusterRole name: lxcfs-initializer-default apiGroup: rbac.authorization.k8s.io --- apiVersion: apps/v1beta1 kind: Deployment metadata: initializers: pending: [] labels: app: lxcfs-initializer name: lxcfs-initializer spec: replicas: 1 template: metadata: labels: app: lxcfs-initializer name: lxcfs-initializer spec: serviceAccountName: lxcfs-initializer-service-account containers: - name: lxcfs-initializer image: dockerhub.nie.netease.com/whale/lxcfs-initializer:0.0.2 imagePullPolicy: Always args: - "-annotation=initializer.kubernetes.io/lxcfs" - "-require-annotation=true" --- apiVersion: admissionregistration.k8s.io/v1alpha1 kind: InitializerConfiguration metadata: name: lxcfs.initializer initializers: - name: lxcfs.initializer.kubernetes.io rules: - apiGroups: - "*" apiVersions: - "*" resources: - deployments
注: 这是一个典型的 Initializer 部署描述,首先我们创建了service account lxcfs-initializer-service-account,并对其授权了 "deployments" 资源的查找、更改等权限。然后我们部署了一个名为 "lxcfs-initializer" 的Initializer,利用上述SA启动一个容器来处理对 "deployments" 资源的创建,如果deployment中包含 initializer.kubernetes.io/lxcfs为true的注释,就会对该应用中容器进行文件挂载
我们可以执行如下命令,部署完成之后就可以愉快地玩耍了
kubectl apply -f lxcfs-initializer.yaml
下面我们部署一个简单的Apache应用,为其分配256MB内存,并且声明了如下注释 "initializer.kubernetes.io/lxcfs": "true"
其manifest文件如下
apiVersion: apps/v1beta1 kind: Deployment metadata: annotations: "initializer.kubernetes.io/lxcfs": "true" labels: app: web name: web spec: replicas: 1 template: metadata: labels: app: web name: web spec: containers: - name: web image: httpd:2 imagePullPolicy: Always resources: requests: memory: "256Mi" cpu: "500m" limits: memory: "256Mi" cpu: "500m"
我们可以用如下方式进行部署和测试
$ kubectl create -f web.yaml deployment "web" created $ kubectl get pod NAME READY STATUS RESTARTS AGE web-7f6bc6797c-rb9sk 1/1 Running 0 32s $ kubectl exec web-7f6bc6797c-rb9sk free total used free shared buffers cached Mem: 262144 2876 259268 2292 0 304 -/+ buffers/cache: 2572 259572 Swap: 0 0 0
我们可以看到 free 命令返回的 total memory 就是我们设置的容器资源容量。
我们可以检查上述Pod的配置,果然相关的 procfs 文件都已经挂载正确
$ kubectl describe pod web-7f6bc6797c-rb9sk ... Mounts: /proc/cpuinfo from lxcfs-proc-cpuinfo (rw) /proc/diskstats from lxcfs-proc-diskstats (rw) /proc/meminfo from lxcfs-proc-meminfo (rw) /proc/stat from lxcfs-proc-stat (rw) ...
在Kubernetes中,还可以通过 Preset 实现类似的功能,篇幅有限。本文不再赘述了。
感谢各位的阅读,以上就是“Kubernetes容器隔离问题实例分析”的内容了,经过本文的学习后,相信大家对Kubernetes容器隔离问题实例分析这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!
分享名称:Kubernetes容器隔离问题实例分析
文章来源:http://azwzsj.com/article/jhgdgo.html