Azure Kubernetes Service のハンズオンしてきた!その復習 (original) (raw)

お盆で帰省してたタイミングで丁度下のイベントが行われていたので参加者として参加してきました!

hiroshima-jug.connpass.com

今まで Azure だと Web App とかで割となんとかなっていたので使うことはなかったのですが興味はあったので丁度いいと思ったのがきっかけ。

復習もかねて実際に使った以下のリポジトリーの内容を見ながら自分でもやってメモっておこうと思います。

github.com

名前のとおり Azure の Kubernetes のサービス。 ノード数とノードのスペックくらいを設定しておけば、あとは割とよしなにやってくれるみたい。急激にスパイクしたときは Azure Container Instance のほうに展開するとかいうこともできるみたい。ノード立ち上げたりすると時間がかかるしね。

作ってみよう

さくっと Kubernetes Service を作ってみた

f:id:okazuki:20190812090648p:plain

f:id:okazuki:20190812090710p:plain

モブプログラミング形式でみんなでやったハンズオンでは、確か下の HTTP の機能を有効にしたけど…

f:id:okazuki:20190812091048p:plain

本番では、HTTPS の構成をしたほうがよさそうということがドキュメントに書いてあった。

こんな感じで。

docs.microsoft.com

とりあえず ON にして始めてみよう。

Azure Container Registry の作成

プライベートな DockerHub みたいな ACR も作っておきます。 これは特筆事項もとくにないくらい作るだけです。

作業用マシンを作ろう

私は Azure 上に Linux VM を一台立ててるのでそれを使います。 az コマンドのインストールと

docs.microsoft.com

docker を入れておきます。

docs.docker.com

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 するスクリプトも書いておきます。これも寺田さん作のもの

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 DOCKERIMAGE:DOCKER_IMAGE:DOCKERIMAGE:VERSION . -f ./Dockerfile docker tag DOCKERIMAGE:DOCKER_IMAGE:DOCKERIMAGE:VERSION DOCKERREPOSITORY/DOCKER_REPOSITORY/DOCKERREPOSITORY/DOCKER_IMAGE:$VERSION docker push DOCKERREPOSITORY/DOCKER_REPOSITORY/DOCKERREPOSITORY/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 されてました。めでたい。

f:id:okazuki:20190815131906p:plain

ちょっとローカルで動かしてみましょう。以下のコマンドをたたいて…

$ 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 については下を見ました。

kubernetes.io

今回の場合はこんな感じだろうか??

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🔞22 +0000 Labels: app=dockerfunc pod-template-hash=68984c94bd Annotations: 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🔞39 +0000 Finished: Thu, 15 Aug 2019 05🔞39 +0000 Ready: False Restart Count: 3 Limits: cpu: 100m Requests: cpu: 100m Environment: 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: 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:

  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:

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 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 アプリケーションのルーティング ドメイン という項目があります。これを控えておきます。

f:id:okazuki:20190815150534p:plain

そして、dockerfunc-service に対してアクセスできるようにしましょう。Ingress を定義する YAML を定義して…

apiVersion: extensions/v1beta1 kind: Ingress metadata: name: dockerfunc annotations: kubernetes.io/ingress.class: addon-http-application-routing spec: rules:

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 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

できました。

f:id:okazuki:20190815151254p:plain

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 に自動ビルドして自動デプロイするようなものもやりました。 一日なのに盛沢山。