容器本身没有价值, 有价值的是"容器编排"

From Dcoker To Kurbernets

Linux 容器:

  • 一组联合挂载在 /var/lib/docker/aufs/mnt 上的 rootfs,这一部分我们称为“容器镜像”(Container Image),是容器的静态视图
  • 一个由 Namespace+Cgroups 构成的隔离环境,这一部分我们称为“容器运行时”(Container Runtime),是容器的动态视图

安装docker

  • 测试开发实验(centos7.x)
1
2
3
4
# 安装docker
sudo curl -fsSL get.docker.com -o get-docker.sh
# 指定国内mirror
sudo sh get-docker.sh --mirror Aliyun
  • 启动docker
1
2
3
4
5
6
# systemd 是系统init程序,开启初始化系统
# 管理资源(.mount, .service, .target, .wants)
# systemctl 是其进程管理命令
# 如果需求开机自启,建议打开
# sudo systemctl enable docker
sudo systemctl start docker
  • 配置科大镜像加速
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# centos 7.x
# 通过systemd进行操作  /etc/docker/daemon.json
sudo mkdir -p /etc/docker
# set paste
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

# docker daemon-reload
# docker restart
sudo systemctl daemon-reload
sudo systemctl restart docker

# docker info查看修改结果

镜像理解

镜像是一种轻量级的,可执行的独立软件包.

用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时所需的库、环境变量和配置文件。

  • 基于UnionFS的文件组织方式
  • bootfs+rootfs

Dockerfile

用来构建Docker镜像, 由一系列命令和参数构成的脚本文件.

保留字 作用
FROM 当前镜像是基于哪个镜像的 第一个指令必须是FROM
MAINTAINER 镜像维护者的姓名和邮箱地址
RUN 构建镜像时需要运行的指令
EXPOSE 当前容器对外暴露出的端口号
WORKDIR 指定在创建容器后,终端默认登录进来的工作目录,一个落脚点
ENV 用来在构建镜像过程中设置环境变量
ADD 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar包
COPY 类似于ADD,拷贝文件和目录到镜像中
将从构建上下文目录中<原路径>的文件/目录复制到新的一层的镜像内的<目标路径>位置
VOLUME 容器数据卷,用于数据保存和持久化工作
CMD 指定一个容器启动时要运行的命令
Dockerfile中可以有多个CMD指令,但只有最后一个生效,CMD会被docker run之后的参数替换
ENTRYPOINT 指定一个容器启动时要运行的命令
ENTRYPOINT的目的和CMD一样,都是在指定容器启动程序及其参数

其实, docker默认会维护一个ENTRYPOINT(/bin/sh -c), 所以, ENTRYPOINT+CMD (/bin/sh -c "python app.py") 从字面上快来看, CMD就是shell服务运行的指令, ENTRYPOINT是系统启动服务运行的指令

Docker 网络

docker_network

  1. Docker启动, 在宿主机初始化一个docker0的虚拟网桥
  2. Docker分配一个宿主机中未被占用的私有网段给docker0, 后面启动的容器也会自动分配同一网段地址
  3. 创建容器,Docker同步创建veth pair网络通道, 一个是eth0,在运行容器中,另一个挂载docker0vethXXX.实现宿主机-容器,容器-容器间的虚拟共享网络.

常用命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 查看docker网络信息
docker network ls
# 创建新的网桥
docker network create -d bridge 网桥名称
# 删除一个网桥
docker network rm 网桥名称
# 指定容器使用网桥
docker run -d -p 8890:80 --name nginx001 --network info nginx 
docker run -d -p 8891:80 --name nginx002 --network info nginx 
#`注意:一旦指定网桥后--name指定名字就是主机名,多个容器指定在同一个网桥时,可以在任意一个容器中使用主机名与容器进行互通`
# curl http://nginx001  在nginx002中即可通过http://container-name 与同一个网桥中的容器进行网络交互

Dcoker 数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷 可以在容器之间共享和重用
  • 数据卷 的修改会立马生效
  • 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除
1
2
3
4
5
6
7
8
# 创建数据卷 默认在/var/lib/docker/volumes目录下创建 自动在my-vol目录下创建_data数据通道文件
docker volume create my-vol
# 查看my-vol详细
docker volumn inspect my-vol
# 挂在数据卷
docker run -d -P --name web -v my-vol:/usr/share/nginx/html  nginx
# 删除数据卷
docker volume rm my-vol

Docker compose

命令对象与格式

对于 Compose 来说,大部分命令的对象既可以是项目本身,也可以指定为项目中的服务或者容器。如果没有特别的说明,命令对象将是项目,这意味着项目中所有的服务都会受到命令影响。

执行 docker-compose [COMMAND] --help 或者 docker-compose help [COMMAND] 可以查看具体某个命令的使用格式。

docker-compose 命令的基本的使用格式是

1
docker-compose [-f=<arg>...] [options] [COMMAND] [ARGS...]

命令选项

  • -f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。
  • -p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。
  • --x-networking 使用 Docker 的可拔插网络后端特性
  • --x-network-driver DRIVER 指定网络后端的驱动,默认为 bridge
  • --verbose 输出更多调试信息。
  • -v, --version 打印版本并退出。

命令使用说明

up

格式为 docker-compose up [options] [SERVICE...]

  • 该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。

  • 链接的服务都将会被自动启动,除非已经处于运行状态。

  • 可以说,大部分时候都可以直接通过该命令来启动一个项目。

  • 默认情况,docker-compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。

  • 当通过 Ctrl-C 停止命令时,所有容器将会停止。

  • 如果使用 docker-compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。

  • 默认情况,如果服务容器已经存在,docker-compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容

down
  • 此命令将会停止 up 命令所启动的容器,并移除网络
exec
  • 进入指定的容器。
ps

格式为 docker-compose ps [options] [SERVICE...]

列出项目中目前的所有容器。

选项:

  • -q 只打印容器的 ID 信息。
restart

格式为 docker-compose restart [options] [SERVICE...]

重启项目中的服务。

选项:

  • -t, --timeout TIMEOUT 指定重启前停止容器的超时(默认为 10 秒)。
rm

格式为 docker-compose rm [options] [SERVICE...]

删除所有(停止状态的)服务容器。推荐先执行 docker-compose stop 命令来停止容器。

选项:

  • -f, --force 强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。
  • -v 删除容器所挂载的数据卷。
start

格式为 docker-compose start [SERVICE...]

启动已经存在的服务容器。

stop

格式为 docker-compose stop [options] [SERVICE...]

停止已经处于运行状态的容器,但不删除它。通过 docker-compose start 可以再次启动这些容器。

选项:

  • -t, --timeout TIMEOUT 停止容器时候的超时(默认为 10 秒)。
top

查看各个服务容器内运行的进程。

unpause

格式为 docker-compose unpause [SERVICE...]

恢复处于暂停状态中的服务。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
version: "3.2"

services:
  tomcat01: #服务名称
    container_name: tomcat01 # 相当于run 的 --name
    image: tomcat:8.0-jre8 #使用哪个镜像  相当于run image
    ports:  #用来完成host与容器的端口映射关系  相当于run -p
      - "8080:8080"
    volumes: #完成宿主机与容器中目录数据卷共享  相当于run -v
      #- /root/apps:/usr/local/tomcat/webapps #使用自定义路径映射
      - tomcatwebapps01:/usr/local/tomcat/webapps
    networks: #代表当前服务使用哪个网络桥     相当于run --networ
      - hello

  tomcat02: #服务名称
    container_name: tomcat02
    image: tomcat:8.0-jre8 #使用哪个镜像
    ports:  #用来完成host与容器的端口映射关系
      - "8081:8080"
    volumes: #完成宿主机与容器中目录数据卷共享
      #- /root/apps:/usr/local/tomcat/webapps #使用自定义路径映射
      - tomcatwebapps02:/usr/local/tomcat/webapps
    networks: #代表当前服务使用哪个网络桥
      - hello

  mysql:
    image: mysql:5.7.32
    container_name: mysql
    ports:
      - "3307:3306"
    volumes:
      - mysqldata:/var/lib/mysql
      - mysqlconf:/etc/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=root
    networks:
      - hello

  redis:
    image: redis:5.0.10
    container_name: redis
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data
    networks:
      - hello
    command: "redis-server --appendonly yes" #run 镜像之后用来覆盖容器内容默认命令

volumes:  #声明上面服务所使用的自动创建的卷名
  tomcatwebapps01: #声明指令的卷名  compose自动创建该卷名但是会在之前加入项目名
    external:    #使用自定义卷名
      false       #true确定使用指定卷名  注意:一旦使用外部自定义卷名启动服务之前必须手动创建
  tomcatwebapps02:
  mysqldata:
  mysqlconf:
  redisdata:

networks: #定义服务用到桥
  hello: #定义上面的服务用到的网桥名称 默认创建就是 bridge
    external:
      true   #使用外部指定网桥  注意:网桥必须存在

常用命令

辅助命令

1
2
3
4
# 1.安装完成辅助命令
    docker version	--------------------------	查看docker的信息
    docker info		--------------------------	查看更详细的信息
    docker --help	--------------------------	帮助命令

镜像命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 1.查看本机中所有镜像
    docker images	--------------------------	列出本地所有镜像
        -a			列出所有镜像(包含中间映像层)
        -q			只显示镜像id

# 2.搜索镜像
	docker search [options] 镜像名	-------------------	去dockerhub上查询当前镜像
        -s 指定值		列出收藏数不少于指定值的镜像
        --no-trunc	    显示完整的镜像信息

# 3.从仓库下载镜像
	docker pull 镜像名[:TAG|@DIGEST]	----------------- 下载镜像

# 4.删除镜像
	docker rmi 镜像名	--------------------------  删除镜像
		-f		强制删除

容器命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 1.运行容器
	docker run 镜像名	--------------------------	镜像名新建并启动容器
        --name 					别名为容器起一个名字
        -d							启动守护式容器(在后台启动容器)
        -p 							映射端口号:原始端口号		 指定端口号启动

	例:docker run -it --name myTomcat -p 8888:8080 tomcat
   	 docker run -d --name myTomcat -P tomcat

# 2.查看运行的容器
	docker ps					--------------------------	列出所有正在运行的容器
	-a			正在运行的和历史运行过的容器
	-q			静默模式,只显示容器编号

# 3.停止|关闭|重启容器
	docker start   容器名字或者容器id  --------------- 开启容器
	docker restart 容器名或者容器id    --------------- 重启容器
	docker stop  容器名或者容器id 	    ------------------ 正常停止容器运行
	docker kill  容器名或者容器id      ------------------ 立即停止容器运行

# 4.删除容器
	docker rm -f 容器id和容器名     
	docker rm -f $(docker ps -aq)		--------------------------	删除所有容器

# 5.查看容器内进程
	docker top 容器id或者容器名 ------------------ 查看容器内的进程

# 6.查看查看容器内部细节
	docker inspect 容器id 		------------------ 查看容器内部细节

# 7.查看容器的运行日志
	docker logs [OPTIONS] 容器id或容器名	------------------ 查看容器日志
        -t			 加入时间戳
        -f			 跟随最新的日志打印
        --tail 	 数字	显示最后多少条

# 8.进入容器内部
	docker exec [options] 容器id 容器内命令 ------------------ 进入容器执行命令
        -i		以交互模式运行容器,通常与-t一起使用
        -t		分配一个伪终端    shell窗口   bash 

# 9.容器和宿主机之间复制文件
	docker cp 文件|目录 容器id:容器路径           -----------------   将宿主机复制到容器内部
	docker cp 容器id:容器内资源路径 宿主机目录路径  -----------------   将容器内资源拷贝到主机上

# 10.数据卷(volum)实现与宿主机共享目录
	docker run -v 宿主机的路径|任意别名:/容器内的路径 镜像名
		注意: 
				1.如果是宿主机路径必须是绝对路径,宿主机目录会覆盖容器内目录内容
				2.如果是别名则会在docker运行容器时自动在宿主机中创建一个目录,并将容器目录文件复制到宿主机中

# 11.打包镜像
	docker save 镜像名 -o  名称.tar

# 12.载入镜像
	docker load -i   名称.tar

# 13.容器打包成新的镜像
	docker commit -m "描述信息" -a "作者信息"   (容器id或者名称)打包的镜像名称:标签

docker镜像

docker的镜像实际是由一层一层的文件系统组成。


容器, 是一个特殊的进程.

进程 –> Namespace&Cgroup –> rootfs –> docker

进程 & 程序

  • 程序: 用于组织和操作数据和设备的可执行指令的集合
  • 进程: 程序运行起来后的动态的计算机执行要素的总和,程序的动态组织运行的逻辑集合

隔离 & 限制

  • 启动一个busybox的container
1
2
3
4
5
6
7
8
# 启动busybox镜像
sudo docker run -it busybox /bin/sh
# 查看当前busybox的container中的所有进程
ps -a

#   PID  USER     TIME   COMMAND
#    1   root     0:00   /bin/sh
#    7   root     0:00   ps -a

进入一个busybox的容器内,只存在启动命令进程/bin/sh和查看ps命令进程ps -a。 并没有云主机的运行进程–container被宿主机隔离了。

container真的是独立自主的运行环境?–这个虚拟机还有什么区别?

虚拟机虚拟了整个宿主操作系统的硬件系统, 一个用于主机的所有的镜像系统

Docker 伪装了进程, 提供一个实现和维护进程的应用.

  • Namespace通过clone将宿主机中一个进程封装成一个局域独立的进程.
  • Cgroups则对这样一个进程进行运行时的资源使用限制,限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等.

Namepsace

Namespacekernel一个功能,隔离一系列的系统资源空间。用于将一个进程只能使用指定的内容。

Namespace类型 调用类型参数 说明
Mount Namespace CLONE_NEWNS 隔离进程所能看到的挂载点视图
UTS Namespace CLONE_NEWUTS 隔离nodename和domainname UTS 用于属于自己的hostname
IPC Namespace CLONE_NEWIPC 隔离System V PIC和POSIX queues
PID Namesapce CLONE_NEWPID 隔离进程PID
Network Namespace CLONE_NEWNET 隔离网络设备、IP地址端口等网络Namespace
User Namespace CLONE_NEWUSER 隔离用户组
  • Namespace API
  1. clone()将host namespace克隆到虚拟namspace
  2. unshare()将进程移除namespace ??
  3. setns()将进程加入namespace中 ??
  • Union File System 把其他文件系统联合到一个联合挂载点的文件系统服务

UTS Namespace

UTS NS 用于隔离nodename和domainname

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

/* ************************************************************************
> File Name:     main.go
> Author:        hanzi_zhu
> Created Time:  Wed 02 Nov 2022 09:46:31 AM EDT
> Description:   mydocker-UTS Namespace
 *********************************************************************** */

package main

import (
    "os/exec"
    "syscall"
    "os"
    "log"
)

func main(){
    // 指定被fork出来的新进程内初始命令 默认sh来执行
    cmd := exec.Command("sh")
    // syscall 调用clone 在sh环境通过CLONE_NEWUTS创建UTS Namespace
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS,
    }

    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }
}

// go run main.go 通过sh,进入子UTS Namepsace 新进程
// 做两实验
// pstree -pl [yum -y install psmisc]
// sshd(1067)─┬─sshd(1601)─┬─bash(1608)───go(85015)─┬─main(85075)─┬─sh(85080)───pstree(85215)
// 其中main程序PID 85075
// echo $$ [查看当前进程]
// 当前进程为85080 同新的进程sh

// readlink /proc/85075/ns/uts  [查看父进程main的uts] 
// uts:[4026531838]
// readlink /proc/85080/ns/uts  [查看sh新进程的uts]
// uts:[4026532417]
// 父子进程的uts是不一致的

// hostname -b bird [5080 修改hostname]
// hostname  [bird]
// 再到main父进程间查看hostname [MiWiFi-R4AC-srv]
// 子进程的操作不影响父进程 隔离开来

IPC/PID/MountUser Namespace

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

/* ************************************************************************
> File Name:     main.go
> Author:        hanzi_zhu
> Created Time:  Wed 02 Nov 2022 09:46:31 AM EDT
> Description:   mydocker main
 *********************************************************************** */

package main

import (
    "os/exec"
    "syscall"
    "os"
    "log"
)

func main(){
    // 指定被fork出来的新进程内初始命令 默认sh来执行
    cmd := exec.Command("sh")
    // syscall 调用clone 在sh环境通过CLONE_NEWUTS创建UTS Namespace
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC |
                    syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
                    syscall.CLONE_NEWUSER,
    }
    // cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(1), Gid: uint32(1)} // 在linux 3.10中不能这样加入 否则报fork/exec /usr/bin/sh: operation not permitted
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }

    os.Exit(-1)
}
// 注意:
// Centos 内核版本 3.10 需要手动开启user namesapce
// 不然运行go run main.go 报fork/exec /usr/bin/sh: invalid argument
// 解决方法:echo 640 > /proc/sys/user/max_user_namespaces

Network namespace

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

/* ************************************************************************
> File Name:     main.go
> Author:        hanzi_zhu
> Created Time:  Wed 02 Nov 2022 09:46:31 AM EDT
> Description:   mydocker main
 *********************************************************************** */

package main

import (
    "os/exec"
    "syscall"
    "os"
    "log"
)

func main(){
    // 指定被fork出来的新进程内初始命令 默认sh来执行
    cmd := exec.Command("sh")
    // syscall 调用clone 在sh环境通过CLONE_NEWUTS创建UTS Namespace
    cmd.SysProcAttr = &syscall.SysProcAttr{
        Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC |
                    syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
                    syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET,
    }
    //cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(1), Gid: uint32(1)}
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        log.Fatal(err)
    }

    os.Exit(-1)
}
// 对比host和main进程内net信息
// 命令:ip addr
// host的net信息完整
// main进程nei信息为空--该进程网络命名空间为隔离状态

Cgroups

Cgroups实现对进程以及其子进程的资源限制、控制和统计能力。这样就可以控制Namespace隔离空间的资源使用、监控和统计,使得进程之间合理使用用以宿主机的而基础设施。

  1. 进程分组管理
  2. subsystem是一组资源控制模块,其关联指定的限制的cgroup,对Cgroup中的进程做相应的限制和控制 例如blkio\cpu\cpuset\cpuacct\devices\freezer\memory\net_cls\net_prio\ns
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
cd /sys/fs/cgroup
ll

# drwxr-xr-x blkio  设置对块的输入输出的访问控制
# lrwxrwxrwx cpu -> cpu,cpuacct  进程cpu的调度策略
# lrwxrwxrwx cpuacct -> cpu,cpuacct 统计cgroup中进程的CPU占用
# drwxr-xr-x cpu,cpuacct
# drwxr-xr-x cpuset   多核机器中设置cgroup中进程可以使用cpu和内存
# drwxr-xr-x devices  设备的访问
# drwxr-xr-x freezer  用于挂起和恢复cgroup中进程
# drwxr-xr-x hugetlb
# drwxr-xr-x memory   控制cgroup进程的内存
# lrwxrwxrwx net_cls -> net_cls,net_prio  进程网络分类,便于区分进程的网络流量
# drwxr-xr-x net_cls,net_prio  
# lrwxrwxrwx net_prio -> net_cls,net_prio  网络优先级
# drwxr-xr-x perf_event
# drwxr-xr-x pids
# drwxr-xr-x systemd
  1. hierachy 实现Cgroup的树状结构,完成Cgroup的继承
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
sudo docker run -itd busybox
# b69f64965d0eef3efd533667c726e2c35997eff2673e9d563244b90300fa05b
cd /sys/fs/cgroup/memory/b69f64965d0eef3efd533667c726e2c35997eff2673e9d563244b90300fa05b
ll
# -rw-r--r-- 1 root root 0 Mar 23 22:47 cgroup.clone_children
# --w--w--w- 1 root root 0 Mar 23 22:47 cgroup.event_control
# -rw-r--r-- 1 root root 0 Mar 23 22:47 cgroup.procs
# -rw-r--r-- 1 root root 0 Mar 23 22:47 tasks
# -rw-r--r-- 1 root root 0 Mar 23 22:47 notify_on_release
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.failcnt
# --w------- 1 root root 0 Mar 23 22:47 memory.force_empty
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.failcnt
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.limit_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.max_usage_in_bytes
# -r--r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.slabinfo
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.tcp.failcnt
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.tcp.limit_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.tcp.max_usage_in_bytes
# -r--r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.tcp.usage_in_bytes
# -r--r--r-- 1 root root 0 Mar 23 22:47 memory.kmem.usage_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.limit_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.max_usage_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.memsw.failcnt
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.memsw.limit_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.memsw.max_usage_in_bytes
# -r--r--r-- 1 root root 0 Mar 23 22:47 memory.memsw.usage_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.move_charge_at_immigrate
# -r--r--r-- 1 root root 0 Mar 23 22:47 memory.numa_stat
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.oom_control
# ---------- 1 root root 0 Mar 23 22:47 memory.pressure_level
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.soft_limit_in_bytes
# -r--r--r-- 1 root root 0 Mar 23 22:47 memory.stat
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.swappiness
# -r--r--r-- 1 root root 0 Mar 23 22:47 memory.usage_in_bytes
# -rw-r--r-- 1 root root 0 Mar 23 22:47 memory.use_hierarchy

cat memory.limit_in_bytes  # 查看cgroup的内存限制
cat memory.usage_in_bytes  # 查看当前cgroup的内存使用大小