1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Laravel を使用していて Docker コンテナの起動に失敗する場合に composer install をするには docker-compose run を実行する

Posted at

TL;DR

コンテナを docker-compose up で起動せずに特定のコマンドを実行するには docker exec ではなく docker-compose run <container-name> <command> を実行する。

発生したエラー

Docker 内で Laravel を使用していたところ、Docker コンテナの起動に失敗する現象が発生しました。

$ docker-compose up
# 中略
my_container | 
my_container | Warning: require(/var/www/my-app/vendor/autoload.php): failed to open stream: No such file or directory in /var/www/my-app/artisan on line 18
my_container | 
my_container | Fatal error: require(): Failed opening required '/var/www/my-app/vendor/autoload.php' (include_path='.:/usr/local/lib/php') in /var/www/my-app/artisan on line 18
my_container exited with code 255

これは composer install が実行されていないために起きるエラーですが、この記事ではこちらを解決していきます。

前提知識:Docker とボリュームのマウントについて

Docker を使用する場合、基本的にソースコードはコンテナの外に配置するようになっています。そうすることで、コンテナの中に入ることなく vim や vscode などでソースコードを編集できます。さて、これをコンテナ内と結びつけるためには docker-compose にボリュームという項目を追加し、リポジトリをマウントすることができます。

docker-compose.yml
services:
  my_container:
    # 中略
    volumes:
      - type: bind
        source: ./my-app
        target: /var/www/my-app

さて、ここで重要なのは、docker-compose 内では(つまりコンテナの起動時には)ボリュームをマウントできる一方で、Dockerfile では(つまりビルド時には)ローカルのフォルダをマウントできないということです。

Dockerfileにボリュームのマウントを設定する際は, ホストマシンのディレクトリを指定することができません

となると、ビルド時に composer install を行い、その結果をローカルファイルに反映するにはひと工夫が必要になってしまいます。これを避けるために、今回は composer install はビルド時ではなくコンテナの初回起動時に行うことにします。

解決方法

まず、コンテナをビルドします。

docker-compose build my_container

次に、docker-compose up でコンテナを初回起動する前に docker-compose run コマンドを使用して composer install を行います。

docker-compose run composer install

最後に、コンテナを起動します。-d を付けるとバックグラウンドで起動します。

docker-compose up

これで無事コンテナを起動することができました。恐らく Python (pip install) や Node.js (npm installyarn install) でも同じ方法で対処できるかなと思います。

もっと良い方法はないか?

個人開発であれば上記で問題ないこともあります。ですが、よりよい環境が作れないか考えてみましょう。

改善案1. docker-compose up 時に必ず composer install が走るようにする

Dockerfile
COPY ./my-startup-script.sh /usr/local/bin/my-startup-script.sh
RUN chmod +x /usr/local/bin/my-startup-script.sh
CMD ["my-startup-script.sh"]
my-startup-script.sh
composer install
apache2-foreground # サーバーを起動

これはお手軽で便利ですね。(スクリプトファイルを用意する代わりに bash -c でも行けると思います。)

もちろん docker-compose.ymlcommand を追加しても構いません。

改善案2. vendor ディレクトリをマウントしない

vendor ディレクトリ(や、node_modules ディレクトリ等)をマウントしないようにすることで、誤ってホスト側で composer install してしまうようなミスを防ぐことができます。そもそもコンテナ内でしか役に立たないファイルなので、コンテナ外から見えないようにするのは確かに合理的に思えます。このテクニックは Volume Trick と呼ばれるそうです。

以下の記事のようにボリュームの中に別のボリュームを作成することで実現できるようです。

または、単に空のボリュームを指定するだけでよいそうです。

docker-compose.yml
services:
  my_container:
    # 中略
    volumes:
      - type: bind
        source: ./my-app
        target: /var/www/my-app
      - type: volume # ボリュームマウント
        target: /var/www/my-app/node_modules # 除外するディレクトリ

さらに良いことに、これによりビルド時の vendor/ ディレクトリが引き継がれるそうです。

例えば自前で Dockerfile を用意し、Dockerfile 内部で npm install などを実行していた場合、イメージ内の node_modules を削除してしまうことになります。

これを避けるためには以下のように名前付きボリュームを用意してあげることで node_modules が名前付きボリュームに格納され、ホストマシンに同期されることがなくなります

ただし、vscode を使用している場合は追加設定が必要になったりなど、自力でいくつかの問題を解決する必要があるかもしれません。その際は Volume Trick などで検索すると解決策が見つかるかもしれません。

オチ

そもそも docker compose up でコンテナがエラー終了してしまうのがおかしいのでは? という話なのですが、これは Dockerfile で CMD が誤って上書きされてしまっていたからというオチでした。上書きされる前のコマンドは Docker Hub 上の元のイメージを検索すると確認できました。

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?