はじめに
これまでいくつかのDockerimageを構築してきました。OpenAPIとSinatraの組み合わせは、お手軽なのですが、これまで作成してきたイメージを作り直したところ問題が発生しました。
Dockerでbundle exec rackup ...を実行したタイミングでエラーメッセージが表示されてしまうようになりました。
$ sudo docker run -it --rm \
--env LC_CTYPE=ja_JP.UTF-8 \
--env RACK_ENV="deployment" \
-p 8080:8080 \
--name webapp mywebapp \
...
+ bundle exec rackup --host 0.0.0.0 --port 8080
env: can't execute 'ruby2.7': No such file or directory
コンテナ内のファイルを確認して、最後のメッセージは ./lib/ruby/2.7.0/bin/rackupコマンドがruby2.7を呼び出しているからということは分かりました。
Dockerfileは次のようになっていて、multi-stage buildを利用しています。
FROM ruby:2.7-alpine as rubydev
RUN apk update && \
apk add --no-cache tzdata bash ca-certificates make gcc libc-dev
COPY . /app
WORKDIR /app
RUN bundle config path lib
RUN bundle install
FROM ruby:2.7-alpine
RUN apk update && \
apk add --no-cache tzdata bash ca-certificates
COPY --from=rubydev /app /app
WORKDIR /app
ADD run.sh /run.sh
RUN chmod +x /run.sh
RUN addgroup sinatra
RUN adduser -S -G sinatra sinatra
USER sinatra
RUN bundle config path lib ## generating the ~/.bundle/config file.
ENTRYPOINT ["/run.sh"]
理由
rackupを探してみると、2箇所に存在していて、なぜか lib/ruby/2.7.0/bin/rackup では ruby2.7バイナリを探していました。
+ find lib/ -name rackup
lib/ruby/2.7.0/bin/rackup
lib/ruby/2.7.0/gems/rack-2.2.3/bin/rackup
+ cat lib/ruby/2.7.0/bin/rackup
#!/usr/bin/env ruby2.7
...
+ cat lib/ruby/2.7.0/gems/rack-2.2.3/bin/rackup
#!/usr/bin/env ruby
...
ワークアラウンドで対応するのであれば、/usr/local/bin/ruby2.7 を準備する方法もあります。
RUN ln -s ruby /usr/local/bin/ruby2.7
ただ、こういった現象が発生するはずがないと思っていたので、少し原因の調査を進めました。
原因
結論からいうと、問題は Dockerfile の次の箇所にありました。
COPY . /app
ここではDockerfileを含めて全てのファイルをコピーしています。
Dockerfile自体は秘密ではないため、この方法自体は横着しているなという程度のものですが、問題はこの時点でカレントディレクトリの./lib/ruby/2.7.0/ ディレクトリもコピーされてしまったことでした。
コンテナを作成する前にコードをデバッグする目的で、ローカルのUbuntu環境でもrackupを起動しています。
そのため、bundle install コマンドを実行することで、./lib/ruby/2.7.0/bin/rackup が配置されていたのでした。
Ubuntuでは /usr/bin/ruby が ruby2.7へのシンボリックリンクとなっているため、このrackupはruby2.7から起動されるようになっていました。
$ head -2 ./lib/ruby/2.7.0/bin/rackup
#!/usr/bin/env ruby2.7
#
他のDockerfileを処理するMakefileの中で、./lib/ruby ディレクトリを削除していたのですが、このイメージを作成する作業用リポジトリのMakefileには、このコードが含まれていませんでした。
## local debug用のコマンド
bundle-install:
bundle install --path lib
## dockerイメージをbuild
docker-build:
rm Gemfile.lock || true
sudo docker build . --tag $(DOCKER_IMAGE)
Gemfile.lockファイル以外に、./lib/ruby/, ./.bundle/ ディレクトリを削除するコードを追加することで恒久的な対策となりました。
NAME = sinatra-webapp
DOCKER_IMAGE = sinatra-webapp
DOCKER_IMAGE_VERSION = 0.1.1
IMAGE_NAME = $(DOCKER_IMAGE)
REGISTRY_SERVER = docker.io
REGISTRY_LIBRARY = please-overwrite-by-your-docker-id
docker-build:
rm -f Gemfile.lock
rm -fr lib/ruby
rm -fr .bundle
sudo docker build . --tag $(IMAGE_NAME)
docker-build-prod:
rm -f Gemfile.lock
rm -fr lib/ruby
rm -fr .bundle
sudo docker build . --tag $(IMAGE_NAME):$(DOCKER_IMAGE_VERSION) --no-cache
docker-tag:
sudo docker tag $(IMAGE_NAME):$(DOCKER_IMAGE_VERSION) $(REGISTRY_SERVER)/$(REGISTRY_LIBRARY)/$(IMAGE_NAME):$(DOCKER_IMAGE_VERSION)
docker-push:
sudo docker push $(REGISTRY_SERVER)/$(REGISTRY_LIBRARY)/$(IMAGE_NAME):$(DOCKER_IMAGE_VERSION)
上記のMakefileはGitHubで公開しているdocker-sccp-sinatra-sampleに含まれる _docker/Makefile からの抜粋です。
結果は勘違いから勝手に悩んでいるだけだったのですが、手持ちのコンテナをbuildするプロセスを再度チェックしました。他のイメージでは ./.bundle/ や、./lib/ruby を削除していたので、思い込みがあったこともあって、解決がすぐに出来ませんでした。
現在はテンプレートを準備しているので、基本的には同じような問題は発生していません。
以上