12億円ほしい人のブログ

メガBIG当たらないかな

Nginx + uWSGI + Flaskアプリを作成

簡単なFlaskアプリをChatGPTに教えてもらいながら作成しました。

Docker Desktopを起動してから以下を順番に進めました。

ディレクトリ構造の作成

mkdir flask_app
cd flask_app

Dockerfileの作成

flask_app/Dockerfile

# ベースイメージとしてUbuntuを使用
FROM ubuntu:latest

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
    python3 \
    python3-pip \
    vim \
    nginx \
    && rm -rf /var/lib/apt/lists/*

# FlaskとuWSGIをインストール
RUN pip3 install flask uwsgi

# アプリケーションディレクトリを作成
RUN mkdir -p /app

# 作業ディレクトリを設定
WORKDIR /app

# Nginxの設定ファイルをコピー
COPY nginx.conf /etc/nginx/nginx.conf

# アプリケーションのソースコードをコピー
COPY . /app

# ポート80を開放
EXPOSE 80

# NginxとuWSGIを起動
CMD service nginx start && uwsgi --ini uwsgi.ini

Nginxの設定ファイルの作成

flask_app/nginx.conf

user  www-data;
worker_processes  auto;
pid /run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        location / {
            include uwsgi_params;
            uwsgi_pass 127.0.0.1:8000;
        }
    }
}

uWSGIの設定ファイルの作成

flask_app/uwsgi.ini

[uwsgi]
module = app:app
master = true
processes = 4
socket = 127.0.0.1:8000
vacuum = true
die-on-term = true

Flaskアプリケーションの作成

flask_app/app.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello, World!"

if __name__ == "__main__":
    app.run()

Dockerコンテナのビルドと起動

flask_appディレクトリで以下を実行

docker build -t flask_app .
docker run -p 80:80 flask_app

動作確認

http://localhost:80 にアクセス

※最初はブラウザでエラーになったが、Docker Desktop上のリンクからだとアクセスできた(謎)

AWS Lambdaのスケーリングについて

AWS Lambdaのスケーリングについてのメモ。

Lambdaにおける「スケーリング」の意味

Lambdaでは、リクエスト数に応じて自動でスケーリングする。この「スケーリング」とは、具体的には「スケールアウト、スケールイン」のことである。
すなわち、リクエストを処理するインスタンスを増やす・減らすことを自動で行ってくれる。

注意点

メモリ(やCPU)のサイズはLambdaを用意する際に最初に設定するものであり、自動でスケーリングはされない。
そのため、メモリをたくさん使用するような処理は、メモリのサイズに問題がないか検討する必要がある。

docker container run ubuntuで直ちに終了する理由について

Dockerの学習にてUbuntuコンテナを起動する機会があり、そのときのメモです。

概要

docker container run ubuntuUbuntuコンテナを起動する際に、-itオプションを付けないと、なぜ、コンテナが起動して直ちに終了するのか。

疑問

上記runコマンドでは、起動後のコマンドを指定しない場合はDockerイメージに定義されたデフォルトコマンドが、コンテナ起動後に実行される。
Ubuntuイメージのデフォルトコマンドは、/bin/bash
ということは、Bashシェルが起動してコンテナはその状態でUpのままになるのでは?

調査結果

runコマンドに-itオプションがない場合、バックグラウンドでの実行となるが、この場合標準入力が有効になっていない(?)ことになるらしい。
その場合、/bin/bashBashシェルが起動するものの、直ちに終了する仕様らしい。
そのため、上記コマンドではコンテナが起動して直ちに終了するみたい。

備考

runコマンドの末尾にコマンドを続けると、デフォルトコマンドの代わりにそのコマンドが実行される。
例えば、pwdを続けると、カレントディレクトリが出力される。(コンテナ自体はその後直ちに終了する。)
pwdは、Unix系シェルのコマンドであるが、これが使用できるということは、すなわちUbuntuの場合はBashシェルが起動しているということを意味する。
ということは、Ubuntuコンテナを起動するとBashシェルが自動で起動するようになっているということかも。(調べてないが)

Flaskアプリ用にPostgreSQLを用意

FlaskベースのWebアプリ用のデータベースとしてPostgreSQLを用意した時のメモ。

PostgreSQLのインストール

以下サイトを参考に実施。

管理者ユーザーでログイン

C:\PostgreSQL\15\bin\psql.exe -U postgres
# Passwordを入力

ロールを作成

CREATE ROLE flaskuser WITH LOGIN PASSWORD 'password';

データベースを作成

WITH OWNER flaskuserをつけないと、flask db migrateで「psycopg2.errors.InsufficientPrivilege: スキーマ public へのアクセスが拒否されました」エラーになりました。

CREATE DATABASE flasktutorial WITH OWNER flaskuser;
# \lでデータベース一覧を確認する

アクセスできることを確認

# ctrl+cで管理者ユーザーからログアウトしておく
C:\PostgreSQL\15\bin\psql.exe -U flaskuser -d flasktutorial
# Passwordを入力

FlaskアプリにてDB接続を実装

SQLALCHEMY_DATABASE_URIに、接続情報を記載

# from pathlib import Path
from flask import Flask
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect

db = SQLAlchemy()
csrf = CSRFProtect()


def create_app():
    app = Flask(__name__)
    app.config.from_mapping(
        SECRET_KEY="flaskapp123456789",
        SQLALCHEMY_DATABASE_URI="postgresql+psycopg2://{user}:{password}@{host}/{name}".format(
            user="flaskuser",
            password="password",
            host="127.0.0.1",
            name="flasktutorial",
        ),
        SQLALCHEMY_TRACK_MODIFICATIONS=False,
        SQLALCHEMY_ECHO=True,
        WTF_CSRF_SECRET_KEY="flaskapp123456789",
    )
    csrf.init_app(app)

    db.init_app(app)
    Migrate(app, db)

    from apps.crud import views as crud_views

    app.register_blueprint(crud_views.crud, url_prefix="/crud")

    return ap

DB周りのflaskコマンドを実行

flask db init
flask db migrate
flask db upgrade

Flaskのデバッグモードでの起動方法のメモ

Flaskのデバッグモードでの起動にあたってのメモです。

  • FLASK_ENVは廃止されているため、FLASK_ENV=developmentとしてもデバッグモードはoffのまま
  • デバッグモードを利用するときは、FLASK_DEBUG=1.envに記載する
参考

qiita.com

msiz07-flask-docs-ja.readthedocs.io

SQLAlchemy, psycopg2の役割についてのメモ

PythonからDBを操作する場合、SQLAlchemyを使用するケースが多いです。 PostgreSQLで使用してみたいと思い、SQLAlchemyのインストール方法とかをググったところ、psycopg2というライブラリもインストールが必要とのことでした。 それぞれのライブラリの役割がよくわからなかったので、調べてみました。

そもそも

1

プログラミング言語からデータベースにアクセスするには専用のソフトウェアが必要。 そのようなソフトウエアはドライバと呼ばれる。
プログラミング言語ごと、データベースごとに様々なドライバがある。

2

Pythonからデータベースにアクセス・操作するためのAPIの仕様を定めたものをDBAPIと呼ぶ。
この仕様に則り、APIが用意されることで、Pythonからは同じコードを使用して異なる種類のデータベースにアクセスすることができるようになる。(らしい。)

psycopg2

DBAPIの実装の1つで、PythonからPostgreSQLを利用するためのライブラリ。
ドライバでもある。(とChatGPTが言ってた。)
ちなみに、psycopg2を再実装してモダンな機能が追加されたpsycopg3もある。

SQLAlchemy

Python用のSQLツールキットかつORM(Object-Relational Mapping)ツール。
様々なデータベースエンジンとのやり取りを簡素化することができる。
なお、PostgreSQLを操作する場合は、内部でpsycopg2を使用している。なので、psycopg2もインストールする必要がある。

SQLAlchemyの内部実装

以下の構成要素があるらしい。

  • Engine
  • Pool
  • Dialect
  • DBAPI

詳細は以下参照 docs.sqlalchemy.org

Flaskのリクエストの情報の取り出されるかについてのメモ

Flaskのrequestについての妄想メモです。

flask.requestについて

以下のようなコードを例とします

from flask import Flask, request

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def index():
    user_email = request.form['user_email']
    # 以下省略

このとき、requestには、クライアントから送られたリクエストの情報が入っています。
ただ、コードを単純に見るとrequestはimportされてそのまま利用されているだけで、特にリクエストの情報を入れているようには見えません。
そのため、requestNoneであるように見えます。
しかし、当然ですが、実際にはリクエストの情報を取り出して利用できます。 これは、裏でFlaskが働いて、リクエストの情報をrequestに入れているのかと思われます。
(参考文献なしなので妄想。)

Flask-WTformsについて

以下のようなコードを例とします。

@auth.route('/signup', methods=['GET', 'POST'])
def signup():
    form = SignUpForm()

    if form.validate_on_submit():
        user = User(
            username=form.username.data,
            email=form.email.data,
            password=form.password.data,
        )
    # 以下省略

上記コードでは、フォームに入力した情報がリクエストを経由してformに格納される。
コード上では、form = SignUpForm()とあるのみで、リクエストの情報を格納しているようには見えない。しかし、実際には、Flask-WTformsが自動で格納しているらしい。
公式ドキュメントに以下の記載があった。

If formdata is not specified, this will use flask.request.form and flask.request.files.

flask-wtf.readthedocs.io