Docker 原理

  1. 基础技术试验
    1. 进程
      1. linux namespace
        1. Mount(mnt) namespace 可以在不影响宿主机文件系统的基础上挂载或者取消挂载文件系统了;
        2. PID(Process ID) 在一个pid namespace中,第一个进程的 pid 为1,所有在此 namespace 中的其他进程都是此进程的子进程,操作系统级别的其他进程不需要在此进程中;
        3. network(netns) namespace 会虚拟出新的内核协议栈,每个 network namespace 包括自己的网络设备、路由表等;
        4. IPC(Interprocess Communication) namespace 用来隔离处于不同 IPC namespace 的进程间通信资源,如共享内存等;
        5. UTS: UTS namespace 用于隔离 hostname 与 domainname 
        6. User
      2. linux control group(cgroup)
        1. /proc下的树形文件系统
          1. /proc/{pid}/mountinfo 里面有cgroup所在位置信息
          2. /proc/self 指向当前进程
          3. /sys/fs/cgroup 下面有系统预定义的一些cgroup
        2. memory
        3. cpu
      3. IPC linux管道
        1. 一个进程默认有3个文件描述符:stdin stdout stderr
        2. 通过ExtraFiles的形式传入管道句柄
        3. 管道结束前是阻塞进程的
    2. 文件系统
      1. aufs
        1. 旧版docker用这个,新版已淘汰
      2. overlay2
        1. 现在docker默认是这个
      3. docker支持的文件系统类型:Docker storage drivers | Docker Documentation
  2. 构造容器,基于go
    1. 创建进程
      1. 子进程覆盖初始化进程,让自己的pid为1
        1. execve(2)
        2. go下:func Exec(argv0 string, argv []string, envv []string) (err error
      2. 为进程添加namespace限制
        1. 添加Cloneflags属性
    2. 为进程添加cgroup限制
      1. 分别在/sys/fs/cgroup对应的预定义限制下,创建文件夹
      2. 写入限制属性
      3. tasks文件里添加进程pid
    3. 使用linux管道传长参数
  3. 构造镜像
    1. pivot_root
    2. 直接打包mnt的busybox
    3. 使用overlay2挂解压的镜像、临时写目录、数据卷
    4. 运行的容器打包镜像
  4. 完善容器
    1. 用detach实现后台运行,
      1. wsl下的现象是:容器内进程挂到父进程下面,父进程退出自动挂到祖父进程
    2. 其他附带探索
      1. java调用go研究 cgo+jnr 打通java(kotlin)和go
    3. 容器信息持久化
      1. 可以实现docker ps 容器查询命令
      2. stdout重定向到文件,可以执行docker log命令
    4. docker exec
      1. 使用cgo进入进程的namespace
    5. 支持多容器、多镜像
      1. 面向对象化
      2. 容器镜像分开文件夹,多对一的关系
    6. 启停、删除
      1. 清理步骤,杀进程,卸载挂载点,删文件夹
    7. 环境变量配置
      1. 容器进程环境变量 
      2. exec进程环境变量
  5. linux网络
    1. 容器网络 
    2. net namespace
      1. 下面所有概念的命名空间
    3. 虚拟网卡
      1. linux bridge网桥
      2. veth网卡对
        1. 串联两个netns
    4. iptables
      1. snat
        1. 作用是内部能访问出去
        2. 试验没成功
      2. dnat
        1. 把端口暴露出来
        2. 外部能访问进来
      3. 4表5链
      4. 是否本机ip走向不同
  6. 现有容器格局
    1. runC
      1. containerd
        1. docker
        2. cri-shim
          1. kubelet
            1. k8s

overlay2

# 参考一个overlayfs的例子
# https://www.cnblogs.com/arnoldlu/p/13055501.html
mount -t overlay -o lowerdir=lower,upperdir=upper,workdir=work overlay merge
 
# 组合命令:
# 在linux目录运行成功,直接挂载windows目录有问题,文件写入失败
mkdir image-layer4 image-layer3 image-layer2 image-layer1 container-layer work mnt
mount -t overlay -o lowerdir=./image-layer4:./image-layer3:./image-layer2:./image-layer1,upperdir=./container-layer,workdir=./work overlay ./mnt
 

linux namespace

从 container 到 pod (morven.life)

直接使用命令行创建namespace,在另外namespace运行程序

unshare --fork --pid --mount-proc bash
 
# 查看当前的namespace
root@fingard-sunht:/mnt/c/Users/ZJBR# lsns
        NS TYPE   NPROCS PID USER COMMAND
4026531835 cgroup      2   1 root bash
4026531837 user        2   1 root bash
4026531992 net         2   1 root bash
4026532233 ipc         2   1 root bash
4026532243 uts         2   1 root bash
4026532347 mnt         2   1 root bash
4026532348 pid         2   1 root bash
 
# 直接在某个cgroup下运行
cgcreate -a morven -g memory:mycgrp
echo 1000 > /sys/fs/cgroup/memory/mycgrp/memory.limit_in_bytes
cgexec -g memory:mycgrp java -version

docker可以做到容器间共享namespace

root@fingard-sunht:~/ns-share-demo# cat nginx.conf
error_log stderr;
events { worker_connections  1024; }
http {
    access_log /dev/stdout combined;
    server {
        listen 81 default_server;
        error_log stderr;
        server_name example.com www.example.com;
        location / {
            proxy_pass http://127.0.0.1:80;
        }
    }
}
 
root@fingard-sunht:~/ns-share-demo# docker run -d --name nginx -v $(pwd)/nginx.conf:/etc/nginx/nginx.conf --ipc=shareable -p 8080:81 nginx
9e2f9a2d32195fbac557371674bc967804771dc283dca630e97565394b2c3bb4
root@fingard-sunht:~/ns-share-demo# docker run -d --name nginx-backend --net=container:nginx --ipc=container:nginx --pid=container:nginx nginx
1c9af683dbf956ae600f5bd9d644e49f5237bcdf400ed7e01394c859ac0d35d9
root@fingard-sunht:~/ns-share-demo# curl http://localhost:8080/
 

使用pod的pause共享命名空间

root@fingard-sunht:~/ns-share-demo# docker run -d --name pause --ipc=shareable -p 8080:80  ran
cher/mirrored-pause:3.1
5d75e1d648c02686b02ce127b8065ae5e6337594c696774d252e1515475e80d7
root@fingard-sunht:~/ns-share-demo# docker run -d --name nginx --net=container:pause --ipc=container:pause --pid=container:pause  nginx
a4e68b82209a197f675fc30a2b1677f0ca37c8c02eaaf2bca1c27383cd72f4c5
root@fingard-sunht:~/ns-share-demo# curl http://localhost:8080/
 

cgroup

官方文档 

Control Groups version 1 — The Linux Kernel documentation

Control Group v2 — The Linux Kernel documentation

Linux的cgroup功能(二):资源限制cgroup v1和cgroup v2的详细介绍By李佶澳 (lijiaocn.com)

K8S 问题排查: cgroup 内存泄露问题 - Vermouth | 血衫非弧の一存 (kelu.org)