「サーバ - HTTP API」の版間の差分
編集の要約なし |
|||
| (同じ利用者による、間の2版が非表示) | |||
| 8行目: | 8行目: | ||
サーバの実装において、Linuxディストリビューションに必要なプログラム言語とフレームワークをインストールする。<br> | サーバの実装において、Linuxディストリビューションに必要なプログラム言語とフレームワークをインストールする。<br> | ||
一般的な選択肢として、PythonのFlaskやFastAPI、Node.jsのExpress、RubyのSinatra、JavaのSpring Boot、Go言語のGin等がある。<br> | 一般的な選択肢として、PythonのFlaskやFastAPI、Node.jsのExpress、RubyのSinatra、JavaのSpring Boot、Go言語のGin等がある。<br> | ||
<br> | <br> | ||
HTTP APIサーバを構築・運用する場合は、以下に示す事柄に注意する。<br> | HTTP APIサーバを構築・運用する場合は、以下に示す事柄に注意する。<br> | ||
| 403行目: | 359行目: | ||
クライアントは以下に示すソースコードを意識することなく、定義されたAPIエンドポイントを通じてデータの操作や機能の実行を行うことが可能となる。<br> | クライアントは以下に示すソースコードを意識することなく、定義されたAPIエンドポイントを通じてデータの操作や機能の実行を行うことが可能となる。<br> | ||
<br> | <br> | ||
まず、基本的なHTTP APIの実装例を示す。<br> | |||
<syntaxhighlight lang="python"> | |||
# 基本的なAPI実装例 | |||
from flask import Flask, request, jsonify | |||
app = Flask(__name__) | |||
# 計算処理のエンドポイント例 | |||
@app.route('/api/calculate', methods=['POST']) | |||
def calculate(): | |||
data = request.get_json() | |||
operation = data.get('operation') | |||
values = data.get('values', []) | |||
if operation == 'sum': | |||
result = sum(values) | |||
elif operation == 'average': | |||
result = sum(values) / len(values) if values else 0 | |||
else: | |||
return jsonify({'error': 'Unknown operation'}), 400 | |||
return jsonify({'result': result}) | |||
# データ取得のエンドポイント例 | |||
@app.route('/api/status', methods=['GET']) | |||
def get_status(): | |||
return jsonify({'status': 'running', 'version': '1.0.0'}) | |||
if __name__ == '__main__': | |||
app.run(debug=True) | |||
</syntaxhighlight> | |||
<br> | |||
次に、エラーハンドリングとロギングを含む、より本格的な実装例を示す。<br> | |||
<syntaxhighlight lang="python"> | <syntaxhighlight lang="python"> | ||
# app.pyファイル | # app.pyファイル | ||
| 634行目: | 624行目: | ||
== 認証と認可 == | == 認証と認可 == | ||
HTTP APIにおいて、認証・認可の仕組みは重要なセキュリティ要素となる。<br> | |||
適切な認証方式を選択することにより、APIへの不正アクセスを防止することができる。<br> | |||
<br> | |||
代表的な認証方式として、APIキー認証、JWT (JSON Web Token) 認証、Basic認証、OAuth2.0等がある。<br> | |||
それぞれの用途や要件に応じて、最適な認証方式を選択する必要がある。<br> | |||
<br> | |||
==== APIキー認証 ==== | ==== APIキー認証 ==== | ||
以下の例では、Python + FlaskでAPI認証を行っている。<br> | 以下の例では、Python + FlaskでAPI認証を行っている。<br> | ||
| 711行目: | 707行目: | ||
return f(*args, **kwargs) | return f(*args, **kwargs) | ||
return decorated_function | return decorated_function | ||
</syntaxhighlight> | |||
<br><br> | |||
== セキュリティ対策 == | |||
HTTP APIサーバのセキュリティ対策は、システム全体の安全性を確保するために不可欠である。<br> | |||
適切なセキュリティ対策を実施することにより、不正アクセス、データ漏洩、サービス妨害等の脅威から保護することができる。<br> | |||
<br> | |||
主要なセキュリティ対策として、以下の項目を実装する必要がある。<br> | |||
* APIキーまたはトークンベースの認証の実装 | |||
* HTTPS通信の設定と強制 | |||
* 入力データのバリデーション | |||
* SQLインジェクション対策 | |||
* クロスサイトスクリプティング (XSS) 対策 | |||
* クロスサイトリクエストフォージェリ (CSRF) 対策 | |||
* レート制限によるDDoS攻撃対策 | |||
* 適切なCORS設定 | |||
<br> | |||
==== 入力バリデーション ==== | |||
入力データの検証は、セキュリティの基本的な対策である。<br> | |||
悪意のあるデータやフォーマットの誤ったデータを早期に検出することにより、後続の処理における問題を防止できる。<br> | |||
<br> | |||
<syntaxhighlight lang="python"> | |||
# validation.pyファイル | |||
from flask import request, jsonify | |||
from functools import wraps | |||
import re | |||
def validate_input(schema): | |||
def decorator(f): | |||
@wraps(f) | |||
def decorated_function(*args, **kwargs): | |||
data = request.get_json() | |||
for field, rules in schema.items(): | |||
if rules.get('required') and field not in data: | |||
return jsonify({'error': f'Missing required field: {field}'}), 400 | |||
if field in data: | |||
value = data[field] | |||
# 型チェック | |||
if 'type' in rules and not isinstance(value, rules['type']): | |||
return jsonify({'error': f'Invalid type for field: {field}'}), 400 | |||
# 最大長チェック | |||
if 'max_length' in rules and len(str(value)) > rules['max_length']: | |||
return jsonify({'error': f'Field {field} exceeds maximum length'}), 400 | |||
# 正規表現チェック | |||
if 'pattern' in rules and not re.match(rules['pattern'], str(value)): | |||
return jsonify({'error': f'Field {field} does not match required pattern'}), 400 | |||
return f(*args, **kwargs) | |||
return decorated_function | |||
return decorator | |||
# 使用例 | |||
@app.route('/api/user', methods=['POST']) | |||
@validate_input({ | |||
'username': {'required': True, 'type': str, 'max_length': 50, 'pattern': r'^[a-zA-Z0-9_]+$'}, | |||
'email': {'required': True, 'type': str, 'pattern': r'^[\w\.-]+@[\w\.-]+\.\w+$'} | |||
}) | |||
def create_user(): | |||
# 処理ロジック | |||
pass | |||
</syntaxhighlight> | |||
<br> | |||
==== SQLインジェクション対策 ==== | |||
SQLAlchemyを使用することにより、パラメータ化されたクエリが自動的に生成されて、SQLインジェクションを防ぐことができる。<br> | |||
直接SQL文字列を構築することは避け、必ずパラメータバインディングを使用する。<br> | |||
<br> | |||
<syntaxhighlight lang="python"> | |||
# 安全な実装例 | |||
from sqlalchemy import text | |||
# 悪い例 (脆弱) | |||
# query = f"SELECT * FROM users WHERE username = '{username}'" | |||
# 良い例 (安全) | |||
query = text("SELECT * FROM users WHERE username = :username") | |||
result = db.session.execute(query, {"username": username}) | |||
</syntaxhighlight> | |||
<br> | |||
==== CORS設定 ==== | |||
CORS (Cross-Origin Resource Sharing) を適切に設定することにより、信頼されたドメインからのアクセスのみを許可することができる。<br> | |||
本番環境では、ワイルドカード (*) の使用を避け、具体的なドメインを指定することが推奨される。<br> | |||
<br> | |||
<syntaxhighlight lang="python"> | |||
# cors_config.pyファイル | |||
from flask_cors import CORS | |||
# 本番環境向けの厳格なCORS設定 | |||
CORS(app, resources={ | |||
r"/api/*": { | |||
"origins": ["https://example.com", "https://app.example.com"], | |||
"methods": ["GET", "POST", "PUT", "DELETE"], | |||
"allow_headers": ["Content-Type", "Authorization"], | |||
"expose_headers": ["Content-Range", "X-Content-Range"], | |||
"supports_credentials": True, | |||
"max_age": 3600 | |||
} | |||
}) | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
| 716行目: | 817行目: | ||
== レート制限 == | == レート制限 == | ||
レート制限を行うことにより、API呼び出しの過度な使用を防止して、サーバリソースを保護することができる。<br> | レート制限を行うことにより、API呼び出しの過度な使用を防止して、サーバリソースを保護することができる。<br> | ||
適切なレート制限の実装により、DDoS攻撃やAPIの乱用を効果的に防ぐことが可能となる。<br> | |||
<br> | <br> | ||
レート制限に必要なライブラリをインストールする。<br> | レート制限に必要なライブラリをインストールする。<br> | ||
| 743行目: | 845行目: | ||
def get_data(): | def get_data(): | ||
return jsonify({'data': 'sample data'}) | return jsonify({'data': 'sample data'}) | ||
</syntaxhighlight> | |||
<br><br> | |||
== パフォーマンスとスケーラビリティ == | |||
HTTP APIサーバの性能向上と拡張性の確保は、サービス品質を維持するために重要である。<br> | |||
適切な最適化とアーキテクチャ設計により、大量のリクエストに対応可能なシステムを構築することができる。<br> | |||
<br> | |||
主要な対策として、以下の項目を検討する必要がある。<br> | |||
<br> | |||
==== キャッシュの実装 ==== | |||
頻繁にアクセスされるデータをキャッシュすることにより、データベースへの負荷を軽減して、レスポンス時間を改善することができる。<br> | |||
RedisやMemcached等のインメモリデータストアを活用することが一般的である。<br> | |||
<br> | |||
キャッシュ戦略としては、以下の方式を検討する。<br> | |||
* キャッシュアサイド (Cache-Aside) パターン | |||
*: アプリケーションがキャッシュの読み書きを制御する方式 | |||
* ライトスルー (Write-Through) キャッシュ | |||
*: データ書き込み時に同時にキャッシュを更新する方式 | |||
* ライトビハインド (Write-Behind) キャッシュ | |||
*: 非同期でキャッシュからデータベースへ書き込む方式 | |||
<br> | |||
==== データベースの最適化 ==== | |||
データベースの性能は、API全体のパフォーマンスに大きく影響する。<br> | |||
以下の最適化手法を実施することが推奨される。<br> | |||
* インデックスの適切な設計と管理 | |||
* クエリの最適化とN+1問題の回避 | |||
* コネクションプーリングの活用 | |||
* クエリキャッシュの利用 | |||
* 読み取り専用レプリカの導入 | |||
<br> | |||
==== ロードバランサの設置 ==== | |||
複数のAPIサーバインスタンスにトラフィックを分散することにより、可用性とスケーラビリティを向上させることができる。<br> | |||
NginxやHAProxy等のロードバランサを使用して、以下の機能を実現する。<br> | |||
* ラウンドロビンまたは最小接続数による負荷分散 | |||
* ヘルスチェックによる障害検知と自動切り離し | |||
* セッションの永続化 (スティッキーセッション) | |||
* SSL/TLSターミネーション | |||
<br> | |||
==== コンテナ化とオーケストレーション ==== | |||
DockerやPodman等のコンテナ技術を使用することにより、アプリケーションの移植性と管理性が向上する。<br> | |||
さらに、Kubernetes等のオーケストレーションツールを導入することにより、以下の機能を実現できる。<br> | |||
* 自動スケーリング (Horizontal Pod Autoscaler) | |||
* ローリングアップデートとロールバック | |||
* サービスディスカバリとロードバランシング | |||
* 自己修復機能 (Pod の自動再起動) | |||
<br><br> | |||
== APIドキュメント化 == | |||
APIのドキュメント化は、開発者がAPIを理解して活用するために不可欠な要素である。<br> | |||
適切なドキュメントを提供することにより、APIの利用促進と問い合わせの削減を実現することができる。<br> | |||
<br> | |||
==== Swagger / OpenAPIの使用 ==== | |||
Swagger (現在のOpenAPI Specification) は、RESTful APIを記述するための標準的な仕様である。<br> | |||
この仕様に基づいてAPIを定義することにより、インタラクティブなAPIドキュメントを自動生成することができる。<br> | |||
<br> | |||
Pythonでは、flasgger等のライブラリを使用して、SwaggerドキュメントをFlaskアプリケーションに統合することができる。<br> | |||
pip install flasgger | |||
<br> | |||
<syntaxhighlight lang="python"> | |||
# Swagger統合の例 | |||
from flasgger import Swagger | |||
from flask import Flask, jsonify | |||
app = Flask(__name__) | |||
swagger = Swagger(app) | |||
@app.route('/api/user/<int:user_id>', methods=['GET']) | |||
def get_user(user_id): | |||
""" | |||
ユーザ情報を取得するエンドポイント | |||
--- | |||
parameters: | |||
- name: user_id | |||
in: path | |||
type: integer | |||
required: true | |||
description: ユーザID | |||
responses: | |||
200: | |||
description: ユーザ情報 | |||
schema: | |||
properties: | |||
id: | |||
type: integer | |||
name: | |||
type: string | |||
email: | |||
type: string | |||
404: | |||
description: ユーザが見つかりません | |||
""" | |||
# 実装ロジック | |||
return jsonify({'id': user_id, 'name': 'Example User', 'email': 'user@example.com'}) | |||
</syntaxhighlight> | |||
<br> | |||
==== API Blueprintの使用 ==== | |||
API Blueprintは、マークダウン形式でAPIを記述することができる軽量な仕様である。<br> | |||
技術者以外でも読みやすい形式であり、ドキュメント駆動開発 (Documentation-Driven Development) に適している。<br> | |||
<br> | |||
==== Postmanコレクションの活用 ==== | |||
Postmanコレクションは、APIエンドポイントのリクエスト例とレスポンス例を集約したものである。<br> | |||
開発者はPostmanコレクションをインポートすることにより、即座にAPIのテストと動作確認を行うことができる。<br> | |||
<br> | |||
Postmanでは、コレクションからドキュメントを自動生成する機能も提供されており、<br> | |||
Webページとして公開することにより、他の開発者と共有することが可能である。<br> | |||
<br><br> | |||
== 監視とログ管理 == | |||
APIサーバの監視とログ管理は、システムの健全性を維持して、問題の早期発見と迅速な対応を可能にする重要な要素である。<br> | |||
適切な監視体制を構築することにより、サービスの可用性と信頼性を向上させることができる。<br> | |||
<br> | |||
==== メトリクスの収集と可視化 ==== | |||
PrometheusとGrafanaを組み合わせることにより、APIサーバのメトリクスを収集して可視化することができる。<br> | |||
これにより、リクエスト数、レスポンス時間、エラー率等の重要な指標をリアルタイムで監視することが可能となる。<br> | |||
<br> | |||
主要な監視項目として、以下の指標を追跡することが推奨される。<br> | |||
* リクエスト数とレスポンス時間の推移 | |||
* HTTPステータスコード別の分布 | |||
* CPU使用率とメモリ使用量 | |||
* データベース接続数とクエリ実行時間 | |||
* エラー発生率とアラート | |||
<br> | |||
==== Prometheusによる監視 ==== | |||
[https://github.com/prometheus/prometheus PrometheusのGithub]にアクセスして、Prometheusをインストールする。<br> | |||
ダウンロードしたファイルを解凍する。<br> | |||
tar xf prometheus-<バージョン>.linux-<アーキテクチャ>.tar.gz | |||
cd prometheus-<バージョン>.linux-<アーキテクチャ> | |||
<br> | |||
Prometheus設定ファイルを作成する。<br> | |||
<syntaxhighlight lang="yaml"> | |||
# prometheus.ymlファイル | |||
global: | |||
scrape_interval: 15s | |||
evaluation_interval: 15s | |||
scrape_configs: | |||
- job_name: 'api_server' | |||
static_configs: | |||
- targets: ['localhost:5000'] | |||
metrics_path: '/metrics' | |||
</syntaxhighlight> | |||
<br> | |||
Pythonアプリケーションにメトリクスエンドポイントを追加する。<br> | |||
pip install prometheus-flask-exporter | |||
<br> | |||
<syntaxhighlight lang="python"> | |||
# app.pyファイルにメトリクス設定を追加 | |||
from prometheus_flask_exporter import PrometheusMetrics | |||
app = Flask(__name__) | |||
metrics = PrometheusMetrics(app) | |||
# カスタムメトリクスの定義も可能 | |||
metrics.info('app_info', 'Application info', version='1.0.0') | |||
</syntaxhighlight> | |||
<br> | |||
==== Grafanaによる可視化 ==== | |||
Grafanaをインストールする。<br> | |||
# RHEL | |||
sudo dnf install grafana | |||
# SUSE | |||
sudo zypper install grafana | |||
# Raspberry Pi | |||
sudo apt install apt-transport-https software-properties-common | |||
sudo wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key | |||
echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list | |||
sudo apt update | |||
sudo apt install grafana | |||
<br> | |||
Grafanaサービスを起動して、自動起動を有効にする。<br> | |||
sudo systemctl start grafana-server | |||
sudo systemctl enable grafana-server | |||
<br> | |||
Webブラウザで http://localhost:3000 にアクセスして、Grafanaにログインする。<br> | |||
デフォルトのログイン情報を以下に示す。<br> | |||
* ユーザ名 | |||
*: admin | |||
* パスワード | |||
*: admin | |||
<br> | |||
==== ログ収集と分析 ==== | |||
ログの適切な管理により、システムの動作状況の把握と問題の診断が容易になる。<br> | |||
ELKスタック (Elasticsearch、Logstash、Kibana) またはLoki等を使用することにより、大量のログデータを効率的に収集して分析することができる。<br> | |||
<br> | |||
ログ収集戦略として、以下の項目を実施することが推奨される。<br> | |||
* 構造化ログ (JSON形式) の採用 | |||
* ログレベルの適切な設定 (DEBUG、INFO、WARNING、ERROR、CRITICAL) | |||
* ログローテーションによるディスク容量の管理 | |||
* 機密情報のマスキング | |||
* 分散トレーシングの導入 (マイクロサービス環境の場合) | |||
<br> | |||
==== ログ管理 ==== | |||
ログローテーションを設定する。<br> | |||
<syntaxhighlight lang="text"> | |||
# /etc/logrotate.d/myapiファイル | |||
/var/log/myapi/*.log { | |||
daily | |||
rotate 30 | |||
compress | |||
delaycompress | |||
notifempty | |||
create 0640 myapi_user myapi_group | |||
sharedscripts | |||
postrotate | |||
systemctl reload myapi > /dev/null 2>&1 || true | |||
endscript | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
構造化ログの例を以下に示す。<br> | |||
<syntaxhighlight lang="python"> | |||
# logging_config.pyファイル | |||
import logging | |||
import json | |||
from datetime import datetime | |||
class JSONFormatter(logging.Formatter): | |||
def format(self, record): | |||
log_data = { | |||
'timestamp': datetime.utcnow().isoformat(), | |||
'level': record.levelname, | |||
'message': record.getMessage(), | |||
'module': record.module, | |||
'function': record.funcName, | |||
'line': record.lineno | |||
} | |||
if hasattr(record, 'user_id'): | |||
log_data['user_id'] = record.user_id | |||
if record.exc_info: | |||
log_data['exception'] = self.formatException(record.exc_info) | |||
return json.dumps(log_data) | |||
# ロガーの設定 | |||
def setup_logger(name): | |||
logger = logging.getLogger(name) | |||
logger.setLevel(logging.INFO) | |||
handler = logging.FileHandler('/var/log/myapi/app.log') | |||
handler.setFormatter(JSONFormatter()) | |||
logger.addHandler(handler) | |||
return logger | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
| 982行目: | 1,336行目: | ||
設定を確認する。<br> | 設定を確認する。<br> | ||
sudo ufw status | sudo ufw status | ||
<br><br> | <br><br> | ||
| 1,160行目: | 1,396行目: | ||
test: | test: | ||
runs-on: ubuntu-latest | runs-on: ubuntu-latest | ||
steps: | steps: | ||
- uses: actions/checkout@v2 | - uses: actions/checkout@v2 | ||
- name: Set up Python | - name: Set up Python | ||
uses: actions/setup-python@v2 | uses: actions/setup-python@v2 | ||
with: | with: | ||
python-version: '3.9' | python-version: '3.9' | ||
- name: Install dependencies | - name: Install dependencies | ||
run: | | run: | | ||
| 1,174行目: | 1,410行目: | ||
pip install -r requirements.txt | pip install -r requirements.txt | ||
pip install pytest pytest-cov | pip install pytest pytest-cov | ||
- name: Run tests | - name: Run tests | ||
run: | | run: | | ||
pytest --cov=app tests/ | pytest --cov=app tests/ | ||
deploy: | deploy: | ||
needs: test | needs: test | ||
runs-on: ubuntu-latest | runs-on: ubuntu-latest | ||
if: github.ref == 'refs/heads/main' | if: github.ref == 'refs/heads/main' | ||
steps: | steps: | ||
- name: Deploy to server | - name: Deploy to server | ||
| 1,197行目: | 1,433行目: | ||
pip install -r requirements.txt | pip install -r requirements.txt | ||
sudo systemctl restart myapi | sudo systemctl restart myapi | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
2026年1月3日 (土) 12:21時点における最新版
概要
HTTP APIは、HTTPプロトコルを使用して、クライアントとサーバ間でデータのやり取りを行うインターフェースである。
HTTPメソッド (GET、POST、PUT、DELETE等) を使用して、サーバ上の機能やデータへのアクセスを実現する。
HTTP APIは、必ずしもRESTの設計原則に従う必要はなく、RPC形式やカスタムエンドポイント設計等、柔軟なアプローチが可能である。
用途や要件に応じて、シンプルな関数呼び出し型のAPI、バッチ処理API、ストリーミングAPI等、様々な形態で構築されている。
サーバの実装において、Linuxディストリビューションに必要なプログラム言語とフレームワークをインストールする。
一般的な選択肢として、PythonのFlaskやFastAPI、Node.jsのExpress、RubyのSinatra、JavaのSpring Boot、Go言語のGin等がある。
HTTP APIサーバを構築・運用する場合は、以下に示す事柄に注意する。
- 定期的なバックアップの実施
- セキュリティアップデートの適用
- アクセスログの監視
- レート制限の実装
- 適切なエラーハンドリングとHTTPステータスコードの使用
- データベースの最適化
- APIドキュメントの作成と管理
- タイムアウト設定の最適化
- CORS (Cross-Origin Resource Sharing) の適切な設定
サーバ環境のインストール
まず、システムの更新を行う。
# RHEL sudo dnf update # SUSE sudo zypper update # Raspberry Pi sudo apt update sudo apt upgrade
次に、Linuxサーバに必要な基本環境をインストールする。
# RHEL sudo dnf install curl wget git # SUSE sudo zypper install curl wget git # Raspberry Pi sudo apt install curl wget git build-essential
開発言語とフレームワークの選択
Python + Flaskを使用する場合
Pythonと仮想環境をインストールする。
# RHEL sudo dnf install python3 python3-pip python3-virtualenv # SUSE sudo zypper install python3 python3-pip python3-virtualenv # Raspberry Pi sudo apt install python3 python3-pip python3-venv
Pythonの仮想環境を作成する。
これにより、プロジェクトごとに独立したPython環境を作成することができる。
python3 -m venv <作成する仮想環境のディレクトリ名>
Pythonの仮想環境をアクティベートする。
source venv/bin/activate
必要なPythonライブラリをインストールする。
pip install flask flask-cors requests
※注意
venv/bin/activateスクリプトの設定を~/.profileファイル等に記述することは非推奨である。
activateスクリプトは相対パスで動作するため、フルパスで指定する必要がある。
これは、常に特定の仮想環境がアクティブになってしまうため、他のプロジェクトで異なる仮想環境を使用する場合に問題が発生するためである。
また、シェルのセッションごとに自動的にアクティベートすると、どの環境で作業しているのか理解し難い。
代替案として、以下に示すような方法が推奨される。
以下に示すような方法ならば、必要な時のみ仮想環境をアクティベートすることが可能なため、柔軟な運用が可能になる。
特に、複数のプロジェクトを扱う場合は、"方法 2" や "方法 3" が便利である。
- エイリアスを設定する方法
- ~/.profileファイル等に記述する。
# 方法 1
alias activate-mcpserver="source <activateスクリプトのパス>"
- プロジェクトディレクトリに入った時のみ自動的にアクティベートする方法
- ~/.profileファイル または ~/.zprofileファイル等に記述する。
# 方法 2 (a) : Bash / Zsh
function check_venv()
{
if [ -d "venv" ]; then
if [ -z "$VIRTUAL_ENV" ]; then
source venv/bin/activate
elif [ "$VIRTUAL_ENV" != "$PWD/venv" ]; then
deactivate
source venv/bin/activate
fi
elif [ -n "$VIRTUAL_ENV" ]; then
deactivate
fi
}
function cd()
{
builtin cd "$@"
check_venv
}
# プロンプト表示前に毎回チェック
PROMPT_COMMAND="check_venv${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
# シェル起動時にもチェック
check_venv
- プロジェクトディレクトリに入った時のみ自動的にアクティベートする方法
- ~/.config/fish/config.fishファイル および ~/.config/fish/functions/check_venv.fishファイル に記述する。
# 方法 2 (b) : Fish
# ~/.config/fish/config.fishファイル
check_venv
# ~/.config/fish/functions/check_venv.fishファイル
function check_venv --on-variable PWD --on-event fish_prompt
if test -d "venv"
if test -z "$VIRTUAL_ENV"
source venv/bin/activate.fish
else if test "$VIRTUAL_ENV" != "$PWD/venv"
deactivate
source venv/bin/activate.fish
end
else if test -n "$VIRTUAL_ENV"
deactivate
end
end
function cd
builtin cd $argv
end
- direnvのような専用ツールを使用する方法
- プロジェクトディレクトリの.envrcファイルに記述する。
# 方法 3
layout python3 # Pythonの仮想環境を自動的に管理
Node.js + Expressを使用する場合
Node.jsをインストールする。
Node.jsの詳細なインストール手順は、インストール - Node.jsを参照すること。
プロジェクトディレクトリを作成する。
mkdir <任意のディレクトリ 例 : ~/my-api> cd <作成したディレクトリ 例 : ~/my-api>
プロジェクトに必要なJavaScriptライブラリをインストールする。
npm init -y npm install express body-parser cors helmet
Go言語を使用する場合
Go言語をインストールする。
# RHEL sudo dnf install golang # SUSE sudo zypper install go # Raspberry Pi sudo apt install golang
プロジェクトディレクトリを作成して、Go Modulesを初期化する。
mkdir <任意のディレクトリ 例 : ~/my-api> cd <作成したディレクトリ 例 : ~/my-api> go mod init <モジュール名 例 : myapi>
必要なパッケージをインストールする。
go get github.com/gin-gonic/gin go get github.com/rs/cors
データベースの設定
PostgreSQLを使用する場合
PostgreSQLをインストールする。
# RHEL sudo dnf install postgresql postgresql-server libpq-devel # SUSE sudo zypper install postgresql postgresql-server libpq5 # Raspberry Pi sudo apt install postgresql postgresql-contrib libpq-dev
PostgreSQLデータベースクラスタを初期化する。
# RHEL / SUSE sudo postgresql-setup --initdb # Raspberry Pi # インストール時に自動的に初期化される
PostgreSQLサービスを起動して、自動起動を有効にする。
sudo systemctl start postgresql sudo systemctl enable postgresql
データベースとユーザを作成する。
sudo -u postgres createdb <任意のデータベース名> sudo -u postgres createuser <任意のデータベースユーザ名> sudo -u postgres psql # PostgreSQLプロンプト内で実行 ALTER USER <任意のデータベースユーザ名> WITH ENCRYPTED PASSWORD '<パスワード>'; GRANT ALL PRIVILEGES ON DATABASE <任意のデータベース名> TO <任意のデータベースユーザ名>;
MariaDBを使用する場合
MariaDBのインストール
まず、インストール - MariaDBのページを参照して、MariaDBをインストールする。
MariaDBサービスを起動して、自動起動を有効にする。
sudo systemctl start mariadb sudo systemctl enable mariadb
次に、インストール - MariaDB#MariaDBの設定のページを参照して、MariaDBのセキュリティを設定する。
- rootパスワードの設定
- anonymousユーザの削除
- リモートrootログインの禁止
- テストデータベースの削除
- 権限テーブルの再読み込み
sudo mysql_secure_installation
次に、MariaDBへログインして、データベースの作成、ユーザの作成、権限の設定を行う。
sudo mysql -u root -p # データベースとユーザの作成 CREATE DATABASE <任意のデータベース名>; CREATE USER '<任意のデータベースユーザ名>'@'localhost' IDENTIFIED BY '<データベースユーザのパスワード>'; # 権限の設定 GRANT ALL PRIVILEGES ON <任意のデータベース名>.* TO '<任意のデータベースユーザ名>'@'localhost'; # 設定を反映 FLUSH PRIVILEGES;
MariaDB向けPythonドライバをインストールする場合の例を示す。
pip install mysql-connector-python sqlalchemy mysqlclient # または pip3 install mysql-connector-python sqlalchemy mysqlclient
必要な場合は、MariaDBのパフォーマンスの最適化を行う。
# /etc/my.cnf.d/server.cnfファイル (RHEL/SUSE)
# /etc/mysql/mariadb.conf.d/50-server.cnfファイル (Raspberry Pi)
[mysqld]
# バッファプールサイズ (利用可能メモリの50-70[%]程度)
innodb_buffer_pool_size = 1G
# クエリキャッシュ
query_cache_type = 1
query_cache_size = 128M
# 接続数の設定
max_connections = 150
# スレッドキャッシュ
thread_cache_size = 8
# テーブルオープンキャッシュ
table_open_cache = 2000
# InnoDBの設定
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 16M
バックアップ設定
バックアップスクリプトを作成する。
#!/usr/bin/env sh
BACKUP_DIR="<任意のバックアップディレクトリ 例: /var/backups/mysql>"
BACKUP_FILE="<任意のバックアップファイル名 例: myapi_db>"
DATE=$(date +%Y%m%d_%H%M%S) # 日付を付加する場合
BACKUP_FILE="$BACKUP_DIR/${BACKUP_FILE}_$DATE.sql" # バックアップファイルのパス
# バックアップの作成
# 例: mysqldump -u myapi_user -p myapi_db > $BACKUP_FILE
mysqldump -u <データベースユーザ名> -p<データベースユーザのパスワード> <データベース名> > $BACKUP_FILE
# 圧縮
gzip $BACKUP_FILE
# 30日以上前のバックアップを削除 (オプション)
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete
crontabに登録して、定期的にバックアップを実行する。
crontab -e # 毎日午前2時にバックアップを実行 0 2 * * * /path/to/backup_script.sh
MariaDBのメンテナンス
定期的な実行が推奨されるメンテナンスを行う。
-- テーブルの分析
ANALYZE TABLE items;
-- テーブルの最適化
OPTIMIZE TABLE items;
-- インデックスの使用状況確認
SHOW INDEX FROM items;
-- スロークエリの確認
SHOW VARIABLES LIKE '%slow%';
SHOW VARIABLES LIKE '%long%';
Redisを使用する場合
Redisは、キャッシュやセッション管理、キューイング等に使用されるインメモリデータベースである。
HTTP APIサーバのパフォーマンス向上のために、頻繁にアクセスされるデータのキャッシュとして活用できる。
Redisをインストールする。
# RHEL sudo dnf install redis # SUSE sudo zypper install redis # Raspberry Pi sudo apt install redis-server
Redisサービスを起動して、自動起動を有効にする。
sudo systemctl start redis sudo systemctl enable redis
Redis向けPythonクライアントをインストールする場合の例を示す。
pip install redis
APIの基本
Python + Flaskを使用する場合
以下に示すソースコードはサーバサイドであり、APIサーバ内部で動作する部分である。
したがって、クライアントが直接触れる部分ではなく、APIサーバの内部実装として機能する。
クライアントは以下に示すソースコードを意識することなく、定義されたAPIエンドポイントを通じてデータの操作や機能の実行を行うことが可能となる。
まず、基本的なHTTP APIの実装例を示す。
# 基本的なAPI実装例
from flask import Flask, request, jsonify
app = Flask(__name__)
# 計算処理のエンドポイント例
@app.route('/api/calculate', methods=['POST'])
def calculate():
data = request.get_json()
operation = data.get('operation')
values = data.get('values', [])
if operation == 'sum':
result = sum(values)
elif operation == 'average':
result = sum(values) / len(values) if values else 0
else:
return jsonify({'error': 'Unknown operation'}), 400
return jsonify({'result': result})
# データ取得のエンドポイント例
@app.route('/api/status', methods=['GET'])
def get_status():
return jsonify({'status': 'running', 'version': '1.0.0'})
if __name__ == '__main__':
app.run(debug=True)
次に、エラーハンドリングとロギングを含む、より本格的な実装例を示す。
# app.pyファイル
from flask import Flask, request, jsonify
from flask_cors import CORS
import logging
app = Flask(__name__)
CORS(app) # クロスオリジンリクエストを許可
# ロギング設定
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# ヘルスチェックエンドポイント
@app.route('/health', methods=['GET'])
def health_check():
return jsonify({'status': 'healthy', 'version': '1.0.0'}), 200
# データ処理エンドポイント
@app.route('/api/process', methods=['POST'])
def process_data():
try:
data = request.get_json()
# 入力バリデーション
if not data or 'input' not in data:
return jsonify({'error': 'Invalid input'}), 400
# 処理ロジック
result = perform_processing(data['input'])
logger.info(f"Processing completed for input: {data['input']}")
return jsonify({'result': result}), 200
except Exception as e:
logger.error(f"Error processing data: {str(e)}")
return jsonify({'error': 'Internal server error'}), 500
def perform_processing(input_data):
# 実際の処理ロジックをここに実装
return input_data.upper()
# エラーハンドラ
@app.errorhandler(404)
def not_found(error):
return jsonify({'error': 'Not found'}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({'error': 'Internal server error'}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=False)
データベースを使用する場合の実装例を示す。
# database.pyファイル
# SQLAlchemyを使用したデータベース接続設定
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os
# 環境変数からデータベース接続情報を取得
DB_USER = os.getenv('DB_USER', 'myapi_user')
DB_PASSWORD = os.getenv('DB_PASSWORD', 'your_password')
DB_HOST = os.getenv('DB_HOST', 'localhost')
DB_NAME = os.getenv('DB_NAME', 'myapi_db')
SQLALCHEMY_DATABASE_URL = f"mysql+mysqlconnector://{DB_USER}:{DB_PASSWORD}@{DB_HOST}/{DB_NAME}"
engine = create_engine(
SQLALCHEMY_DATABASE_URL,
pool_size=5,
max_overflow=10,
pool_timeout=30,
pool_recycle=1800
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
# データベースセッションの取得
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Node.js + Expressを使用する場合
// server.jsファイル
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const helmet = require('helmet');
const app = express();
const PORT = process.env.PORT || 3000;
// ミドルウェアの設定
app.use(helmet()); // セキュリティヘッダーの設定
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// ロギングミドルウェア
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
next();
});
// ヘルスチェックエンドポイント
app.get('/health', (req, res) => {
res.json({ status: 'healthy', version: '1.0.0' });
});
// データ処理エンドポイント
app.post('/api/process', (req, res) => {
try {
const { input } = req.body;
if (!input) {
return res.status(400).json({ error: 'Invalid input' });
}
// 処理ロジック
const result = input.toUpperCase();
res.json({ result });
}
catch (error) {
console.error('Error processing data:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
// エラーハンドリングミドルウェア
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal server error' });
});
// サーバ起動
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Go言語を使用する場合
// main.goファイル
package main
import (
"net/http"
"log"
"github.com/gin-gonic/gin"
)
type ProcessRequest struct {
Input string `json:"input" binding:"required"`
}
type ProcessResponse struct {
Result string `json:"result"`
}
func main() {
// Ginルータの初期化
router := gin.Default()
// CORSミドルウェアの設定
router.Use(corsMiddleware())
// ヘルスチェックエンドポイント
router.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "healthy",
"version": "1.0.0",
})
})
// データ処理エンドポイント
router.POST("/api/process", processData)
// サーバ起動
log.Println("Server starting on port 8080")
router.Run(":8080")
}
func processData(c *gin.Context) {
var req ProcessRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}
// 処理ロジック
result := strings.ToUpper(req.Input)
c.JSON(http.StatusOK, ProcessResponse{Result: result})
}
func corsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
認証と認可
HTTP APIにおいて、認証・認可の仕組みは重要なセキュリティ要素となる。
適切な認証方式を選択することにより、APIへの不正アクセスを防止することができる。
代表的な認証方式として、APIキー認証、JWT (JSON Web Token) 認証、Basic認証、OAuth2.0等がある。
それぞれの用途や要件に応じて、最適な認証方式を選択する必要がある。
APIキー認証
以下の例では、Python + FlaskでAPI認証を行っている。
# auth.pyファイル
from flask import request, jsonify
from functools import wraps
import os
# 環境変数からAPIキーを取得
VALID_API_KEYS = os.getenv('API_KEYS', '').split(',')
def require_api_key(f):
@wraps(f)
def decorated_function(*args, **kwargs):
api_key = request.headers.get('X-API-Key')
if not api_key or api_key not in VALID_API_KEYS:
return jsonify({'error': 'Invalid or missing API key'}), 401
return f(*args, **kwargs)
return decorated_function
# 使用例
@app.route('/api/protected', methods=['GET'])
@require_api_key
def protected_endpoint():
return jsonify({'message': 'Access granted'})
JWT認証
JWT認証に必要なライブラリをインストールする。
pip install pyjwt
以下の例では、JWT認証を行っている。
# jwt_auth.pyファイル
import jwt
import datetime
from flask import request, jsonify
from functools import wraps
import os
SECRET_KEY = os.getenv('JWT_SECRET_KEY', 'your-secret-key')
def generate_token(user_id):
payload = {
'user_id': user_id,
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=24),
'iat': datetime.datetime.utcnow()
}
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
return token
def require_jwt(f):
@wraps(f)
def decorated_function(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'error': 'Token is missing'}), 401
try:
# Bearer トークンの形式から実際のトークンを抽出
if token.startswith('Bearer '):
token = token[7:]
payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
request.user_id = payload['user_id']
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Token has expired'}), 401
except jwt.InvalidTokenError:
return jsonify({'error': 'Invalid token'}), 401
return f(*args, **kwargs)
return decorated_function
セキュリティ対策
HTTP APIサーバのセキュリティ対策は、システム全体の安全性を確保するために不可欠である。
適切なセキュリティ対策を実施することにより、不正アクセス、データ漏洩、サービス妨害等の脅威から保護することができる。
主要なセキュリティ対策として、以下の項目を実装する必要がある。
- APIキーまたはトークンベースの認証の実装
- HTTPS通信の設定と強制
- 入力データのバリデーション
- SQLインジェクション対策
- クロスサイトスクリプティング (XSS) 対策
- クロスサイトリクエストフォージェリ (CSRF) 対策
- レート制限によるDDoS攻撃対策
- 適切なCORS設定
入力バリデーション
入力データの検証は、セキュリティの基本的な対策である。
悪意のあるデータやフォーマットの誤ったデータを早期に検出することにより、後続の処理における問題を防止できる。
# validation.pyファイル
from flask import request, jsonify
from functools import wraps
import re
def validate_input(schema):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
data = request.get_json()
for field, rules in schema.items():
if rules.get('required') and field not in data:
return jsonify({'error': f'Missing required field: {field}'}), 400
if field in data:
value = data[field]
# 型チェック
if 'type' in rules and not isinstance(value, rules['type']):
return jsonify({'error': f'Invalid type for field: {field}'}), 400
# 最大長チェック
if 'max_length' in rules and len(str(value)) > rules['max_length']:
return jsonify({'error': f'Field {field} exceeds maximum length'}), 400
# 正規表現チェック
if 'pattern' in rules and not re.match(rules['pattern'], str(value)):
return jsonify({'error': f'Field {field} does not match required pattern'}), 400
return f(*args, **kwargs)
return decorated_function
return decorator
# 使用例
@app.route('/api/user', methods=['POST'])
@validate_input({
'username': {'required': True, 'type': str, 'max_length': 50, 'pattern': r'^[a-zA-Z0-9_]+$'},
'email': {'required': True, 'type': str, 'pattern': r'^[\w\.-]+@[\w\.-]+\.\w+$'}
})
def create_user():
# 処理ロジック
pass
SQLインジェクション対策
SQLAlchemyを使用することにより、パラメータ化されたクエリが自動的に生成されて、SQLインジェクションを防ぐことができる。
直接SQL文字列を構築することは避け、必ずパラメータバインディングを使用する。
# 安全な実装例
from sqlalchemy import text
# 悪い例 (脆弱)
# query = f"SELECT * FROM users WHERE username = '{username}'"
# 良い例 (安全)
query = text("SELECT * FROM users WHERE username = :username")
result = db.session.execute(query, {"username": username})
CORS設定
CORS (Cross-Origin Resource Sharing) を適切に設定することにより、信頼されたドメインからのアクセスのみを許可することができる。
本番環境では、ワイルドカード (*) の使用を避け、具体的なドメインを指定することが推奨される。
# cors_config.pyファイル
from flask_cors import CORS
# 本番環境向けの厳格なCORS設定
CORS(app, resources={
r"/api/*": {
"origins": ["https://example.com", "https://app.example.com"],
"methods": ["GET", "POST", "PUT", "DELETE"],
"allow_headers": ["Content-Type", "Authorization"],
"expose_headers": ["Content-Range", "X-Content-Range"],
"supports_credentials": True,
"max_age": 3600
}
})
レート制限
レート制限を行うことにより、API呼び出しの過度な使用を防止して、サーバリソースを保護することができる。
適切なレート制限の実装により、DDoS攻撃やAPIの乱用を効果的に防ぐことが可能となる。
レート制限に必要なライブラリをインストールする。
pip install flask-limiter
以下の例では、レート制限を行っている。
# rate_limit.pyファイル
from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
# レート制限の設定
limiter = Limiter(
app=app,
key_func=get_remote_address,
default_limits=["200 per day", "50 per hour"],
storage_uri="redis://localhost:6379"
)
# 特定のエンドポイントに対するレート制限
@app.route('/api/data')
@limiter.limit("10 per minute")
def get_data():
return jsonify({'data': 'sample data'})
パフォーマンスとスケーラビリティ
HTTP APIサーバの性能向上と拡張性の確保は、サービス品質を維持するために重要である。
適切な最適化とアーキテクチャ設計により、大量のリクエストに対応可能なシステムを構築することができる。
主要な対策として、以下の項目を検討する必要がある。
キャッシュの実装
頻繁にアクセスされるデータをキャッシュすることにより、データベースへの負荷を軽減して、レスポンス時間を改善することができる。
RedisやMemcached等のインメモリデータストアを活用することが一般的である。
キャッシュ戦略としては、以下の方式を検討する。
- キャッシュアサイド (Cache-Aside) パターン
- アプリケーションがキャッシュの読み書きを制御する方式
- ライトスルー (Write-Through) キャッシュ
- データ書き込み時に同時にキャッシュを更新する方式
- ライトビハインド (Write-Behind) キャッシュ
- 非同期でキャッシュからデータベースへ書き込む方式
データベースの最適化
データベースの性能は、API全体のパフォーマンスに大きく影響する。
以下の最適化手法を実施することが推奨される。
- インデックスの適切な設計と管理
- クエリの最適化とN+1問題の回避
- コネクションプーリングの活用
- クエリキャッシュの利用
- 読み取り専用レプリカの導入
ロードバランサの設置
複数のAPIサーバインスタンスにトラフィックを分散することにより、可用性とスケーラビリティを向上させることができる。
NginxやHAProxy等のロードバランサを使用して、以下の機能を実現する。
- ラウンドロビンまたは最小接続数による負荷分散
- ヘルスチェックによる障害検知と自動切り離し
- セッションの永続化 (スティッキーセッション)
- SSL/TLSターミネーション
コンテナ化とオーケストレーション
DockerやPodman等のコンテナ技術を使用することにより、アプリケーションの移植性と管理性が向上する。
さらに、Kubernetes等のオーケストレーションツールを導入することにより、以下の機能を実現できる。
- 自動スケーリング (Horizontal Pod Autoscaler)
- ローリングアップデートとロールバック
- サービスディスカバリとロードバランシング
- 自己修復機能 (Pod の自動再起動)
APIドキュメント化
APIのドキュメント化は、開発者がAPIを理解して活用するために不可欠な要素である。
適切なドキュメントを提供することにより、APIの利用促進と問い合わせの削減を実現することができる。
Swagger / OpenAPIの使用
Swagger (現在のOpenAPI Specification) は、RESTful APIを記述するための標準的な仕様である。
この仕様に基づいてAPIを定義することにより、インタラクティブなAPIドキュメントを自動生成することができる。
Pythonでは、flasgger等のライブラリを使用して、SwaggerドキュメントをFlaskアプリケーションに統合することができる。
pip install flasgger
# Swagger統合の例
from flasgger import Swagger
from flask import Flask, jsonify
app = Flask(__name__)
swagger = Swagger(app)
@app.route('/api/user/<int:user_id>', methods=['GET'])
def get_user(user_id):
"""
ユーザ情報を取得するエンドポイント
---
parameters:
- name: user_id
in: path
type: integer
required: true
description: ユーザID
responses:
200:
description: ユーザ情報
schema:
properties:
id:
type: integer
name:
type: string
email:
type: string
404:
description: ユーザが見つかりません
"""
# 実装ロジック
return jsonify({'id': user_id, 'name': 'Example User', 'email': 'user@example.com'})
API Blueprintの使用
API Blueprintは、マークダウン形式でAPIを記述することができる軽量な仕様である。
技術者以外でも読みやすい形式であり、ドキュメント駆動開発 (Documentation-Driven Development) に適している。
Postmanコレクションの活用
Postmanコレクションは、APIエンドポイントのリクエスト例とレスポンス例を集約したものである。
開発者はPostmanコレクションをインポートすることにより、即座にAPIのテストと動作確認を行うことができる。
Postmanでは、コレクションからドキュメントを自動生成する機能も提供されており、
Webページとして公開することにより、他の開発者と共有することが可能である。
監視とログ管理
APIサーバの監視とログ管理は、システムの健全性を維持して、問題の早期発見と迅速な対応を可能にする重要な要素である。
適切な監視体制を構築することにより、サービスの可用性と信頼性を向上させることができる。
メトリクスの収集と可視化
PrometheusとGrafanaを組み合わせることにより、APIサーバのメトリクスを収集して可視化することができる。
これにより、リクエスト数、レスポンス時間、エラー率等の重要な指標をリアルタイムで監視することが可能となる。
主要な監視項目として、以下の指標を追跡することが推奨される。
- リクエスト数とレスポンス時間の推移
- HTTPステータスコード別の分布
- CPU使用率とメモリ使用量
- データベース接続数とクエリ実行時間
- エラー発生率とアラート
Prometheusによる監視
PrometheusのGithubにアクセスして、Prometheusをインストールする。
ダウンロードしたファイルを解凍する。
tar xf prometheus-<バージョン>.linux-<アーキテクチャ>.tar.gz cd prometheus-<バージョン>.linux-<アーキテクチャ>
Prometheus設定ファイルを作成する。
# prometheus.ymlファイル
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'api_server'
static_configs:
- targets: ['localhost:5000']
metrics_path: '/metrics'
Pythonアプリケーションにメトリクスエンドポイントを追加する。
pip install prometheus-flask-exporter
# app.pyファイルにメトリクス設定を追加
from prometheus_flask_exporter import PrometheusMetrics
app = Flask(__name__)
metrics = PrometheusMetrics(app)
# カスタムメトリクスの定義も可能
metrics.info('app_info', 'Application info', version='1.0.0')
Grafanaによる可視化
Grafanaをインストールする。
# RHEL sudo dnf install grafana # SUSE sudo zypper install grafana # Raspberry Pi sudo apt install apt-transport-https software-properties-common sudo wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list sudo apt update sudo apt install grafana
Grafanaサービスを起動して、自動起動を有効にする。
sudo systemctl start grafana-server sudo systemctl enable grafana-server
Webブラウザで http://localhost:3000 にアクセスして、Grafanaにログインする。
デフォルトのログイン情報を以下に示す。
- ユーザ名
- admin
- パスワード
- admin
ログ収集と分析
ログの適切な管理により、システムの動作状況の把握と問題の診断が容易になる。
ELKスタック (Elasticsearch、Logstash、Kibana) またはLoki等を使用することにより、大量のログデータを効率的に収集して分析することができる。
ログ収集戦略として、以下の項目を実施することが推奨される。
- 構造化ログ (JSON形式) の採用
- ログレベルの適切な設定 (DEBUG、INFO、WARNING、ERROR、CRITICAL)
- ログローテーションによるディスク容量の管理
- 機密情報のマスキング
- 分散トレーシングの導入 (マイクロサービス環境の場合)
ログ管理
ログローテーションを設定する。
# /etc/logrotate.d/myapiファイル
/var/log/myapi/*.log {
daily
rotate 30
compress
delaycompress
notifempty
create 0640 myapi_user myapi_group
sharedscripts
postrotate
systemctl reload myapi > /dev/null 2>&1 || true
endscript
}
構造化ログの例を以下に示す。
# logging_config.pyファイル
import logging
import json
from datetime import datetime
class JSONFormatter(logging.Formatter):
def format(self, record):
log_data = {
'timestamp': datetime.utcnow().isoformat(),
'level': record.levelname,
'message': record.getMessage(),
'module': record.module,
'function': record.funcName,
'line': record.lineno
}
if hasattr(record, 'user_id'):
log_data['user_id'] = record.user_id
if record.exc_info:
log_data['exception'] = self.formatException(record.exc_info)
return json.dumps(log_data)
# ロガーの設定
def setup_logger(name):
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
handler = logging.FileHandler('/var/log/myapi/app.log')
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)
return logger
HTTPS対応
Let's Encryptの使用
Certbotをインストールする。
# RHEL sudo dnf install ca-certificates python3-certbot python3-certbot-nginx # SUSE sudo zypper install python3-certbot python3-certbot-nginx # Raspberry Pi sudo apt install certbot python3-certbot-nginx
SSL証明書を取得する。
sudo certbot --nginx -d <ドメイン名 例: api.example.com>
証明書の自動更新を設定する。
# crontabに登録 sudo crontab -e # 例 : 毎日午前3時に更新チェックを実行 0 3 * * * certbot renew --quiet
自己署名証明書の使用
開発環境やテスト環境では、自己署名証明書を使用することができる。
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
以下の例では、Pythonアプリケーションで使用している。
# HTTPS対応でFlaskアプリを起動
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, ssl_context=('cert.pem', 'key.pem'))
リバースプロキシの設定
Nginxを使用する場合
Nginxをインストールする。
# RHEL sudo dnf install nginx # SUSE sudo zypper install nginx # Raspberry Pi sudo apt install nginx
Nginx設定ファイルを作成する。
# /etc/nginx/sites-available/myapiファイル (Raspberry Pi)
# または /etc/nginx/conf.d/myapi.confファイル (RHEL/SUSE)
upstream api_backend {
server localhost:5000;
# ロードバランシングの場合
# server localhost:5001;
# server localhost:5002;
}
server {
listen 80;
server_name api.example.com;
# HTTPSへリダイレクト
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.example.com;
# SSL証明書の設定
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
# SSL設定の最適化
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# セキュリティヘッダ
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# ログ設定
access_log /var/log/nginx/api_access.log;
error_log /var/log/nginx/api_error.log;
# プロキシ設定
location / {
proxy_pass http://api_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# タイムアウト設定
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# バッファ設定
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
# 静的ファイルの配信 (必要な場合)
location /static/ {
alias /var/www/api/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Raspberry Piの場合は、設定ファイルのシンボリックリンクを作成する。
sudo ln -s /etc/nginx/sites-available/myapi /etc/nginx/sites-enabled/
設定ファイルの文法チェックを行う。
sudo nginx -t
Nginxサービスを起動して、自動起動を有効にする。
sudo systemctl start nginx sudo systemctl enable nginx
設定を変更した場合は、Nginxをリロードする。
sudo systemctl reload nginx
Systemdサービスファイルの作成
Python + Flaskを使用する場合
# /etc/systemd/system/<任意のサービス名>.serviceファイル
[Unit]
Description=HTTP API Server
After=network.target
[Service]
Type=simple
User=<実行する任意のユーザ名>
Group=<実行する任意のグループ名>
WorkingDirectory=<HTTP APIのプロジェクトディレクトリ 例: /home/myapi_user/my-api>
Environment="PATH=<仮想環境のbinディレクトリ 例: /home/myapi_user/my-api/venv/bin>"
ExecStart=<Python実行ファイルのパス> app.py
# 例: ExecStart=/home/myapi_user/my-api/venv/bin/python app.py
Restart=always
RestartSec=10
# セキュリティ設定
PrivateTmp=true
NoNewPrivileges=true
# ログ設定
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapi
[Install]
WantedBy=multi-user.target
Node.js + Expressを使用する場合
# /etc/systemd/system/<任意のサービス名>.serviceファイル
[Unit]
Description=HTTP API Server (Node.js)
After=network.target
[Service]
Type=simple
User=<実行する任意のユーザ名>
Group=<実行する任意のグループ名>
WorkingDirectory=<HTTP APIのプロジェクトディレクトリ 例: /home/myapi_user/my-api>
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=PORT=3000
# セキュリティ設定
PrivateTmp=true
NoNewPrivileges=true
# ログ設定
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapi-node
[Install]
WantedBy=multi-user.target
サービスファイルを作成した後は、systemdデーモンをリロードして、サービスを起動する。
sudo systemctl daemon-reload sudo systemctl start <サービス名> sudo systemctl enable <サービス名>
サービスの状態を確認する。
sudo systemctl status <サービス名>
ログを確認する。
sudo journalctl -u <サービス名> -f
ファイヤーウォールの設定
Firewalldの場合
sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload
特定のポートを開放する場合は、以下のようにする。
sudo firewall-cmd --permanent --add-port=5000/tcp sudo firewall-cmd --reload
設定を確認する。
sudo firewall-cmd --list-all
UFWの場合
sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable
特定のポートを開放する場合は、以下のようにする。
sudo ufw allow 5000/tcp
設定を確認する。
sudo ufw status
CI/CD環境の構築
GitLabを使用する場合
# .gitlab-ci.ymlファイル
stages:
- test
- build
- deploy
variables:
PYTHON_VERSION: "3.9"
test:
stage: test
image: python:${PYTHON_VERSION}
script:
- pip install -r requirements.txt
- pip install pytest pytest-cov
- pytest --cov=app tests/
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
build:
stage: build
script:
- docker build -t myapi:${CI_COMMIT_SHORT_SHA} .
- docker tag myapi:${CI_COMMIT_SHORT_SHA} myapi:latest
only:
- main
deploy:
stage: deploy
script:
- ssh user@yourserver 'cd /path/to/api && git pull origin main'
- ssh user@yourserver 'cd /path/to/api && source venv/bin/activate && pip install -r requirements.txt'
- ssh user@yourserver 'sudo systemctl restart myapi'
only:
- main
when: manual
GitHub Actionsを使用する場合
# .github/workflows/deploy.ymlファイル
name: Deploy API
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-cov
- name: Run tests
run: |
pytest --cov=app tests/
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /path/to/api
git pull origin main
source venv/bin/activate
pip install -r requirements.txt
sudo systemctl restart myapi