Ambassador

Kubernetes専用のAPI Gateway。Envoyプロキシベースで、GitOpsワークフロー、カナリアデプロイメント、レート制限機能を提供。

API GatewayKubernetesEnvoyEmissary-IngressGitOpsCNCFマイクロサービス認証セキュリティ

概要

Ambassadorは、Kubernetes専用に設計されたオープンソースのAPI Gatewayです。Envoy Proxyをベースに構築され、Kubernetes CRD(Custom Resource Definitions)を使用した宣言的な設定が特徴です。現在は「Emissary-Ingress」として知られ、CNCFのIncubatingプロジェクトとして開発されています。

GitOpsワークフロー、カナリアデプロイメント、レート制限機能、高度な認証機能を提供し、Kubernetes環境でのマイクロサービス管理に最適化されています。Datawire(現Ambassador Labs)によって開発され、Kubernetes環境でのAPI Gateway標準選択肢として広く採用されています。

主要な特徴

  • Kubernetes Native: CRDによる完全なKubernetes統合
  • Envoy Proxy基盤: 高性能で実績のあるL7プロキシエンジン
  • 宣言的設定: GitOpsフレンドリーなYAML設定
  • 自動サービスディスカバリ: Kubernetesサービスとの自動統合
  • 豊富な認証機能: OAuth、JWT、mTLSなどの包括的な認証サポート

主要機能

コア機能

  • 動的ルーティング: 高度なトラフィックルーティングとロードバランシング
  • TLS終端: 自動証明書管理とSSL/TLS終端
  • 認証・認可: JWT、OAuth 2.0、Basic Auth、外部認証サービス統合
  • レート制限: 柔軟なレート制限ポリシー
  • カナリアデプロイメント: トラフィック分割による段階的デプロイ

高度な機能

  • Circuit Breaker: 障害耐性のための回路ブレーカーパターン
  • Retry Policy: 自動リトライ機能
  • CORS支援: Cross-Origin Resource Sharing設定
  • WebSocket対応: リアルタイム通信のサポート
  • gRPC支援: gRPCプロトコルの完全サポート

インストール・セットアップ

Helmを使用したインストール

Emissary-Ingress(OSS版)のインストール

# Helm リポジトリ追加
helm repo add datawire https://app.getambassador.io
helm repo update

# 名前空間作成
kubectl create namespace emissary-system

# Emissary-Ingress インストール
helm install emissary-ingress datawire/emissary-ingress \
  --namespace emissary-system \
  --set service.type=LoadBalancer \
  --set adminService.type=ClusterIP

# インストール確認
kubectl get pods -n emissary-system
kubectl get services -n emissary-system

Ambassador Edge Stack(Commercial版)のインストール

# Edge Stack インストール
helm install ambassador datawire/edge-stack \
  --namespace ambassador \
  --create-namespace \
  --set service.type=LoadBalancer \
  --set enableAES=true \
  --set licenseKey.value=YOUR_LICENSE_KEY

マニフェストでのインストール

CRDインストール

# 最新のCRDを適用
kubectl apply -f https://app.getambassador.io/yaml/emissary/3.9.0/emissary-crds.yaml

# Emissary-Ingress本体をインストール
kubectl apply -f https://app.getambassador.io/yaml/emissary/3.9.0/emissary-emissaryns.yaml

# リソース確認
kubectl wait --timeout=90s --for=condition=available deployment emissary-ingress -n emissary-system

手動インストール設定

# emissary-ingress.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: emissary-ingress
  namespace: emissary-system
spec:
  replicas: 3
  selector:
    matchLabels:
      service: ambassador
  template:
    metadata:
      labels:
        service: ambassador
    spec:
      serviceAccountName: emissary-ingress
      containers:
      - name: emissary
        image: docker.io/emissaryingress/emissary:3.9.0
        ports:
        - name: http
          containerPort: 8080
        - name: https
          containerPort: 8443
        - name: admin
          containerPort: 8877
        env:
        - name: AMBASSADOR_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        resources:
          requests:
            memory: "512Mi"
            cpu: "200m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /ambassador/v0/check_alive
            port: admin
          periodSeconds: 3
        readinessProbe:
          httpGet:
            path: /ambassador/v0/check_ready
            port: admin
          periodSeconds: 3
---
apiVersion: v1
kind: Service
metadata:
  name: emissary-ingress
  namespace: emissary-system
spec:
  type: LoadBalancer
  ports:
  - name: http
    port: 80
    targetPort: http
  - name: https
    port: 443
    targetPort: https
  selector:
    service: ambassador

Dev Modeでの簡易セットアップ

# 開発用セットアップ(単一Pod)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: ambassador
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
  selector:
    service: ambassador
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ambassador
spec:
  replicas: 1
  selector:
    matchLabels:
      service: ambassador
  template:
    metadata:
      labels:
        service: ambassador
    spec:
      containers:
      - name: ambassador
        image: docker.io/emissaryingress/emissary:3.9.0
        ports:
        - containerPort: 8080
        env:
        - name: AMBASSADOR_SINGLE_NAMESPACE
          value: "true"
EOF

基本的な使い方

基本的なMapping設定

シンプルなHTTPサービス

# basic-mapping.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: httpbin-mapping
  namespace: default
spec:
  hostname: api.example.com
  prefix: /httpbin/
  service: httpbin:80
  timeout_ms: 5000
  retry_policy:
    retry_on: "5xx"
    num_retries: 3

パスベースルーティング

# path-based-routing.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: api-v1-mapping
spec:
  prefix: /api/v1/
  service: backend-api-v1:8080
  rewrite: /
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: api-v2-mapping
spec:
  prefix: /api/v2/
  service: backend-api-v2:8080
  rewrite: /v2/

ホストベースルーティング

# host-based-routing.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: admin-mapping
spec:
  hostname: admin.example.com
  prefix: /
  service: admin-dashboard:3000
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: api-mapping
spec:
  hostname: api.example.com
  prefix: /
  service: api-service:8080

Listenerの設定

# listener.yaml
apiVersion: getambassador.io/v3alpha1
kind: Listener
metadata:
  name: ambassador-listener-8080
spec:
  port: 8080
  protocol: HTTP
  securityModel: XFP
  hostBinding:
    namespace:
      from: ALL
---
apiVersion: getambassador.io/v3alpha1
kind: Listener
metadata:
  name: ambassador-listener-8443
spec:
  port: 8443
  protocol: HTTPS
  securityModel: XFP
  hostBinding:
    namespace:
      from: ALL

Hostリソースの設定

# host.yaml
apiVersion: getambassador.io/v3alpha1
kind: Host
metadata:
  name: example-com-host
spec:
  hostname: api.example.com
  acmeProvider:
    authority: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
  tlsSecret:
    name: example-com-tls

設定例

ロードバランシングとヘルスチェック

# load-balancing.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: load-balanced-service
spec:
  prefix: /api/
  service: backend-service:8080
  load_balancer:
    policy: round_robin
  circuit_breakers:
  - priority: default
    max_connections: 1024
    max_pending_requests: 256
    max_requests: 1024
    max_retries: 3
    consecutive_5xx: 5
    interval: 30s
    base_ejection_time: 30s
    max_ejection_percent: 50

複数のバックエンドサービス

# weighted-routing.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: canary-deployment
spec:
  prefix: /app/
  service: stable-app:8080
  weight: 90
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: canary-deployment-new
spec:
  prefix: /app/
  service: canary-app:8080
  weight: 10

リクエスト・レスポンス変換

# transformations.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: header-transformation
spec:
  prefix: /api/
  service: backend:8080
  add_request_headers:
    x-backend-version: "v1.0"
    x-request-source: "ambassador"
  add_response_headers:
    x-powered-by: "Ambassador"
  remove_request_headers:
  - "x-internal-token"
  remove_response_headers:
  - "server"

認証・セキュリティ

JWT認証の実装

JWT Filter設定

# jwt-filter.yaml
apiVersion: getambassador.io/v3alpha1
kind: Filter
metadata:
  name: jwt-filter
spec:
  JWT:
    jwksURI: "https://auth.example.com/.well-known/jwks.json"
    validAlgorithms:
    - "RS256"
    - "HS256"
    audience: "api.example.com"
    issuer: "https://auth.example.com"
    requireAudience: false
    requireIssuedAt: true
    requireExpiresAt: true
    requireNotBefore: false
    injectRequestHeaders:
    - name: "X-User-ID"
      value: "{{ .token.sub }}"
    - name: "X-User-Email"
      value: "{{ .token.email }}"

JWT FilterPolicyの適用

# jwt-policy.yaml
apiVersion: getambassador.io/v3alpha1
kind: FilterPolicy
metadata:
  name: jwt-policy
spec:
  rules:
  - host: "api.example.com"
    path: "/protected/*"
    filters:
    - name: jwt-filter
      arguments:
        scope: "read write"
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: protected-api
spec:
  hostname: api.example.com
  prefix: /protected/
  service: protected-service:8080

OAuth 2.0認証

# oauth-filter.yaml
apiVersion: getambassador.io/v3alpha1
kind: Filter
metadata:
  name: oauth2-filter
spec:
  OAuth2:
    authorizationURL: "https://oauth.example.com/oauth/authorize"
    tokenURL: "https://oauth.example.com/oauth/token"
    clientID: "ambassador-client"
    clientSecret: "ambassador-secret"
    protectedOrigins:
    - origin: "https://api.example.com"
    grantType: "AuthorizationCode"
    scopes:
    - "read"
    - "write"
    - "admin"

外部認証サービス

# external-auth.yaml
apiVersion: getambassador.io/v3alpha1
kind: AuthService
metadata:
  name: authentication
spec:
  auth_service: "auth-service:80"
  path_prefix: "/extauth"
  timeout_ms: 5000
  allowed_request_headers:
  - "x-custom-auth"
  allowed_authorization_headers:
  - "authorization"
  - "cookie"
  include_body:
    max_bytes: 4096
    allow_partial: true

mTLS(相互TLS)設定

# mtls-host.yaml
apiVersion: getambassador.io/v3alpha1
kind: Host
metadata:
  name: mtls-host
spec:
  hostname: secure.example.com
  tlsSecret:
    name: mtls-secret
  tls:
    min_tls_version: v1.2
    max_tls_version: v1.3
    cipher_suites:
    - "ECDHE-RSA-AES128-GCM-SHA256"
    - "ECDHE-RSA-AES256-GCM-SHA384"
    alpn_protocols:
    - "h2"
    - "http/1.1"
    cert_required: true

APIキー認証

# api-key-filter.yaml
apiVersion: getambassador.io/v3alpha1
kind: Filter
metadata:
  name: api-key-filter
spec:
  Plugin:
    name: "api-key-auth"
    config:
      keys:
      - header: "X-API-Key"
        secret: "api-keys-secret"
        key: "valid-keys"
      - query_param: "api_key"
        secret: "api-keys-secret"
        key: "valid-keys"

レート制限・トラフィック管理

レート制限の設定

# rate-limit-filter.yaml
apiVersion: getambassador.io/v3alpha1
kind: Filter
metadata:
  name: rate-limit-filter
spec:
  RateLimit:
    domain: "ambassador"
    rules:
    - dimensions:
      - remote_address
      - header:
          name: "X-API-Key"
          skip_if_absent: true
    timeout_ms: 500
    failure_mode_deny: false
---
apiVersion: getambassador.io/v3alpha1
kind: RateLimitService
metadata:
  name: rate-limit-service
spec:
  service: "ratelimit:5000"

Rate Limitの詳細設定

# advanced-rate-limit.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: rate-limit-config
data:
  config.yaml: |
    domain: ambassador
    descriptors:
    - key: remote_address
      rate_limit:
        unit: minute
        requests_per_unit: 100
    - key: header
      value: premium
      descriptors:
      - key: remote_address
        rate_limit:
          unit: minute
          requests_per_unit: 1000
    - key: header
      value: basic
      descriptors:
      - key: remote_address
        rate_limit:
          unit: minute
          requests_per_unit: 10

Circuit Breaker設定

# circuit-breaker.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: circuit-breaker-mapping
spec:
  prefix: /api/
  service: backend-service:8080
  circuit_breakers:
  - priority: default
    max_connections: 100
    max_pending_requests: 50
    max_requests: 200
    max_retries: 3
    consecutive_5xx: 5
    interval: 30s
    base_ejection_time: 30s
    max_ejection_percent: 50
  retry_policy:
    retry_on: "5xx"
    num_retries: 3
    per_try_timeout: "10s"

トラフィック分割

# traffic-splitting.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: feature-flag-routing
spec:
  prefix: /api/
  service: stable-service:8080
  precedence: 1
  headers:
    feature-flag: "new-feature"
  weight: 100
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: default-routing
spec:
  prefix: /api/
  service: stable-service:8080
  precedence: 0
  weight: 100

モニタリング・ログ

Prometheusメトリクス設定

# prometheus-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ambassador-admin
  namespace: emissary-system
  labels:
    service: ambassador-admin
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8877"
    prometheus.io/path: "/metrics"
spec:
  ports:
  - name: admin
    port: 8877
    targetPort: admin
  selector:
    service: ambassador

カスタムメトリクス

# custom-metrics.yaml
apiVersion: getambassador.io/v3alpha1
kind: Module
metadata:
  name: ambassador
spec:
  config:
    diagnostics:
      enabled: true
    enable_grpc_http11_bridge: true
    enable_grpc_web: true
    stats:
      - name: "request_duration"
        type: "histogram"
        labels:
        - "method"
        - "status"
        - "service"

分散トレーシング設定

# tracing.yaml
apiVersion: getambassador.io/v3alpha1
kind: TracingService
metadata:
  name: tracing
spec:
  service: "jaeger:14268"
  driver: zipkin
  config:
    collector_endpoint: "/api/v2/spans"
    trace_id_128bit: true
    shared_span_context: false
    collector_endpoint_version: httpJson

ログ設定

# log-service.yaml
apiVersion: getambassador.io/v3alpha1
kind: LogService
metadata:
  name: access-log
spec:
  service: "log-collector:8080"
  driver: http
  config:
    additional_log_headers:
    - header_name: "x-request-id"
      during_request: true
      during_response: false
    - header_name: "x-user-id"
      during_request: true
      during_response: false

Grafanaダッシュボード

{
  "dashboard": {
    "title": "Ambassador API Gateway",
    "panels": [
      {
        "title": "Request Rate",
        "type": "graph",
        "targets": [
          {
            "expr": "rate(envoy_http_downstream_rq_total[5m])",
            "legendFormat": "{{method}} {{response_code}}"
          }
        ]
      },
      {
        "title": "Response Time",
        "type": "graph", 
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(envoy_http_downstream_rq_time_bucket[5m]))",
            "legendFormat": "95th percentile"
          }
        ]
      }
    ]
  }
}

高度な機能

gRPCサポート

# grpc-mapping.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: grpc-service
spec:
  prefix: /grpc.example.v1.ExampleService/
  service: grpc-backend:9000
  grpc: true
  timeout_ms: 10000
  connect_timeout_ms: 5000

WebSocketサポート

# websocket-mapping.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: websocket
spec:
  prefix: /ws/
  service: websocket-service:8080
  use_websocket: true
  timeout_ms: 0
  connect_timeout_ms: 10000
  allow_upgrade:
  - websocket

GraphQL統合

# graphql-mapping.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: graphql-api
spec:
  prefix: /graphql
  service: graphql-service:4000
  method: POST
  headers:
    content-type: "application/json"
  add_request_headers:
    x-apollo-tracing: "true"

カスタムプラグイン

# custom-plugin.yaml
apiVersion: getambassador.io/v3alpha1
kind: Filter
metadata:
  name: custom-lua-filter
spec:
  Plugin:
    name: "custom-auth"
    config:
      script: |
        function envoy_on_request(request_handle)
          local headers = request_handle:headers()
          local auth_header = headers:get("authorization")
          
          if not auth_header then
            request_handle:respond(
              {[":status"] = "401"},
              "Unauthorized"
            )
            return
          end
          
          -- カスタム認証ロジック
          if auth_header ~= "Bearer valid-token" then
            request_handle:respond(
              {[":status"] = "403"},
              "Forbidden"
            )
            return
          end
          
          -- 認証成功時のヘッダー追加
          headers:add("x-user-authenticated", "true")
        end

DevPortal統合(Edge Stack)

# devportal.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: openapi-docs
  annotations:
    getambassador.io/config: |
      ---
      apiVersion: getambassador.io/v2
      kind: DevPortal
      name: api-docs
      content:
        url: https://api.example.com/openapi.json
        docs:
        - url: https://api.example.com/docs/
spec:
  prefix: /docs/
  service: api-docs-service:8080

パフォーマンス最適化

Envoy設定の最適化

# envoy-config.yaml
apiVersion: getambassador.io/v3alpha1
kind: Module
metadata:
  name: ambassador
spec:
  config:
    # 接続設定
    cluster:
      connect_timeout: 5s
      per_connection_buffer_limit_bytes: 1048576
    
    # リソース制限
    listener:
      per_connection_buffer_limit_bytes: 1048576
    
    # HTTP設定
    http:
      request_headers_timeout: 10s
      request_timeout: 60s
      stream_idle_timeout: 5m
      
    # バッファ設定
    buffer:
      max_request_bytes: 8192
    
    # 圧縮設定
    gzip:
      memory_level: 6
      window_bits: 15
      compression_level: 6
      compression_strategy: default

リソース制限とHPA

# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: ambassador-hpa
  namespace: emissary-system
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: emissary-ingress
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

PodDisruptionBudget

# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: ambassador-pdb
  namespace: emissary-system
spec:
  minAvailable: 2
  selector:
    matchLabels:
      service: ambassador

キャッシング設定

# caching.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: cached-api
spec:
  prefix: /api/cache/
  service: backend-service:8080
  add_response_headers:
    cache-control: "public, max-age=300"
    vary: "Accept-Encoding"
  envoy_override:
    route:
      response_headers_to_add:
      - header:
          key: "x-cache-status"
          value: "cached"
        append: false

トラブルシューティング

デバッグとログ確認

Ambassador診断ページ

# Ambassador診断情報の取得
kubectl port-forward -n emissary-system svc/ambassador-admin 8877:8877

# ブラウザで以下にアクセス
# http://localhost:8877/ambassador/v0/diag/

# または curl で取得
curl http://localhost:8877/ambassador/v0/diag/

ログの確認

# Ambassadorのログ確認
kubectl logs -n emissary-system deployment/emissary-ingress -f

# 特定のPodのログ
kubectl logs -n emissary-system pod/emissary-ingress-xxxx -c emissary

# 設定エラーの確認
kubectl logs -n emissary-system deployment/emissary-ingress --grep="ERROR"

設定検証

# CRDリソースの確認
kubectl get mappings,hosts,filters,filterpolicies -A

# 設定の詳細確認
kubectl describe mapping my-mapping
kubectl describe host my-host

# envoy設定の確認
curl http://localhost:8877/config_dump

よくある問題と解決法

503 Service Unavailable エラー

# サービスエンドポイント確認
kubectl get endpoints

# バックエンドサービスの確認
kubectl describe service backend-service

# Mapping設定の確認
kubectl get mapping -o yaml

TLS/SSL証明書の問題

# Host設定の確認
kubectl describe host my-host

# 証明書の確認
kubectl get secret tls-secret -o yaml

# ACME Challenge の確認(Let's Encrypt)
kubectl get challenges
kubectl describe challenge my-challenge

パフォーマンス問題の診断

# メトリクス確認
curl http://localhost:8877/stats/prometheus

# Envoy管理画面
curl http://localhost:8877/stats
curl http://localhost:8877/clusters
curl http://localhost:8877/server_info

ヘルスチェック設定

# health-check.yaml
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: health-check
spec:
  prefix: /health
  service: health-service:8080
  bypass_auth: true
  timeout_ms: 1000
  headers:
    x-health-check: "true"

設定バックアップとリストア

# 設定のバックアップ
kubectl get mappings,hosts,filters,filterpolicies,modules -A -o yaml > ambassador-config-backup.yaml

# 設定の復元
kubectl apply -f ambassador-config-backup.yaml

# 特定名前空間の設定のみバックアップ
kubectl get mappings,hosts,filters,filterpolicies -n production -o yaml > prod-config.yaml

参考リンク

公式ドキュメント

GitHub リポジトリ

ツールとエコシステム

  • Telepresence - ローカル開発環境統合
  • Forge - マイクロサービス開発ツール
  • ArgoCD - GitOps デプロイメント

コミュニティ

学習リソース