本記事では、Kubernetesの内部ストレージ処理について説明し、PV、PVC、StorageClass、kubelet、CSIの呼称関係について解説します。
著者:アリババクラウドの開発エンジニア、Sun Zhiheng(Huizhi)氏
#Kubernetesの永続ストレージ:基本的なコンセプト
Kubernetesのストレージプロセスを説明する前に、Kubernetesの永続ストレージの基本概念を確認しておきましょう。
###1)用語
-
In-tree(インツリー):コードロジックがKubernetesのリポジトリ内にあること。
-
Out-of-tree(アウトオブツリー):コードロジックはKubernetesリポジトリの外にあり、Kubernetesのコードから切り離されています。
-
PersistentVolume(PV):クラスタ管理者または外部プロビジョナーによって作成されるクラスタレベルのリソースです。PVのライフサイクルは、PVを使用するポッドとは独立しています。PVの.Specには、ストレージデバイスの詳細が格納されています。
-
PersistentVolumeClaim(PVC):これは、VolumeClaimTemplateに基づいてユーザまたはStatefulSetコントローラによって作成される名前空間レベルのリソースです。PVCはポッドに似ています。ポッドはノードリソースを消費し、PVCはPVリソースを消費します。ポッドは、特定のレベルのリソース(CPUやメモリ)を要求することができ、PVC は、特定のボリュームのサイズとアクセスモードを要求できます。
-
StorageClass:クラスタ管理者が作成するクラスタレベルのリソースです。StorageClassは、ボリュームを動的にプロビジョニングするためのクラステンプレートを管理者に提供します。StorageClassの.Specには、PVの異なるQoS(Quality of Service)やバックアップ・ポリシーが定義されています。
-
CSI:業界標準に準拠したインターフェースで、CSIベースのプラグインを使用することで、ストレージプロバイダー(SP)がさまざまなコンテナオーケストレーション(CO)システムで動作することを可能にしています。COシステムには、Kubernetes、Mesos、Swarmなどがあります。
###2)コンポーネント
-
PV Controllerは、PVとPVCを結びつけ、そのライフサイクルを管理します。また、必要に応じてデータボリュームのProvisionとDeleteを行います。
-
AD Controllerは、データボリュームのAttachとDetachを行い、デバイスをターゲットノードに装着します。
-
Kubeletは、各ノードで動作するメインのノードエージェントです。ポッドのライフサイクルを管理し、コンテナの健全性をチェックし、コンテナを監視します。
-
Volume Manager は、kubelet のコンポーネントです。データボリュームのマウント、アンマウント、アタッチ、およびデタッチ操作を実行します。これらの操作には、Kubelet の特定のパラメータ設定が必要です。また、ボリュームデバイスのフォーマットも行います。
-
Volume Plugins は、ストレージベンダーが開発したストレージプラグインです。様々なストレージクラスのボリューム管理機能を拡張したり、サードパーティ製のストレージの操作機能を実装したりするために使用されます(青色で表示されている操作)。ボリュームプラグインには、インツリーとアウトオブツリーがあります。
-
External Provisionerは、Volume PluginsのCreateVolume関数とDeleteVolume関数を呼び出してProvisionとDeleteの操作を行うサイドカーコンテナです。Kubernetes PV ControllerはVolume Pluginsの関数を直接呼び出すことはできません。これらの関数は、gRPCを介してExternal Provisionerから呼び出されます。
-
External Attacherはサイドカーコンテナで、Volume PluginsのControllerPublishVolume関数とControllerUnpublishVolume関数を呼び出してAttachとDetachの操作を行います。Kubernetes AD Controllerは、Volume Pluginsの機能を直接呼び出すことはできません。これらの関数は、External AttacherからgRPCを通じて呼び出されます。
###3)PVの使用方法
KubernetesはPVとPVCを導入し、アプリケーションや開発者がストレージデバイスの詳細を気にすることなく、適切にストレージリソースを要求できるようにしています。PVは以下のいずれかの方法で作成します。
- クラスタ管理者が、アプリケーションが必要とするPVを手動で静的に作成します。
- ユーザーが手動でPVCを作成し、Provisionerコンポーネントが対応するPVを動的に作成します。
ここでは、ネットワークファイルシステム(NFS)の共有ストレージを例に挙げて、2つのPV作成方法の違いを説明します。
静的にPVを作成する
次の図は、PVを静的に作成するプロセスを示しています。
ステップ1)クラスター管理者は、NFS PVを作成します。NFSは、Kubernetesがネイティブにサポートするツリー内ストレージの一種です。YAMLファイルは以下の通りです。
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.4.1
path: /nfs_storage
ステップ2)ユーザーがPVCを作成します。YAMLファイルは以下の通りです。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
kubectl get pvコマンドを実行して、PVとPVCがバインドされていることを確認します。
[root@huizhi ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-pvc Bound nfs-pv-no-affinity 10Gi RWO 4s
ステップ 3) ユーザーはアプリケーションを作成し、ステップ2で作成したPVCを使用します。
apiVersion: v1
kind: Pod
metadata:
name: test-nfs
spec:
containers:
- image: nginx:alpine
imagePullPolicy: IfNotPresent
name: nginx
volumeMounts:
- mountPath: /data
name: nfs-volume
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: nfs-pvc
NFSリモートストレージは、ポッド内のNGINXコンテナの/data
ディレクトリにマウントされます。
PVの動的な作成
PVを動的に作成するには、クラスタにNFSクライアントプロビジョナと対応するStorageClassが配備されていることを確認します。
静的にPVを作成する場合と比較して、動的にPVを作成する場合は、クラスタ管理者の介入は必要ありません。次の図は、PVを動的に作成するプロセスを示しています。
クラスタ管理者は、環境にNFS関連のStorageClassが含まれていることを確認するだけです。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: nfs-sc
provisioner: example.com/nfs
mountOptions:
- vers=4.1
ステップ1)ユーザーがPVCを作成し、storageClassNameにNFS関連のStorageClassの名前を設定します。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfs
annotations:
volume.beta.kubernetes.io/storage-class: "example-nfs"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Mi
storageClassName: nfs-sc
ステップ2)クラスタ内のNFSクライアントプロビジョナーが、対応するPVを動的に作成します。環境にPVが作成され、PVCにバインドされます。
[root@huizhi ~]# kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE
pvc-dce84888-7a9d-11e6-b1ee-5254001e0c1b 10Mi RWX Delete Bound default/nfs 4s
ステップ3)ユーザーがアプリケーションを作成し、ステップ2で作成したPVCを使用します。このステップは、PVを静的に作成するステップ3と同じです。
#Kubernetes Persistent Storageのプロセス
###1) 概要
次の図は、Kubernetes Persistent Storageのプロセスを示しています。この図は、Junbao社が提供しているクラウドネイティブストレージのコースから引用しています。
では、その手順を見ていきましょう。
- ユーザーは、ダイナミックPVを使用するPVCを含むポッドを作成します。
- スケジューラは、ポッドの構成、ノードの状態、PVの構成に基づいて、ポッドを適切なワーカーノードにスケジュールします。
- PVコントローラは、ポッドが使用するPVCがPending状態であることを監視し、Volume Plugin(ツリー内)を呼び出してPVとPVオブジェクトを作成します。アウトオブツリーの処理は、External Provisionerによって実装されます。
- AD Controllerは、ポッドとPVCが「To Be Attached」状態であることを検出し、Volume Pluginを呼び出して、ストレージデバイスをターゲットのワーカーノードにアタッチします。
- ワーカーノードでは、キューブレット内のボリュームマネージャが、ストレージデバイスが接続されるまで待機し、Volume Pluginを使用して、デバイスをグローバルディレクトリ
/var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PV name]
にマウントします(iscsiは例として使用します)。 - kubeletはDockerを使用してポッド内のコンテナを起動し、bind mountメソッドを使用して、local-globalディレクトリにマウントされているボリュームをコンテナにマッピングします。
次の図は、詳細なプロセスを示しています。
###2)プロセスの説明
永続記憶装置のプロセスは、Kubernetesのバージョンによって若干異なります。本記事ではKubernetes 1.14.8を例に説明します。
前述のプロセスマップでは、ボリュームが作成されてからアプリケーションで使用されるまでの3つの段階を示しています。Provision/Delete」、「Attach/Detach」、「Mount/Unmount」です。
ボリュームのプロビジョニング
PVコントローラーワーカー
- ClaimWorker は、PVC の Add、Update、Delete イベントと PVC のステータス変更を処理します。
- VolumeWorker は、PV のステータス変更を処理します。
PVステータスの変更 (UpdatePVStatus)
- PV は Available 状態で起動し、PVC にバインドされると Bound 状態に変化します。
- バインドされた PVC が削除されると PV は解放状態になります。
- PV のリクレームポリシーが Recycled になった場合や、PV の .Spec.ClaimRef が手動で削除された場合、PV は Available 状態に変わります。
- PV リクレイムポリシーが不明な場合、リサイクル操作が失敗した場合、またはボリュームが削除できない場合、PV は失敗状態に変わります。
- PV の .Spec.ClaimRef が手動で削除されると、PV は Available 状態に変わります。
PVC ステータスの変更 (UpdatePVCStatus)
- クラスタにPVCにマッチするPVが含まれていない場合、PVCはPending状態に変更されます。PVC は PV にバインドされると、Pending 状態から Bound 状態に変わります。
- バインドされた PV が環境から削除されると、PVC は Lost 状態に変わります。
- PVC は、前の PV と同じ名前の PV にバインドされた後、Bound 状態に変わります。
プロビジョニングプロセス(ユーザーがPVCを作成する場合を想定)
スタティックボリューム処理(FindBestMatch):PVコントローラーが環境内のAvailable状態のPVを選択し、新規PVCにマッチさせます。
-
DelayBinding:PV コントローラは、PVC バインディングを遅延させるかどうかを判断します。まず、PVコントローラーはPVCのアノテーションに
volume.kubernetes.io/selected-node
が含まれているかどうかをチェックします。はいの場合、PVCはスケジューラーによってノードにスケジューリングされます(PVCはProvisionVolumeに属しています)。この場合、バインディングは遅延しません。次に、PVCのアノテーションにvolume.kubernetes.io/selected-node
が含まれておらず、StorageClassが存在しない場合、バインディングは遅延しません。StorageClassが存在する場合、PV ControllerはVolumeBindingModeフィールドをチェックします。WaitForFirstConsumer に設定されている場合、バインディングは遅延します。Immediate に設定されている場合、バインディングは遅延しません。 -
FindBestMatchPVForClaim:PVコントローラは、PVCにマッチする環境のPVを見つけようとします。PV コントローラはすべての PV を横断し、候補となる PV の中から最適な PV を選択します。フィルタリングのルール 1) PV コントローラは、VolumeMode が一致するかどうかをチェックします。2) PV コントローラは、PV が PVC にバインドされているかどうかを確認します。3) PV コントローラは、PV の Status Phase が Available であるかどうかを確認します。4) PV コントローラは LabelSelector を使用して PV と PVC のラベルが同じであるかどうかを確認します。5)PV コントローラは、PV と PVC が同じ StorageClass であるかどうかを確認します。6) PV コントローラは、各反復において PVC の要求サイズを満たす最小の PV を更新し、それを最終結果として返します。
-
Bind:PV コントローラは、選択した PV を PVC にバインドします。1、 PV の .Spec.ClaimRef を現在の PVC に更新します。2、PV の .Status.Phase が Bound に更新されます。3、 PV にアノテーション
pv.kubernetes.io/bound-by-controller
: "yes" が追加されます。4、PVC の .Spec.VolumeName が PV の名前に更新されます。5、PVC の .Status.Phase が Bound に更新されます。6、 PVC にアノテーションpv.kubernetes.io/bound-by-controller: “yes"
とpv.kubernetes.io/bind-completed: "yes “
がPVCに追加されます。
ダイナミックボリュームプロセス(ProvisionVolume):環境に適切なPVが存在しない場合、ダイナミックプロビジョニングプロセスが開始されます。
- プロビジョニングの前に: 1)PVコントローラは、PVCが使用するStorageClassがツリー内かツリー外かを判断します。そのため、PV コントローラは、StorageClass の Provisioner フィールドに
kubernetes.io/
のプレフィックスが含まれているかどうかを確認します。2) PV コントローラは PVC のアノテーションを以下のように更新します。
claim.Annotations["volume.beta.kubernetes.io/storage-provisioner"] = storageClass.Provisioner.
- ツリー内プロビジョニング(内部プロビジョニング): 1) ツリー内プロビジョナーは、ProvisionableVolumePlugin インターフェースの NewProvisioner メソッドを実装し、新しいプロビジョナーを返します。2) PV コントローラは、プロビジョナーの Provision 関数を呼び出し、PV オブジェクトを返します。3) PV コントローラは返された PV オブジェクトを作成し、PVC にバインドします。Spec.ClaimRefはPVCに、.Status.PhaseはBoundに、.Spec.StorageClassNameはPVCのStorageClassの名前と同じStorageClassNameに設定されます。以下のアノテーションが追加されています。
"pv.kubernetes.io/bound-by-controller"="yes "と "pv.kubernetes.io/provisioned-by"=plugin.GetPluginName() “
を指定します。
- Out-of-tree Provisioning(外部プロビジョニング): 1)外部プロビジョナーは、PVC内のclaim.Spec.VolumeNameが空であるかどうかをチェックします。空でない場合、そのPVCはスキップされます。2) External Provisionerは、PVC内の
claim.Annotations["volume.beta.kubernetes.io/storage-provisioner”]
が、そのプロビジョナー名と同じかどうかをチェックします。External Provisionerは、起動時に--provisionerパラメータを渡してプロビジョナー名を決定します。3) PVCのVolumeModeがBlockに設定されている場合、External ProvisionerはそのPVCがブロックデバイスをサポートしているかどうかを確認します。4) External ProvisionerはProvision関数を呼び出し、gRPCを通じてCSI Storage Plug-inのCreateVolumeインター フェースを呼び出します。External Provisionerはボリュームを表すPVを作成し、そのPVをPVCにバインドします。
ボリュームの削除
ボリュームの削除は、プロビジョニングボリュームとは逆のプロセスです。
ユーザーが PVC を削除すると、PV コントローラは PV.Status.Phase
を Released に変更します。
PV.Status.Phase が Released に設定されると、PV コントローラは Spec.PersistentVolumeReclaimPolicy の値を確認します。PV.Status.Phase が Released になると、PV Controller は Spec.PersistentVolumeReclaimPolicy の値を確認します。Delete に設定されている場合は、以下のオプションのいずれかが実行されます。
- In-tree Deleting: 1)ツリー内プロビジョナーは、DeletableVolumePluginインターフェースのNewDeleterメソッドを実装して、新しい削除機能を返します。2) PVコントローラーが削除装置のDelete機能を呼び出し、対応するボリュームを削除します。3) ボリュームが削除されると、PV コントローラは PV オブジェクトを削除します。
- Out-of-tree Deleting: 1) External ProvisionerはDelete機能を呼び出し、CSIプラグインのDeleteVolumeインター フェースをgRPCにより呼び出します。2) ボリュームが削除された後、External ProvisionerがPVオブジェクトを削除します。
ボリュームのアタッチ
kubelet と AD Controller の両方で、Attach と Detach の操作が行われます。これらの操作は、キューブレットの起動パラメータに kubelet if --enable-controller-attach-detach が指定されている場合に実行されます。それ以外の場合は、AD Controller がこれらの操作を行います。以下では、AD Controller を例にとり、Attach および Detach の操作について説明します。
ADコントローラーの2つのコア変数
- DesiredStateOfWorld (DSW)は、ノード->ボリューム->ポッドの情報を含む、クラスタ内の予想されるボリュームのアタッチ状態を示します。
- ActualStateOfWorld (ASW)は、ボリューム->ノードの情報を含む、クラスターにおける実際のボリュームのアタッチ状態を示します。
アタッチのプロセス
AD コントローラーは、クラスター内のリソース情報に基づいて、DSW と ASW を初期化します。
ADコントローラーには、DSWとASWを定期的に更新する3つのコンポーネントがあります。
-
Reconciler componentは、GoRoutineを定期的に実行して、ボリュームのアタッチまたはデタッチを確認します。この間、ASWは継続的に更新されます。
-
In-tree Attaching: 1) In-tree Attachingは、新しいアタッカーを返すために AttachableVolumePlugin インターフェースの NewAttacher メソッドを実装します。2) AD コントローラは、アタッチャの Attach 関数を呼び出し、デバイスをアタッチします。3) ASW が更新されます。
-
Out-of-tree Attaching: 1) in-tree CSIAttacher が呼び出されて VolumeAttachement (VA) オブジェクトが作成されます。このオブジェクトにはアタッ チャー情報、ノード名、アタッチされる PV に関する情報が含まれます。2) 外部アタッカーは、クラスタ内の VolumeAttachement リソースを監視します。アタッチすべきデータボリュームがある場合、External AttacherはAttach関数を呼び出し、CSIプラグインのControllerPublishVolumeインターフェースをgRPC経由で呼び出します。
-
DesiredStateOfWorldPopulatorコンポーネントは、DSWを更新するために定期的にGoRoutineを実行します。
-
FindAndRemoveDeletedPodsは、DSW内のすべてのポッドを巡回する。クラスタから削除されたポッドは、DSWから削除されます。
-
FindAndAddActivePodsは、すべてのPodListersにあるポッドをトラバースします。DSWに存在しないポッドはすべてDSWに追加されます。
-
PVC Workerコンポーネントは、PVCのAddおよびUpdateイベントを監視し、PVC関連のポッドを処理し、DSWをリアルタイムに更新します。
ボリュームの切り離し
デタッチのプロセス
- ポッドが削除されると、ADコントローラはこのイベントを監視します。ポッドがあるノードに
volumes.kubernetes.io/keep-terminated-pod-volumes
ラベルが含まれているかどうかをチェックします。はいの場合は、操作は行われません。いいえの場合、ボリュームは DSW から削除されます。 - AD Controller は Reconciler を使用して ASW ステータスを DSW ステータスに移行します。DSW に存在しないボリュームが ASW に含まれている場合は、Detach オペレーションが実行されます。
a) In-tree Detaching: 1) AD コントローラーは、新しいディタッチャーを返すために、 AttachableVolumePlugin インターフェースの NewDetacher メソッドを実装します。2) AD コントローラは、デタッチャの Detach 関数を呼び出し、ボリュームに対する Detach 操作を実行します。3) AD コントローラは、ASW を更新します。
b) Out-of-tree Detaching: 1) AD コントローラは、インツリー CSIAttacher を呼び出し、関連する VolumeAttachement オブジェクトを削除します。2) 外部アタッカーは、クラスタ内の VolumeAttachement(VA)リソースを監視します。データボリュームを削除する必要がある場合、External AttacherはDetach関数を呼び出し、CSIプラグインのControllerUnpublishVolumeインターフェースをgRPC経由で呼び出します。3) ADコントローラーはASWを更新します。
ボリュームマネージャー
2つのコア変数を持っています。
- DesiredStateOfWorld (DSW)は、ボリューム->ポッドに関する情報を含む、クラスタ内の期待されるボリュームマウントステータスを示します。
- ActualStateOfWorld (ASW)は、ボリューム->ポッドの情報を含む、クラスター内の実際のボリュームマウント状態を示します。
マウントとアンマウントの処理は以下の通りです。
グローバルディレクトリ(グローバルマウントパス)は、Linuxシステムに一度だけマウントされるブロックデバイスです。Kubernetesでは、1つのPVが1つのノード上の複数のポッドインスタンスにマウントされることがあります。フォーマットされたブロックデバイスが、ノード上の一時的なグローバルディレクトリにマウントされます。その後,Linux のバインドマウント技術を用いて,グローバルディレクトリをポッドの対応するディレクトリにマウントします。先のプロセスマップでは,グローバルディレクトリは,/var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PVname]
となっています。
VolumeManagerは、クラスタ内のリソース情報に基づいてDSWとASWを初期化します。
VolumeManagerは、DSWとASWを定期的に更新する2つのコンポーネントを持っています。
- DesiredStateOfWorldPopulatorは、DSWを更新するためにGoRoutineを定期的に実行します。
- Reconcilerは定期的にGoRoutineを実行し、ボリュームのマウントまたはアンマウントを確認します。この間、ASWは継続的に更新されます。
UnmountVolumes は、ポッドが削除された後にボリュームがアンマウントされることを保証します。ASW内のすべてのポッドが横断され、いずれかのポッドがDSWにない場合(このポッドが削除されたことを示す)、以下の操作が実行されます(例としてVolumeMode=FileSystemが使用される)。
- UnmounterのTearDownインターフェイスを呼び出すか、アウトオブツリーモードのCSIプラグインのNodeUnpublishVolumeインターフェイスを呼び出すことにより、すべてのバインドマウントを削除します。
- DeviceUnmounterのUnmountDevice関数を呼び出すか、CSIプラグインのNodeUnstageVolumeインターフェイスをツリー外モードで呼び出すことにより、ボリュームをアンマウントします。
- ASWを更新します。
MountAttachVolumesは、ポッドが使用するボリュームが正常にマウントされていることを確認します。DSWのすべてのポッドが横断され、いずれかのポッドがASWにない場合(ディレクトリがマウントされ、ポッドにマッピングされる場合)、以下の操作が実行されます(例としてVolumeMode=FileSystemが使用されます)。
- ボリュームがExternal Attacherまたはkubeletによってノードにアタッチされるまで待ちます。
- DeviceMounterのMountDevice関数を呼び出すか、CSIプラグインのNodeStageVolumeインター フェースをアウトオブツリー・モードで呼び出すことにより、グローバル・ディレクトリにボ リュームをマウントします。
- ボリュームがグローバル・ディレクトリにマウントされている場合、ASWを更新します。
- MounterのSetUpインターフェイスを呼び出すか、CSIプラグインのNodePublishVolumeインターフェイスをツリー外モードで呼び出すことにより、bind-mountを通してボリュームをポッドにマウントします。
- ASWをアップデートします。
UnmountDetachDevicesは、ボリュームのアンマウントを確実に行います。ASWのすべてのUnmountedVolumesがトラバースされます。DSWにUnmountedVolumesが存在しない場合(これらのボリュームがもはや使用されていないことを示す)、以下の操作が実行されます。
- DeviceUnmounterのUnmountDevice関数を呼び出すか、アウトオブツリーモードのCSIプラグインのNodeUnstageVolumeインターフェースを呼び出すことにより、ボリュームをアンマウントします。
- ASWを更新します。
#概要
本記事では、Kubernetesの永続的ストレージの基本と使い方を紹介し、Kubernetesの内部ストレージプロセスを分析しました。Kubernetesでは、すべてのストレージタイプで先の処理が必要ですが、AttachとDetachの操作は特定のシナリオでは実行されません。環境におけるストレージの問題は、これらのプロセスのいずれかに障害があることに起因します。
コンテナストレージは、特にプライベートクラウド環境では複雑です。しかし、このプロセスを通じて、より多くの課題に挑戦しながら、より多くのチャンスをつかむことが可能になります。現在、中国のプライベートクラウド市場のストレージ事情は、競争が激化しています。私たちPaaSコンテナチームは、より良い未来を築くために、才能あるプロフェッショナルの参加を常に求めています。
#参考資料
- Kubernetesコミュニティのソースコード
- KUBERNETES-DESIGN-PROPOSITIONS ボリュームプロビジョニング
- kubernetes-design-proposals Kubernetes Design DocにおけるCSIボリュームプラグイン
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ