AWS EKSを活用した高可用性MQTTブローカークラスタの構築
はじめに
IoTチームの高原です。
みなさんは「AWS IoT Coreを使わずにスケーラブルなMQTTブローカーを使いてぇ〜」って思ったことありません?ありますよね?
例えば、デバイスがMQTT over TLSに対応しておらず、閉域網経由で平文のMQTTメッセージが飛んでくる場合です。
調べてみたところEMQXなるMQTTブローカーソフトウェアがあるようなのですが、これをk8s上で管理するためにEMQX Operatorというツールが提供されています。
今回はそれを使わせてもらって、AWS EKS上にいい感じのMQTTブローカークラスターを構築します。
VerneMQもMQTTブローカーのHAクラスターは組めるっぽいのですが、ノードをIPで手動追加する形式だったので今回は採用を見送りました。
k8sは素人なので何か微妙な点があっても手加減してください。
完成物
上記のような感じでAWS EKSを構築します。
で、EKSにEMQXをカスタムリソースとしてデプロイします。
できること
上記のAWSリソース図を見てもらうとわかるのですが、NLBが2つ作成されます。
1つはMQTTブローカーのエンドポイントとして利用できます。
デフォルトだと4種のポートが使えます。
- 1883: 素のMQTT (TCP)
- 8083: MQTT over WebSocket
- 8084: MQTT over WebSocket over TLS (SSL)
- 8883: MQTT over TLS (SSL)
もう1つはEMQXの管理画面にアクセスできるエンドポイントです。
おしゃれなダッシュボードにアクセスできます。
作成手順
ここからはMQTTブローカークラスターの構築を目指して、CLIを叩くだけです。
準備
以下のツールをインストールしておきます。
- kubectl
- k8sクラスターを管理するためのCLI
- eksctl
- EKSクラスターを管理するためのCLI
- helm
- k8sのパッケージマネージャー
VPC, Subnetの作成
aws cloudformation create-stack \
--region ap-northeast-1 \
--stack-name my-eks-vpc-stack \
--template-url https://s3.us-west-2.amazonaws.com/amazon-eks/cloudformation/2020-10-29/amazon-eks-vpc-private-subnets.yaml
上記CloudFormationで以下のネットワークリソースが作成されます。
コンソールを開いてpublic, private subnetのIDを控えておきましょう。
EKSクラスターの作成
まず下記ファイルを用意して、信頼ポリシーの準備をします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "eks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
そしてEKSクラスター用IAM Roleを作成
aws iam create-role \
--role-name my-eks-cluster-role \
--assume-role-policy-document file://eks-cluster-trust-policy.json
aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AmazonEKSClusterPolicy \
--role-name my-eks-cluster-role
そしてEKSクラスターを作成します。
作成したsubnet 4個とsg 1個を指定します。
その後、kubectl用に設定更新。
aws eks create-cluster \
--name my-eks-cluster \
--region ap-northeast-1 \
--role-arn arn:aws:iam::XXXXXXXXXXXX:role/my-eks-cluster-role \
--resources-vpc-config subnetIds=subnet-0XXXXXXXXXXXXXXX1,subnet-0XXXXXXXXXXXXXXX2,subnet-0XXXXXXXXXXXXXXX3,subnet-0XXXXXXXXXXXXXXX4,securityGroupIds=sg-0XXXXXXXXXXXXXXX0
aws eks update-kubeconfig \
--region ap-northeast-1 \
--name my-eks-cluster
ノードグループ(ワーカーノード)の作成
同様に下記ファイルを用意して、信頼ポリシーの準備をします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
もう1つ、別に下記ファイルも用意しておきましょう。
EMQXはデプロイ時にEBSへの書き込みが走るので、それ関連のIAMが必要です。
(ここ詰まりポイントです)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:AttachVolume",
"ec2:CreateSnapshot",
"ec2:CreateTags",
"ec2:CreateVolume",
"ec2:DeleteSnapshot",
"ec2:DeleteTags",
"ec2:DeleteVolume",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInstances",
"ec2:DescribeSnapshots",
"ec2:DescribeTags",
"ec2:DescribeVolumes",
"ec2:DescribeVolumesModifications",
"ec2:DetachVolume",
"ec2:ModifyVolume"
],
"Resource": "*"
}
]
}
ファイルが準備できたら、ノードグループ用IAMを作成します。
aws iam create-role \
--role-name my-eks-node-role \
--assume-role-policy-document file://node-group-trust-policy.json
aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy \
--role-name my-eks-node-role
aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly \
--role-name my-eks-node-role
aws iam attach-role-policy \
--policy-arn arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy \
--role-name my-eks-node-role
aws iam put-role-policy \
--role-name my-eks-node-role \
--policy-name EBSFullAccess \
--policy-document file://ebs-policy.json
続いてノードグループを作成します。
aws eks create-nodegroup \
--region ap-northeast-1 \
--cluster-name my-eks-cluster \
--nodegroup-name my-eks-cluster-node \
--subnets subnet-0XXXXXXXXXXXXXXX1 subnet-0XXXXXXXXXXXXXXX2 \
--node-role arn:aws:iam::XXXXXXXXXXXX:role/my-eks-node-role \
--scaling-config minSize=2,maxSize=2,desiredSize=2 \
--ami-type AL2_x86_64 \
--instance-types t3.medium
ここまでで、下記が完成しました。
k8sサービスアカウントとIAMロールのマッピング
IAMロールをk8sサービスアカウントに関連づけるために、EKSクラスターにOIDCプロバイダーをリンクさせます。
eksctl utils associate-iam-oidc-provider --cluster my-eks-cluster --approve
NLBデプロイ準備
NLB用IAM policyをダウンロードして、IAM Roleを作成します。
curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.7.2/docs/install/iam_policy.json
aws iam create-policy \
--policy-name my-eks-lb-controller-policy \
--policy-document file://iam_policy.json
NLBのIAM Roleをリンクさせた k8sサービスアカウントを作成します。
eksctl create iamserviceaccount \
--cluster=my-eks-cluster \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--role-name my-eks-lb-controller-policy \
--attach-policy-arn=arn:aws:iam::XXXXXXXXXXXX:policy/my-eks-lb-controller-policy \
--approve \
--override-existing-serviceaccounts
クラスターにaws-load-balancer-controllerをインストール。
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=my-eks-cluster \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller
EMQXデプロイ
クラスターにcert-manager
とemqx
をインストール。(参考)
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm upgrade --install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true
helm repo add emqx https://repos.emqx.io/charts
helm repo update
helm upgrade --install emqx-operator emqx/emqx-operator \
--namespace emqx-operator-system \
--create-namespace
さらに下記コマンドでAmazon EBS CSI Driverをクラスターにインストール。
kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/ecr/?ref=release-1.9"
そしてEBS書き込み用のk8sサービスアカウントを準備。
aws iam create-policy \
--policy-name my-eks-ebs-csi-driver-policy \
--policy-document file://ebs-policy.json
eksctl create iamserviceaccount \
--name ebs-csi-controller-sa \
--namespace kube-system \
--cluster my-eks-cluster \
--attach-policy-arn arn:aws:iam::XXXXXXXXXXXX:policy/my-eks-ebs-csi-driver-policy \
--approve \
--region ap-northeast-1
下記yamlファイルを用いて、カスタムリソースとしてemqxをapply。(参考)
aws-load-balancer-subnets
にはprivate subnetを指定。
apiVersion: apps.emqx.io/v2beta1
kind: EMQX
metadata:
name: emqx
spec:
image: emqx:5
coreTemplate:
spec:
## EMQX custom resources do not support updating this field at runtime
volumeClaimTemplates:
## More content: https://docs.aws.amazon.com/eks/latest/userguide/storage-classes.html
## Please manage the Amazon EBS CSI driver as an Amazon EKS add-on.
## For more documentation please refer to: https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/managing-ebs-csi.html
storageClassName: gp2
resources:
requests:
storage: 10Gi
accessModes:
- ReadWriteOnce
dashboardServiceTemplate:
metadata:
## More content: https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/guide/service/annotations/
annotations:
## Specifies whether the NLB is Internet-facing or internal. If not specified, defaults to internal.
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
## Specify the availability zone to which the NLB will route traffic. Specify at least one subnet, either subnetID or subnetName (subnet name label) can be used.
service.beta.kubernetes.io/aws-load-balancer-subnets: subnet-0XXXXXXXXXXXXXXX3,subnet-0XXXXXXXXXXXXXXX4
spec:
type: LoadBalancer
## More content: https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/guide/service/nlb/
loadBalancerClass: service.k8s.aws/nlb
listenersServiceTemplate:
metadata:
## More content: https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/guide/service/annotations/
annotations:
## Specifies whether the NLB is Internet-facing or internal. If not specified, defaults to internal.
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
## Specify the availability zone to which the NLB will route traffic. Specify at least one subnet, either subnetID or subnetName (subnet name label) can be used.
service.beta.kubernetes.io/aws-load-balancer-subnets: subnet-0XXXXXXXXXXXXXXX3,subnet-0XXXXXXXXXXXXXXX4
spec:
type: LoadBalancer
## More content: https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/guide/service/nlb/
loadBalancerClass: service.k8s.aws/nlb
下記コマンドでデプロイ!
kubectl apply -f ./emqx.yaml
listenersServiceTemplate
の方に作成されたエンドポイントをMQTTブローカーとして使用できます。
dashboardServiceTemplate
の方に作成されたエンドポイントには18083でダッシュボードがホスティングされています。
終わり
これにてEMQXをAWS EKSにデプロイできました。
以上で手順は終了です。お疲れ様でした。
Discussion