Docker相关的概念和原理

chroot

chroot就是可以改变某进程的根目录,使这个程序不能访问目录之外的其他目录。
Docker是利用Linux的Namespace、Cgroups和联合文件系统三大机制来保证实现的,它的原理是使用Namespace做主机名、网络、pid等资源的隔离,
使用Cgroups对进程或者进程组做资源的限制,联合文件系统用于镜像构建和容器运行环境。

Docker的命名空间

Docker主要用到一下五种命名空间:

  • pid namespace:用于隔离进程id。
  • net namespace:隔离网络接口。
  • mnt namespace:文件系统挂在点隔离。
  • ipc namespace:信号量,消息队列和共享内存的隔离。
  • ust namespace:主机名和域名的隔离。

Cgroups是一种unix内核功能,可以限制和隔离进程的资源使用情况。在容器的实现中,Cgroups通常用来限制容器的CPU和内存等资源的使用。

Docker有两个至关重要的组件:runC和containerd。

  • runC是Docker官方按照OCI容器运行时标准得到的一个实现。runC是一个用来运行容器的轻量级工具,是真正用来运行容器的。
  • containerd是从Docker中剥离出来的,containerd通过containerd-shim启动并管理runC,可以说containerd真正管理了容器的生命周期。

可以通过sudo ps aux|grep dockerd,查找到dockerd的pid,然后使用sudo pstree -l -a -A pid查看 docker组件之间的调用关系。

docker启动时,containerd就随之启动,dockerd与containerd一直存在。containerd会创建container-shim充当垫片进程,然后启动容器的真正进程。

镜像相关操作

镜像操作:

  • 拉取镜像,docker pull从本地拉取镜像获取从远程拉取镜像到本地;
  • 重命名镜像,docker tag重命名镜像
  • 查看镜像,docker images /docker image ls 查看现有镜像。
  • 删除镜像,docker rmi 镜像编号;
  • 构建镜像,docker build基于Dockerfile构建镜像,docker commit基于运行的容器提交为镜像。

Dockerfile操作

Dockerfile常用指令:

Dockerfile指令 指令介绍
FROM Dockerfile除了注释行,第一行必须是FROM,FROM后面跟镜像名称,作为基础镜像
RUN 跟具体命令,类似于Linux命令行的执行命令
ADD 拷贝本地文件或者远程文件到镜像内
COPY 拷贝本地文件到镜像内
USER 指定容器启动的用户
ENDPOINT 容器的启动命令
CMD CMD为ENDPOINT提供默认参数,也可以单独使用CMD指定容器启动参数
ENV 指定容器运行时的环境变量,格式为key=value
ARG 定义外部变量,构建镜像时可以使用build-arg=的格式传递参数用于构建
EXPOSE 指定容器监听的端口,格式为[port]/tcp或者[port]/udp
WORKDIR 为Dockerfile中跟在其后的所有RUN、CMD、ENDPOINT、COPY和ADD命令设置工作目录,只在当前有效

Docker清除数据的命令:

  • docker image prune -af 仅仅清除没有被容器使用的镜像文件
  • docker system prune -f 清除多余的数据,包括包含的容器、多余的镜像、未使用的volume等等。

Dockerfile书写原则:

  • 单一指责:一个Dockerfile只完成一个功能
  • 提供备注信息:提供注释信息
  • 保持容器最小化:只保留必要的内容
  • 合理选择基础镜像
  • 使用.dockerignore文件
  • 尽量使用构建缓存
  • 正确设置时区
    • Ubuntu和Debian系统: RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
      && RUN echo "Asia/Shanghai" >> /etc/timezone
    • Centos系统:RUN ln -sd /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  • 使用国内软件源增加构建镜像构建速度
  • 最小化镜像层数。

Dockerfile书写建议:

  • Run指令在构建时会生成一个新的镜像层并且执行RUN指令后面的内容。
    • RUN指令后面跟内容复杂时,建议使用反斜杠结尾且换行;
    • RUN后面的内容尽量按照字母顺序排列,提高可读性。
  • CMD和ENDPOINT
    • CMD/ENDPOINT指令方式:CMD/ENDPOINT ["command","param"](exec模式,Docker 官方推荐的方式);
      CMD/ENDPOINT command param ,称为shell模式。
    • exec模式,容器的1号进程就是CMD/ENDPOINT指定的命令,shell模式启动的进程在容器中实际上并不是1号进程。
    • ENDPOINT可以和CMD指令结合使用,也可以单独使用,CMD只能单独使用。
    • Dockerfile中如果使用了ENDPOINT指令,启动Docker容器时需要使用--entrypoint参数才能覆盖Dockerfile中的ENDPOINT指令,
      使用CMD设置的命令,可以被docker run后面的参数直接覆盖。
  • ADD和COPY
    • COPY指令只支持基本的文件和文件拷贝功能,ADD则支持更多文件来源类型,比如自动提取tar包,并且可以支持源文件为URL格式。

基于内核的弱隔离系统如何保证安全

Docker与虚拟机的区别:

  • 虚拟机是通过管理系统(hypervisor)模拟出CPU、内存、网络等硬件,这样做的好处是虚拟机有自己的内核和操作系统,
    并且硬件是通过虚拟机管理系统模拟出来的,用户程序无法直接使用到主机的操作系统和硬件资源,虚拟机也对隔离性和安全性有着更好的保证。
  • Docker容器则是通过Linux内核的Namespace技术实现了文件系统、进程、设备以及网络的隔离,然后通过cgroups对CPU、内存等资源进行限制,
    最终实现容器之间互相不受影响,由于容器的隔离性仅仅依靠内核来提供,因此容器的隔离型也远弱于虚拟机。
Docker容器的安全问题 解决方法
Docker作为一款容器引擎,
本身也会存在一些安全漏洞
使用最新的版本,有相应的命名空间
镜像安全 私有镜像仓库自己进行扫描
Namespace隔离不够,然后部分关键
内容没有被安全隔离
宿主机内核应该尽量安装最新补丁;
使用capabilities划分权限;使用SELinux、
AppArmor、GRSecurity等安全组件加强安全;
每个容器都要限制资源使用
所有容器共享主机内核 使用安全容器来代替runC

cadvisor相关

cadvisor可以监控物理机和容器的状态;可以展示监控历史数据。
cadvisor的监控是基于cgroupsde。cgroups的工作目录是/sys/fs/cgroup,/sys/fs/cgroup目录,容器启动之后,可以在
/sys/fs/cgroup/memory/docker目录下查看内存相关的数据。memory.limit_in_bytes(限制总量)、memory.usage_in_bytes(使用量)。
如果监控网络信息,在/proc/{pid}/net/dev下面读取。

为什么容器需要Namespace

Namespace产生时间

命名空间与版本的关系:

Namespace名称 作用 内核版本
Mount(mnt) 隔离挂载点 2.4.19
Process(pid) 隔离进程id 2.6.24
Network(net) 隔离网络设备,端口号 2.6.29
Interprocess Communication(ipc) 隔离System IPC和POSIX Message Queue 2.6.19
UTS Namespace(uts) 隔离主机名和域名 2.6.19
User Namespace(user) 隔离用户和用户组 3.8
Control group Namespace 隔离cgroups根目录 4.6
Time Namespace 隔离系统时间 5.6

各个Namespace的作用

Mount Namespace

Mount Namespace用来隔离不同的进程或进程组看到的挂载点。
使用unshare命令可以新建Mount Namespace,并且在新建的Mount Namespace内Mount是和外部完全隔离的。

[root@centos7 centos]# sudo unshare --mount --fork /bin/bash
[root@centos7 centos]# mkdir /tmp/tmpfs
[root@centos7 centos]# mount -t tmpfs -o size=20m tmpfs /tmp/tmpfs
[root@centos7 centos]# ls -l /proc/self/ns

PID Namespace

PID Namespace的作用是用来隔离进程。

[root@centos7 centos]# sudo unshare --pid --fork --mount-proc /bin/bash
[root@centos7 centos]# ps aux # 查看进程

UTS Namespace

UTS Namespace主要是用来隔离主机名的,允许每个UTS Namespace拥有一个独立的主机名。

[root@centos7 centos]# sudo unshare --uts --fork /bin/bash
[root@centos7 centos]# hostname -b docker
[root@centos7 centos]# hostname

IPC Namespace

IPC Namespace主要是用来隔离进程间通信。

[root@centos7 centos]# sudo unshare --ipc --fork /bin/bash
[centos@centos7 ~]$ ipcs -q
[root@centos7 centos]# ipcmk -Q

ipcs -q命令:用来查看系统间通信队列列表。
ipcmk -Q命令:用来创建系统间通信队列。

User Namespace

User Namespace 主要是用来隔离用户和用户组的。

[centos@centos7 ~]$ unshare --user -r /bin/bash # 如果没有权限,执行echo 65535 > /proc/sys/user/max_user_namespaces,然后再次尝试创建 User Namespace。

Net Namespace

Net Namespace用来隔离网络设备、IP地址和端口等信息。

[root@centos7 centos]#  sudo unshare --net --fork /bin/bash

如何通过Cgroups机制实现资源限制

cgroups主要功能:

  • 资源限制:限制资源的使用量,设置上限。
  • 优先级控制:不同的组可以有不同的资源使用优先级。
  • 审计:计算控制组的资源使用情况。
  • 控制:控制进程的挂起或恢复。

Cgroups功能的实现依赖与三个核心概念:子系统、控制组、层级树。

  • 子系统:是一个内核的组件,一个子系统代表一类资源调度控制器。
  • 控制组:一组进程和一组带有参数的子系统的关联关系。
  • 层级树:一系列的控制组按照树状结构排列组成的。
sudo mount -t cgroup # 查看当前系统已经挂载的cgroups信息

CPU子系统

在cpu子系统下创建cgroup

[root@centos7 centos]# mkdir /sys/fs/cgroup/cpu/mydocker

创建进程,加入cgroup

[root@centos7 centos]# cd /sys/fs/cgroup/cpu/mydocker
[root@centos7 centos]# echo $$ > tasks

执行CPU耗时任务,验证cgroup是否可以限制cpu使用时间

[root@centos7 centos]# cd /sys/fs/cgroup/cpu/mydocker
[root@centos7 centos]# echo 50000 > cpu.cfs_quota_us 

memory 子系统

在memory子系统下创建cgroup

[root@centos7 centos]# mkdir /sys/fs/cgroup/memory/mydocker

创建进程,加入cgroup

[root@centos7 centos]# cd /sys/fs/cgroup/cpu/mydocker
[root@centos7 centos]# echo $$ > tasks

删除cgroups

[root@centos7 centos]# rmdir /sys/fs/cgroup/memory/mydocker

Docker组件机器原理

docker相关的组件:docker、dockerd、docker-init和docker-proxy。
containerd相关的组件:containerd、container-shim和ctr。
容器运行时相关的组件:runC。

dockerd

dockerd是Docker服务端的后台常驻进程,用来接收客户端发送的请求,执行具体的处理任务,处理完成后将结果返回给客户端。
Docker客户端与dockerd的交互方式:

  • unix套接字与服务端通信:配置格式为,unix://socket_path,默认为/var/run/docker.sock,该文件只有root或者docker用户可以访问。
  • TCP与服务端通信:可以配置为tcp://host:port
  • 文件描述符的方式与服务端通信:配置格式为: fd:// 这种格式一般用于systemd管理的系统中。

docker-init

init进程是1号进程,在docker run启动容器时可以添加--init参数,此时docker会使用docker-init作为1号进程。

docker-proxy

docker-proxy主要是用来做端口映射的。docker-proxy组件就会把容器内先赢的端口映射到主机上来,底层是依赖iptables实现的。

[root@centos7 centos]# sudo iptables -L -nv -t nat # iptables nat表的规则。

docker是官方实现的标准客户端,dockerd是Docker服务端的入口,负责接收客户端发送的指令并返回相应结果,docker-init在业务主进程没有进程回收功能,
docker-proxy组件是实现Docker网络访问的主要组件。

containerd

containerd不仅负责容器生命周期的管理,同时负责一些其他的功能:

  • 镜像的管理
  • 接收dockerd的请求,通过适当的参数调用runC启动容器;
  • 管理存储相关资源;
  • 管理网络相关资源;

containerd包含一个后台常驻进程,默认的socket路径为/run/containerd/containerd.sock,dockerd通过unix套接字向containerd发送请求,
containerd接收到请求后负责执行相关的动作并把执行结果返回给dockerd。

container-shim

container-shim的主要作用是将containerd和真正的容器进程解耦,使用container-shim作为容器进程的父进程,
从而实现containerd不影响已经启动的容器进程。

ctr

ctr是containerd-ctr,它是containerd的客户端,主要用来开发和调试。

runC

[root@centos7 centos]# cd  /home/centos
[root@centos7 centos]# mkdir runc # 创建runc运行根目录
[root@centos7 centos]# mkdir rootfs &&& docker export $(docker create busybox) | tar -C rootfs -xvf -  # 导入rootfs镜像文件
[root@centos7 centos]# runc spec 
[root@centos7 centos]# cat config.json
[root@centos7 centos]# runc run busybox  # 启动容器
[root@centos7 centos]# cd /home/centos/runc 
[root@centos7 centos]# runc list 
组件分类 组件名称 作用剖析
Docker相关组件 docker Docker客户端,负责发送Docker操作请求
Docker相关组件 dockerd Docker服务端入口,负责接收客户端请求并返回结果
Docker相关组件 docker-init 业务主进程没有回收能力时,docker-init可以作为容器的1号进程,负责管理容器的内子进程
Docker相关组件 docker-proxy 用于Docker的网络实现,通过设置iptables规则使得访问到主机的流量可以转发到容器中
containerd相关组件 containerd 负责管理容器的生命周期,接收dockerd的请求,执行启动或者销毁容器操作
containerd相关组件 containerd-shim 将containerd和真正的容器进行解解耦,使用containerd-shim作为容器进程的父进程,可以实现重启containerd不影响已经启动的容器进程
containerd相关组件 ctr containerd的客户端,可以直接向containerd发送容器操作请求,主要用来测试和开发
容器运行时组件 runc 通过Namespace、Cgroups等系统接口,实现容器的创建和销毁

Docker网络模型

libnetwork常见的网络模式 作用 业务场景
null空间模式 不提供任何容器网络 处理一些保密数据,需要一个隔离的网络环境执行一些纯计算任务
bridge桥接模式 使得容器与容器之间网络可以互通 容器需要实现网络通信或者提供网络服务
host主机模式 让容器内的程序可以使用到主机的网络 容器需要控制主机网络或者使用主机网络提供服务
container网络模式 将两个容器放到同一个网络空间中,可以直接通过localhost本地访问 两个容器之间需要通过localhost通信
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335