Docker——Docker in Docker原理与实战

(3) 2024-04-18 08:23

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Docker——Docker in Docker原理与实战,希望能够帮助你!!!。

摘要

本博文介绍Docker in Docker问题背景与构建方式。

一、Docker in Docker原理

Docker-in-Docker的方式主要是为了帮助核心团队更快地进行 Docker 开发。在 Docker-in-Docker 之前,典型的开发周期是:

  • 黑客攻击
  • 建造
  • 停止当前运行的 Docker 守护进程
  • 运行新的 Docker 守护进程
  • 测试
  • 重复

如果你想要一个漂亮的、可重复的构建(即在容器中),那就有点复杂了:

  • 黑客攻击
  • 确保 Docker 的可用版本正在运行
  • 用旧的 Docker 构建新的 Docker
  • 停止 Docker 守护进程
  • 运行新的 Docker 守护进程
  • 测试
  • 停止新的 Docker 守护进程
  • 重复

1.1 Docker in Docker的缺点

Docker-in-Docker 并非 100% 有几个问题需要注意。

  • 关于 LSM(Linux 安全模块),AppArmor 和 SELinux:当启动容器时,“内部 Docker”可能会尝试应用会与“外部 Docker”冲突或混淆的安全配置文件。这实际上是尝试合并 -privileged 标志的原始实现时最难解决的问题。在Debian 机器和 Ubuntu 测试 VM 上有效(并且所有测试都通过了),但它会在 Michael Crosby 的机器(如果我没记错的话是 Fedora)上崩溃。可能是可以使用配置 SELINUX=enforce运行,而我的更改没有考虑 SELinux 配置文件。
  • 存储驱动程序有关。当你在 Docker 中运行 Docker 时,外部 Docker 运行在普通文件系统(EXT4、BTRFS、你有什么)之上,但内部 Docker 运行在写时复制系统(AUFS、BTRFS、设备映射器等)之上.,取决于外部 Docker 设置使用的内容)。有很多组合是行不通的。例如,您不能在 AUFS 之上运行 AUFS。如果你在 BTRFS 之上运行 BTRFS,它首先应该可以工作,但是一旦你有嵌套的子卷,删除父子卷就会失败。 Device Mapper 没有命名空间,因此如果 Docker 的多个实例在同一台机器上使用它,它们都将能够看到并影响彼此的镜像和容器支持设备。许多这些问题都有解决方法;例如,如果您想在内部 Docker 中使用 AUFS,只需将 /var/lib/docker 提升为卷即可。 Docker 为 Device Mapper 目标名称添加了一些基本的命名空间,这样如果 Docker 的多个调用在同一台机器上运行,它们就不会相互影响。
  • 对于构建缓存也存在很大问题。如果运行 Docker-in-Docker;如何使用位于我的主机上的images,而不是在内部 Docker 中再次拉取所有内容?”,或许你尝试将 /var/lib/docker 从主机绑定挂载到 Docker-in-Docker 容器中。有时他们与多个容器共享 /var/lib/docker。但是对于Docker 守护进程被明确设计为具有对 /var/lib/docker 的独占访问权限。任何其他东西都不应触及、戳或挠隐藏在那里的任何 Docker 文件。这是为什么?这是从 dotCloud 时代吸取的教训之一。 dotCloud 容器引擎通过让多个进程同时访问 /var/lib/dotcloud 来工作。例如像原子文件替换(而不是就地编辑),或者是代码中添加咨询和强制锁定,以及对 SQLite 和 BDB 等安全系统的试验;当我们重构我们的容器引擎(最终成为 Docker)时,一个重大的设计决策就是将所有容器操作集中在一个守护进程下,并完成所有并发访问的请求。如果您在多个 Docker 实例之间共享 /var/lib/docker 目录,获取它可能会起作用,尤其是在早期测试期间。但是尝试做一些更复杂的事情(从两个不同的实例中提取相同的镜像.....)。如果您的 CI 系统进行构建和重建,每次您将重新启动 Docker-in-Docker 容器时,您都可能会破坏它的缓存.

1.2 Docker in Docker 方法

Docker run Docker的方式有三种

1.2.1 使用Sysbox运行时的Docker中的Docker

Docker——Docker in Docker原理与实战_https://bianchenghao6.com/blog__第1张

因为在特权模式下运行容器。Nestybox尝试通过使用sysbox Docker运行时来解决该问题。如果使用Nestybox sysbox运行时创建容器,则它可以在能够运行systemd,docker,kubernetes的容器内创建虚拟环境,而无需特权访问基础主机系统。解释sysbox需要足够的理解力,因此我不在本文的讨论范围之内。请参考此页面以全面了解sysbox。

 Sysbox 是一个 OCI 运行时,可以用来代替 runc,或者作为 runc 的补充。它可以运行通常需要特权标志的“系统容器”,而无需特权标志;并在这些容器之间以及这些容器与其宿主之间提供足够的隔离。Sysbox 还提供了运行容器中容器的优化。具体来说,当并排运行多个 Docker 实例时,可以使用一组共享的镜像“播种”它们。这节省了大量磁盘空间和大量时间,我认为这在运行时会产生巨大的差异,例如容器中的 Kubernetes 节点。(在容器中运行 Kubernetes 节点对于 CI/CD 尤其有用,当您想要部署 Kubernetes 暂存应用程序或在其自己的集群中运行测试时,无需在专用机器上部署完整集群的基础架构成本和时间开销。)

步骤1:安装sysbox运行时环境。请参阅此页面以获取有关安装sysbox运行时的最新官方说明。

sysbox:https://github.com/nestybox/sysbox
​
第2步:一旦拥有sysbox运行时可用,您要做的就是使用sysbox运行时标志启动docker容器,如下所示。在这里,我们使用的是官方docker dind映像。

docker run --runtime=sysbox-runc --name sysbox-dind -d docker:dind

步骤3:现在将exec会话带到sysbox-dind容器。

docker exec -it sysbox-dind /bin/sh

1.2.2 Docker out of Docker

Docker——Docker in Docker原理与实战_https://bianchenghao6.com/blog__第2张

如果您只是希望能够从 CI 系统运行 Docker(特别是:构建、运行,有时推送容器和映像),而该 CI 系统本身位于容器中?您想要的只是一个解决方案,以便您的 CI 系统(如 Jenkins)可以启动容器。最简单的方法是将 Docker 套接字公开给 CI 容器,方法是使用 -v 标志绑定挂载它。简而言之,当您启动 CI 容器(Jenkins 或其他)时,不要使用 Docker-in-Docker 来激活成功教程某些东西,而是从以下内容开始:现在这个容器将可以访问 Docker 套接字,因此能够启动容器。除了启动“子”容器之外,它将启动“兄弟”容器。你可以是使用 docker 官方镜像(包含 Docker 二进制文件):

#启动的容器

docker run -it -d -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker docker(镜像ID)

#进入容器并使用docker 

sudo docker exec -it 容器(ID) /bin/bash

#使用的docker的相关指令docker pull

docker pull busybox  

这看起来像 Docker-in-Docker,感觉像 Docker-in-Docker,但它不是 Docker-in-Docker:当这个容器将创建更多容器时,这些容器将在顶级 Docker 中创建。您不会遇到嵌套副作用,并且构建缓存将在多个调用之间共享。

Dockerfile内容以从容器内部测试映像构建。

​# 基础镜像

FROM ubuntu:18.04

# 安装依赖

RUN apt-get update && apt-get install -y  apt-transport-https   ca-certificates   curl  gnupg-agent  software-properties-common

# 安装秘钥

RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -

# 添加仓库

RUN add-apt-repository   "deb [arch=amd64] https://download.docker.com/linux/ubuntu  $(lsb_release -cs) stable"

# 安装docker 

RUN apt-get install docker-ce docker-ce-cli containerd.io -y
​
编译Dockerfile

docker build -t images_name .

​Docker 的启动命令

docker container run -it -v /var/run/docker.sock:/var/run/docker.sock testabc:1.0 /bin/bash
​

Docker——Docker in Docker原理与实战_https://bianchenghao6.com/blog__第3张

1.2.3 Docker In Docker(不推荐使用)

Docker——Docker in Docker原理与实战_https://bianchenghao6.com/blog__第4张

此方法实际上在容器内部创建一个子容器。仅当您确实要在容器中包含容器和镜像时才使用此方法。否则,我建议您使用第一种方法。为此,您只需要使用带有dind标签的官方docker镜像即可。该dind映像使用Docker所需的实用程序进行制作以在Docker容器中运行。

注意:这要求您的容器以特权模式运行。

步骤1:建立dind-test以docker:dind镜像命名的容器

docker run --privileged -d --name dind-test docker:dind

步骤2:使用exec登录到容器。

docker exec -it dind-test /bin/sh

现在,您可以尝试使用Dockerfile构建映像,如先前方法所示。关键注意事项

  • 仅在必要时在Docker中使用Docker。在将任何工作流程迁移到Docker-in-Docker方法之前,请进行足够的测试。
  • 如果您打算使用Nestybox(Sysbox),请确保已通过企业架构师/安全团队的测试和批准。
  • 在特权模式下使用容器时,请确保您已获得企业安全团队有关计划执行的必要批准。
  • 在带有kubernetes容器的Docker中使用Docker时,存在一些挑战。请参阅此博客以了解更多信息。

二、Docker in Docker实战

2.1 DockerFile

# 基础镜像
FROM silverlogic/python3.6

# 描述
MAINTAINER python3.6+mysql5.6+redis

# 添加必要秘钥,以便添加apt库 archive.ubuntu.com

RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5

# 换中国源,其中archive.ubuntu.com用于安装mysql

RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak \
&& echo "deb http://mirrors.163.com/debian/ stretch main non-free contrib" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.163.com/debian/ stretch-updates main non-free contrib" >>/etc/apt/sources.list \
&& echo "deb-src http://mirrors.163.com/debian/ stretch main non-free contrib" >>/etc/apt/sources.list \
&& echo "deb-src http://mirrors.163.com/debian/ stretch-updates main non-free contrib" >>/etc/apt/sources.list \
&& echo "deb http://archive.ubuntu.com/ubuntu trusty universe" >>/etc/apt/sources.list \
&& apt-get update

# 安装mysql5.6
RUN { \
echo mysql-server mysql-server/root_password password '123456'; \
echo mysql-server mysql-server/root_password_again password '123456'; \
} | debconf-set-selections \
&& apt-get install -y mysql-server-5.6 mysql-client-5.6

# 装redis
RUN apt install -y redis-server

# 启动命令
ENTRYPOINT service mysql start && redis-server # 让进程一直跑, 否则容器会exit

2.2 Docker build

sudo docker build -t python3.6-mysql-redis .
# 创建+启动容器, 如果启动不了, 需要调试并修改dockerfile或启动脚本.需要外部连接的就用-p 暴露端口

sudo docker run -itd -p 3308:3306 --name py_mysql_redis py_mysql_redis

# 进入bash
	
sudo docker exec -it python3.6-mysql-redis bash

# 在容器里测试是否成功:

mysql -uroot -p123456
redis-cli
python

# 数据库端口暴露之后,在容器内部可以访问,但是外部还是无法连接

进到 docker 容器里面修改一下mysql账号允许从远程登录就行,

#by 后面是密码, 可自行修改。也是远程登录的密码 两条命令如下:

grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option;

#刷新配置

flush privileges;

博文参考

 是否能在docker中装ubuntu,再嵌套部署启动一个docker? - 知乎
Using Docker-in-Docker for your CI or testing environment? Think twice.

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

上一篇

已是最后文章

下一篇

已是最新文章

发表回复