こんにちは。クレスコ・デジタルテクノロジーズのN.Oです。
業務ではネットワーク構築をメインに行っていますが、クラウドにも興味があり、AZ-104取得に向けた勉強も兼ねてプライベートでAzureを触ってみました。クラウド環境の構築やアプリ開発は全くの初心者でしたが、思いのほか簡単にWebアプリのインターネット公開が出来ました!

  • Webアプリの開発環境

まずWebアプリですが、今回はデプロイにフォーカスするため、簡単な動的ページを生成するシンプルなものを、学生時代に学んだPythonで作成しました。
次に開発環境ですが、私は普段デスクトップPC、ノートPC、iPad(開発サーバーへのVNC)を活用し、どこからでもプログラミングを行えるようにしています。複数の端末でソースコードを共有・管理するのであれば、GitHubを使うと便利ですが、3台の端末それぞれにPythonをインストールしてプログラミングを行うと、次のような問題が発生し、Webアプリが期待通りに動かない可能性があります。

    • 環境変数の不一致
    • Pythonのインストール方法、インストール先の不一致
    • Pythonのパッケージ不足やバージョンの不一致

よって、開発環境を簡単に配布するためDockerを活用しました。DockerであればイメージやDockerfileで簡単に開発ツールやパッケージをそろえることができます。
ここまでの構成をまとめると次図のようになります。

  • ローカル環境でのテスト

まずは、ローカルでWebアプリを作成してテストしてみます。
dockerfileとdocker-compose.yamlは次の通りです。

dockerfile :

FROM Python:3.9.8
WORKDIR /opt/build
ADD requirements.txt /opt/build/
RUN Python -m pip install --upgrade pip
RUN pip install -r requirements.txt
RUN pip install Flask==2.1.0
RUN pip install MarkupSafe==2.0.1

docker-compose.yaml:

version: "3.9.8"
services:
app:
build: .
ports:
- "80:80"
container_name: CDT-blog
volumes:
- ./:/opt/build
command: flask run --host 0.0.0.0 --port 80

Webアプリのソースコード等については後述します。

この内容で

docker-compose up

を実行し、ブラウザからlocalhostにアクセスすると、作成したWebアプリが表示されます。

  • Azure App Serviceで継続的デプロイ

ローカルでのテスト後、いよいよ作成したWebアプリをAzureにデプロイしますが、次の方法が考えられます。

    1. Webアプリのみをデプロイする
    2. コンテナをデプロイする
      ①Container Registryからデプロイする
      ②GitHub Actionsでデプロイする

「1. Webアプリのみをデプロイする」ではAzure App Serviceで定められている言語のWebアプリしかデプロイできません。一方、「2. コンテナをデプロイする」の場合、言語の指定はなく、コンテナ内でWebアプリが起動できればOKです。ただし、Webアプリの起動に失敗した場合はコンテナのデプロイに関する問題なのか、Webアプリの問題なのか切り分けが必要になります。

PythonはAzure App Serviceで定められている言語であるため、今回は「1. Webアプリのみをデプロイする」を試してみました。

デプロイした際のパラメータは次の通りです。

Azure App Serviceは、特定条件下であれば「無料」で利用できます。手軽にWebアプリをデプロイできるのは嬉しいですね。
Azure App Serviceのインスタンスを作成したら、継続的デプロイの設定をします。今回はGitHub Actionsを利用します。GitHub内のリポジトリに変更があれば自動的にデプロイが開始されるので便利です。

継続的デプロイの設定を保存すると自動でデプロイが開始されます。
デプロイに成功するとログに「成功」と表示されます。

デプロイしたWebアプリにアクセスすると、期待通りに表示されました!

  • 今回作成したWebアプリとFlaskの仕様について

今回はPythonのWebアプリ用フレームワークを活用してアプリを作成しました。フレームワークには様々な種類がありますが、有名なものでいうとDjangoかFlaskでしょうか。Djangoは多機能なフルスタック・フレームワークですが、今回はそこまでの機能を必要としないため、シンプルな機能を備えているFlaskを利用しました。

作成したソースコード(抜粋)は次の通りです。

app.py :

from flask import Flask, render_template, request, redirect, url_for, send_from_directory, Response
app = Flask(__name__)
from datetime import datetime
site_title = 'Test Blog'
date = datetime.now()
@app.route('/')
def index():
return render_template('index.html',site_title = site_title, year = date.year)
@app.route('/work/page1/')
def work_c1():
return render_template('page',page_title = 'page1',site_title = site_title, year = date.year)
@app.route('/work/page3/')
def work_c3():
return render_template('hogehoge', page_title = 'page3',site_title = site_title, year = date.year)
@app.errorhandler(404) # 404エラーが発生した場合の処理
def error_404(error):
return render_template('error/error.html', error_code = '404', site_title = site_title, year = date.year), 404
@app.errorhandler(500) # 500エラーが発生した場合の処理
def error_500(error):
return render_template('error/error.html', error_code = '500', site_title = site_title, year = date.year), 500
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80, debug=True)

PythonでWebアプリを作成するメリットの一つは「Pythonで学んだことをそのまま生かせる」点だと思います。例えば、ソースコード7行目の「date = datetime.now()」は
Pythonのdatetimeライブラリをそのまま利用しています。既にPythonを学んでいる方にとっては言語を学びなおす必要がないため、Webアプリ作成のハードルが低くなります。

ソースコード中の”render_template”は作成済みのページを表示するモジュールです。ここでは、「index.html」と「page」、「error.html」が作成済みページとして存在するものとします。(「hogehoge」は意図的に作成していません。理由は後述します。)
デコレータ関数”@app.errorhandler()”内の render_templateモジュールは、末尾にステータスコードを記載するとHTTPステータスラインにステータスコードを出力します。つまり、ステータスコードを記載しないとエラーページがステータス200(OK)としてクライアントに認識され、検索エンジンにエラーページが載るなどの事故が発生するので注意です!

次に、作成済みページの一例として「page」のソースコードを示します。

page :

{% include "header.html" %}
{% if page_title == 'page1' %} {% include "contents/page1/" %} {% elif page_title == 'page2' %}
This page is {{page_title}} .
{% else %}
No specified page.
{%endif%}
{% include "footer.html" %}

{{}}内の変数には、app.pyのrender_templateモジュールで渡されたパラメータが入ります。

また、{% include “ページ名”%}で作成済みページの挿入ができたり、{%if ~%}のように制御構文を挿入できたりします。

ちなみにCSSも内製ですが、詳細は割愛します。

app.pyのソースコードでも触れましたが、「hogehoge」というページを意図的に作成しませんでした。このページにアクセスしようとした際にどのような結果になるのでしょうか?
実際にアクセスすると次のようになります。

存在しないページを読み込もうとしたので内部エラーが発生したようです。この500エラーのページを表示させるために「hogehoge」というページを作成しませんでした。

では、その500エラーのページは本当に500エラーなのでしょうか?
開発者ツールで確認してみると確かに「状態」が500になっており問題ないようです。

開発者ツールの画面を見ると、CSSが読み込まれているのが分かります。このCSSは「/static/css/」に配置されていますが、「/static/」配下にあるファイルは全て閲覧可能なのでしょうか? 今回は「secret.txt」というファイルを作成し、「/static/」に配置してみました。アクセスすると次のようになります。

ファイルの中身が表示されました。デフォルトでは「/static」配下のコンテンツはインターネットからアクセス可能であるため注意が必要です。

では、今回使用した作成済みページにはインターネットからアクセスできるのでしょうか?
CSSが「/static」配下に配置されていた点から推測すると、作成済みページは「/templates」にありそうです。では確認してみましょう。

アクセスできませんでした。「/templates」配下のコンテンツにはインターネットからアクセスできないようです。

このように、Flaskには「インターネットからアクセス可能な場所」「インターネットからアクセスできない場所」が存在します。コンテナのデプロイであれば権限設定も容易ですが、GitHubからのデプロイの場合は権限設定に注意が必要です。

  • さいごに

今回、入社1年目の私が業務では経験のないサービスや技術を扱いましたが、Webアプリの作成からデプロイまで、思いのほか簡単に行うことができました。プログラミング学習やESP32の開発のために学んでいたPythonの汎用性の高さを改めて実感したほか、アジャイル開発で重要となるCI/CDを体験することもできました。また、クラウドサービスでは、課金も気になるところですが、今回のように無料で活用できるサービスがあるほか、アラート設定により適切にコントロールすることもできるので、興味ある方は是非チャレンジしてみてほしいです。

最後までお読みいただきありがとうございました。