我目前在使用Serverspec测试Docker镜像构建时遇到问题。简而言之,我想要做的是确保在图像构建期间明确清除Ruby gems构建缓存,例如,通过在Dockerfile中发出rm -rf /usr/lib/ruby/gems/*/cache/*.gem
。
我正在使用的Dockerfile框架如下所示:
# Dockerfile
FROM alpine:3.7
RUN apk add --no-cache \
dumb-init \
ruby \
&& apk add --no-cache --virtual .build-deps \
build-base \
ruby-dev
RUN gem install --no-rdoc --no-ri json \
&& gem install --no-rdoc --no-ri oj
RUN apk del .build-deps \
&& rm -rf /var/cache/apk/* \
/tmp/* /var/tmp/*
在添加gem cache删除命令之前,我实现了以下Serverspec测试,以便能够从失败的测试开始:
# ./spec/Dockerfile_spec.rb
describe "Dockerfile" do
before(:all) do
@image = Docker::Image.build_from_dir('.')
@image.tag(repo: 'demo', tag: 'latest')
set :os, family: :alpine
set :backend, :docker
set :docker_image, @image.id
end
it "removes build dependencies during cleanup" do
# Some more assertions
# ...
expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to eq 0
end
end
假设上面的Dockerfile,测试运行为绿色:
$ bundle exec rspec spec/Dockerfile_spec.rb
....
Finished in 3.95 seconds (files took 0.24091 seconds to load)
4 examples, 0 failures
然而,这不应该是这种情况,因为尚未清除gem缓存,因此不是空的,即我期望在测试执行期间相应的断言失败。
通过从新构建的图像中启动容器并检查gems缓存目录,可以轻松验证这一点:
$ docker run --rm -it demo:latest sh
/ # ls /usr/lib/ruby/gems/2.4.0/cache/
json-2.1.0.gem oj-3.4.0.gem
以相反的方式测试并期望非空目录,执行失败并显示错误消息:
# ./spec/Dockerfile.rb
it "removes build dependencies during cleanup" do
# Some more assertions
# ...
expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0
end
# Command line
$ bundle exec rspec spec/Dockerfile_spec.rb
.F..
Failures:
1) Dockerfile removes build dependencies during cleanup
Failure/Error: expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0
expected: value != 0
got: 0
(compared using ==)
很明显,Dir.glob()
命令不会返回gems缓存目录中正确数量的文件。
有趣的是,在容器内手动运行Dir.glob()
命令会返回预期的结果:
$ docker run --rm -it demo:latest sh
/ # apk add --no-cache ruby-irb
...
/ # irb
irb(main):001:0> Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size
=> 2
首先,这让我觉得测试没有在容器内正确执行,而是在主机上执行,但是进一步的实验无法证实这一点。
你有什么想法吗?这可能是Serverspec / Rspec问题吗?
THX!
首先,这让我觉得测试没有在容器内正确执行,而是在主机上执行,但是进一步的实验无法证实这一点。
我终于发现这个假设是错误的。实际上,无论出于何种原因,Dir.glob()
调用都在容器外执行。
答案 0 :(得分:0)
我终于弄清楚出了什么问题,实际上它非常简单:
假设我们有一个像这样的Serverspec测试用例:
it "removes build dependencies during cleanup" do
# Some more assertions
# ...
expect(Dir.glob('/usr/lib/ruby/gems/*/cache/*.gem').size).to_not eq 0
end
现在执行期间发生的事情是在调用Dir.glob()
例程之前评估expect()
调用。因此,正如我在帖子中已经指出的那样,Dir.glob()
命令在容器范围之外运行,并检查相应的主机目录。
如果您想检查容器文件夹是否为空,可以按如下方式实现:
it "clears gem/apk caches as well as tmp files/dirs" do
expect(command('ls /usr/lib/ruby/gems/*/cache/*.gem | wc -l').stdout).to eq "0\n"
end
不可否认,这可能不是最美丽和最优雅的解决方案,但基本上可以胜任。如果您有其他想法和建议,请随时发布。