佐賀人のIT技術者のブログ

IT技術、日常のブログです。たまに地元・佐賀について書きます。

【Python】マルチワーカー環境でのWebアプリログの残し方

Gunicorn と uWSGI を用いた Web アプリケーションのデプロイ

Gunicorn や uWSGI (またはそれに類するもの) を使って Web アプリケーションをデプロイする場合、クライアントのリクエストを処理するために複数のワーカープロセスが作られます。そのような環境では、 web アプリケーションに直接ファイルベースのハンドラを作ることは避けてください。 代わりに、 SocketHandler を使って別のプロセスとして動作するリスナーに web アプリケーションからログを送信するようにしてください。これは Supervisor のようなプロセス管理ツールを使うことで設定できます。

ロギングソケットのリスナーを実行する

作成中のロギングリスナーを実行するためには、 Supervisor のようなプロセス管理ツールを使う必要があるかもしれません。 [こちらの Gist] (https://gist.github.com/vsajip/4b227eeec43817465ca835ca66f75e2b)は Supervisor を使って上記の機能を実行するための必要最低限のファイルを提供しています。以下のファイルが必要になります:

ファイル 目的
prepare.sh 試験用の環境を準備する Bash スクリプト
supervisor.conf リスナーとマルチプロセスの web アプリケーションのための設定を含む Supervisor の設定ファイル
ensure_app.sh Supervisor が上記の設定で実行されていることを保証するための Bash スクリプト
log_listener.py ログイベントを受信してファイルに記録するソケットリスナープログラム
main.py リスナーに接続されたソケットを通じてロギングを実行する簡単な web アプリケーション
webapp.json web アプリケーションのための JSON 設定ファイル
client.py web アプリケーションを起動するための Python スクリプト

この web アプリケーションは、リクエストを処理する複数のワーカープロセスを起動する web アプリケーションサーバーである Gunicorn を使っています。ここに例として挙げた設定は、複数のワーカーがいかにして互いに衝突することなく同じログファイルに書き込みを行うことができるかを示しています --- ワーカーは全てソケットリスナーを通じてログを書き込むのです。

これらのファイルを試すためには、 POSIX 環境において以下を行なってください:

  1. Download ZIP ボタンを押して Gist を ZIP アーカイブとしてダウンロードしてください。

  2. アーカイブファイルをスクラッチディレクトリに展開してください。

  3. 準備のために、スクラッチディレクトリにおいて bash prepare.sh を実行してください。これにより Supervisor 関連のファイルおよびログファイルのための run サブディレクトリ、および bottle, gunicorn そして supervisor がインストールされる仮想環境を含む venv サブディレクトリが生成されます。

  4. bash ensure_app.sh を実行して Supervisor が上記の設定で実行されていることを確認してください。

  5. venv/bin/python client.py を実行して web アプリケーションを起動してください。これによりログにレコードが書き込まれるはずです。

  6. run サブディレクトリにあるログファイルを調べてください。最新のログは、パターン app.log* に一致する名前のファイルにあるはずです。ログは異なるワーカープロセスによって非決定論的な形で並行に処理されるため、特定の決まった順番にはなりません。

  7. リスナーと web アプリケーションは venv/bin/supervisorctl -c supervisor.conf shutdown を実行することでシャットダウンできます。

ありそうもないことですが、テスト環境で設定したポートが別の設定と衝突してしまった場合、設定ファイルを修正する必要があるかもしれません。

参考

Logging クックブック — Python 3.13.1 ドキュメント

2025年明けましておめでとうございます

初日の出を見に行きました。 昨年はありがとうございました。本年もよろしくお願いします。

昨年は仕事・プライベートともにいろいろあり、体調に気を配りながら過ごした一年でした。

後厄の年でもあったのですが、大事がなくてよかったです。

本年も今続けていることを継続してやっていければいいなと思います。 ブログ、体力強化、アプリ開発は継続したい!!

初心者向け!SSHポートフォワーディングの設定方法と実例

SSH(Secure Shell)は、ネットワーク越しに安全に通信を行うためのプロトコルです。SSHには、リモートマシンとローカルマシンの間でポート転送を行う機能があります。このポート転送には、ローカルポートフォワーディングとリモートポートフォワーディングの2つの主な方法があります。これらは、特定の通信を安全にトンネル経由で転送するために使われます。

1. ローカルポートフォワーディング(Local Port Forwarding)

ローカルポートフォワーディングは、ローカルマシン上のポート(クライアント側)をリモートサーバー上の特定のポートに転送する方法です。具体的には、ローカルマシンで指定したポートに送られたデータが、SSHトンネルを経由してリモートサーバーの指定されたポートに転送されます。

使用例

例えば、ローカルマシンからリモートサーバーの内部サービス(例えば、データベースやWebサーバー)にアクセスする場合、ローカルポートフォワーディングを使うことができます。リモートサーバーの内部サービスが直接インターネットに公開されていない場合でも、安全にアクセスできます。

ssh -L <ローカルポート>:<リモートホスト>:<リモートポート> <ユーザー名>@<リモートサーバー>

例えば、ローカルマシンの8080ポートを、リモートサーバーのlocalhostで動作するポート80に転送したい場合、次のようにコマンドを実行します。

ssh -L 8080:localhost:80 [email protected]

このコマンドを実行すると、ローカルマシンのhttp://localhost:8080にアクセスすると、リモートサーバーのhttp://localhost:80に転送されることになります。

使用シーン

  • 開発環境: ローカルで動作するWebアプリケーションを、リモートサーバーの環境で確認したいとき。
  • データベースアクセス: インターネット上に公開されていないデータベースにアクセスする場合。

2. リモートポートフォワーディング(Remote Port Forwarding)

リモートポートフォワーディングは、リモートサーバー上のポートをローカルマシンの指定したポートに転送する方法です。これは、リモートサーバーがローカルのサービスにアクセスする必要がある場合に使用されます。例えば、リモートサーバーからローカルマシンで動作するWebサーバーやデータベースにアクセスしたい場合に有用です。

使用例

リモートサーバーの8080ポートをローカルマシンの80ポートに転送する場合、次のコマンドを実行します。

ssh -R <リモートポート>:<ローカルホスト>:<ローカルポート> <ユーザー名>@<リモートサーバー>

例えば、リモートサーバーでlocalhost:8080にアクセスできるようにするために、ローカルマシンで動作するWebサービスを公開する場合:

ssh -R 8080:localhost:80 [email protected]

このコマンドを実行すると、リモートサーバーのhttp://localhost:8080にアクセスすると、ローカルマシンのhttp://localhost:80に転送されます。

使用シーン

  • サーバー上でのデバッグ: リモートサーバーからローカルで動作している開発環境のサービスにアクセスする場合。

  • セキュアなバックドアアクセス: ローカルサーバーがインターネット越しにアクセスできないが、リモートからアクセスしたい場合。

ローカルポートフォワーディングとリモートポートフォワーディングの違い

特徴 ローカルポートフォワーディング リモートポートフォワーディング
転送元 ローカルマシン リモートサーバー
転送先 リモートサーバー内のホスト ローカルマシン
主な用途 ローカルからリモートサービスへのアクセス リモートからローカルサービスへのアクセス

まとめ

SSHのポートフォワーディング機能は、セキュアなトンネルを介してローカルマシンとリモートサーバー間で通信を行うための強力なツールです。ローカルポートフォワーディングはローカルからリモートサービスへのアクセスを可能にし、リモートポートフォワーディングはリモートからローカルサービスへのアクセスを提供します。これらを駆使することで、ネットワーク越しに安全なデータ転送やリモートサービスへのアクセスが実現できます。

FlaskのJinjaで自作関数・自作フィルタを追加する方法

FlaskではJinjaテンプレートエンジンを用いてHTMLを生成しますが、標準のフィルタや関数だけでは要件を満たせない場合があります。その際、自作の関数やフィルタをJinjaに追加して活用することができます。本記事では、その具体的な方法を解説します。

1. 自作フィルタの追加方法

フィルタとは?

Jinjaでは、テンプレート内でデータを加工するために「フィルタ」を使用できます。たとえば、upperフィルタを使って文字列を大文字に変換するなどが可能です。

自作フィルタの手順

  1. Python関数を作成
  2. Flaskアプリケーションに登録

実装例 以下の例では、文字列を逆順にするフィルタを作成します。

from flask import Flask, render_template

app = Flask(__name__)

# 自作フィルタ関数
def reverse_string(s):
    return s[::-1]

# フィルタを登録
app.jinja_env.filters['reverse'] = reverse_string

@app.route('/')
def index():
    return render_template('index.html', name="Flask")

if __name__ == '__main__':
    app.run(debug=True)

テンプレート側での使用例 以下のようなindex.htmlでreverseフィルタを使用できます。

<!DOCTYPE html>
<html>
<head>
    <title>Custom Filter Example</title>
</head>
<body>
    <h1>{{ name | reverse }}</h1>
</body>
</html>

出力結果:

Flask → ksalF

2. 自作関数の追加方法

関数とは?

テンプレート内で直接呼び出して使用する機能を関数として登録できます。フィルタとの違いは、引数や用途の柔軟性です。

自作関数の手順

  1. Python関数を作成
  2. Flaskアプリケーションに登録

実装例 以下の例では、現在の日付を取得する関数を作成します。

from flask import Flask, render_template
from datetime import datetime

app = Flask(__name__)

# 自作関数
def current_date():
    return datetime.now().strftime("%Y-%m-%d")

# 関数を登録
app.jinja_env.globals['current_date'] = current_date

@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)

テンプレート側での使用例 index.htmlで以下のように呼び出します。

<!DOCTYPE html>
<html>
<head>
    <title>Custom Function Example</title>
</head>
<body>
    <h1>Today is {{ current_date() }}</h1>
</body>
</html>

出力例:

Today is 2024-11-29

Blueprintアーキテクチャでの登録手順

Blueprintアーキテクチャを使用している場合でも、フィルタや関数を登録する手順は同じです。ファクトリ関数の中でjinja_envを操作します。

from flask import Flask
from datetime import datetime

def create_app():
    app = Flask(__name__)

    # 自作フィルタを登録
    def reverse_string(s):
        return s[::-1]
    app.jinja_env.filters['reverse'] = reverse_string

    # 自作関数を登録
    def current_year():
        return datetime.now().year
    app.jinja_env.globals['current_year'] = current_year

    # Blueprintを登録
    from blueprints.main import main
    app.register_blueprint(main)

    return app

ファクトリ関数で生成したアプリケーションオブジェクトを使えば、Blueprint全体で共通のフィルタや関数を利用できます。

まとめ

FlaskのJinjaで自作フィルタや関数を追加することで、テンプレートの表現力を大幅に向上させることができます。本記事では以下を解説しました:

  • 自作フィルタの追加方法
  • 自作関数の登録手順
  • Blueprintアーキテクチャでの登録手順

ちなみに、Blueprintアーキテクチャをアプリケーションファクトリパターンと呼ぶみたいですね。

FileZillaの移行手順を簡単解説!~設定を引き継いで快適に移行する方法~

FileZillaは、多くのユーザーに利用されているFTPクライアントですが、新しいPCやOSへの移行時に設定を引き継ぎたいと感じることも多いでしょう。本記事では、FileZillaの設定をスムーズに移行する手順をわかりやすく解説します。

目次

  1. 移行に必要な準備
  2. FileZillaの設定をエクスポートする方法
  3. 新しい環境でのインポート手順
  4. 注意点とトラブルシューティング

1. 移行に必要な準備

必要なもの

  • 移行元PC(現在使っているPC)
  • 移行先PC(新しいPCã‚„OS)
  • FileZillaのインストールが完了していること

移行可能なデータ

  • サイトマネージャー(接続先情報)
  • 接続履歴
  • FileZillaの設定全般

注意:保存した接続情報にはパスワードが含まれる場合があります。セキュリティを考慮し、安全な方法でデータを取り扱いましょう。

2. FileZillaの設定をエクスポートする方法

手順

  1. FileZillaを起動します。
  2. メニューから「ファイル」→「エクスポート」を選択。
  3. 「設定エクスポート」ダイアログが表示されます。以下のオプションを選択してください。
  4. サイトマネージャーエントリ
  5. 設定データ
  6. 必要に応じてその他の項目
  7. 保存先を指定し、「OK」をクリック。エクスポートされたファイル(FileZilla.xmlなど)が作成されます。

3. 新しい環境でのインポート手順

手順

  1. 新しいPCでFileZillaをインストール・起動。
  2. メニューから「ファイル」→「インポート」を選択。
  3. エクスポートしたファイルを選択し、「開く」をクリック。
  4. 移行したいデータ(サイトマネージャー、設定など)を選択して「OK」をクリック。
  5. インポートが完了すると、以前と同じ接続情報や設定が使用できるようになります。

4. 注意点とトラブルシューティング

よくある問題

  • パスワードがインポートされない:

    • FileZillaでは、パスワードの保存が暗号化されており、移行先で再入力が必要な場合があります。
  • インポートが失敗する:

    • ファイル形式が壊れている可能性があります。再度エクスポートを試してください。

セキュリティ面の注意 移行ファイルには重要な情報が含まれるため、以下の点を守りましょう。

  • 不要になった移行ファイルは確実に削除。
  • 移行時は信頼できるストレージやUSBデバイスを使用。

まとめ

FileZillaの移行手順は非常にシンプルですが、事前準備やセキュリティ対策を行うことで、安心して移行作業を行えます。新しい環境でもスムーズなファイル転送を実現するために、ぜひこの記事を参考にしてください!

SSHとは?初心者でもわかる基本解説

1. SSHとは?

SSH(Secure Shell)は、リモートコンピュータに安全に接続して操作できるツールです。 普段のインターネット通信と違い、SSHではデータが暗号化されて送られるため、第三者に内容を盗み見られる心配がありません。

主な用途

  • 遠隔地にあるサーバーの管理
  • ファイルのやり取り
  • コマンドの実行

2. SSHを使うための準備

2.1 必要なもの

1. SSHクライアント

  • Windowsでは「Windowsターミナル」や「PuTTY」など。
  • macOSã‚„Linuxではターミナルが標準搭載されています。

2. 接続情報

  • 接続先のサーバーのIPアドレスまたはホスト名
  • ユーザー名
  • パスワード、または公開鍵(詳細は後述)

3. SSHの使い方

3.1 基本の接続方法

ターミナルで以下のコマンドを入力します:

ssh ユーザー名@ホスト名

例: ユーザー名がuser、接続先がexample.comの場合

ssh [email protected]

初回接続時の注意

初めて接続する際に「このホストを信頼してもいいですか?」というメッセージが出ます。yesと入力すると接続が完了します。

4. ファイルの転送

サーバーにファイルを送ったり、逆にサーバーからダウンロードしたい場合、以下のコマンドを使います:

4.1 サーバーにファイルを送る

scp ローカルファイル ユーザー名@ホスト名:保存先パス

例: test.txtをサーバーの/home/user/に送る場合

scp test.txt [email protected]:/home/user/

4.2 サーバーからファイルをダウンロードする

scp ユーザー名@ホスト名:リモートファイル ローカルパス

例: サーバーの/home/user/test.txtを自分のPCにダウンロードする場合

scp [email protected]:/home/user/test.txt .

5. 公開鍵認証を設定してパスワード入力を省略する

パスワードを毎回入力するのが面倒な場合、公開鍵認証を使うと便利です。

5.1 公開鍵を作成

ターミナルで以下を入力:

ssh-keygen -t rsa

何も変更せずにEnterを数回押すと、鍵ペア(秘密鍵と公開鍵)が作成されます。

5.2 公開鍵をサーバーに登録

以下のコマンドを使います:

ssh-copy-id ユーザー名@ホスト名

これで、次回以降はパスワードを入力せずにログインできるようになります。

まとめ

SSHは初心者にも使いやすいリモート接続ツールです。 パスワード認証から始め、慣れてきたら公開鍵認証に挑戦してみましょう。ファイル転送や基本操作ができれば、リモートでの作業がスムーズに行えます。少しずつ学び、活用範囲を広げてみてください!

Flask初心者向け!安全なファイルアップロードと元の名前でのダウンロード完全ガイド

Flaskを使用してファイルを安全にアップロードし、元の名前でダウンロードできる機能を実装する方法をご紹介します。この方法は、werkzeug.utilsモジュールのsecure_filenameを活用して、セキュリティを考慮したファイル名の管理を実現します。

目次

  1. Flaskでのファイルアップロードの仕組み
  2. 安全なファイル名を作るsecure_filenameの役割
  3. ファイルアップロードとダウンロードの具体的な実装
  4. 実行例と注意点

1. Flaskでのファイルアップロードの仕組み

Flaskでは、request.filesを使用してクライアントから送信されたファイルを取得できます。取得したファイルはfile.save(path)メソッドでサーバーに保存可能です。ただし、ユーザーが指定したファイル名をそのまま使うと、セキュリティ上のリスクがあります。

例えば、「../../etc/passwd」のような名前のファイルをアップロードされると、サーバー内部のディレクトリ構造にアクセスされる可能性があります。これを防ぐために、secure_filenameで安全なファイル名に変換することが重要です。

2. 安全なファイル名を作るsecure_filenameの役割

secure_filenameは、ファイル名を安全な形式に変換してくれる便利な関数です。以下のように、特殊文字やディレクトリトラバーサル攻撃を防ぎます。

変換例

元のファイル名 変換後のファイル名
../../etc/passwd etc_passwd
hello world!.jpg hello_world.jpg
my<script>.png my_script_.png

3. ファイルアップロードとダウンロードの具体的な実装

必要なモジュールをインポート

以下のモジュールをインポートします。

from flask import Flask, request, send_from_directory, jsonify
from werkzeug.utils import secure_filename
import os

サーバーの基本設定

保存先のフォルダを指定し、初期設定を行います。

app = Flask(__name__)
UPLOAD_FOLDER = 'uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

ファイルアップロードの実装

ファイルをアップロードし、安全な名前に変換して保存する処理を実装します。

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        return jsonify({"error": "No file part"}), 400
    
    file = request.files['file']
    if file.filename == '':
        return jsonify({"error": "No selected file"}), 400
    
    original_filename = file.filename
    secure_name = secure_filename(original_filename)
    save_path = os.path.join(app.config['UPLOAD_FOLDER'], secure_name)
    
    file.save(save_path)
    return jsonify({
        "message": "File uploaded successfully",
        "original_name": original_filename,
        "saved_name": secure_name
    }), 200

ファイルダウンロードの実装

アップロードされたファイルを元の名前でダウンロードする処理を実装します。

@app.route('/download/<filename>', methods=['GET'])
def download_file(filename):
    secure_name = secure_filename(filename)
    file_path = os.path.join(app.config['UPLOAD_FOLDER'], secure_name)
    
    if not os.path.exists(file_path):
        return jsonify({"error": "File not found"}), 404
    
    return send_from_directory(
        app.config['UPLOAD_FOLDER'],
        secure_name,
        as_attachment=True,
        download_name=filename  # Flask 2.0以降
    )

4. 実行例と注意点

実行例

ファイルアップロード

curl -X POST -F "[email protected]" http://localhost:5000/upload

ファイルダウンロード

curl -O http://localhost:5000/download/example.txt

注意点

  • ファイル名の管理: サーバーに保存された名前と元の名前のマッピングが必要な場合は、データベースやJSONファイルを利用するのがおすすめです。
  • セキュリティ: secure_filenameを必ず使用して、不正なファイル名を排除しましょう。

まとめ

Flaskでのファイルアップロードとダウンロードを実装する際、secure_filenameを活用することでセキュリティを向上させることができます。また、ユーザーにとって使いやすい元の名前でのファイルダウンロードも簡単に実現できます。

ぜひこの方法を活用して、安全で便利なファイル操作機能を作成してみてください!