因我现在的日常工作常会涉及到docker技术,所以希望通过看书和学习总结出一套入门docker的指南,以备之后自己查看和供他人学习之用。本文主要参考《Docker — 从入门到实践》一书进行总结而来。
1. 虚拟技术的分类
主要对比的是 Docker 和传统虚拟方式的区别和优劣!
Docker 使用 Google 公司推出的 Go 语言进行开发实现,基于 Linux 内核的 cgroup 和 namespace 技术,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离, 属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初是基于 LXC 实现的,从 0.7 版本开始转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。
- 传统虚拟技术
- 现代虚拟技术
- 技术对比总结
2. 容器技术的优势
我们使用容器化技术,能给我们带来哪些好处呢?
Docker 是个跨时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的运行效率,降低了云计算资源供应的成本。使用 Docker 可以让应用的部署、测试和分发都变得前所未有的高效和轻松。
- 更高效的利用系统资源
- 无论是应用执行速度、 内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比于传统的虚拟机技术,一个相同配置的主机往往可以运行更多数量的应用。
- 绝对一致的运行环境
- 一致的环境保证了一次创建或配置,可以在任意地方正常的运行。从而,保证了持续交付和部署变得简单了。不仅仅开发团队可以方便的使用容器,给客户部署、升级也变得简单了。
- 更轻松的迁移服务和应用
- 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。同时,使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。
3. 理解核心概念点
开始学习 Docker 技术之前,我们必须先要了解关于容器技术的几个核心知识点,比如镜像(Image)、容器(Container)、仓库(Repository)。如果能做到融会贯通的话,那么你就基本的理解了 Docker 的整个生命周期。
- 镜像
- Docker 的镜像就相当于是一个 root 文件系统,且不包含任何动态数据,其内容在构建之后不会被改变。镜像只是一个虚拟的概念,是由多层文件系统分层存储,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层,因此,在构建镜像的时候,需要额外小心。
- 容器
- 镜像是静态的定义,容器是镜像运行时的实体,类似于类和实例的关系。容器的实质是进程,而这个进程运行于属于自己的独立的命名空间的隔离环境里。按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。 所有的文件写入操作, 都应该使用数据卷(Volume)或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主或网络存储发生读写,其性能和稳定性更高。
- 仓库
- Registry 就是一个集中的存储、分发镜像的服务,可包含多个仓库。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本,默认为 lastest 标签。
4. 容器的快速安装
Docker 划分为社区版(CE)和企业版(EE),前者为免费使用,维护周期为三个月,而后者需要付费才能够使用,主要强调的是安全性与维护周期的时长。
- [1] Ubuntu 的安装
# 卸载旧版本 $ sudo apt-get remove docker docker-engine docker.io # 升级apt软件 $ sudo apt-get update
# 使用APT安装 # APT使用HTTPS进行传输的,所以需要安装软件包以及CA证书 $ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common # 添加软件源的GPG密钥,是为了确认所下载软件包的合法性 $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo apt-key fingerprint 0EBFCD88 # 向source.list中添加Docker软件源 $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# 安装Docker-CE服务 $ sudo apt-get update $ sudo apt-get install docker-ce # 启动Docker-CE服务 $ sudo systemctl enable docker $ sudo systemctl start docker # 建立docker用户组 $ sudo groupadd docker $ sudo usermod -aG docker $USER # 测试安装是否成功 $ docker run hello-world
# 卸载docker服务 $ sudo apt-get purge docker-ce # 删除docker软件目录 $ sudo rm -rf /var/lib/docker
- [2] CentOS
# 卸载旧版本 $ sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine
# 升级yum软件 $ yum update # 安装依赖包 $ sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加yum软件源 $ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 安装Docker-CE服务 $ sudo yum makecache fast $ sudo yum install docker-ce # 启动Docker-CE服务 $ sudo systemctl enable docker $ sudo systemctl start docker # 建立docker用户组 $ sudo groupadd docker $ sudo usermod -aG docker $USER # 测试安装是否成功 $ docker run hello-world
# 添加内核参数屏蔽警告信息 $ sudo tee -a /etc/sysctl.conf <<-EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF # 重新加载sysctl.conf配置 $ sudo sysctl -p
# 卸载docker服务 $ sudo apt-get purge docker-ce # 删除docker软件目录 $ sudo rm -rf /var/lib/docker
- [3] 使用脚本自动安装
# 官方在测试或开发环境中为了简化安装提供了安装脚本 $ curl -fsSL https://get.docker.com -o get-docker.sh $ sudo sh get-docker.sh # 建议使用阿里云镜像(国内的话快很多) $ curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
- [4] 配置镜像加速器
注册阿里云账号后,即可在阿里云控制台看到类似如下的页面。
5. 核心的要点分析
这里主要参考 卡瓦邦噶 的《Docker(容器)的原理》博客而来!
Docker 里面并没有什么黑魔法,只不过是将一些 Linux 已经有的功能集合在一起,并提供了一个简单的 UI 来创建 “容器”,并没有发明什么新的技术。
- [1] OCI 容器标准
- image spec => 容器如何打包
- 本质上一个 image 就是一个 tar 包
- layers 就是组成 rootfs 的一些文件
- runtime spec => 容器如何运行
- 创建一个 container 然后在 container 中运行进程
- 容器里面就可以不依赖系统的任何依赖,自己即可独立运行
- image spec => 容器如何打包
# 容器的镜像层 $ tar xvf nginx_image.tar -C nginx ...... x fa03658ad40153748b0abbe573db2aaf943049a0749d192a4cfa56f107a80270/ x fa03658ad40153748b0abbe573db2aaf943049a0749d192a4cfa56f107a80270/VERSION x fa03658ad40153748b0abbe573db2aaf943049a0749d192a4cfa56f107a80270/json x fa03658ad40153748b0abbe573db2aaf943049a0749d192a4cfa56f107a80270/layer.tar x manifest.json x repositories
# 容器的实现和生态 - Docker oci/runc -> containerd -> moby -> docker # 容器的实现和生态 - Kubernetes oci/runc -> cri-o -> kubernetes oci/runc -> containerd -> cri-containerd -> kubernetes
- [2] 进程之间的隔离
- 在容器里面,一个进程只能看到同一个容器下面的其他进程(pid/mount)
- Namespaces 可以控制进程在 container 中可以看到什么(隔离)
# 手动运行runc来创建我们自己的容器(用busybox演示) $ mkdir /mycontainer && cd /mycontainer && mkdir rootfs $ docker export $(docker create busybox) | tar -C rootfs -xvf - # 创建一个默认的config.json作为配置文件 $ cd /mycontainer $ runc run mycontainerid # 查看namespace命名空间(两个终端|一个运行一个观察) 92518 90576 runc run xyxy 92524 92521 runc init 92528 92527 sh # 查看宿主机这个进程的pidns是什么 $ ps -p 92528 -o pid,pidns PID PIDNS 92528 4026534092 # 查看对应的ns里面到底有什么 $ ls -l /proc/92528/ns total 0 lrwxrwxrwx 1 root root 0 Apr 4 23:41 [[cgroup]] -> [[cgroup]]:[4026531835] lrwxrwxrwx 1 root root 0 Apr 4 23:28 ipc -> ipc:[4026534091] lrwxrwxrwx 1 root root 0 Apr 4 23:28 mnt -> mnt:[4026534089] lrwxrwxrwx 1 root root 0 Apr 4 23:27 net -> net:[4026534094] lrwxrwxrwx 1 root root 0 Apr 4 23:28 pid -> pid:[4026534092] # 更为详细的信息 $ cinf -namespace 4026534092
- [3] cgroups 资源分组
- cgroups 可以控制进程可以使用的资源(资源)
- 当一个新的 container 创建的时候,容器会为每种资源创建一个 cgroup 来限制容器可以使用的资源
- 默认情况下的容器是不限制资源的,比如说内存,需要限制的话,可以将限制写入到这个文件里面
# 将系统上的cgroup保存到一个文档当中 $ lscgroup | tee cgroup.b # 启动一个容器并观察cgroup的变化 $ lscgroup | tee cgroup.a $ diff cgroup.b cgroup.a 4a5 > net_cls,net_prio:/xyxy 12a14 > pids:/user.slice/user-0.slice/session-c9.scope/xyxy 121a124 > cpuset:/xyxy 129a133 > blkio:/user.slice/user-0.slice/session-c9.scope/xyxy 242a247 > cpu,cpuacct:/user.slice/user-0.slice/session-c9.scope/xyxy 352a358
- [4] Linux Capabilities
- 可以在用户有 root 权限的同时,限制 root 使用某些权限
- 这就是我们在使用容器的时候常见的 CAP_SYS_ADMIN 权限限制了
# 准备好一个带有Libcap的容器 $ docker run -it alpine sh -c 'apk add -U libcap; capsh --print'; # 将这个docker容器导出到runc的rootfs里面 $ docker export 5aad51652320 | tar -C rootfs -xvf - # 生成一个spec来 $ mkdir test_cap && mv rootfs/ test_cap/ && cd test_cap/ $ runc spec $ ls config.json rootfs # 进入容器发现,即使是root也无法修改hostname $ runc run mycap $ sudo id uid=0(root) gid=0(root) $ sudo hostname xintao.local hostname: sethostname: Operation not permitted
"capabilities": { "bounding": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_SYS_ADMIN", "CAP_NET_BIND_SERVICE" ], "effective": [ "CAP_AUDIT_WRITE", "CAP_SYS_ADMIN", "CAP_KILL", "CAP_NET_BIND_SERVICE" ], "inheritable": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ], "permitted": [ "CAP_AUDIT_WRITE", "CAP_SYS_ADMIN", "CAP_KILL", "CAP_NET_BIND_SERVICE" ], "ambient": [ "CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE" ] },
- [5] 文件系统的隔离(mount namespace)
- 在容器中只能看到容器里面的文件,而不能看到 host 上面的文件
- 如果 mount namespace 下面有任何进程修改了 mount table 其他的进程也会受到影响
- chroot 并没有改变任何 mount table,它只是让进程的 / 看起来就是一个指定的目录
# 启动一个容器可以看到有新的mount的ns $ sudo cinf | grep mnt 4026531840 mnt 102 0,1,103,104,112,1000 /sbin/initx 4026532185 mnt 1 0 sh $ sduo cinf -namespace 4026532185 PID PPID NAME CMD NTHREADS CGROUPS STATE 14013 14003 sh sh 1 12:blkio:/user.slice/yoyo # 在host进程上查看mount的info信息 cat /proc/14013/mounts | sort | uniq cgroup /sys/fs/cgroup/blkio cgroup ro,nosuid,nodev 0 0 cgroup /sys/fs/cgroup/cpu,cpuacct cgroup ro,nosuid,nodev 0 0 cgroup /sys/fs/cgroup/cpuset cgroup ro,nosuid,nodev 0 0
- [6] User and root
- 不推荐使用 root 来跑容器
- config.json 文件中的 User 字段可以指定容器的进程以什么 uid 来运行,默认是 0,即 root
# 字段不是必须的,如果删去,依然是以uisd=0运行 $ id uid=0(root) gid=0(root)
- [7] 网络
- 在网络方面,OCI Runtime Spec 只做了创建和假如 network ns, 其他的工作需要通过 hooks 完成
- 使用默认的 config.json,就只有一个 loop device,没有 eth0 ,所以也就不能连接到容器外面的网络
# Export the sha256sum for verification. $ export NETNS_SHA256="8a3a48183ed5182a0619b18f05ef42ba5c4c3e3e499a2e2cb33787bd7fbdaa5c" # Download and check the sha256sum. $ curl -fSL "https://github.com/genuinetools/netns/releases/download/v0.5.3/netns-linux-amd64" -o "/usr/local/bin/netns" && echo "${NETNS_SHA256} /usr/local/bin/netns" | sha256sum -c - && chmod a+x "/usr/local/bin/netns" $ echo "netns installed!" # Run it! $ netns -h
6. 参考的链接地址
送人玫瑰,手有余香!