我目前在通过 Serverspec 测试 Docker 映像构建时遇到问题。简而言之,我想做的是确保在图像构建期间 Ruby gems 构建缓存被明确清除,例如通过rm -rf /usr/lib/ruby/gems/*/cache/*.gem
在 Dockerfile 中发出。
我正在使用的 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 缓存删除命令之前,我实现了以下 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 问题吗?
谢谢!
编辑
首先,这让我认为测试没有在容器内正确执行,而是在主机上执行,但进一步的实验无法证实这一点。
我终于发现这个假设是错误的。实际上,Dir.glob()
无论出于何种原因,调用都是在容器外部执行的。