ACK で OpenKruise をためしてみた

こんにちは、ソリューションアーキテクトの有馬です。
今回はオープンソースで提供されている OpenKruise を、Container Service for Kubernetes (ACK) へデプロイして動作をためしてみたいと思います。

f:id:sbc_sarima:20210414114035p:plain

Alibaba サービスでのコンテナ利用の背景

Alibaba では以前からサービスのコアシステムとして、Kubernetes ベースのクラウドネイティブ環境へ移行を行ってきました。
Double 11 Global Shopping Festival などを含む、Alibaba サービスで利用されているコンテナクラスター内のアプリケーションの数は100,000を超え、そのコンテナーの数は数百万に達しています。 このような大規模な環境下でのコンテナリリース管理では、多数のアプリケーションのアップデートで発生する Pod のスケジューリング・ディスク割り当て・イメージ取得などにより、リリース完了までの時間がさらに長くなります。 Kubernetes ネイティブワークロードだけでこれらの要件を満たす事は難しく、Alibaba Cloud ではこの問題を解決する為に OpenKruise を開発しました。
OpenKruiseは、Alibaba サービスの多くのアプリケーションの展開とリリース管理で使用されています。



OpenKruise の主な特徴

OpenKruise は Alibaba Cloud によって大規模アプリケーション向けに開発された、オープンソースのアプリケーションマネージメントエンジンです。
Deployment や StatefulSet など、Kubernetes ネイティブワークロードと同様の機能に加え、多くの拡張機能を提供しています。
現在 OpenKruise は、Cloud-Native Computing Foundation(CNCF)の下、サンドボックスプロジェクトとしてホストされています。

OpenKruise の重要な機能の1つに「インプレースアップデート」があります。リリース時にコンテナーを再作成・スケジュールする事なく Pod のイメージのみをアップグレードする事ができます。
Alibaba 環境ではこの機能を利用し、従来のアップグレードよりも展開速度が80%以上向上しました。
その他にも、サイドカーコンテナインジェクションやドメイン単位でのワークロード管理、イメージのプレダウンロードをサポートしています。

◆ インプレースアップデート

・ Pod を削除および再作成せずにコンテナイメージを更新する方法で、Pod の配置先やIPアドレス・名前などが変更されないため、アップデーと時間を短縮します。

◆ サイドカーマネージ

・ Pod へサイドカーコンテナをインジェクトし管理します。

◆ マルチフォールトドメインデプロイメント

・ さまざまなドメインのワークロードのレプリカ、テンプレートおよびアップデートを管理できます。

◆ イメージプレダウンロード

・指定したノードへコンテナイメージをダウンロードします。



リソース

OpenKruise で利用可能なリソースには、上記で説明した特徴的な機能が含まれており、各リソース名からもわかるように、Kubernetes のネイティブリソースを拡張させたような機能が利用できるようになっています。 以下に各リソースを簡単にまとめてみましたが、詳細な設定内容を確認したい場合は、オフィシャルのドキュメントを参照頂ければと思います。

openkruise.io

CloneSet

CloneSet は、ステートレスアプリケーションを管理するためのリソースで、スケールアウト、スケールイン、インプレースアップデートや指定した Pod の削除などが実行でき、Deployment を拡張したようなリソースです。

Advanced StatefulSet

Advanced StatefulSet は、インプレースアップデート、MaxUnavailable を使用したローリングアップデートがサポートされた StatefulSet を拡張したリソースです。

SidecarSet

SidecarSet は AdmissionWebhook を使用し、 Pod にサイドカーコンテナをインジェクトします。サイドカーコンテナのインプレースアップデート、ボリュームのマウントをサポートしています。

Advanced DaemonSet

Advanced DaemonSet は、ノードに1つだけ Pod を展開し、MaxSurge, partition, Pause を使用したローリングアップデートをサポートしています。

UnitedDeployment

UnitedDeployment は、クラスター内のラベルで識別された複数ノードのグループに対してリソースを Pod をデプロイします。 statefulSetTemplate, advancedStatefulSetTemplate, cloneSetTemplate, deploymentTemplate をサポートしています。

BroadcastJob

BroadcastJob は、クラスター内の全てのノードへ Pod をデプロイしジョブを実行します。

AdvancedCronJob

AdvancedCronJob は、スケジュールされたジョブや BroadcastJob を実行します。

ImagePullJob

ImagePullJob は、selector を使用して指定したノードへコンテナイメージをダウンロードし、事前にイメージをウォームアップします。


実行例

では、公式ドキュメントを参考に動作を確認して行きたいと思います。
事前準備として、OpenKruise を Kubernetes 環境へデプロイしておく必要があり、Alibaba Cloud の ACK コンソールの App Catalog からもデプロイする事も出来るのですが、こちらは最新バージョンではない為、今回はドキュメント記載の Helm からセットアップしています。

デプロイすると CustomResourceDefinition (CRD) , Service , Deployment, Daemonset が作成されます。

$ helm install kruise https://github.com/openkruise/kruise/releases/download/v0.8.1/kruise-chart.tgz

$ kubectl get all -n kruise-system
NAME                                             READY   STATUS    RESTARTS   AGE
pod/kruise-controller-manager-6797f89d9b-28vg9   1/1     Running   0          26s
pod/kruise-controller-manager-6797f89d9b-hkz7f   1/1     Running   0          26s
pod/kruise-daemon-9ss7t                          1/1     Running   0          26s
pod/kruise-daemon-dgtr7                          1/1     Running   0          26s
pod/kruise-daemon-jcjvn                          1/1     Running   0          26s

NAME                             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/kruise-webhook-service   ClusterIP   172.16.13.160   <none>        443/TCP   26s

NAME                           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
daemonset.apps/kruise-daemon   3         3         3       3            3           <none>          26s

NAME                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/kruise-controller-manager   2/2     2            2           26s

NAME                                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/kruise-controller-manager-6797f89d9b   2         2         2       26s


$ kubectl get crd | grep kruise
advancedcronjobs.apps.kruise.io                  2021-04-14T06:14:48Z
broadcastjobs.apps.kruise.io                     2021-04-14T06:14:48Z
clonesets.apps.kruise.io                         2021-04-14T06:14:48Z
daemonsets.apps.kruise.io                        2021-04-14T06:14:48Z
imagepulljobs.apps.kruise.io                     2021-04-14T06:14:48Z
nodeimages.apps.kruise.io                        2021-04-14T06:14:48Z
sidecarsets.apps.kruise.io                       2021-04-14T06:14:48Z
statefulsets.apps.kruise.io                      2021-04-14T06:14:48Z
uniteddeployments.apps.kruise.io                 2021-04-14T06:14:48Z


CloneSet

OpenKruise の重要な機能の一つである「インプレースアップデート」と、Pod のスケールインの際に指定した Pod を削除する 「Selective Pod deletion」 を CloneSet から実行してみます。
インプレースアップデートを使用する事で、Pod の Name, IPアドレス , 起動しているホストを変更せず、コンテナイメージだけアプデートすることが出来るようになっています。

f:id:sbc_sarima:20210422151405p:plain


ドキュメント記載のサンプルマニフェストを作成し CloneSet をデプロイします。
updateStrategytype: InPlaceOnly を設定することで、インプレースアップデートでアップデートが実行されるようになります。

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
  labels:
    app: sample
  name: sample
spec:
  replicas: 5
  selector:
    matchLabels:
      app: sample
  updateStrategy:
    type: InPlaceOnly
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: nginx
        image: nginx:alpine


CloneSet から nginx:alpine イメージで Pod が作成されました。
作成された Pod のステータスを確認しておきます。

$ kubectl get cloneset
NAME     DESIRED   UPDATED   UPDATED_READY   READY   TOTAL   AGE
sample   5         5         5               5       5       15s

$ kubectl get pod -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP           NODE                           NOMINATED NODE   READINESS GATES
sample-4d2gd   1/1     Running   0          16s   10.8.0.150   ap-northeast-1.192.168.1.170   <none>           1/1
sample-7457c   1/1     Running   0          16s   10.8.0.148   ap-northeast-1.192.168.1.170   <none>           1/1
sample-kh852   1/1     Running   0          16s   10.8.0.147   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tcnpm   1/1     Running   0          16s   10.8.0.149   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tvrb7   1/1     Running   0          16s   10.8.0.151   ap-northeast-1.192.168.1.170   <none>           1/1


$ kubectl get pods sample-4d2gd -o jsonpath='Name: {.metadata.name}{"\n"}uid: {.metadata.uid}{"\n"}hostIP: {.status.hostIP}{"\n"}podIP: {.status.podIP}{"\n"}image: {.status.containerStatuses[].image}{"\n"}'
Name: sample-4d2gd
uid: 9cae0047-8f4b-448f-bc35-c804ff4b3fbe
hostIP: 192.168.1.170
podIP: 10.8.0.150
image: nginx:alpine


ではコンテナイメージをnginx:alpine から debian ベースのnginx:latestへマニフェストを変更しデプロイします。
一度 Pod が再起動された為 RESTARTS1 になっています 。
Pod のステータスを確認すると、アップデート前と同じ状態のままでコンテナイメージだけが変更されています。

$ kubectl get pod -o wide
NAME           READY   STATUS    RESTARTS   AGE     IP           NODE                           NOMINATED NODE   READINESS GATES
sample-4d2gd   1/1     Running   1          2m43s   10.8.0.150   ap-northeast-1.192.168.1.170   <none>           1/1
sample-7457c   1/1     Running   1          2m43s   10.8.0.148   ap-northeast-1.192.168.1.170   <none>           1/1
sample-kh852   1/1     Running   1          2m43s   10.8.0.147   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tcnpm   1/1     Running   1          2m43s   10.8.0.149   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tvrb7   1/1     Running   1          2m43s   10.8.0.151   ap-northeast-1.192.168.1.170   <none>           1/1


$ kubectl get pods sample-4d2gd  -o jsonpath='Name: {.metadata.name}{"\n"}uid: {.metadata.uid}{"\n"}hostIP: {.status.hostIP}{"\n"}podIP: {.status.podIP}{"\n"}image: {.status.containerStatuses[].image}{"\n"}'
Name: sample-4d2gd
uid: 9cae0047-8f4b-448f-bc35-c804ff4b3fbe
hostIP: 192.168.1.170
podIP: 10.8.0.150
image: nginx:latest


次に Pod のスケールインで、指定した Pod だけ削除される Selective Pod deletion を試してみます。
こちらは、先ほどのマニフェストへ scaleStrategy:podsToDelete:- <削除したいPod> を設定します。今回は replicas4 へ変更し sample-4d2gd が削除されるようにしてみます。
※ podsToDelete を設定しない場合は、ランダムに Pod が削除されます。

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
  labels:
    app: sample
  name: sample
spec:
  replicas: 4
  scaleStrategy:
    podsToDelete:
    - sample-4d2gd
  selector:
    matchLabels:
      app: sample
  updateStrategy:
    type: InPlaceOnly
  template:
    metadata:
      labels:
        app: sample
    spec:
      containers:
      - name: nginx
        image: nginx:latest


指定した sample-4d2gd が削除され、CloneSet の値も更新されています。

$ kubectl get pod -o wide
NAME           READY   STATUS        RESTARTS   AGE   IP           NODE                           NOMINATED NODE   READINESS GATES
sample-4d2gd   0/1     Terminating   1          12m   10.8.0.150   ap-northeast-1.192.168.1.170   <none>           1/1
sample-7457c   1/1     Running       1          12m   10.8.0.148   ap-northeast-1.192.168.1.170   <none>           1/1
sample-kh852   1/1     Running       1          12m   10.8.0.147   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tcnpm   1/1     Running       1          12m   10.8.0.149   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tvrb7   1/1     Running       1          12m   10.8.0.151   ap-northeast-1.192.168.1.170   <none>           1/1

$ kubectl get pod -o wide
NAME           READY   STATUS    RESTARTS   AGE   IP           NODE                           NOMINATED NODE   READINESS GATES
sample-7457c   1/1     Running   1          12m   10.8.0.148   ap-northeast-1.192.168.1.170   <none>           1/1
sample-kh852   1/1     Running   1          12m   10.8.0.147   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tcnpm   1/1     Running   1          12m   10.8.0.149   ap-northeast-1.192.168.1.170   <none>           1/1
sample-tvrb7   1/1     Running   1          12m   10.8.0.151   ap-northeast-1.192.168.1.170   <none>           1/1

$ kubectl get cloneset
NAME     DESIRED   UPDATED   UPDATED_READY   READY   TOTAL   AGE
sample   4         4         4               4       4       20m


SidecarSet

Pod へ サイドカーコンテナをインジェクトする SidecarSet を実行してみたいと思います。
SidecarSet は指定したラベルにマッチした Pod に対して、サイドカーコンテナを自動的にインジェクトします。
ロギングやモニタリング・コンテンツ共有などの、サイドカーパターンなどでの利用が想定されます。

f:id:sbc_sarima:20210421171250p:plain

では、こちらも公式ドキュメントのサンプルを参考に、SidecarSet を実行していきます。
実行内容として、はじめに SidecarSet を作成、その後に Pod をデプロイしサイドカーコンテナのインジェクトを確認します。
SidecarSet では Volume を利用する事が出来るので、今回は SidecarSet でインジェクトされたサイドカーコンテナと、
Pod のメインコンテナ で emptyDir をマウントし、コンテナ間でのデータ共有も確認してみたいと思います。

f:id:sbc_sarima:20210422151433p:plain

SidecarSet のマニフェストを作成しデプロイします。
サイドカーコンテナをインジェクトする Pod のラベル app:busybox をセレクターへ設定します。
ボリュームの設定として、emptyDir で ボリュームを /var/log へマウントするようにも設定しています。

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: test-sidecarset
spec:
  selector:
    matchLabels:
      app: busybox
  updateStrategy:
    type: RollingUpdate
    maxUnavailable: 1
  containers:
  - name: sidecar
    image: centos:7
    args:
    - /bin/sh
    - -c
    - sleep 3600
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  volumes:
  - name: log-volume
    emptyDir: {}


SidecarSet が作成されました。

$ kubectl get sidecarsets
NAME              MATCHED   UPDATED   READY   AGE
test-sidecarset   0         0         0       11s


次に Pod のラベルへ app: busybox を設定しデプロイします。
こちらも emptyDir で ボリュームを /var/log へマウントし、データ共有を確認するためテストファイルを書き込むようにしておきます。

apiVersion: v1
kind: Pod
metadata:
  labels:
    app: busybox # matches the SidecarSet's selector
  name: test-pod
spec:
  containers:
  - image: busybox
    name: main
    args:
    - /bin/sh
    - -c
    - echo "sidecar test" > /var/log/test.log && sleep 3600
    volumeMounts:
    - name: log-volume
      mountPath: /var/log
  volumes:
  - name: log-volume
    emptyDir: {}


Pod をデプロイすると、READY2/2 となり、Pod へ サイドカーコンテナ (sidecar) と、メインコンテナー ( busybox) が作成されます。

$ kubectl get sidecarsets
NAME              MATCHED   UPDATED   READY   AGE
test-sidecarset   1         1         1       66s

$ kubectl get pod -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE                           NOMINATED NODE   READINESS GATES
test-pod   2/2     Running   0          6s    10.8.0.183   ap-northeast-1.192.168.1.170   <none>           <none>

$ kubectl describe pod test-pod | tail
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  37s   default-scheduler  Successfully assigned default/test-pod to ap-northeast-1.192.168.1.170
  Normal  Pulled     36s   kubelet            Container image "centos:7" already present on machine
  Normal  Created    36s   kubelet            Created container sidecar
  Normal  Started    36s   kubelet            Started container sidecar
  Normal  Pulling    36s   kubelet            Pulling image "busybox"
  Normal  Pulled     34s   kubelet            Successfully pulled image "busybox" in 2.655062088s
  Normal  Created    34s   kubelet            Created container main
  Normal  Started    34s   kubelet            Started container main


2つのコンテナからマウントされたボリュームで、テストファイルを参照する事ができています。

$ kubectl exec -it test-pod -c main -- cat /var/log/test.log
sidecar test

$ kubectl exec -it test-pod -c sidecar -- cat /var/log/test.log
sidecar test


まとめ

OpenKruise の CloneSet と SidecarSet をためしてみましたが、ご紹介できなかった他のリソースについても簡単に実行できるようになっています。
今回参考にした OpenKruise 公式ドキュメントの他、Alibaba Cloud のブログにも OpenKruise の導入背景や機能が詳しく書かれているので、もしご興味があればぜひ参照してみてください。

community.alibabacloud.com