自下而上学习容器( 二 )


3
运行容器不一定需要镜像
不过 , 构建镜像需要容器 。
对于熟悉runc是如何启动容器的人来说 , 他们都知道镜像并非是必需的 。 要运行一个容器 , 运行时需要一个bundle , 其中包括:
一个config.json文件 , 里面包含了与容器有关的参数(例如可执行文件的路径、环境变量 , 等等);
包含可执行文件及其相关文件(如果有的话)的目录 。
通常 , bundle的目录结构与Linux发行版的文件结构类似(/var、/usr、/lib、/etc , 等等) 。 当runc启动这样的一个容器 , 运行在容器中的进程就获得了一个根文件系统 , 看起来与Linux(比如Debian、CentOS或Alpine)很像 。
但这种文件结构并非是强制性的 。 现在 , 所谓的Scratch或Distroless容器越来越流行 , 越是小巧的容器出现安全漏洞的可能性就越少 。
自下而上学习容器
文章图片
使用dive查看scratch镜像
既然运行容器不一定需要镜像 , 那我们为什么还要有容器镜像?
当每一个容器都包含根文件系统的一个数兆字节那么大的拷贝副本时 , 所需的磁盘空间就会急剧增加 。 因此 , 镜像的存在是为了有效地解决存储和发行问题 。 对这个问题感兴趣的可以阅读这篇文章 。
你有没有想过镜像是如何构建出来的?
Docker所推广的工作流程试图让你认为镜像才是主要的 , 容器次之 。 在执行dockerrun命令时 , 你需要指定一个镜像才能运行容器 。 但我们知道 , 严格来说 , 事情并没有这么简单 。 实际上 , 你需要(临时)运行容器来构建镜像!想知道为什么 , 请阅读这篇文章 。
4
单宿主机上的容器管理器
在现实世界中 , 我们发明了集装箱是为了增加一艘船可以装载的物品数量 , 类似的 , 容器是为了提高服务器的资源利用率 。
一个典型的服务器现在运行数十或数百个容器 。 因此 , 它们需要有效地共存在一台服务器上 。 单个容器运行时关注的是单个容器的生命周期 , 而容器管理器关注的是在单台主机上共存的多个容器 。
容器管理器的主要职责包括镜像的拉取、解包、配置容器间网络、存储容器日志 , 等等 。
在这方面 , 你可能认为Docker就是一个很好的例子 。 但我发现containerd是一个更具代表性的例子 。 与runc一样 , containerd在一开始只是Docker的一个组件 , 后来被提取到一个独立的项目中 。 containerd可以使用runc或任何实现了containerd-shim接口的运行时 。 最酷的是 , 你可以像使用Docker一样使用containerd来轻松地运行容器 。
自下而上学习容器
文章图片
containerd与docker
现在 , 我们准备好要了解Docker了!如果我们忽略(现在已弃用)Swarm , 那么Docker包含如下这些:
dockerd——位于containerd守护进程前面的一个高级守护进程;
docker——一个命令行客户端 , 用于与dockerd交互 。
自下而上学习容器
文章图片
Docker的分层架构
在我看来 , Docker目前的主要任务是让容器工作流变得更友好 。 为了简化开发人员的工作 , Docker将所有主要容器用例整合到一个工具中:
构建/拉取/推送/扫描图像;
启动/暂停/检查/杀死容器;
创建网络/重定向端口;
挂载/卸载/删除卷;
其他 。
但是到了2021年 , 几乎每个用例都被写成了一个定制的软件(如podman、buildah、skopeo、kaniko , 等等) , 以便提供更好的替代解决方案 。
5