k8s(一)走进docker
- April 24, 2022
Build,Ship and Run Any App,Anywhere
作者:lomtom
个人网站:lomtom.cn 🔗
个人公众号:博思奥园 🔗
你的支持就是我最大的动力。
k8s系列:
在软件开发中,环境配置往往是最繁琐的任务之一。以安装 Python 应用为例,除了获取应用的二进制文件,系统还需安装 Python 解析器、满足应用的外部依赖,并配置环境变量等。考虑到不同操作系统的因素,比如跨平台兼容性,这些步骤使得配置运行时环境(runtime)变得复杂。
由于环境配置的复杂性,一个可行的解决方案是实现软件与其运行时环境一起安装。虚拟机的快照(snapshot)提供了这样一种解决方案,允许用户还原软件的原始环境。然而,虚拟机的缺点在于每个虚拟机都包含完整的操作系统,导致资源占用较大、启动较慢等问题。
为了克服虚拟机的这些不足,Linux 发展出了另一种虚拟化技术:容器。容器技术将应用程序及其所有运行时依赖封装在一个独立的容器中,实现了轻量级、快速启动、资源占用少的优势。
容器
容器是一个标准化的单元,是一个轻量级、可移植的软件打包技术。它将软件代码及其相关依赖打包,使应用程序可以在任何计算介质中运行。简单来讲,容器就像一个标准化的盒子,能够装很多不同类型的东西,并且装完后能够塞进很多不同类型的柜子里。
- 容器的特性:
- 封装性:将软件打包成标准化单元以进行开发、迁移和部署
- 隔离性:计算、存储、网络等资源彼此隔离(运行环境的隔离)
- 高效性:轻量、快速启停、快速部署与迁移
- 职责分工明确:开发专心写代码,运维专注基础环境配置
- 镜像增量分发
- 容器使用难题
- 统一平台:在k8s之前,并没有一个统一的平台去操作容器,基本上所有操作都由开发人员/运维人员完成
- 易用性
虚拟机 VS 容器
- 容器没有虚拟化层,所以容器通常称为轻量级虚拟化技术,然而他真正的实现是通过namespace来进行进程隔离,进入到容器内该Namespace下挂载的目录、文件或者网络。究其根源,容器只是宿主机器上的一个特殊的进程。
- 虚拟化虚拟的是硬件,容器虚拟的是操作系统,所以更高效
docker使用
- 通常在项目工程里使用Makefile文件,指定一系列的操作步骤;
- 通过
docker build
构建成一个可运行镜像; - 使用
docker push
推送到指定的镜像仓库; - 在任何可以连接镜像仓库的地方,拉取镜像并且运行:
docker pull
与docker run
docker原理
docker的运行时是runc,runc采用的技术:基于Linux内核的Cgroup(资源限制),Namespace(隔离),以及UnionFS(文件系统)等技术,对进程进行封装隔离
-
Namespace:对进程进行隔离,解决了进程之间的可见性问题。
- 系统可以为进程分配不同的 Namespace,使进程能够拥有独立的运行环境,并且能够拥有网络与宿主机器进行通信。而container本质上是宿主机器上的一个进程;
- 一个进程的Namespace信息以一个文件的形式存在于宿主机器上。
-
Cgroup:对进程组资源进行限制及监控。(全称为control Group)
-
保证不同的进程组资源独立分配、进程组彼此隔离,即不同的进程组下的进程互不干扰 。这些资源包括:主机名、用户权限、文件系统、网络、进程号、进程间通信。
-
对进程的资源控制通常存在于
/sys/fs/cgroup
,每一个进程都会在这里存在一个单独的文件夹,文件夹下会存在各种资源的使用限制,可以修改里面的值进行相应的资源控制。[root@master cpu]# cd /sys/fs/cgroup # 创建文件夹 [root@master cpu]# mkdir lomtom # 进入文件夹 [root@master cpu]# cd lomtom [root@master lomtom]# ls cgroup.clone_children cgroup.procs cpuacct.usage cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release cgroup.event_control cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat tasks ... # 另起一个窗口,运行一个消耗cpu的程序(用写几个for循环即可) [root@master root]# cat > main.go << EOF package main func main() { go func() { for { } }() go func() { for { } }() for { } } EOF [root@master root]# go build ./main.go [root@master root]#./main # 查询该进程的进程号 [root@master lomtom]# ps -ef|grep main|grep -v grep|awk '{print $2}' # 将进程号写入该文件夹下的cgroup.procs [root@master lomtom]# echo 26630 > cgroup.procs [root@master lomtom]# cat cgroup.procs 26630 # 限制cpu使用率上限(10%) [root@master lomtom]# echo 10000 > cpu.cfs_quota_us [root@master lomtom]# cat cpu.cfs_quota_us 10000
限制前cpu占有率
限制后cpu占有率
-
-
除此之外,还会对进程(也就是容器)切换根目录。并且docker镜像是以一层一层的结构构成,rootfs在原有的基础镜像(例如centos)增加自己所要执行的操作即在可读写层添加相应的层。
并且,下一步操作如果与之前的操作冲突,将会覆盖之前的。例如我先创建一个文件config,然后我又重新写入config文件,那么最终保存的就是最后一次执行的config文件。
echo 你好 > config echo 你快乐吗 > config
最终在config中只有“你快乐吗”,这在日常的使用中也是这样的,所以这很好理解。
对于docker镜像来说,Dockerfile的每一步操作都会生成一层可读写层,并且后来的操作也就是上层会覆盖下层,可以通过
docker history [images ID]
,来查看镜像的层级结构。[root@master ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE rancher/mirrored-flannelcni-flannel v0.17.0 9247abf08677 7 weeks ago 59.8MB [root@master ~]# docker history 9247abf08677 IMAGE CREATED CREATED BY SIZE COMMENT 9247abf08677 7 weeks ago ENTRYPOINT ["/opt/bin/flanneld"] 0B buildkit.dockerfile.v0 <missing> 7 weeks ago RUN /bin/sh -c /iptables-wrapper-installer.s… 1.93kB buildkit.dockerfile.v0 <missing> 7 weeks ago COPY dist/iptables-wrapper-installer.sh / # … 7.66kB buildkit.dockerfile.v0 <missing> 7 weeks ago COPY dist/mk-docker-opts.sh /opt/bin/ # buil… 2.14kB buildkit.dockerfile.v0 <missing> 7 weeks ago COPY dist/flanneld-amd64 /opt/bin/flanneld #… 39.8MB buildkit.dockerfile.v0 <missing> 7 weeks ago RUN /bin/sh -c apk add wireguard-tools --no-… 2.63MB buildkit.dockerfile.v0 <missing> 7 weeks ago RUN /bin/sh -c apk add --no-cache iproute2 n… 11.8MB buildkit.dockerfile.v0 <missing> 7 weeks ago ENV FLANNEL_ARCH=amd64 0B buildkit.dockerfile.v0 <missing> 5 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 5 months ago /bin/sh -c #(nop) ADD file:5a707b9d6cb5fff53… 5.62MB
这样的一个好处是,当我在进行容器的分发时,发现底层层级没有发生改变时,那么每次发布只会发布改变的部分。
容器与k8s
CRI接口是kubelet调用容器运行时的grpc接口,也就是k8s定义的容器运行时接口。
而dockershim、cri-containerd都是CRI接口的实现。
而以下都是当下比较流行的运行时的实现。
- runc
容器公用主机资源,采用Ns,Cgroup等技术,本质上是一个进程,更轻量。
- kata
容器有独立的内核,裁剪了一些不必要的设备、总线、驱动、文件系统等,本质上是一个轻量化的虚拟机,更安全。