2024年04月19日
連載 第5回
Kubernetes Ingress、
AWS LB Controller、
External DNS Controllerの理解
今回はAWSロードバランサーコントローラ(Load Balancer Controller)とエクスターナルDNSコントローラ(External DNS Controller)を利用してクバネティスイングレス(Ingress)について実習を通して確認します。それでは先ず、イングレスの概念を説明します。
クラスター外部でクラスター内部のポッドに接続する方法は、前回に確認したクバネティスサービスのノードポート、ロードバランサーと今回確認するイングレスがあります。クバネティスイングレスはノードポート、ロードバランサータイプのサービスと異なりドメイン(host)とパス基盤でコネクションでき、一般的に最も多く使用されています。イングレスは伝統的な解釈でL4やL7スイッチに分類すると、L7機能になります。
イングレスの主な機能は次のようになります。
- パス(経路)の基盤ルーティング:イングレスは入ってくるリクエストの経路を基準に適切な内部ポッドにルーティングします。例えば、 ‘/api’ 経路に入ってくるリクエストはバックエンドAPIサービスに伝達できます。
- 仮想ホスト基盤ルーティング:イングレスはリクエストされたドメイン名を基準に仮想ホストを設定して互いに異なるドメイン又はサブドメインへアクセスするリクエストをサービスに伝達できます。例えば、 argocd.myweb.comと grafana.myweb.comでドメインを区分してコネクションします。
- SSL証明書の管理:イングレスはHTTPSプロトコルをサポートし、SSL証明書を管理して暗号化された通信を提供できます。イングレスコントローラで証明書を処理し、イングレスコントローラと内部ポッド間のコネクションは平文(http)で通信します。バックエンドポッド側で証明書の管理をしないため、管理の利便性が向上します。
- ロードバランシング:イングレスは多くのバックエンドサービスに対するロードバランシングをサポートしてトラフィックを分散します。バックエンドサービス間にトラフィックを均等に分配したり、特定のルールでトラフィックを伝達できます。
今回の実習のために、事前にAWS Route 53にメインが登録されている必要があります。テスト用の会社ドメイン又はRoute53 ホームページとGoogle検索などによって各自で実習に必要な個人のドメインを準備してください。
1. AWSロードバランサーコントローラとエクスターナルDNSコントローラの理解
AWS EKS環境では管理の利便性と性能向上のためにロードバランサーコントローラとエクスターナルDNSコントローラを使用できます。
先ず、AWSロードバランサーコントローラ(以下LBコントローラ)はクバネティス環境で外部CSP(Cloud Service Provider、クラウドサービスプロバイダ)のロードバランサーサービスを管理するためのオープンソースプロジェクトです。このコントローラはクバネティスアプリケーションのためのロードバランサーを生成、構成と管理をします。LBコントローラを使用すると既存のALB(Application Load Balancer、アプリケーションロードバランサー)でターゲットのポッドに直接コネクションでき、ネットワーク経路のステップ数を既存の4ステップから2ステップに減らせます。
LBコントローラはクバネティスイングレスリソースと同時に使用されてクラスター内部のサービスに対する外部トラフィックを管理し、クバネティスイングレスマニフェスト(Ingress manifest、イングレス明細書、YAMLファイル)を基盤に外部のAWSアプリケーションロードバランサー(Application Load Balancer、ALB)またはネットワークロードバランサー(Network Load Balancer、NLB)の生成と構成をします。
LBコントローラは次の機能を提供します。:
- ロードバランサーの生成と構成:LBコントローラはクバネティスイングレスリソースを基盤にALBまたはNLBを生成して管理します。アプリケーションの要求事項に合わせてロードバランサーを構成でき、SSL/TLS証明書の管理、経路基盤ルーティング、セッションの管理など多様な設定をサポートします。
- AWSオートスケーリング(AWS Auto Scaling)グループとの統合:LBコントローラは外部のAWSオートスケーリンググループと一緒に使用されて、ロードバランサーが自動でサーバインスタンスを調整できるようにサポートします。オートスケーリンググループ内のインスタンスの拡張と縮小を自動で検知して、ロードバランサーの構成をアップデートして自動でトラフィックを分散させます。
- エクスターナルDNS コントローラは、イングレスリソースにイングレスで使用するDNSを登録すると、これを基盤にDNSコントローラがAWS Route53 DNSレコードを自動で管理するオープンソースコントローラーです。これによってクバネティス内のサービス、イングレスリソースの配布、変更のときに該当情報がAWSのDNS サービスのRoute53に自動で反映されます。既存の手動で登録することで発生するヒューマンエラーなどを防止できます。
簡単な使用例です。
- イングレスリソースで www.myweb.com ドメインに対するサービスを生成します。
- AWSエクスターナルDNSコントローラがこれを自動で検知してAWS Route53で www.myweb.com に対する新しいDNSレコードを生成します。
- ユーザが www.myweb.com にアクセスすると自動でクバネティスクラスターの該当サービスにルーティングされます。
以上のような機能でAWSエクスターナルDNSコントローラはクバネティスとAWS Route53の連携を簡略化し自動化します。これにより運用チームはDNS管理のコストと時間を大幅に削減できます。
2. IRSA(Identity and Access Management Roles for Service Accounts)の理解
LBコントローラ実習の前にインストールに必要なIRSAについて説明します。LBコントローラはEKS外部のAWSロードバランサーとオートスケーリンググループを管理するためには、リソースに対して追加の権限が必要です。このようなEKS外部リソースの権限を取得するためにAWSはIRSAを使用します。
IRSAはAWSが提供する機能で、クバネティスクラスター内のサービスアカウント(Service Account)に対してAWS IAM(Identity and Access Management)の役割を取り込みます。サービスアカウントとはクバネティスクラスター内のアプリケーションのために特別にデザインされたアカウント形式です。これはクラスター内のリソースにアクセスし、操作するときに使用できる資格証明を提供します。一般的に、ポッドが特定のリソースにアクセスする必要があるときにサービスアカウントを使用できるようにします。
内部リソースではなく外部AWSリソースを使用する場合、ポッドはAWSクラスターの資格証明を使用します。ポッドはAWS APIに直接アクセスできないため、ポッドが実行中のノードのAWS IAMの役割を利用してアクセスをトライします。しかし、これによりセキュリティー上の問題が発生する可能性があります。
IRSAを使用すると、ポッドが特定AWSリソースに対するアクセス権限が必要なときに、サービスアカウントにコネクションされたAWS IAMの役割を使用して認証と許可を実行できます。IRSAの設定には次のステップを実行します。
- クバネティスでサービスアカウントを生成します。
- AWS IAMがサービスアカウントのためのIAMの役割(Role)とポリシー(Policy)を生成します。
- クバネティスクラスターがIRSAリソースを生成してサービスアカウントとIAMの役割をコネクションします。
- ポッドのマニュフェストでサービスアカウントを使用できるように指定します。
IRSAを使用するとクバネティスクラスター内でAWSリソースに対する権限をより精密に制御可能で、ポッドはAWS APIに直接アクセスする必要がなく、IAMの役割によって安全にリソースにアクセスできます。このようにセキュリティーを強化し、権限の管理を改善できます。
IRSAの生成はeksctlコマンドでできますが、コードではないので、今後の再利用などの管理が難しい場合があります。そのため、terraformコードで管理することをお勧めします。
LBコントローラで使用するIRSAは次のterraformモジュールを使用します。
IRSA.tf
1. module "load_balancer_controller_irsa_role" {
2. source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
3.
4. role_name = "load-balancer-controller"
5. attach_load_balancer_controller_policy = true
6.
7. oidc_providers = {
8. ex = {
9. provider_arn = module.eks.oidc_provider_arn
10. namespace_service_accounts = ["kube-system:aws-load-balancer-controller"]
11. }
12. }
13.
14. tags = local.tags
15. }
. oidc_providers.namespace_service_accounts
AWS IAMとRoleにコネクションするサービスアカウント名を指定します。LBコントローラに使用する名前は一般的に変更せずに、aws-load-balancer-controllerをそのまま使用します。
既存のEKSを生成したterraformコードで上記のirsa.tfを追加してLB コントローラ用の役割(Role)とポリシー(Policy)を生成します。
1. tf plan -out planfile
2. tf apply planfile
適用が完了するとAWSコンソールで生成されたリソースを確認できます。
3. AWS LBコントローラとエクスターナルDNSコントローラのインストール
以上でIRSAの準備が完了しました。次はHelmを利用してLB コントローラ、エクスターナルDNSコントローラをインストールします。Helmのンストール方法は既存の方法と同じです。
1. $ (⎈ |switch-oregon-stage:kube-system) helm repo add eks https://aws.github.io/eks-charts
2. $ (⎈ |switch-oregon-stage:kube-system) helm pull eks/aws-load-balancer-controller
3. $ (⎈ |switch-oregon-stage:kube-system) tar xvfz aws-load-balancer-controller-1.4.8.tgz
4. $ (⎈ |switch-oregon-stage:kube-system) cd aws-load-balancer-controller
5. $ (⎈ |switch-oregon-stage:kube-system) cp values.yaml ci/my-values.yaml
次にaws load balancer-controller Helmをインストールします。下記のmy-values.yamlファイルとHelmチャートは筆者のgithubで確認できます。
1. serviceAccount:
2. create: true
3. name: aws-load-balancer-controller
4. annotations:
5. eks.amazonaws.com/role-arn: arn:aws:iam::$ACCOUNT_ID:role/load-balancer-controller
6. clusterName: $CLSTER_NAME
. serviceAccount.name
IRSA生成時、使用したaws-load-balancer-controller名を同じく使用します。
. serviceAccount.annotations
上記で生成したIRSAのROLE ARN情報を下記スクリーンショットを参考にして入力します。IRSA – Rolesメニューでload-balancer-controllerを検索するとRoleを確認できます。Roleの詳細情報からARN情報を確認できます。
. clusterName
各自でcluster名を入力します。
上記のmy-values.yaml基準でkube-systemネームスペースにインストールします。
1. $ (⎈ |switch-singapore-test:kube-system) cd aws-load-balancer-controller/aws-load-balancer-controller-1.4.8
2. $ (⎈ |switch-singapore-test:kube-system) helm install aws-load-balancer-controller -n kube-system -f my-values.yaml .
3. NAME: aws-load-balancer-controller
4. LAST DEPLOYED: Fri Jun 9 09:31:05 2023
5. NAMESPACE: kube-system
6. STATUS: deployed
7. REVISION: 1
8. TEST SUITE: None
9. NOTES:
10. AWS Load Balancer controller installed!
インストールが完了するとLBコントローラポッドが確認できます。
1. $ (⎈ |switch-singapore-test:kube-system) k get pod
2. NAME READY STATUS RESTARTS AGE
3. aws-load-balancer-controller-7654f65466-hjtc5 1/1 Running 0 50s
4. aws-load-balancer-controller-7654f65466-mmd98 1/1 Running 0 50s
インストールしたLBコントローラの動作確認は今後のイングレスの実習で行います。
次にAWSエクスターナルDNSコントローラをインストールします。
AWS LBコントローラと同じく、外部Route53リソースを制御するためエクスターナルDNSコントローラ用のIRSAの設定が必要です。
module “external_dns_irsa_role” {
source = “terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks”
role_name = “external-dns”
attach_external_dns_policy = true
external_dns_hosted_zone_arns = [“arn:aws:route53:::hostedzone/{HOSTED_ZONE_ID}”]
oidc_providers = {
ex = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = [“kube-system:external-dns”]
}
}
tags = local.tags
}
. external_dns_hosted_zone_arns
Route53で事前に準備したドメインの{HOSTED_ZONE_ID}情報に下記のスクリーンショット情報を参考にして入力します。AWSRoute53コンソールで ‘Hosted zone details’ メニューを選択します。
HOSTED_ZONE_IDを各自で変更してインストールを進めます。
変更したコードでterraformコードを実行します。 ‘tf plan’ で事前に結果を確認して意図した通りであれば ‘tf apply’ を実行します。
1. tf plan -out planfile
2. tf apply planfile
IRSAの準備ができたため、エクスターナルDNSコントローラHelmチャートを配布します(筆者 github ソースを参照)。
Helm Value ファイル
1. serviceAccount:
2. create: true
3. annotations:
4. eks.amazonaws.com/role-arn: arn:aws:iam::${aws_account_id}:role/external-dns
$EXTERNAL_DNS_ROLE_ARN
上記で生成したIRSAの実行結果で生成されたエクスターナルDNSのRoleを入力します。AWS IAM–Rolesで下記のように ‘external-dns’ で検索した結果を下記のスクリーンショットを参照して“arn:aws:iam:XXXXX” と入力します。
準備したHelm Valueファイルを利用してHelmチャートを配布します。
1. $ (⎈ |switch-singapore-test:kube-system) helm install external-dns -f my-values.yaml .
2. NAME: external-dns
3. LAST DEPLOYED: Tue Jul 11 09:12:49 2023
4. NAMESPACE: kube-system
5. STATUS: deployed
6. REVISION: 1
7. TEST SUITE: None
8. NOTES:
9. ***********************************************************************
10. * External DNS *
11. ***********************************************************************
12. Chart version: 1.12.2
13. App version: 0.13.4
14. Image tag: registry.k8s.io/external-dns/external-dns:v0.13.4
15. ***********************************************************************
エクスターナルDNSコントローラポッドが正常に実行されました。
1. [(jerry-test:kube-system) external-dns-1.12.2]$ k get pod external-dns-5555f6796f-77n4d
2. NAME READY STATUS RESTARTS AGE
3. external-dns-5555f6796f-77n4d 1/1 Running 0 3m22s
external-dns-xxxポッドのログが下記のように “All records are already up to date”であれば正常に配布されています。
1. [(jerry-test:kube-system) external-dns-1.12.2]$ k logs -f external-dns-5555f6796f-77n4d
2. time="2023-09-02T22:25:03Z" level=info msg="Applying provider record filter for domains: [jerryljh.me. .jerryljh.me.]"
3. time="2023-09-02T22:25:03Z" level=info msg="All records are already up to date"
4. イングレスの実習
以上で準備が完了しました。先に準備したAWS LBコントローラとエクスターナルDNSコントローラを利用してイングレスを使用します。テストに使用した ‘echoservice’ アプリケーションのデプロイメント(deployment)、サービス(service)などのコードの事例は筆者のGithubで確認できます。
デプロイメント、サービスマニュフェスト(service manifest)は基本設定のため、詳細は省略します。 イングレスマニュフェストは次のように作成します。
1. apiVersion: networking.k8s.io/v1
2. kind: Ingress
3. metadata:
4. name: echoserver
5. namespace: echoserver
6. annotations:
7. alb.ingress.kubernetes.io/scheme: internet-facing
8. alb.ingress.kubernetes.io/target-type: ip
9. alb.ingress.kubernetes.io/group.name: sg-external
10. alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
11. alb.ingress.kubernetes.io/ssl-redirect: "443"
12. alb.ingress.kubernetes.io/certificate-arn: $CERTIFICATE_ARN
13. external-dns.alpha.kubernetes.io/hostname: $INGRESS_HOST
14. spec:
15. ingressClassName: alb
16. rules:
17. - host: $INGRESS_HOST
18. http:
19. paths:
20. - path: /echo-server
21. pathType: Prefix
22. backend:
23. service:
24. name: echoserver
25. port:
26. number: 80
. alb.ingress.kubernetes.io/target-type: ip
target-typeは ‘instance’ または ‘ip’ が指定できます。インスタンスを指定すると、ポッドではなくインスタンスが実行するEKS全体ノードのノードポートにトラフィックを伝達します。そのため、ネットワークの経路が1ステップ増加します。IPを指定すると、ロードバランサーでポッドのIPにトラフィックを直接伝達します。ipのオプションを使用するにはeks CNIにaws vpc cniを使用します。eks環境の場合はaws vpc cniを使用することが多いです。
. alb.ingress.kubernetes.io/group.name
グループ名に任意の名前が使用できます。ALBグループを指定しないと、イングレス毎にロードバランサーを別途で生成することになります。現場でイングレスを多数生成すると不要なコストが発生します。Security Group別にサービスを区分しないか、サービストラフィックがALBで処理しきれないくらい大きくない場合は、同じALBに処理が可能です。グループを指定してイングレス毎にALBを作らずに一つのALBで複数のイングレスを処理します。
. alb.ingress.kubernetes.io/certificate-arn
ロードバランサーでTLS証明書を処理できるように各自で生成した証明書情報を登録します。AWSコンソールの ‘Certificate Manager’ で下記のスクリーンショットを参照して証明書のARN情報を入力します。
. external-dns.alpha.kubernetes.io/hostname, spec.rules.[]host:
イングレスが処理するドメイン情報を入力します。例えばwww.myweb.com, www.jerryljh.me など各自でサービスのドメイン情報を入力します。ドメイン情報は、前のステップでインストールしたエクスターナルDNSコントローラにより自動でAWS Route53に登録されて、そのドメインが見てるTargetはイングレスに生成されたLoad Balancer情報で、自動でマッピングされます。
. spec.ingressClassName: alb
イングレスが生成するロードバランサーを区分するためにClass名を指定します。今回はAWS ALBを使用するためにALBに指定します。
それでは、3つのマニュフェスト(イングレス、デプロイメント、サービス)を一回で配布します。3つのマニュフェストが位置するディレクトリへ移動します。
1. # echoservice manifest ファイルが位置するディレクトリへ移動します。
2. $ (⎈ |switch-singapore-test:kube-system) cd private/k8s-class/aws-load-balancer-controller/aws-lb-controller-examples/
3. $ (⎈ |switch-singapore-test:kube-system) pwd
4. /Users/jerry/private/k8s-class/aws-load-balancer-controller/aws-lb-controller-examples
echoserviceディレクトリ全体のマニュフェストを実行します。k apply -f {ディレクトリ 名}を指定するとディレクトリに下記の全てのリソースが一回で配布されます。
1. $ (⎈ |switch-singapore-test:kube-system) ls echoservice
2. echoserver-deployment.yaml echoserver-ingress.yaml echoserver-service.yaml
3. $ (⎈ |switch-oregon-stage:default) k apply -f echoservice/
特別な問題がなければ下記のようにリソースが確認できます。
1. $ (⎈ |switch-oregon-stage:default) k get ing,pod,svc
2. NAME CLASS HOSTS ADDRESS PORTS AGE
3. ingress.networking.k8s.io/echoserver alb www.XXXX.com k8s-XXXX.ap-northeast-2.elb.amazonaws.com 80 3d13h
4.
5. NAME READY STATUS RESTARTS AGE
6. pod/echoserver-7757d5ff4d-7m2fp 1/1 Running 0 3d13h
7.
8. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
9. service/echoserver ClusterIP 172.20.198.114 <none> 80/TCP 3d13h
ここで、正常にAWS Resourceが生成されたかを確認します。先ず、Route53にドメインが自動で登録されたかを確認します。
各自で生成したドメインがRoute53に登録されました。ドメインの ‘A’ レコードを確認すると、ロードバランサーにマッピングされたことが確認できます。
次はALBのTarget Groupを確認します。EC2 – Load balancers メニューを確認すると、先ほど生成したロードバランサーを確認できます。
ロードバランサーを選択して、詳細メニューで ‘Listeners and rules’ を確認します。‘Rules’ を再度選択します。
イングレスマニュフェストで作成したホスト名とパス(/echo-server)が確認できます。ターゲットグループを確認するために下記のスクリーンショットを参照して ‘k8s-default-echoserver-xxxxx’ を選択します。
ALBが見るターゲットグループ情報が確認できます。ターゲットの数、正常の是非などが確認できます。
下記の詳細情報を見るとIPが確認できます。
IPはノードIPではないポッドのIPで、ALBで見るターゲットがポッドであることを確認できます。
上記のスクリーンショットで 10.110.8.100はechoserverポッドのIPです。
1. [(jerry-test:default) argo-cd-app-of-apps-qa]$ k get pod -o wide echoserver-65dfd67c69-pzjgr
2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
3. echoserver-65dfd67c69-pzjgr 1/1 Running 0 44h 10.110.8.100 ip-10-110-0-162.ap-northeast-2.compute.internal <none> <none>
もし、target-type: IPではなく、target-type: instanceを指定するかLB コントローラを使用しない場合は、下記のように全体ノードのノードポートがターゲットグループに登録されます。
勿論、ブラウザで直接確認すると ‘echoserver’ が正常に応答することを確認できます。
追加で、AWSで生成した公認の証明書も正常に登録されたことが確認できます。
5. Argo-CD イングレスの適用
では、実習をベースに実際に運用するArgo-CDにイングレスを適用します。連載第3回でPort-forwardを利用してArgo-CDを接続しました。しかし、実際の運用環境でArgo-CDは開発担当者も接続するサービスのため ‘kubectl’ コマンドに慣れていないユーザが接続することは難しいです。イングレスを使用するとドメイン情報だけで接続できるためArgo-CD Helmの配布時、イングレスの設定も同時に含めて配布します。
ここで、イングレスの設定を含む Helm Valueファイルを確認します。
1. server:
2. extraArgs:
3. - --insecure
4. ingress:
5. enabled: true
6. annotations:
7. alb.ingress.kubernetes.io/scheme: internet-facing
8. alb.ingress.kubernetes.io/target-type: ip
9. alb.ingress.kubernetes.io/group.name: sg-external
10. alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
11. alb.ingress.kubernetes.io/ssl-redirect: "443"
12. alb.ingress.kubernetes.io/certificate-arn: $CERTIFICATE_ARN
13. external-dns.alpha.kubernetes.io/hostname: $ARGOCD_HOST
14. ingressClassName: "alb"
15. hosts:
16. - $ARGOCD_HOST
17. paths:
18. - /
19. configs:
20. credentialTemplates:
21. ssh-creds:
22. url: git@github.com:junghoon2
23. sshPrivateKey: |
24. -----BEGIN OPENSSH PRIVATE KEY-----
25. XXXX
26. -----END OPENSSH PRIVATE KEY-----
27. repositories:
28. k8s-class:
29. name: k8s-class
30. url: git@github.com:junghoon2/k8s-class.git
. server.extraArgs: –insecure
AWS ALBでTLS証明書を処理してALBと Argo=CD Webサーバは一般のHTTP通信をするためinsecureオプションを追加します。
. server.ingress.alb.ingress.kubernetes.io/group.name
前の例で生成した ‘echoserver’ と同一グループ名を指定します。同じイングレスグループ名を指定すると、追加のALBを生成せずに既存のALBを一緒に使用できます。不要なALBの浪費を避けられます。
. server.ingress.alb.ingress.kubernetes.io/certificate-arn
前で生成した証明書ARN情報を入力します。
. server.ingress.external-dns.alpha.kubernetes.io/hostname
Argo-cd接続に使用するドメイン情報を入力します。例えば、 argocd.myweb.comなどArgo-cd用で別途にドメインを生成するか www.myweb.com/argocd などドメインは同じにしてパスで サービスを分離できます。二つを一緒に使用できますが、筆者は Argo-cd用で別途にドメインを分離することにします。 同様にgrafana.myweb.com、kafka-ui.myweb.comなども分離します。
. configs.credentialTemplates.ssh-creds
連載第3回で使用したsshキー情報を入力します。
イングレス情報を含むHelm ValuesファイルでArgo-CDを再配布します。
1. [(jerry-test:argocd) argo-cd-5.14.1]$ helm upgrade argocd -f ci/ingress-values.yaml .
2. (省略)
次にPort-forwardはなく、ドメインアドレスを入力すると Argo-CDに接続できます。
以上でイングレス、AWSロードバランサーコントローラ、エクスターナルDNSコントローラを確認しました。AWSが提供するLB コントローラ、DNSコントローラを使用すると、イングレスをより便利に使用できます。これはマネジードクバネティスサービスのメリットです。
イングレスは現場で頻繁に使用するリソースです。同じ設定を繰り返すため、何回も経験すると自然に理解できると思います。