この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS上でKubernetesのLoadBalancer Serviceを利用すると、CLB/NLBを簡単に作成することができます。
ですが、特別な理由がない限りはL7の機能が強化されたALBを利用したい場面が多いのではないでしょうか。
ということで今回は、先日EKSでの利用が正式にサポートされたAWS ALB Ingress Controllerを使ってALBを作成する方法を紹介したいと思います。
今回構築する環境のイメージ
今回は以下の作業を実施します。
- EKSクラスタ構築
- AWS ALB Ingress Controllerのデプロイ
- サンプルアプリケーションのデプロイ
- ALB作成
AWS ALB Ingress ControllerによりALBを作成し、サンプルアプリケーションに対しパスベースルーティングができることを確認します。
では早速やっていきましょう!
EKSクラスタ構築
まずはEKSクラスタを構築します。今回はeksctlを利用しサクッと構築していきます。
$ eksctl create cluster --name=k8s-cluster --nodes=2 --node-type=t2.medium
2つのWorkerノードをクラスタ化した状態からスタートします。 kubectlでの実行結果は以下のようになります。
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-21-187.ap-northeast-1.compute.internal Ready <none> 8m v1.11.5
ip-192-168-57-228.ap-northeast-1.compute.internal Ready <none> 8m v1.11.5
$ kubectl get all
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 14m
パブリックサブネットへのタグ追加
ALB Ingress ControllerがALBに使用されるサブネットを自動検出できるようにパブリックサブネットにタグkubernetes.io/role/elb
を付与します。
AWS ALB Ingress Controllerのデプロイ
AWS ALB Ingress Controllerとは
IngressリソースがEKSに登録されたタイミングでALBを作成するモジュールです。
AWS ALB Ingress ControllerはEKSクラスタにPodとして起動させます。コントローラはAPIサーバーと通信し、要件を満たすIngressリソースを見つけるとALBの作成を開始します。
詳細は公式ドキュメント を参照してください。
Workerノードへのポリシー追加
WorkerノードにALBを作成するための権限を付与します。 IAMよりポリシーを作成し、WorkerノードのIAMロールに作成したポリシーをアタッチします。
ServiceAccount作成
AWS ALB Ingress Controllerで利用するServiceAccountを作成し、そのServiceAccountに対しRoleをバインドします。
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.0.0/docs/examples/rbac-role.yaml
clusterrole.rbac.authorization.k8s.io/alb-ingress-controller created
clusterrolebinding.rbac.authorization.k8s.io/alb-ingress-controller created
serviceaccount/alb-ingress created
AWS ALB Ingress Controllerのデプロイ
ローカルにマニュフェストファイルをダウンロードします。
$ curl -sS "https://raw.githubusercontent.com/kubernetes-sigs/aws-alb-ingress-controller/v1.0.0/docs/examples/alb-ingress-controller.yaml" > alb-ingress-controller.yaml
–cluster-name
の部分を今回作成したクラスタ名に変更した後、マニュフェストをapplyします。
$ kubectl apply -f alb-ingress-controller.yaml
deployment.apps/alb-ingress-controller created
デプロイが完了すると名前空間kube-systemにAWS ALB Ingress ControllerのPodが作成されます。
$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system alb-ingress-controller-77666996cf-ksv4b 1/1 Running 0 3m
kube-system aws-node-nmwjv 1/1 Running 1 24m
kube-system aws-node-q97ss 1/1 Running 1 24m
kube-system coredns-7774b7957b-4kh8p 1/1 Running 0 29m
kube-system coredns-7774b7957b-rx8l4 1/1 Running 0 29m
kube-system kube-proxy-8gfjx 1/1 Running 0 24m
kube-system kube-proxy-kwwf7 1/1 Running 0 24m
サンプルアプリケーションのデプロイ
パスベースルーティングを確認するため簡単なアプリケーションを作成します。作成したアプリケーションはECR経由でEKSにデプロイします。
ECR作成
ECRを作成しECRにログインします。
$ aws ecr create-repository --repository-name eks-test-app
{
"repository": {
"repositoryArn": "arn:aws:ecr:ap-northeast-1:XXXXXXXXXXXX:repository/eks-test-app",
"registryId": "XXXXXXXXXXXX",
"repositoryName": "eks-test-app",
"repositoryUri": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/eks-test-app",
"createdAt": 1546156701.0
}
}
$ aws ecr get-login --no-include-email
$ docker login -u AWS -p XXXXXX
アプリケーション作成
httpレスポンスとしてPod名を返却するWebサーバーを作成します。まずは/target1
のリクエストに対応するアプリケーションを作成します。
server.go
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "healthy!")
})
http.HandleFunc("/target1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "/target1:" + os.Getenv("POD_NAME"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
ビルド用のDockerfileを作成します。
Dockerfile
FROM golang
ADD . /go/src/
EXPOSE 8080
CMD ["/usr/local/go/bin/go", "run", "/go/src/server.go"]
タグtarget1
でビルドし、ECRにPushします。
$ docker build -t eks-test-app:target1 .
$ docker tag eks-test-app:target1 XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/eks-test-app:target1
$ docker push XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/eks-test-app:target1
/target2
のリクエストに対応するアプリケーションも同様に作成し、タグtarget2
でビルド後、ECRにデプロイします。
マニュフェスト作成
アプリケーションをデプロイするためのマニュフェストを作成します。
マニュフェストにはtarget1
,target2
のリクエストに対応するDeploymentおよびServiceを定義します。また、Deploymentで指定するDockerImageには先ほど作成したアプリケーションを指定し、Podの環境変数にはPOD_NAME
を設定します。
test-app.yaml
apiVersion: v1
kind: Namespace
metadata:
name: "test-app"
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: "test-app-deployment-target1"
namespace: "test-app"
spec:
replicas: 2
template:
metadata:
labels:
app: "test-app-target1"
spec:
containers:
- image: XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/eks-test-app:target1
imagePullPolicy: Always
name: "test-app-target1"
ports:
- containerPort: 8080
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: "test-app-deployment-target2"
namespace: "test-app"
spec:
replicas: 2
template:
metadata:
labels:
app: "test-app-target2"
spec:
containers:
- image: XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/eks-test-app:target2
imagePullPolicy: Always
name: "test-app-target2"
ports:
- containerPort: 8080
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
---
apiVersion: v1
kind: Service
metadata:
name: "test-app-service-target1"
namespace: "test-app"
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: NodePort
selector:
app: "test-app-target1"
---
apiVersion: v1
kind: Service
metadata:
name: "test-app-service-target2"
namespace: "test-app"
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: NodePort
selector:
app: "test-app-target2"
アプリケーションのデプロイ
アプリケーションをデプロイします。
$ kubectl apply -f test-app.yaml
namespace/test-app unchanged
deployment.extensions/test-app-deployment-target1 created
deployment.extensions/test-app-deployment-target2 created
service/test-app-service-target1 created
service/test-app-service-target2 created
$ kubectl get all --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system pod/alb-ingress-controller-77666996cf-ksv4b 1/1 Running 0 1h
kube-system pod/aws-node-nmwjv 1/1 Running 1 1h
kube-system pod/aws-node-q97ss 1/1 Running 1 1h
kube-system pod/coredns-7774b7957b-4kh8p 1/1 Running 0 1h
kube-system pod/coredns-7774b7957b-rx8l4 1/1 Running 0 1h
kube-system pod/kube-proxy-8gfjx 1/1 Running 0 1h
kube-system pod/kube-proxy-kwwf7 1/1 Running 0 1h
test-app pod/test-app-deployment-target1-778f6fddc7-p766m 1/1 Running 0 1m
test-app pod/test-app-deployment-target1-778f6fddc7-txkb7 1/1 Running 0 1m
test-app pod/test-app-deployment-target2-7589dcc48d-6ptwx 1/1 Running 0 1m
test-app pod/test-app-deployment-target2-7589dcc48d-8n5qn 1/1 Running 0 1m
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 1h
kube-system service/kube-dns ClusterIP 10.100.0.10 <none> 53/UDP,53/TCP 1h
test-app service/test-app-service-target1 NodePort 10.100.38.252 <none> 80:30583/TCP 1m
test-app service/test-app-service-target2 NodePort 10.100.124.165 <none> 80:32110/TCP 1m
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-system daemonset.apps/aws-node 2 2 2 2 2 <none> 1h
kube-system daemonset.apps/kube-proxy 2 2 2 2 2 <none> 1h
NAMESPACE NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kube-system deployment.apps/alb-ingress-controller 1 1 1 1 1h
kube-system deployment.apps/coredns 2 2 2 2 1h
test-app deployment.apps/test-app-deployment-target1 2 2 2 2 1m
test-app deployment.apps/test-app-deployment-target2 2 2 2 2 1m
NAMESPACE NAME DESIRED CURRENT READY AGE
kube-system replicaset.apps/alb-ingress-controller-77666996cf 1 1 1 1h
kube-system replicaset.apps/coredns-7774b7957b 2 2 2 1h
test-app replicaset.apps/test-app-deployment-target1-778f6fddc7 2 2 2 1m
test-app replicaset.apps/test-app-deployment-target2-7589dcc48d 2 2 2 1m
ALB作成
ingressのマニュフェストを作成します。ALBはパスベースでターゲットを切り替える設定とします。
ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: "ingress"
namespace: "test-app"
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing
labels:
app: test-app
spec:
rules:
- http:
paths:
- path: /target1
backend:
serviceName: "test-app-service-target1"
servicePort: 80
- path: /target2
backend:
serviceName: "test-app-service-target2"
servicePort: 80
マニュフェストをapplyします。
$ kubectl apply -f ingress.yaml
しばらくするとALBおよびターゲットグループが作成されます。
$ aws elbv2 describe-load-balancers
{
"LoadBalancers": [
{
"LoadBalancerArn": "arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:loadbalancer/app/b0a25c21-testapp-ingress-1d16/8e4a9e9c192eae33",
"DNSName": "b0a25c21-testapp-ingress-1d16-2135925699.ap-northeast-1.elb.amazonaws.com",
"CanonicalHostedZoneId": "Z14GRHDCWA56QT",
"CreatedTime": "2018-12-30T08:54:13.480Z",
"LoadBalancerName": "b0a25c21-testapp-ingress-1d16",
"Scheme": "internet-facing",
"VpcId": "vpc-02498dac27677a79a",
"State": {
"Code": "active"
},
"Type": "application",
"AvailabilityZones": [
{
"ZoneName": "ap-northeast-1c",
"SubnetId": "subnet-028193ac333ae6241"
},
{
"ZoneName": "ap-northeast-1d",
"SubnetId": "subnet-0aaf687b20bb26445"
},
{
"ZoneName": "ap-northeast-1a",
"SubnetId": "subnet-0c0df73a4671b923c"
}
],
"SecurityGroups": [
"sg-0c25b1682e9fd7b72"
],
"IpAddressType": "ipv4"
}
]
}
$ aws elbv2 describe-target-groups
{
"TargetGroups": [
{
"TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:targetgroup/b0a25c21-ce379bdf1fac9fac389/f0458909d46b1a19",
"TargetGroupName": "b0a25c21-ce379bdf1fac9fac389",
"Protocol": "HTTP",
"Port": 1,
"VpcId": "vpc-02498dac27677a79a",
"HealthCheckProtocol": "HTTP",
"HealthCheckPort": "traffic-port",
"HealthCheckEnabled": true,
"HealthCheckIntervalSeconds": 15,
"HealthCheckTimeoutSeconds": 5,
"HealthyThresholdCount": 2,
"UnhealthyThresholdCount": 2,
"HealthCheckPath": "/",
"Matcher": {
"HttpCode": "200"
},
"LoadBalancerArns": [
"arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:loadbalancer/app/b0a25c21-testapp-ingress-1d16/8e4a9e9c192eae33"
],
"TargetType": "instance"
},
{
"TargetGroupArn": "arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:targetgroup/b0a25c21-daec225fa0154bfbe17/55129d7eec6da764",
"TargetGroupName": "b0a25c21-daec225fa0154bfbe17",
"Protocol": "HTTP",
"Port": 1,
"VpcId": "vpc-02498dac27677a79a",
"HealthCheckProtocol": "HTTP",
"HealthCheckPort": "traffic-port",
"HealthCheckEnabled": true,
"HealthCheckIntervalSeconds": 15,
"HealthCheckTimeoutSeconds": 5,
"HealthyThresholdCount": 2,
"UnhealthyThresholdCount": 2,
"HealthCheckPath": "/",
"Matcher": {
"HttpCode": "200"
},
"LoadBalancerArns": [
"arn:aws:elasticloadbalancing:ap-northeast-1:XXXXXXXXXXXX:loadbalancer/app/b0a25c21-testapp-ingress-1d16/8e4a9e9c192eae33"
],
"TargetType": "instance"
}
]
}
最後に正しくルーティングができているか確認してみましょう。
$ kubectl get pod --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system alb-ingress-controller-77666996cf-ksv4b 1/1 Running 0 1h
kube-system aws-node-nmwjv 1/1 Running 1 1h
kube-system aws-node-q97ss 1/1 Running 1 1h
kube-system coredns-7774b7957b-4kh8p 1/1 Running 0 2h
kube-system coredns-7774b7957b-rx8l4 1/1 Running 0 2h
kube-system kube-proxy-8gfjx 1/1 Running 0 1h
kube-system kube-proxy-kwwf7 1/1 Running 0 1h
test-app test-app-deployment-target1-778f6fddc7-p766m 1/1 Running 0 27m
test-app test-app-deployment-target1-778f6fddc7-txkb7 1/1 Running 0 27m
test-app test-app-deployment-target2-7589dcc48d-6ptwx 1/1 Running 0 27m
test-app test-app-deployment-target2-7589dcc48d-8n5qn 1/1 Running 0 27m
# /target1に対するリクエストはtarget1のpodにルーティングされることを確認
$ curl http://b0a25c21-testapp-ingress-1d16-2135925699.ap-northeast-1.elb.amazonaws.com/target1
/target1:test-app-deployment-target1-778f6fddc7-p766m
$ curl http://b0a25c21-testapp-ingress-1d16-2135925699.ap-northeast-1.elb.amazonaws.com/target1
/target1:test-app-deployment-target1-778f6fddc7-txkb7
# /target2に対するリクエストはtarget2のpodにルーティングされることを確認
$ curl http://b0a25c21-testapp-ingress-1d16-2135925699.ap-northeast-1.elb.amazonaws.com/target2
/target2:test-app-deployment-target2-7589dcc48d-8n5qn
$ curl http://b0a25c21-testapp-ingress-1d16-2135925699.ap-northeast-1.elb.amazonaws.com/target2
/target2:test-app-deployment-target2-7589dcc48d-6ptwx
リクエストがパス毎に正しくルーティングされることが確認できました!!
さいごに
AWS ALB Ingress Controllerを使ってEKSからALBを作成する方法を紹介しました。
AWS ALB Ingress Controllerを利用するとCFnやTerraformを使わずALBを作成することができます。Kubernetesのマニュフェストという統一されたフォーマットでリソースを作成することで、リソースの管理/運用もしやすくなるのではないでしょうか。
Service Brokerあたりもうまく利用することで、初期構築以外ではCFnやTerraformを利用しない!そんな時代がくるかもしれませんね。