miuuuu’s blog

ひよこからにわとりになりたいエンジニアによる日々の記録

PythonアプリケーションのDocker環境上でvirtualenvを有効化する

Docker環境上でvirtualenvを使う際の有効化についてのメモ。

今回使ったアプリケーションでは、Docker環境上でvirtualenvを使用するとライブラリのインポートが上手くいったのでvirtualenvを使用しました。 そもそもDocker環境上でvirtualenvを使う必要があるのか?という話はありますが、軽く調べたところ必要であるという意見も必要ないという意見もあったので、また別の機会に色々実験して調べてみたいと思います。

NGパターン

まずは自分が初めに失敗したNGパターンから。

virtualenv環境をイメージ作成時に有効にするだけだとコンテナ起動時は無効の状態になるので、virtualenv環境で起動したいときは、RUNだけでなくCMDでもactivateを適用して有効化する必要がある。

# activate
RUN virtualenv env && . env/bin/activate && pip install -r requirements.txt

# deactivate
CMD ./dev_app.sh

また、次のようにRUNを複数に分けてしまうとactivateが次のRUNで引き継がれないので、virtualenv環境で行いたい処理は && で続けて書く必要がある。

# activate
RUN virtualenv env && . env/bin/activate

# deactivate
RUN pip install -r requirements.txt

# activate
CMD . env/bin/activate && ./dev_app.sh

OKパターン

このようにすれば、有効化されたvirtualenv環境にライブラリをインストールでき、Dockerコンテナ上のvirtualenv環境でアプリケーションを起動できる。

# activate
RUN virtualenv env && . env/bin/activate && pip install -r requirements.txt

# activate
CMD . env/bin/activate && ./dev_app.sh

ちなみに上記の例はUbuntuのOSを例にしており、sourceコマンドは使えないので代わりに" . "を使用している。

Linuxコマンドの&&と&の優先順位を知らず一瞬ハマった話

問題

このようなディレクトリ構造で現在 main/ にいます。

app
  - main
    - src

次のコマンドを実行したとき、2個目のpwdでは何が出力されるでしょう??

cd src && pwd & cd .. && pwd

答え

答えは、

app

でした〜

(自分は最初は app/main だと思ってハマりました)

解説

&& は & より優先されるので、上のコマンドは下記のように解釈されて実行されます。

(cd src && pwd) & (cd .. && pwd)

つまり、cd src && pwd がまとめてバックグラウンドで実行されてたわけですね。

GAEのローカル開発サーバー起動時にプロンプトを無効化する

DockerでGAEのローカル開発サーバーをdev_appserverで起動する際、下記のような入力が必要な場合にエラーが発生してしまうので、このようなプロンプトを無効化したい。

For the latest release notes, please visit:
  https://dl.google.com/dl/cloudsdk/release/RELEASE_NOTES
Do you want to continue (Y/n)? 

調べたこと

gcloudコマンドには--quietフラグがあるようだ。

Disable all interactive prompts when running gcloud commands. If input is required, defaults will be used, or an error will be raised. Overrides the default core/disable_prompts property value for this command invocation. This is equivalent to setting the environment variable CLOUDSDK_CORE_DISABLE_PROMPTS to 1.

https://cloud.google.com/sdk/gcloud/reference

だが、dev_appserver.py --quiet は機能しない。

解決方法

環境変数 CLOUDSDK_CORE_DISABLE_PROMPTS を定義する方法だと無効化できた。

export CLOUDSDK_CORE_DISABLE_PROMPTS=1

Python2+GAEスタンダートのローカル開発サーバーで IOError: [Errno 13] file not accessible: '/dev/null'

Python2+GAEのローカル開発サーバーをdev_appserver.pyで起動しようとしたところ、下記のエラーがでた。

File "/usr/lib/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/python/runtime/stubs.py", line 303, in __init__
    raise IOError(errno.EACCES, 'file not accessible', filename)
IOError: [Errno 13] file not accessible: '/dev/null'

原因

結論から言うと、App Engine Python 2.7 ランタイムで使用できる組み込みライブラリを、それ以外のライブラリを保存するプロジェクト内のディレクトリ(lib/)に入れてしまっていたことが原因だった。

App Engine Python 2.7 ランタイムに組み込まれているライブラリ

App Engine Python 2.7ランタイムに組み込まれているライブラリは、 app.yaml 内の libraries で指定するだけで使用できる。

app.yaml

libraries:
- name: ssl
  version: latest
- name: PIL
  version: latest

ローカル開発用サーバーで使用する前にローカルにインストールする必要があるライブラリ

ただし、App Engine Python 2.7ランタイムで使用できる組み込みライブラリの中でも、ローカル開発用サーバーで使用する前にローカルにインストールする必要があるライブラリもあるので、その場合は下記のようにローカルにインストールする。

pip install -r test_requirements.txt

App Engine Python 2.7 ランタイムに組み込まれていないライブラリ

上記以外のライブラリは、プロジェクト配下のディレクトリ(例:lib/)にインストールする必要がある。

pip install -t lib -r requirements.txt

逆に、App Engine Python 2.7 ランタイムに組み込まれているライブラリはプロジェクト配下のディレクトリ(例:lib/)に入れてはいけない。

GAEスタンダート環境のローカル開発サーバーをDockerで動かすときのDockerfileの置き場

DockerでPython+GAEスタンダートの開発環境を構築していた時に、下記のようなエラーに遭遇した。

File "/usr/lib/google-cloud-sdk/platform/google_appengine/google/appengine/tools/devappserver2/application_configuration.py", line 180, in __init__
When there is a Dockerfile in the current directory, the only supported runtime is runtime: custom.  Please switch to runtime: custom.  The devappserver does not actually use your Dockerfile, so please use either the --runtime flag to specify the runtime you want or use the --custom_entrypoint flag to describe how to start your application.

app.yamlのruntimeをpython27にしていたので、もしかしてcustomでしかできない??

と思って色々調べたりしていたが全然出てこない。

原因

今回はGAEスタンダード環境だが、フレキシブル環境のドキュメントにこう記載があった。

Dockerfile のファイルが app.yaml ファイルと同じディレクトリにある必要があります。

Dockerfile と app.yaml を同じディレクトリに置いていたので、おそらくフレキシブル環境と認識されてしまうのが原因かと思われる。

解決方法

Dockerfileをapp.yamlと同じ階層のココに置いていたが、

app
  - src
  - app.yaml
  - Dockerfile

1階層下に移動してみたら解決した。

app
  - docker
      - Dockerfile
  - src
  - app.yaml

結論

GAEスタンダート環境のローカル開発サーバーをDockerで動かす場合は、Dockerfile を app.yaml と同じカレントディレクトリに置いてはいけない。

(よく見たらエラー文ほぼそのままだった。。。)

Dockerで上の階層はCOPYできないので、ビルドコンテキストを上の階層にする

ディレクトリ構成がこういう場合のCOPYとビルドコンテキストについて。

app
  - docker
      - Dockerfile
  - src

※ ビルドコンテキストとは、docker buildコマンドを実行したときのカレントディレクトリのこと

NGパターン

下記のように、Dockerfileがあるディレクトリで実行して上の階層をCOPYしようとするとエラーになる。

Dockerfile

COPY ../

ビルドコマンド

cd app/docker
docker build -t [イメージ名:タグ] .

エラー

COPY failed: Forbidden path outside the build context: ../

OKパターン

上の階層はCOPYできないため、このようにCOPYしたいディレクトリで実行しDockerfileのパスを指定すれば正しく動作する。

Dockerfile

COPY .

ビルドコマンドの場合

cd app
docker build -t [イメージ名:タグ] -f docker/Dockerfile .

docker-compose.yamlの場合

build:
  context: ./
  dockerfile: ./docker/Dockerfile

DockerfileのCMD、docker-compose.yamlのcommandに、複数コマンドを複数行で書く

dockerコンテナを起動するときのコマンドを、複数コマンドまたは複数行で書く書き方のメモ。

複数コマンドを複数行で書く

Dockerfile

CMD sh -c “hogehogehoge && fugafugafuga && \
    piyopiyopiyopiyopiyopiyopiyopiyopiyo"

docker-compose.yaml

Command: > 
    sh -c "hogehogehoge && fugafugafuga &&
    piyopiyopiyopiyopiyopiyopiyopiyopiyo"

まとめ

  • DockerfileのCMDもdocker-compose.yamlのcommandも、1ファイル一回しか書けない
  • 複数コマンドを実行したいときは、sh -c “hoge && fuga" のようにする
  • Dockerfileに複数行で書きたい場合は、(CMDに限らず) 行末に \ をつける
  • docker-compose.yamlに複数行で書きたい場合は、Command: > として行末には何もつけず改行する