お盆で帰省してたタイミングで丁度下のイベントが行われていたので参加者として参加してきました!
今まで Azure だと Web App とかで割となんとかなっていたので使うことはなかったのですが興味はあったので丁度いいと思ったのがきっかけ。
復習もかねて実際に使った以下のリポジトリーの内容を見ながら自分でもやってメモっておこうと思います。
Azure Kubernetes Service (AKS)
名前のとおり Azure の Kubernetes のサービス。 ノード数とノードのスペックくらいを設定しておけば、あとは割とよしなにやってくれるみたい。急激にスパイクしたときは Azure Container Instance のほうに展開するとかいうこともできるみたい。ノード立ち上げたりすると時間がかかるしね。
作ってみよう
さくっと Kubernetes Service を作ってみた
モブプログラミング形式でみんなでやったハンズオンでは、確か下の HTTP の機能を有効にしたけど…
本番では、HTTPS の構成をしたほうがよさそうということがドキュメントに書いてあった。
こんな感じで。
とりあえず ON にして始めてみよう。
Azure Container Registry の作成
プライベートな DockerHub みたいな ACR も作っておきます。 これは特筆事項もとくにないくらい作るだけです。
作業用マシンを作ろう
私は Azure 上に Linux VM を一台立ててるのでそれを使います。 az コマンドのインストールと
docker を入れておきます。
ACR へのログインを docker コマンドでしておきます。ACR のアクセスキーのところで見れます。管理者ユーザーっていうのを有効にするみたい。
docker login -u ユーザー名 -p "パスワード" ACRの名前.azurecr.io
実際に値を当てはめるとこんなかんじ。
sudo docker login -u kazukicr -p "sugo-kunagai-portal-kara-toreru-pasuwa-do-dayo!" kazukicr.azurecr.io
適当に ACR に push
ということでついに docker push の機運が高まってきました。Azure Functions でやってみます。
以下のように --docker
オプションをつけておくと docker ファイルもできます。便利。
func init --docker
そして、何もないと寂しいので func new
で HttpTrigger の関数でも追加しておきます。そして以下のコマンドをたたいてビルド
sudo docker build .
ビルドが通ったので、とりあえずタグ付けとか docker push
するスクリプトも書いておきます。これも寺田さん作のもの
#!/bin/bash set -e if [ "$1" = "" ] then echo "./build-create.sh [version-number]" exit 1 fi export VERSION=$1 DOCKER_IMAGE=okazukirc/dockerfunc DOCKER_REPOSITORY=kazukicr.azurecr.io docker build -t $DOCKER_IMAGE:$VERSION . -f ./Dockerfile docker tag $DOCKER_IMAGE:$VERSION $DOCKER_REPOSITORY/$DOCKER_IMAGE:$VERSION docker push $DOCKER_REPOSITORY/$DOCKER_IMAGE:$VERSION docker rm $(docker ps -aq) docker images | awk '/<none/{print $3}' | xargs docker rmi
bash のスクリプト慣れないなぁ。chmod して sudo ./docker-build.sh 1.0
してビルド成功!!
ACR の画面を見るとちゃんと push されてました。めでたい。
ちょっとローカルで動かしてみましょう。以下のコマンドをたたいて…
$ sudo docker run -p 8080:80 kazukicr.azurecr.io/okazukirc/dockerfunc:1.0
curl でたたくと…
$ curl http://local/Hello?name=aaaaaa Hello, aaaaaa
動きましたね。
ついに Kubernetes
kubectl
を入れます。コマンドでさくっと
$ az aks install-cli
コマンドが入ったので AKS への認証情報をゲットしましょう。
$ az aks get-credentials --resource-group AKS-rg --name kazukiaks Merged "kazukiaks" as current context in /home/okazuki/.kube/config
ノードも取れました。
$ kubectl get node NAME STATUS ROLES AGE VERSION aks-agentpool-51334753-0 Ready agent 3d4h v1.12.8 aks-agentpool-51334753-1 Ready agent 3d4h v1.12.8 aks-agentpool-51334753-2 Ready agent 3d4h v1.12.8 virtual-node-aci-linux Ready agent 3d4h v1.13.1-vk-v0.9.0-1-g7b92d1ee-dev
AKSからACRへのアクセスの認証情報を追加します。
$ kubectl create secret docker-registry \ > docker-reg-credential \ > --docker-server=kazukicr.azurecr.io \ > --docker-username=kazukicr \ > --docker-password=your-password \ > --docker-email=k_ota28@hotmail.com
ここら辺から、デプロイメントやサービスなどの定義の YAML を適用するのですが、ハンズオン時には出来上がった YAML の一部を編集して使ってたのでよくわからんって感じになりますね。
まず、Deployment については下を見ました。
今回の場合はこんな感じだろうか??
apiVersion: apps/v1 kind: Deployment metadata: name: dockerfunc-deployment labels: app: dockerfunc spec: replicas: 3 selector: matchLabels: app: dockerfunc template: metadata: labels: app: dockerfunc spec: securityContext: runAsUser: 1000 imagePullSecrets: - name: docker-reg-credential containers: - name: dockerfunc image: kazukicr.azurecr.io/okazukirc/dockerfunc:1.0 ports: - containerPort: 80
imagePullSecrets
が先ほど kubectl
で作った secret の名前です。runAsUser
は、ルートで動かさないという感じ。1000 にしてるってことは最初に作られたユーザーアカウントってことなんだろう。
kubectl apply -f ファイル名
で適用できるので、上記ファイルを deploy.yaml
という名前で保存している場合は以下のようなコマンドで適用できます。
$ kubectl apply -f deploy.yaml deployment.apps/dockerfunc-deployment created
deployment がちゃんとできてるかコマンドを打って確認します。
$ kubectl get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE dockerfunc-deployment 3 3 3 0 74s
出来てるっぽい。Pod は…?
$ kubectl get po NAME READY STATUS RESTARTS AGE dockerfunc-deployment-76bb667bd-6cl68 0/1 CrashLoopBackOff 4 2m43s dockerfunc-deployment-76bb667bd-9jnsh 0/1 CrashLoopBackOff 4 2m43s dockerfunc-deployment-76bb667bd-vtcg8 0/1 CrashLoopBackOff 4 2m43s
CrashLoopBackOff なので激しく失敗してますね…。エラーのときは kubectl description
コマンドでログ見てみるといいといわれたので見てみます。
$ kubectl describe pods dockerfunc-deployment-76bb667bd-6cl68
結果はこれ…よくわからない...
Name: dockerfunc-deployment-68984c94bd-4vbr2 Namespace: default Priority: 0 Node: aks-agentpool-51334753-1/10.240.0.66 Start Time: Thu, 15 Aug 2019 05:18:22 +0000 Labels: app=dockerfunc pod-template-hash=68984c94bd Annotations: <none> Status: Running IP: 10.240.0.68 Controlled By: ReplicaSet/dockerfunc-deployment-68984c94bd Containers: dockerfunc: Container ID: docker://c62fc1f1d2a3818651d0b703140b3119ad3755ffb8cb99efc49ea563c15c2ddc Image: kazukicr.azurecr.io/okazukirc/dockerfunc:1.0 Image ID: docker-pullable://kazukicr.azurecr.io/okazukirc/dockerfunc@sha256:2ef5515278a6f17053dfc14289fdf5f77ea7d127819d75f6951b1beac00a212e Port: 80/TCP Host Port: 0/TCP State: Terminated Reason: Error Exit Code: 1 Started: Thu, 15 Aug 2019 05:19:05 +0000 Finished: Thu, 15 Aug 2019 05:19:05 +0000 Last State: Terminated Reason: Error Exit Code: 1 Started: Thu, 15 Aug 2019 05:18:39 +0000 Finished: Thu, 15 Aug 2019 05:18:39 +0000 Ready: False Restart Count: 3 Limits: cpu: 100m Requests: cpu: 100m Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-psnwx (ro) Conditions: Type Status Initialized True Ready False ContainersReady False PodScheduled True Volumes: default-token-psnwx: Type: Secret (a volume populated by a Secret) SecretName: default-token-psnwx Optional: false QoS Class: Burstable Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s node.kubernetes.io/unreachable:NoExecute for 300s Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 50s default-scheduler Successfully assigned default/dockerfunc-deployment-68984c94bd-4vbr2 to aks-agentpool-51334753-1 Normal Pulled 8s (x4 over 49s) kubelet, aks-agentpool-51334753-1 Container image "kazukicr.azurecr.io/okazukirc/dockerfunc:1.0" already present on machine Normal Created 8s (x4 over 49s) kubelet, aks-agentpool-51334753-1 Created container Normal Started 7s (x4 over 48s) kubelet, aks-agentpool-51334753-1 Started container Warning BackOff 7s (x5 over 47s) kubelet, aks-agentpool-51334753-1 Back-off restarting failed container
とりあえず RunAsUser を外してみたら動いた…う~ん。
apiVersion: apps/v1 kind: Deployment metadata: name: dockerfunc-deployment labels: app: dockerfunc version: v1 spec: replicas: 3 selector: matchLabels: app: dockerfunc template: metadata: labels: app: dockerfunc spec: # securityContext: # runAsUser: 1000 imagePullSecrets: - name: docker-reg-credential containers: - name: dockerfunc image: kazukicr.azurecr.io/okazukirc/dockerfunc:1.0 ports: - containerPort: 80 resources: requests: cpu: 100m limits: cpu: 100m
apply したら動いた
$ kubectl get po NAME READY STATUS RESTARTS AGE dockerfunc-deployment-674d45656d-jgb9b 1/1 Running 0 119s dockerfunc-deployment-674d45656d-lsbf4 1/1 Running 0 115s dockerfunc-deployment-674d45656d-mnb8v 1/1 Running 0 2m1s
ポートフォワーディングして動いてるか確認しましょう。
$ kubectl port-forward dockerfunc-deployment-674d45656d-jgb9b 8080:80 Forwarding from 127.0.0.1:8080 -> 80 Forwarding from [::1]:8080 -> 80
curl でたたいてみると…
$ curl http://localhost:8080/api/Hello?name=okazuki Hello, okazuki
動いた!!
サービス
pod をまとめるサービスかな。それの定義を作りましょう。
apiVersion: v1 kind: Service metadata: labels: app: dockerfunc-service name: dockerfunc-service spec: ports: - port: 80 name: http targetPort: 80 selector: app: dockerfunc sessionAffinity: None type: LoadBalancer
dockerfunc の v1 で選択したやつをさくっとまとめておきます。最後の type: LoadBalancer
は本番では使わないで!って言われたけど、とりあえず動きを確認するために設定。これをしてると簡単に外から叩ける。
適用します。
$ kubectl apply -f service.yaml service/dockerfunct-service created
しばらく待つと IP が割り当てられます。(今回は 13.71.149.89)
$ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE dockerfunc-service LoadBalancer 10.0.52.83 13.71.149.86 80:31505/TCP 7m4s kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 3d5h
curl でたたくと…
$ curl http://13.71.149.86/api/Hello?name=kubernates Hello, kubernates
動いた!!
では、サービスを LoadBalancer で公開するのをやめます。ClusterIP にしておくのがよさそう。ClusterIP にしたら一度サービス削除して再度アプライしました。
$ kubectl delete service dockerfunc-service $ kubectl apply -f service.yaml
Ingress を作成
Ingress がサービスに処理を割り振ってくれるような動きをしてくれるものみたいです。これを作って、ここにサービスの割り振りを設定することでちゃんと外からアクセスできるようになります。
Ingress を作りにあたってドメインをゲットしましょう。ポータルの AKS を開くと HTTP アプリケーションのルーティング ドメイン
という項目があります。これを控えておきます。
そして、dockerfunc-service に対してアクセスできるようにしましょう。Ingress を定義する YAML を定義して…
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: dockerfunc annotations: kubernetes.io/ingress.class: addon-http-application-routing spec: rules: - host: dockerfunc.ada93e44ae4b4d9ea00a.japaneast.aksapp.io http: paths: - backend: serviceName: dockerfunc-service servicePort: 80 path: /
apply しましょう。
$ kubectl apply -f ingress.yaml ingress.extensions/dockerfunc created
kubectl get ing
で Ingress の状態が見れます。
$ kubectl get ing NAME HOSTS ADDRESS PORTS AGE dockerfunc dockerfunc.ada93e44ae4b4d9ea00a.japaneast.aksapp.io 104.41.172.168 80 52s
では、curl でアクセスしてみましょう。
$ curl http://dockerfunc.ada93e44ae4b4d9ea00a.japaneast.aksapp.io/api/Hello?name=Ingress Hello, Ingress
動いた!!
バージョンアップしてみよう
ちょっと Azure Functions のコードを変えていきます。
using System; using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace DockerFunc { public static class Hello { [FunctionName("Hello")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string name = req.Query["name"]; string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); dynamic data = JsonConvert.DeserializeObject(requestBody); name = name ?? data?.name; return name != null ? (ActionResult)new OkObjectResult($"こんにちは, {name}") : new BadRequestObjectResult("Please pass a name on the query string or in the request body"); } } }
ビルドスクリプトを実行して version 2.0 を作ります。
$ sudo ./docker-build.sh 2.0
できました。
deployment を新しくして…
apiVersion: apps/v1 kind: Deployment metadata: name: dockerfunc-deployment labels: app: dockerfunc version: v1 spec: replicas: 3 selector: matchLabels: app: dockerfunc template: metadata: labels: app: dockerfunc version: v2 spec: # securityContext: # runAsUser: 1000 imagePullSecrets: - name: docker-reg-credential containers: - name: dockerfunc image: kazukicr.azurecr.io/okazukirc/dockerfunc:2.0 ports: - containerPort: 80 resources: requests: cpu: 100m limits: cpu: 100m
apply してみましょう。
$ kubectl apply -f deploy.yaml deployment.apps/dockerfunc-deployment configured
curl でたたいてみると…
$ curl http://dockerfunc.ada93e44ae4b4d9ea00a.japaneast.aksapp.io/api/Hello?name=Ingress こんにちは, Ingress
ふむ…
deployment のラベルを完全に別名にして、サービスを新たに作って Ingress で新しいサービスを指し示すようにしたりすると古いサービスに戻すとかもできそう。
まとめ
まだまだ、いろいろな機能があるんだろうね…。 でも、とっかかりにとてもいいイベントでした!!
ちなみに実際には、このほかにも Azure DevOps を使って AKS に自動ビルドして自動デプロイするようなものもやりました。 一日なのに盛沢山。