MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
サーバ - HTTP APIのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
サーバ - HTTP API
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == HTTP APIは、HTTPプロトコルを使用して、クライアントとサーバ間でデータのやり取りを行うインターフェースである。<br> HTTPメソッド (GET、POST、PUT、DELETE等) を使用して、サーバ上の機能やデータへのアクセスを実現する。<br> <br> HTTP APIは、必ずしもRESTの設計原則に従う必要はなく、RPC形式やカスタムエンドポイント設計等、柔軟なアプローチが可能である。<br> 用途や要件に応じて、シンプルな関数呼び出し型のAPI、バッチ処理API、ストリーミングAPI等、様々な形態で構築されている。<br> <br> サーバの実装において、Linuxディストリビューションに必要なプログラム言語とフレームワークをインストールする。<br> 一般的な選択肢として、PythonのFlaskやFastAPI、Node.jsのExpress、RubyのSinatra、JavaのSpring Boot、Go言語のGin等がある。<br> <br> 以下の例では、PythonのFlaskを使用した基本的なHTTP APIを構築している。<br> <syntaxhighlight lang="python"> 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> セキュリティでは、APIキーの実装、HTTPS通信の設定、入力データのバリデーション、SQLインジェクション対策等を行う必要がある。<br> また、認証・認可の仕組みとして、JWTトークン、Basic認証、OAuth2.0の実装も検討する必要がある。<br> <br> パフォーマンスとスケーラビリティでは、キャッシュの実装 (例: Redis、Memcached)、データベースの最適化 (インデックス設計等)、ロードバランサの設置が重要である。<br> また、コンテナ化 (Docker、Podman等) やオーケストレーション (Kubernetes) の導入も検討する。<br> <br> APIのドキュメント化も重要な要素となる。<br> Swagger / OpenAPI、API Blueprint、Postmanコレクション等を使用することにより、APIの仕様を明確に定義して、自動的にドキュメントを生成することができる。<br> これにより、他の開発者がAPIを理解および使用することが容易になる。<br> <br> モニタリングとログ収集の設定において、PrometheusやGrafanaを使用してメトリクスを収集・可視化して、<br> ELKスタック (Elasticsearch、Logstash、Kibana) またはLoki等でログを収集・分析することにより、APIの健全性を監視することができる。<br> <br> HTTP APIサーバを構築・運用する場合は、以下に示す事柄に注意する。<br> * 定期的なバックアップの実施 * セキュリティアップデートの適用 * アクセスログの監視 * レート制限の実装 * 適切なエラーハンドリングとHTTPステータスコードの使用 * データベースの最適化 * APIドキュメントの作成と管理 * タイムアウト設定の最適化 * CORS (Cross-Origin Resource Sharing) の適切な設定 <br><br> == サーバ環境のインストール == まず、システムの更新を行う。<br> # RHEL sudo dnf update # SUSE sudo zypper update # Raspberry Pi sudo apt update sudo apt upgrade <br> 次に、Linuxサーバに必要な基本環境をインストールする。<br> # RHEL sudo dnf install curl wget git # SUSE sudo zypper install curl wget git # Raspberry Pi sudo apt install curl wget git build-essential <br><br> == 開発言語とフレームワークの選択 == ==== Python + Flaskを使用する場合 ==== Pythonと仮想環境をインストールする。<br> # 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 <br> Pythonの仮想環境を作成する。<br> これにより、プロジェクトごとに独立したPython環境を作成することができる。<br> python3 -m venv <作成する仮想環境のディレクトリ名> <br> Pythonの仮想環境をアクティベートする。<br> source venv/bin/activate <br> 必要なPythonライブラリをインストールする。<br> pip install flask flask-cors requests <br> <u>※注意</u><br> <u>venv/bin/activateスクリプトの設定を~/.profileファイル等に記述することは非推奨である。</u><br> <br> <u>activateスクリプトは相対パスで動作するため、フルパスで指定する必要がある。</u><br> <u>これは、常に特定の仮想環境がアクティブになってしまうため、他のプロジェクトで異なる仮想環境を使用する場合に問題が発生するためである。</u><br> <br> <u>また、シェルのセッションごとに自動的にアクティベートすると、どの環境で作業しているのか理解し難い。</u> <br> 代替案として、以下に示すような方法が推奨される。<br> 以下に示すような方法ならば、必要な時のみ仮想環境をアクティベートすることが可能なため、柔軟な運用が可能になる。<br> <br> 特に、複数のプロジェクトを扱う場合は、"方法 2" や "方法 3" が便利である。<br> <br> * エイリアスを設定する方法 *: ~/.profileファイル等に記述する。 <syntaxhighlight lang="sh"> # 方法 1 alias activate-mcpserver="source <activateスクリプトのパス>" </syntaxhighlight> <br> * プロジェクトディレクトリに入った時のみ自動的にアクティベートする方法 *: ~/.profileファイル または ~/.zprofileファイル等に記述する。 <syntaxhighlight lang="sh"> # 方法 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 </syntaxhighlight> <br> * プロジェクトディレクトリに入った時のみ自動的にアクティベートする方法 *: ~/.config/fish/config.fishファイル および ~/.config/fish/functions/check_venv.fishファイル に記述する。 <syntaxhighlight lang="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 </syntaxhighlight> <br> * direnvのような専用ツールを使用する方法 *: プロジェクトディレクトリの.envrcファイルに記述する。 <syntaxhighlight lang="sh"> # 方法 3 layout python3 # Pythonの仮想環境を自動的に管理 </syntaxhighlight> <br> ==== Node.js + Expressを使用する場合 ==== Node.jsをインストールする。<br> Node.jsの詳細なインストール手順は、[[インストール_-_Node.js|インストール - Node.js]]を参照すること。<br> <br> プロジェクトディレクトリを作成する。<br> mkdir <任意のディレクトリ 例 : ~/my-api> cd <作成したディレクトリ 例 : ~/my-api> <br> プロジェクトに必要なJavaScriptライブラリをインストールする。<br> npm init -y npm install express body-parser cors helmet <br> ==== Go言語を使用する場合 ==== Go言語をインストールする。<br> # RHEL sudo dnf install golang # SUSE sudo zypper install go # Raspberry Pi sudo apt install golang <br> プロジェクトディレクトリを作成して、Go Modulesを初期化する。<br> mkdir <任意のディレクトリ 例 : ~/my-api> cd <作成したディレクトリ 例 : ~/my-api> go mod init <モジュール名 例 : myapi> <br> 必要なパッケージをインストールする。<br> go get github.com/gin-gonic/gin go get github.com/rs/cors <br><br> == データベースの設定 == ==== PostgreSQLを使用する場合 ==== PostgreSQLをインストールする。<br> # 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 <br> PostgreSQLデータベースクラスタを初期化する。<br> # RHEL / SUSE sudo postgresql-setup --initdb # Raspberry Pi # インストール時に自動的に初期化される <br> PostgreSQLサービスを起動して、自動起動を有効にする。<br> sudo systemctl start postgresql sudo systemctl enable postgresql <br> データベースとユーザを作成する。<br> sudo -u postgres createdb <任意のデータベース名> sudo -u postgres createuser <任意のデータベースユーザ名> sudo -u postgres psql # PostgreSQLプロンプト内で実行 ALTER USER <任意のデータベースユーザ名> WITH ENCRYPTED PASSWORD '<パスワード>'; GRANT ALL PRIVILEGES ON DATABASE <任意のデータベース名> TO <任意のデータベースユーザ名>; <br> ==== MariaDBを使用する場合 ==== ===== MariaDBのインストール ===== まず、[[インストール_-_MariaDB|インストール - MariaDBのページ]]を参照して、MariaDBをインストールする。<br> <br> MariaDBサービスを起動して、自動起動を有効にする。<br> sudo systemctl start mariadb sudo systemctl enable mariadb <br> 次に、[[インストール - MariaDB#MariaDBの設定のページ]]を参照して、MariaDBのセキュリティを設定する。<br> * rootパスワードの設定 * anonymousユーザの削除 * リモートrootログインの禁止 * テストデータベースの削除 * 権限テーブルの再読み込み <br> sudo mysql_secure_installation <br> 次に、MariaDBへログインして、データベースの作成、ユーザの作成、権限の設定を行う。<br> sudo mysql -u root -p # データベースとユーザの作成 CREATE DATABASE <任意のデータベース名>; CREATE USER '<任意のデータベースユーザ名>'@'localhost' IDENTIFIED BY '<データベースユーザのパスワード>'; # 権限の設定 GRANT ALL PRIVILEGES ON <任意のデータベース名>.* TO '<任意のデータベースユーザ名>'@'localhost'; # 設定を反映 FLUSH PRIVILEGES; <br> MariaDB向けPythonドライバをインストールする場合の例を示す。<br> pip install mysql-connector-python sqlalchemy mysqlclient # または pip3 install mysql-connector-python sqlalchemy mysqlclient <br> 必要な場合は、MariaDBのパフォーマンスの最適化を行う。<br> <syntaxhighlight lang="ini"> # /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 </syntaxhighlight> <br> ===== バックアップ設定 ===== バックアップスクリプトを作成する。<br> <br> <syntaxhighlight lang="sh"> #!/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 </syntaxhighlight> <br> crontabに登録して、定期的にバックアップを実行する。<br> crontab -e # 毎日午前2時にバックアップを実行 0 2 * * * /path/to/backup_script.sh <br> ===== MariaDBのメンテナンス ===== 定期的な実行が推奨されるメンテナンスを行う。<br> <br> <syntaxhighlight lang="mysql"> -- テーブルの分析 ANALYZE TABLE items; -- テーブルの最適化 OPTIMIZE TABLE items; -- インデックスの使用状況確認 SHOW INDEX FROM items; -- スロークエリの確認 SHOW VARIABLES LIKE '%slow%'; SHOW VARIABLES LIKE '%long%'; </syntaxhighlight> <br> ==== Redisを使用する場合 ==== Redisは、キャッシュやセッション管理、キューイング等に使用されるインメモリデータベースである。<br> HTTP APIサーバのパフォーマンス向上のために、頻繁にアクセスされるデータのキャッシュとして活用できる。<br> <br> Redisをインストールする。<br> # RHEL sudo dnf install redis # SUSE sudo zypper install redis # Raspberry Pi sudo apt install redis-server <br> Redisサービスを起動して、自動起動を有効にする。<br> sudo systemctl start redis sudo systemctl enable redis <br> Redis向けPythonクライアントをインストールする場合の例を示す。<br> pip install redis <br><br> == APIの基本 == ==== Python + Flaskを使用する場合 ==== 以下に示すソースコードはサーバサイドであり、APIサーバ内部で動作する部分である。<br> したがって、クライアントが直接触れる部分ではなく、APIサーバの内部実装として機能する。<br> <br> クライアントは以下に示すソースコードを意識することなく、定義されたAPIエンドポイントを通じてデータの操作や機能の実行を行うことが可能となる。<br> <br> <syntaxhighlight lang="python"> # 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) </syntaxhighlight> <br> データベースを使用する場合の実装例を示す。<br> <syntaxhighlight lang="python"> # 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() </syntaxhighlight> <br> ==== Node.js + Expressを使用する場合 ==== <syntaxhighlight lang="javascript"> // 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}`); }); </syntaxhighlight> <br> ==== Go言語を使用する場合 ==== <syntaxhighlight lang="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() } } </syntaxhighlight> <br><br> == 認証と認可の実装 == ==== APIキー認証を実装する場合 ==== Python + Flaskでの実装例を示す。<br> <syntaxhighlight lang="python"> # 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'}) </syntaxhighlight> <br> ==== JWT認証を実装する場合 ==== 必要なライブラリをインストールする。<br> pip install pyjwt <br> JWT認証の実装例を示す。<br> <syntaxhighlight lang="python"> # 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 </syntaxhighlight> <br><br> == レート制限の実装 == レート制限を実装することにより、API呼び出しの過度な使用を防止して、サーバリソースを保護することができる。<br> <br> 必要なライブラリをインストールする。<br> pip install flask-limiter <br> 以下の例では、レート制限を行っている。<br> <syntaxhighlight lang="python"> # 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'}) </syntaxhighlight> <br><br> == HTTPS対応 == ==== Let's Encryptを使用する場合 ==== Certbotをインストールする。<br> # 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 <br> SSL証明書を取得する。<br> sudo certbot --nginx -d <ドメイン名 例: api.example.com> <br> 証明書の自動更新を設定する。<br> # crontabに登録 sudo crontab -e # 例 : 毎日午前3時に更新チェックを実行 0 3 * * * certbot renew --quiet <br> ==== 自己署名証明書を使用する場合 ==== 開発環境やテスト環境では、自己署名証明書を使用することができる。<br> <br> openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes <br> Pythonアプリケーションで使用する場合の例を示す。<br> <syntaxhighlight lang="python"> # HTTPS対応でFlaskアプリを起動 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000, ssl_context=('cert.pem', 'key.pem')) </syntaxhighlight> <br><br> == リバースプロキシの設定 == ==== Nginxを使用する場合 ==== Nginxをインストールする。<br> # RHEL sudo dnf install nginx # SUSE sudo zypper install nginx # Raspberry Pi sudo apt install nginx <br> Nginx設定ファイルを作成する。<br> <syntaxhighlight lang="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"; } } </syntaxhighlight> <br> Raspberry Piの場合は、設定ファイルのシンボリックリンクを作成する。<br> sudo ln -s /etc/nginx/sites-available/myapi /etc/nginx/sites-enabled/ <br> 設定ファイルの文法チェックを行う。<br> sudo nginx -t <br> Nginxサービスを起動して、自動起動を有効にする。<br> sudo systemctl start nginx sudo systemctl enable nginx <br> 設定を変更した場合は、Nginxをリロードする。<br> sudo systemctl reload nginx <br><br> == Systemdサービスファイルの作成 == ==== Python + Flaskを使用する場合 ==== <syntaxhighlight lang="ini"> # /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 </syntaxhighlight> <br> ==== Node.js + Expressを使用する場合 ==== <syntaxhighlight lang="ini"> # /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 </syntaxhighlight> <br> サービスファイルを作成した後は、systemdデーモンをリロードして、サービスを起動する。<br> sudo systemctl daemon-reload sudo systemctl start <サービス名> sudo systemctl enable <サービス名> <br> サービスの状態を確認する。<br> sudo systemctl status <サービス名> <br> ログを確認する。<br> sudo journalctl -u <サービス名> -f <br><br> == ファイヤーウォールの設定 == ==== Firewalldの場合 ==== sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --permanent --add-service=https sudo firewall-cmd --reload <br> 特定のポートを開放する場合は、以下のようにする。<br> sudo firewall-cmd --permanent --add-port=5000/tcp sudo firewall-cmd --reload <br> 設定を確認する。<br> sudo firewall-cmd --list-all <br> ==== UFWの場合 ==== sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable <br> 特定のポートを開放する場合は、以下のようにする。<br> sudo ufw allow 5000/tcp <br> 設定を確認する。<br> sudo ufw status <br><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> デフォルトのログイン情報は、ユーザ名: admin、パスワード: admin である。<br> <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> <br><br> == CI/CD環境の構築 == ==== GitLabを使用する場合 ==== <syntaxhighlight lang="yaml"> # .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 </syntaxhighlight> <br> ==== GitHub Actionsを使用する場合 ==== <syntaxhighlight lang="yaml"> # .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 </syntaxhighlight> <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> <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設定 ==== <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> <br><br> {{#seo: |title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki |keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板 |description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux |image=/resources/assets/MochiuLogo_Single_Blue.png }} __FORCETOC__ [[カテゴリ:RHEL]][[カテゴリ:SUSE]][[カテゴリ:Raspberry_Pi]][[カテゴリ:Web]]
サーバ - HTTP API
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse