サーバ - REST API

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

REST APIは、HTTPプロトコルを使用して、クライアントとサーバ間でリソースのやり取りを行うインターフェースである。
主なHTTPメソッド (GET、POST、PUT、DELETE) を使用して、リソースの取得、作成、更新、削除等の操作を実現する。

サーバの実装において、Linuxディストリビューションに必要なプログラム言語とフレームワークをインストールする。
一般的な選択肢としては、PythonのFlaskやDjango、Node.jsのExpress、RubyのRails、JavaのSpring Boot等がある。

以下の例では、PythonのFlaskを使用した基本的なREST APIを構築している。

 from flask import Flask, request, jsonify
 
 app = Flask(__name__)
 
 # データストアの代わりとなる簡単なディクショナリ
 books = {} 
 
 @app.route('/api/books', methods=['GET'])
 def get_books():
    return jsonify(books)
 
 @app.route('/api/books', methods=['POST'])
 def add_book():
    book = request.get_json()
    books[len(books) + 1] = book
    return jsonify({'message': 'Book added successfully'})
 
 if __name__ == '__main__':
    app.run(debug=True)


セキュリティでは、APIキーの実装、HTTPS通信の設定、入力データのバリデーション、SQLインジェクション対策等を行う必要がある。
また、認証・認可の仕組みとして、JWTトークンやOAuth2.0の実装も検討する必要がある。

パフォーマンスとスケーラビリティでは、キャッシュの実装 (例: Redis)、データベースの最適化 (インデックス設計等)、ロードバランサの設置が重要である。
また、コンテナ化 (Podman等) やオーケストレーション (Kubernetes) の導入も検討する。

APIのドキュメント化も重要な要素となる。
Swagger / OpenAPIを使用することにより、APIの仕様を明確に定義して、自動的にドキュメントを生成することができる。
これにより、他の開発者がAPIを理解および使用することが容易になる。

モニタリングとログ収集の設定において、PrometheusやGrafanaを使用してメトリクスを収集・可視化して、
ELKスタック (Elasticsearch、Logstash、Kibana) でログを収集・分析することにより、APIの健全性を監視することができる。

REST APIサーバを構築・運用する場合は、以下に示す事柄に注意する。

  • 定期的なバックアップの実施
  • セキュリティアップデートの適用
  • アクセスログの監視
  • レート制限の実装
  • 適切なエラーハンドリング
  • データベースの最適化
  • APIドキュメントの作成と管理



サーバ環境のインストール

まず、システムを更新を行う。

# 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 + FastAPIを使用する場合

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 fastapi uvicorn sqlalchemy


※注意
venv/bin/activateスクリプトを~/.profileファイル等に設定することは非推奨である。

  • activateスクリプトは相対パスで動作するため、フルパスで指定する必要がある。
  • 常に特定の仮想環境がアクティブになってしまうため、他のプロジェクトで異なる仮想環境を使用する場合に問題が発生する。
  • シェルのセッションごとに自動的にアクティベートすると、どの環境で作業しているのか理解しにくい。


代替案として、以下に示すような方法が推奨される。
以下に示すような方法ならば、必要な時のみ仮想環境をアクティベートすることが可能なため、柔軟な運用が可能になる。

特に、複数のプロジェクトを扱う場合は、2番目や3番目の方法が便利である。

 # 方法 1
 # エイリアスを設定する方法 (~/.profileファイル等に記述)
 
 alias activate-myproject='source <activateスクリプトのパス>'


 # 方法 2
 # プロジェクトディレクトリに入った時のみ自動的にアクティベートする方法 (~/.profileファイル等に記述)
 
 function cd()
 {
    builtin cd "$@"
 
    # プロジェクトディレクトリに入った時のみ仮想環境をアクティベート
    if [ -d "venv" ]; then
       # 既に仮想環境がアクティブでない場合のみ
       if [ -z "$VIRTUAL_ENV" ]; then
          source venv/bin/activate
       fi
    fi
 }


 # 方法 3
 # direnvのような専用ツールを使用する方法
 # プロジェクトディレクトリの.envrcファイルに記述
 
 layout python3  # Pythonの仮想環境を自動的に管理


Node.js + Expressを使用する場合

Node.jsをインストールする。
Node.jsの詳細なインストール手順は、インストール_-_Yarnを参照すること。

プロジェクトディレクトリを作成する。

mkdir <任意のディレクトリ  例: ~/my-api>
cd    <作成したディレクトリ  例: ~/my-api>


プロジェクトに必要なJavaScriptライブラリをインストールする。

npm init
npm install express body-parser cors



データベースの設定

PostgreSQLを使用する場合

PostgreSQLをインストールする。

# RHEL
sudo dnf install postgresql libpq5

# SUSE
sudo zypper install postgresql libpq5

# Raspberry Pi
sudo apt install postgresql postgresql-contrib


データベースを作成する。

sudo -u postgres createdb   <任意のデータベース名>
sudo -u postgres createuser <任意のデータベースユーザ名>


MariaDBを使用する場合

MariaDBのインストール

MariaDBをインストールする。

# RHEL
sudo dnf install 

# SUSE
sudo zypper install 

# Raspberry Pi
sudo apt install mariadb-server mariadb-client


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のパフォーマンスの最適化を行う。

 [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 myapi_db > $BACKUP_FILE
 mysqldump -u <データベースユーザ名> <データベース名> > $BACKUP_FILE
 
 # 圧縮
 gzip $BACKUP_FILE
 
 # 30日以上前のバックアップを削除 (オプション)
 find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete


MariaDBのメンテナンス

定期的な実行が推奨されるメンテナンスを行う。

 -- テーブルの分析
 ANALYZE TABLE items;
 
 -- テーブルの最適化
 OPTIMIZE TABLE items;
 
 -- インデックスの使用状況確認
 SHOW INDEX FROM items;
 
 -- スロークエリの確認
 SHOW VARIABLES LIKE '%slow%';
 SHOW VARIABLES LIKE '%long%';



APIの基本的な実装

Python + FastAPIを使用する場合

以下に示すソースコードはサーバサイドであり、APIサーバ内部で動作する部分である。
したがって、クライアントが直接触れる部分ではなく、APIサーバの内部実装として機能する。

クライアントは以下に示すソースコードを意識することなく、定義されたAPIエンドポイントを通じてデータの操作を行うことが可能となる。

PostgresSQLを使用する場合
 # main.pyファイル
 
 from fastapi import FastAPI
 from pydantic import BaseModel
 
 app = FastAPI()
 
 class Item(BaseModel):
    name: str
    description: str = None
 
 @app.get("/items")
 async def read_items():
    return {"items": []}
 
 @app.post("/items")
 async def create_item(item: Item):
    return item


MariaDBを使用する場合

以下の例では、APIサーバがMariaDBと通信するための設定を行う。

  • データベースへの接続設定
  • コネクションプールの管理
  • セッション管理の設定


 # database.py
 # SQLAlchemyを使用したデータベース接続設定
 
 from sqlalchemy import create_engine
 from sqlalchemy.ext.declarative import declarative_base
 from sqlalchemy.orm import sessionmaker
 
 SQLALCHEMY_DATABASE_URL = "mysql+mysqlconnector://myapi_user:your_password@localhost/myapi_db"
 
 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()


以下の例では、データベースのテーブル構造をPythonのクラスとして定義している。

  • データベーステーブルの構造定義
  • SQLAlchemyによるORMマッピング
  • テーブルのカラム定義とその制約


 # models.py
 # モデルの定義例
 
 from sqlalchemy import Column, Integer, String, DateTime
 from sqlalchemy.sql import func
 from database import Base
 
 class Item(Base):
    __tablename__ = "items"
 
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(100), nullable=False)
    description = Column(String(500))
    created_at = Column(DateTime(timezone=True), server_default=func.now())
    updated_at = Column(DateTime(timezone=True), onupdate=func.now())


これらをREST APIサーバとして使用する場合、以下に示すような構成となる。

 # main.py (APIエンドポイントの定義)
 
 from fastapi import FastAPI, Depends
 from sqlalchemy.orm import Session
 from . import models, database
 
 app = FastAPI()
 
 # データベースセッションの依存性注入 (DIコンテナ)
 def get_db():
    db = database.SessionLocal()
    try:
       yield db
    finally:
       db.close()
 
 # APIエンドポイント例
 @app.post("/items/")
 def create_item(name: str, description: str, db: Session = Depends(get_db)):
    db_item = models.Item(name=name, description=description)
    db.add(db_item)
    db.commit()
    db.refresh(db_item)
    return db_item


REST APIサーバの情報を取得する場合は、クライアントからREST APIエンドポイントに対してHTTPリクエストを送信する。

 # クライアントからのAPIリクエスト例
 
 curl -X POST "http://your-server/items/" \
      -H "Content-Type: application/json" \
      -d '{"name": "テスト項目", "description": "説明文"}'



HTTPS対応

Let's Encryptを使用する場合

Certbotをインストールする。

# RHEL
sudo dnf install ca-certificates python3-certbot

# SUSE
sudo zypper install python3-certbot python3-certbot-nginx

# Raspberry Pi
sudo apt install certbot python3-certbot-nginx


SSL証明書を取得する。

sudo certbot --nginx -d <ドメイン名  例: example.com>



リバースプロキシの設定

Nginxを使用する場合

 # /etc/nginx/sites-available/myapiファイル
 
 server {
    listen 80;
    server_name yourdomain.com;
 
    location / {
       proxy_pass http://localhost:8000;
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
    }
 }



Systemdサービスファイルの作成

 # /etc/systemd/system/<任意のサービス名>.serviceファイル
 
 [Unit]
 Description=<サービスファイルの説明>
 After=network.target
 
 [Service]
 User=<実行する任意のユーザ名>
 WorkingDirectory=<REST APIのプロジェクトディレクトリ  例: /home/<ユーザ名>/my-api>
 ExecStart=/<uvicorn実行ファイルのパス> main:app --host <ホスト名またはIPアドレス> --port <ポート番号>
 # 例: ExecStart=/home/myapi_user/my-api/venv/bin/uvicorn main:app --host 0.0.0.0 --port 8000
 Restart=always
 
 [Install]
 WantedBy=multi-user.target



ファイヤーウォールの設定

Firewalldの場合

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https

sudo firewall-cmd --reload


UFWの場合

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

sudo ufw enable



監視とログ管理

PrometheusのGithubにアクセスして、Prometheusをインストールする。
ダウンロードしたファイルを解凍する。

tar xf prometheus-<バージョン>.tar.gz


Grafanaをインストールする。

sudo apt install apt-transport-https
sudo apt install software-properties-common

sudo wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key


MariaDBを使用している場合は、MySQLエクスポータ (Prometheus向け) をインストールする。

sudo apt install prometheus-mysqld-exporter


MySQLエクスポータ (Prometheus向け) の設定ファイルを作成する。

sudo vi /etc/default/prometheus-mysqld-exporter


# /etc/default/prometheus-mysqld-exporterファイル

# 例: DATA_SOURCE_NAME="myapi_user:your_password@(localhost:3306)/"
DATA_SOURCE_NAME="<データベースユーザ名>:<データベースユーザのパスワード>@(localhost:<MariaDBのポート番号>)/"



CI/CD環境の構築

GitLabを使用する場合

 # .gitlab-ci.ymlファイル
 
 stages:
   - test
   - deploy
 
 test:
   stage: test
   script:
     - pip install -r requirements.txt
     - pytest
 
 deploy:
   stage: deploy
   script:
     - ssh user@yourserver 'cd /path/to/api && git pull && systemctl restart myapi'
   only:
     - master