Docker中容器底层实现技术cgroup和namespace

cgroup 实现资源限额, namespace 实现资源隔离

一、cgroup

1、概述

cgroup 全称 Control Group

Linux 操作系统通过 cgroup 设置进程使用 CPU、内存 和 IO 资源的限额

--cpu-shares、-m、--device-write-bps 就是在配置 cgroup

cgroup 可在 /sys/fs/cgroup 中找到它

2、启动一个容器--cpu-shares=512

docker  run -it  --cpu-shares 512 progrium/stress -c 1

容器内部curl怎么用 容器cgroup机制_Docker

容器的 ID:6a3c3c8f60c8

容器内部curl怎么用 容器cgroup机制_容器内部curl怎么用_02

在 /sys/fs/cgroup/cpu/docker 下,Linux 为每个容器创建一个 cgroup 目录,以容器长ID 命名:6a3c3c8f60c82d9f651ccf3f17dce279905a053626a6c93d25b4d933929e4efb

ll /sys/fs/cgroup/cpu/docker/
ll /sys/fs/cgroup/cpu/docker/6a3c3c8f60c82d9f651ccf3f17dce279905a053626a6c93d25b4d933929e4efb/
cat  /sys/fs/cgroup/cpu/docker/6a3c3c8f60c82d9f651ccf3f17dce279905a053626a6c93d25b4d933929e4efb/cpu.shares

容器内部curl怎么用 容器cgroup机制_linux_03

目录中包含所有与 cpu 相关的 cgroup 配置

文件 cpu.shares 保存的就是 --cpu-shares 的配置,值为 512

3、内存的 cgroup 配置

/sys/fs/cgroup/memory/docker

容器内部curl怎么用 容器cgroup机制_微服务_04

4、Block IO 的 cgroup 配置

/sys/fs/cgroup/blkio/docker

容器内部curl怎么用 容器cgroup机制_容器内部curl怎么用_05

二、namespace

在容器中,可看到文件系统、网卡等资源,这些资源看上去是容器自己的

如网卡,每个容器都会认为自己有一块独立的网卡,即使 host 上只有一块物理网卡,使得容器更像一个独立的计算机

Linux 实现这种方式的技术是 namespace

namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它

namespace 实现了容器间资源的隔离

Linux 使用了六种 namespace,分别对应六种资源:Mount、UTS、IPC、PID、Network 和 User

1、Mount namespace

Mount namespace 让容器看上去拥有整个文件系统

容器有自己的 / 目录,可以执行 mount 和 umount 命令,操作只在当前容器中生效,不会影响到 host 和其他容器

2、UTS namespace

UTS namespace 让容器有自己的 hostname

默认情况下,容器的 hostname 是它的短ID,可以通过 -h 或 --hostname 参数设置

docker  run  -h  zolahost  -it  centos

容器内部curl怎么用 容器cgroup机制_微服务_06

3、IPC namespace

IPC namespace 让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与 host 和其他容器的 IPC 混在一起

4、PID namespace

容器在 host 中以进程的形式运行

容器内部curl怎么用 容器cgroup机制_linux_07

查看容器在host上的进程

ps  axf

-a : 显示现行终端机下的所有进程,包括其他用户的进程

-x :通常与 a 这个参数一起使用,可列出较完整信息

-f : 用树形格式来显示进程

容器内部curl怎么用 容器cgroup机制_容器内部curl怎么用_08

此次练习使用的是docker版本:

Docker version 19.03.8

从Docker 1.11开始,Docker容器运行已经不是简单的通过Docker daemon来启动,而是集成了containerd、runC等多个组件

A、Docker Daemon

作为Docker容器管理的守护进程,Docker Daemon从最初集成在docker命令中(1.11版本前),到后来的独立成单独二进制程序(1.11版本开始),其功能正在逐渐拆分细化,被分配到各个单独的模块中去

B、Containerd

containerd是容器技术标准化之后的产物,为了能够兼容OCI标准,将容器运行时及其管理功能从Docker Daemon剥离。理论上,即使不运行dockerd,也能够直接通过containerd来管理容器。(containerd本身只是一个守护进程,容器的实际运行时由runC控制)

containerd主要职责是镜像管理(镜像、元信息等)、容器执行(调用最终运行时组件执行)

containerd向上为Docker Daemon提供了gRPC接口,使得Docker Daemon屏蔽下面的结构变化,确保原有接口向下兼容。向下通过containerd-shim结合runC,使得引擎可以独立升级,避免之前Docker Daemon升级会导致所有容器不可用的问题

C、Docker、containerd和containerd-shim之间的关系

观察进程之间的关联

yum -y install psmisc

查看进程之间的父子关系

pstree -l -a -A dockerd的PID

容器内部curl怎么用 容器cgroup机制_docker_09

说明:当Docker daemon启动之后,dockerd和docker-containerd进程一直存在

当启动容器之后,docker-containerd进程(即containerd组件)会创建docker-containerd-shim进程,其中的参数 容器长ID 就是要启动容器的id

docker-containerd-shim子进程,是实际在容器中运行的进程
docker-containerd-shim另一个参数,是一个和容器相关的目录ls /var/run/docker/containerd/,里面的内容有:
find / -name config.json
ls /run/containerd/io.containerd.runtime.v1.linux/moby/

容器内部curl怎么用 容器cgroup机制_docker_10

ls /var/run/docker/
ls /var/run/docker/containerd/

容器内部curl怎么用 容器cgroup机制_Docker_11

其中包括了容器配置和标准输入、标准输出、标准错误三个管道文件

D、RunC

OCI定义了容器运行时标准,runC是Docker按照开放容器格式标准(OCF, Open Container Format)制定的一种具体实现

runC是从Docker的libcontainer中迁移而来的,实现了容器启停、资源隔离等功能

Docker默认提供了docker-runc实现,事实上,通过containerd的封装,可以在Docker Daemon启动的时候指定runc的实现

从Docker 1.11之后,Docker Daemon被分成了多个模块以适应OCI标准

拆分之后:

containerd独立负责容器运行时和生命周期(如创建、启动、停止、中止、信号处理、删除等),其他一些如镜像构建、卷管理、日志等由Docker Daemon的其他模块处理

容器内部curl怎么用 容器cgroup机制_容器内部curl怎么用_12

dockerd 对应启动container(容器)的关系:

docker run 访问了docker服务提供的接口

然后由 dockerd 装载 image,拉起 container

E、查看容器自己的进程

进入到某个容器,然后 ps

docker  exec  -it  ID  bash

docker  exec  -it  94028ecfa080  bash

ps  axf

容器内部curl怎么用 容器cgroup机制_docker_13

容器中进程的 PID 不同于 host 中对应进程的 PID

容器中 PID=1 的进程当然也不是 host 的 init 进程

容器拥有自己独立的一套 PID,这就是 PID namespace 提供的功能

5、Network namespace

Network namespace 让容器拥有自己独立的网卡、IP、路由等资源

6、User namespace

User namespace 让容器能够管理自己的用户,host 不能看到容器中创建的用户

容器内部curl怎么用 容器cgroup机制_微服务_14

在容器中创建docker01用户,不会出现在host里面

在host中创建的zola用户,不会出现在容器中

create      创建容器  

run         运行容器  

pause       暂停容器  

unpause     取消暂停继续运行容器  

stop        发送 SIGTERM 停止容器  

kill        发送 SIGKILL 快速停止容器  

start       启动容器  

restart     重启容器  

attach      attach 到容器启动进程的终端  

exec        在容器中启动新进程,通常使用 "-it" 参数  

logs        显示容器启动进程的控制台输出,用 "-f" 持续打印  

rm          从磁盘中删除容器