Cloud Native Buildpacksを使ってRails 7のDockerイメージを作成する

はじめに

Railsアプリを書いているのですが、Dockerfileをちまちま書くのが面倒だったので、Cloud Native Buildpacksを使ってみることにしました。

参考

@satococoaさんがすでに検証記事を書かれていたのでそれを参考に進めていきます。

zenn.dev

対象アプリとして、以下で作成しているRailsアプリを利用します。

github.com

Rails 7にwebpackを使っています。

また、公式サイトも参考にします。

buildpacks.io

環境準備

packコマンドをインストールします。公式サイトにやり方が書かれているのでそれをそのまま実行します。

buildpacks.io

また、Procfileというものが必要ということで作成します。中身は以下のようなものにしました。

web: bundle exec puma -t 5:5 -p ${PORT:-8080} -e ${RACK_ENV:-production}
release: bin/rails db:migrate

Procfileの説明は以下のものを参照。

devcenter.heroku.com

Railsアプリの起動に関する記述については以下のものを参考にしました。

devcenter.heroku.com

作成する

以下のコマンドを実行します。

$ pack build イメージ名 --builder heroku/buildpacks:20

以下のようなログが流れてイメージが作成されます。

20: Pulling from heroku/buildpacks
Digest: sha256:2b638e111bb446d298e463442258da052884f9c64b2347f5cb4fc1c5af334d98
Status: Image is up to date for heroku/buildpacks:20
20-cnb: Pulling from heroku/heroku
Digest: sha256:e12a18dc1ea56391d5c178a6ae32485b01f2474c9b72db2d56197bb575fb40ec
Status: Image is up to date for heroku/heroku:20-cnb
===> ANALYZING
Previous image with name "イメージ名" not found
===> DETECTING
Warning: Buildpack 'heroku/ruby@0.0.0' requests deprecated API '0.4'
Warning: Buildpack 'heroku/python@0.0.0' requests deprecated API '0.4'
Warning: Buildpack 'heroku/scala@0.0.0' requests deprecated API '0.4'
Warning: Buildpack 'heroku/php@0.0.0' requests deprecated API '0.4'
Warning: Buildpack 'heroku/go@0.0.0' requests deprecated API '0.4'
Warning: Buildpack 'heroku/nodejs-function@0.9.18' requests deprecated API '0.2'
Warning: Buildpack 'heroku/nodejs@0.5.14' requests deprecated API '0.2'
Warning: Buildpack 'heroku/gradle@0.0.0' requests deprecated API '0.4'
heroku/ruby     0.0.0
heroku/procfile 2.0.0
===> RESTORING
===> BUILDING
-----> Installing bundler 2.3.25
-----> Removing BUNDLED WITH version in the Gemfile.lock
-----> Compiling Ruby/Rails
-----> Using Ruby version: ruby-3.2.1
-----> Installing dependencies using bundler 2.3.25
       Running: BUNDLE_WITHOUT='development:test' BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4
       Fetching gem metadata from https://rubygems.org/.........
       Fetching rake 13.0.6
       (省略)
       Bundle completed (13.31s)
       Cleaning up the bundler cache.
       Removing bundler (2.3.25)
-----> Installing node-v16.18.1-linux-x64
-----> Installing yarn-v1.22.19
-----> Detecting rake tasks
-----> Preparing app for Rails asset pipeline
       Running: rake assets:precompile
       yarn install v1.22.19
       [1/4] Resolving packages...
       [2/4] Fetching packages...
       [3/4] Linking dependencies...
       [4/4] Building fresh packages...
       Done in 20.31s.
       yarn run v1.22.19
       $ webpack --config webpack.config.js
       asset application.js 203 KiB [emitted] [minimized] (name: application) 2 related assets
       orphan modules 449 KiB [orphan] 80 modules
       runtime modules 670 bytes 3 modules
       cacheable modules 446 KiB
         ./app/javascript/application.js + 68 modules 427 KiB [built] [code generated]
         ./node_modules/@rails/actioncable/src/index.js + 9 modules 19.1 KiB [built] [code generated]
       webpack 5.75.0 compiled successfully in 5486 ms
       Done in 6.28s.
       yarn install v1.22.19
       [1/4] Resolving packages...
       success Already up-to-date.
       Done in 0.22s.
       yarn run v1.22.19
       $ sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules
       Done in 3.45s.
       (省略)
       Asset precompilation completed (32.99s)
       Cleaning assets
       Running: rake assets:clean
-----> Detecting rails configuration



[Discovering process types]
Procfile declares types -> web, release
===> EXPORTING
Adding layer 'heroku/ruby:profile'
Adding layer 'buildpacksio/lifecycle:launch.sbom'
Adding 1/1 app layer(s)
Adding layer 'buildpacksio/lifecycle:launcher'
Adding layer 'buildpacksio/lifecycle:config'
Adding layer 'buildpacksio/lifecycle:process-types'
Adding label 'io.buildpacks.lifecycle.metadata'
Adding label 'io.buildpacks.build.metadata'
Adding label 'io.buildpacks.project.metadata'
Setting default process type 'web'
Saving イメージ名...
*** Images (26cf6771ef02):
      イメージ名
Adding cache layer 'heroku/ruby:shim'
Successfully built image イメージ名

builderで指定するのは以下で公開されているものからheroku/buildpacks:20を指定しました。

github.com

ドキュメントによると、pack builder suggestで候補を得られるようです。

buildpacks.io

起動

これでイメージが作成されたので、起動します。

$ $ docker run --rm -p 8080:8080 my_rails_app:1.0.0
Puma starting in single mode...
* Puma version: 5.6.5 (ruby 3.2.1-p31) ("Birdie's Version")
*  Min threads: 5
*  Max threads: 5
*  Environment: production
*          PID: 1

無事起動しました。

docker psで見てみます。無事起動していることが確認できます。

$ docker ps
CONTAINER ID   IMAGE                COMMAND              CREATED          STATUS          PORTS                                       NAMES
コンテナID       イメージ名  "/cnb/process/web"   26 seconds ago   Up 25 seconds   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   goofy_matsumoto

db:migrate

db:migrateをどうやって実行しようかと思いましたが、こんな感じで起動しているコンテナ内でreleaseを実行させることで実現しました。今回、データベースはSQLite3にしているので、コンテナ内でコマンドを実行する必要がありました。

$ docker exec -it コンテナID /cnb/process/release

SQLite3以外の場合は、docker runentrypointオプションを指定してあげるとよさそうです。

docs.docker.com

具体的には、docker run --entrypoint release イメージ名としてあげると良いようです。