Dapr Advent Calendar 13日目 - Dapr + k8sでHello World
こんにちは Dapr Advent Calendar 13日目です。ついに半分を超えましたよ! 人間、やればできるもんなんですね!! いや、まだ終わってないですけどね。
k8sでDaprのHello World
前回のエントリーではk8sを使わずに分散環境で使ってみようと試みましたが、今回からは公式ドキュメントでも推奨されているとおりk8sを使って試してみます。
そんなに難しくはないので、k8sをこれまで触ってこなかったという方も、この機会に気軽に試してもらえれば嬉しいです。
Dapr + k8s環境のセットアップ
ツールのインストール
k8s上でDaprを利用するために、次のツールをインストールする必要があります。
- Dapr CLI
- docker
- kubectl
- minikube
Dapr CLIはこれまでのエントリーでも使ってきた、Daprのセットアップや実行をするためのCLIツールです。
dockerは仮想化環境構築ツールです。
kubectlはk8sをコマンドラインで操作するためのツールです。ローカル環境のk8sでも、リモートやクラウド上のk8sであっても、同じようにコマンドで操作ができます。
minikubeはローカル環境にk8sを構築するためのツールです。minikube以外にもローカル環境にk8sを構築するためのツールはあるのですが、Daprのドキュメントでminikubeが使われていたため、これを使うことにしました。
インストール手順は、それぞれのURLから確認してください。
minikubeを起動する
まずはローカル環境でk8sを動作させるため、minikubeを起動します。
minikube start --cpus=4 --memory=4096
(※2021-12-16追記) オプションを指定しないと2CPU、メモリ2GBで運用しようとするので、アプリを6つほど起動しようとするとリソース不足になります。
数十秒ほど掛かって
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
このような表示が出たら起動は完了です。
起動したminikubeにkubectlコマンドでアクセスしてみましょう。念のため、kubectlの接続先がminikubeになっていることを確認します。
kubectl config get-contexts
kubectlコマンドの接続先の一覧が表示されます。
CURRENT NAME CLUSTER AUTHINFO NAMESPACE * minikube minikube minikube default
minikubeしか接続先がない場合は、このように表示されます。
複数の接続先が表示された場合は、minikube
の CURRENT
に *
がついているかを確認してください。もし別の環境に接続していた場合は、次のコマンドでminikubeを参照するようにします。
kubectl config use-context minikube
これでkubectlコマンドがminikubeに繋がるようになりました。
podの一覧を見る
kubectlコマンドを使って、minikube上にあるpodの一覧を見てみましょう。
「pod」とは、1つ〜複数のコンテナをまとめたリソースです。たとえばDaprでは、アプリケーションのコンテナとDaprのコンテナを一つの「pod」にまとめて利用します。
次のコマンドでpodの一覧を表示します。
kubectl get pods -A
次のように表示されます
NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-558bd4d5db-h579g 1/1 Running 0 1d kube-system etcd-minikube 1/1 Running 0 1d kube-system kube-apiserver-minikube 1/1 Running 0 1d kube-system kube-controller-manager-minikube 1/1 Running 0 1d kube-system kube-proxy-bgt78 1/1 Running 0 1d kube-system kube-scheduler-minikube 1/1 Running 0 1d kube-system storage-provisioner 1/1 Running 0 1d kubernetes-dashboard dashboard-metrics-scraper-7976b667d4-9ffr8 1/1 Running 0 1d kubernetes-dashboard kubernetes-dashboard-6fcdf4f6d-z5vjh 1/1 Running 0 1d
何やらk8s関連のpodだけが起動しているようですね。詳しくは僕も知らないので、フーンという感じで眺めるだけにしておきます。
Daprのセットアップ
それではminikube上にDapr関連のpodを作成しましょう。次のコマンドでDaprの初期化を行います。
dapr init -k
オプションの -k
がk8sを対象とするという意味です。
初期化が終わったら、もう一度podの一覧を見てみましょう。
kubectl get pods -A
次のように表示されるはずです。
NAMESPACE NAME READY STATUS RESTARTS AGE dapr-system dapr-dashboard-57b4db56fc-tng2j 1/1 Running 0 54s dapr-system dapr-operator-5b4b68b5c5-cljjl 1/1 Running 0 54s dapr-system dapr-placement-server-0 1/1 Running 0 54s dapr-system dapr-sentry-c6b746cdf-nkvnv 1/1 Running 0 54s dapr-system dapr-sidecar-injector-6f749dbf87-vr9f6 1/1 Running 0 54s kube-system coredns-558bd4d5db-h579g 1/1 Running 0 1d kube-system etcd-minikube 1/1 Running 0 1d kube-system kube-apiserver-minikube 1/1 Running 0 1d kube-system kube-controller-manager-minikube 1/1 Running 0 1d kube-system kube-proxy-bgt78 1/1 Running 0 1d kube-system kube-scheduler-minikube 1/1 Running 0 1d kube-system storage-provisioner 1/1 Running 0 1d kubernetes-dashboard dashboard-metrics-scraper-7976b667d4-9ffr8 1/1 Running 0 1d kubernetes-dashboard kubernetes-dashboard-6fcdf4f6d-z5vjh 1/1 Running 0 1d
NAMESPACE
が dapr-system
となっているpodが5つ追加されました。これらがDaprを運用するために必要なpod群です。
Dapr CLIのコマンドでも、状況を確認できます。
dapr status -k
次のように表示されます。
NAME NAMESPACE HEALTHY STATUS REPLICAS VERSION AGE CREATED dapr-dashboard dapr-system True Running 1 0.9.0 5m 2021-12-13 00:00.00 dapr-sidecar-injector dapr-system True Running 1 1.5.1 5m 2021-12-13 00:00.00 dapr-placement-server dapr-system True Running 1 1.5.1 5m 2021-12-13 00:00.00 dapr-operator dapr-system True Running 1 1.5.1 5m 2021-12-13 00:00.00 dapr-sentry dapr-system True Running 1 1.5.1 5m 2021-12-13 00:00.00
同じように5つのpodが動いていることを確認できます。
ちなみに、これらのpodを削除したい場合は、次のコマンドを実行します。
dapr uninstall -k
とても簡単ですね。
Hello Worldアプリケーションをk8sの上で動かす
続いて、Hello Worldアプリケーションをk8s上で動かしてみましょう。Hello WorldアプリケーションのソースコードはGitHubにあります。
https://github.com/cero-t/dapr-advent-2021
この中から Dapr Advent Calendar 2日目 - DaprでHello World で作成した hello
アプリケーションを使います。
作成したアプリケーションのおさらい
helloアプリケーションのメインロジックである HelloController
のソースコードは次のようになっています。
(hello) HelloController.java
@RestController public class HelloController { @GetMapping("/hello") public Map<String, String> hello() { return Map.of("message", "Hello, world!"); } }
Hello, world!
を含んだメッセージを返すだけの簡単なアプリケーションです。
アプリケーションのイメージ作成
続いて、Hello Worldアプリケーションの(Docker)イメージを作成しましょう。
イメージ作成は docker build
コマンドでもできるのですが、Spring Bootでは mvn spring-boot:build-image
コマンドでBuildpackを使って簡単にイメージ作成をすることができるので、これを利用しましょう。できあがるイメージのサイズも最適化されているし、Javaのオプションなども適切に指定されるため、手軽に最適なイメージを作成することができます。
イメージを作成する前に、イメージ作成先がminikubeのイメージレジストリになるよう次のコマンドを実行します。
eval $(minikube docker-env)
このコマンドを実行せずにイメージ作成すると、作成したはずのイメージをminikubeが見つけられない、というハメになります。僕も何度となくハマりました。
それではイメージを作成します。次のコマンドを実行します。
cd (GitHubのディレクトリパス)/dapr-advent-2021/hello ../mvnw spring-boot:build-image
初回はビルドするためのイメージなどをダウンロードするため時間が掛かります。
作成が終わったら、イメージ一覧を見てみましょう。
docker images
次のようなイメージが見つかるはずです。
hello 1.0.0 d0a362971d5c 41 years ago 261MB
41年の由緒正しい歴史あるイメージができあがりました。なぜか1980年に作られたことになってしまうみたいですね。まぁ気にしないでおきましょう。
アプリケーションをデプロイしてアクセスする
それでは、作成したイメージをk8sで動かしてみましょう。
イメージをk8sにデプロイ
まずはイメージをk8sにデプロイします。次のコマンドを実行します。
cd (GitHubのディレクトリパス)/dapr-advent-2021 kubectl apply -f k8s/hello-app.yaml
この kubectl apply -f
はマニフェストファイルを用いてk8sにデプロイするコマンドです。hello-app.yaml
の詳しい説明は後ほど行います。
実行すると次のように表示されるはずです。
deployment.apps/hello-app created service/hello-svc created
hello-app
という名前のデプロイメントと、hello-svc
という名前のサービスが作成されました。hello-app
がpodで、hello-svc
がpodに外部からアクセスできるようにするためのサービスです。
起動したpodを次のコマンドで確認しましょう。
kubectl get pods
次のように表示されるはずです。
NAME READY STATUS RESTARTS AGE hello-app-fb644cd5d-wf785 2/2 Running 0 20s
READY
の 2/2
は、アプリケーションのコンテナと、Daprのコンテナの2つがそれぞれ起動していることを示しています。
起動には少し時間が掛かるため、起動中の場合は READY
が 2/2
になっていないこともありますが、何度か kubectl get pods
コマンドを実行して 2/2
になるのを確認してください。
何度実行しても次のように表示される場合は、設定や環境に問題があります。
NAME READY STATUS RESTARTS AGE hello-app-5fd947547b-vs8lj 0/2 ImagePullBackOff 0 17s
もしこのように表示された場合は、次のコマンドで詳細を確認しましょう。
kubectl describe pods hello-app-5fd947547b-vs8lj
たとえばイメージが見つからない場合には、次のように表示されます。
Normal Scheduled 27s default-scheduler Successfully assigned default/hello-app-5fd947547b-vs8lj to minikube Normal Pulling 9s (x2 over 27s) kubelet Pulling image "hello:1.0.0" Warning Unhealthy 9s (x3 over 21s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500 Warning Unhealthy 9s (x3 over 21s) kubelet Readiness probe failed: HTTP probe failed with statuscode: 500 Normal Killing 9s kubelet Container daprd failed liveness probe, will be restarted Warning Failed 6s (x2 over 24s) kubelet Failed to pull image "hello:1.0.0": rpc error: code = Unknown desc = Error response from daemon: pull access denied for hello, repository does not exist or may require 'docker login': denied: requested access to the resource is denied Normal Started 6s (x2 over 24s) kubelet Started container daprd Warning Failed 6s (x2 over 24s) kubelet Error: ErrImagePull Normal Pulled 6s (x2 over 24s) kubelet Container image "docker.io/daprio/daprd:1.5.1" already present on machine Normal Created 6s (x2 over 24s) kubelet Created container daprd Normal BackOff 5s (x3 over 23s) kubelet Back-off pulling image "hello:1.0.0" Warning Failed 5s (x3 over 23s) kubelet Error: ImagePullBackOff
この辺りはk8sの使いこなしの話になるので、いったんここでは詳細は割愛します。
もしこのような問題が起きたら、ここまでの手順を見直して、抜けていることなどないか確認してください。
k8s上のアプリケーションにアクセスする
それでは、起動したアプリケーションにアクセスしてみましょう。
次のコマンドを実行します。
kubectl port-forward service/hello-svc 8080:8080
次のように表示されるはずです。
Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080
これでローカルPCの8080番ポートから、minikubeの8080番ポートにアクセスできるようになりました。
それでは、別のコンソールからアクセスしてみましょう。
curl localhost:8080/hello
次のメッセージが取得できるはずです。
{"message":"Hello, world!"}
無事にアクセスできましたね。
作成したpodの削除
作成したpodは次のコマンドで削除できます。
kubectl delete -f k8s/hello-app.yaml
実行すると次のように表示されます。
deployment.apps "hello-app" deleted service "hello-svc" deleted
これでpodとServiceがそれぞれ削除されました。
また、次のコマンドでそれぞれ削除することもできます。
kubectl delete deployment hello-app kubectl delete service hello-svc
マニフェストファイルがない場合や、マニフェストファイルを書き換えてしまった場合には、この方法を用いても良いでしょう。
やったことの解説
それでは、今回行ったことの説明です。
セットアップのおさらい
環境構築に関わる部分をおさらいします。
- kubectlをインストールした
- minikubeをインストールした
dapr init -k
で環境を構築したeval $(minikube docker-env)
でminikubeのイメージレジストリにするイメージが作成されるようにしたmvnw spring-boot:build-image
でイメージを作成したkubectl apply -f k8s/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: hello:1.0.0 imagePullPolicy: IfNotPresent ports: - containerPort: 8080 --- kind: Service apiVersion: v1 metadata: name: hello-svc labels: app: hello spec: selector: app: hello ports: - protocol: TCP port: 8080 targetPort: 8080 type: LoadBalancer
後半の ---
より上がpodのデプロイの設定、下がサービスの設定です。k8sのマニフェストファイルは、---
で区切ることで1ファイル内に複数の設定を書くことができます。
デプロイの設定
まずはデプロイ設定の主要な部分を説明します。デプロイ設定はpodを作成するための設定です。
metadata.name
の hello-app
がアプリケーション名です。podの名前などに利用されます。
spec.template.metadata.annotations
がDapr関連の設定です。
dapr.io/enabled: "true" dapr.io/app-id: "hello-app" dapr.io/app-port: "8080"
dapr.io/enabled
を true
にすると、このアプリケーションのサイドカーとしてDaprが適用されます。
dapr.io/app-id
はアプリケーションのIDで、dapr run
コマンドの --app-id
に該当します。
dapr.io/app-port
はアプリケーションのポート番号で dapr run
コマンドの --app-port
に該当します。
つまり dapr run --app-id hello-app --app-port 8080
でアプリケーションを起動するのと同等となります。
spec.template.spec.containers
がコンテナ関連の設定です。
- name: hello image: hello:1.0.0 imagePullPolicy: IfNotPresent ports: - containerPort: 8080
imagePullPolicy
の IfNotPresent
はローカルにイメージがなければDocker Hubを見に行くという設定です。ここの設定を Always
にしてしまうと常にDocker Hubを見に行ってしまい、Docker Hubに hello:1.0.0
などないというエラーが発生してしまいます。よくハマりました。
ports
にはアプリケーション側のポート番号を指定します。このポートをpod外に公開します。
これでこのアプリケーションがDaprとともに起動するpodができ、podの8080番ポートがpod外に公開されることになります。
サービスの設定
つづいて、サービス設定の主要部分を説明します。podを外部に公開するための設定です。
metadatea.name
がサービスの名称です。
spec
がポートに関する設定です。
spec: selector: app: hello ports: - protocol: TCP port: 8080 targetPort: 8080 type: LoadBalancer
selector.app
で対象となるpodのラベルを指定します。ここでは hello-app
のラベルである hello
を指定します。
ports
でpodのどのポートを公開するかを設定します。ここでは TCP
の 8080
番ポートを、minikubeの 8080
番ポートとして公開するように指定しています。
type
がこのサービスのタイプです。LoadBalancer
を指定することで、podのレプリカを複数起動した際に、ラウンドロビン形式でリクエストを分散させることができます。
これでminikubeの8080番ポートにアクセスすると hello-app
の8080番ポートにアクセスできるようになる、ということです。
ただしminikubeの各ポートは、通常そのままでは外部からはアクセスできないため kubectl port-forward service/hello-svc 8080:8080
を使うことで localhost:8080
をminikubeの8080番ポートのフォワードして、アクセスができるようになったのです。
この辺りは少し分かりにくいところではあるのですが、使っていくうちに慣れていくと思います。
あれ? Daprは使ってない?
ここまで説明してきましたが、もしかしたら「あれ、Daprにアクセスしてないのでは?」と気づいた方もいらっしゃるかも知れません。その通りです。
アプリケーションを8080番ポートで起動し、podの8080番ポートを公開し、サービスを使ってminikubeの8080番ポートにマッピングし、ポートフォワードでlocalhost:8080からアクセスできるようになりました。つまりアクセスしたのはあくまでもアプリケーションの8080番ポートです。
今回の手順では、アプリケーションをDaprとともに起動しましたが、あくまでもアプリケーションのポートを外部からアクセスできるようにしただけに過ぎず、Daprは通過すらしていません。Daprの機能を利用するような処理は、明日以降のブログで紹介していきます。
まとめ
- Daprをk8sで使うために、Dapr CLI、docker、kubectl、minikubeをインストールしましょう
dapr init -k
で環境を構築しましょうeval $(minikube docker-env)
でminikubeのイメージレジストリを指すようにしましょうmvnw spring-boot:build-image
でイメージを作成しましょうkubectl apply -f k8s/hello-app.yaml
でアプリケーションをデプロイしましょう- DeploymentとServiceの設定を使って、アプリケーションを外部からアクセスできるようにします
- ポートフォワードを使ってlocalhostからminikubeにアクセスできるようにします
なお、今回はアプリケーションの8080番ポートにアクセスしましたが、サービスの設定などで3500番ポートを公開してアクセスすれば、外部からDaprにアクセスすることもできます。しかし、アプリケーションのポートを公開するべきか、Daprのポートを公開するべきかは慎重な設計が必要になるところです。その辺りについては、またいずれ触れたいと思います。
それでは、また明日!