サイトアイコン IT & ライフハックブログ|学びと実践のためのアイデア集

FastAPIをKubernetesへデプロイする方法:Deployment、Service、Ingress、HPAの実践ガイド

green snake

Photo by Pixabay on Pexels.com

FastAPIのコンテナイメージを用意できたら、Kubernetesでは「アプリを動かす」「クラスタ内で接続する」「外部へ公開する」「設定を渡す」「負荷に応じて増減する」という役割を別々のリソースに分けて構成します。

この記事では、単一のFastAPIサービスを題材に、Deployment、Service、Ingress、ConfigMap、Secret、Horizontal Pod Autoscaler(HPA)を組み合わせる手順を解説します。

掲載するYAMLは学習と検証に使う最小例であり、そのまま本番要件を満たす完成品ではありません。

この記事で作る構成

最初に、各リソースが担当する範囲を把握しておくと、設定に問題が起きたときも調査対象を絞れます。

リソース 役割 確認する点
Deployment Podの数とコンテナの実行設定を管理する イメージ、ラベル、リソース量、プローブ
Service 入れ替わるPodに対して安定した接続先を提供する selector、port、targetPort
Ingress HTTPまたはHTTPSの入口と転送ルールを定義する Ingressクラス、ホスト名、パス、TLS
ConfigMap 機密ではない設定値をアプリへ渡す 環境ごとの差分、キー名
Secret パスワードやトークンなどをアプリへ渡す 保存方法、権限、ログへの露出防止
HPA 観測した指標に応じてPod数を調整する metrics API、resource requests、最小数と最大数

本稿では、FastAPIアプリがポート8000で待ち受け、/health/livenessと/health/readinessを返せることを前提にします。

FastAPI自体の採用判断や基本設計から確認したい場合は、FastAPIとは何かを整理した入門記事を先に読むと、アプリ側と基盤側の責任を分けやすくなります。

デプロイ前に用意するもの

ローカルではMinikubeやkindなどを利用できますが、イメージの読み込み方やIngressの有効化手順は環境ごとに異なります。

FastAPIのコンテナイメージを確認する

まず、アプリがコンテナ内で0.0.0.0:8000をリッスンし、非rootユーザーで起動するDockerfileを用意します。

FROM python:3.11-slim

ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

WORKDIR /app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY app ./app

RUN useradd --create-home appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8000

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

実際のプロジェクトでは、依存パッケージの固定、イメージの脆弱性確認、ビルド手順の再現性も確認します。

ビルドからレジストリへの保存、Kubernetesへの反映までを自動化したい場合は、FastAPIとGitHub ActionsによるCI/CD構築も参考になります。

DeploymentでFastAPIを動かす

Deploymentは、稼働させたいPod数と、各Podで動かすコンテナの設定を宣言するリソースです。

次の例では、FastAPIを2 Podで起動し、CPUとメモリのrequestsおよびlimits、liveness probe、readiness probeを設定します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: fastapi-app
  template:
    metadata:
      labels:
        app: fastapi-app
    spec:
      containers:
        - name: fastapi
          image: my-fastapi:1.0.0
          ports:
            - name: http
              containerPort: 8000
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          livenessProbe:
            httpGet:
              path: /health/liveness
              port: http
            initialDelaySeconds: 10
            periodSeconds: 10
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /health/readiness
              port: http
            initialDelaySeconds: 5
            periodSeconds: 5
            failureThreshold: 3

selector.matchLabelsとPodテンプレートのlabelsは一致させます。

requestsはスケジューリングやCPU使用率ベースのHPA計算に使われるため、単なるメモではありません。

例の数値はすべてのアプリに適した推奨値ではないため、負荷試験と実測値を基に調整します。

livenessとreadinessの違い

readinessでは、アプリの起動が完了し、リクエストを処理できるかを確認します。

一方、外部サービスの短い障害までlivenessの失敗にすると、再起動を繰り返して復旧を遅らせる可能性があるため、二つの判定条件は分けて設計します。

ServiceでPodへの接続先を固定する

Serviceは、作成と削除でIPアドレスが変わるPod群に対し、クラスタ内から利用できる安定した接続先を提供します。

apiVersion: v1
kind: Service
metadata:
  name: fastapi-service
spec:
  type: ClusterIP
  selector:
    app: fastapi-app
  ports:
    - name: http
      port: 80
      targetPort: http

selectorはDeploymentが作るPodのラベルと一致させ、targetPortはDeploymentで付けたポート名httpを参照します。

クラスタ内の別Podからは、同じ名前空間であればhttp://fastapi-serviceを接続先として利用できます。

IngressでHTTPの入口を作る

Ingressは、ホスト名やURLパスに基づいて通信をServiceへ転送するルールです。

Ingressリソースだけでは通信を処理できないため、クラスタにはNGINX Ingress Controllerなど、ingressClassNameに対応するコントローラが必要です。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: fastapi-ingress
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: fastapi-service
                port:
                  number: 80

この例では、api.example.com/api以下をfastapi-serviceへ転送します。

パスを書き換えずに転送するため、FastAPI側も/apiを含むURLを処理できるようにします。

URLの先頭部分を削除して転送したい場合はIngressコントローラ固有の設定が必要になるため、利用中のコントローラの資料に合わせて設定します。

本番公開では、DNS、TLS証明書、HTTPからHTTPSへの誘導も別途設計します。

ConfigMapとSecretで設定を分離する

ConfigMapにはログレベルや許可するオリジンなどの非機密設定を置き、環境ごとの差分をコンテナイメージから分離します。

apiVersion: v1
kind: ConfigMap
metadata:
  name: fastapi-config
data:
  APP_NAME: "My FastAPI on Kubernetes"
  ENV: "production"
  LOG_LEVEL: "info"
  CORS_ORIGINS: "https://app.example.com"

Secretは、パスワードやトークンなどの機密値をPodの定義やコンテナイメージから分離するためのリソースです。

apiVersion: v1
kind: Secret
metadata:
  name: fastapi-secret
type: Opaque
stringData:
  SECRET_KEY: "replace-before-apply"
  DATABASE_URL: "replace-before-apply"

stringDataは例を読みやすくするために使っていますが、実際の秘密情報をこのままGitへ保存してはいけません。

Secretのdataで使うbase64は暗号化ではなく、値の表現方法にすぎません。

本番環境では、保存時の暗号化、最小権限のRBAC、外部のSecret管理基盤など、クラスタの運用方針に合う保護策を組み合わせます。

Deploymentから設定を読み込む

# Deploymentのcontainers配下に追加
envFrom:
  - configMapRef:
      name: fastapi-config
  - secretRef:
      name: fastapi-secret

FastAPI側では、これらの値を通常の環境変数として読み取れます。

すべてのキーを一括で取り込む必要がなければ、必要なキーだけを個別に参照すると、アプリが扱う設定の範囲を明示できます。

HPAでPod数を調整する

Horizontal Pod Autoscaler(HPA)は、CPU使用率などの指標を参照し、Deploymentなどのレプリカ数を設定範囲内で調整します。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: fastapi-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: fastapi-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 60

この60はノード全体のCPU使用率ではなく、対象Podに設定したCPU requestに対する平均使用率の目標値です。

対象コンテナにCPU requestがなければ、この方式の利用率を計算できないため、Deployment側のresources.requests.cpuとセットで設定します。

HPAが参照するresource metricsを提供する仕組みもクラスタに必要であり、適用後はkubectl describe hpa fastapi-hpaで取得状況とイベントを確認します。

マニフェストを適用して動作を確認する

ファイルをk8sディレクトリへ保存した場合は、依存する設定から順に適用します。

kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
kubectl apply -f k8s/ingress.yaml
kubectl apply -f k8s/hpa.yaml

適用できたことだけで完了とせず、Podの準備状態、Serviceの接続先、Ingressの割り当て、HPAの指標を順に確認します。

kubectl get deployment,pods,service,ingress,hpa
kubectl rollout status deployment/fastapi-app
kubectl describe pod <pod-name>
kubectl logs deployment/fastapi-app

Ingressの前にアプリとServiceの疎通を確かめたい場合は、一時的なkubectl port-forward service/fastapi-service 8080:80を使い、ローカルからヘルスチェックURLへアクセスします。

更新とロールバックの流れ

新しいイメージタグをDeploymentへ反映すると、Deploymentは設定された更新戦略に従ってPodを段階的に置き換えます。

readiness probeに成功したPodだけをServiceの転送先にすることで、起動途中のPodへ通信が届く事態を避けやすくなります。

kubectl rollout status deployment/fastapi-app
kubectl rollout history deployment/fastapi-app
kubectl rollout undo deployment/fastapi-app

ただし、アプリを以前のバージョンへ戻せても、データベースの変更まで自動で元に戻るわけではありません。

データベース移行を伴う更新では、新旧アプリが同時に動く時間を考慮し、互換性のある変更手順と復旧手順を先に決めます。

ログとメトリクスを運用へつなげる

コンテナのログは標準出力と標準エラーへ出し、クラスタ側のログ収集基盤で検索できる形にすると、Podが入れ替わっても調査を続けやすくなります。

HPAのCPU指標だけでは、APIの遅延、エラー率、依存先の不調までは判断できません。

リクエストを追跡できる構造化ログ、アプリのメトリクス、必要に応じたトレースを組み合わせる考え方は、FastAPIのロギングと監視の実践ガイドで詳しく解説しています。

よくある問題の切り分け

症状 最初に確認する点 確認コマンド
Podが起動しない イメージ名、環境変数、Secret名、リソース不足 kubectl describe pod
Podが再起動を繰り返す アプリのログ、livenessのパスと待機時間 kubectl logs --previous <pod-name>
Serviceへ接続できない Podのreadiness、selectorとlabels、portとtargetPort kubectl get endpointslices
Ingressが404を返す Ingressクラス、host、path、Service名 kubectl describe ingress
HPAの指標が不明になる metrics API、CPU request、対象Deployment名 kubectl describe hpa

本番公開前のチェックリスト

FastAPIをKubernetesで運用する要点

FastAPIをKubernetesへ載せる作業は、YAMLを適用してPodを起動するだけでは終わりません。

Deploymentで実行状態を管理し、ServiceとIngressで通信経路を作り、ConfigMapとSecretで設定を分離し、プローブと監視で異常を捉えられる状態まで整える必要があります。

まずはローカルクラスタで各リソースの役割と障害時の挙動を確かめ、その後にTLS、権限、バックアップ、監視、復旧手順を対象環境に合わせて追加すると、変更点を追いやすくなります。

参考資料

この記事に関連する株式会社greedenの取り組み

FastAPIをKubernetesへ載せる作業は、公開後の監視や改善まで設計して初めて運用になります。株式会社greedenは、Webシステム開発としてAPI開発、クラウド環境の構築、保守と運用改善を一貫して支援しています。
モバイルバージョンを終了