はじめに
データ処理案件とNext.js + FastAPIの構成を作るにあたって、AWSを本格的に使い始めた。
AWSはサービスが多すぎて最初に何を覚えればいいかわからない。「EC2とS3とRDSは聞いたことある」くらいの状態から始めて、実務で必要になったサービスを中心に整理した。PHPエンジニアが最初に覚えるべきサービスという視点でまとめる。
AWSの全体像
まず使う頻度が高いサービスのカテゴリを把握する。
コンピュート:
EC2 — 仮想サーバー(PHPのVPSに相当)
ECS — Dockerコンテナの実行環境
Lambda — サーバーレス関数実行
ストレージ:
S3 — オブジェクトストレージ(ファイル置き場)
EBS — EC2にアタッチするブロックストレージ
データベース:
RDS — マネージドRDBMS(MySQL/PostgreSQL等)
Aurora — AWSのRDS互換高性能DB
DynamoDB— NoSQL
ネットワーク:
VPC — 仮想プライベートネットワーク
ALB — ロードバランサー
Route53 — DNSサービス
CloudFront — CDN
認証・認可:
IAM — ユーザー・ロール・権限の管理
その他よく使う:
CloudWatch — ログ・メトリクス監視
ECR — Dockerイメージのレジストリ
SQS — メッセージキュー
SNS — 通知サービス
Secrets Manager — シークレット管理
全部一度に覚える必要はない。まずIAM・VPC・EC2かECS・S3・RDS・CloudWatchの7つを理解すれば実務の大半は動く。
IAM — 最初に理解すべき概念
AWSで最初に理解すべきなのがIAM(Identity and Access Management)。「誰が何をできるか」を管理する。
IAMの構成要素:
ユーザー — 人間が使うアカウント
グループ — ユーザーをまとめる
ロール — AWSサービスが使う権限(EC2がS3を読む、など)
ポリシー — 何ができるかを定義するJSON
最小権限の原則
// 悪い例 — 全権限を与える(絶対にやらない)
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
// 良い例 — 必要な権限だけ
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
PHPのアプリからAWSのサービスを使うとき、アクセスキーをコードに直書きするのは絶対にNG。EC2やECSにIAMロールをアタッチして、コードは認証情報を直接持たない設計にする。
# 悪い例(アクセスキーを直書き)
import boto3
s3 = boto3.client(
's3',
aws_access_key_id = 'AKIAIOSFODNN7EXAMPLE',
aws_secret_access_key = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
)
# 良い例(IAMロールで自動取得)
import boto3
s3 = boto3.client('s3') # EC2/ECSのロールから自動取得
VPC — ネットワークの基本
VPC(Virtual Private Cloud)はAWS内の仮想ネットワーク。サーバーを「どのネットワークに置くか」を定義する。
VPC(例: 10.0.0.0/16)
├── パブリックサブネット(10.0.1.0/24)
│ ├── ALB(ロードバランサー)
│ └── NAT Gateway
└── プライベートサブネット(10.0.2.0/24)
├── ECS/EC2(アプリサーバー)
└── RDS(データベース)
ルール:
パブリックサブネット → インターネットから直接アクセスできる
プライベートサブネット → インターネットから直接アクセスできない
DBはプライベートサブネットに置く(外から直接叩けないようにする)
PHPを普通のVPSで動かしていたときはこういった分離を意識しなかったが、AWSではネットワーク設計が重要になる。
セキュリティグループ
セキュリティグループ = ファイアウォールのルール
ALBのセキュリティグループ:
インバウンド: 0.0.0.0/0 → 80, 443(全てのIPからHTTP/HTTPS)
アウトバウンド: ECSのSG → 8000(ECSにのみ転送)
ECSのセキュリティグループ:
インバウンド: ALBのSG → 8000(ALBからのみ受け付ける)
アウトバウンド: 0.0.0.0/0 → 443(外部API呼び出しのため)
RDSのセキュリティグループ:
インバウンド: ECSのSG → 5432(ECSからのみ受け付ける)
アウトバウンド: なし
「どこからどこへのアクセスを許可するか」を明示的に設定する。PHPのVPSではファイアウォールを手動で設定していたが、AWSではセキュリティグループで管理する。
S3 — ファイルストレージ
S3(Simple Storage Service)は容量無制限のオブジェクトストレージ。
PHPでの使い方と比較:
PHPのstorage/ → S3バケット
ファイルをPHPサーバーに保存 → S3に保存
公開ファイルをpublic/に置く → S3の署名付きURLで配信
import boto3
s3 = boto3.client('s3', region_name='ap-northeast-1')
# アップロード
s3.upload_file(
'local_file.txt',
'my-bucket',
'path/in/bucket/file.txt'
)
# ダウンロード
s3.download_file(
'my-bucket',
'path/in/bucket/file.txt',
'downloaded_file.txt'
)
# 署名付きURL(一時的なダウンロードURL)
url = s3.generate_presigned_url(
'get_object',
Params = {'Bucket': 'my-bucket', 'Key': 'private/file.pdf'},
ExpiresIn = 3600, # 1時間有効
)
S3のバケットポリシー
// パブリックアクセスをブロックする(デフォルト推奨)
// 署名付きURLで個別にアクセスを許可する
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
EC2 — 仮想サーバー
EC2(Elastic Compute Cloud)はいわゆる仮想サーバー。PHPのVPSに相当する。
# EC2に接続
ssh -i my-key.pem ec2-user@xxx.xxx.xxx.xxx
ただし今はEC2を直接使うより、ECSでDockerコンテナを動かすほうが主流。EC2を使うケースは限られてきた。
EC2を選ぶとき:
✓ 特定のOSやミドルウェアが必要
✓ 長時間実行するプロセス(バッチ処理など)
✓ GPUが必要(機械学習など)
ECSを選ぶとき(現代的な選択):
✓ Dockerで動くアプリ
✓ スケールイン/アウトが必要
✓ マネージドサービスに任せたい
ECS — Dockerコンテナの実行
ECS(Elastic Container Service)はDockerコンテナを実行するサービス。FastAPIのデプロイで実際に使っている。
ECSの構成:
クラスター — コンテナを動かす環境のまとまり
タスク定義 — コンテナの設定(イメージ・CPU・メモリ・環境変数)
サービス — タスクを指定台数起動し続ける
タスク — 実際に動いているコンテナ
// タスク定義(一部)
{
"family": "fastapi-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"containerDefinitions": [
{
"name": "fastapi",
"image": "123456789.dkr.ecr.ap-northeast-1.amazonaws.com/fastapi:latest",
"portMappings": [
{ "containerPort": 8000, "protocol": "tcp" }
],
"environment": [
{ "name": "APP_ENV", "value": "production" }
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123:secret:database-url"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/fastapi",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
Fargate vs EC2起動タイプ
EC2起動タイプ:
自分でEC2インスタンスを管理する
コストが安い(大規模なら)
Fargate:
サーバーレス。インスタンスを管理しなくていい
コストが高め(少量なら)
→ 最初はFargateで始めるのが楽
RDS — マネージドデータベース
RDS(Relational Database Service)はMySQLやPostgreSQLなどのマネージドサービス。
PHPのMySQLと比べて何が違うか:
自前MySQL: サーバーの管理・バックアップ・パッチ当てを自分でやる
RDS: バックアップ・フェイルオーバー・パッチを自動でやってくれる
# Pythonからの接続(FastAPIと組み合わせる場合)
DATABASE_URL = os.environ.get("DATABASE_URL")
# "postgresql://user:pass@rds-endpoint.rds.amazonaws.com:5432/mydb"
engine = create_engine(DATABASE_URL)
RDSのエンドポイント
プライマリエンドポイント: 読み書き両用
リードレプリカのエンドポイント: 読み取り専用(複数レプリカある場合)
RDSはプライベートサブネットに置いて、ECSからのみアクセスできるようにするのが基本。
ALB — ロードバランサー
ALB(Application Load Balancer)はHTTPSの終端とトラフィックの振り分けを担う。
Internet → ALB(HTTPS終端・SSL証明書) → ECS(HTTP)
ALBの主な役割:
✓ HTTPS終端(SSL証明書の管理)
✓ 複数のECSタスクへのトラフィック振り分け
✓ ヘルスチェック(正常なタスクのみに転送)
✓ パスベースルーティング(/api/ → FastAPI、/ → Next.js)
パスベースルーティングの例:
https://example.com/ → Next.js(ECS)
https://example.com/api/* → FastAPI(ECS)
CloudWatch — ログと監視
CloudWatch はAWSのログ収集・メトリクス監視サービス。
# ECSのタスク定義にawslogsドライバーを設定すると
# print()の出力がCloudWatch Logsに自動で送られる
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@app.get("/users")
def list_users():
logger.info("ユーザー一覧を取得")
users = db.query(User).all()
logger.info(f"取得件数: {len(users)}")
return users
# CLIでログを確認
aws logs tail /ecs/fastapi --follow
# 直近のログを取得
aws logs get-log-events \
--log-group-name /ecs/fastapi \
--log-stream-name ecs/fastapi/タスクID
アラートの設定
# CloudWatchアラームをboto3で作成
import boto3
cloudwatch = boto3.client('cloudwatch', region_name='ap-northeast-1')
cloudwatch.put_metric_alarm(
AlarmName = 'HighErrorRate',
ComparisonOperator = 'GreaterThanThreshold',
EvaluationPeriods = 2,
MetricName = '5XXError',
Namespace = 'AWS/ApplicationELB',
Period = 300, # 5分
Statistic = 'Sum',
Threshold = 10,
AlarmDescription = '5xxエラーが10件を超えた',
AlarmActions = ['arn:aws:sns:ap-northeast-1:123:MyTopic'],
)
Secrets Manager — 認証情報の安全な管理
環境変数に直接シークレットを入れるのではなく、Secrets Managerで管理する。
import boto3
import json
def get_secret(secret_name: str) -> dict:
client = boto3.client(
'secretsmanager',
region_name='ap-northeast-1'
)
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
# 使う側
db_secret = get_secret('production/database')
db_url = f"postgresql://{db_secret['username']}:{db_secret['password']}@{db_secret['host']}/{db_secret['dbname']}"
ECSではタスク定義のsecretsでSecrets Managerの値をコンテナの環境変数に注入できる(前述のタスク定義のサンプルを参照)。
ECR — Dockerイメージレジストリ
ECS用のDockerイメージをECR(Elastic Container Registry)に保存する。
# ECRにログイン
aws ecr get-login-password --region ap-northeast-1 | \
docker login --username AWS --password-stdin \
123456789.dkr.ecr.ap-northeast-1.amazonaws.com
# イメージをビルドしてプッシュ
docker build -t fastapi .
docker tag fastapi:latest \
123456789.dkr.ecr.ap-northeast-1.amazonaws.com/fastapi:latest
docker push \
123456789.dkr.ecr.ap-northeast-1.amazonaws.com/fastapi:latest
PHPサーバー構成との比較
PHP(従来型VPS):
Nginx + PHP-FPM → MySQL(同じサーバーまたは別サーバー)
SSH + git pull でデプロイ
ファイルはサーバーのローカルに保存
AWS(モダン構成):
ALB → ECS(FastAPI) → RDS
↓
S3(ファイル)
GitHub Actions → ECR → ECS(CI/CD)
シークレットはSecrets Manager
ログはCloudWatch
最初の1週間で覚えること
Day 1: IAMユーザーとロールの概念
→ アクセスキーを作らずにIAMロールを使う設計を理解する
Day 2: S3の基本操作
→ バケット作成・ファイルのアップロード/ダウンロード・署名付きURL
Day 3: VPCとセキュリティグループ
→ パブリック/プライベートサブネットの違いを理解する
Day 4: RDSの作成と接続
→ PostgreSQLをプライベートサブネットに作ってEC2から接続する
Day 5: ECSでDockerコンテナを動かす
→ ECRにプッシュしてFargateでタスクを動かす
Day 6: ALBの設定
→ ALBの後ろにECSを置いてHTTPS経由でアクセスする
Day 7: CloudWatchでログを確認
→ ECSのログをCloudWatch Logsで見る
まとめ
- IAMはAWSの全サービスの基盤。最初に理解する
- VPCはネットワーク設計。DBはプライベートサブネットに置く
- S3はファイルストレージ。アプリのファイルはS3に保存
- ECSはDockerコンテナの実行環境。EC2より管理が楽
- RDSはマネージドDB。バックアップやフェイルオーバーが自動
- ALBはHTTPS終端とロードバランシング
- CloudWatchはログと監視
- Secrets Managerでシークレットを管理してコードに直書きしない
AWSは最初の設定が多くて大変だが、一度構築したら運用が楽になる部分が多い。「インフラの管理をAWSに任せてアプリの開発に集中できる」というのが実感できるようになるまで少し時間がかかった。