Kubernetesに触れる: データの永続化についてとKafkaクラスターのデプロイ
之前:
-
- KubernetesからみたDocker
-
- Kubernetesに触れる
- Kubernetesに触れる: Kubernetes内のネットワークとDNS
今回はKubernetes上のデータの永続化(Volume, PersistentVolumeClaim)について説明したあと、KafkaとZookeeperクラスターをKubernetes上にデプロイするManifestファイルを眺めながら、そこで出てくるリソースについて軽く説明する。
关于数据持久化
Kubernetes不适合的情况
いわゆる状態を持つことをKubernetesでもそのままstatefulという言葉で表現する。Kubernetesにおける”stateful”の明確な定義を私はまだ述べられないけれど、少なくともDBやKafkaのような、永続化されたデータを扱うタイプのPodのことをstatefulと呼んでいる。これはNginxがstatelessと呼ばれることと対比して考えるとわかりやすい。
Cloud Native DevOps with Kubernetesという書籍では、はっきりと「データベース(のようなstatefulなPod)をKubernetesで動かすことはお勧めしない」と記載されている。似たようなことを言う人はWeb上にも沢山見受けられる。例えば -> https://www.quora.com/Is-it-a-good-way-to-run-Kafka-on-Kubernetes
勘違いのないように述べておくと、Kubernetesでもデータの永続化をしたり、それを操作するDBをデプロイすることはできるし、さらに完全にプロダクションレベルでそれを動かすことはできる。
作为不推荐的理由之一,是因为Manifest非常技巧性并且变得复杂。引入Kubernetes的初衷之一应该是为了减少对基础架构的关注,集中精力在核心工作上。考虑到这一点,也许无法找到在Kubernetes上运行数据库需要付出巨大努力的价值。相比之下,直接在主机上运行可能更容易管理和处理故障。
Kubernetes上のアプリケーションでデータベースを使う際の代替案として挙げられているのが、クラウドプロバイダーが提供するPaaSを利用することである。
尽管有这样的背景,我们现在要看一下如何在Kubernetes上持久化数据的方法。
体积
Dockerのときと同じように、Podでもホスト上のファイルやディレクトリをマウントできる。例えば以下のようにManifestファイルを記述すればよい。
apiVersion: v1
kind: Pod
metadata:
name: cent-hostpath
labels:
app: myapp
spec:
containers:
- name: myapp-container-1
image: centos:7
command: ['sleep', '3600']
volumeMounts:
- mountPath: "/tmp"
name: sample-hostpath
volumes:
- name: sample-hostpath
hostPath:
path: /tmp
type: DirectoryOrCreate
これはCentOS7のcotainerをsleep 3600コマンドを実行しているだけの無意味なものである。spec.volumeMountsのフィールドにあるところで、Container内の/tmpのディレクトリをsample-hostpathという名前のVolumeで定義されたところにマウントすることを指定している。そのあとのspec.volumesでsample-hostpathという名前で指定されたホストの領域を指定している。
尽管可以利用主机域,但这是明显的反模式,所以不会继续深入探讨。
或许以下的emptyDir可能还有其它的用途。
apiVersion: v1
kind: Pod
metadata:
name: cent
labels:
app: myapp
spec:
containers:
- name: myapp-container-1
image: centos:7
command: ['sleep', '3600']
volumeMounts:
- mountPath: "/tmp"
name: sample-emptydir
- name: myapp-container-2
image: centos:7
command: ['sleep', '3600']
volumeMounts:
- mountPath: "/tmp"
name: sample-emptydir
volumes:
- name: sample-emptydir
emptyDir: {}
spec.volumesの中にemptyDirが定義された。これはPod内に作成される疑似的な”ホスト上のディレクトリ”で、Podが削除されたらこの領域も削除される。従ってデータの永続化はできない。とはいえ、上のようにPodの中に複数のContainerがある場合には、この領域を互いのContainerで共有できることにある。例えばPodの中でPub/Subを実現したい場合に、Pub用のContainerがこの領域に何かを書き込んで、Sub用のContainerがこの領域内のファイルをtailするような構成にすればそれができる。こうした使い方は有意義かもしれない。
SC, PV, PVC 可以被汉化为”SC,PV,PVC”。
首先,在使用Kubernetes进行数据持久化时,假设数据存储位置在Kubernetes集群的外部。Pod会通过网络访问该存储位置。
データを永続化したい場合は、StorageClass(SC), PersistentVolumeClaim(PVC)と呼ばれる概念を知る必要がある。(今回はDynamic Provisioningを扱う。PersistentVolumeを手動で定義する方法は扱わないので、この2つの概念しか説明しない)
存储类
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: managed-premium-retain
provisioner: kubernetes.io/azure-disk
reclaimPolicy: Retain
parameters:
storageaccounttype: Premium_LRS
kind: Managed
-
- クラウドプロバイダーが提供するディスク(例えばAzureであればAzure File)をKubernetesで扱うための設定。
-
- この例ではmanaged-premium-retainというStorageClassリソース名で「AzureのPremium_LRSというタイプのディスク(SSDのやつ)を」を宣言している。
- クラウドプロバイダー別にパラメータが変わる。Kubernetes公式ドキュメントには利用できるprovisionerが一覧となっている => https://kubernetes.io/docs/concepts/storage/storage-classes/
持久存储卷索求
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: azure-managed-disk
spec:
accessModes:
- ReadWriteOnce
storageClassName: managed-premium-retain
resources:
requests:
storage: 5Gi
-
- 使用するディスクのサイズや、アクセスモード(リードオンリーや色々なPodから書き込みもできるかどうか)といったことを定義するもの。
- この例ではazure-managed-diskというPersistentVolumeClaimリソース名で「managed-premium-retainというディスクを使って、一つのPodからしかアクセスできず(ReadWriteOnce)、サイズは5Gi使う」ということを宣言している。
要真正地使用这个,
只需将上述定义的persistentVolumeClaim指定在spec.volumes中的Pod定义中。
kind: Pod
apiVersion: v1
metadata:
name: mypod
spec:
containers:
- name: mypod
image: nginx:1.15.5
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
volumeMounts:
- mountPath: "/mnt/azure"
name: volume
volumes:
- name: volume
persistentVolumeClaim:
claimName: azure-managed-disk
如果总结以上内容的话
PodはPersistentVolumeClaimを指定する。
PersistentVolumeClaimはStorageClassを指定する。
这意味着从Pod无法知道使用了哪个实际的磁盘。这里实现了Pod与实际磁盘的松耦合关系。
またStatefulSetならば、Podの定義と同じファイルにPersistentVolumeClaimを記述できる。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: cent
spec:
selector:
matchLabels:
app: cent
replicas: 3
template:
metadata:
labels:
app: cent
spec:
containers:
- name: cent
image: centos:7
command: ['sleep', '3600']
volumeMounts:
- name: instatefulset
mountPath: /tmp
volumeClaimTemplates:
- metadata:
name: instatefulset
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "managed-premium-retain"
resources:
requests:
storage: 1Gi
部署Kafka和Zookeeper集群。
上記を踏まえてKafkaとZookeeperのクラスターをデプロイするyamlを眺める。
动物园管理员:https://github.com/kubernetes/contrib/blob/6530ce7c043610a18bdcfbfccda0984d3b068659/statefulsets/zookeeper/zookeeper.yaml
Kafka:https://github.com/kubernetes/contrib/blob/6a70f578cb71d04992ecc0543aef291b30614d00/statefulsets/kafka/kafka.yaml 改写成中文的版本:Kafka:https://github.com/kubernetes/contrib/blob/6a70f578cb71d04992ecc0543aef291b30614d00/statefulsets/kafka/kafka.yaml
这是一个相当庞大的宣言。以下内容得到关注:
-
- ClusterIPをつけない、HeadlessなServiceを作る。ホストに対して直接名前でアクセスできるように。前回のCNAME云々の話を参照
-
- StatefulSetとしてPodをデプロイしている。
-
- 例えばNodeのメンテナンスなどであるNodeからZookeeperを退避させないときに、動いているZookeeperの数を少なくとも2つは保っておくことを定義する -> PodDisruptionbudget
同じNodeにZookeeperが立ってはならない -> podAntiAffinity
Podが正常に動いているかの確認 -> readinessProbeとlivvenessProbe
zookeeperのmyidやkafkaのbeokerIdを、Podのhostnameから動的につけられるようにしている。
データの永続化