您现在的位置是:首页 >  编程分享 > LINUX丨SYSTEMLINUX丨SYSTEM

容器隔离问题调研

Markz0928 转载 编程分享 2018-07-18

简介 会获取到错误的数据。 /proc/meminfo,/proc/cpuinfo, /proc/stat


背景

2018-03-15

  • 容器中看到的/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

业界讨论主要有以下几种方案:

IdId 方案一、

-  直接修改proc文件系统 

- https://lkml.org/lkml/2012/5/28/299

- mount -t proc -o meminfo-from-cgroup none /path/to/container/proc

缺点:不可能要求并入内核




IdId 方案二、

- Procg 方案: 把文件系统挂载进容器,替代原本的proc文件系统

-  通过读cgroup指定的信息替代原本的/proc/meminfo信息

- https://github.com/fabiokung/procg/

缺点:内核的cgroup中没有公开读取内存数据函数接口




IdId 方案三、

基于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函数可以看出,初始化的过程包括:

  1. 将运行时工作目录/run/lxcfs/controllers/ 挂载到 tmpfs文件系统

  2. 将当前系统的各个group子系统重新挂载到 /run/lxcfs/controllers/ 目录

  3. 调用libfuse库的主函数 fuse_main(), 指定用户态文件系统的目标目录- /var/lib/lxcfs/

  4. 使用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子系统信息

存在的问题以及如何解决

  1. LXCFS的部署问题以及带来的影响和成本? 见如下补充的 LxcFS - k8s实践
  2. 故障恢复,如何自动remount? 如果lxcfs进程重启了,那么容器里的/proc/cpuinfo等等都会报transport connected failed 这个是因为/var/lib/lxcfs会删除再重建,inode变了。所以参考豆瓣的做法,共享mount事件,重新给容器挂载

User Namespace

解决什么问题?

  • 用户UID/GID的映射,导致容器中的进程具有宿主上相同uid/gid用户的权限

  • docker 从1.10版本 开始支持user namespace 隔离。使用参数:DOCKER_OPTS="--userns-remap=default"

相关文档链接:

  1. 基于LXCFS增强docker容器隔离性的分析 2015.12.9
  2. docker容器显示问题及修复 2017-03-23
  3. lxc-1.0.9 lxcfs-2.0.0 fuse-2.8.7源码详细注释分析
  4. Kubernetes之路 2 - 利用LXCFS提升容器资源可见性
  5. Kubernetes Initializers
  6. 看看大阿里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 实现类似的功能,篇幅有限。本文不再赘述了。

总结

本文介绍了通过 lxcfs 提供容器资源可见性的方法,可以帮助一些遗留系统更好的识别容器运行时的资源限制。

同时,在本文中我们介绍了利用容器和DaemonSet的方式部署lxcfs FUSE,这不但极大简化了部署。也可以方便地利用Kubernetes自身的容器管理能力,支持lxcfs进程失效时自动恢复,在集群伸缩时也可以保证节点部署的一致性。这个技巧对于其他类似的监控或者系统扩展都是适用的。

另外我们介绍了利用Kubernetes的扩展机制 Initializer,实现对 lxcfs 文件的自动化挂载。整个过程对于应用部署人员是透明的,可以极大简化运维复杂度。同时利用类似的方法,我们可以灵活地定制应用部署的行为,满足业务的特殊要求。

原地址:https://my.oschina.net/markz0928/blog/1833673


本篇评论 —— 揽流光,涤眉霜,清露烈酒一口话苍茫。


    声明:参照站内规则,不文明言论将会删除,谢谢合作。


      最新评论



ABOUT ME

Name:袅袅牧童 | Arkin

Job:Web全栈技术工程师

WeChat:nnmutong

Email:nnmutong@icloud.com

标签云