Docker, Rails7.1.3.2の環境構築が上手くいかなかった記録

# 初めに
初学者です。
webアプリを個人で開発するために、dockerを使用してruby on railsでの開発環境構築を実施した際の記録を共有します。
公開日時点での最新安定版を使用しています。

本記事ではdockerを使いherokuへのデプロイをgithub actionsで自動化するところまで行いますが、最終的にエラーで行き詰まり、別記事にて再度環境構築をやり直します。

成功した手順のみを参考にしたい方は、こちらの記事を読んでください。
https://qiita.com/tkhero555/items/a1811369c59021077d62


## 構築したい技術構成
| カテゴリ | 技術 |
|---|---|
| フロントエンド | javascript/Hotwire/bootstrap　|
| バックエンド | ruby3.3.0/rails7.1.3.2 |
| データベース | PostgreSQL16.2 |
| 認証 | devise |
| 環境構築 | Docker / docker-compose |
| CI/CD | Github Actions |
| インフラ　|　heroku |


## docker関係のファイルを作成~rails newまで
作業するディレクトリを作成した後、docker関係のファイルを作成します。
- compose.yml
- Dockerfile
- Gemfile
- Gemfile.lock

各ファイルの構成
```compose.yaml
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```
Dockerfile
```
FROM ruby:3.3.0

# データベース用にPostgreSQLをインストール
RUN apt-get update -qq && apt-get install -y postgresql-client
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
COPY . /myapp

# Rails サーバ起動
CMD ["rails", "server", "-b", "0.0.0.0"]
```
Gemfile
```
source 'https://rubygems.org'
gem 'rails', '~> 7.1', '>= 7.1.3.2'
```
イメージをbuildする
```
docker compose build
```

rails newを実行する。
```
$ docker compose run --rm web rails new . --force --no-deps --database=postgresql
```

上手くいかずログを確認すると、
```
! db The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 0.0s 
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: exec: "rails": executable file not found in $PATH: unknown
```
dockerが実行しようとするプラットフォームと、ホストのプラットフォームが違うよ〜というエラーと、railsが$path内に見つからないよとのエラーが出ている。

Dockerfileにbundle installを追記
```
FROM ruby:3.3.0

RUN apt-get update -qq && apt-get install -y postgresql-client
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp/

CMD ["rails", "server", "-b", "0.0.0.0"]
```
コマンドにプラットフォームオプションを追加して再実行
```
docker compose run --platform linux/amd64 --rm web rails new . --force --no-deps --database=postgresql
```
下記の表示が返ってきて上手くいかない
```
unknown flag: --platform
```
compose.yamlにplatformの指定を追記する
```
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    platform: linux/amd64
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```
再度rails newを実行
```
docker compose run web rails new . --force --no-deps --database=postgresql
```
rails関係のファイルが作成されたことが確認できた。

## データベースの作成をしようとしたらエラー発生

rails newで作成されたファイルの中から、データベースのymlファイルを編集する。
```config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  host: db
  username: postgres
  password: password

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test
```
データベースを作成する
```
docker compose run web rails db:create
```
エラーが発生したので、実行ログを見てみる。
```
Could not find sprockets-rails-3.4.2, pg-1.5.6, puma-6.4.2, importmap-rails-2.0.1, turbo-rails-2.0.5, stimulus-rails-1.3.3, jbuilder-2.11.5, bootsnap-1.18.3, debug-1.9.2, web-console-4.2.1, capybara-3.40.0, selenium-webdriver-4.19.0, sprockets-4.2.1, msgpack-1.7.2, bindex-0.8.1, addressable-2.8.6, regexp_parser-2.9.0, xpath-3.2.0, rubyzip-2.3.2, websocket-1.2.10, public_suffix-5.0.4 in locally installed gems
Run `bundle install --gemfile /myapp/Gemfile` to install missing gems.
```
必要なgemが存在しないとのこと。
先ほどrails newをした際にgemfileに追記されたgemがinstallされていないのが原因とか？
bundle installしてみる
```
docker compose run web bundle install
```
再度データベースを作成してみるが、同じエラーが継続

Gemfileの変更内容がdockerイメージに反映されておらず、ローカルとコンテナのファイルに差異があるとか？
buildして、再度データベースを作成してみる。
実行結果
```
$ docker compose run web rails db:create
[+] Creating 1/0
 ✔ Container runteq_graduation_exam-db-1  R...                                         0.0s 
/rails/bin/docker-entrypoint: line 8: exec: rails: not found
```
ダメそう。

また、docker compose upしてみるとエラーが発生している。
```
/usr/local/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/application.rb:658:in `validate_secret_key_base': Missing `secret_key_base` for 'production' environment, set this string with `bin/rails credentials:edit` (ArgumentError)
web-1  | 
web-1  |         raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `bin/rails credentials:edit`"
```
本番環境で使うsecret_key_baseが不足している？
この時点では開発環境の設定で環境を用意したつもりなのだけど、どういうことだ？

調べるとDockerfileの記述がいつの間にか変わっており、railsの環境がproduction設定になっていた。
```
# Set production environment
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development"
```
どうやらrails7.1の機能で、rails newの際に自動的に本番環境用のDockerfileを生成するようになっているらしい。
この機能で用意していたDockerfileが上書きされておかしくなったようだ。

development指定に変更して、BUNDLE_WITHOUTを削除してみる。
buildしてupしたところ、無事にコンテナが起動した。
再度データベース作成したらエラー
```
$ docker compose exec web rails db:create
OCI runtime exec failed: exec failed: unable to start container process: 
exec: "rails": executable file not found in $PATH: unknown
```

railsコマンドの前にbundle execをつけてみる。
```
$ docker compose exec web bundle exec rails db:create
Created database 'myapp_development'
Created database 'myapp_test'
```
行けたっぽい。

localhost::3000にブラウザでアクセスして、Railsアプリのページが表示されていることを確認できた！


## herokuへのデプロイ設定
参考記事に従って、docker関連ファイルに変更を加える。
※後から気づいたが、herokuCLIを利用してデプロイする場合、ここでの本番環境に関する設定変更は不要だった。

Dockerfile
ENV設定をproductionに変更
```
# Set production environment
ENV RAILS_ENV="production" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle"
```
いつの間にか追記されていた下2行は一旦コメントアウト
```database.yml
production:
  <<: *default
  database: myapp_production
  # username: myapp
  # password: <%= ENV["MYAPP_DATABASE_PASSWORD"] %>
```
```config/enviroments/production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = true # false
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = true # ENV["RAILS_SERVE_STATIC_FILES"].present?
```

herokuに会員登録して、月額費用を支払う設定を完了しておく。

Heroku CLIをインストールする。
macなのでhomebrew
```
brew tap heroku/brew && brew install heroku
```
heroku loginコマンドでアカウントログインを完了する。

作業中のアプリのディレクトリで、herokuのアプリケーションを作成する。
```
heroku create -a アプリ名
```
デプロイする。
```
git push heroku main
```
デプロイしたアプリにアクセスする。
```
heroku open
```


## github actionsでデプロイを自動化
herokuのAPIキーをサイトから取得する。

githubにアクセスし、リポジトリのメニューからsettingsタブ、secrets and variablesのactionsでnew repository secretをクリック
2つのsecretを作成する。
- HEROKU_API_KEY: herokuのAPIキーを内容に入れておく
- HEROKU_APP_NAME:デプロイ先のherokuアプリ名を入れておく

ローカルの作業ディレクトリで.github/workflowsディレクトリを作成し、その中でdeploy.ymlファイルを作成する。
```.github/workflows/deploy.yml
name: Deploy to Heroku
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Login to Heroku
        uses: akhileshns/heroku-deploy@v3.12.12 # This is the action
        with:
          heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
          heroku_app_name: ${{ secrets.HEROKU_APP_NAME }}
          heroku_email: "nmt.1727@gmail.com"
```

作成したワークフローファイルをリモートリポジトリにプッシュすれば設定完了

mainブランチに変更を加えた際に自動でherokuへのデプロイを実行する設定にしている。
githubのリポジトリに行ってactionsタブから動作を確認できる。


## controllerを作成しようとした際にエラー発生
コントローラーを作成しようとしたところ、エラー発生
```
/usr/local/bundle/ruby/3.3.0/gems/thor-1.3.1/lib/thor/actions/
create_file.rb:64:in `initialize': Permission denied @ rb_sysopen - 
/rails/app/controllers/line_bot_controller.rb (Errno::EACCES)
```
コントローラーを作成しようとした時に、書き込み権限が無いエラー

ディレクトリの所有者をls -laで確認してみる。
```
$ cd app/controllers/
mynameMacBook-Air:controllers myname$ ls -la
total 8
drwxr-xr-x   4 myname  staff  128  4  1 14:13 .
drwxr-xr-x  11 myname  staff  352  4  1 14:13 ..
-rw-r--r--   1 myname  staff   57  4  1 14:13 application_controller.rb
drwxr-xr-x   3 myname  staff   96  4  1 14:13 concerns
```
dockerのコンテナの中に入ってから、ls -laで確認してみる。
```
rails@f1bf5a5f353c:/rails$ cd app/
rails@f1bf5a5f353c:/rails/app$ cd controllers/
rails@f1bf5a5f353c:/rails/app/controllers$ ls -la
total 16
drwxr-xr-x  3 root root 4096 Mar 31 09:18 .
drwxr-xr-x 11 root root 4096 Mar 31 09:18 ..
-rw-r--r--  1 root root   57 Mar 31 09:18 application_controller.rb
drwxr-xr-x  2 root root 4096 Mar 31 09:18 concerns
```
rootが所有者になっている。
```
rails@f1bf5a5f353c:/rails/app/controllers$ whoami
rails
```
コンテナ内での作業者はrailsになっている。
rootが所有しているディレクトリでrailsが作業しようとして、権限が無いというのが現状？

DockerfileにUSER rails:railsという記述があり、これによって作業者がrailsになっているんだと思う。
この記述はRails7で自動的に追加されるもので、セキュリティ的にいい効果があるようだが、動くこと優先で削除してみる。

```
docker compose build
docker compose up
```
コンテナ起動中にエラー発生
```
/usr/local/bundle/ruby/3.3.0/gems/railties-7.1.3.2/lib/rails/application.rb:658:in `validate_secret_key_base': Missing `secret_key_base` for 'production' environment, set this string with `bin/rails credentials:edit` (ArgumentError)
web-1  | 
web-1  |         raise ArgumentError, "Missing `secret_key_base` for '#{Rails.env}' environment, set this string with `bin/rails credentials:edit`"
```
herokuへのデプロイ設定の時に変更していた本番環境の指定が原因なので、変更前に戻す。
Dockerfile
```
# Set production environment
ENV RAILS_ENV="development" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle"
```
```config/enviroments/production.rb
 # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
    #config.public_file_server.enabled = true
    
# Do not fall back to assets pipeline if a precompiled asset is missed.
    config.assets.compile = false
```
buildしてupすると無事にコンテナが起動した。

コンテナ内の所有者と作業者を再確認
```
root@b961a7cb106f:/rails/app# cd controllers/
root@b961a7cb106f:/rails/app/controllers# ls -la
total 16
drwxr-xr-x  3 root root 4096 Apr  3 04:26 .
drwxr-xr-x 11 root root 4096 Mar 31 09:18 ..
-rw-r--r--  1 root root   57 Apr  1 05:13 application_controller.rb
drwxr-xr-x  2 root root 4096 Apr  2 11:46 concerns
root@b961a7cb106f:/rails/app/controllers# whoami
root
```
ディレクトリの所有者と作業者、両方ともrootになっている。

gemfileにgemを追加してbundle installをしようとすると、gemが追加されない。
どういうことだろうか？
gemfileに追記した状態でbuildしなおしてみるとエラー
```
=> ERROR [web build 3/6] RUN bundle install &&     rm -rf ~/.bundle/ "/usr/  0.3s
------
 > [web build 3/6] RUN bundle install &&     rm -rf ~/.bundle/ "/usr/local/bundle"/ruby/*/cache "/usr/local/bundle"/ruby/*/bundler/gems/*/.git &&     bundle exec bootsnap precompile --gemfile:
0.264 The dependencies in your gemfile changed, but the lockfile can't be updated
0.264 because frozen mode is set
0.264 
0.264 You have added to the Gemfile:
0.264 * line-bot-api
0.264 
0.264 Run `bundle install` elsewhere and add the updated Gemfile to version control.
0.264 If this is a development machine, remove the Gemfile.lock freeze by running
0.264 `bundle config set frozen false`.
------
failed to solve: process "/bin/sh -c bundle install &&     rm -rf ~/.bundle/ \"${BUNDLE_PATH}\"/ruby/*/cache \"${BUNDLE_PATH}\"/ruby/*/bundler/gems/*/.git &&     bundle exec bootsnap precompile --gemfile" did not complete successfully: exit code: 16
```

frozenモードを削除しようとしてみる。
```
bundle config set frozen false
```
効果なし。

## 解決を諦める
参考にした記事と使用するrailsのバージョンが違ったことで、Rails7.1で自動生成されるDockerfileとのぶつかり合いが起こり、そこからよく分からなくなった。
今の状況からどこを修正すれば解決できるか、悩んでも思いつかなかった為、別ディレクトリで新しく環境を作り直そうと思います。


## 参考記事・サイト
https://zenn.dev/takuty/articles/6f7afabca322d1

https://zenn.dev/ryo_t/articles/9f8ea1f01b7fb4

https://zenn.dev/ken3pei/articles/1abbf7d974cf5d

https://zenn.dev/kingdom0927/articles/9cfd03eaa7d494

https://hisa-tech.site/docker-on-rails-stumble/

https://app-tatsujin.com/heroku-register-how-to-use/

https://devcenter.heroku.com/ja/articles/heroku-cli

https://zenn.dev/takuty/articles/b7aa6164fc85bb

https://qiita.com/kimascript/items/e6ab64e90fdab6eb7754

https://qiita.com/Uts/items/05ecbac62c9ca933278a

https://qiita.com/4EAE_Learner/items/1e0824a71cb4eb41780a

https://qiita.com/mkt1234/items/70cdb2cfcc9a051d3385