走进容器世界:手动构建Docker容器环境之旅

Docker/容器
30
0
0
2024-04-24
标签   Docker

近日见闻

  1. 自世界标准时间2024年4月1日00:00(中国时间2024年4月1日上午8时)起,任何 CNCF 相关认证的有效期缩短为24个月。持证人需在每24个月内重新认证一次,以确保其认证的有效性。此外,任何在 UTC 时间 2024 年 4 月 1 日 00:00 之前安排并通过考试的学员仍然将获得 36 个月的认证有效期。--CNCF
  2. XIAOJUSURVEY 源自滴滴内部问卷系统,已在集团内为各业务线提供在线调研服务,并为外部提供多种企业级调研场景解决方案。经过5年多的发展,它已发展成一个相对完整成熟的企业级平台。本次开源旨在为行业和社区提供一种轻量、安全的问卷系统解决方案,让调研更轻松。--滴滴
  3. 华中农业大学导师学术造假,对于这个事情,个人认为做科研的容不得半点虚假,1就是1,0就是0。
  4. 摘抄

代码语言:javascript

复制

你只管做自己的事,

把你的全部身心都投入到正在发生的事情中去,

而不是把你的全部身心都投入到个人的敏感多虑中去。


——迈克尔·辛格《清醒地活》

Docker:容器化技术的普及者

容器化是一种轻量级、可移植、自给自足的软件打包技术,能够让软件运行在几乎任何地方。这听起来有点像虚拟机,但容器是更轻量级的,它不需要像虚拟机那样模拟整个操作系统。在去年的文章已经普及过docker的基础概念和命令行操作,不知道还记得否,今天就来复习下:

Docker的五个基础概念:

  1. 镜像(Images):容器的基础,它包含运行应用程序所需的代码、运行库、环境变量和配置文件。
  2. 容器(Containers):镜像的运行实例,它在操作系统级别虚拟出来的隔离环境中运行。
  3. 仓库(Repositories):存放镜像的地方,比如Docker Hub。
  4. Dockerfile:一个文本文件,包含了创建Docker镜像所需的指令。
  5. Docker Compose:一种工具,允许您定义和运行多容器Docker应用程序。

容器化的优势:

  • 快速,轻量级:容器共享宿主机的系统内核,启动速度快。
  • 一致的运行环境:开发、测试、生产环境保持一致,避免了“在我机器上可正常工作”的问题。
  • 持续集成和持续部署(CI/CD):容器使得自动化测试和部署变得更加容易。

Docker vs Containerd

自从k8s-1.24版本之后,k8s采删除了内置支持Docker Engine(Docker引擎)容器运行时环境的功能,v1.24 之前的 Kubernetes 版本直接集成了 Docker Engine 的一个组件,名为 dockershim。所以现在containerd成为广泛使用的容器运行时,更为详细的操作建议直接官方查阅学习。https://github.com/containerd/containerd/blob/main/docs/getting-started.md

Docker 是一个完整的容器化平台,它不仅提供容器运行时,还包含了图形界面、命令行界面、镜像管理以及一个集成的容器编排工具(Docker Compose)。

containerd 是一个容器运行时守护进程,它专注于管理容器的生命周期,例如容器的创建、执行、暂停和销毁。它是一个更为“纯粹”的容器运行时,去除了很多Docker附加的功能,比如图形界面和编排。

最直接的区别在于:

  • Docker提供了完整的“生态系统”,对开发者更为友好,容易上手和使用。
  • containerd更为轻量级,通常用于需要较低系统开销的环境,比如生产环境中的Kubernetes,它会直接使用containerd来运行容器。

在linux系统上创建一个简单的模拟容器

创建一个简单一点的容器模拟器,我们可以按照以下几个步骤操作。会用到Linux的一些高级特性,如命名空间(namespaces)、控制组(Control Groups, cgroups)、文件系统隔离(chroot)等,以实现容器的基本功能。

但这只是一个简单示例,实际上Docker的实现要复杂得多,包括网络设置、存储卷管理、镜像分层、安全机制等等。

下面是在 CentOS 7.9 上创建一个简单容器并在其中运行进程的步骤:

步骤1: 安装必要的软件

代码语言:javascript

复制

sudo yum install -y yum-utils

步骤2: 创建容器的根文件系统

代码语言:javascript

复制

# 创建一个目录作为容器的根文件系统
sudo mkdir -p /path/to/container-rootfs

# 安装一个最小的CentOS系统
sudo yum --releasever=7 --installroot=/path/to/container-rootfs --setopt=tsflags=nodocs install -y bash coreutils yum

步骤3: 进入新的命名空间

CentOS 7 使用的是旧版本的unshare,可能不支持 --mount-proc 选项, 但你可以手动挂载 proc 系统。

代码语言:javascript

复制

# 创建新的网络、进程、IPC、挂载点命名空间
sudo unshare --fork --pid --net --ipc --uts --mount

然后,另外开一个终端,继续以下步骤:

步骤4: 挂载必要的文件系统

代码语言:javascript

复制

# 为新的PID命名空间挂载proc文件系统
sudo mount -t proc proc /path/to/container-rootfs/proc

# 为容器挂载sys文件系统(可选)
sudo mount -t sysfs sys /path/to/container-rootfs/sys

# 为容器挂载dev文件系统(可选)
sudo mount --rbind /dev /path/to/container-rootfs/dev

步骤5: 使用chroot切换到容器的根目录

代码语言:javascript

复制

# 切换到容器的根目录
sudo chroot /path/to/container-rootfs /bin/bash

步骤6: 创建cgroups并限制资源

代码语言:javascript

复制

# 创建cgroups(如果系统没有安装cgroups工具,需要先安装它)
sudo yum install -y libcgroup
sudo systemctl start cgconfig

# 创建cgroup并限制CPU和内存
sudo cgcreate -g cpu,memory:/mycontainer
echo 512 > /sys/fs/cgroup/cpu/mycontainer/cpu.shares
echo 100000000 > /sys/fs/cgroup/memory/mycontainer/memory.limit_in_bytes

步骤7: 将进程添加到cgroups

在chroot容器环境中执行以下命令:

代码语言:javascript

复制

# 获取当前shell的进程ID
echo $$

# 使用获取到的进程ID,替换下面命令中的<PID>
sudo cgclassify -g cpu,memory:/mycontainer <PID>

步骤8: 运行简单的进程

现在你可以在这个隔离环境中运行一个简单的进程,例如一个bash shell。

代码语言:javascript

复制

/bin/bash

至此,你已经在CentOS 7.9上使用Linux命名空间和控制组功能手动创建了一个简单的容器环境,并运行了一个简单的bash进程。对于学习容器原理而言,这是一个不错的开始。

问题

  1. 运行以上步骤的时候会遇到安装cgroups失败的问题,因为网络的问题。

所以简化以下流程:

更新系统

代码语言:javascript

复制

sudo yum update -y

安装必要的依赖

代码语言:javascript

复制

sudo yum install -y epel-release
sudo yum install -y libcgroup libcgroup-tools

启动 cgroup 服务

代码语言:javascript

复制

sudo systemctl start cgconfig

创建 cgroup

代码语言:javascript

复制

sudo cgcreate -g cpu,memory:/mycontainer

设置资源限制(可选,但推荐)

  • mycontainer容器设置CPU资源限制:echo 512 | sudo tee /sys/fs/cgroup/cpu/mycontainer/cpu.shares
  • mycontainer容器设置内存资源限制:echo $((100*1024*1024)) | sudo tee /sys/fs/cgroup/memory/mycontainer/memory.limit_in_bytes

创建一个隔离的文件系统

代码语言:javascript

复制

mkdir -p ~/mycontainer/{bin,lib64}

安装必要的程序和库

  • 使用ldd来检查需要哪些库,例如安装bashldd /bin/bash
  • bash和它所需要的库复制到容器的文件系统中: cp /bin/bash ~/mycontainer/bin/ cp -v $(ldd /bin/bash | grep -o '/lib.*[ ].so[^ ]*' | sort | uniq) ~/mycontainer/lib64/ 注意:这里如果复制不成功,就手动复制

代码语言:javascript

复制

    linux-vdso.so.1 =>  (0x00007fffd7f2b000)
    libtinfo.so.5 => /lib64/libtinfo.so.5 (0x00007f9b76f4e000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007f9b76d4a000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f9b7697c000)
    /lib64/ld-linux-x86-64.so.2 (0x00005620622cc000)

代码语言:javascript

复制

cp /lib64/libtinfo.so.5 ~/mycontainer/lib64/
cp /lib64/libdl.so.2 ~/mycontainer/lib64/
cp /lib64/libc.so.6 ~/mycontainer/lib64/
cp /lib64/ld-linux-x86-64.so.2 ~/mycontainer/lib64/

运行一个隔离的进程:命令解释:

  • cgexec用于在指定的cgroup中启动程序。
  • unshare用于创建隔离的命名空间。
  • -u, -m, -p 分别用于隔离UTS, 挂载点, 和进程。
  • -f表示创建新的会话。
  • chroot更改根目录到~/mycontainer
  • /bin/bash是隔离环境中运行的shell。
  • 使用cgexec命令在mycontainer cgroup中启动一个隔离的进程:sudo cgexec -g cpu,memory:/mycontainer unshare -u -m -p -f chroot ~/mycontainer /bin/bash

通过这些步骤,您就能够在CentOS 7.9上模拟出一个基本的容器环境了。

检查:

在这个新的PID命名空间中,bash进程将有一个PID为1。您可以通过运行echo $$来检查当前bash会话的PID,如果返回1,则表示您在新的PID命名空间中。

通过以上步骤,虽然不能完全模拟,但也能大致明白其中流程了,希望对各位读者有用,觉得有用的话,关注下希里安!