前回こちらの記事で、flaskアプリケーションを作成し、ローカルで確認するところまでを行った。
今回は、FlaskアプリケーションをDocker化してCloud Runにデプロイする部分を記載。
FlaskアプリケーションのDocker化
こちらの記事がわかりやすかった
https://www.freecodecamp.org/news/how-to-dockerize-a-flask-app/
Dockerfile の作成
ローカル環境でアプリケーションのルートディレクトリに Dockerfile
を作成する。 Dockerfile
は、Flaskアプリケーションを実行するために必要な環境を定義。
# 使用するPythonのベースイメージ
FROM python:3.9-slim
# 作業ディレクトリを設定
WORKDIR /app
# 依存関係ファイルをコンテナにコピー
COPY requirements.txt ./
# 依存関係のインストール
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションのコードをコピー
COPY . .
# アプリケーションを実行
# ホストOSからDockerコンテナ内のアプリケーションにアクセスするには、アプリケーションを 0.0.0.0 でリッスンさせる必要がある。0.0.0.0 はすべてのネットワークインターフェースでリッスンし、外部からのアクセスを受け付けるため、以下のように指定
CMD ["flask", "run", "--host=0.0.0.0"]
(備考)Dockerfileから作成されるDockerイメージの各レイヤに関して。上記Dockerfileの定義は、Dockerイメージの各レイヤを定義している。
https://qiita.com/Yona_Sou/items/107c14ed651080170044
requirements.txt
flask
pillow
Dockerfileとrequirements.txtは、アプリのルートディレクトリに配置
Dockerイメージのビルドとテスト
Dockerfile
があるディレクトリで、以下のコマンドを実行してDockerイメージをビルド。
これにより、アプリケーションがローカルのDocker環境で正しく動作することを確認できる。
# -t オプションは「tag」の略で、ビルドされるイメージに名前(タグ)を付けるために使用される
# app-name はそのイメージに付ける名前です。この名前は任意で、後でイメージを識別しやすくするために使います。例えば、アプリケーションの名前やバージョンなど、意味のある名前を付けるのが一般的
# ドット . は、Dockerfileがあるディレクトリ。この場合、現在の作業ディレクトリ(コマンドを実行しているディレクトリ)。Dockerfile が別のディレクトリにある場合、そのディレクトリのパスを指定する必要あり。
docker build -t your-app-name .
# ビルドしたイメージを基にDockerコンテナの実行。ローカルのポート8080(localhost:5000)にアクセスすると、dockerコンテナの内部でポート5000で動いているアプリケーションに接続できるようになる。
docker run -p 8080:5000 your-app-name
# Dockerコンテナを実行する際に環境変数をセットする場合
docker run -p 8080:5000 -e OPENAI_API_KEY='your_openai_api_key_here' your-app-name
ポートマッピングに関して
Dockerのポートマッピングを図解すると以下のようになります。この例では、docker run -p 8080:5000 your-app-name
コマンドの場合
┌─────────────────────────────────────────┐
│ ホストマシン │
│ │
│ ┌─────────────────────────────┐ │
│ │ Dockerコンテナ │ │
│ │ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Flaskアプリケーション │ │ │
│ │ │ (ポート5000) │ │ │
│ │ └──────────┬──────────┘ │ │
│ │ │ │ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ │ │
│ ┌───────────────┴───────────────┐ │
│ │ ポートマッピング 8080:5000 │ │
│ └───────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
- Dockerコンテナ: この中にFlaskアプリケーションがあり、5000番ポートで待機している。
- ポートマッピング 8080:5000: ホストマシンの8080番ポートからコンテナの5000番ポートにトラフィックが転送される。
- ホストマシン: ここでDockerコンテナが実行されており、外部からのリクエストを受け取る。8080番ポートにアクセスすると、そのリクエストはコンテナの5000番ポートに転送され、Flaskアプリケーションに到達する。
ホストマシンの8080番ポートとコンテナの5000番ポートは、Dockerのポートマッピング機能によって連携している。このため、http://localhost:8080
にブラウザからアクセスすると、実際にはコンテナ内のFlaskアプリケーションにリクエストが届く。
Dockerのport mappingに関して詳しくはこちら
~/dev/automate/mia-eye-test docker run -p 8080:5000 mia-eye-test
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://172.17.0.2:5000
Press CTRL+C to quit
172.17.0.1 - - [09/Dec/2023 01:24:55] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [09/Dec/2023 01:24:55] "GET /static/mia_face.png HTTP/1.1" 200 -
172.17.0.1 - - [09/Dec/2023 01:24:56] "GET /favicon.ico HTTP/1.1" 404 -
172.17.0.1 - - [09/Dec/2023 01:25:11] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [09/Dec/2023 01:25:11] "GET /static/mia_face.png HTTP/1.1" 200 -
http://localhost:8080/でアクセスすると、無事画面が開いた。
Google Cloud Runにアプリケーションをデプロイ
Flaskアプリケーションをそのまま gcloud run deploy
コマンドを使ってGoogle Cloud Runにデプロイすることはできない。Google Cloud Runはコンテナベースのサービスであり、アプリケーションはDockerコンテナとしてパッケージ化され、コンテナレジストリ(例:Artifact Registry)にアップロードされた後でなければデプロイできない。
Google CloudにCLIでログイン
Google Cloud SDK (gcloud コマンドラインツール) をインストールし、Google Cloudにログイン。
gcloud CLIのインストール(Mac)方法は、こちらの記事参照
ターミナルにgcloud CLI認証リンクが表示されるので、クリックしてgoogleアカウントでログインすると認証される。
Artifact RegistryでDockerリポジトリを作成
- Google Cloud Consoleにログインし、Artifact Registryページにアクセス。
- 「リポジトリの作成」を選択。
- リポジトリ名、リージョン、フォーマット(Docker)を指定。
Dockerイメージをローカルにビルドし、DockerでArtifact RegistryにPush
Dockerイメージをビルド
# IMAGE_URLは、コンテナ イメージへの参照(us-docker.pkg.dev/cloudrun/container/hello:latest など)に置き換える。下記に詳細
docker build . --tag IMAGE_URL
# [REGION]、[PROJECT-ID]、[REPOSITORY-NAME]、[IMAGE]、[TAG]は適宜置き換える。
docker build -t [REGION]-docker.pkg.dev/[PROJECT-ID]/[REPOSITORY-NAME]/[IMAGE]:[TAG] .
# 今回の場合
# app: イメージの名前
# latest: イメージのタグ。ビルドのバージョンやリリースを区別するために使う。
docker build -t asia-northeast1-docker.pkg.dev/mia-linebot-dev/mia-eye-test/mia-eye-test-image:tag1 .
このままpushしようとしても
denied: Permission "artifactregistry.repositories.uploadArtifacts" denied on resource "projects/mia-line-dev/locations/asia-northeast1/repositories/mia-eye-test" (or it may not exist)
とパーミッションのエラーが出る。
認証を構成
イメージを push または pull する前に、Google Cloud CLI を使用して Artifact Registry に対するリクエストを認証する。
https://cloud.google.com/artifact-registry/docs/docker/store-docker-container-images?hl=ja
ちなみに、Container Registryは非推奨になった。Google Cloud でコンテナの管理を始めるには、Artifact Registry を使用する。
https://cloud.google.com/container-registry/docs/access-control?hl=ja
# リージョン`asia-northeast1`のDockerリポジトリに対する認証を設定
$ gcloud auth configure-docker asia-northeast1-docker.pkg.dev
Adding credentials for: asia-northeast1-docker.pkg.dev
After update, the following will be written to your Docker config file located at [/Users/ky/.docker/config.json]:
{
"credHelpers": {
"asia-northeast1-docker.pkg.dev": "gcloud"
}
}
Do you want to continue (Y/n)? y
Docker configuration file updated.
これによりDocker構成が更新される。Google CloudプロジェクトのArtifact Registryに接続して、イメージのpushとpullができるようになる。
ビルドしたイメージをArtifact RegistryにPush
docker push [REGION]-docker.pkg.dev/[PROJECT-ID]/[REPOSITORY-NAME]/[IMAGE]:[TAG]
# 今回の場合
docker push asia-northeast1-docker.pkg.dev/mia-linebot-dev/mia-eye-test/mia-eye-test-image:tag1
無事うまくいくと、下記のようになる。
最初、認証もできたはずなのに、なぜかpushされなくて、よくよくみたらプロジェクトIDを間違えて記載していたorz
コンテナイメージをCloud Runにデプロイ
https://cloud.google.com/run/docs/deploying?hl=ja
Cloud Runで新規にサービスを作成し、「既存のコンテナ イメージから 1 つのリビジョンをデプロイする」を選択する。選択ボタンを押すと、artifact registoryが開くので、作成したリポジトリ名を選択する。Artifact RegistryでプッシュしたイメージのURL。
CLIで行う場合
gcloud run deploy [SERVICE-NAME] --image [REGION]-docker.pkg.dev/[PROJECT-ID]/[REPOSITORY-NAME]/[IMAGE]:[TAG] --platform managed --region [REGION]
[SERVICE-NAME]
はCloud Runでのサービス名、[REGION]
、[PROJECT-ID]
、[REPOSITORY-NAME]
、[IMAGE]
、[TAG]
はそれぞれ適宜置き換える。
環境変数の設定
環境変数を設定する場合は、こちらのドキュメント参照。
https://cloud.google.com/run/docs/configuring/environment-variables?hl=ja
少し動線が分かりにくいが、「コンテナの編集」画面で、下にスクロールして「変数とシークレット」というタブをクリックする。すると、環境変数設定画面が表示されるので、変数を追加する。
デプロイしたらエラー:port修正
デプロイしたところ、下記エラーが発生
Default STARTUP TCP probe failed 1 time consecutively for container "mia-eye-test-image-1" on port 8080. The instance was not started.
port 8080 をリッスンしていないのでデプロイできませんというエラー。
Docker の場合との違い
- Docker ローカル環境では、開発者が任意のポートを選択し、ポートマッピングを手動で設定することができる(例:
p 8080:5000
)。 - しかしCloud Run ではこのようなマッピングは行われない。代わりに、Cloud Run はアプリケーションに
PORT
環境変数を通じてポート番号を伝え、そのポートでのリッスンを期待する。
公式ドキュメント:https://cloud.google.com/run/docs/container-contract?hl=ja#port
app.pyを下記のように修正し、Dockerイメージを再ビルドし、Artifact Registry に再プッシュして、Cloud Run に再デプロイ。
import os
from flask import Flask
app = Flask(__name__)
# 他のルート定義 ...
if __name__ == '__main__':
# 環境変数 'PORT' からポート番号を取得。デフォルトは8080。
port = int(os.environ.get('PORT', 8080))
app.run(host='0.0.0.0', port=port)
今度は、無事成功!
めでたし、めでたし。
コメント