前回こちらの記事で、flaskアプリケーションを作成し、ローカルで確認するところまでを行った。
今回は、FlaskアプリケーションをDocker化してCloud Runにデプロイする部分を記載。
FlaskアプリケーションのDocker化
こちらの記事がわかりやすかった
![](https://www.freecodecamp.org/news/content/images/2021/11/flask-docker.png)
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-user-contents.imgix.net/https%3A%2F%2Fcdn.qiita.com%2Fassets%2Fpublic%2Farticle-ogp-background-412672c5f0600ab9a64263b751f1bc81.png?ixlib=rb-4.0.0&w=1200&mark64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZ3PTk3MiZoPTM3OCZ0eHQ9RG9ja2VyJUUzJTgyJUE0JUUzJTgzJUExJUUzJTgzJUJDJUUzJTgyJUI4JUUzJTgxJUFFJUU2JTlDJUFDJUU4JUIzJUFBJnR4dC1hbGlnbj1sZWZ0JTJDdG9wJnR4dC1jb2xvcj0lMjMyMTIxMjEmdHh0LWZvbnQ9SGlyYWdpbm8lMjBTYW5zJTIwVzYmdHh0LXNpemU9NTYmcz03OTdmN2U1ZjZmMGU1OWZkZDYwYjM4N2Y2NWU0ZTE0Zg&mark-x=142&mark-y=57&blend64=aHR0cHM6Ly9xaWl0YS11c2VyLWNvbnRlbnRzLmltZ2l4Lm5ldC9-dGV4dD9peGxpYj1yYi00LjAuMCZoPTc2Jnc9NzcwJnR4dD0lNDBZb25hX1NvdSZ0eHQtY29sb3I9JTIzMjEyMTIxJnR4dC1mb250PUhpcmFnaW5vJTIwU2FucyUyMFc2JnR4dC1zaXplPTM2JnR4dC1hbGlnbj1sZWZ0JTJDdG9wJnM9NGJjYWUwOWI1NDM2MjE4MjY3N2VjODIxZDhmYzM3Zjg&blend-x=142&blend-y=486&blend-mode=normal&s=87821de7c773ae0a322a7756cd61ce62)
requirements.txt
flask
pillow
Dockerfileとrequirements.txtは、アプリのルートディレクトリに配置
![](https://kazulog.fun/wp-content/uploads/2023/12/image-25.png)
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/でアクセスすると、無事画面が開いた。
![](https://kazulog.fun/wp-content/uploads/2023/12/スクリーンショット-2023-12-09-10.27.47.png)
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アカウントでログインすると認証される。
![](https://kazulog.fun/wp-content/uploads/2023/12/image-26-1024x365.png)
Artifact RegistryでDockerリポジトリを作成
- Google Cloud Consoleにログインし、Artifact Registryページにアクセス。
- 「リポジトリの作成」を選択。
- リポジトリ名、リージョン、フォーマット(Docker)を指定。
![](https://kazulog.fun/wp-content/uploads/2023/12/image-27.png)
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/_static/cloud/images/social-icon-google-cloud-1200-630.png?hl=ja)
ちなみに、Container Registryは非推奨になった。Google Cloud でコンテナの管理を始めるには、Artifact Registry を使用する。
![](https://cloud.google.com/_static/cloud/images/social-icon-google-cloud-1200-630.png?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
![](https://kazulog.fun/wp-content/uploads/2023/12/image-28-1024x456.png)
コンテナイメージをCloud Runにデプロイ
![](https://cloud.google.com/_static/cloud/images/social-icon-google-cloud-1200-630.png?hl=ja)
Cloud Runで新規にサービスを作成し、「既存のコンテナ イメージから 1 つのリビジョンをデプロイする」を選択する。選択ボタンを押すと、artifact registoryが開くので、作成したリポジトリ名を選択する。Artifact RegistryでプッシュしたイメージのURL。
![](https://kazulog.fun/wp-content/uploads/2023/12/image-29-1024x308.png)
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/_static/cloud/images/social-icon-google-cloud-1200-630.png?hl=ja)
少し動線が分かりにくいが、「コンテナの編集」画面で、下にスクロールして「変数とシークレット」というタブをクリックする。すると、環境変数設定画面が表示されるので、変数を追加する。
![](https://kazulog.fun/wp-content/uploads/2023/12/スクリーンショット-2023-12-15-10.45.55.png)
デプロイしたらエラー: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)
今度は、無事成功!
![](https://kazulog.fun/wp-content/uploads/2023/12/image-30-1024x359.png)
めでたし、めでたし。
コメント