大きなファイルを 各 Pod から参照する際、DaemonSet をキャッシュとして扱う

今回とあるお客様と出会い、ハックフェストを実施した際に出てきた課題と、私からお客様に提案した対処方法が、他でも同様のニーズがあった場合に有効になるのではないかと想定し、ここにその方法を共有します。

今回のやり方にあうユースケース:

  1. 各 Pod から同一の巨大ファイル(GBクラスのファイル)を読み込む必要がある
  2. ファイルは読み込みのみで、書き込みは発生しない
    (書き込みが必要な場合、別の手法で対応可能)
  3. 巨大ファイルをできるだけ早く読み込みたい

※ ご注意:ユースケースに応じて手法は各自でご検討ください。

上記のようなユースケースの場合、DaemonSet をキャッシュとして利用する方法もご検討ください。各 Node に一つ Pod を起動する DaemonSet を利用し、DaemonSetの各 Pod にファイルのコピーを持たせます。そしてファイルを参照・取得したい Pod は、DaemonSet の Pod にあるコピー・ファイルを取得・参照する事で、ネットワークを跨いだ通信を削減できるだけでなく、ローカルでのファイル転送になるため短時間でファイルを取得できるようになります。
まぁ、普通に考えて当たり前の事を当たり前に説明しているだけなのですが。

一般的な Kubernetes での Volume のマウント方法


Kubernetes の各 Pod で永続的なボリュームを扱う際、Volume を利用して各 Pod にマウントする手法が用意されています。そして多くのクラウド・プロバイダは各クラウドのストレージ上で Persistence Volume を扱うためのプラグインを提供し、各 Pod から ストレージ内のディスクをマウントできる手法を用意しています(方法は3種類:ReadWriteOnce、ReadOnlyMany、ReadWriteMany)。

Kubernetes のドキュメントをご参照:
* Volumes
* Persistent Volumes

Azure で各 Pod からディスクを 1対1 でマウントする場合 (ReadWriteOnce)、Azure Disk を利用します。また複数の Pod から同一 Disk を共有したい場合 (ReadOnlyMany、ReadWriteMany)、Azure Files を利用します。 今回のユースケースでは、サイズの大きなファイルを複数の Pod から参照したいので、通常であれば Azure Files (ReadOnlyMany) を選択します。

具体的には、下記の手順で Azure Files を作成し、Kubernetes の Persistence Volume として Azure Files を 利用できます。

$ export AKS_PERS_STORAGE_ACCOUNT_NAME=myfilestorageaccount
$ export AKS_PERS_RESOURCE_GROUP=Yoshio-Storage
$ export AKS_PERS_LOCATION=japaneast
$ export AKS_PERS_SHARE_NAME=aksshare

$ az storage account create -n $AKS_PERS_STORAGE_ACCOUNT_NAME -g $AKS_PERS_RESOURCE_GROUP -l $AKS_PERS_LOCATION --sku Standard_LRS

$ export AZURE_STORAGE_CONNECTION_STRING=`az storage account show-connection-string -n $AKS_PERS_STORAGE_ACCOUNT_NAME -g $AKS_PERS_RESOURCE_GROUP -o tsv`

$ az storage share create -n $AKS_PERS_SHARE_NAME

$ STORAGE_KEY=$(az storage account keys list --resource-group $AKS_PERS_RESOURCE_GROUP --account-name $AKS_PERS_STORAGE_ACCOUNT_NAME --query "[0].value" -o tsv)

$ kubectl create secret generic azure-secret --from-literal=azurestorageaccountname=$AKS_PERS_STORAGE_ACCOUNT_NAME --from-literal=azurestorageaccountkey=$STORAGE_KEY

続いて、これを Pod からマウントするために volumeMounts, volumes の設定を各 Pod で行います。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu
spec:
  replicas: 2
  selector:
    matchLabels:
      app: ubuntu
  template:
    metadata:
      labels:
        app: ubuntu
        version: v1
    spec:
      containers:
      - name: ubuntu
        image: ubuntu
        command:
          - sleep
          - infinity
        volumeMounts:
          - mountPath: "/mnt/azure"
            name: volume
        resources:
          limits:
            memory: 4000Mi
          requests:
            cpu: 1000m
            memory: 4000Mi
        env:
          - name: NODE_IP
            valueFrom:
              fieldRef:
                fieldPath: status.hostIP
      volumes:
        - name: volume
          azureFile:
            secretName: azure-secret
            shareName: aksshare
            readOnly: true

※ 今回ファイルサイズの大きなファイルをコピーするため4GBのメモリをアサインしています。

上記のファイルを deployment.yaml として保存し下記のコマンドを実行してください。

$ kubectl apply -f deployment.yaml

Pod が正常に起動したのち、Pod 上で mount コマンドを実行すると Azure Files が /mnt/azure にマウントされていることを確認できます。

$ kubectl exec -it ubuntu-884df4bfc-7zgkz mount |grep azure
//**********.file.core.windows.net/aksshare on /mnt/azure type cifs (rw,relatime,vers=3.0,cache=strict,username=********,domain=,uid=0,noforceuid,gid=0,noforcegid,addr=40.***.***.76,file_mode=0777,dir_mode=0777,soft,persistenthandles,nounix,serverino,mapposix,rsize=1048576,wsize=1048576,echo_interval=60,actimeo=1)

この時、このファイルの実態は Azure Storage の Files Share (***************.file.core.windows.net/aksshare) 上に存在しています。

Pod から /aksshare に存在するファイルを参照する際にファイルを SMB プロトコルを利用してダウンロードします。
実際に、/mnt/azure にあるサイズの大きなファイルを Pod 内にコピーします。

# date
Wed Jan 15 16:29:03 UTC 2020
# cp /mnt/azure/Kuberenetes-Operation-Demo.mov ~/mount.mov
# date
Wed Jan 15 16:29:54 UTC 2020 (51秒)

上記を確認すると Pod に約 3 Gb の動画ファイルをコピーするのに 51 秒ほど時間を要している事がわかります。仮に複数の Pod が起動し同一ファイルを参照した場合、ネットワークをまたいでファイルコピーが繰り返されます。これが通常の利用方法になります。

そして今回は、このファイル共有におけるネットワーク転送回数、スピードなどを DaemonSet を利用し改善します。

DaemonSet をキャッシュとして利用するための環境構築


今回、DaemonSet 上で稼働する Pod を nginx を利用して作成します。nginx のコンテキスト・ルート (/app) 配下に、Azure Blog Storage にあるファイルをコピーし、HTTP でファイルを取得できるようにします。
下記の設定により /app にあるコンテンツを http://podIP/ 経由で取得できるように設定します。

FROM alpine:3.6

RUN apk update && \
    apk add --no-cache nginx

RUN apk add curl

ADD default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
RUN mkdir /app
RUN mkdir -p /run/nginx

WORKDIR /app
CMD nginx -g "daemon off;"

上記の内容を Dockerfile として保存してください。

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  root /app;

  location / {
  }
}

上記の内容を default.conf として保存してください。

このイメージをビルドし Azure Container Registry に Push します。

$ docker build -t tyoshio2002/nginxdatamng:1.1 .
$ docker tag tyoshio2002/nginxdatamng:1.1 yoshio.azurecr.io/tyoshio2002/nginxdatamng:1.1
$ docker login -u yoshio yoshio.azurecr.io 
Password: 
$ docker push yoshio.azurecr.io/tyoshio2002/nginxdatamng:1.1

次に Kubernetes から Azure Container Registry に接続するための Secret を作成します。

kubectl create secret docker-registry docker-reg-credential --docker-server=yoshio.azurecr.io --docker-username=yoshio  --docker-password="***********************" --docker-email=foo-bar@microsoft.com

Azure Container Registry にイメージを Push したので、Daemonset のマニュフェストを記述します。

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: mydaemonset
  labels:
    app: mydaemonset
spec:
  selector:
    matchLabels:
      name: mydaemonset
  template:
    metadata:
      labels:
        name: mydaemonset
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      imagePullSecrets:
        - name: docker-reg-credential
      volumes:
        - name: host-pv-storage
          hostPath:
            path: /mnt
            type: Directory
      containers:
      - name: nginx
        image: yoshio.azurecr.io/tyoshio2002/nginxdatamng:1.1
        volumeMounts:
         - name: host-pv-storage
           mountPath: /mnt
        resources:
          limits:
            memory: 4000Mi
          requests:
            cpu: 1000m
            memory: 4000Mi
        lifecycle:
          postStart:
            exec:
              command:
                - sh
                - -c
                - "curl https://myfilestorageaccount.blob.core.windows.net/fileshare/Kuberenetes-Operation-Demo.mov -o /app/aaa.mov"
      terminationGracePeriodSeconds: 30

※ 今回は検証を簡単にするため、単一ファイル (Kuberenetes-Operation-Demo.mov) を postStart のフェーズで /app 配下にダウンロードしています。複数ファイルを扱う場合、コンテナのイメージ内で複数ファイルをダウンロードする仕組みを別途実装してください。また Nginx をカスタマイズ、もしくは他の Web サーバ、App サーバを利用する事でコンテンツキャッシュを効かせるなど効率化をはかることも可能かと想定します。

上記マニュフェストファイルを daemonset.yaml として保存し、下記のコマンドを実行してください。

$ kubectl apply -f daemonset.yaml

DaemonSet を作成したのち、DaemonSet を Service として登録します。Service を作成する事で各ノードの 30001 番ポート番号にアクセスし Nginx に Node の IP アドレスとポート番号でアクセスできるようにします。

apiVersion: v1
kind: Service
metadata:
  labels:
    app: daemonset-service
  name: daemonset-service
spec:
  ports:
  - port: 80
    name: http
    targetPort: 80
    nodePort: 30001
  selector:
    name: mydaemonset
  sessionAffinity: None
  type: NodePort

上記を service.yaml として保存し、下記のコマンドを実行してください。

$ kubectl apply -f service.yaml

Nginx の DaemonSet と Ubuntu の Deployment をそれぞれ配備したのち、下記のコマンドを実行し、どのノードでどの Pod が動作しているかを確認します。

$ kubectl get no -o wide
NAME                                STATUS   ROLES   AGE    VERSION                         INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
aks-agentpool-41616757-vmss000000   Ready    agent   176m   v1.15.7                         10.240.0.4            Ubuntu 16.04.6 LTS   4.15.0-1064-azure   docker://3.0.8
aks-agentpool-41616757-vmss000001   Ready    agent   176m   v1.15.7                         10.240.0.35           Ubuntu 16.04.6 LTS   4.15.0-1064-azure   docker://3.0.8
aks-agentpool-41616757-vmss000002   Ready    agent   176m   v1.15.7                         10.240.0.66           Ubuntu 16.04.6 LTS   4.15.0-1064-azure   docker://3.0.8
virtual-node-aci-linux              Ready    agent   175m   v1.14.3-vk-azure-aci-v1.1.0.1   10.240.0.48                                  
$ kubectl get po -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP            NODE                                NOMINATED NODE   READINESS GATES
mydaemonset-gqlrw        1/1     Running   0          72m   10.240.0.24   aks-agentpool-41616757-vmss000000              
mydaemonset-nnn5l        1/1     Running   0          72m   10.240.0.50   aks-agentpool-41616757-vmss000001              
mydaemonset-pzvzx        1/1     Running   0          72m   10.240.0.82   aks-agentpool-41616757-vmss000002              
ubuntu-884df4bfc-7zgkz   1/1     Running   0          64m   10.240.0.69   aks-agentpool-41616757-vmss000002              
ubuntu-884df4bfc-gd26h   1/1     Running   0          63m   10.240.0.33   aks-agentpool-41616757-vmss000000              
ubuntu-884df4bfc-vh7rg   1/1     Running   0          63m   10.240.0.49   aks-agentpool-41616757-vmss000001              

上記では、ubuntu-884df4bfc-vh7rg は aks-agentpool-41616757-vmss000001 のノード上で動作しており、aks-agentpool-41616757-vmss000001 のノード上では mydaemonset-nnn5l の DaemonSet の Pod がうごしています。
そして aks-agentpool-41616757-vmss000001 のノードの IP アドレスは 10.240.0.35 である事がわかります。Ubuntu の Pod 内では Pod が稼働している Node の IP アドレスを環境変数 $NODE_IP で取得できるように設定しています。

$ kubectl exec -it ubuntu-884df4bfc-vh7rg env|grep NODE_IP
NODE_IP=10.240.0.35

補足
最後に、ここでは環境構築方法の詳細説明を割愛しますが、Azure Blob Storage で Standard と Premium (高速) のそれぞれ作成し、それぞれのストレージに同一の動画ファイルを配置しておきます。
※ Azure Blob の Premium Storage は AKS の VNET と接続し VNET 経由でファイル取得しています。

その他の参考情報

* Azure Premium Storage: 高パフォーマンス用に設計する
* Troubleshoot Azure Files problems in Linux
* Troubleshoot Azure Files performance issues

検証内容

今回、下記の 4 点を検証しました。

1. Azure Files をマウントしたディレクトリに存在するファイルを Pod 内にコピーする際にかかる所用時間
2. Azure Blob (Standard) に存在するファイルを Pod で取得する際の所用時間
3. Azure Blob (Premium) に存在するファイルを Pod で取得する際の所用時間
4. DaemonSet 内にコピーしたファイルから取得する際の所用時間

1回目(実際の検証内容の動画)

下記のコマンド実行で確認しています

kubectl exec -it ubuntu-884df4bfc-vh7rg

## File Put on Azure Files (File shares : Samba)
$ date
$ cp /mnt/azure/Kuberenetes-Operation-Demo.mov ~/mount.mov
$ date

## File Put on Azure Blob (Containers)
$ curl https://myfilestorageaccount.blob.core.windows.net/fileshare/Kuberenetes-Operation-Demo.mov -o ~/direct.mov
$ curl https://mypremiumstorageaccount.blob.core.windows.net/fileshare/Kuberenetes-Operation-Demo.mov -o ~/premium2.mov
$ curl http://$NODE_IP:30001/aaa.mov -o ~/fromNodeIP.mov
回数 PV からのコピー Standard Storage からの入手 Premium Storage からの入手 DaemonSet キャッシュからの入手
1回目 51 秒 69 秒 27 秒 20 秒
2回目 60 秒 60 秒 23 秒 11 秒
3回目 53 秒 57 秒 20 秒 8 秒



※ ちなみに、DaemonSet の Nginx を利用した際も、初回アクセス時は時間がかかっています。これはおそらく Nginx のメモリに乗っていない為、取得に時間がかかっているように想定します。他の環境でも2回目アクセス以降は早くなっています。

検証結果のまとめ

上記、3回の実行結果より、一旦 DaemonSet にファイルを取得し、DaemonSet のキャッシュからファイルを入手するのが最も高速に入手できる事がわかりました。
Premium のストレージを利用する事で Standard のストレージよりも高速にファイル転送ができますが、Premium にすると Standard より高コストになります。また、Storage からの入手の場合、毎回ネットワークを経由してのダウンロードになる事から、それを考えても、一旦同一のノード(VM) にファイルを取ってきて、そこから入手する方が最も効率的で早い事は当たり前ですね。

DaemonSet を利用する利点
ネットワーク通信量の削減
ファイル取得スピードの高速化
DaemonSet 内のコンテナの実装を機能豊富にすれば、他にも用途が広がる(例:書き込み対応など)

懸念事項

* 現在は、特定の単一ファイルを取得していますが、複数ファイルを扱う場合、もしくはファイルの更新があった場合は、DaemonSet の内部で定期的に更新をチェックし、更新分をダウンロードする仕組みなどを実装する必要あり
* DaemonSet の Pod 内のファイルサイズが肥大化する可能性がありクリーン・アップをする必要がある
* ファイルの書き込みについては、今回の検証方法をそのまま利用した場合は対応は難しいが、DaemonSet 内のコンテナで独自に In Memory Grid 製品のような機能を実装すれば対応かと想定します。

最後に:
その他にも、内部的に検証をしたのですが、説明内容が増え視点がぼやける可能性があった為、特に効果的な点だけに絞ってまとめました。また、極力簡単にするために、DaemonSet で Nginx を使用していますが、Nginx の部分をさらにチューニングをしたり、Nginx の部分を他の実装に変更するなどで、さらなるパフォーマンス向上も見込まれるかと思います。

ぜひ、こちらの情報を元にさらなるチューニングなどを施していただければ誠に幸いです。

2020年1月17日 at 6:05 PM コメントを残す

Azure Spring Cloud の環境構築方法のご紹介

本ブログエントリは、Microsoft Azure Advent Calendar の 20日目の内容です。
昨日は @mstakaha1113 さんによる 「AzureでIoT系のサービスをサラッと(?)使ってみよう」でした。
明日は @hoisjp さんです。

Pivotal と Microsoft は 2019/10/8 オースティンで開催された SpringOne Platform 2019 Azure Spring Cloud というサービスを発表しました。


(SpringOne 2019 における Azure Spring Cloud の発表内容)

Azure Spring Cloud の特徴


Azure Spring Cloud は Microsoft と Pivotal が共同開発した Spring Cloud アプリケーションの開発・運用に特化した環境で、これを利用した場合、アプリケーションの運用・管理コストを大幅に軽減してくれます。

実際の実行環境は Azure Kubernets Service (AKS) ですが、Kubernetes 環境を全く意識することなく、本来必要となる複雑な管理操作を完全に隠蔽しています。実際 Kubernetes コマンドを直接実行する事はできません。その為、おそらく多くの方が Kubernetes 上で動作している事に気づかないかもしれません。

筆者は Kubernetes による Java アプリケーションの開発・運用も経験をしていますが、Kubernetes 上での開発・運用に比べて、特にインフラ面の管理コストが大幅に削減できると考えています。
通常 Kubernetes 上で Java アプリケーションを動作させるためには、コンテナを強く意識したビルド・プロセス、リリース・プロセスが必要です (コンテナのビルド、コンテナレジストリへの Push、YAML の生成、k8s への適用など)。

# Build the Source Code
$ mvn clean package

# Build the Container Image
$ docker build -t $DOCKER_IMAGE:$VERSION . -f Dockerfile
$ docker tag $DOCKER_IMAGE:$VERSION $DOCKER_REPOSITORY/$DOCKER_IMAGE:$VERSION

# Push the image to Container Registry
$ docker push $DOCKER_REPOSITORY/$DOCKER_IMAGE:$VERSION

# Create and Modify the  Deployment YAML file

# Apply to the Kubernetes
kubectl apply --record -f deployment.yaml

上記以外にも、Kuberenes 自身のメンテナンスやパッチ適用、クラスタ自身の運用管理を行わなければなりません。

しかし、Azure Spring Cloud を利用すると Pivotal Build Service という機能を完全統合しているため、アプリケーション開発者は、ソースコードの開発により集中できるようになります。具体的には開発者はソースコードの開発に集中し、コンテナのビルドや YAML ファイルの生成は不要で、コマンドを2、3回実行するだけで簡単にアプリケーションをデプロイできるようになります。デプロイに関しても最初から Blue-Green デプロイの機能を持つため、最新のビルドは Staging 環境にデプロイし、動作確認したのち、本番環境と切り替えるといった操作も容易に実現できます。
さらに、既存の Azure の Managed Service (現時点では Cosmos DB, MySQL, Redis) とのサービス・バインディングの機能も持つ為、JDBC などの接続情報を容易に作成することもできとても便利です。

上記以外にも多くの機能を持つ Azure Spring Cloud ですが、筆者は比較的小規模〜中規模のマイクロサービス提供をしたい場合、今後有用な選択肢になると考えています。



実際の操作イメージを掴む為、下記のデモ動画をご覧ください。

下記動画のデモ内容


  1. デモアプリケーションのご紹介 (PetStore)
  2. Azure DataBase for MySQL の動作確認
  3. Visual Studio Code を利用した Spring Boot アプリの実装のご紹介
  4. ソースコードのビルド
  5. Azure Spring Cloud アプリの作成
  6. Azure Spring Cloud アプリから Managed MySQL へのバインド
  7. CPU, Memory, インスタンスなどのスケール機能のご紹介
  8. Spring Boot アプリケーションを Azure Spring Cloud アプリへデプロイ
  9. Log Analytics による、アプリケーション・ログの確認
  10. アプリケーションの動作確認
  11. 分散トレーシング機能のご紹介 (Application Insights を利用)
  12. ブルー・グリーン・デプロイ機能の検証
      ソースコード修正→ビルド→ステージング環境へのデプロイ
  13. ステージング・アプリと本番アプリの切り替え
  14. GitHub Actions を利用した CI/CD の構築


(GUI で Azure Spring Cloud と MySQL を接続する環境構築方法)

※ ご注意:本デモ動画は筆者のプレゼン時に操作時の待ち時間を短縮するため、操作待ちの箇所をカット(編集)しています。実際には、本デモ動画よりも所用時間が長くなることをご理解ください。

NeXTSTEP :Azure Spring Cloud 構築の事前準備 (CLI 版)

以降のエントリーでは、上記 GUI (動画ビデオ) で環境構築した内容と、ほぼ同等の内容を CLI (コマンドライン)だけで構築する方法について紹介します。

CLI で環境構築する場合は別途、Log AnalyticsApplication Insights の連携などを自ら行う必要がある為、まずはこれらを作成するところから始めます。

1. Azure へのログインと Spring Cloud 用エクステンションの追加


まず、Azure CLI を利用するために az login コマンドでログインをしましょう。そして次に、Azure Spring Cloud を操作するために、az extension コマンドでエクステンションを追加してください。

$  az login
$  az extension add --name spring-cloud

2. 環境変数の設定


次に、各種リソースの名前などを可変で変更しやすくするため、さらに入力ミスを極力減らす目的で、下記の環境変数を設定してください。

$  export RESOURCE_GROUP=Spring-Cloud-Services
$  export SPRING_CLOUD_SVC_NAME=spring-cloud-services-jp
$  export LOCATION=southeastasia
$  export APP_INSIGHTS_NAME=spring-clouds-appinsights
$  export MYSQL_NAME=mysql4springcloud
$  export MYSQL_USERNAME=terada
$  export MYSQL_PASSWORD=whelp90-OCRs
$  export MYSQL_DB_NAME=petstoredb
$  export SPRING_CLOUD_APP_NAME=petstore-app
$  export SPRING_CLOUD_APP_NEW_VERSION_NAME=petstore-app-new-version

上記の環境変数名のそれぞれの意味を下記に記載します。適宜ご自身の環境に応じて上記の環境変数の値は変更してください。

環境変数名 説明
RESOURCE_GROUP リソースグループ名
SPRING_CLOUD_SVC_NAME Azure Spring Cloud のサービス名
LOCATION リソースを配置するリージョンの場所
APP_INSIGHTS_NAME Application Insights の名前
MYSQL_NAME MySQL の名前
MYSQL_USERNAME MySQL のログイン・ユーザ名
MYSQL_PAAWORD MySQL のユーザ・パスワード
MYSQL_DB_NAME MySQL のデータベース名
SPRING_CLOUD_APP_NAME Azure Spring Cloud のデプロイするアプリケーション名
CLOUD_APP_NEW_VERSION_NAME Azure Spring Cloud のステージング環境に配備するアプリケーション名

3. Azure リソースグループの作成


それでは早速、Azure Spring Cloud をインストールするリソース・グループを作成します。下記のコマンドを実行してください。

$  az group create \
     --location $LOCATION \
     --name $RESOURCE_GROUP

4. Application Insights のインストール


リソース・グループを作成した後、Application Insights をインストールしてください。Application Insights をインストールする事で、Azure Spring Cloud から分散トレーシング機能を利用できるようになります。

$ az resource create \
     --resource-group $RESOURCE_GROUP \
     --resource-type "Microsoft.Insights/components" \
     --name $APP_INSIGHTS_NAME \
     --location $LOCATION \
     --properties '{"Application_Type":"web"}'

上記コマンドを実行した結果が、JSON 形式で出力されます。出力結果の中に、下記のような InstrumentationKey が表示されます。
後の設定で利用するため、この値をコピーして保存しておいてください。

    "InstrumentationKey": "d25f6639-****-****-****-f1b******b41",

5. Log Analytics Workspace のインストール

次に、Log Analytics Workspace をインストールします。下記の defaultValue の値をご自身の環境にあわせて修正して deploy_logworkspacetemplate.json というファイル名で保存してください。

{
"$schema": "https://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
    "workspaceName": {
        "type": "String",
 		"defaultValue": "workspace-name(適宜名前を修正してください)", 
 		"metadata": {
          "description": "Specifies the name of the workspace."
        }
    },
    "location": {
        "type": "String",
 		"allowedValues": [
 		  "eastus",
                  "southeastasia",
 		  "westus"
 		],
 		"defaultValue": "southeastasia",
 		"metadata": {
 		  "description": "Specifies the location in which to create the workspace."
 		}
    },
    "sku": {
        "type": "String",
 		"allowedValues": [
          "Standalone",
          "PerNode",
 	      "PerGB2018"
        ],
 		"defaultValue": "PerGB2018",
         "metadata": {
        "description": "Specifies the service tier of the workspace: Standalone, PerNode, Per-GB"
 	}
      }
},
"resources": [
    {
        "type": "Microsoft.OperationalInsights/workspaces",
        "name": "[parameters('workspaceName')]",
        "apiVersion": "2015-11-01-preview",
        "location": "[parameters('location')]",
        "properties": {
            "sku": {
                "Name": "[parameters('sku')]"
            },
            "features": {
                "searchVersion": 1
            }
        }
      }
   ]
}

上記の JSON を deploy_logworkspacetemplate.json ファイルに保存したのち、下記のコマンドを実行し Log Analytics の Workspace を作成してください。

$ az group deployment create \
     --resource-group $RESOURCE_GROUP \
     --name spring-cloud-log-analytics-workspace \
     --template-file deploy_logworkspacetemplate.json

6. Azure Database for MySQL のインストールと検証用 DB の作成

最後の準備事項として、下記のコマンドを実行して Azure Database for MySQL をインストールします。

※ ご注意:今回の MySQL の作成方法は検証目的で作成しています。本番環境用ではないためご注意ください。

$ az mysql server create \
     -l $LOCATION \
     -g $RESOURCE_GROUP \
     -n $MYSQL_NAME \
     -u $MYSQL_USERNAME \
     -p $MYSQL_PASSWORD \
     --sku-name GP_Gen5_2

MySQL を作成した直後は Firewall の設定により全てのクライアントからのリクエストを受け付けられなくなっています。そこで Firewall の設定を行い MySQL に接続できるようにします。ここでは、Azure 内の IP アドレス (0.0.0.0) からだけ MySQL に接続をできるようにしています。

※ ご注意:本来は個別に接続可能な IP アドレスを設定してください。

$ az mysql server firewall-rule create \
     -g $RESOURCE_GROUP \
     -s $MYSQL_NAME \
     -n allowfrominternal \
     --start-ip-address 0.0.0.0 \
     --end-ip-address 0.0.0.0

上記の設定で、Azure 内のネットワークからは MySQL に接続できるようになります。たとえばコマンドの実行環境が Cloud Shell などを利用している場合、これで接続ができるようになるかと想定します。

一方、MySQL への接続が外部のネットワークからの場合、0.0.0.0 の部分を変更し個別に接続可能な IP アドレスを指定してください。管理ポータルを利用すると簡単にクライアントの IP アドレスを入手し登録する事もできます。MySQL の「Connection Security」より「Add ClientIP」というボタンを押下するとご自身の IP アドレスが自動的に取得できます。「Save」ボタンを押下してご自身のパソコンなどからアクセスできるように設定してください。


Firewall 設定が完了すると、mysql コマンドを利用して MySQL に接続ができるようになります。下記のコマンドでは日本語入力に対応する設定や、データベースの作成、さらにはテーブル作成やデータをインサートを行なっています。 ( catalog.sql は後述する git clone で入手できます)

$ mysql -u $MYSQL_USERNAME@$MYSQL_NAME \
     -h $MYSQL_NAME.mysql.database.azure.com -p

mysql> show databases;
mysql> show variables like "char%";
mysql> set character_set_database=utf8mb4;
mysql> set character_set_server=utf8mb4;
mysql> create database petstoredb default character set utf8mb4;
mysql> use petstoredb;
mysql> source ./catalog.sql
mysql> select * from item;

CLI 版:Azure Spring Cloud の環境構築

7. Azure Spring Cloud の構築

上記の準備を完了することで、Azure Spring Cloud を便利に利用できるための環境が整いました。そこで、ここから実際に Azure Spring Cloud のサービスを作っていきます。リソース・グループ内に Azure Spring Cloud のサービスを作成してください。

$ az spring-cloud create \
     -n $SPRING_CLOUD_SVC_NAME \
     -g $RESOURCE_GROUP

8.Azure Spring Cloud と Log Analytics の連携

Azure Spring Cloud のサービスを作成したのち、Log Analytics WorkSpace と連携するための設定を行ってください。
大変残念ながら、この連携の設定は現時点で Azure 管理ポータルからしかできません。
管理ポータルから Azure Spring Cloud を選択し、「Monitoring」→「Diagnostics settings」を選択してください。

次に「+ Add diagnostic setting」を押下し「Name」に適切な名前を入力してください。

次に「Send to Log Analytics」にチェックし「Subscription」と「Log Analytics workspace」から、準備段階で作成したワークスペース名を選択してください。log, metrics の全項目にチェックし「Save」ボタンを押下してください。

以上で、デプロイしたアプリケーションのログなどが Log Analytics 経由で参照できるようになります。

9. Azure Spring Cloud 上に新規アプリの作成

Azure Spring Cloud のサービスを作成したので、次にサービス内に Spring Boot/Spring Cloud のマイクロサービス・アプリケーションをデプロイします。

$ az spring-cloud app create \
     -n $SPRING_CLOUD_APP_NAME \
     -s $SPRING_CLOUD_SVC_NAME \
     -g $RESOURCE_GROUP

10. MySQL へのバインドを作成

次に、Azure Spring Cloud から MySQL を利用できるようにバインド設定を行います。GUI からバインドを作成する際は、GUI からサブスクリプションを選択しその中に含まれる MySQL を選択することで容易にバインド情報を作成できますが、CLI からバインドを行うためにはまず MySQL の ID を取得しなければなりません。そこで、MySQL の ID を取得するために、下記のコマンドを実行して取得してください。ここで、「“id”: “/subscriptions/***」 ではじまる行の内 「/databases/petstoredb」 より前の部分を MySQL の リソース ID (–resource-id) として利用します。

$ az mysql db show \
     --name $MYSQL_DB_NAME \
     -g $RESOURCE_GROUP \
     --server-name $MYSQL_NAME
{
  "charset": "utf8mb4",
  "collation": "utf8mb4_general_ci",
  "id": "/subscriptions/********-****-****-****-************/resourceGroups/Spring-Cloud-Env/providers/Microsoft.DBforMySQL/servers/mysql4springcloud/databases/petstoredb",
  "name": "petstoredb",
  "resourceGroup": "Spring-Cloud-Env",
  "type": "Microsoft.DBforMySQL/servers/databases"
}

取得した リソース ID を指定して MySQL のバインドを作成してください。

$ az spring-cloud app binding mysql add \
     --app $SPRING_CLOUD_APP_NAME \
     --resource-id "/subscriptions/********-****-****-****-************/resourceGroups/Spring-Cloud-Env/providers/Microsoft.DBforMySQL/servers/mysql4springcloud" \
     --database-name $MYSQL_DB_NAME \
     --name "mysql-binding"  \
     -s $SPRING_CLOUD_SVC_NAME \
     -g $RESOURCE_GROUP \
     --username $MYSQL_USERNAME@$MYSQL_NAME \
     --key $MYSQL_PASSWORD

バインドが完了すると Azure Portal 上では下記のような内容を確認できます。

11. サンプル・ソースコードの入手

次に、GitHub よりサンプルのソースコードやデータ、設定ファイルなどを入手してください。

$ git clone https://github.com/yoshioterada/Azure-Spring-Cloud-with-MySQL-Sample

ディレクトリ構成は下記のようになっています。

├── catalog.sql (MySQL に挿入するデータ)
├── deploy_logworkspacetemplate.json (Log Analytics Workspace を作成するためのJSON)
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── yoshio3
    │   │           └── demo
    │   │               ├── CatItemRepository.java (Repository クラス)
    │   │               ├── DemoApplication.java  (Main クラス)
    │   │               ├── Item.java   (JPA Entity クラス)
    │   │               └── PetItemController.java (REST API 実装クラス)
    │   └── resources
    │       ├── AI-Agent.xml
    │       ├── application.properties
    │       ├── log4j2.xml
    │       ├── static
    │       │   ├── css
    │       │   │   ├── new-design.css  (新デザイン)
    │       │   │   └── old-design.css  (旧デザイン)
    │       │   └── images
    │       └── templates
    │           └── index.html   ( Web ページ ThymeLeaf )
    └── test
        └── java
            └── com
                └── yoshio3
                    └── demo
                        └── DemoApplicationTests.java

12. ソースコードのビルドとアプリのデプロイ

入手したソースコードを mvn コマンドでビルドしてください。ビルドが完了すると target ディレクトリ配下に demo-0.0.1-SNAPSHOT.jar ができます。

$ mvn clean package -DskipTests
$ ls target/
classes
demo-0.0.1-SNAPSHOT.jar
demo-0.0.1-SNAPSHOT.jar.original
generated-sources
generated-test-sources
maven-archiver
maven-status
test-classes

ビルドの成果物である demo-0.0.1-SNAPSHOT.jar を Azure Spring Cloud のアプリに対してデプロイします。

この時、事前準備で作成した Application Insights の InstrumentationKey を環境変数 (–env “azure.application-insights.instrumentation-key=) に設定します。これにより Java で実装した Application Insights を動作できます。デプロイには少し時間がかかりますので、しばらくお待ちください。

$ az spring-cloud app deploy \
     -n $SPRING_CLOUD_APP_NAME \
     -s $SPRING_CLOUD_SVC_NAME \
     -g $RESOURCE_GROUP \
     --instance-count 2 --memory 2 \
     --runtime-version Java_8  \
     --env "azure.application-insights.instrumentation-key=********-****-****-****-************" \
     --jar-path ./target/demo-0.0.1-SNAPSHOT.jar --version 1 

13. 外部から接続可能な Public URL を作成

デプロイ時に Public URL の公開オプションを指定せずにデプロイすると下記のように 「Test Endpoint」しか作成されません。

GUI からは「Assign domain」のボタンを押下するだけで容易に Public URL を付加する事ができますが、CLI では下記のコマンドを実行することで Public からアクセス可能な URL を作成できます。

$ az spring-cloud app update \
     -n $SPRING_CLOUD_APP_NAME  \
     -s $SPRING_CLOUD_SVC_NAME  \
     -g $RESOURCE_GROUP \
     --is-public true

作成された URL を確認するために、下記のコマンドを実行してください。”url” の行に URL が表示されます。

$ az spring-cloud app show \
     -n $SPRING_CLOUD_APP_NAME  \
     -s $SPRING_CLOUD_SVC_NAME  \
     -g $RESOURCE_GROUP

    ........ (中略)

    "url": "https://spring-cloud-services-******-********-***.azuremicroservices.io"
  },
  "resourceGroup": "Spring-Cloud-Env",
  "type": "Microsoft.AppPlatform/Spring/apps"
}

14. 動作確認

上記の URL に対して curl コマンド、もしくは Web ブラウザを利用してアクセスしてみてください。

$ curl https://spring-cloud-services-******-********-***.azuremicroservices.io/cats

15. 新バージョンをステージングへデプロイ

上記で一通りアプリケーションのデプロイはできるようになりましたが、さらにアプリケーションを更新して、Blue-Green デプロイの機能も試してみましょう。
アプリケーションの一部を更新して新バージョンを作成します。
ここではわかりやすくするため、デザインを変更してみましょう。index.html を開き、CSS を “old-design.css” から “new-design.css” に修正して、さらに <H1>Hello Java<H1> の行も追加します。

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Springboot</title>
    <meta charset="utf-8" />
    <link th:href="@{/css/new-design.css}" rel="stylesheet" type="text/css"></link>
</head>
<body>
<H1>Hello Java</H1>

編集が終わったのち、新しいバージョンを作るために再度 mvn コマンドでソースコードをビルドします。

$ mvn clean package -DskipTests

今回は、アプリケーションを Staging 環境にデプロイするため、下記のコマンドを実行します。–name で新規バージョンにつける名前を指定します。

(ご注意:上記で実行した az spring-cloud app deploy コマンドとは違います)

$ az spring-cloud app deployment create \
     -s $SPRING_CLOUD_SVC_NAME \
     -g $RESOURCE_GROUP \
     --name $SPRING_CLOUD_APP_NEW_VERSION_NAME \
     --version 2 \
     --app $SPRING_CLOUD_APP_NAME \
     --jar-path target/demo-0.0.1-SNAPSHOT.jar

16. 本番とステージングのアプリの入れ替え

上記で、Staging 環境に新しいバージョンがデプロイされましたが、新バージョンを本番環境に適用するためには、旧バージョンから新バージョンにリクエストの振り先を変える必要があります。そこで、振り先を新バージョンに変更するために下記のコマンドを実行してください。

$ az spring-cloud app set-deployment \
     --deployment $SPRING_CLOUD_APP_NEW_VERSION_NAME \
     --name $SPRING_CLOUD_APP_NAME \
     -s $SPRING_CLOUD_SVC_NAME \
     -g $RESOURCE_GROUP

このようにして簡単にバージョンの切り替えも可能です。

17. GitHub Actions による CI/CD

さらに、下記にのような GitHub Actions のワークフローを作成(.github/workflows/main.yaml)することで、GitHub に対してソースコードを Push する度に、ソースコードのビルドからデプロイまでを自動化することもできます。

このワークフローを作成する上で注意したポイントは、Staging にすでにアプリケーションがデプロイされている場合、上書きできずエラーになるため、一旦既存のデプロイ済みのアプリケーションを削除して追加しています。また、ソースコードリビジョンとデプロイ名を一致させるために、環境変数に Git のリビジョン番号などを代入し、それを名前に付加するようにしています。

name: Build and deploy on Azure Spring Cloud

on: [push]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v1
    - name: Set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Get Year Month Day 
      run: echo ::set-env name=DATE::$(date '+%Y%m%d-%H%M%S')
    - name: Get the Git Revision Number
      run: echo ::set-env name=GIT_SHORT_VERSION::$(git rev-parse --short HEAD)
    - name: Build with Maven
      run: mvn package -DskipTests -Pcloud
    - name: Login to Azure Spring Cloud
      uses: azure/actions/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}
    - name: Install Azure Spring Cloud extension
      run: az extension add -y --source https://azureclitemp.blob.core.windows.net/spring-cloud/spring_cloud-0.1.0-py2.py3-none-any.whl
    - name: Get Un Active Service Name 
      run: echo ::set-env name=UNACTIVE_SERVICE::$(az spring-cloud app deployment list -g Spring-Cloud-Services -s spring-cloud-services-jp --app petstore-app  -o json |  jq -c '.[]| [.name, .properties.active]' | grep false | awk -F'["]' '{print $2}')
    - name: Delete UnActive Service 
      run: az spring-cloud app deployment delete -n ${{env.UNACTIVE_SERVICE}} -g Spring-Cloud-Services -s spring-cloud-services-jp --app petstore-app
    - name: Deploy to Azure Spring Cloud
      run: az spring-cloud app deployment create -g Spring-Cloud-Services -s spring-cloud-services-jp --name green-${{env.DATE}}-${{env.GIT_SHORT_VERSION}} --version ${{env.DATE}}-${{env.GIT_SHORT_VERSION}} --app petstore-app --jar-path target/demo-0.0.1-SNAPSHOT.jar

以上、で GUI でデモした内容とほぼ同等の内容を CLI だけで実施しましたが、自動化設定を行う際には本内容が必要になる場合もあるかと想定します。本エントリを少しでもお役に立てていただければ誠に幸いです。

その他の参考情報

Azure Spring Cloud は 2019 年 10 月に発表されたばかりのサービスで、現時点ではまだ Preview 版になります。しかしながら現状でも便利な機能が含まれるため、ぜひ多くの皆様にお試しいただければ誠に幸いです。また、本紙の都合上取り上げなかった機能に関しても下記のようなドキュメントから参照していただく事ができます。

Azure Spring Cloud
Azure Spring Cloud のドキュメント
その他 Spring on Azure に関する情報
Azure Spring Cloud のご紹介

また、Microsoft MVP である@setoazusaさんも、12/19 に「Azure Spring CloudでBlue-Green Deploy」というエントリーを投稿してくださっているようです。こちらも合わせてご覧ください。

さいごに

Azure Spring Cloud の正式リリース(GA) は来年 2020 年の Q2 あたりを予定しています。ご興味のある方はぜひ、お早めにお試しいただきぜひ使用感に関してフィードバックをいただければ誠に幸いです。
より多くのフィードバックをいただき改善していくことでさらに良くなってまいりますので、ぜひ多くの皆様お試しください。
ご協力のほどどうぞよろしくお願いします。

2019年12月20日 at 12:22 AM

日本全国 Java & AKS (k8s) サマー・ツアー 開始

7月下旬から、8月下旬の約1ヶ月をかけて、日本全国で Java & AKS (Kubernetes) のサマー・ツアーを開始します。

既に、昨日(7月27日)、一昨日(7月28日)と仙台で Kubernetes のハッカソン・イベントを開催してきました。

今後の日本全国サマー・ツアーの予定は下記の通りです。ぜひ各地方のエンジニアの皆様どうぞお越しください。

仙台(7/27-7-28):https://jazug.connpass.com/event/135986/

札幌(7/29):https://jazug.connpass.com/event/137069/

東京女子部(8/3):https://jazug.connpass.com/event/137077/
Java 女子部(8/3):https://javajo.doorkeeper.jp/events/94041

名古屋(8/5):https://75az.connpass.com/event/137002/

大阪 Day 1(8/7):https://jazug.connpass.com/event/136591/
大阪 Day 2(8/8):https://kanjava.connpass.com/event/139275/

岡山(8/10):https://okajug.doorkeeper.jp/events/94008

広島(8/11):https://hiroshima-jug.connpass.com/event/135650/

福岡(8/17-8/18):https://jazug.connpass.com/event/138372/

熊本(8/19):https://kumamotojava.doorkeeper.jp/events/94011

沖縄(8/24-8/25):https://java-kuche.doorkeeper.jp/events/94636

● 東京(8/31) : Coming Soon

皆様とお会いできるのを心より楽しみにしています。

2019年7月29日 at 5:15 PM 1件のコメント

Kubernetes の導入時に考えるべきこと

Azure にシステム導入・移行するお客様に日頃ご支援をさせていただいているのですが、多くのお客様やエンジニアの皆様から、たびたび似たようなご質問を頂くので、その内容をここに紹介したいと思います。(実際、今日もとあるお客様で下記のご相談を頂いたので)

ご質問:

お客様からたびたび頂くご質問の例

  • このシステムを Kubernetes (AKS) 上で構築するのは正しいですか?
  • とあるコンサルから Kubernetes を導入しないと会社の未来はないと言われたのですが、本当ですか?
  • 今、Azure Web App for Container か AKS (Kubernetes) の何れかの選択で悩んでいるのですが、どちらが良いですか?
  • 今、このような Kubernetes(AKS) のシステム構成を考えているのですが、正しいですか?

質問の背景:

これらのご質問は、メッセージは異なるのですが基本的には同じ質問です。結局どのような場合に kubernetes (AKS) を活用するのが効果的なのかが理解出来ていないため出てくる質問です。

個人的には当たり前になっていたので、この手の内容はまとめなくても良いかな?と思っていたのですが、度々、お問い合わせを頂くので多くの方がお悩みなのかな?と思ったのと、今日ミーティングに同席された方から、ぜひまとめて欲しいとのご要望を承ったので書くことにしました。

ご質問いただいた際の私の対応:

このようなご質問をいただく際、私からは必ず、質問者に対して下記の2つを問い合わせます。

  1. そのシステムは変更の激しいシステムですか?例えば今後サービスの追加は多いですか?もしくは修正が多いですか?
  2. コンテナ化するサービスは大量の CPU やメモリのリソースを消費しますか?

Kubernetes を選択するか否か:

A. Kubernetes が適する場合

上記の質問に対する答えに対し、1 が「はい」、2 が「いいえ」 の条件を共に満たす場合は Kubernetes の導入が適するかと思います。(前提:アジャイル、DevOps などが出来る環境が整っている会社の場合)

1: はい (変更が激しい)
2: いいえ (大量の CPU/メモリは消費しない)

B. Kubernetes が適さない場合:

一方で、上記の質問に対する答えに対し、1 が「いいえ」、2 が「はい」の条件のどちらか片方でも条件を満たす場合は、Kubernetes の導入は適さないと思います。

1: いいえ (変更は激しくない)
2: はい (大量の CPU,メモリを消費する)

理由:

システムは変更の激しいシステムか否か

まず、1 の質問の背景はマイクロサービス化をしたいと考えるお客様に対しても同様の質問をするのですが、「変更」には2つの観点があります。一つは、「既存のサービスに対し頻繁な更新・バージョン・アップ」、そしてもう一つは「新機能の追加が多くある」です。

「既存のサービスに対し頻繁な更新・バージョン・アップ」が必要な場合、一早いサービスの入れ替えが必要になります。それを実現するためには、アジャイルでの開発を実践し、DevOps でビルドやリリースのパイプラインを自動化していきます。そして、古いバージョンから新しいバージョンに安全に入れ替えるために、ローリング・アップデートやブルー・グリーン・デプロイ、カナリー・デプロイなどの手法を用いて入れ替えていく必要があります。
その際、システム構築スピードであったり、構築やサービス入れ替えの容易性が重要になってきます。デプロイ先として IaaS を選ぶ場合、VM の構築には時間が掛かります。そして上記のようなローリング・アップグレードやブルー・グリーン・デプロイ、カナリー・デプロイなどの手法を取り入れるためには、ご自身でこれらを実現するための環境を整える必要があり多大な労力を伴います。
こうした手間や労力を軽減してくれるのが PaaS (Azure では Azure Web App)であったり Kubernetes(AKS) になります。

この時点では、まだ選択肢として PaaS, Kubernetes 何れもありうる状況ですが、次に考えるのがもう一つの「新機能の追加が多くある」か否かです。デプロイするサービス数が増えて来たり、サービス間の連携などが増えてくると、PaaS では管理が煩雑になる場合があります。その場合、Kubernetes クラスター内でまとめて管理をする方がサービス間の管理やサービス全体の運用管理が楽になったり見通しが楽になったりします。
具体的なサービスの追加数は、個々のシステムや企業における運用・管理ポリシーによって異なるので本来ならば明示は避けたい所ですが、追加するサービス数が 5-10 程度ならば、迷わず PaaS をお勧めします。
しかし、サービス数が、20を超えるようならば Kubernetes の導入をお勧めします。
もちろん、 5-10 程度でも Kubernetes をご利用いただいても良いかと思いますが、導入するサービス数に対して、運用・管理、さらに学習コストの方が高くつくのではないかと想定します。

CPU やメモリのリソースを消費するか否か

また、2 の稼働させるサービスは「大量の CPU やメモリのリソースを消費しますか?」についてですが、こちらも選択する際の重要な要素と考えています。

Kubernetes 上で稼働する Pod (コンテナを束ねた物) が実際にはどこで動いているのかを考えて頂くと納得していただけるのですが、Pod(コンテナ) は、実際には、Kubernetes クラスタのノード上で動いています。ノードは実際には 1VM になります。例えば Kubernetes クラスタを 3 ノード (3 VM) で構成し、各ノードの VMに CPU 4 コア、16GB の VM を割り当てた場合を考えます。

この場合、3ノード全体で利用できる CPU やメモリーのリソースは下記の通りです。

CPU: 4 Core * 3 node = 12 Core(1200m)
Memory: 16 Gb * 3 node = 48 GB

自身が提供するサービスは、上記の 12 Core, 48 GB を超えてスケール・アウト(増やす)事は出来ません。実際には、Kubernetes を動作させるために必要な管理用の Pod(kube-system内のpod)で利用する CPU や Memory のリソースも含まれるため、上記の MAX 分までを自身のサービスを利用することも出来ません。

極端な例でわかりやすく説明すると、仮に自身が提供するサービスが 1 Pod (コンテナ) あたり CPU 1Core 分を消費するようなアプリケーションの場合、1ノードあたり 2 〜 3 Pod、トータルで 6 〜 10 pod 位しか作れないのです。また、1 Pod (コンテナ) あたり 2 GB 位メモリを消費するようなアプリの場合は、約 20 pod 位しか作れないことになります。
もし、CPU や メモリーを大量に消費するようなサービスを提供したい場合は、わざわざ Node の CPU やメモリを共有する Kubernetes 環境で提供するよりも、IaaS 環境で 1 VM に1サービスをデプロイして提供した方が、Kubernetes のオーバヘッドもなく、パフォーマンスも発揮するかと想定します。

つまり、Kubernetes で提供するサービスは、簡単にスケール・アウトしやすいサービスのデプロイには向いていますが、パフォーマンスを上げるためにスケール・アップが必要なサービスには向かないと考えています。
その場合、IaaS でも PaaS でも Serverless でも違う選択肢を考えた方が良いかと思います。

さいごに

Kubernetes は決して銀の弾丸ではないため、どうぞ適材・適所で稼働に向くサービスを稼働させるようにしてください。

2019年7月20日 at 1:51 AM

Quarkus: コンテナ上で Java アプリを高速起動する新しい手法のご紹介

Docker 環境上で Java のアプリを起動するのは遅いと思っていらっしゃる方は必見!!
どうぞ下記の内容をご参照いただき、どうぞお試しください!!

先日、Red Hat から Quarkus (https://quarkus.io) という新しい技術が発表されました。こちらを実際に試して見ましたが、想定通りというか、まさにこれを待っていた!!という技術でした。今後、私の中で注目の技術の一つになりそうです。もし、Docker/k8s 上で Java アプリを動かす方は、こちらの方法をご覧いただき、ぜひ試しください。

Quarkus を簡単にご説明すると、Java のソースコードを GraalVM を利用して Linux の Native バイナリを作成し、その Linux バイナリをコンテナ上で起動することにより、今まで Java アプリの課題であった起動時間を大幅に短縮することができる技術です。

※ GraalVM については、きしださんや Java Champion である阪田さんがまとめてくださっていますので、ここではその詳細説明を割愛させていただきます。
* GraalVMについて (きしださん)
* 詳説GraalVM(1) イントロダクション(阪田さん)

事前準備

GraalVM を入手したのち、.bash_profile などに環境変数 GRAALVM_HOME を指定しておきます。

GRAALVM_HOME=/Library/Java/JavaVirtualMachines/graalvm-ce-1.0.0-rc13/Contents/Home
export GRAALVM_HOME

それでは、実際に試して見ましょう。

1. Quarkus のクィック・スタートに従い Java のプロジェクトを作成します

$ mvn io.quarkus:quarkus-maven-plugin:0.11.0:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=getting-started \
    -DclassName="org.acme.quickstart.GreetingResource" \
    -Dpath="/hello"

プロジェクトを作成すると下記のようなファイルが作成されます。

├── pom.xml
└── src
    ├── main
    │   ├── docker
    │   │   └── Dockerfile
    │   ├── java
    │   │   └── org
    │   │       └── acme
    │   │           └── quickstart
    │   │               └── GreetingResource.java
    │   └── resources
    │       └── META-INF
    │           ├── microprofile-config.properties
    │           └── resources
    │               └── index.html
    └── test
        └── java
            └── org
                └── acme
                    └── quickstart
                        ├── GreetingResourceTest.java
                        └── NativeGreetingResourceIT.java

2. Java のソースコードをビルドし Linux の Native バイナリを作成します

$ mvn package -Pnative -Dnative-image.docker-build=true

-Dnative-image.docker-build=true は Docker on Linux 上で実行するバイナリを生成する事を明示するオプションです。これを指定しない場合、ビルドをした環境(例えば Mac OS/X なら Mac )用のバイナリが生成されます。

2.1 作成された Linux 用のバイナリを確認します


target ディレクトリ配下に成果物ができあがります。

$ ls -l target/getting-started-1.0-SNAPSHOT-runner
-rwxr-xr-x 1 yoterada staff 20112760 3 11 13:52 getting-started-1.0-SNAPSHOT-runner

2.2 file コマンドでファイル内容を確認すると Linux ELF バイナリが作成されています

$ file getting-started-1.0-SNAPSHOT-runner
getting-started-1.0-SNAPSHOT-runner: ELF 64-bit LSB executable, 
x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, 
for GNU/Linux 2.6.32, BuildID[sha1]=61ef9e78267993b688b9cf2de04e2aff9f1a4bfa, 
with debug_info, not stripped

3. Docker のビルド

Dockerfile のサンプルも下記の Dockerfile が自動的に作成されていますので、こちらをそのまま利用してビルドを行います。
17行目の COPY でビルド時に生成された Linux ELF バイナリを追加しています。

自動生成された Dockerfile

####
# Before building the docker image run:
#
# mvn package -Pnative -Dnative-image.docker-build=true
#
# Then, build the image with:
#
# docker build -f src/main/docker/Dockerfile -t quarkus/getting-started .
#
# Then run the container using:
#
# docker run -i --rm -p 8080:8080 quarkus/getting-started
#
###
FROM registry.fedoraproject.org/fedora-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

Dockerfile からコンテナ・イメージをビルドしてください。

$ docker build -f src/main/docker/Dockerfile -t tyoshi2002/quarkus-quickstart:1.0 .

4. Docker の起動


作成したコンテナ・イメージを利用してコンテナを起動します。

$ docker run -i --rm -p 8080:8080 tyoshi2002/quarkus-quickstart:1.0
2019-03-11 05:10:42,697 INFO [io.quarkus] (main) Quarkus 0.11.0 started in 0.005s. Listening on: http://0.0.0.0:8080
2019-03-11 05:10:42,699 INFO [io.quarkus] (main) Installed features: [cdi, resteasy]

私の環境では 0.005 秒で CDI+REST アプリが起動できました。
Quarkus 0.11.0 started in 0.005s. Listening on: http://0.0.0.0:8080

5. 動作確認


curl コマンド、もしくはブラウザを利用して起動したアプリケーションにアクセスします。

$ curl http://localhost:8080/hello
hello

6. 備考


Quarkus は開発モード(Development Mode)も用意されており、開発時にホット・デプロイもできるようになっています。
ホット・デプロイを行うためには、下記のコマンドを実行します。

$ mvn compile quarkus:dev

こちらで起動したのち、ソースコードを修正しコンテンツを再読み込みします。
すると、再起動は不要で修正されたコードの内容を確認することができます。

7. 注意事項


Quarkus で内部的に利用されている CDI の実装 (ArC と呼ぶ) は、CDI の仕様に完全準拠しているのではなく、一部の仕様を実装したサブ・セットの実装になっています。下記にそれぞれ CDI におけるサポート、未サポートの機能についての機能一覧が説明されていますので、こちらをどうぞご確認ください。
CDI でサポートされている機能一覧
CDI で未サポートの機能一覧

8. Extension

Quarkus のアプリケーションに対して、追加のエクステンションを追加できます。追加可能なエクステンションのリストは下記から参照できます。

$ mvn quarkus:list-extensions
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< org.acme:getting-started >----------------------
[INFO] Building getting-started 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- quarkus-maven-plugin:0.11.0:list-extensions (default-cli) @ getting-started ---
[INFO] Available extensions:
[INFO] 	 * Agroal - Database connection pool (io.quarkus:quarkus-agroal)
[INFO] 	 * Arc (io.quarkus:quarkus-arc)
[INFO] 	 * AWS Lambda (io.quarkus:quarkus-amazon-lambda)
[INFO] 	 * Camel Core (io.quarkus:quarkus-camel-core)
[INFO] 	 * Camel Infinispan (io.quarkus:quarkus-camel-infinispan)
[INFO] 	 * Camel Netty4 HTTP (io.quarkus:quarkus-camel-netty4-http)
[INFO] 	 * Camel Salesforce (io.quarkus:quarkus-camel-salesforce)
[INFO] 	 * Eclipse Vert.x (io.quarkus:quarkus-vertx)
[INFO] 	 * Hibernate ORM (io.quarkus:quarkus-hibernate-orm)
[INFO] 	 * Hibernate ORM with Panache (io.quarkus:quarkus-hibernate-orm-panache)
[INFO] 	 * Hibernate Validator (io.quarkus:quarkus-hibernate-validator)
[INFO] 	 * Infinispan Client (io.quarkus:quarkus-infinispan-client)
[INFO] 	 * JDBC Driver - H2 (io.quarkus:quarkus-jdbc-h2)
[INFO] 	 * JDBC Driver - MariaDB (io.quarkus:quarkus-jdbc-mariadb)
[INFO] 	 * JDBC Driver - PostgreSQL (io.quarkus:quarkus-jdbc-postgresql)
[INFO] 	 * Kotlin (io.quarkus:quarkus-kotlin)
[INFO] 	 * Narayana JTA - Transaction manager (io.quarkus:quarkus-narayana-jta)
[INFO] 	 * RESTEasy (io.quarkus:quarkus-resteasy)
[INFO] 	 * RESTEasy - JSON-B (io.quarkus:quarkus-resteasy-jsonb)
[INFO] 	 * Scheduler (io.quarkus:quarkus-scheduler)
[INFO] 	 * Security (io.quarkus:quarkus-elytron-security)
[INFO] 	 * SmallRye Fault Tolerance (io.quarkus:quarkus-smallrye-fault-tolerance)
[INFO] 	 * SmallRye Health (io.quarkus:quarkus-smallrye-health)
[INFO] 	 * SmallRye JWT (io.quarkus:quarkus-smallrye-jwt)
[INFO] 	 * SmallRye Metrics (io.quarkus:quarkus-smallrye-metrics)
[INFO] 	 * SmallRye OpenAPI (io.quarkus:quarkus-smallrye-openapi)
[INFO] 	 * SmallRye OpenTracing (io.quarkus:quarkus-smallrye-opentracing)
[INFO] 	 * SmallRye Reactive Messaging (io.quarkus:quarkus-smallrye-reactive-messaging)
[INFO] 	 * SmallRye Reactive Messaging - Kafka Connector (io.quarkus:quarkus-smallrye-reactive-messaging-kafka)
[INFO] 	 * SmallRye Reactive Streams Operators (io.quarkus:quarkus-smallrye-reactive-streams-operators)
[INFO] 	 * SmallRye Reactive Type Converters (io.quarkus:quarkus-smallrye-reactive-type-converters)
[INFO] 	 * SmallRye REST Client (io.quarkus:quarkus-smallrye-rest-client)
[INFO] 	 * Spring DI compatibility layer (io.quarkus:quarkus-spring-di)
[INFO] 	 * Undertow (io.quarkus:quarkus-undertow)
[INFO] 	 * Undertow WebSockets (io.quarkus:quarkus-undertow-websockets)
[INFO] 
Add an extension to your project by adding the dependency to your project or use `mvn quarkus:add-extension -Dextensions="name"`
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.696 s
[INFO] Finished at: 2019-03-11T19:45:32+09:00
[INFO] ------------------------------------------------------------------------

上記のリストを元に、追加するエクステンションの groupId と artifactId を指定し追加ができます。

$ mvn quarkus:add-extension -Dextensions="groupId:artifactId" 

例:

$ mvn quarkus:add-extension -Dextensions="io.quarkus:quarkus-smallrye-health" 

9. さいごに


Quarkus は他に、CompletionStage を利用した非同期処理の実装や、JUnit のテストも REST Assured を利用して非常に書きやすくなっており、全体的にとても良くできた技術だと感じています。
ご興味ある方はぜひ、お試しください!

その他の Java on Azure に関する日本語情報へのリンク

2019年3月11日 at 3:27 PM 1件のコメント

Kubernetes を本番環境に適用するための Tips

先日 JAPAN CONTAINERDAYS v18.12 に参加し 「40 Topic of Kubernetes in 40 Minutes」 という内容で 40 個の Kubernetes に関するトピックを 40 分でご紹介する内容を発表させていただきました。

第一弾の記事:JAPAN CONTAINERDAYS v18.12 での発表について

時間の関係上、セッション中でも詳しくお伝えできなかった所もあるので、ここで真意も含めて共有したいと思います。特に Kubernetes に関連した Topic 26-33 の部分を詳しくご紹介します。



26. すべての k8s 機能を利用する必要はない
Please Remember J2EE !!

27. 複雑な構成を作らない
運用構成を変えればできることを無理して k8s でしない
Simple is the Best

28. K8s にはポータビリティがある?
ある点とない点がある

29. どのバージョンを利用していますか?
バージョン毎に設定や機能の差異がある

30. 大規模 k8s クラスタを構築しない !!
ノード数を多く作らない

31. K8s のバージョン・アップは慎重に!!

32. ボリュームを扱う際には注意しましょう
Persistence Volume は極力使わないで

33. DB はマネージド・サービスを利用 !!
開発環境は OK !! でも本番は NG !!


26. すべての k8s 機能を利用する必要はない
Please Remember J2EE !!

今、Kubernetes は急速な勢いでエコシステムを拡大しています。エコシステム全体を通すと、並行して 2000 を超えるプロジェクトが走っており、日々何か新しい k8s 関連のツールや機能ができていると言っても過言ではありません。これは、もちろん Kubernetes のコンセプトが素晴らしく、多くの可能性が秘められているため、多くの方が賛同しこの状況が生まれていると思います。一方で、私は過去の経験から、今の急速な k8s の拡大に危惧している点もあります。出来る事が多くなればなる程、教育コストや運用・管理コストは大きくなっていきます。

私は Java を古くから見ている人間なので、今の状況を J2EE の時と同じような目で見ています。全てのエンタープライズ開発のニーズに応える為に J2EE はどんどん肥大化していき、結果として複雑で扱いづらくなりました。J2EE 当時は XML での設定でしたが、今 k8s では YAML で設定してます、そして YAML が辛いとか言ってらっしゃるのをお見かけします。ちょっと似てますね。

自分のやりたい事、実現したいことに対して、本当に、k8s の全機能は必要でしょうか?

例えば、マイクロサービスを作って行く事を考えると、Deployment は必須で一番の核となる機能です。一方で、それ以外の機能はどうでしょうか?すべてのマイクロサービス開発者や運用者にとって必要でしょうか?(理解しておくのは必要ですが使うかどうかは別)例えば、Job にしても Cron Job にしても、本当に k8s で実行する必要あるのでしょうか?他の別の場所でも実行出来ませんか?全てを k8s 中心で考えるのではなく、こうした事を考えるのはとても重要です。また DaemonSet にしても log を収集したりする際には設定が必要ですが、マイクロサービスを開発する多くの場合においては、多くの開発者にとっては必要無いでしょう。こうした考えを持つ事で、優先的にどの技術から検証をして行けば良いのか優先づけを行う事ができるようになります。

古いたとえですが、
J2EE で言うところの「JSP, Servlet は便利で使うけど、EJB は難しいし設定も大変なので積極的には使わない」というのに似ています(おっさんなので例えが古く、若い方にはわからない例え話で申し訳ありませんが)

でもこれ以外にも、J2EE と (k8s + エコシステム)って色々な点で良く似ているなと思います。例えばコンテナの Init Container や Pod の PostStart や PreStop なんて、Java で言う所のコンストラクタであったり、PostConstruct, PreDestroy と同様の概念ですし、Istio なんかは、AOP(Aspect Oriented Programming)をコンテナの世界に持ってきたような物です。つまり、今までエンタープライズ Java とかをやってきた人にとっては、k8s は決して目新しい物ではなく、今まで Java でやってきた事がコンテナに置き換わったんだなと思える部分は多々あると思います。

その観点で言うと、時代は繰り返すと言いますが、J2EE において Struts や Spring が出てきたように、今の k8s に対して、新しい簡単で軽量な別のオーケストレーション・ツールが出てきてもおかしくないかな?それを作った人は第二の Rod Johnson (Springの作者)になり、もしかしたらそこに新しい別のビジネスチャンスがあるのでは?と思う位です。

結局、ここで何が言いたいかと言うと、k8s に僕たちが使われるのではなく、k8s の便利な機能を私たちが選んで使うんだ!と言う気持ちや感覚が大切なのです。

無理して全部使わなくても良いし別の手段があるならば別を使っても良いと思っています。

27. 複雑な構成を作らない
運用構成を変えればできることを無理して k8s でしない
Simple is the Best

これも、26 によく似た話なのですが、k8s では複雑な構成を組むこともできます。名前空間の概念があるので、1つの k8s クラスタでマルチテナントとか、開発、テスト、本番環境を構築することも「できます」。さらに、RBAC も利用できる為、各ユーザ毎に細かい認証・認可の設定も「できます」。

でも、やります?

できるのと、実際やるかどうかは別の話で、少なくとも本番環境と他は分けるべきと私は思っています。本番環境のアプリと開発・テストのアプリが同一 Linux VM で動作するのは気持ち悪いです。
頑張って、名前空間を切り RBAC を設定し、さらには NodeSelector や Node Affinity の設定をして特定の POD を特定の Node (Linux VM) 上で走らせる事で影響を少なくできるでしょう。確かにできるのですが、やればやるほど設定は増えて行くし、設定ミスを誘発しやすくなります。そして設定をミスした時の影響はより大きくなります。

そこで、設定にしても構成にしても難しい構成にしないように留めておくべきと私は考えます。なぜなら人間は必ずミスをするからです。

私ならば、本番環境と開発・テスト環境などを分けて構築します。本番環境を触れられる人を物理的に制限しておく方が RBAC で一生懸命頑張って設定するよりも簡単ですし、確実に守れるからです。このように構成を変える事で簡単にそしてより安全にできるならば、そちらを選んだ方が私は良いのではないかと思っています。

28. k8s にはポータビリティがある?
ある点とない点がある

k8s で良く言われることに、k8s はポータビリティがあるから導入したいとか、k8s にするとベンダーロックインがなくなり移行しやすいと言われる事があるのですが、これは正しくもあり間違いもあります。

一部の設定に限って言うと同じ設定を他の環境で動かすこともできます。

しかし、細かい話をすると、実際にはノードで動いている Linux VM の種類やカーネルバージョン、さらにはコンテナ・ランタイム(Docker)のバージョンも各環境で違います。また、筆者は Azure Kubernetes Engine を利用してカスタムの k8s クラスタ環境を構築した経験もありますが、利用するテンプレートによって、機能が有効になったり無効になったりと構築時のオプション指定で、同じ k8s バージョンでも利用できる機能、できない機能を使い分ける事ができます。
これは、同じバージョンの k8s でも環境によっては使える機能、使えない機能があると言う事を示しています。

さらに言うと、k8s はご存知の通りバージョン・アップが頻繁に行われていますが、バージョンが変わる事で YAML の書き方や、場合によってはコマンドの引数が変わることまであります。
各ベンダーで提供している k8s のバージョンも時期によってメジャーバージョンやマイナーバージョンで差異が大きくありますので、全く同時期に同じバージョンのクラスタを構築するのは難しいかもしれません。

同じベンダーの k8s を利用した場合であっても、バージョンが異なると動かなくなる可能性があるのに、異なるベンダー環境 (OS, コンテナ・ランタイム(Docker)) で動かす事は本当にポータビリティがあると言って良いのでしょうか?

プレゼンでの発表時は例として、LoadBalancer や Volumeが違うことを例にあげましたが、それだけではない差がある事を、ここでお分かりいただけるかと思います。

私は、ポータビリティがあるからといって k8s の事をお勧めされる方のお話にはご注意くださいと、お客様には申し上げています。

29. どのバージョンを利用していますか?
バージョン毎に設定や機能の差異がある

28 で既に、バージョンの差異における課題について申し上げましたが、上記のような理由からどのバージョンを利用しているのかを強く意識する必要があります。

30. 大規模 k8s クラスタを構築しない !!
ノード数を多く作らない

AKS では 100 ノード、AKS Engine を使うと 1200 ノードまで、Kubernetes のノード数を増やす事が「できます」。もちろん、ニーズに応じて大規模クラスタを構築して1つのクラスタ内で全てを完結したいと思われる方もいらっしゃるでしょう。しかし、それほど大規模なクラスタを構築するのが果たして本当に必要なのかどうなのか十分に考えてお作りください。特に、メンテナンス性を考えて構築することを強くお勧めします。

たとえば、仮に1度クラスタを作ってしまえば、クラスタのメンテナンスが一切不要と言うことでしたら、大規模クラスタを構築するのもありでしょう。しかし実際には k8s もメンテナンスが必要です。各ノードの Linux VM にパッチを適用する必要もありますしバージョン・アップが必要になる場合もあります。各ノードをローリング・アップグレードで更新し完了するまでどれ程の時間を要するでしょうか。またそのメンテナンスの際、ノードが予期せぬ不具合にあった場合、もしくは API サーバ(管理サーバ)にトラブルが発生した場合どのような事態が発生するかを考えてみていただけないでしょうか。最悪の場合 k8s クラスタが壊れてまったく操作できなくなる事もありえます。その場合、またイチから 100-1200 ノードの k8s クラスタを構築するのにどれくらい時間がかかるでしょうか。再構築する間は一切のサービスが受けられなくなる程の大影響を及ぼしてしまいます。

POD を利用しコンテナ・レベルでいくら、マイクロサービスの考えに従って回復性や、可用性やスケールなどを考えていても、一番下側の VM 側がメンテナンスしやすいように作られていなければ、結局モノリシックに構築しているのと全く変わらなくなってしまいます。1つのクラスタに対するトラブルで全サービスに影響が出てしまいます。

私ならば、k8s のクラスタレベルでも適度なサイズ(もちろんマイクロやミニのレベルではなくてよく)、ミドルサイズくらい、もっと言うならば何か不足な事態が発生してもすぐに対応がしやすいようなサイズで構築することをお勧めします。限界にチャレンジしてもあまり良いことはないように思います。

31. K8s のバージョン・アップは慎重に!!

30 でも少しバージョン・アップに対して触れましたが k8s のバージョン・アップはとても危険を伴います。ベンダーが提供するバージョン・アップ・ツールやコマンドを利用すると簡単にバージョン・アップが「できる」でしょう。ただしその恩恵が得られるのは、コマンドやツールが正常に完了した場合のみです。コマンドやツールが失敗する事もありますし、その場合、最悪クラスタが壊れる事もありえます。

さらに言うならば、k8s はバージョン・アップする事で古い YAML の設定ファイルが、新しいバージョンの環境で必ず動くと言う保証はどこにもありません。そのような事態が発生する可能性がある中で、それを実行しますか?
本番サービスに直ちに影響を及ぼしても良いでしょうか?

せっかく、Deployment や Istio などを使って、ブルー・グリーン・デプロイやカナリー・デプロイなどの手法を使って、pod レベルでは本番に影響しないように頑張っても、クラスタ・レベルでも同様の事を考えていなければ意味がありません。影響は pod の比ではないほど甚大です。

もし、k8s クラスタのバージョン・アップをするならば、私ならば k8s レベルでも、新規にクラスタを構築し、リクエストのルーティングをクラスタ・レベルで 8:2 などで試しながら安全にバージョン・アップしていくでしょう。

32. ボリュームを扱う際には注意しましょう
Persistence Volume は極力使わないで

追記:この項目は Azure 用です (2018/12/07)

k8s では PV (Persistence Volue), PVC (Persistence Volume Claim) を利用して外部ストレージをマウントする事ができます。しかし、筆者の経験では PV や PVC を扱いボリュームをマウントさせた際に、今まで様々な問題に遭遇してきました。

代表的なのは下記の URL の記事のような問題です。

* How to Understand & Resolve “Warning Failed Attach Volume” and “Warning Failed Mount” Errors in Kubernetes on Azure
* Getting Unstuck with EBS: Primer on How to Use Docker and EBS

簡単に説明します。Deployment で2つの replica を常時起動しているように設定し、それぞれの pod にボリュームを1つづつマウントさせる場合を考えます。

1 pod に 1 ボリュームをマウントした場合、内部的にそれぞれ Attatch, Detach の状態を保持しているのですが、仮に1つの Pod が不意に死んだ場合、綺麗にボリュームのマウント状態を Detach してくれない場合があります。
この場合、k8s のスケジューラは1つが停止したため、新しい Pod の再作成を試みますが、ボリュームが Detach されていないため、再作成の Pod がマウントするボリュームがないため再起動に失敗する場合があります。一応、コマンドを打てば修正する方法はあるのですが、pod がいつ不意に停止するかは想定できません。

また、それ以外にもボリュームのメンテナンスはそれなりに大変です。実際にボリュームのマウントには時間を要します (実装に応じて異なる)。そして、k8s v1.13 までは、使用中のボリューム・サイズを拡張する事は出来ませんでしたので、マウントしているボリュームがいっぱいになると別のボリュームを再作成しマウントし直すなどの必要もありました。(v 1.13 に対応しているのはまだ少ないので多くの方がまだこの状況)

さらには、k8s や AKS の GitHub の Issue を見ても数多くの PV, PVC に関する問題が報告されています。

https://github.com/kubernetes/kubernetes/issues
https://github.com/Azure/acs-engine/issues

将来的には、PV, PVC のクオリティも向上し、利用する場面におけるベストプラクティスなどが出て安心して使用できるようになる時がくる事が期待されますが、現時点では、私ならば極力本番環境には適用したくないと言うのが本心です。

もしも、違う方法で回避ができるならばそちらの方法をお勧めしています。
例えば、ファイルの保存などが目的なのであれば、Stroage 用の SDK を使ってプログラム側から直接、保存・更新・削除などを実装する方法をお勧めしています。こちらの方が、POD 増加・減少それぞれにおいてもスケールしますし、Attach, Detach を気にする事もありません。

追記:2018/12/07 ここから —



また、上記以外でも私は、PV, PVC を積極的に採用しない理由があります。
実装するアプリケーションやサービス側から見て、static でマウントするにせよ dynamic でマウントするにせよ、1:1 でマウントする場合は、ボリュームのメンテも必要になります。なぜならはk8s の v1.13 まではボリューム・サイズを固定で設定しなければならずボリュームの拡張もできません。結果として管理項目も増えます。そしてアプリケーションのスケールの観点でもスケールがし辛くなります。

私ならば、プログラム的に Azure Storage などの外部ストレージに書き出した方が、アプリケーションのスケールもしやすいと考えますし、ディスク管理や制限に対する考え方も容易になります。

プログラムやサービス側からは、インメモリ・グリッドのような横にスケール・アウトさせる事ができるようなサービスを利用する方が、アプリケーションをスケールさせ易いので、私ならば、こうした違う方法を模索します。

そして、同僚の真壁さん (@tmak_tw) からのご意見はコチラ




追記:2018/12/07 ここまで —

それでも、もちろんボリュームをマウントして使いたいと言うニーズもあると思います。その際は上記のようなことが発生する可能性や他にも issue に上がっているような問題を認識した上でお使いいただく事で問題が発生した時に迅速に対応ができるようになるかと思います。ぜひお気をつけてお使いください。

33. DB はマネージド・サービスを利用 !!
開発環境は OK !! でも本番は NG !!

追記:この項目は Azure 用です (2018/12/07)

32. の「ボリュームを極力使わないでください」の延長線上で、ボリュームを扱うサービスは極力外に出したいです。例えば、代表的な所で DB が一番最初にきますが、プライベートの Docker レジストリや、Git のレポジトリなどボリュームを扱かわなければならないようなサービスは k8s の中ではなく、極力外で管理をしたいです。特に DB などのサービスはマネージドな DB の方が自分で管理をするよりも簡単に安全にご使用できる場合がありますので、仮想ネットワーク経由で安全な接続を利用した上で外部サービスに接続する方法をお勧めしたいと思います。

ご参考:AKS access to Azure Database for MySQL via VNet

最後に

k8s は今、エンタープライズ・システムの構築に必要な機能やサービスを、どんどんと追加しています。その際に、我々は一旦立ち止まって、全てを k8s 上に実装しなくても良いのでは?という考えを持つ事が重要なのではないかと思います。
それにより簡単に運用管理ができるような場面もあると思いますし、思い切ってここは使うけど、ここは使わないと言うご検討をいただくのがより有効にご活用いただけるようになるのではないかと思います。

この記事は 2018 年 12 月時点で、私が k8s HackFest などを通じて経験してきた内容をまとめていますが、もちろん今後 k8s は品質も向上しさらなる機能追加も行われていくと思います。今後、私たちは常に k8s の最新動向をウォッチしながら、できる事と、試す事、実際に本番に適用する事を分けて考えて考えて行かなければならないと思います。

ご自身にとって最適な構成をご検討・構築いただければ誠に幸いです。

私も完璧な人間ではありませんので、もし、ここは違うよなどがあれば、この記事をもとに議論したりさらなるご検討をいただければ誠に幸いです。

2018年12月6日 at 8:20 PM 1件のコメント

JAPAN CONTAINERDAYS v18.12 での発表について

先日 JAPAN CONTAINERDAYS v18.12 に参加し 「40 Topic of Kubernetes in 40 Minutes」 という内容で 40 個の Kubernetes に関するトピックを 40 分でご紹介する内容を発表させていただきました。

1-16 のトピックについては当日はデモをしながら説明をしたのですが、参加してくださった方のお一人が具体的に私がどのようなデモをしたのかをまとめてくださっていました。(誠にありがとうございます。)

Topic 1-16 のまとめ記事はコチラ。
Container Days × kubernetes(k8s) × 「40 topic of kubernetes in 40 minutes」メモ

今回の私の発表内容には賛否両論があるかと思いますが、この内容は、2018 年 12 月の現時点で、私が私のお客様に対してお伝えする、Kubernetes(k8s) を本番環境に対して適用する際の注意ポイントをまとめた内容です。

まず、私の立ち位置を簡単にご紹介しますと、私は k8s の製品営業でも k8s の担当エンジニアでもございません。k8s の良さを理解しつつ、正しい所で正しく使っていただきたいと思っているエンジニアです。もし、提供するサービスが k8s にあっていなければ、違う方法を選択したほうが良いですよと私のお客様には言っています。そして、それによって扱うエンジニアが幸せになり、企業もより良いサービスを素早く提供していただきたいと考えています。

余談ですが、実話としてあるお客様から以前こんなご相談を受けました。

相談者:「寺田さん、御社では無い方からこのような事を言われまして」
某営業さん:「このシステムを将来的にコンテナ化し k8s を導入しないと駄目だ!将来がなくなる」
相談者:「えっ?えっ?」
相談者:「寺田さん、本当なんですか?」
寺田:「どのようなシステムなんですか?」
相談者:「こんなシステムで、今はこのような開発人員構成です」
寺田:「それなら、無理にしなくていいです」

最初に話を伺った際に、マジかこの営業?お客様を地獄の底に連れて行きたいのか?と思ったくらいでした。

k8s は確かに素晴らしいので、上手く使いこなせばとても大きな効果を得られます。しかし、扱い方を間違えると効果が得られないばかりでなく、余計管理コストは高くつき大変な思いをすることにもなるでしょう。

今回のイベント会場の懇親会会場でも別の方から、下記のようなご質問をいただきました。
「エンドのお客様がコンテナ化や k8s 化に期待しており、会社としてやらなければならないのだが、上司から今までと作り方もやり方は変えられない。」
寺田:「いえ、それコンテナ化してもメリット無いですよ!」

このようなやりとりは、今の日本に少なからずあると思うのです。k8s を導入したらそれだけで幸せになれると思っているのでしょうか?

まず、その場合に私が最初にお伺いするのは下記のような内容です。

「サービスは今後どのように成長して行きますか?」
「もっというならば、新機能の追加は今後数多くありますか?」
「作ったサービスの改良やバージョンアップは頻繁にありますか?」

共に YES ならば k8s を検討し始めても良いですし、そうでなければ違う道を模索して良いと思います。

実は、このセッションは私の、k8s を扱うまでの三部作シリーズの3本目のコンテンツになります。

1. 今後の開発ビジョン


2. Java on Kuberenetes on Azure

3. 40 Topic of Kuberenetes in 40 Minutes

1 に関しては、Kubernetesは銀の弾丸ではない――エンジニアが生き残るために必要な技術とは【デブサミ2018 関西】に発表内容がまとめられていますので、こちらをご参考にいただければと思います。k8s を扱うその前に、心構えであるとかバックグラウンドを説明したのがこちらです。

ポイントは、「どのような時に、そして何のために k8s を使うのか?」を考える事がとても重要だと言う事です。

実際に k8s を扱うにしても、出来る事は数多くあります、その際、この機能は必要?別のほうが簡単じゃない?管理が楽になるんじゃ無い?と思うならば、率先して違う手段も考えるのも良いと思います。いや、それでも k8s のここが便利だから使って、いち早くサービスを提供・展開したいと思って初めて、k8s を扱う土俵に立てるのだと思います。

こうした背景を踏まえて、三部作の3つ目として作成したのが今回のコンテンツでした。特に 26-33 の項目は、おそらく今まで誰も表立って触れてこなかった内容だと思いますので、あえてこの場を借りて発表しました。

時間の関係上、セッション中でも詳しい点をお伝えできなかった所もあるので、ここで真意も含めて共有したいと思いますが、長くなるので別エントリに分けます。

本エントリの続きはこちら
第2弾:Kubernetes を本番環境に適用するための Tips

2018年12月6日 at 1:36 PM 1件のコメント

過去の投稿


Java Champion & Evangelist

Translate

ご注意

このエントリは個人の見解であり、所属する会社の公式見解ではありません

カレンダー

2020年2月
« 1月    
 12
3456789
10111213141516
17181920212223
242526272829  

カテゴリー

Twitter

clustermap

ブログ統計情報

  • 1,172,846 hits

RSSフィード

アーカイブ