Help us understand the problem. What is going on with this article?

Rubyが動くdistroless image は作ることができるのか

More than 1 year has passed since last update.

distroless とは

Googleが公開している、ランタイムとして使用するためのDockerイメージです。

GoogleContainerTools/distroless: 🥑 Language focused docker images, minus the operating system.

しかし公式で提供されている言語の中にはRubyが含まれていません。

ならやってみましょう。

distrolessについての参考URL

目標

  • $ ruby -v ができる
  • sinatra でコンテナからレスポンスを返すことができる

これを目指してがんばっていきます。

$ ruby -v ができるまで

突然ですが、 ruby:2.5.3-stretch というオフィシャルイメージにおける /usr/local/bin/ 以下を見てみましょう。

$ docker run --rm -it ruby:2.5.3-stretch bash
root@c494cf51c146:/# ls -al /usr/local/bin/
total 188
drwxrwsr-x 1 root staff    102 Oct 18 23:33 .
drwxrwsr-x 1 root staff     84 Oct 18 23:33 ..
-rwxr-xr-x 1 root staff    605 Oct 18 23:33 bundle
-rwxr-xr-x 1 root staff    607 Oct 18 23:33 bundler
-rwxr-xr-x 1 root staff   4839 Oct 18 23:33 erb
-rwxr-xr-x 1 root staff    548 Oct 18 23:33 gem
-rwxr-xr-x 1 root staff    192 Oct 18 23:33 irb
-rwxr-xr-x 1 root staff    589 Oct 18 23:33 rake
-rwxr-xr-x 1 root staff    940 Oct 18 23:33 rdoc
-rwxr-xr-x 1 root staff    190 Oct 18 23:33 ri
-rwxr-xr-x 1 root staff 148928 Oct 18 23:33 ruby
-rwxr-xr-x 1 root staff    655 Oct 18 23:33 update_rubygems
root@c494cf51c146:/#

なるほど、これを雑に全部持ってくれば Ruby の動作において過不足なさそうですし、 $ ruby -v も動きそうです。

FROM ruby:2.5.3-stretch as ruby
FROM gcr.io/distroless/base
COPY --from=ruby /usr/local/bin/ /usr/local/bin

CMD ["/usr/local/bin/ruby", "-v"]
$ docker build --no-cache -t distroless-ruby .
...snip...

$ docker run --rm distroless-ruby
/usr/local/bin/ruby: error while loading shared libraries: libruby.so.2.5: cannot open shared object file: No such file or directory

だめでした。共有ライブラリが見付からないので ruby が実行できません。

ので、共有ライブラリも持ってきます。

FROM ruby:2.5.3-stretch as ruby
FROM gcr.io/distroless/base
COPY --from=ruby /usr/local/lib/ /usr/local/lib
COPY --from=ruby /usr/local/bin/ /usr/local/bin

CMD ["/usr/local/bin/ruby", "-v"]
$ docker build --no-cache -t distroless-ruby .
...snip...

$ docker run --rm distroless-ruby
ruby 2.5.3p105 (2018-10-18 revision 65156) [x86_64-linux]

よさそうですね。

sinatra でコンテナからレスポンスを返すことができるまで

公式サイトの例にある、この単純なsinatra appを動かすことを目標とします。

server.rb
require 'sinatra'

configure do
  set :bind, '0.0.0.0'
end

get '/' do
  'Hello world!'
end
Gemfile
# frozen_string_literal: true

source "https://rubygems.org"
gem "sinatra"

そして、このようなDockerfileを作成し、buildしてみます。

Dockerfile
FROM distroless-ruby
EXPOSE 4567
WORKDIR /app
COPY . .
RUN ["/usr/local/bin/bundle", "install"]
CMD ["/usr/local/bin/bundle", "exec", "ruby", "server.rb"]

RUNCMD が exec形式1なのは、distrolessには sh が含まれていないために、単に bundle install と書くとshellが無くてエラーになるためです。

$ docker build --no-cache -t distroless-ruby-sinatra .
Sending build context to Docker daemon  2.074MB
Step 1/6 : FROM distroless-ruby
 ---> c07af2ab24f5
Step 2/6 : EXPOSE 4567
 ---> Running in 7aa0572c9251
Removing intermediate container 7aa0572c9251
 ---> 2d33d9d70ece
Step 3/6 : WORKDIR /app
 ---> Running in 0189cc6b0a87
Removing intermediate container 0189cc6b0a87
 ---> 7d922895beac
Step 4/6 : COPY . .
 ---> aa73bbf36f8e
Step 5/6 : RUN ["/usr/local/bin/bundle", "install"]
 ---> Running in acd4f58ea502
Don't run Bundler as root. Bundler can ask for sudo if it is needed, and
installing your bundle as root will break this application for all non-root
users on this machine.
/usr/local/lib/ruby/site_ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require': libz.so.1: cannot open shared object file: No such file or directory - /usr/local/lib/ruby/2.5.0/x86_64-linux/zlib.so (LoadError)
...snip...

ダメでしたね。zlib が無いので、どこかから持ってくるしかないです。(distrolessにはパッケージマネージャーがない)

依存するライブラリを持ってくる

zlib はどこにあるのでしょうか。

ruby のバイナリを持ってきている元、 ruby:2.5.3-stretch 、つまりDebian stretch で zlib をインストールする際には、 apt install zlib1g というコマンドを実行します。

Debian -- stretch の zlib1g パッケージに関する詳細

ならば、このときインストールされるファイルを持ってくればいいことになります。

それは dpkg --listfiles で取得できるので、見てみましょう。

$ docker run --rm -it ruby:2.5.3-stretch bash
root@1ed6fdb8acb4:/# dpkg --listfiles zlib1g
/.
/lib
/lib/x86_64-linux-gnu
/lib/x86_64-linux-gnu/libz.so.1.2.8
/usr
/usr/share
/usr/share/doc
/usr/share/doc/zlib1g
/usr/share/doc/zlib1g/changelog.Debian.gz
/usr/share/doc/zlib1g/changelog.gz
/usr/share/doc/zlib1g/copyright
/lib/x86_64-linux-gnu/libz.so.1

実体は /lib/x86_64-linux-gnu/libz.so.1.2.8 のようですね。これらをコピーしてきましょう。

Dockerfile
FROM ruby:2.5.3-stretch as ruby
FROM gcr.io/distroless/base
COPY --from=ruby /lib/x86_64-linux-gnu/libz.so.* /lib/x86_64-linux-gnu/
COPY --from=ruby /usr/local/lib/ /usr/local/lib
COPY --from=ruby /usr/local/bin/ /usr/local/bin

CMD ["/usr/local/bin/ruby", "-v"]

同様に、libyaml も無くエラーになってしまうので、最終的に sinatraのbundle install が成功する distroless-ruby のDockerfileはこうなります。

Dockerfile
FROM ruby:2.5.3-stretch as ruby
FROM gcr.io/distroless/base

COPY --from=ruby /lib/x86_64-linux-gnu/libz.so.* /lib/x86_64-linux-gnu/
COPY --from=ruby /usr/lib/x86_64-linux-gnu/libyaml* /usr/lib/x86_64-linux-gnu/
COPY --from=ruby /usr/local/lib/ /usr/local/lib
COPY --from=ruby /usr/local/bin/ /usr/local/bin

CMD ["/usr/local/bin/ruby", "-v"]

docker run sinatra

それでは、 sinatraを動かしてみます。

$ curl -v http://localhost:4567
* Rebuilt URL to: http://localhost:4567/
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 4567 (#0)
> GET / HTTP/1.1
> Host: localhost:4567
> User-Agent: curl/7.61.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html;charset=utf-8
< Content-Length: 12
< X-Xss-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< Server: WEBrick/1.4.2 (Ruby/2.5.3/2018-10-18)
< Date: Thu, 25 Oct 2018 15:42:46 GMT
< Connection: Keep-Alive
<
* Connection #0 to host localhost left intact
Hello world!
$ docker run --rm -it -p 4567:4567 distroless-ruby-sinatra
[2018-10-25 14:47:17] INFO  WEBrick 1.4.2
[2018-10-25 14:47:17] INFO  ruby 2.5.3 (2018-10-18) [x86_64-linux]
== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from WEBrick
[2018-10-25 14:47:17] INFO  WEBrick::HTTPServer#start: pid=1 port=4567
172.17.0.1 - - [25/Oct/2018:14:47:21 +0000] "GET / HTTP/1.1" 200 12 0.0158
172.17.0.1 - - [25/Oct/2018:14:47:21 UTC] "GET / HTTP/1.1" 200 12
- -> /

動きましたね。

https://github.com/unasuke/distroless-ruby

サイズ

$ docker image ls | grep ruby
distroless-ruby  latest                53.3MB
ruby             2.5.3-alpine3.8       45.1MB
ruby             2.5.3-slim-stretch    178MB
ruby             2.5.3-stretch         869MB

なるほど、alpineよりは大きいですが、slimの半分くらいにはなっていますね。

今後の課題

sinatraで数行程度という、軽く依存もほとんどないRuby scriptが動くようにはなりました。しかしデータベースに接続する、Railsのような大きなアプリケーションは依存関係を解決するのが難しそうです。

また、 Ruby は バッククオートでシェルコマンドを実行できる言語機能がありますが、これも動きません。busyboxなどを入れる必要がありそうです。

`cat foo.txt`  # /bin/cat が無いので動作しない
yu_suke1994
帰って寝たい
https://unasuke.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした