Pythonアプリケーションをビルドしてデプロイするには?

Back4app パイソンカバー

Pythonはフリーでオープンソースのプログラミング言語であり、多くの用途がある。Pythonは1991年にGuido Van Rossumによって作成され、それ以来最も人気のあるプログラミング言語の1つに発展してきた。

この記事では、Pythonについて、そのメリット、デメリット、ユースケース、デプロイオプションについてお話します。その上で、シンプルなPythonアプリケーションを構築し、Docker化し、Back4app Containersにデプロイする方法を紹介します。

Pythonプロジェクトをビルドしてデプロイする方法については、このまま読み進めてください。

パイソンの概要

Pythonは高レベルの汎用プログラミング言語である。インタプリタ型で動的型付けが可能で、プログラミングだけでなくスクリプトにも使用できる。開発者は通常、Pythonの豊富な標準ライブラリにより、Pythonを「バッテリーが付属している」言語と表現する。

Pythonは1991年に初めて登場し、以来、そのシンプルで読みやすい構文により、最も人気があり、愛されているプログラミング言語のひとつとなっている。Pythonの主な設計原則は、ティム・ピーターズ著『The Zen of Python』でカバーされている。

Pythonには巨大な開発者コミュニティとすぐに使えるパッケージがある。最近では、Pythonはサーバー管理、科学計算、機械学習、RESTful APIの構築など、ほとんどあらゆる場所で使われている。Pythonは、現代の開発者の必須スキルの1つと考えられる。

Pythonの利点

Pythonの利点

使いやすい

Pythonは最も使いやすく、習得しやすい言語のひとつです。Pythonの主な設計思想はシンプルさとコードの読みやすさを重視しているため、学習曲線はほとんど存在しない。Pythonは基本的なプログラミングの概念を学ぶのに最適なツールであるため、教育現場でも好まれている。

迅速な開発

Pythonのもうひとつの大きな特長は、迅速な開発が可能なことだ。Pythonはプロトタイピングに最適な言語であり、素早く仕事を終わらせることができる。Pythonを使うことで、複雑な構文やメモリ割り当てなどを気にする必要がなくなり、その代わりにアプリに集中することができます。

多用途

Pythonは最も汎用性の高いプログラミング言語の一つである。幅広い業界や分野で使用されている。Pythonが使われている分野には、会計、科学研究、データサイエンス、ウェブ開発、ゲーム開発、オートメーションなどがあります。

ポータブル

Pythonは(Javaのような)インタプリタ型言語であり、(C++のような)コンパイル型言語ではないため、移植性が高い。これは、あるプラットフォームで書かれたコードを別のプラットフォームに簡単に移行できることを意味する。そのためPythonは、Windows、macOS、Linux、Androidなど複数のプラットフォームをサポートする必要がある開発者に人気のある選択肢となっている。

豊富なライブラリー

プログラミング言語Pythonは、巨大な開発者コミュニティに支えられている。あなたが想像できるほとんどすべてのライブラリがある。最も人気のあるライブラリのいくつかは以下の通りです:

そして、それはほとんど表面しか見ていない。素晴らしいPythonパッケージの全リストを見たい場合は、GitHubの awesome-pythonを見てほしい。

動的型付け

Pythonは動的型付け言語であり、変数を作るときにデータ型を宣言する必要がない。変数の型はコードが実行されるときにのみ代入されます。

それは素晴らしいことだが、動的型付け言語は時として諸刃の剣となる。通常、動的型付け言語は静的型付け言語に比べてエラーが起こりやすく、メモリ制御のレベルも低い。

拡張可能

プロジェクト要件に重い計算タスクが含まれている場合は、CやC++のような高速言語でそれらを記述し、Pythonコードからそれらを呼び出すことができます。

PythonからC/C++を呼び出す方法については、こちらの記事をご覧ください。

Pythonの限界

パイソンの短所

悪いパフォーマンス

Pythonは他のプログラミング言語と比べて比較的遅い。Pythonのパフォーマンスを最も妨げる2つの要因は、Pythonがインタプリタ言語であることと、動的型付けであることです。もしあなたのプロジェクトの要件に重い計算やマルチスレッドが含まれているなら、Pythonは最も適切なツールではないかもしれません。CやC++、あるいは他のコンパイルされた言語を使う方がずっとよいでしょう。

メモリ・インテンシブ

静的型付け言語で書かれたプログラムと比較すると、Pythonプログラムは通常、より多くのメモリを消費し、メモリに対する制御のレベルが低くなります。アプリがメモリ効率的である必要がある場合、これは少し問題になります。

ランタイムエラーを起こしやすい

Pythonはインタプリタ言語であるため、ランタイムエラーが発生しやすい。コンパイルプロセスがないので、コンパイル時にバグを発見することができない。その上、Pythonは動的型付け言語なので、開発者はいつでも変数の型を変更することができます。これは時にエラーにつながる可能性があり、開発者は変数の型に注意する必要があります。

グローバル・インタープリター・ロック(GIL)

Pythonのグローバル・インタープリタ・ロック(GIL)は、一度に1つのスレッドだけがPythonバイトコードを実行することを保証するメカニズムです。これは言語の実装を単純化し、ある種のプログラムには性能上の利点をもたらしますが、CPUに縛られたアプリケーションでマルチコアプロセッサをフルに活用する能力を制限することにもなります。

データベースアクセスに最適化されていない

Pythonアプリケーションでデータベースを扱うことは、Java Database Connectivity (JDBC)のような強力でユーザーフレンドリーなインターフェースがないため、より困難な場合があります。Pythonは単純な読み書きタスクを含むデータベース操作にはまだ使えますが、大規模で複雑なデータベースを扱う必要があるアプリケーションには最適な選択肢ではないかもしれません。

Pythonの展開オプション

Pythonアプリケーションはいくつかのクラウドプラットフォームにデプロイすることができる。一般的には、以下の3つのカテゴリーに分けることができる:

  • インフラストラクチャー・アズ・ア・サービス(IaaS)
  • サービスとしてのプラットフォーム(PaaS)
  • サービスとしてのコンテナ(CaaS)

IaaSは最も抽象度が低く、CaaSは最も抽象度が高い。従来型のホスティングもありますが、そちらはすでにご存知でしょう。

インフラストラクチャー・アズ・ア・サービス(IaaS)

IaaS(Infrastructure as a Service)とは、クラウド・コンピューティング・モデルの一つで、サードパーティ・ベンダーが、サーバー、ストレージ、オペレーティング・システム、ネットワークなどの仮想化されたコンピューティング・リソースをインターネット経由で提供するものである。提供されたリソースは、高度なダッシュボードや高レベルのAPIを介して管理することができ、顧客はインフラ全体を完全に制御することができます。

IaaSの主なメリットは、スケーラビリティ、コスト削減、サポートの強化、パフォーマンス、セキュリティである。ほとんどのIaaSプロバイダーが採用している支払い方式は従量課金制で、利用したリソースに対してのみ料金が発生する。

IaaSは最も柔軟なクラウド・コンピューティング・モデルであり、2010年代初頭にリリースされて以来、最も人気のあるオプションであり続けている。IaaSの最大の欠点は、顧客がアプリケーション、オペレーティング・システム、データに対して全責任を負うことだ。

サービスとしてのプラットフォーム(PaaS)

PaaS(Platform as a Service)は、アプリケーションを作成、管理、提供するためのクラウド環境をユーザーに提供するクラウド・コンピューティング・サービスである。PaaSは、アプリの開発、カスタマイズ、テストのためにあらかじめ構築されたツールを提供する。PaaSでは、サービス・プロバイダーがサーバー、オペレーティング・システム、ソフトウェア、バックアップなどの基盤となるインフラを管理するため、ユーザーはアプリに集中することができる。

PaaSの利点には、市場投入までのスピードの速さ、セキュリティの向上、費用対効果、スケーラビリティ、高可用性などがあり、一般に必要なコードが少なくて済む。しかし、いくつかのマイナス面もある。主な3つのデメリットは、柔軟性の欠如、コントロールの欠如、ベンダーロックインのリスクである。とはいえ、PaaSを利用することで、ユーザーはより迅速に、より少ない管理オーバーヘッドでアプリを構築することができる。

サービスとしてのコンテナ(CaaS)

Containers as a Service(CaaS)とは、コンテナ仮想化技術を利用することで、コンテナのアップロード、実行、拡張、管理を可能にするクラウド・コンピューティング・モデルである。CaaSベンダーは、インフラ、オペレーティング・システム、ソフトウェア、コンテナ化エンジンなど、必要な作業の多くを抽象化する。

コンテナの素晴らしいところは、一度アプリをコンテナ化すれば、ほとんどどこにでもデプロイでき、同じように動作することが保証されることだ。必要があれば、CaaSベンダーから別のベンダーに移行することもできる。CaaSの顧客は通常、コンテナごとに課金される(コンテナの仕様に基づいて)。

CaaSは一般的にIaaSやPaaSに比べて高価で、柔軟性や制御のレベルが低く、アプリケーションのドッカー化など、いくつかの初期作業が必要になる。とはいえ、一度アプリをコンテナ化してしまえば、クラウド・コンピューティング・モデルの中では使いやすい部類に入る。

コンテナについての詳細は、「クラウド・コンピューティングにおけるコンテナとは?

Python導入プロセス

チュートリアルのこのセクションでは、簡単なPythonアプリケーションを構築してデプロイする方法を順を追って説明します。WebアプリケーションはFlaskフレームワークを使って実装し、Back4app Containersにデプロイします。

前提条件

  • PythonとFlaskの使用経験
  • Dockerの基本的な理解
  • Pythonバージョン3.8以降とDocker Desktopのインストール

Back4app Containersとは?

Back4app Containersは、グローバルに分散されたコンテナを使用してアプリケーションをデプロイし、スケーリングすることを可能にする無料のオープンソースプラットフォームです。

Back4app Containersを使えば、DevOpsの心配をすることなく、ソフトウェアの構築と迅速な出荷に集中することができます。

このプラットフォームはGitHubと緊密に統合されており、CI/CDシステムが組み込まれています。Back4app Containersを使うことで、数分でアプリをオンラインにすることができます。

なぜBack4app Containersを使うのですか?

  • GitHubとうまく統合
  • スケーリングとゼロダウンタイムのデプロイメント
  • 非常に使いやすい
  • 優れたカスタマーサポート

プロジェクト紹介

この記事では、TODOリストとして機能するシンプルなRESTful APIを構築し、デプロイする。このウェブAPIでは、ユーザが基本的なCRUD操作(タスクの追加、削除、完了マークなど)を行えるようにする。

まずソースコードに取り組み、後でBack4app Flask Containerにデプロイします。WebアプリケーションはFlask Webフレームワークを使って実装します。

コード・アプリ」のセクションは読み飛ばして、ご自分のPythonプロジェクトに沿って進んでください。

コードアプリ

イニシャル・プロジェクト

Flaskアプリ専用のディレクトリを作成し、そこに移動することから始めます:

$ mkdir flask-todo
$ cd flask-todo

仮想環境を作成し、起動する:

$ python3 -m venv venv && source venv/bin/activate

次に、pip経由でFlaskをインストールする:

$ (venv) pip install Flask==2.2.2

すべてが動作することを確認するために、app.pyの内容を以下のように置き換えてみましょう:

# app.py

from flask import Flask

app = Flask(__name__)


@app.route('/')
def index_view():
    return {
        'detail': 'Hello world'
    }

このコードはFlaskを初期化し、メッセージを返すシンプルなエンドポイントを作成します。

でサーバーを起動する:

$ (venv) flask run

最後に、お気に入りのブラウザでhttp://localhost:5000/。Hello worldというメッセージが表示されるはずだ。

データベース

続いて、データベースを扱おう。

生のSQLを実行する代わりに、シンプルなFlaskORMであるFlask-SQLAlchemyを使います。インストールするには

$ (venv) pip install Flask-SQLAlchemy

次に、app.pyの内容を以下のように置き換える:

# app.py

from datetime import datetime

from flask import Flask, jsonify, request
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
app = Flask(__name__)
app.config['SECRET_KEY'] = '5b3ef5s80gl3b217c20fb37044fe4k33'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///default.db"
db.init_app(app)


class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128), nullable=False)
    description = db.Column(db.String(256), nullable=False)

    is_done = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.now(), nullable=True)
    updated_at = db.Column(db.DateTime, default=None, nullable=True)

    def as_dict(self):
        return {c.name: getattr(self, c.name) for c in self.__table__.columns}

    def __repr__(self):
        return f'<Task {self.title}>'


@app.route('/')
def index_view():
    return {
        'name': 'flask-todo',
        'description': 'a simple todo app written in flask',
        'version': 1,
    }

このコードはFlaskを設定し、Taskという新しいモデルを定義します。タスクモデルには、名前説明is_done、そして動的に更新するcreated_atupdated_atといった変数があります。

次に、init_db.pyという名前の Python スクリプトを作成し、SQLite データベースを初期化して投入します:

# init_db.py

from app import db, app
from app import Task

with app.app_context():
    db.create_all()

    if Task.query.count() == 0:
        tasks = [
            Task(name='Backup the database', description='Make sure to backup the database with all the tables.'),
            Task(name='Setup 2FA', description='Setup the two factor authentication to secure your account.'),
            Task(name='Malware scan', description='Perform a malware scan.'),
        ]

        for task in tasks:
            db.session.add(task)

        db.session.commit()

コマンドラインからスクリプトを実行する:

$ (venv) python init_db.py

instanceという名前の新しいディレクトリが作成されていることに気づくだろう。このディレクトリの中にはデフォルトのデータベースであるdefault.dbがあります。

API

Flaskアプリは以下のURLを持つことになる:

  1. /api/は全てのタスクのリストを返す。
  2. /api// 特定のタスクを表示または削除する
  3. /api/create/新しいタスクを作成する
  4. /api/toggle// 特定のタスクのis_doneプロパティをトグルする。

これらを実装するには、app.pyの最後に以下を追加します:

# app.py

@app.route('/api/')
def list_view():
    json = [task.as_dict() for task in Task.query.all()]
    return jsonify(json)


@app.route('/api/<int:task_id>/', methods=['GET', 'DELETE'])
def detail_view(task_id):
    task = db.get_or_404(Task, task_id)

    if request.method == 'DELETE':
        db.session.delete(task)
        db.session.commit()

        return {
            'detail': 'Task has been successfully deleted.'
        }
    else:
        return task.as_dict()


@app.route('/api/create/', methods=['POST'])
def create_view():
    name = request.form.get('name')
    description = request.form.get('name')

    if name is None or description is None:
        return {
            'detail': 'Please provide the name and the description.'
        }, 400

    task = Task(name=name, description=description)
    db.session.add(task)
    db.session.commit()

    return task.as_dict()


@app.route('/api/toggle/<int:task_id>/')
def toggle_view(task_id):
    task = db.get_or_404(Task, task_id)

    if task.is_done:
        task.is_done = False
    else:
        task.is_done = True

    task.updated_at = datetime.now()
    db.session.commit()

    return task.as_dict()

このコードはかなり自明だ。必要なルートを定義し、必要なロジックを実装した。データベースに変更を加えるたびに、commit()しなければなりません。

これでFlaskアプリは完成だ。次のセクションでは、デプロイのためにプロジェクトを準備する。

ガニコーン

FlaskのWebサーバーは、一度に1つのリクエストを処理するように設計されており、大量のトラフィックを処理できない可能性があるため、本番環境では推奨されません。というのも、FlaskのWebサーバーは一度に1つのリクエストを処理するように設計されており、大量のトラフィックを処理できない可能性があるからだ。そのため、Gunicorn(本番環境に対応したPython WSGIサーバー)と交換してみよう。

まず、pipでインストールする:

$ (venv) pip install gunicorn==20.1.0

インストールに成功したら、次のようにサーバーを起動する:

$ (venv) gunicorn -w 2 -b 0.0.0.0:5000 app:app

[INFO] Starting gunicorn 20.1.0
[INFO] Listening at: http://0.0.0.0:5000 (1)
[INFO] Using worker: sync
[INFO] Booting worker with pid: 3
[INFO] Booting worker with pid: 4

このコマンドはUNIXベースのオペレーティング・システムでのみ動作することに留意してほしい。

これで2つのGunicornワーカーが起動し、あなたのアプリがポート5000に公開されます。アプリにアクセスするには、お気に入りのウェブブラウザを開き、http://localhost:5000。

要件.txt

アプリをDocker化する前にしなければならない最後のことは、requirements.txtファイルを作成することだ。requirements.txtファイルは、プロジェクトの依存関係を指定するために使用する。

以下のコマンドを実行して生成する:

$ (venv) pip freeze > requirements.txt

他の人(Dockerコンテナを含む)は、requirements.txtファイルを次のように利用できる:

$ (venv) pip install -r requirements.txt

Dockerizeアプリ

アプリをDocker化するには、Dockerfileを使う。Dockerfileは、Dockerイメージの構築手順を定義するために使用します。Dockerfileでは、ベースイメージ、作業ディレクトリ、環境変数、コマンドの実行などを設定できます。

.dockerignore

Dockerfileを作成する前に、.dockerignoreファイルを作成しましょう。.dockerignoreファイルは、イメージから省略するフォルダやファイルを指定するために使用します。

.git/
.idea/
instance/
__pycache__/

必要に応じて.dockerignoreを変更してください。

ドッカーファイル

プロジェクト・ルートにDockerfileという新しいファイルを以下の内容で作成する:

# syntax=docker/dockerfile:1.4
FROM --platform=$BUILDPLATFORM python:3.10-alpine

WORKDIR /app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install the requirements
COPY requirements.txt /app
RUN --mount=type=cache,target=/root/.cache/pip \
    pip3 install -r requirements.txt

COPY . .

# initialize the database (create DB, tables, populate)
RUN python init_db.py

EXPOSE 5000/tcp

CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "app:app"]

このDockerfileはpython:3.10-alpineをベースイメージとして使用しています。そして作業ディレクトリを設定し、必要なものをインストールし、プロジェクトをコピーし、データベースを初期化し、最後にポート5000でGunicornサーバーを起動します。

Dockerfileの詳細については、Dockerfile リファレンスをチェックしてください。

テスト

Dockerfileが動作することを確認するために、ローカルでビルドして実行してみよう。イメージをビルドするには

$ docker build -t flask-todo:1.0 .

画像をリストアップすると、新しい画像が表示されるはずです:

$ docker images

REPOSITORY        TAG       IMAGE ID       CREATED       SIZE
flask-todo        1.0       7ege66240eb1   3 hours ago   109MB

最後に、そのイメージを使って新しいDockerコンテナをスピンアップする:

$ docker run -it -p 5000:5000 flask-todo:1.0

[2023-02-02 20:08:57 +0000] [1] [INFO] Starting gunicorn 20.1.0
[2023-02-02 20:08:57 +0000] [1] [INFO] Listening at: http://0.0.0.0:5000 (1)
[2023-02-02 20:08:57 +0000] [1] [INFO] Using worker: sync
[2023-02-02 20:08:57 +0000] [7] [INFO] Booting worker with pid: 3
[2023-02-02 20:08:57 +0000] [8] [INFO] Booting worker with pid: 4

dを使用すると、Dockerコンテナをデタッチモードで起動できる。つまり、コンテナはターミナルのバックグラウンドで実行され、入力を受け取ったり出力を表示したりしない。

素晴らしい、あなたのアプリケーションは今コンテナで動いている。お気に入りのウェブ・ブラウザでhttp://localhost:5000にアクセスし、あなたのウェブ・アプリケーションの動きを見てみよう。

{
    "name": "flask-todo",
    "description": "a simple todo app written in flask",
    "version": 1
}

GitHubにプッシュする

以下の手順では、GitHub のアカウントが必要です。まだ持っていない場合は、サインアップするかログインしてください。さらに、Gitがインストールされ、設定されていることを確認してください。

GitHub にログインしたら、画面右上の “plus” ボタンを使ってドロップダウンを開きます。次に、”New repository” を選択します:

GitHubインデックスページ

リポジトリのカスタム名を決めます。ここでは “flask-todo “とし、”Create repository “をクリックします:

GitHub リポジトリの作成

リポジトリが作成されたら、リモートURLに注意してください:

GitHub SSHリモートアドレス

さて、ローカル・プロジェクトに戻ってコードをプッシュしよう。

Git にプッシュしたくないファイルがいくつかあるので、プロジェクトルートに.gitignoreファイルを作成します。このファイルには次のように書きますが、必要に応じて自由に変更してください:

instance/*
!instance/.gitignore
.webassets-cache
.env

__pycache__/
*.py[cod]
*$py.class

次に、コマンドラインを開き、以下のコマンドを実行する:

$ git init
$ git remote add origin <your_remote_url>
$ git add .
$ git commit -m "init"
$ git push origin master

これで新しい Git リポジトリが初期化され、リモートオリジンが追加されてすべてのファイルが VCS され、最初のコミットが作成されます。最後に、ソースコードを GitHub リポジトリにプッシュします。

これで完了です。ブラウザでリポジトリに移動すると、すべてのファイルがコミットされたことが確認できるはずです。

アプリのデプロイ

以下のステップでは、Back4appのアカウントが必要です。既にアカウントをお持ちの場合はログインし、そうでない場合は無料アカウントにサインアップしてください。

ログインすると、アプリのダッシュボードにリダイレクトされます。Build new app “ボタンをクリックし、アプリの作成プロセスを開始します。

Back4app アプリ作成

Back4appでは、Backend as a Service (BaaS)とContainers as a Service (CaaS)の2種類のアプリをビルドしてデプロイすることができる。我々はDocker化されたアプリをデプロイしたいので、”Container as a Service “を使うことにする。

サービスとしてのBack4appコンテナ

次に、GitHubとBack4appアカウントを接続します。前のステップで作成したリポジトリにBack4appのパーミッションを与えていることを確認してください。次に、緑色の “Select” ボタンをクリックして選択します。

Back4app Select リポジトリ

Back4app Containersでは、デプロイプロセスを設定することができます。デフォルトのブランチ、ルートディレクトリ、自動デプロイの有効/無効、環境変数などを設定できる。アプリの名前を決めて、”Create app “をクリックしよう。

アプリの設定

Back4appはコンテナをビルドし、コンテナレジストリにアップロードし、コンテナを実行するのに少し時間がかかります。コンテナの準備が完了すると、ステータスが “Ready “に変わり、画面の左側に緑色のURLが表示されます。

Back4appの導入に成功

URLをクリックすると、ブラウザでウェブアプリが開きます。Back4appがあなたのアプリのSSL証明書を自動的に発行し、Pythonアプリを無料でホストできることに気づくでしょう。

結論

この記事では、Pythonについて、その利点、欠点、デプロイオプションについて学びました。また、シンプルなPython RESTful APIのデプロイにも成功しました。これで、簡単なAPIをビルドしてBack4app Containersにデプロイできるようになるはずです。

最終的なソースコードはGitHubで見ることができる。

今後のステップ

  • 現時点では、再デプロイするたびにすべてのデータがクリアされます。これはデータベースがDockerイメージに含まれているためです。マネージドデータベースインスタンスへの切り替えを検討してください。
  • デプロイプロセスをスピードアップするために、マルチステージビルドを検討する。

Leave a reply

Your email address will not be published.