Amazon EKS のロードバランサーで ALB を使う

はじめに

  • 以前の記事で Amazon EKS で LoadBalancer に設定されたサービスを作成すると、Elastic Load Balancing (ELB) が自動的に生成され、EKS のサービスが外部に公開できることを試しましたが、EKS で利用できるロードバランサーについてもう少し深く調べてみました。

blog.linkode.co.jp

  • EKS で利用できるロードバランサーのうち、 ALB を利用してサービスを公開してみます。
    • ALB は EKS のデータプレーンとして EC2 を利用した場合でも Fargate を利用した場合でも利用できる唯一の手段です(2020年6月時点)。

本記事で使用した K8sマニフェストや IAM ポリシーの yaml ファイルはGitLabのリポジトリで公開しております。

Elastic Load Balancing (ELB) の概要

f:id:linkode-okazaki:20200428150107p:plain

Classic Load Balancer (CLB)

  • Amazon EC2 インスタンスにおける基本的な負荷分散を提供します。
  • OSI モデルのレイヤー 4 のトランスポート層と、レイヤー 7 のアプリケーション層のどちらでも機能するロードバランサーで、TCPSSL、HTTP、HTTPS に対応しています。
  • CLB は、EC2-Classic ネットワーク内に構築されたアプリケーションを対象としています。
    • 近年デフォルトとなっている Virtual Private Cloud (VPC) を使用する場合は、CLB ではなく、以下に挙げる ALB や NLB の利用が推奨されます(参考:AWS 保里氏の発表資料 )。

Application Load Balancer (ALB)

  • ALB はレイヤー 7 のアプリケーション層での機能に特化したロードバランサーです。
  • CLB を利用するのと違い、主に以下のメリットがあります。
    • HTTP/2 と WebSocket の対応
    • 最新のアプリケーションアーキテクチャのサポート
      • コンテナ化されたアプリケーション
      • ターゲットとしての Lambda 関数の登録
    • ALBはターゲットグループと呼ばれる別々のサーバグループに対してインスタンスを紐付け、ルーティングを設定することが可能

Network Load Balancer (NLB)

  • NLB は、レイヤー 4 のトランスポート層で機能します。
  • 毎秒数百万のリクエストを処理できるため、高パフォーマンスが必要な環境においての負荷分散に最適とされるロードバランサーモデルです。
  • CLB や ALB と異なり、トラフィックの宛先IP をターゲットのIPに書き換えて転送するため、帰りはロードバランサを介さず、ターゲットからクライアントに直接通信する形になります。
    • そのため、NLBのターゲットはクライアントへのネットワーク到達性を考慮する必要があります。

f:id:linkode-okazaki:20200428150111p:plain

EKS におけるロードバランサー

  • 前回の記事で試したように、LoadBalancer のサービスを作成した場合、デフォルトでは CLB のロードバランサーが作成されます。
  • 指定や設定を変更することで、NLB や ALB を選択することが出来ます。

AWS ALB Ingress Controller for Kubernetes

f:id:linkode-okazaki:20200520182526p:plain

EKS で ALB を利用できるようにする

  • 以下、kubectl を使って K8s クラスタを操作できることと、eksctl (バージョン 0.17.0 以降)を使って EKS を操作できることを前提条件とします。

EKS クラスターに ALB Ingress Controller をデプロイする

  • データプレーンとして EC2 を利用している場合も、Fargate を利用している場合も以下の手順は同じです。

eksctl コマンドを利用してクラスタを作成した場合はすでに VPC のサブネットに必要なタグ付けセットはなされていますが、別の方法でクラスタを作成した場合は、Amazon EKS の ALB Ingress Controller - Amazon EKS を参考に、適切なタグ付けをする必要があります。

②IAM OIDC プロバイダーの作成とクラスターへの関連付けを行います。

$ eksctl utils associate-iam-oidc-provider \
    --region [リージョン名] \
    --cluster [クラスター名] \
    --approve

③以下の内容の IAM ポリシーを ALBIngressControllerIAMPolicy という名前で作成します。

policy-alb-ingress-controller.yml

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "acm:DescribeCertificate",
        "acm:ListCertificates",
        "acm:GetCertificate"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ec2:AuthorizeSecurityGroupIngress",
        "ec2:CreateSecurityGroup",
        "ec2:CreateTags",
        "ec2:DeleteTags",
        "ec2:DeleteSecurityGroup",
        "ec2:DescribeAccountAttributes",
        "ec2:DescribeAddresses",
        "ec2:DescribeInstances",
        "ec2:DescribeInstanceStatus",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeSubnets",
        "ec2:DescribeTags",
        "ec2:DescribeVpcs",
        "ec2:ModifyInstanceAttribute",
        "ec2:ModifyNetworkInterfaceAttribute",
        "ec2:RevokeSecurityGroupIngress"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "elasticloadbalancing:AddListenerCertificates",
        "elasticloadbalancing:AddTags",
        "elasticloadbalancing:CreateListener",
        "elasticloadbalancing:CreateLoadBalancer",
        "elasticloadbalancing:CreateRule",
        "elasticloadbalancing:CreateTargetGroup",
        "elasticloadbalancing:DeleteListener",
        "elasticloadbalancing:DeleteLoadBalancer",
        "elasticloadbalancing:DeleteRule",
        "elasticloadbalancing:DeleteTargetGroup",
        "elasticloadbalancing:DeregisterTargets",
        "elasticloadbalancing:DescribeListenerCertificates",
        "elasticloadbalancing:DescribeListeners",
        "elasticloadbalancing:DescribeLoadBalancers",
        "elasticloadbalancing:DescribeLoadBalancerAttributes",
        "elasticloadbalancing:DescribeRules",
        "elasticloadbalancing:DescribeSSLPolicies",
        "elasticloadbalancing:DescribeTags",
        "elasticloadbalancing:DescribeTargetGroups",
        "elasticloadbalancing:DescribeTargetGroupAttributes",
        "elasticloadbalancing:DescribeTargetHealth",
        "elasticloadbalancing:ModifyListener",
        "elasticloadbalancing:ModifyLoadBalancerAttributes",
        "elasticloadbalancing:ModifyRule",
        "elasticloadbalancing:ModifyTargetGroup",
        "elasticloadbalancing:ModifyTargetGroupAttributes",
        "elasticloadbalancing:RegisterTargets",
        "elasticloadbalancing:RemoveListenerCertificates",
        "elasticloadbalancing:RemoveTags",
        "elasticloadbalancing:SetIpAddressType",
        "elasticloadbalancing:SetSecurityGroups",
        "elasticloadbalancing:SetSubnets",
        "elasticloadbalancing:SetWebACL"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iam:CreateServiceLinkedRole",
        "iam:GetServerCertificate",
        "iam:ListServerCertificates"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "waf-regional:GetWebACLForResource",
        "waf-regional:GetWebACL",
        "waf-regional:AssociateWebACL",
        "waf-regional:DisassociateWebACL"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "tag:GetResources",
        "tag:TagResources"
      ],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "waf:GetWebACL"
      ],
      "Resource": "*"
    }
  ]
}

$ aws iam create-policy \
    --policy-name ALBIngressControllerIAMPolicy \
    --policy-document policy-alb-ingress-controller.yml

④alb-ingress-controller という名前の Kubernetes サービスアカウントを kube-system 名前空間に作成します。以下のコマンドを実行します。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/rbac-role.yaml

⑤ALB Ingress Controller の IAM ロールを作成し、このロールを前のステップで作成したサービスアカウントにアタッチします。

$ eksctl create iamserviceaccount \
    --region [リージョン名] \
    --name alb-ingress-controller \
    --namespace kube-system \
    --cluster [クラスター名] \
    --attach-policy-arn [ALBIngressControllerIAMPolicy の arn] \
    --override-existing-serviceaccounts \
    --approve

⑥ALB Ingress Controller をデプロイします。 https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.1.4/docs/examples/alb-ingress-controller.yaml の内容を参考に下記の内容のマニフェスト(alb-ingress-controller.yml)を作成し、適用する。

alb-ingress-controller.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/name: alb-ingress-controller
  name: alb-ingress-controller
  namespace: kube-system
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: alb-ingress-controller
  template:
    metadata:
      labels:
        app.kubernetes.io/name: alb-ingress-controller
    spec:
      containers:
        - name: alb-ingress-controller
          args:
            - --ingress-class=alb
            - --cluster-name=[クラスター名]
            - --aws-vpc-id=[EKS の VPC ID]
            - --aws-region=[リージョン名]
          image: docker.io/amazon/aws-alb-ingress-controller:v1.1.4
      serviceAccountName: alb-ingress-controller

$ kubectl apply -f alb-ingress-controller.yml

⑦次のコマンドで正常に動作していることを確認します。

$ kubectl get pods -n kube-system
NAME                                      READY   STATUS    RESTARTS   AGE
alb-ingress-controller-69b4c465df-4fxb2   1/1     Running   0          44s
  • ここまでで ALB を利用する準備は完了です。

EC2 をデータプレーンとして利用している場合で ALB を利用してアプリケーションを作成する

  • 前回作成した nginx の初期画面を表示するだけのサービスを ALB を用いて公開します。以下の 4 つのマニフェストを作成します。

namespace.yml

apiVersion: v1
kind: Namespace
metadata:
  name: "sample"

deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: "sample-deployment"
  namespace: "sample"
spec:
  selector:
    matchLabels:
      app: "sample-app"
  replicas: 1
  template:
    metadata:
      labels:
        app: "sample-app"
    spec:
      containers:
      - name: nginx-container
        image: nginx:1.7.9
        ports:
        - containerPort: 80
  • こちらは前回作成したデプロイメントと変わりはほとんどありません(名前空間の情報のみ追加)。

service.yml

apiVersion: v1
kind: Service
metadata:
  name: "sample-service"
  namespace: "sample"
spec:
  type: NodePort
  selector:
    app: "sample-app"
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  • サービスとしては、前回と異なり「NodePort」を指定します。

ingress.yml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "sample-ingress"
  namespace: "sample"
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
  labels:
    app: "sample-ingress"
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: "sample-service"
              servicePort: 80
  • ALB を利用するために Ingress のサービスを定義します。
  • metadata.annotations に ALB を利用する際に必要な注釈をつけています。

  • 作成したら、適用してデプロイをします。
$ kubectl apply -f namespace.yml
$ kubectl apply -f deployment.yml
$ kubectl apply -f service.yml
$ kubectl apply -f ingress.yml
  • 数分後、次のコマンドを使用して、Ingress リソースが作成されたことを確認します。
$ kubectl get ingress/sample-ingress -n sample
  • 正しくサービスが公開されている場合は、ADDRESS に公開先の URL が表示されます。そこにアクセスして、前回同様に Nginx の初期画面が表示されたら成功です。
    • 数分経っても ADDRESS に URL が表示されない場合、下記のコマンドでログを出すことによって原因を探ることが出来ます。
$ kubectl logs -n kube-system   deployment.apps/alb-ingress-controller
  • 確認が終了したら、下記コマンドでアプリケーションを削除します。
$ kubectl delete -f ingress.yml
$ kubectl delete -f service.yml
$ kubectl delete -f deployment.yml
$ kubectl delete -f namespace.yml  

Fargate をデータプレーンとして利用している場合で ALB を利用してアプリケーションを作成する

  • 次のコマンドを使用して、サンプルアプリケーションの名前空間を含む Fargate プロファイルを作成します。
    • 今回は名前空間名は「sample」にしておきます。
$ eksctl create fargateprofile --cluster [クラスター名] --region [リージョン名] --name [プロファイル名] --namespace [名前空間名]
  • EC2 で行ったのと同じ内容の namespace.ymldeployment.ymlservice.yml を作成します。
  • ingress-fargate.yml については、以下の内容で作成します。

ingress-fargate.yml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: "sample-ingress"
  namespace: "sample"
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
  labels:
    app: "sample-ingress"
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: "sample-service"
              servicePort: 80
  • ingress.yml との違いは、metadata.annotationsalb.ingress.kubernetes.io/scheme: internet-facing の下の行に alb.ingress.kubernetes.io/target-type: ip を加えています。

  • 作成したら、適用してデプロイをします。
$ kubectl apply -f namespace.yml
$ kubectl apply -f deployment.yml
$ kubectl apply -f service.yml
$ kubectl apply -f ingress-fargate.yml
  • 数分後、次のコマンドを使用して、Ingress リソースが作成されたことを確認します。ADDRESS に公開先の URL が表示されます。そこにアクセスして、前回同様に Nginx の初期画面が表示されたら成功です。
$ kubectl get ingress/sample-ingress -n sample
  • こちらも、試用が終わったら、アプリケーションを削除しておきます。
$ kubectl delete -f ingress-fargate.yml
$ kubectl delete -f service.yml
$ kubectl delete -f deployment.yml
$ kubectl delete -f namespace.yml  

まとめ

  • AWS EKS で利用可能なロードバランサーについてまとめました
  • ロードバランサーのうち、ALB については利用する場面が多そうなため、EC2 上のクラスターの場合と Fargate 上のクラスターの場合の両方で作成してみました。
  • NLB については利用方法が独特なため、別の機会に触ってみようと思います。

参考