スマートスピーカー Advent Calendar 2020 に空きがあったので、クリスマスもう終わってますが後から追加です。4日目の記事です。
KubernetesやってるとPodの増減とかをAlexaでやってみたくなりますよねー。ということで、もう何番煎じかわかりませんが、やってみました。
目次
- 目次
- 動画
- Kubernetesクラスタ構築
- kubectl proxyでapiserverにかんたんにアクセスする
- podを増やす
- kube-apiserverへのアクセスを用意する
- Alexaからkube-apiserverにアクセスする
- 補足
- まとめ
動画
Kubernetesクラスタ構築
Vagrantを使ってローカルにKubernetesクラスタを構築します。以下のレポジトリをcloneしてください。
このリポジトリのVagrantfileでは
- master x 1台、worker x 2台、おまけ x 1台
- プロビジョニングでKubernetesのクラスタ構築を自動で実施
を行っていますので、vagrant up
すれば、すぐにクラスタにpodをデプロイできる状態になっています。
ただちょっとリソース的には必要かもなので、マシンパワーが心許ない場合はmaster
とworker-1
だけupすればいいかなと思います。
$ git clone https://github.com/kun432/k8s-book-vagrant $ cd k8s-book-vagrant # リソース余裕な方 $ vagrant up # リソース辛い・・・な方 $ vagrant up master $ vagrant up worker-1
vagrant up
で全部上げるとこんな感じです。
$ vagrant status Current machine states: master running (virtualbox) worker-1 running (virtualbox) worker-2 running (virtualbox) test running (virtualbox) (snip)
masterのvagrantユーザでkubectlが叩けるようにしてありますので、masterにsshします。
$ vagrant ssh master
nodeを見てみましょう。
$ kubectl get node NAME STATUS ROLES AGE VERSION master Ready master 14m v1.18.0 worker-1 Ready <none> 2m16s v1.18.0 worker-2 Ready <none> 2m16s v1.18.0
適当なdeploymentをでっちあげます。Kubernetesの公式にあるnginxのdeploymentを今回はそのまま使いましょう。
中身はこんな感じですね。
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80
namespace: defaultにdeployment名: nginx-deploymentで、nginxのpodが3台立ち上がるようになっています。適用してみましょう。
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/master/content/ja/examples/controllers/nginx-deployment.yaml
deployment.apps/nginx-deployment created
deploymentを見てみましょう
$ kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 3/3 3 3 30s
はい、3台のnginxのpodが立ち上がっています。
これで、クラスタとpodの準備は完了です。
kubectl proxyでapiserverにかんたんにアクセスする
Kubernetesのクラスタに対する操作は、masterのkube-apiserverに対して行います。kubectlも裏側ではkube-apiserverに対してリクエストを送っています。つまりcurlでkube-apiserverのREST APIにアクセスすればいいということになります。kubectlの設定を見てみます。
$ kubectl config view | grep server server: https://10.240.0.21:6443
kube-apiserverのURLがわかりました。ではcurlで叩いてみましょう。
$ curl -k https://10.240.0.21:6443/api/ { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "forbidden: User \"system:anonymous\" cannot get path \"/api/\"", "reason": "Forbidden", "details": { }, "code": 403
anonymousなアクセスは許可されていないのでダメだと言われます。kubectlでkube-apiserverにアクセスする場合は.kube/configの設定が利用されるのですが、ここにapiserverにアクセスするためのクライアント証明書が含まれていて、これで認証を行っています。つまり認証をパスしないとAPIは叩けないということです。
認証方式には複数の種類があり本来はきちんと認証を設定すべきですが、今回はデモなのでちょっと楽をします。kubectl proxy &
を実行してください。
$ kubectl proxy & [1] 8200 Starting to serve on 127.0.0.1:8001
これによりkube-apiserverへのリバースプロキシが用意され、http://127.0.0.1:8001でアクセスできるようになります。やってみましょう。
$ curl http://127.0.0.1:8001/api/ { "kind": "APIVersions", "versions": [ "v1" ], "serverAddressByClientCIDRs": [ { "clientCIDR": "0.0.0.0/0", "serverAddress": "10.240.0.21:6443" } ] }
はい、HTTPSではなくHTTPの8001番ポート、かつ認証不要でアクセスできるというわけですね。非常に開放的な状態ですがw、あくまでもデモだということでこのまま進めます。
podを増やす
ではAPI経由でpodを操作してみましょう。
まず、現在のdeploymentのpod数(replicas)を取得してみます。/apis/apps/v1/namespaces/namespace名/deployments/deployment名/scale
をGETします。
$ curl http://127.0.0.1:8001/apis/apps/v1/namespaces/default/deployments/nginx-deployment/scale { "kind": "Scale", "apiVersion": "autoscaling/v1", "metadata": { "name": "nginx-deployment", "namespace": "default", "selfLink": "/apis/apps/v1/namespaces/default/deployments/nginx-deployment/scale", "uid": "dafaae12-48ae-4697-a642-b81bf819a764", "resourceVersion": "1352", "creationTimestamp": "2020-12-25T16:24:12Z" }, "spec": { "replicas": 3 }, "status": { "replicas": 3, "selector": "app=nginx" } }
spec.replicas
が設定されている(立ち上がっているべき)pod数、status.replicasが実際に立ち上がっているpod数です。3個のpodが上がるように定義されていることがわかります。
では、podを増やしてみましょう。同じAPIエンドポイントにPATCHリクエストを送ります。PATCHの場合はContent-Type: application/strategic-merge-patch+json
ヘッダを指定して、-dで更新する内容を記載します。
$ curl -X PATCH \ -H 'Content-Type: application/strategic-merge-patch+json' \ http://127.0.0.1:8001/apis/apps/v1/namespaces/default/deployments/nginx-deployment/scale \ -d '{"spec":{"replicas":5}}'
こんな感じでstatus.replicas
が更新されていればOKです。
(snip) "spec": { "replicas": 5 }, "status": { "replicas": 3, "selector": "app=nginx" } }
実際に増えているか見てみましょう。
$kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 5/5 5 5 47m
増えていますね。curlでも見てみます。
$ curl http://127.0.0.1:8001/apis/apps/v1/namespaces/default/deployments/nginx-deployment/scale (snip) "spec": { "replicas": 5 }, "status": { "replicas": 5, "selector": "app=nginx" } }
実行直後はstatus.replicas
が3だったのが5になっていますね。あるべきpod数(spec.replicas
)と実際のpod数(status.replicas
)の違いはこういうことです。
詳細はKubernetesのAPIリファレンスを見てください。
kube-apiserverへのアクセスを用意する
あとはAlexa、というか、LambdaからこのAPIをよしなに叩けばいいわけですが、今回はローカル環境にKubernetesクラスタを立ち上げているので、インターネット側からの通信が通るようにしてあげる必要があります。
そこでngrokを使って、ローカルからトンネルをはってインターネット上のURLからアクセスできるようにします。
上記にアクセスしてログインもしくはサインアップします。
セットアップのページが開きます。ここのLinux版のダウンロードリンクをコピーします。
master上でコピーしたURLに対してwgetします。URLが固定なのかわからないので、x のところは読み替えてください。
$ wget https://bin.equinox.io/x/xxxxxxxx/ngrok-stable-linux-amd64.zip
unzipします。
$ sudo apt-get install unzip $ unzip ngrok-stable-linux-amd64.zip $ ls -l -rwxr-xr-x 1 vagrant vagrant 26683198 Oct 9 2019 ngrok (snip)
セットアップのページの「2. Connect your account」にあるコマンドをmasterで実行します。これにより、ngrokを使うためのトークンが設定されます。
$ ./ngrok authtoken XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Authtoken saved to configuration file: /home/vagrant/.ngrok2/ngrok.yml
ではngrokを立ち上げます。セットアップのページに記載されているものからは少し変えています。
$ ./ngrok http -host-header=localhost -auth="ユーザ名:パスワード" 8001
※ユーザ名とパスワードは自由に設定してください。
まず。kubectl proxyで立ち上げたエンドポイントはhostヘッダがlocalhost以外の場合、リクエストを拒否します。-host-header
オプションをつけることでngrokからアクセスする際にhostヘッダを書き換えてくれます。
あと、現在の状態だとapiserverに認証なしでアクセスできて「何でも」できてしまいます。そこで-auth
でBASIC認証をかけています。ただし、ないよりはマシ程度の気休めにしかならないと思うので、くれぐれもご注意ください。
ngrokを立ち上げるとこんな感じで表示されます。真ん中にあるのがngrokが発行したインターネットからアクセス可能なURLになります。
別のターミナルを開いてmasterにsshして、実際にcurlでアクセスできるか確認してみましょう。BASIC認証をかけているので以下のようなAuthorizationヘッダを付与する必要があります。
Authorization: Basic XXXXXXXXXXXXXX
BASIC
のあとに指定する文字列は、"ユーザ名:パスワード"をbase64エンコーディングした文字列になりますので、以下で取得できます。
$ echo -n "ユーザ名:パスワード" | base64 XXXXXXXXXXXXXX
こんな感じで返ってくればOKです。これでインターネットからkube-apiserverにアクセスできるようになりました。
$ curl -H 'Authorization: Basic XXXXXXXXXXXXXX' https://xxxxxxxx.ngrok.io/apis/apps/v1/namespaces/default/deployments/nginx-deployment/scale (snip) "spec": { "replicas": 5 }, "status": { "replicas": 5, "selector": "app=nginx" } }
ngrok側でもアクセスが来たことがわかりますね。
Alexaからkube-apiserverにアクセスする
あとはAlexaスキルのバックエンドのAWS LambdaからngrokのURLにアクセスすればOKです。今回はLambdaを使わずにVoiceflowでやってみました。Podの数を参照するところと、変更するところでAPI Blockを使ってngrokのURLにアクセスしてます。
参照するところはこんな感じ。ngrokのURLとBASIC認証のbase64文字列はSet Blockで最初に変数にしてあります。リクエストの結果はresponse.spec.replicasで取得できますので、変数pod_numにいれて喋らせてます。
ユーザからの発話をうけとるところです。変更したいpod数をスロット"{number}"で受け取ります。
ユーザから指定されたpod数で今度は変更を行います。変更の場合はPATCHリクエストになり、Content-typeヘッダを追加します。
実際の変更内容はBodyタブで指定します。スロット"{number}"を含めた変更内容をここで指定します。
これで終わりです。あとはkubectl get deploy -w
しながら、alexaスキルを起動してpodの増減が行なわれることを確認してください。
vagrant@master:~$ kubectl get deploy -w NAME READY UP-TO-DATE AVAILABLE AGE nginx-deployment 5/5 5 5 11h nginx-deployment 5/4 5 5 12h nginx-deployment 5/4 5 5 12h nginx-deployment 4/4 4 4 12h nginx-deployment 4/2 4 4 12h nginx-deployment 4/2 4 4 12h nginx-deployment 2/2 2 2 12h nginx-deployment 2/1 2 2 12h nginx-deployment 2/1 2 2 12h nginx-deployment 1/1 1 1 12h
終わったらctrl+cでngrokは止めておくのをお忘れなく。Vagrant環境の削除は以下でOKです。
$ vagrant destroy -f
補足
- kubectl proxyを使わずにAPIサーバに直接接続させることも可能です。ただし、
- 認証を行う必要があります。やり方は色々ありますが、tokenを使って"Authorization: Bearer"で指定するのが一番簡単だと思います。defaultのservice accountでは権限が足りないので、clusterRoleBindingを付与したservice accountを作成して、そのsecretを取得すれば良いと思います。
- ngrokでは、tlsやtcpでリバースプロキシさせることも可能なんですが・・・
- tlsの場合は有償プランが必要です・・・
- tcpの場合はkube-apiserverでSSLを終端させることになりますが、kube-apiserverの証明書はオレオレ証明書なので、Voiceflowだとつなげませんでした・・・
- Lambdaでnode.jsとかであればできると思います。例えばaxiosを使う場合だと
httpsAgent
にrejectUnauthorized: true
を指定すればいけるみたいです。
- ということで、今回はkubectl proxyを使っています。
- BASIC認証だけなのでくれぐれもご注意ください。
まとめ
スマートスピーカーよりも Kubernetes の話のほうが多かった気がしますが、気にしない!
AlexaでこういうのがコントロールできるとChatOps的で楽しいですね。