您可以在Docker容器中运行ChefDK和kitchen-docker吗?

本文介绍如何在Docker容器中安装ChefDK并隔离其依赖项,同时利用Kitchen-Docker进行测试。作者详细记录了解决IPv6问题、SSH连接失败等挑战的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我想将ChefDK安装在Docker容器中,以便将其所有依赖项与主机上的所有Ruby安装隔离开来,并允许我轻松地并行运行不同版本以进行测试升级。

所以我从一个基本的Dockerfile开始来安装ChefDK…

from ubuntu:16.04
RUN apt-get update
RUN apt-get install -y curl
RUN curl -O https://packages.chef.io/files/stable/chefdk/2.4.17/ubuntu/16.04/chefdk_2.4.17-1_amd64.deb
RUN dpkg -i chefdk_2.4.17-1_amd64.deb

如果我们构建它,然后在其中放入bash外壳……

docker build -t chefdk .
docker run -it chefdk bash

我们已经安装了厨师和刀!

root@6bb552c50752:/# which chef
/usr/bin/chef
root@6bb552c50752:/# which knife
/usr/bin/knife

太好了,现在我需要将食谱安装在容器中,以便可以运行测试厨房,将它们安装在/cookbooks

docker run -it -v /Users/aaronkalair/cookbooks:/cookbooks chefdk

那行得通,但是因为我们没有在Dockerfile中指定用户,所以我们是root用户,因此每个新文件也由容器内部的root用户拥有。

root@d374bab13add:/cookbooks/teabot# touch test.txt
root@d374bab13add:/cookbooks/teabot# ls -lah test.txt
-rw-r--r-- 1 root root 0 Dec 23 15:28 test.txt

为了避免出现任何问题,让我们在容器内创建一个与主机上的用户具有相同UID的用户...

在主机上,我的名称是UID 501,这在我对其进行测试的3台计算机上都是一致的,因此Mac似乎将501分配给了创建的第一个用户帐户。

Aarons-iMac:chefdk aaronkalair$ id
uid=501(aaronkalair) ...

因此,我们可以通过将其添加到Dockerfile的末尾来使用uid 501在容器内创建一个名为aaronkalair的用户

RUN useradd -u 501 aaronkalair
RUN mkdir -p /home/aaronkalair
RUN chown aaronkalair:aaronkalair /home/aaronkalair
USER aaronkalair

现在让我们测试一下如何为我的Teabot食谱创建一个测试厨房。 首先,我们需要安装gem

root@6c602759d146:/cookbooks/teabot# bundle install
bash: bundle: command not found

嗯,我们没有安装Ruby或其任何关联的工具,但是我们不需要这样做,因为Chef附带了它们的嵌入式版本。

root@6c602759d146:/cookbooks/teabot# ls /opt/chefdk/embedded/bin/
... <124 packages including, bundle, gem, ruby etc... >

因此,通过将其添加到我们的Dockerfile中,将其放到我们的路径中

ENV PATH="/opt/chefdk/embedded/bin:${PATH}"

现在让我们再次尝试bundle install

当我在不同的食谱中使用不同版本的Gems时,只需将它们安装到此食谱中的文件夹中,而不必根据我上次使用的食谱在全球范围内安装不同版本的杂烩

bundle install --path vendor --binstubs

不幸的是,这激起了关于extconf失败的错误

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
current directory: /cookbooks/teabot/vendor/ruby/2.4.0/gems/libyajl2-1.2.0/ext/libyajl2
/opt/chefdk/embedded/bin/ruby -r ./siteconf20171223-8-1u0q211.rb extconf.rb
creating Makefile
/cookbooks/teabot/vendor/ruby/2.4.0/gems/libyajl2-1.2.0/ext/libyajl2
extconf.rb:104:in `makemakefiles': unhandled exception
from extconf.rb:138:in `<main>'
extconf failed, exit code 1

原生扩展,Makefiles和extconf听起来像我们缺少一些依赖来安装C依赖关系,让我们抓住了build-essentials ,看看会发生什么,并且在我们编辑Dockerfile时,让bundle命令成为别名。

RUN apt-get install -y curl build-essential 
 RUN echo "alias bundle-install='/opt/chefdk/embedded/bin/bundle install --path vendor --binstubs'" >> /home/aaronkalair/.bashrc 

我们去那工作了

aaronkalair@3ac49ab61513:/cookbooks/teabot$ bundle-install
....
Bundle complete! 3 Gemfile dependencies, 104 gems now installed.
Bundled gems are installed into ./vendor.

现在我们需要容器中可用的Docker,以便Kitchen-docker可以制造容器。 最简单的方法似乎是将套接字从主机安装到容器中。

因此,我们将运行命令更改为:

docker run -it -v /Users/aaronkalair/cookbooks:/cookbooks -v /var/run/docker.sock:/var/run/docker.sock chefdk

并将Docker CLI安装在容器中

RUN apt-get install -y curl build-essential docker.io 

这使我们可以访问容器内的Docker CLI,但似乎无法与主机上的Deamon进行通信

aaronkalair@4280bf11224b:/$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.26/containers/json : dial unix /var/run/docker.sock: connect: permission denied

查看其权限揭示了为什么

aaronkalair@4280bf11224b:/$ ls -lah /var/run/docker.sock
srw-rw---- 1 root staff 0 Dec 22 11:32 /var/run/docker.sock

只有root用户或staff组中的用户才能从套接字读取和写入数据,我将通过将aaronkalair用户添加到staff组来解决此问题。 但是请务必谨慎,因为可以访问Docker套接字的任何人都可以访问主机。

RUN usermod -a -G staff aaronkalair

现在我们可以谈谈Docker守护进程

aaronkalair@43db476d04f7:/$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
43db476d04f7 chefdk "/bin/bash" 2 seconds ago Up 1 second upbeat_panini

因此,让我们尝试融合我们的测试厨房!

aaronkalair@43db476d04f7:/cookbooks/teabot$ ./bin/kitchen converge
....Lots of Good Things...
Failed to complete #create action: [Cannot assign requested address - connect(2) for [::1]:32776] on default-ubuntu-1604

奇怪的是, kitchen list只说Errno::EADDRNOTAVAIL并没有多大帮助

我是否真的很倒霉而其他人已经将32776用于其外围端口? 让我们安装netstat看看。

aaronkalair@43db476d04f7:/cookbooks/teabot$ apt-get install net
-tools
E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)
E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?

糟糕,我们需要以root用户身份进入容器进行调试

docker run -it -u root -v /Users/aaronkalair/cookbooks:/cookbooks -v /var/run/docker.sock:/var/run/docker.sock chefdk

然后netstat在端口50332上仅显示一个连接

root@e075956d7a2b:/# netstat
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 e075956d7a2b:50332 keeton.canonical.c:http TIME_WAIT
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags Type State I-Node Path

有趣的是,我们正在Docker内部做一些奇怪的Docker事情,也许它是在主机上使用的?

Aarons-iMac:~ aaronkalair$ netstat | grep 32776

也没有在那里不使用。

我们可以在厨房外绑定到端口32776吗?

root@e075956d7a2b:/# nc -l 32776
root@e075956d7a2b:/# nc -l 0.0.0.0 32776

是的,一切正常

我们可以curl这个占用端口32776神奇东西吗?

root@e075956d7a2b:/# curl -v localhost:32776
* Rebuilt URL to: localhost:32776/
* Trying 127.0.0.1...
* TCP_NODELAY set
* connect to 127.0.0.1 port 32776 failed: Connection refused
* Trying ::1...
* TCP_NODELAY set
* Immediate connect fail for ::1: Cannot assign requested address
* Trying ::1...
* TCP_NODELAY set
* Immediate connect fail for ::1: Cannot assign requested address
* Failed to connect to localhost port 32776: Connection refused
* Closing connection 0
curl: (7) Failed to connect to localhost port 32776: Connection refused

有趣的是, ::1:给我们的错误与来自test-kitchen的错误相同,这是IPv6有点奇怪吗?

快速谷歌确认的IPv6只能在码头工人,如果你指定一个特殊的标志- https://docs.docker.com/engine/userguide/networking/default_network/ipv6/

--ipv6

好的,我实际上不需要IPv6,那么如何停止使用它呢? 好localhost/etc/hosts定义为

root@e075956d7a2b:/# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 e075956d7a2b

因此,如果我仅对那些IPv6线路进行核对,也许会很好吗?

(旁注:显然,编辑/etc/gai.conf可以在IPv4行为发生之前更改IPv6 — https://askubuntu.com/a/38468

root@e075956d7a2b:/# vi /etc/hosts
root@e075956d7a2b:/# cat /etc/hosts
127.0.0.1 localhost
172.17.0.2 e075956d7a2b

并且让我们尝试再次收敛(如果有帮助,请使用调试日志记录)

root@e075956d7a2b:/cookbooks/teabot# ./bin/kitchen converge -l debug
...
D [SSH] opening connection to kitchen@localhost<{:user_known_hosts_file=>"/dev/null", :port=>32776, :compression=>false, :compression_level=>0, :keepalive=>true, :keepalive_interval=>60, :timeout=>15, :keys_only=>true, :keys=>["/cookbooks/teabot/.kitchen/docker_id_rsa"], :auth_methods=>["publickey"], :verify_host_key=>false}>
$$$$$$ [SSH] connection failed, terminating (#<Errno::ECONNREFUSED: Connection refused - connect(2) for 127.0.0.1:32776>)

好吧,这确认了上面的问题是由于尝试在未为其配置的Docker环境中使用IPv6。

现在我们有了一个新问题,到端口32776的SSH连接被拒绝。

实际上在本地端口上监听了什么吗? 回到我们的朋友netstat

root@e075956d7a2b:/cookbooks/teabot# netstat --listening
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
Active UNIX domain sockets (only servers)
Proto RefCnt Flags Type State I-Node Path

不。

测试厨房是否真的设法使容器运行SSH守护进程?

root@e075956d7a2b:/cookbooks/teabot# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9ca4e56ab0b4 2fe546b254ce "/usr/sbin/sshd -D..." 56 minutes ago Up About an hour 0.0.0.0:32776->22/tcp defaultubuntu1604-nologin-43db476d04f7-hds4zcup

当然看起来像这样,如果我们在那个sshd容器中执行exec实际正在运行?

root@e075956d7a2b:/cookbooks/teabot# docker exec -it 9ca4e56ab0b4 bash
root@9ca4e56ab0b4:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 16:17 ? 00:00:00 /usr/sbin/sshd -D -o UseDNS=no -o UsePAM=no -o PasswordAuthentication=yes
root 6 0 0 17:15 pts/0 00:00:00 bash
root 16 6 0 17:15 pts/0 00:00:00 ps -ef

是的

再看上面的32776 docker ps的输出,我们看到32776来自哪里,它是容器中22映射到主机上的端口。

但是我们不在主机上,而是在Dockers自己的网络上的容器内。

好的,这就是我们的位置:

  • kitchen-docker制作了一个可以在其中运行chef的容器,但它假定我们在主机上运行,​​并且sshd侦听Docker映射到localhost上的22端口
  • 我们位于网络上的Docker容器中,而不是主机Docker,因此无法使用端口映射22 -> 32776 ,无论它位于localhost上的端口是什么

幸运的是,所有这些问题都应该可以解决,我们与运行sshd的容器位于同一网络上,因此我们应该能够通过其IP地址与IP进行通信。

root@e075956d7a2b:/cookbooks/teabot# docker inspect 9ca4e56ab0b4 | grep -i ipaddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.3",
"IPAddress": "172.17.0.3",

快速检查一下是否可以与我们交谈

In the container created by kitchen-docker
root@9ca4e56ab0b4:/# nc -l -p 5000
PING
^C

在容器中运行chefdk

root@e075956d7a2b:/cookbooks/teabot# telnet 172.17.0.3 5000
Trying 172.17.0.3...
Connected to 172.17.0.3.
Escape character is '^]'.
PING

非常好,可以工作,现在可以修改kitchen-docker

在此处设置端口和主机名-https: //github.com/test-kitchen/kitchen-docker/blob/master/lib/kitchen/driver/docker.rb#L124

因此,让我们快速将IP地址和端口22入侵其中以检查其是否有效

root@e075956d7a2b:/cookbooks/teabot# vi vendor/ruby/2.4.0/gems/kitchen-docker-2.6.0/lib/kitchen/driver/docker.rb
<some quick edits>

让我们看看是否可行:

root@e075956d7a2b:/cookbooks/teabot# ./bin/kitchen converge -l debug
...
D      [SSH] kitchen@172.17.0.3<{:user_known_hosts_file=>"/dev/null", :port=>"22", :compression=>false, :compression_level=>0, :keepalive=>true, :keepalive_interval=>60, :timeout=>15, :keys_only=>true, :keys=>["/cookbooks/teabot/.kitchen/docker_id_rsa"], :auth_methods=>["publickey"], :verify_host_key=>false}> (sudo -E sh -c '
[SSH] Established
...

到这里,它起作用了!

我为kitchen-docker编写了此补丁,以在不进行IP地址硬编码的情况下完成上述操作-https: //github.com/test-kitchen/kitchen-docker/pull/283/files

(旁注:给定Docker ID的方式是获得Docker容器IP地址的一种非常简单的方法)

docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container id>

因此,我们可以在Docker容器内运行ChefDK和kitchen-docker(对kitchen-docker进行了一些小的更改)。 为此,我将vim安装在容器中,并将vimgitchef配置从主机安装到容器中,因此不必离开容器即可在编辑和测试之间进行切换。

(尽管食谱是从主机安装的,所以您可以在主机上进行编辑,然后切换到容器以测试是否需要)

您可以在此处查看最终的Dockerfile并在我的Github存储库上运行命令-https: //github.com/AaronKalair/docker_apps/tree/master/chefdk

在Twitter上关注我@AaronKalair

From: https://hackernoon.com/can-you-run-chefdk-and-kitchen-docker-inside-of-a-docker-container-10c384571f34

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值