TechFULの中の人

TechFULスタッフ・エンジニアによる技術ブログ。IT関連のことやTechFULコーディングバトルの超難問の深掘り・解説などを毎週更新

【初心者向け】Python(Flask) で作成したWeb APIをGCEにデプロイする

こんにちは。
444株式会社エンジニアの河端です。

本記事ではローカル環境で動作するアプリケーションを、
GCEにデプロイする方法をなるべく簡単に説明します。

本記事について

目的
- ローカル環境で動作するアプリケーションを、GCEのIPアドレスにアクセスすることで動作するようにする

本記事の対象者
- プログラムは書けるが、サーバーにデプロイする方法がわからない人 - ローカル環境で動作したプログラムを、実際のサーバーにデプロイしたい人

事前準備
- GCPのアカウント登録

注意
- 最低限の仕組み説明のため、開発用サーバーを実サーバー上でそのまま立てる等、実開発としては適切ではない手順があります

手順1. ローカル環境で動くアプリケーションを作成

まずは、ローカル環境 (自身のPC) 上で動くコードを書いてみましょう。 今回は以下の構成で環境作成します。

早速ターミナルを開き、ローカル環境を作成しましょう。

# virtualenv をインストール
~/test_app
$ sudo pip install virtualenv

# virtualenvで、myvenvという仮想環境を作成
~/test_app
$ virtualenv myvenv

# 作成した仮想環境に入ります
~/test_app
$ source myvenv/bin/activate

# flask を仮想環境にインストール
~/test_app
$ (myvenv) pip install flask

# パッケージをコードベースで管理できるようにrequirements.txtに書き出す
~/test_app
$ (myvenv) pip freeze > requirements.txt

作成した環境に、flaskで作成したアプリケーションを置きます。

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, world!'

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5000)

ターミナルに戻り、上記アプリケーションを開発用サーバーで起動します。

$ (myvenv) python main.py
 * Serving Flask app 'main' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

 

http://127.0.0.1:5000/ にアクセスすると、Hello world! と表示されました! f:id:techful-444:20210805181138p:plain

ここまでできたら、gitにコードをcommitしておきましょう。 GitHubGitLab 等のリモートリポジトリにpushするとよいでしょう。

手順2. サーバーを準備

次に、サーバーを準備します。 GCPにログインし、GCEのインスタンスを作成します。 (以降はサーバー => インスタンス と表現します)

  1. 左上のメニューから、"Compute Engin" > "VMインスタンス" と選択 f:id:techful-444:20210805184551p:plain

  2. "インスタンス作成" ボタンを押下

  3. 下記設定で、インスタンスを作成

  4. GCEインスタンス一覧から"ssh" というボタンを押下し、ブラウザ上でインスタンスsshログインする f:id:techful-444:20210805185832p:plain

作成されたインスタンスの外部IPをメモしておきましょう。
この外部IPは、世界でこのインスタンスだけが保持しているIPアドレスです。
最終的にここにアクセスすることで、"Hello, world!" と表示されるようにします。

インスタンスにログインできましたか?
ログインできたら準備は完了です。

手順3. ローカル環境で動作した環境を試す

さてここからが本番です。
いよいよ先程作成したアプリケーションをサーバー上にデプロイしていきましょう。

# お作法。update & upgrade
root@blog-instance:/home/user# sudo apt update
root@blog-instance:/home/user# sudo apt upgrade


# pipがないため、インストール
root@blog-instance:/home/user# apt install python3-pip


# 手順1で作成したアプリケーションをインスタンス内に持ってくる
root@blog-instance:/home/user# git pull [自分のリポジトリ] 


# 手順1と同様に環境をactivate
root@blog-instance:/home/user# cd test_app
root@blog-instance:/home/user/test_app# source myvenv/bin/activate

# 環境にパッケージをインストール
root@blog-instance:/home/user/test_app# pip install -r requirements.txt

# 開発用サーバーを起動
(myvenv) root@blog-instance:/home/user/test_app# python main.py
 * Serving Flask app 'main' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

これでローカル環境と同じ環境を作成し、開発用サーバーを起動できました!
早速、先程メモした外部IPアドレスにアクセスしましょう!!

f:id:techful-444:20210805194832p:plain

....あれ?

手順4. 外部IPアドレスへのアクセスで動作するようにする

なぜ動作しないのでしょうか。 それは、"インスタンス(サーバー)が、受けとったリクエストをどこで処理をすればよいか分かっていないため" です。

f:id:techful-444:20210806103956p:plain

簡単に説明画像を作ってみました。
今は画像のように、リクエストに対しどのような処理を行うかが定義されていない状態になっています。
そのため、そのリクエストをちゃんと処理できないよ~ というエラーが帰ってきているわけですね。
では、リクエストに対しどの処理を行えばよいかを紐付けてあげましょう。

今回は nginx を導入し、リクエストの紐付けを行います。

(myvenv) root@blog-instance:/home/user/test_app# sudo apt update
(myvenv) root@blog-instance:/home/user/test_app# sudo apt install nginx

(myvenv) root@blog-instance:/home/user/test_app# sudo systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2021-08-06 01:54:58 UTC; 2min 12s ago
       Docs: man:nginx(8)
    Process: 31618 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 31619 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 31620 (nginx)
      Tasks: 3 (limit: 1164)
     Memory: 10.0M
     CGroup: /system.slice/nginx.service
             ├─31620 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─31621 nginx: worker process
             └─31622 nginx: worker process

nginxが起動しているのを確認できたら、httpで先程の外部IPアドレスにアクセスしましょう。

f:id:techful-444:20210806105911p:plain

"Welcome to nginx!" と表示されていたら成功です。
これは、外部IPアドレスにhttpリクエストをした際に、
"Welcome to nginx!" と表示されるアプリケーションが動作した と捉えてください。

この動作するアプリケーションを、先程開発したものに差し替えます。

~ (略) ~
         include /etc/nginx/conf.d/*.conf;
#       include /etc/nginx/sites-enabled/*; # ここをコメントアウト or 削除する

~ (略) ~

/etc/nginx/conf.d/ ディレクトリに、test_app.conf というファイルを作成し、以下のように記載します。 リクエストが80ポート (http) に来たとき、http://127.0.0.1:5000で処理を行う、という設定をしています。

server {
    listen       80; 
    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_redirect default;
    }
}

ここまでできたら、設定を反映するためにnginxを再起動します。 そして、アプリケーションを起動しましょう。

(myvenv) root@blog-instance:/home/user/test_app# systemctl restart nginx
(myvenv) root@blog-instance:/home/user/test_app# python main.py
 * Serving Flask app 'main' (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

アプリケーションを起動できたら、外部IPアドレスにhttpリクエストでアクセスしましょう。

f:id:techful-444:20210806113051p:plain

Hello, world! と表示されました! 成功です。

httpリクエスト (80番ポート) に来たリクエストを、インスタンス内部で正常に処理することができました。

f:id:techful-444:20210806113805p:plain

終わりに

Web API をGCE上のインスタンスにデプロイし、動作させる方法をざっくり説明しました。
この記事を読んで、実際のサーバー上へのデプロイの大筋の流れを把握してもらえていたら嬉しいです。