aws EKS で いろいろなロードバランサー構成を試してみた

AWS EKSで使えるロードバランサーには複数の種類があって、どう構成するべきか悩みどころなので色々試してみました。

## 今回使ったKubernetesのロードバランサーの種類

今回は以下の３種類をつかって、ロードバランサーの構成パターンを試してみました。
これ以外の種類もあります。

| ロードバランサー     | 種類 | 導入するController |
|:-----------------|:-----------------:|:------------------|
| aws ALB (Application Load Balancer)| Ingress(L7)| AWS ALB Ingress Controller |
| aws NLB (Network Load Balancer) | Service(L4)| 不要 |
| NGINX Ingress Controller  | Ingress(L7)| NGINX Ingress Controller |

## aws ALB（L7）構成
一番シンプルに構築できるロードバランサー構成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/9d806aef-6060-2981-7b30-1c12ec0b2010.png)

- 良い点
    - 管理する対象が一つで済むので、他の構成より障害のリスクが小さくなる
    - IPモードにすると、Podへ直接ロードバランシングしてくれるのでオーバーヘッドを少なくできる
    - AWSのセキュリティグループが使える

- 懸念事項
    - 異なるNamespaceのService（Podも含む）を混在出来ないので、ALBをNamespace毎に作成する必要がある（費用が増える）
    - rewrite-targetが使えない

```yaml:Deployment+Service(app-a).yaml
apiVersion: v1
kind: Namespace
metadata:
  name: app-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: app-a
  name: app-a
  labels:
    run: app-a
spec:
  replicas: 3
  selector:
    matchLabels:
      run: app-a
  template:
    metadata:
      labels:
        run: app-a
    spec:
      containers:
      - image: nginx
        name: app-a
---
apiVersion: v1
kind: Service
metadata:
  namespace: app-a
  name: app-a
  labels:
    run: app-a
spec:
  type: NodePort
  selector:
    run: app-a
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
```

```yaml:ALB.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: app-a
  name: alb-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
spec:
  rules:
    - http:
        paths:
          - path: /*
            backend:
              serviceName: app-a
              servicePort: 80
```

## aws ALB(L7) + Nginx Ingress(L7)構成  
aws ALBの良いところを活かしつつ、ALBの懸念事項をNginx Ingress Controllerがカバーする構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/62947b5b-9051-4651-bceb-df0a06cedd7b.png)

- 良い点
    - 異なるNamespaceのService（Podも含む）を混在させることが可能。AWS上で作成するLBの数を減らすことができる
    - rewrite-targetが使える
    - AWSのセキュリティグループが使える

- 懸念事項
    - NGINX Ingress ControllerのPodの可用性を考える必要がある

```yaml:ALB.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: kube-system
  name: alb-ingress
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
spec:
  rules:
    - http:
        paths:
          - backend:
              serviceName:  （NGINX Ingress Controllerのサービス名）
              servicePort: 80
```

```yaml:Deployment+Service(app-a).yaml
apiVersion: v1
kind: Namespace
metadata:
  name: app-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: app-a
  name: app-a
  labels:
    run: app-a
spec:
  replicas: 3
  selector:
    matchLabels:
      run: app-a
  template:
    metadata:
      labels:
        run: app-a
    spec:
      containers:
      - image: nginx
        name: app-a
---
apiVersion: v1
kind: Service
metadata:
  namespace: app-a
  name: app-a
  labels:
    run: app-a
spec:
  type: ClusterIP
  selector:
    run: app-a
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
```

```yaml:NGINX_Ingress(app-a).yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: app-a
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: （ALBのDNS名）
    http:
      paths:
      - path: /app-a(/|$)(.*)
        backend:
          serviceName: app-a
          servicePort: 80
```

```yaml:Deployment+Service(app-b).yaml
apiVersion: v1
kind: Namespace
metadata:
  name: app-b
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: app-b
  name: app-b
  labels:
    run: app-b
spec:
  replicas: 3
  selector:
    matchLabels:
      run: app-b
  template:
    metadata:
      labels:
        run: app-b
    spec:
      containers:
      - image: nginx
        name: app-b
---
apiVersion: v1
kind: Service
metadata:
  namespace: app-b
  name: app-b
  labels:
    run: app-b
spec:
  type: ClusterIP
  selector:
    run: app-b
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
```

```yaml:NGINX_Ingress(app-b).yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: app-b
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: （ALBのDNS名）
    http:
      paths:
      - path: /app-b(/|$)(.*)
        backend:
          serviceName: app-b
          servicePort: 80
```


## aws NLB(L4) + Nginx Ingress(L7)構成
aws ALB ＋ Nginx Ingressとよく似ている。細かい点で一長一短あるものの、Google検索してみるとALBの方が関連記事も多く、事例が多い感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120824/cdbafdde-3083-d507-6ea1-d27a5b0cc6e6.png)

- 良い点
    - 外部LBのIPアドレスを固定にできるので、IPを制限してセキュリティを向上させることがやりやすい
    - 異なるNamespaceのService（Podも含む）を混在させることが可能。AWS上で作成するLBの数を減らすことができる
    - rewrite-targetが使える

- 懸念事項
    - NGINX Ingress ControllerのPodの可用性を考える必要がある
    - AWSのセキュリティグループが使えない

```yaml:NLB.yaml
apiVersion: v1
kind: Service
metadata:
  namespace: kube-system
  name: nlb-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
  type: LoadBalancer
  selector:
    release: nginx-ingress-controller
  ports:
  - name: http
    port: 80
    protocol: TCP
```

```yaml:Deployment+Service(app-a).yaml
apiVersion: v1
kind: Namespace
metadata:
  name: app-a
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: app-a
  name: app-a
  labels:
    run: app-a
spec:
  replicas: 3
  selector:
    matchLabels:
      run: app-a
  template:
    metadata:
      labels:
        run: app-a
    spec:
      containers:
      - image: nginx
        name: app-a
---
apiVersion: v1
kind: Service
metadata:
  namespace: app-a
  name: app-a
  labels:
    run: app-a
spec:
  type: ClusterIP
  selector:
    run: app-a
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
```

```yaml:NGINX_Ingress(app-a).yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: app-a
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: （NLBのDNS名）
    http:
      paths:
      - path: /app-a(/|$)(.*)
        backend:
          serviceName: app-a
          servicePort: 80
```

```yaml:Deployment+Service(app-b).yaml
apiVersion: v1
kind: Namespace
metadata:
  name: app-b
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: app-b
  name: app-b
  labels:
    run: app-b
spec:
  replicas: 3
  selector:
    matchLabels:
      run: app-b
  template:
    metadata:
      labels:
        run: app-b
    spec:
      containers:
      - image: nginx
        name: app-b
---
apiVersion: v1
kind: Service
metadata:
  namespace: app-b
  name: app-b
  labels:
    run: app-b
spec:
  type: ClusterIP
  selector:
    run: app-b
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
```

```yaml:NGINX_Ingress(app-b).yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: app-b
  name: nginx-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - host: （NLBのDNS名）
    http:
      paths:
      - path: /app-b(/|$)(.*)
        backend:
          serviceName: app-b
          servicePort: 80
```

# 感想
個人的な感想としては、なるべくシンプルな構成で作成するLBの数を減らすことが出来てセキュリティグループも使える「aws ALB + Nginx Ingress構成」が使いやすいのかとは思います。
ただ、スロットリングなどを実装しようとすると、また色々考えなければならないなと思います。AWS側で制御させるのであればAPI Gatewayを組み合わせるなどのパターンもありかもしれません。

## 参考
Amazon EKSでIngress Controllerに何を採用するか検討した
https://tech.recruit-mp.co.jp/infrastructure/post-19310/

Namespaceの異なるIngressリソースを1つのALBで対応する
https://qiita.com/samskeyti/items/4b5b25f92f66bbaa9a36

API Gateway as an Ingress Controller for Amazon EKS
https://aws.amazon.com/jp/blogs/opensource/network-load-balancer-nginx-ingress-controller-eks/