LoginSignup
0
1

More than 1 year has passed since last update.

rails(Docker)+nginxでPermission denied @ rb_io_reopen - /myapp/log/puma.stdout.log (Errno::EACCES)が発生する

Last updated at Posted at 2022-01-16

はじめに

Rails環境をDockerを利用して環境構築していたのですが、Nginxをつないだところエラーが発生してほかの記事があまり参考にならなかったため自分なりにまとめていきます。

主にWSL2でDockerを使っている方がこのエラーの対象になるかと思います。

問題

DockerでRailsとNginxの環境を用意して、Railsコンテナのユーザーをapp(rootではない)にdocker-compose.ymlで変更した状態でコンテナを起動すると以下のエラーが発生します。

rails        | * Listening on http://0.0.0.0:3000
rails        | * Listening on unix:///myapp/tmp/sockets/puma.sock
rails        | Use Ctrl-C to stop
rails        | bundler: failed to load command: puma (/usr/local/bundle/bin/puma)
rails        | /usr/local/bundle/gems/puma-5.5.2/lib/puma/runner.rb:123:in `reopen': Permission denied @ rb_io_reopen - /myapp/log/puma.stdout.log (Errno::EACCES)
rails        |  from /usr/local/bundle/gems/puma-5.5.2/lib/puma/runner.rb:123:in `redirect_io'
rails        |  from /usr/local/bundle/gems/puma-5.5.2/lib/puma/single.rb:56:in `run'
rails        |  from /usr/local/bundle/gems/puma-5.5.2/lib/puma/launcher.rb:181:in `run'
rails        |  from /usr/local/bundle/gems/puma-5.5.2/lib/puma/cli.rb:80:in `run'
rails        |  from /usr/local/bundle/gems/puma-5.5.2/bin/puma:10:in `<top (required)>'
rails        |  from /usr/local/bundle/bin/puma:25:in `load'
rails        |  from /usr/local/bundle/bin/puma:25:in `<top (required)>'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/cli/exec.rb:58:in `load'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/cli/exec.rb:58:in `kernel_load'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/cli/exec.rb:23:in `run'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/cli.rb:478:in `exec'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/cli.rb:31:in `dispatch'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/cli.rb:25:in `start'
rails        |  from /usr/local/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/libexec/bundle:49:in `block in <top (required)>'
rails        |  from /usr/local/lib/ruby/3.0.0/bundler/friendly_errors.rb:103:in `with_friendly_errors'
rails        |  from /usr/local/lib/ruby/gems/3.0.0/gems/bundler-2.2.32/libexec/bundle:37:in `<top (required)>'
rails        |  from /usr/local/bin/bundle:23:in `load'
rails        |  from /usr/local/bin/bundle:23:in `<main>'
rails exited with code 1

Permission denied @ rb_io_reopen - /myapp/log/puma.stdout.log (Errno::EACCES)ということでpuma.stdout.logが権限的にみれないよとエラーを出しています。

調査

これはpumaのログがコンテナ内でroot権限で作成されることが原因です。

私の作成したdocker-compose.ymlは以下のようになっています。

docker-compose.yml
version: "3.9"
services:
  rails:
    build: .
    container_name: rails
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/myapp
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
      - log-data:/myapp/log
    env_file:
      - .env
    depends_on:
      - db
    environment:
      WEBPACKER_DEV_SERVER_HOST: webpacker
    user: app

  webpacker:
    build: .
    volumes:
      - .:/myapp
    command: ./bin/webpack-dev-server
    environment:
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    ports:
      - "3035:3035"

  db:
    image: mysql:8.0.27
    container_name: db
    environment:
      TZ: Asia/Tokyo
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    ports:
      - "3306:3306"
    volumes:
      - db:/var/lib/mysql

  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
    ports:
      - 80:80
    depends_on:
      - rails

volumes:
  db:
    driver: local
  bundle:
    driver: local
  public-data:
  tmp-data:
  log-data:

ここで注目してほしいのはrailsコンテナのuser: appという箇所でユーザーを指定しているところです。これでコンテナ内でrails gなどでファイル生成してもappユーザー(uid=1000)で作成されるため、WSL2のユーザーと権限が同じなので編集ができるようになります。この設定をしないとrootユーザーでファイルが作成されるためWSL2ユーザーでは編集ができません。

しかし、appを指定したことでpumaログがroot権限で作成されているため編集できずエラーとなっていました。

ここで考えられるのは以下の方法です。

  • WSL2をrootでログインするように設定する
  • コンテナ内の参照するフォルダ(と中身のファイル)の権限をappユーザーが開けるように変更する

今回はコンテナ内の/{workdir}/log(ここではmyapp/log)の権限を変更して対応していきます。

まずは実際に権限がrootになっているのか確認します。
いまのままではコンテナが落ちてしまうのでdocker-compose.ymlのrailsコンテナのコマンドを以下に変更します。

docker-compose.yml
command: sh -c "while :; do sleep 10; done" && bundle exec puma -C config/puma.rb

コンテナの中に入って以下のコマンドを打ちます。

# コンテナの起動
# $ docker-compose up
# コンテナに入る
# $ docker exec -it rails sh

# 権限の確認
$ ls -la
# rootになっている
drwxr-xrwx    2 root     root          4096 Jan 16 08:49 log

この権限をappに変更できれば良さそうです。

こちらの記事によると、Volumesで設定するとrootになってしまうようです。
ここで2つの回避策を考えましたので紹介します。

回避策1 コンテナの中で権限を変更する

コンテナの中で直接権限を変更します。
注意としてはrootでコンテナに入る必要があるので、docker-compose.ymlのuserをコメントにします。

rootであれば権限問題が起きないのでコンテナは立ち上がるかと思います。

# 別ターミナルを開く
# コンテナに入る
$ docker exec -it rails sh
# 権限を変更する
$ chown app:app -R /myapp/log

chownでエラーが出る場合はrootユーザーになっていないのが原因かと思いますのでid -u -nコマンドで確認してください。

一度変更すれば以降は変更された状態になります。
しかし、ボリュームを削除したり、gitからダウンロードした場合にrootでアクセスしてdocker-compose.ymlを編集してuser:appに変更するという手間も発生するので微妙かなとは思いました。

回避策2 Nginxで権限変更のシェルを実行する

1の欠点を補うためにNginxコンテナを起動するときにシェルを走らせることで回避することにしました。
まず準備としてdocker-compose.ymlのNginxのボリュームを変更します。

docker-compose.yml
  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/myapp/public
      - tmp-data:/myapp/tmp
      - log-data:/myapp/log
    ports:
      - 80:80
    depends_on:
      - rails

myapp/logをvolumesに設定しました。
そして、Dockerfileentrypoint.shを設定できるようにします。

FROM nginx:alpine

# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*
RUN adduser -D app -u ${UID:-1000}

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/conf.d/myapp.conf

# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf

次にnginxのDockerfileと同じディレクトリにentrypoint.shを作成します。

entrypoint.sh
#!/bin/sh
set -e
chown app:app -R /myapp/log
exec "$@"

ここまでの設定を行うことで、Nginxコンテナが立ち上がったときにentrypoint.shが実行され、volumesで設定されているmyapp/logの権限をappに変更します。また同じユーザーをDockerfileで作成することも忘れないようにしてください。

nginx自体はrootユーザーで起動するため権限変更chownが利用でき変更が可能です。
railsコンテナで同じように権限を変更しようとなるとrootでアクセスする必要があるため今回はnginxで権限を変更するように設定しました。

回避策3 マウントに権限を付与する

マウントで権限を与える方法が分かりました。
tmpfsマウントを使うと権限を与えることが可能になります。
今回はコンテナ間で共有しないのでこれで良さそうです。

docker-compose.yml
tmpfs:
  - /myapp/log:exec,mode=777,uid=1000,gid=1000

解決策

解決策はいたってシンプルでDockerfileでユーザーを追加して、ディレクトリコピーの時にchownすればOKでした。

FROM ruby:alpine3.13

ARG UID

RUN adduser -D app -u ${UID:-1000} && \ # user追加
      apk update \
      && apk add --no-cache gcc make libc-dev g++ mariadb-dev tzdata nodejs~=14 yarn

WORKDIR /myapp
COPY Gemfile .
COPY Gemfile.lock .
RUN bundle install
COPY --chown=app:app . /myapp # 権限を付与してコピー
RUN chown app:app -R /myapp/node_modules/.bin
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
# EXPOSE 3000
# CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]

USER app # ユーザー変更
RUN mkdir -p tmp/sockets # フォルダ作成はユーザー設定してから
RUN mkdir -p tmp/pids

おわりに

権限問題が複雑に絡まっており解決するまで時間がかかりました。
調べてもよくわからず自分なりに工夫をして解決することができました。

参考

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1