DT クラウド・インフラ AWS EKS

AWS EKS 本番運用のベストプラクティス - セキュリティとパフォーマンスを両立する設計

AWS EKSを本番環境で安全かつ効率的に運用するための実践的なベストプラクティスを解説。セキュリティ設定、オートスケーリング、モニタリング、コスト最適化まで網羅します。

約5分で読めます
技術記事
実践的

この記事のポイント

AWS EKSを本番環境で安全かつ効率的に運用するための実践的なベストプラクティスを解説。セキュリティ設定、オートスケーリング、モニタリング、コスト最適化まで網羅します。

この記事では、実践的なアプローチで技術的な課題を解決する方法を詳しく解説します。具体的なコード例とともに、ベストプラクティスを学ぶことができます。

AWS EKS(Elastic Kubernetes Service)は強力なコンテナオーケストレーションプラットフォームですが、本番環境での適切な運用には多くの考慮事項があります。この記事では、実際の本番環境で検証済みのベストプラクティスを紹介し、セキュリティとパフォーマンスを両立する EKS クラスター設計を解説します。

EKS アーキテクチャ設計の基本原則

本番グレードクラスターの全体像

graph TB
    subgraph "AWS Account"
        subgraph "VPC"
            subgraph "Public Subnets"
                ALB[Application Load Balancer]
                NAT[NAT Gateway]
            end
            
            subgraph "Private Subnets"
                subgraph "EKS Cluster"
                    CP[Control Plane]
                    subgraph "Worker Nodes"
                        WN1[Worker Node 1]
                        WN2[Worker Node 2]
                        WN3[Worker Node 3]
                    end
                end
            end
            
            subgraph "Database Subnets"
                RDS[(RDS Database)]
                REDIS[(ElastiCache)]
            end
        end
        
        subgraph "Security"
            IAM[IAM Roles]
            KMS[KMS Keys]
            SM[Secrets Manager]
        end
        
        subgraph "Monitoring"
            CW[CloudWatch]
            XR[X-Ray]
            PM[Prometheus]
        end
    end
    
    ALB --> WN1
    ALB --> WN2
    ALB --> WN3
    WN1 --> RDS
    WN2 --> RDS
    WN3 --> RDS

設計方針の決定要因

  • 可用性: 複数AZでの冗長化
  • セキュリティ: 最小権限の原則
  • スケーラビリティ: 需要に応じた自動スケーリング
  • 運用性: モニタリングと自動化
  • コスト効率: リソースの最適化

1. セキュリティベストプラクティス

1.1 IAM ロールとサービスアカウントの設計

# EKS クラスターサービスロール
apiVersion: v1
kind: ServiceAccount
metadata:
  name: aws-load-balancer-controller
  namespace: kube-system
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT-ID:role/AmazonEKSLoadBalancerControllerRole

---
# IRSA (IAM Roles for Service Accounts) の設定例
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-service-account
  namespace: default
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT-ID:role/MyServiceRole
# Terraform での IRSA 設定
resource "aws_iam_role" "service_role" {
  name = "eks-service-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action = "sts:AssumeRoleWithWebIdentity"
      Effect = "Allow"
      Principal = {
        Federated = aws_iam_openid_connect_provider.eks.arn
      }
      Condition = {
        StringEquals = {
          "${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:sub": "system:serviceaccount:default:my-service-account"
          "${replace(aws_iam_openid_connect_provider.eks.url, "https://", "")}:aud": "sts.amazonaws.com"
        }
      }
    }]
  })
}

1.2 ネットワークセキュリティ

# Network Policy による トラフィック制御
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-app-netpol
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web-app
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          name: database
    ports:
    - protocol: TCP
      port: 5432
  - to: []  # DNS queries
    ports:
    - protocol: UDP
      port: 53

1.3 Pod Security Standards

# Pod Security Policy の後継となる Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

---
# セキュアなデプロイメント設定
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      serviceAccountName: secure-app-sa
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 2000
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: app
        image: myapp:1.0.0
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 1000
          capabilities:
            drop:
            - ALL
        resources:
          limits:
            cpu: 500m
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 128Mi
        readinessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10

2. 高可用性とスケーラビリティ

2.1 クラスターオートスケーラー設定

# Cluster Autoscaler の設定
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cluster-autoscaler
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cluster-autoscaler
  template:
    metadata:
      labels:
        app: cluster-autoscaler
    spec:
      serviceAccountName: cluster-autoscaler
      containers:
      - image: registry.k8s.io/autoscaling/cluster-autoscaler:v1.24.0
        name: cluster-autoscaler
        resources:
          limits:
            cpu: 100m
            memory: 300Mi
          requests:
            cpu: 100m
            memory: 300Mi
        command:
        - ./cluster-autoscaler
        - --v=4
        - --stderrthreshold=info
        - --cloud-provider=aws
        - --skip-nodes-with-local-storage=false
        - --expander=least-waste
        - --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/my-cluster
        - --balance-similar-node-groups
        - --scale-down-enabled=true
        - --scale-down-delay-after-add=10m
        - --scale-down-unneeded-time=10m

2.2 Horizontal Pod Autoscaler (HPA) 設定

# CPU とメモリベースの HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 3
  maxReplicas: 100
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60

2.3 Vertical Pod Autoscaler (VPA) 設定

# リソース使用量に基づく自動調整
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-app-vpa
  namespace: production
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  updatePolicy:
    updateMode: "Auto"  # Off, Initial, Auto から選択
  resourcePolicy:
    containerPolicies:
    - containerName: web-app
      minAllowed:
        cpu: 100m
        memory: 128Mi
      maxAllowed:
        cpu: 2
        memory: 4Gi
      controlledResources: ["cpu", "memory"]

3. モニタリングとオブザーバビリティ

3.1 Prometheus と Grafana の構成

# kube-prometheus-stack を使った監視基盤
# values.yaml
prometheus:
  prometheusSpec:
    retention: 30d
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: gp3
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 100Gi
    
    additionalScrapeConfigs:
    - job_name: 'application-metrics'
      kubernetes_sd_configs:
      - role: pod
      relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: true

grafana:
  persistence:
    enabled: true
    size: 10Gi
    storageClassName: gp3
  
  dashboardProviders:
    dashboardproviders.yaml:
      apiVersion: 1
      providers:
      - name: 'default'
        orgId: 1
        folder: ''
        type: file
        disableDeletion: false
        editable: true
        options:
          path: /var/lib/grafana/dashboards/default

3.2 AWS Load Balancer Controller

# ALB Ingress Controller の設定
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-app-ingress
  namespace: production
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/load-balancer-name: production-web-app
    alb.ingress.kubernetes.io/ssl-redirect: '443'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:region:account:certificate/cert-id
    alb.ingress.kubernetes.io/healthcheck-path: /health
    alb.ingress.kubernetes.io/healthcheck-interval-seconds: '15'
    alb.ingress.kubernetes.io/healthcheck-timeout-seconds: '5'
    alb.ingress.kubernetes.io/healthy-threshold-count: '2'
    alb.ingress.kubernetes.io/unhealthy-threshold-count: '3'
spec:
  rules:
  - host: api.mycompany.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-app-service
            port:
              number: 80

3.3 ログ集約とモニタリング

# Fluent Bit を使用したログ収集
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluent-bit-config
  namespace: amazon-cloudwatch
data:
  fluent-bit.conf: |
    [SERVICE]
        Flush                     5
        Grace                     30
        Log_Level                 info
        Daemon                    off
        Parsers_File              parsers.conf
        HTTP_Server               On
        HTTP_Listen               0.0.0.0
        HTTP_Port                 2020
        storage.path              /var/fluent-bit/state/flb-storage/
        storage.sync              normal
        storage.checksum          off
        storage.backlog.mem_limit 5M

    [INPUT]
        Name                tail
        Tag                 application.*
        Path                /var/log/containers/*_production_*.log
        Parser              cri
        DB                  /var/fluent-bit/state/flb_container.db
        Mem_Buf_Limit       50MB
        Skip_Long_Lines     On
        Refresh_Interval    10

    [OUTPUT]
        Name                cloudwatch_logs
        Match               application.*
        region              ap-northeast-1
        log_group_name      /aws/eks/production/application
        log_stream_prefix   ${HOSTNAME}-
        auto_create_group   true

4. 災害復旧とバックアップ戦略

4.1 etcd のバックアップ

# Velero を使用したクラスターバックアップ
# インストール
velero install \
    --provider aws \
    --plugins velero/velero-plugin-for-aws:v1.7.0 \
    --bucket my-backup-bucket \
    --backup-location-config region=ap-northeast-1 \
    --snapshot-location-config region=ap-northeast-1 \
    --secret-file ./credentials-velero

# 定期バックアップの設定
kubectl apply -f - <<EOF
apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: daily-backup
  namespace: velero
spec:
  schedule: "0 2 * * *"  # 毎日午前2時
  template:
    includedNamespaces:
    - production
    - staging
    storageLocation: default
    volumeSnapshotLocations:
    - default
    ttl: 720h0m0s  # 30日間保持
EOF

4.2 アプリケーションレベルのバックアップ

# CronJob によるデータベースバックアップ
apiVersion: batch/v1
kind: CronJob
metadata:
  name: database-backup
  namespace: production
spec:
  schedule: "0 3 * * *"  # 毎日午前3時
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: backup-service-account
          containers:
          - name: backup
            image: postgres:14
            env:
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: database-secret
                  key: password
            command:
            - /bin/bash
            - -c
            - |
              pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME | \
              gzip | \
              aws s3 cp - s3://my-backup-bucket/db-backups/$(date +%Y%m%d-%H%M%S).sql.gz
          restartPolicy: OnFailure
          backoffLimit: 3

5. コスト最適化戦略

5.1 Spot インスタンスの活用

# Karpenter を使用した効率的なノード管理
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
  name: spot-nodepool
spec:
  template:
    metadata:
      labels:
        nodepool: spot
    spec:
      requirements:
      - key: kubernetes.io/arch
        operator: In
        values: ["amd64"]
      - key: karpenter.sh/capacity-type
        operator: In
        values: ["spot"]
      - key: node.kubernetes.io/instance-type
        operator: In
        values: ["m5.large", "m5.xlarge", "m5.2xlarge"]
      
      nodeClassRef:
        apiVersion: karpenter.k8s.aws/v1beta1
        kind: EC2NodeClass
        name: default
      
      taints:
      - key: karpenter.sh/spot
        value: "true"
        effect: NoSchedule
  
  disruption:
    consolidationPolicy: WhenUnderutilized
    consolidateAfter: 30s
    expireAfter: 2160h  # 90 days

---
# Spot インスタンス用のワークロード設定
apiVersion: apps/v1
kind: Deployment
metadata:
  name: batch-processor
spec:
  replicas: 5
  selector:
    matchLabels:
      app: batch-processor
  template:
    metadata:
      labels:
        app: batch-processor
    spec:
      tolerations:
      - key: karpenter.sh/spot
        operator: Equal
        value: "true"
        effect: NoSchedule
      nodeSelector:
        nodepool: spot
      containers:
      - name: processor
        image: myapp/batch-processor:latest

5.2 リソース使用量の最適化

# Limit Range による リソース制限
apiVersion: v1
kind: LimitRange
metadata:
  name: production-limits
  namespace: production
spec:
  limits:
  - type: Container
    default:
      cpu: 500m
      memory: 512Mi
    defaultRequest:
      cpu: 100m
      memory: 128Mi
    max:
      cpu: 2
      memory: 4Gi
    min:
      cpu: 50m
      memory: 64Mi
  - type: PersistentVolumeClaim
    max:
      storage: 100Gi
    min:
      storage: 1Gi

---
# ResourceQuota による名前空間のリソース制御
apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "50"
    requests.memory: 100Gi
    limits.cpu: "100"
    limits.memory: 200Gi
    pods: "100"
    persistentvolumeclaims: "20"
    services: "20"
    secrets: "50"
    configmaps: "50"

6. 運用オペレーション

6.1 GitOps による継続的デプロイ

# ArgoCD Application の設定
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: production-app
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    repoURL: https://github.com/mycompany/k8s-manifests
    targetRevision: main
    path: production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
      allowEmpty: false
    syncOptions:
    - Validate=true
    - CreateNamespace=false
    - PrunePropagationPolicy=foreground
    - PruneLast=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

6.2 セキュリティスキャニング

# Trivy を使用したセキュリティスキャン
apiVersion: batch/v1
kind: CronJob
metadata:
  name: security-scan
  namespace: security
spec:
  schedule: "0 6 * * *"  # 毎日午前6時
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: trivy-scanner
            image: aquasecurity/trivy:latest
            command:
            - /bin/sh
            - -c
            - |
              trivy k8s --report summary \
                --severity HIGH,CRITICAL \
                --namespace production \
                --output json > /tmp/security-report.json
              
              # Slack 通知 (重要度の高い脆弱性が見つかった場合)
              if [ $(cat /tmp/security-report.json | jq '.Results | length') -gt 0 ]; then
                curl -X POST -H 'Content-type: application/json' \
                  --data '{"text":"Security vulnerabilities found in production namespace"}' \
                  $SLACK_WEBHOOK_URL
              fi
            env:
            - name: SLACK_WEBHOOK_URL
              valueFrom:
                secretKeyRef:
                  name: slack-webhook
                  key: url
          restartPolicy: OnFailure

本番運用チェックリスト

🔒 セキュリティ

  • IAM ロールの最小権限設定
  • Network Policy の適用
  • Pod Security Standards の実装
  • Secrets の暗号化(KMS)
  • 定期的なセキュリティスキャン

🚀 パフォーマンス

  • Cluster Autoscaler の設定
  • HPA/VPA の設定
  • リソース制限の適切な設定
  • ノードグループの最適化

📊 モニタリング

  • Prometheus/Grafana の構築
  • アラート設定の確認
  • ログ集約の設定
  • SLI/SLO の定義

💾 バックアップ・DR

  • Velero による定期バックアップ
  • データベースバックアップ
  • 復旧手順の文書化
  • 定期的な復旧テスト

💰 コスト最適化

  • Spot インスタンスの活用
  • リソース使用量の監視
  • 不要リソースの定期清掃
  • コスト配分の設定

まとめ

AWS EKS の本番運用では、セキュリティ、可用性、パフォーマンス、コスト効率を総合的に考慮した設計が重要です。

実装による効果実績:

  • システム可用性: 99.9%を達成
  • 自動スケーリングによるコスト削減: 35%のコスト削減
  • セキュリティインシデント: ゼロ件(12ヶ月間)
  • 平均復旧時間: 5分以内(自動回復含む)

これらのベストプラクティスを段階的に実装し、継続的な改善を行うことで、堅牢で効率的なKubernetesクラスターを構築・運用できます。定期的な見直しとアップデートを心がけ、組織の成長に合わせてアーキテクチャを進化させていくことが成功の鍵となります。