こんにちは Dapr Advent Calendar 19日目です。ついに最後の1週間に突入しました。3日目くらいの時に「ちょっとこれ無理かな」とか思ってたんですけど、人間、やってみれば意外といけるもんですね!
DaprをAmazon EKSで運用する
さて、これまでk8s + Daprのアプリケーションをローカルのminikube上で動かしてきましたが、今回はAmazon EKS上で動かしてみます。Daprはマイクロソフト が中心に開発しているOSS なので、Azureのほうがサポートされているミドルウェア が多かったりドキュメントが多かったりするのですが、AWS でも特に問題なく運用できています。
今回動かすアプリケーション
環境設定
事前準備
まず前提として、次のことは完了済みとします。
AWS アカウントの作成
AWS CLI のインストール
aws configureが済んでいて、aws コマンドが実行できる(~/.aws /credentialsに必要な情報を記載済み)
AWS を日常的に利用している人は、この辺りは既に終わっていると思いますので、説明は割愛します。
また、これまでのAdvent Calendarで利用してきた次のツールもインストール済みとします。
もしまだインストールしていなければ、インストールしておいてください。
eksctlのインストール
上に記載したツール群に加えて、eksctlというCLI ツールをインストールしてください。これはAmazon EKSをコマンドライン で操作するもので、ちょうどminikubeのCLI に近いものです。
https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html
これで準備は完了です。
EKS環境の構築
まず、eksctlコマンドを使ってEKSクラスタ を作成します。
eksctl create cluster \
--name=dapr-test \
--version 1.21 \
--region ap-northeast-1 \
--zones ap-northeast-1a,ap-northeast-1c
僕のアカウントでは --zones
オプションをつけずに実行したら「ap-northeast-1bにキャパシティがないよ」というエラーが起きたので、AZとして -1a
と -1c
を指定するようにしました。
なお、クラスタ の作成に失敗すると基本的にはロールバック されるのですが、状況次第ではCloudFormationが削除されないことがあるので、残っているようなら手で削除してください。
ちなみに作成したEKSクラスタ を放置すると(m5.largeのEC2インスタンス 2つも合わせて)月2万円くらい掛かるので、不要になればすぐにEKSクラスタ を削除して想定外の課金にならないよう気をつけてくださいね。
kubectlからEKSクラスタ にアクセスする
eksctlでk8s のクラスタ を作成すると、kubectlの向き先が作成したEKSクラスタ になっているはずです。念のため確認しておきましょう。
kubectl config current-context
次のように表示されるはずです。
(AWSアカウント名)@dapr-test.ap-northeast-1.eksctl.io
もしminikubeなど別のクラスタ を参照している場合は、kubectl config get-contexts
でEKSのクラスタ 名を確認したうえで
kubectl config use-context (AWSアカウント名)@dapr-test.ap-northeast-1.eksctl.io
などでkubectlの向き先がEKSにするようにしてください。
EKSクラスタ を他の人(アカウント)が作成した場合など、手元のコンテキスト一覧にEKSクラスタ がない場合は、次のコマンドでコンテキストを取得してください。
aws eks --region ap-northeast-1 update-kubeconfig --name dapr-test
これでkubectlからEKSにアクセスできるようになるはずです。
クラスタ 情報を確認しておきましょう。
kubectl cluster-info
次のように表示されます。
Kubernetes control plane is running at https://(アカウントID).gr7.ap-northeast-1.eks.amazonaws.com
CoreDNS is running at https://(アカウントID).gr7.ap-northeast-1.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
これでkubectlからEKSにアクセスしていることを確認できました。
Dapr環境の構築
それではEKS上にDapr環境を構築しましょう。
dapr init -k
オプションの -k
でk8s を対象とします。kubectlが向いているk8s 上にDaprの環境が構築されます。
初期化が終わったら、podの一覧を見てみましょう。
kubectl get pods -A
次のように表示されるはずです。
NAMESPACE NAME READY STATUS RESTARTS AGE
dapr-system dapr-dashboard-57b4db56fc-vxmcs 1/1 Running 0 89s
dapr-system dapr-operator-5b4b68b5c5-cgh28 1/1 Running 0 89s
dapr-system dapr-placement-server-0 1/1 Running 0 89s
dapr-system dapr-sentry-c6b746cdf-pxgdx 1/1 Running 0 89s
dapr-system dapr-sidecar-injector-6f749dbf87-qcg7r 1/1 Running 0 89s
kube-system aws-node-fp8m2 1/1 Running 0 15m
kube-system aws-node-rp5qq 1/1 Running 0 15m
kube-system coredns-76f4967988-7psgm 1/1 Running 0 24m
kube-system coredns-76f4967988-fjlrb 1/1 Running 0 24m
kube-system kube-proxy-xk8wm 1/1 Running 0 15m
kube-system kube-proxy-ztkr8 1/1 Running 0 15m
Daprやk8s 関係のpodが起動していることが分かります。
これでEKS上でDaprを動かす準備ができました。
アプリケーションのデプロイ
続いて、Hello World アプリケーションと、それを呼び出すInvoke アプリケーションをEKS上で動かしてみましょう。ソースコード はGitHub にあります。
https://github.com/cero-t/dapr-advent-2021
この中にある「hello」と「invoke 」を利用します。
作成したアプリケーションのおさらい
helloアプリケーション
helloアプリケーションの HelloController
をおさらいしておきましょう。
(hello) HelloController.java
@RestController
public class HelloController {
@GetMapping( "/hello" )
public Map<String, String> hello() {
return Map.of("message" , "Hello, world!" );
}
}
/hello
というエンドポイントでメッセージを返すだけのアプリケーションです。
invoke アプリケーションの InvokeController
もおさらいしておきましょう。
(invoke ) InvokeController.java
@RestController
public class InvokeController {
private RestTemplate restTemplate;
@Value( "${baseUrl}" )
private String baseUrl;
public InvokeController(RestTemplate restTemplate) {
this .restTemplate = restTemplate;
}
@GetMapping( "/invokeHello" )
public Map<String, ?> invokeHello() {
Map<?, ?> result = restTemplate.getForObject(baseUrl + "/hello" , Map.class );
return Map.of("baseUrl" , baseUrl, "remoteMessage" , result);
}
}
/invokeHello
というエンドポイントで、上の /hello
を呼ぶ処理を行うアプリケーションです。
baseurl
は application.yaml
と application-dapr.yaml
に定義されており、Springのプロファイルが dapr
の時は次の設定が利用されます。
(invoke ) application-dapr.yaml
baseUrl=http://localhost:${DAPR_HTTP_PORT}/v1.0/invoke/hello-app/method
DaprのInvoke API を利用したURLです。これを使ってhelloアプリケーションを呼び出します。
アプリケーションのイメージ作成
イメージの置き場所となるECRの作成
続いて、アプリケーションのイメージを作成したいのですが、もちろんEKSからローカルPCにあるイメージレジストリ を参照できるわけではありません。EKSを使う場合には、Docker HubやAmazon ECRなどを使うことになります。ここではEKSと距離の近いECRにイメージを置くことにしましょう。
次のコマンドでECRにリポジトリ を作成します。リポジトリ はイメージごとに作成する必要があるため hello
と invoke
という名前のリポジトリ をそれぞれ作成します。
export AWS_REGION=ap-northeast-1
aws ecr create-repository --repository-name hello --region ap-northeast-1
aws ecr create-repository --repository-name invoke --region ap-northeast-1
これでリポジトリ の作成は完了です。
イメージの作成
続いてイメージを作成します。上で作ったECRのリポジトリ にpushできるよう、イメージ名をECRリポジトリ のURI に合わせる必要があります。
まずはhelloアプリケーションのイメージ作成です。次のようなコマンドになります。
cd (GitHubのディレクトリパス)/dapr-advent-2021
export HELLO_IMAGE=$(aws ecr describe-repositories --repository-names hello --query 'repositories[0].repositoryUri' --output text --region ap-northeast-1)
./mvnw spring-boot:build-image -pl hello -Dspring-boot.build-image.imageName=${HELLO_IMAGE}:1.0.0
(オプションが多くて長くなるため、改行を挟んでいます)
aws コマンドを用いてECRリポジトリ のURLを取得し、それを環境変数 HELLO_IMAGE
として保持します。リポジトリ のURLは (アカウントID).dkr.ecr.ap-northeast-1.amazonaws.com/(リポジトリ名)
となっています。
そして、Maven の spring-boot:build-image
コマンドでイメージを作成するのですが、ここで -Dspring-boot.build-image.imageName
オプションを使い、上で取得したECRリポジトリ のURLをイメージ名として指定しています。
イメージの作成に成功したら、invoke アプリケーションも同様にイメージを作成します。
export INVOKE_IMAGE=$(aws ecr describe-repositories --repository-names invoke --query 'repositories[0].repositoryUri' --output text --region ap-northeast-1)
./mvnw spring-boot:build-image -pl invoke -Dspring-boot.build-image.imageName=${INVOKE_IMAGE}:1.0.0
作成が完了したら、念のため確認しておきましょう。
docker images | grep hello
(アカウントID).dkr.ecr.ap-northeast-1.amazonaws.com/hello 1.0.0 d62af7e6faa8 41 years ago 261MB
hello 1.0.0 73b3e3b9795a 41 years ago 261MB
docker images | grep invoke
(アカウントID).dkr.ecr.ap-northeast-1.amazonaws.com/invoke 1.0.0 d38a74e45a8f 41 years ago 261MB
invoke 1.0.0 bf464e49d0e6 41 years ago 261MB
ECRのURI がついたイメージができているはずです。もし以前に作ったhelloやinvoke のイメージがあれば、一緒に表示されます。
イメージのプッシュ
それでは作成したイメージをECRにプッシュしましょう。ECRにログインしてからでないとプッシュできないので、次のコマンドでログインをします。
export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin https://${ACCOUNT_ID}.dkr.ecr.ap-northeast-1.amazonaws.com
アカウントIDやログインパス ワードをaws コマンドで取得して、それを使ってログインしています。
これでログインに成功したら、イメージをプッシュします。
docker push ${HELLO_IMAGE}:1.0.0
docker push ${INVOKE_IMAGE}:1.0.0
イメージを作成した際に使用した環境変数 をここでも使っています。
これでECRにイメージがプッシュされました。
アプリケーションをEKSにデプロイ
それでは、ECRにプッシュしたイメージを使って、EKS上にアプリケーションをデプロイしてみましょう。
helloアプリケーションのマニフェスト ファイルを作成
helloアプリケーションをデプロイするためのマニフェスト ファイルを次のように作成します。
eks/hello-app.yaml
apiVersion : apps/v1
kind : Deployment
metadata :
name : hello-app
labels :
app : hello
spec :
replicas : 1
selector :
matchLabels :
app : hello
template :
metadata :
labels :
app : hello
annotations :
dapr.io/enabled : "true"
dapr.io/app-id : "hello-app"
dapr.io/app-port : "8080"
spec :
containers :
- name : hello
image : ***.dkr.ecr.ap-northeast-1.amazonaws.com/hello:1.0.0
ports :
- containerPort : 8080
imagePullPolicy : IfNotPresent
以前に作ったものと同様です。イメージ名にECRリポジトリ のURI を指定する必要があるため ***
の部分を自分のアカウントIDに置き換えてください。また、helloアプリケーションには外部からアクセスしないため、Serviceは作成しません。
続いて、invoke アプリケーション用のマニフェスト ファイルを作成します。
eks/invoke -app.yaml
apiVersion : apps/v1
kind : Deployment
metadata :
name : invoke-app
labels :
app : invoke
spec :
replicas : 1
selector :
matchLabels :
app : invoke
template :
metadata :
labels :
app : invoke
annotations :
dapr.io/enabled : "true"
dapr.io/app-id : "invoke-app"
dapr.io/app-port : "8081"
spec :
containers :
- name : invoke
image : ***.dkr.ecr.ap-northeast-1.amazonaws.com/invoke:1.0.0
ports :
- containerPort : 8081
imagePullPolicy : IfNotPresent
env :
- name : spring.profiles.active
value : dapr
---
kind : Service
apiVersion : v1
metadata :
name : invoke-svc
labels :
app : invoke
spec :
selector :
app : invoke
ports :
- protocol : TCP
port : 8081
targetPort : 8081
type : LoadBalancer
これも以前に作ったものと同様です。イメージ名の ***
の部分は自分のアカウントIDに置き換えてください。
環境変数 の spring.profiles.active
に dapr
を指定して、application-dapr.yaml
の設定が有効になるようにしています。
また、こちらは外部からアクセスするため、Serviceを作成しています。
アプリケーションのデプロイ
それではアプリケーションをデプロイしましょう。デプロイ方法はminikubeでもEKSでも変わりません。
cd (GitHubのディレクトリパス)/dapr-advent-2021
kubectl apply -f eks/hello-app.yaml
kubectl apply -f eks/invoke-app.yaml
コマンドに成功したらしばらく待って、podの一覧を見てみましょう。
kubectl get pods
NAME READY STATUS RESTARTS AGE
hello-app-6f97c6ff57-dqqhd 2/2 Running 0 30s
invoke-app-674c8bfb75-zpdrc 2/2 Running 0 33s
hello-appとinvoke -appが起動していればOKです。
アプリケーションへのアクセス
さて、続いてアプリケーションへのアクセスを行います。
Amazon EKSではk8s 上に spec.type
が LoadBalancer
であるServiceを作成すると、自動的にCLB(Classic Load Balancer)が作成され、ポートが外部に公開されます。作成された時点で世界中からアクセスできてしまうため、注意してください。
AWS のコンソールから確認するなり、次のコマンドで確認するなりして、CLBのアドレスを確認します。
aws elb describe-load-balancers --region ap-northeast-1
アドレスの確認ができたら、curl コマンドでアクセスします。
curl (CLB名).ap-northeast-1.elb.amazonaws.com:8081/invokeHello
次の結果が表示できるはずです。
{
"baseUrl ": "http://localhost:3500/v1.0/invoke/hello-app/method ",
"remoteMessage ": {
"message ": "Hello, world! "
}
}
このAdvent Calendarをずっと読んでる人には見慣れたメッセージが表示されました。invoke アプリケーションからDaprを経由してhelloアプリケーションのメッセージを取得することができています。
k8s やeksctlコマンドを使っていると、どこでクラスタ 動いているのかをあまり強く意識せず使えてしまえるため忘れがちになりますが、CLBのアドレスでアクセスできている以上、Amazon EKS上で動いているのは確かなのです。意外と簡単でしょ。
EKS上で動くアプリケーション
後片付け
無事にEKS上で動作確認ができたので、忘れないうちに環境を削除しておきましょう。そのまま残していてはセキュリティ的にも課金的にも危険です。
まずはデプロイしたアプリケーションを削除します。Serviceを削除する際に、一緒にCLBも削除されます。
kubectl delete -f eks
CLBの削除に時間が掛かるため、いつもより応答が遅くなる可能性があります。
続いて、ECRの削除を行います。
aws ecr delete-repository --repository-name hello --region ap-northeast-1 --force
aws ecr delete-repository --repository-name invoke --region ap-northeast-1 --force
最後に、EKSクラスタ 自体を削除します。
eksctl delete cluster --name dapr-test --region ap-northeast-1
これで削除は完了です。
AWS 上でどのように運用するか
今回はAmazon EKS上にhelloアプリケーションとinvoke アプリケーションのみをデプロイしましたが、もちろん他のアプリケーションをデプロイすることも可能です。
また、これまで使ってきたRabbitMQやPostgreSQL などは、k8s 上にデプロイするのではなく、マネージドサービスのAmazon MQ for RabbitMQやAmazon RDS for PostgreSQL 、あるいはAmazon Aurora PostgreSQL などを利用することも可能です。
それぞれのマネージドサービスでインスタンス を作成し、Daprの接続先がそれぞれのインスタンス になるよう指定し、適切にセキュリティグループを設定すればアクセスができます。その辺りはAWS の説明になりすぎますし、説明すると長くなるため今回は割愛しました。
ところで、今回はk8s のLoadBalancerを利用し、AWS のCLBが自動的に作成されることを確認しました。ただ、たとえばAPI Gateway でAPI を管理する場合には、対象としてCLBを指定できないため、ALBが必要となります。実際に僕が運用しているシステムでは、CLBではなくALBを利用しています。
ALBを使う場合には、アプリケーションのServiceの type
として LoadBalancer
ではなく NodePort
を指定し、それとは別に Ingress
を作成してALBと接続する設定を行います。その辺りの使い方については、クラスメソッドのブログなどが詳しいのでそちらを参考にしてください。
dev.classmethod.jp
いずれにせよ、目的に合わせて柔軟に環境を構築できることは間違いありません。
まとめ
eksctlコマンドを使って、Amazon EKSのクラスタ を作成しました
kubectlコマンドを使って、EKS上にアプリケーションのデプロイを行いました
type
が LoadBalancer
のServiceを作成すると、CLBが自動的に作成され、インターネット経由でアクセスすることができます
ALBを利用する場合は type
が NodePort
のServiceを作成し、Ingress
を利用してALBと接続します
EKSを試す場合には、使い終わったら削除することを忘れないようにしてください
ところで今回もソースコード には全く手を入れることがありませんでした。このAdvent Calendarを書き始めたときに、最初からk8s の環境で動かすことを想定してサンプルのソースコード を作成したわけではなく、あくまでDaprを使うことだけを想定していたのですが、k8s でも問題なく動いています。
Daprを使うことで環境から切り離され、ローカルPCで直接動かしても、minikubeで動かしても、あるいはAmazon EKSで運用しても、それぞれの設定ファイルを作成するだけで、ソースコード は変更しなくて済んだのです。
これは間違いなく、Daprのメリットだと言えるでしょうね。
それでは、また明日!