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とは何かを整理した入門記事を先に読むと、アプリ側と基盤側の責任を分けやすくなります。
デプロイ前に用意するもの
- 動作確認済みのFastAPIアプリ
- FastAPIを格納したコンテナイメージ
kubectlで操作できるKubernetesクラスタ- Ingressを使う場合は、クラスタに対応するIngressコントローラ
- CPU使用率でHPAを動かす場合は、resource metricsを取得できる環境
ローカルでは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の違い
- liveness probe:コンテナを再起動すべき状態かを判定します。
- readiness probe:そのPodへ新しい通信を送ってよい状態かを判定します。
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 |
本番公開前のチェックリスト
- 固定したイメージタグを使い、更新対象を識別できる
- livenessとreadinessの目的を分けている
- requestsとlimitsを実測に基づいて調整している
- SecretをGitやログへ残さない運用がある
- IngressのDNSとTLSを確認している
- ログ、メトリクス、通知先を用意している
- ロールバックとデータベース移行の手順を検証している
- HPAの最小数と最大数がクラスタ容量や依存先の上限と矛盾しない
FastAPIをKubernetesで運用する要点
FastAPIをKubernetesへ載せる作業は、YAMLを適用してPodを起動するだけでは終わりません。
Deploymentで実行状態を管理し、ServiceとIngressで通信経路を作り、ConfigMapとSecretで設定を分離し、プローブと監視で異常を捉えられる状態まで整える必要があります。
まずはローカルクラスタで各リソースの役割と障害時の挙動を確かめ、その後にTLS、権限、バックアップ、監視、復旧手順を対象環境に合わせて追加すると、変更点を追いやすくなります。
参考資料
- Kubernetes公式ドキュメント:Deployments
- Kubernetes公式ドキュメント:Service
- Kubernetes公式ドキュメント:Ingress
- Kubernetes公式ドキュメント:ConfigMap
- Kubernetes公式ドキュメント:Secret
- Kubernetes公式ドキュメント:Horizontal Pod Autoscaling
- FastAPI公式ドキュメント:Deployment

