EXERCISE 60分

Mission 1: 本番用 Deployment マニフェストの作成

ECサイトの注文APIサービスの本番用 Deployment マニフェストを作成してください。

要件:

  • レプリカ数: 4(最小3、最大12のHPA付き)
  • Pod Anti-Affinity: 同じノードに同じPodを配置しない
  • Topology Spread: AZ間で均等に分散
  • Init Container: DBマイグレーション実行
  • Security Context: Restricted レベル準拠
  • Resource requests/limits 設定
  • Readiness / Liveness / Startup Probe 設定
  • Graceful Shutdown: terminationGracePeriodSeconds 設定
解答例
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: production
  labels:
    app: order-service
    version: v2.1.0
spec:
  replicas: 4
  revisionHistoryLimit: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1
      maxSurge: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
        version: v2.1.0
    spec:
      serviceAccountName: order-service
      terminationGracePeriodSeconds: 45
      securityContext:
        runAsNonRoot: true
        seccompProfile:
          type: RuntimeDefault
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values: ["order-service"]
              topologyKey: kubernetes.io/hostname
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: order-service
      initContainers:
        - name: db-migration
          image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/order-service:v2.1.0
          command: ["npx", "prisma", "migrate", "deploy"]
          envFrom:
            - secretRef:
                name: order-db-credentials
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            runAsUser: 1000
            capabilities:
              drop: ["ALL"]
      containers:
        - name: order-service
          image: 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/order-service:v2.1.0
          ports:
            - containerPort: 3000
              name: http
          env:
            - name: NODE_ENV
              value: "production"
            - name: LOG_LEVEL
              value: "info"
          envFrom:
            - configMapRef:
                name: order-service-config
            - secretRef:
                name: order-db-credentials
          startupProbe:
            httpGet:
              path: /health
              port: http
            failureThreshold: 30
            periodSeconds: 2
          readinessProbe:
            httpGet:
              path: /ready
              port: http
            initialDelaySeconds: 5
            periodSeconds: 10
            successThreshold: 1
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /health
              port: http
            initialDelaySeconds: 15
            periodSeconds: 20
            failureThreshold: 3
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              memory: 512Mi
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            runAsUser: 1000
            runAsGroup: 1000
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: tmp
          emptyDir:
            sizeLimit: 100Mi
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: order-service
  namespace: production
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: order-service
  minReplicas: 3
  maxReplicas: 12
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 300

Mission 2: RBAC 設計

以下のチーム構成に対して適切な RBAC を設計してください。

チーム構成:

  • Platform Team: クラスタ全体の管理、全Namespaceへのフルアクセス
  • Backend Team: backend Namespace の Deployment/Service/ConfigMap の管理、Pod のログ閲覧
  • Data Team: data Namespace の StatefulSet/Job/CronJob の管理、PVC の管理
  • QA Team: staging Namespace のリソース読み取りのみ、Pod の exec は不可
解答例
# --- Platform Team ---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: platform-team-admin
subjects:
  - kind: Group
    name: platform-team
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
---
# --- Backend Team ---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: backend-developer
  namespace: backend
rules:
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: [""]
    resources: ["services", "configmaps"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: backend-developer-binding
  namespace: backend
subjects:
  - kind: Group
    name: backend-team
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: backend-developer
  apiGroup: rbac.authorization.k8s.io
---
# --- Data Team ---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: data-engineer
  namespace: data
rules:
  - apiGroups: ["apps"]
    resources: ["statefulsets"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: ["batch"]
    resources: ["jobs", "cronjobs"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: data-engineer-binding
  namespace: data
subjects:
  - kind: Group
    name: data-team
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: data-engineer
  apiGroup: rbac.authorization.k8s.io
---
# --- QA Team ---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: qa-viewer
  namespace: staging
rules:
  - apiGroups: ["", "apps", "batch", "networking.k8s.io"]
    resources: ["*"]
    verbs: ["get", "list", "watch"]
  # pods/exec は明示的に含めない → exec 不可
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: qa-viewer-binding
  namespace: staging
subjects:
  - kind: Group
    name: qa-team
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: qa-viewer
  apiGroup: rbac.authorization.k8s.io

Mission 3: Ingress と Gateway API の設計

以下のマイクロサービスに対するルーティングを設計してください。

サービス構成:

  • api.example.com/v1/* → api-v1 Service
  • api.example.com/v2/* → api-v2 Service(Canary: 20%のトラフィック)
  • web.example.com/* → web-frontend Service
  • admin.example.com/* → admin Service(IP制限あり: 203.0.113.0/24 のみ)

要件:

  • TLS終端
  • レート制限: api は 100 req/s
  • Gateway API を使用
解答例
# Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: main-gateway
  namespace: gateway-system
spec:
  gatewayClassName: nginx
  listeners:
    - name: https-api
      hostname: api.example.com
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - name: api-tls
    - name: https-web
      hostname: web.example.com
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - name: web-tls
    - name: https-admin
      hostname: admin.example.com
      port: 443
      protocol: HTTPS
      tls:
        mode: Terminate
        certificateRefs:
          - name: admin-tls
---
# API Routes (Canary)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-routes
  namespace: production
spec:
  parentRefs:
    - name: main-gateway
      namespace: gateway-system
  hostnames:
    - api.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /v1
      backendRefs:
        - name: api-v1
          port: 80
    - matches:
        - path:
            type: PathPrefix
            value: /v2
      backendRefs:
        - name: api-v1
          port: 80
          weight: 80
        - name: api-v2
          port: 80
          weight: 20
---
# Web Routes
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: web-routes
  namespace: production
spec:
  parentRefs:
    - name: main-gateway
      namespace: gateway-system
  hostnames:
    - web.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: web-frontend
          port: 80
---
# Admin Routes (NetworkPolicy でIP制限)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: admin-routes
  namespace: production
spec:
  parentRefs:
    - name: main-gateway
      namespace: gateway-system
  hostnames:
    - admin.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: admin-service
          port: 80
---
# IP制限はNetworkPolicyで実装
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: admin-ip-restriction
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: admin-service
  policyTypes:
    - Ingress
  ingress:
    - from:
        - ipBlock:
            cidr: 203.0.113.0/24
      ports:
        - port: 80

Mission 4: シークレット管理の設計

以下の秘密情報を安全に管理する仕組みを設計してください。

管理対象:

  • データベースの接続情報(ユーザー名、パスワード、ホスト)
  • 外部API キー(Stripe、SendGrid)
  • TLS 証明書

要件:

  • Git リポジトリにシークレットをコミットしない
  • シークレットのローテーションに対応
  • 監査ログが取得できる
  • 開発/ステージング/本番で異なる値を使用

External Secrets Operator と AWS Secrets Manager を使用した設計を記述してください。

解答例
# 1. ClusterSecretStore の設定
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: aws-secrets-manager
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-1
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets
---
# 2. DB認証情報(環境ごとに異なるキー)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h  # 1時間ごとにローテーション対応
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: db-credentials
    creationPolicy: Owner
    template:
      engineVersion: v2
      data:
        DATABASE_URL: "postgres://{{ .username }}:{{ .password }}@{{ .host }}:5432/{{ .dbname }}"
  data:
    - secretKey: username
      remoteRef:
        key: production/database/credentials
        property: username
    - secretKey: password
      remoteRef:
        key: production/database/credentials
        property: password
    - secretKey: host
      remoteRef:
        key: production/database/credentials
        property: host
    - secretKey: dbname
      remoteRef:
        key: production/database/credentials
        property: dbname
---
# 3. 外部APIキー
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: external-api-keys
  namespace: production
spec:
  refreshInterval: 30m
  secretStoreRef:
    name: aws-secrets-manager
    kind: ClusterSecretStore
  target:
    name: external-api-keys
  data:
    - secretKey: STRIPE_SECRET_KEY
      remoteRef:
        key: production/stripe
        property: secret_key
    - secretKey: SENDGRID_API_KEY
      remoteRef:
        key: production/sendgrid
        property: api_key
---
# 4. TLS証明書はcert-managerで管理
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: api-tls
  namespace: production
spec:
  secretName: api-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - api.example.com
  renewBefore: 720h  # 30日前に更新

監査ログ: AWS Secrets Manager の CloudTrail ログにより、誰がいつシークレットにアクセスしたかを記録。

ローテーション: AWS Secrets Manager のローテーション Lambda と External Secrets の refreshInterval により自動対応。