【初心者】Amazon EKSを使ってみる #3 (AWS Load Balancer Controller によるIngressの利用)

# 目的
- k8sの学習のため、EKSを触っている。前回記事：「[【初心者】Amazon EKSを使ってみる #2（WordPressのデプロイ）」](https://qiita.com/mksamba/items/15e49ed01606dd63b6be)でService type: Loadbalancerは使えるようになったので、今回はIngressができるようにする。

# やったこと
- 「AWS Load Balancer Controller」を用いて、ServiceをIngress(ALB)で外部公開する。
- 従来、EKSでは「ALB Ingress Controller」によりIngressが実装されていたが、ちょうど2020/10月末頃に、新しい方式である「AWS load balancer controller」がリリースされた。せっかくなのでそちらを利用して動作確認する。
- 2020/10のAWS公式ブログ 「[Introducing the AWS Load Balancer Controller](https://aws.amazon.com/jp/blogs/containers/introducing-aws-load-balancer-controller/)」によると、「ALB ingress controller は今、Load Balancer Controllerになりました」とのこと。従来のALB ingress controllerは解説記事がたくさんあるのでそれをそのままやればよいと思っていたが、、

> We are pleased to announce that the ALB ingress controller is now the AWS Load Balancer Controller with added functionality and features such as:
> - Network Load Balancers (NLB) for Kubernetes services
> - Share ALBs with multiple Kubernetes ingress rules
> - New TargetGroupBinding custom resource
> - Support for fully private clusters

# 構成図
- EKSの環境は[前回記事](https://qiita.com/mksamba/items/15e49ed01606dd63b6be)にてeksctlで作成したものを引き続き利用する。
- Ingress Controller(AWS Load Balancer Controller)を環境にデプロイし、その機能によりALBを動的に作成する。

![ingress構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/dc145108-ecfb-9d7d-8d61-b5605891fd94.png)



# 作業手順
## 1. AWS Load Balancer Controller のデプロイ
### 1.1 作業手順の確認
- 「AWS Load Balancer Controller」のインストール方法について探したが、結局、公式githubのインストール手順である「[AWS Load Balancer Controller Installation guide](https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/main/docs/guide/controller/installation.md)」の手順でやることにした。

### 1.2 Create IAM OIDC provider

- Ingress ControllerはPodとして動作するが、そのPodにALB作成等の権限を付与する必要がある。IAM OIDC providerの仕組みを使って、IAMとk8sのServiceAccountの連携を行い、Podへの権限設定を実現する。Developers.IOの記事: 「[[アップデート] EKSでIAMロールを使ったPod単位のアクセス制御が可能になりました！](https://dev.classmethod.jp/articles/eks-supports-iam-roles-for-service-accounts/) 」にそのあたりの解説あり。
- 私の環境ではeksctlでクラスタを作成(eksctl create cluster)する際、--with-oidc オプションを付与していたため、IAM OIDC providerは作成済の状態となっていた。そのためここでの作成は不要だった。

![02a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/ea6c746f-a775-2f46-8605-bbb49902c0de.png)


### 1.3 IAM Policy, IAM Role, k8s ServiceAccountの作成

- ALBの操作などが可能なPolicyをアタッチしたIAM Roleを作成し、それをk8sのServiceAccountの権限設定として連携させる。
- Installation Guide通りに設定する。

```
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ curl -o iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/main/docs/install/iam_policy.json
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ aws iam create-policy \
    --policy-name AWSLoadBalancerControllerIAMPolicy \
    --policy-document file://iam-policy.json
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ eksctl create iamserviceaccount \
    --cluster=<cluster-name> \
    --namespace=kube-system \
    --name=aws-load-balancer-controller \
    --attach-policy-arn=arn:aws:iam::<AWS_ACCOUNT_ID>:policy/AWSLoadBalancerControllerIAMPolicy \
    --approve
```
- 結果として、ServiceAccountが作成され、Annotationsを用いてAWS IAMと連携した権限が設定される。

```
[ec2-user@ip-172-16-0-29 ~]$ kubectl get serviceaccount -n kube-system
NAME                                 SECRETS   AGE
～省略～
aws-load-balancer-controller         1         40h
aws-node                             1         12d
certificate-controller               1         12d
～省略～

[ec2-user@ip-172-16-0-29 ~]$ kubectl describe serviceaccount  aws-load-balancer-controller -n kube-system
Name:                aws-load-balancer-controller
Namespace:           kube-system
Labels:              <none>
Annotations:         eks.amazonaws.com/role-arn: arn:aws:iam::XXXXXXXXXXXX:role/eksctl-CLUSTERNAME-addon-iamservicea-Role1-171Z0QTO4GAE5
Image pull secrets:  <none>
Mountable secrets:   aws-load-balancer-controller-token-fbs5d
Tokens:              aws-load-balancer-controller-token-fbs5d
Events:              <none>
```
### 1.4 cert-manager 
- 証明書管理用のツールが必要といったん理解しておく。手順通りにインストールする。namespace, serviceaccount等、各種リソースが作成される。

```
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.2/cert-manager.yaml
```

### 1.5 AWS Load Balancer Controller のインストール
- yamlをダウンロードし、マニュアル通り以下2点を修正した上でkubectl applyを実行する。
    - --cluster-name に自分のEKS Cluster の名前を設定
    - ServiceAccountの箇所を削除(既に上記手順1.3で作成済のため)

```
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/main/docs/install/v2_0_1_full.yaml
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ kubectl apply -f v2_0_1_full.yaml
```
- AWS Load Balancer Controller の pod が作成されていることを確認する。

```
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ kubectl get pods -n kube-system
NAME                                            READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-74588c476f-lhbqv   1/1     Running   0          47h
～略～
```

## 2. AWS Load Balancer Controller の動作確認
### 2.1 公開対象のDeployment/Serviceの作成
- nginx のDeploymentとServiceを適当に作成(ServiceはNodePort)する。

``` nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
```
``` nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
    - port: 80
      protocol: TCP
  type: NodePort
  selector:
    app: nginx
```

```
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx created
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ kubectl apply -f nginx-service.yaml
service/nginx-service created
```

### 2.2 Ingressの作成

- Serviceを外部公開するためのIngressを作成する。

``` nginx-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/tags: Environment=dev,Team=test
spec:
  rules:
    - host: eks.mksambaaws.com (#実際のFQDNは異なる)
      http:
        paths:
          - path: /
            backend:
              serviceName: nginx-service
              servicePort: 80
```

```
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ kubectl apply -f nginx-ingress.yaml
ingress.extensions/nginx-ingress created
```

- Ingressの作成をトリガに、ALBが作成されることを確認する。

```
[ec2-user@ip-172-16-0-29 aws-loadbalancer]$ kubectl get ingress
NAME            CLASS    HOSTS                        ADDRESS                                                                      PORTS   AGE
nginx-ingress   <none>   eks.mksambaaws.com   k8s-default-nginxing-XXXXXXXXX-XXXXXXXXXX.ap-northeast-1.elb.amazonaws.com   80      11m
```

![04a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/6c17f239-bbff-7e2b-13bb-f26da78eabf8.png)

- ALBのFQDN　(k8s-default-nginxing-XXXXXXXXX-XXXXXXXXX.ap-northeast-1.elb.amazonaws.com)　にhttpアクセスしてみるが、nginxの画面が表示されない。
- ALBのルールを確認すると、Ingressを作成するyaml内で指定したFQDN(eks.mksambaaws.com)でアクセスしないと、リクエストが転送されない設定になっている。

![05a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/05c2b1d5-fb31-f40f-7c3f-ef66c9e7c03e.png)

- Route 53で、FQDN(eks.mksambaaws.com) と、作成されたALBを紐づけ登録する。

![03a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/5dd02e4a-60dd-1b25-3fe2-7e370b4d445e.png)

- 登録したFQDN(eks.mksambaaws.com)へhttpアクセスすると、無事nginxの画面が表示される。

![06a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/c44394a4-5fc9-05f1-d1bc-73012e3918ab.png)

### 2.3 https化

- 今回はIngress Controllerから直接https対応のALBを作成することが技術力不足でできなかった。
- 一応手動でALBに以下の設定を追加してhttpsでnginxにアクセスすることはできた。
    - https(443)のリスナー追加、httpと同じターゲットグループへの転送
    - ACMで作成済の証明書を関連付け
    - SecurityGroup(Ingress Controllerが自動作成したもの、Inboundは80/tcpのみ許可)に443/tcpの許可を追加


![07a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/a7ea2e4d-d8de-8d22-f55a-3a7a74d9e790.png)



# 所感
- 最初、従来のALB Ingress Controllerのほうの手順を検索し実施したりして、少し混乱したが、Ingressについても一応使うことができた(https化するきちんとした手順が分からなかったが、、)。他の機能についても確認していきたい。