k8s-tutorials-stateful-application (#5112)

* k8s-tutorials-stateful-application

* change author and email to pass CLA
This commit is contained in:
打雷不怕
2017-09-03 07:04:43 +08:00
committed by Andrew Chen
parent cc8d5283b4
commit 593dc8cca1
15 changed files with 3981 additions and 0 deletions
@@ -0,0 +1,17 @@
# This is an image with Percona XtraBackup, mysql-client and ncat installed.
FROM debian:jessie
RUN \
echo "deb http://repo.percona.com/apt jessie main" > /etc/apt/sources.list.d/percona.list \
&& echo "deb-src http://repo.percona.com/apt jessie main" >> /etc/apt/sources.list.d/percona.list \
&& apt-key adv --keyserver keys.gnupg.net --recv-keys 8507EFA5
RUN \
apt-get update && apt-get install -y --no-install-recommends \
percona-xtrabackup-24 \
mysql-client \
nmap \
&& rm -rf /var/lib/apt/lists/*
CMD ["bash"]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: cassandra
name: cassandra
spec:
clusterIP: None
ports:
- port: 9042
selector:
app: cassandra
@@ -0,0 +1,94 @@
apiVersion: "apps/v1beta1"
kind: StatefulSet
metadata:
name: cassandra
spec:
serviceName: cassandra
replicas: 3
template:
metadata:
labels:
app: cassandra
spec:
containers:
- name: cassandra
image: gcr.io/google-samples/cassandra:v12
imagePullPolicy: Always
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
resources:
limits:
cpu: "500m"
memory: 1Gi
requests:
cpu: "500m"
memory: 1Gi
securityContext:
capabilities:
add:
- IPC_LOCK
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "PID=$(pidof java) && kill $PID && while ps -p $PID > /dev/null; do sleep 1; done"]
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEEDS
value: "cassandra-0.cassandra.default.svc.cluster.local"
- name: CASSANDRA_CLUSTER_NAME
value: "K8Demo"
- name: CASSANDRA_DC
value: "DC1-K8Demo"
- name: CASSANDRA_RACK
value: "Rack1-K8Demo"
- name: CASSANDRA_AUTO_BOOTSTRAP
value: "false"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /ready-probe.sh
initialDelaySeconds: 15
timeoutSeconds: 5
# These volume mounts are persistent. They are like inline claims,
# but not exactly because the names need to match exactly one of
# the stateful pod volumes.
volumeMounts:
- name: cassandra-data
mountPath: /cassandra_data
# These are converted to volume claims by the controller
# and mounted at the paths mentioned above.
# do not use these in production until ssd GCEPersistentDisk or other ssd pd
volumeClaimTemplates:
- metadata:
name: cassandra-data
annotations:
volume.beta.kubernetes.io/storage-class: fast
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
type: pd-ssd
@@ -0,0 +1,851 @@
---
title: "Example: Deploying Cassandra with Stateful Sets"
title: “示例:使用 Stateful Sets 部署 Cassandra”
---
## 目录
- [准备工作](#prerequisites)
- [Cassandra docker 镜像](#cassandra-docker)
- [快速入门](#quickstart)
- [步骤1:创建 Cassandra Headless Service](#step-1-create-a-cassandra-headless-service)
- [步骤2:使用 StatefulSet 创建 Cassandra Ring环](#step-2-use-a-statefulset-to-create-cassandra-ring)
- [步骤3:验证并修改 Cassandra StatefulSet](#step-3-validate-and-modify-the-cassandra-statefulset)
- [步骤4:删除 Cassandra StatefulSet](#step-4-delete-cassandra-statefulset)
- [步骤5:使用 Replication Controller 创建 Cassandra 节点 pods](#step-5-use-a-replication-controller-to-create-cassandra-node-pods)
- [步骤6Cassandra 集群扩容](#step-6-scale-up-the-cassandra-cluster)
- [步骤7:删除 Replication Controller](#step-7-delete-the-replication-controller)
- [步骤8:使用 DaemonSet 替换 Replication Controller](#step-8-use-a-daemonset-instead-of-a-replication-controller)
- [步骤9:资源清理](#step-9-resource-cleanup)
- [Seed Provider Source](#seed-provider-source)
下文描述了在 Kubernetes 上部署一个_云原生_ [Cassandra](http://cassandra.apache.org/) 的过程。当我们说_云原生_时,指的是一个应用能够理解它运行在一个集群管理器内部,并且使用这个集群的管理基础设施来帮助实现这个应用。特别的,本例使用了一个自定义的 Cassandra `SeedProvider` 帮助 Cassandra 发现新加入集群 Cassandra 节点。
本示例也使用了Kubernetes的一些核心组件:
- [_Pods_](/docs/user-guide/pods)
- [ _Services_](/docs/user-guide/services)
- [_Replication Controllers_](/docs/user-guide/replication-controller)
- [_Stateful Sets_](/docs/concepts/workloads/controllers/statefulset/)
- [_Daemon Sets_](/docs/admin/daemons)
## 准备工作
本示例假设你已经安装运行了一个 Kubernetes集群(版本 >=1.2),并且还在某个路径下安装了 [`kubectl`](https://kubernetes.io/docs/tasks/tools/install-kubectl/) 命令行工具。请查看 [getting started guides](https://kubernetes.io/docs/getting-started-guides/) 获取关于你的平台的安装说明。
本示例还需要一些代码和配置文件。为了避免手动输入,你可以 `git clone` Kubernetes 源到你本地。
## Cassandra Docker镜像
Pods 使用来自 Google 的 [container registry](https://cloud.google.com/container-registry/docs/) 的 [```gcr.io/google-samples/cassandra:v12```](https://github.com/kubernetes/examples/blob/master/cassandra/image/Dockerfile) 镜像。这个 docker 镜像基于 `debian:jessie` 并包含 OpenJDK 8。该镜像包含一个从 Apache Debian 源中安装的标准 Cassandra。你可以通过使用环境变量改变插入到 `cassandra.yaml` 文件中的参数值。
| ENV VAR | DEFAULT VALUE |
| ---------------------- | :------------: |
| CASSANDRA_CLUSTER_NAME | 'Test Cluster' |
| CASSANDRA_NUM_TOKENS | 32 |
| CASSANDRA_RPC_ADDRESS | 0.0.0.0 |
## 快速入门
如果你希望直接跳到我们使用的命令,以下是全部步骤:
```sh
#
# StatefulSet
#
# clone the example repository
git clone https://github.com/kubernetes/examples
cd examples
# create a service to track all cassandra statefulset nodes
kubectl create -f cassandra/cassandra-service.yaml
# create a statefulset
kubectl create -f cassandra/cassandra-statefulset.yaml
# validate the Cassandra cluster. Substitute the name of one of your pods.
kubectl exec -ti cassandra-0 -- nodetool status
# cleanup
grace=$(kubectl get po cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \
&& kubectl delete statefulset,po -l app=cassandra \
&& echo "Sleeping $grace" \
&& sleep $grace \
&& kubectl delete pvc -l app=cassandra
#
# Resource Controller Example
#
# create a replication controller to replicate cassandra nodes
kubectl create -f cassandra/cassandra-controller.yaml
# validate the Cassandra cluster. Substitute the name of one of your pods.
kubectl exec -ti cassandra-xxxxx -- nodetool status
# scale up the Cassandra cluster
kubectl scale rc cassandra --replicas=4
# delete the replication controller
kubectl delete rc cassandra
#
# Create a DaemonSet to place a cassandra node on each kubernetes node
#
kubectl create -f cassandra/cassandra-daemonset.yaml --validate=false
# resource cleanup
kubectl delete service -l app=cassandra
kubectl delete daemonset cassandra
```
## 步骤1:创建 Cassandra Headless Service
Kubernetes _[Service](/docs/user-guide/services)_ 描述一组执行同样任务的 [_Pods_](/docs/user-guide/pods)。在Kubernetes中,一个应用的原子调度单位是一个 Pod:一个或多个_必须_调度到相同主机上的容器。
这个 Service 用于在Kubernetes 集群内部进行 Cassandra 客户端和 Cassandra Pods之间的 DNS 查找。
以下为这个 service 的描述:
```yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: cassandra
name: cassandra
spec:
clusterIP: None
ports:
- port: 9042
selector:
app: cassandra
```
[下载示例](https://raw.githubusercontent.com/kubernetes/examples/master/cassandra-service.yaml)
为 StatefulSet 创建 service
```console
$ kubectl create -f cassandra/cassandra-service.yaml
```
以下命令显示了 service 是否被成功创建。
```console
$ kubectl get svc cassandra
```
命令的响应应该像这样:
```console
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cassandra None <none> 9042/TCP 45s
```
如果返回错误则表示 service 创建失败。
## 步骤2:使用 StatefulSet 创建 Cassandra Ring环
StatefulSets(以前叫做 PetSets)特性在 Kubernetes 1.5 中升级为一个 <i>Beta</i> 组件。在集群环境中部署类似于 Cassandra 的有状态分布式应用是一项具有挑战性的工作。我们实现了StatefulSet,极大的简化了这个过程。本示例使用了 StatefulSet 的多个特性,但其本身超出了本文的范围。[请参考 Stateful Set 文档。](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/)
以下是StatefulSet 的清单文件,用于创建一个由三个 pods 组成的 Cassandra ring环。
本示例使用了 GCE Storage Class,请根据你运行的云平台做适当的修改。
```yaml
apiVersion: "apps/v1beta1"
kind: StatefulSet
metadata:
name: cassandra
spec:
serviceName: cassandra
replicas: 3
template:
metadata:
labels:
app: cassandra
spec:
containers:
- name: cassandra
image: gcr.io/google-samples/cassandra:v12
imagePullPolicy: Always
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
resources:
limits:
cpu: "500m"
memory: 1Gi
requests:
cpu: "500m"
memory: 1Gi
securityContext:
capabilities:
add:
- IPC_LOCK
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "PID=$(pidof java) && kill $PID && while ps -p $PID > /dev/null; do sleep 1; done"]
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEEDS
value: "cassandra-0.cassandra.default.svc.cluster.local"
- name: CASSANDRA_CLUSTER_NAME
value: "K8Demo"
- name: CASSANDRA_DC
value: "DC1-K8Demo"
- name: CASSANDRA_RACK
value: "Rack1-K8Demo"
- name: CASSANDRA_AUTO_BOOTSTRAP
value: "false"
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
readinessProbe:
exec:
command:
- /bin/bash
- -c
- /ready-probe.sh
initialDelaySeconds: 15
timeoutSeconds: 5
# These volume mounts are persistent. They are like inline claims,
# but not exactly because the names need to match exactly one of
# the stateful pod volumes.
volumeMounts:
- name: cassandra-data
mountPath: /cassandra_data
# These are converted to volume claims by the controller
# and mounted at the paths mentioned above.
# do not use these in production until ssd GCEPersistentDisk or other ssd pd
volumeClaimTemplates:
- metadata:
name: cassandra-data
annotations:
volume.beta.kubernetes.io/storage-class: fast
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: fast
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
```
[下载示例](https://raw.githubusercontent.com/kubernetes/examples/master/cassandra-statefulset.yaml)
创建 Cassandra StatefulSet 如下:
```console
$ kubectl create -f cassandra/cassandra-statefulset.yaml
```
## 步骤3:验证和修改 Cassandra StatefulSet
这个 StatefulSet 的部署展示了 StatefulSets 提供的两个新特性:
1. Pod 的名称已知
2. Pod 以递增顺序部署
首先,运行下面的 `kubectl` 命令,验证 StatefulSet 已经被成功部署。
```console
$ kubectl get statefulset cassandra
```
这个命令的响应应该像这样:
```console
NAME DESIRED CURRENT AGE
cassandra 3 3 13s
```
接下来观察 Cassandra pods 以一个接一个的形式部署。StatefulSet 资源按照数字序号的模式部署 pods:1, 2, 3 等。如果在 pods 部署前执行下面的命令,你就能够看到这种顺序的创建过程。
```console
$ kubectl get pods -l="app=cassandra"
NAME READY STATUS RESTARTS AGE
cassandra-0 1/1 Running 0 1m
cassandra-1 0/1 ContainerCreating 0 8s
```
上面的示例显示了三个 Cassandra StatefulSet pods 中的两个已经部署。一旦所有的 pods 都部署成功,相同的命令会显示一个完整的StatefulSet。
```console
$ kubectl get pods -l="app=cassandra"
NAME READY STATUS RESTARTS AGE
cassandra-0 1/1 Running 0 10m
cassandra-1 1/1 Running 0 9m
cassandra-2 1/1 Running 0 8m
```
运行 Cassandra 工具 `nodetool` 将显示 ring环的状态。
```console
$ kubectl exec cassandra-0 -- nodetool status
Datacenter: DC1-K8Demo
======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 10.4.2.4 65.26 KiB 32 63.7% a9d27f81-6783-461d-8583-87de2589133e Rack1-K8Demo
UN 10.4.0.4 102.04 KiB 32 66.7% 5559a58c-8b03-47ad-bc32-c621708dc2e4 Rack1-K8Demo
UN 10.4.1.4 83.06 KiB 32 69.6% 9dce943c-581d-4c0e-9543-f519969cc805 Rack1-K8Demo
```
你也可以运行 `cqlsh` 来显示集群的 keyspaces。
```console
$ kubectl exec cassandra-0 -- cqlsh -e 'desc keyspaces'
system_traces system_schema system_auth system system_distributed
```
你需要使用 `kubectl edit` 来增加或减小 Cassandra StatefulSet 的大小。你可以在 [文档](/docs/user-guide/kubectl/kubectl_edit) 中找到更多关于 edit 命令的信息。
使用以下命令编辑 StatefulSet。
```console
$ kubectl edit statefulset cassandra
```
这会在你的命令行中创建一个编辑器。你需要修改的行是 `replicas`。这个例子没有包含终端窗口的所有内容,下面示例中的最后一行就是你希望改变的 replicas 行。
```console
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
creationTimestamp: 2016-08-13T18:40:58Z
generation: 1
labels:
app: cassandra
name: cassandra
namespace: default
resourceVersion: "323"
selfLink: /apis/apps/v1beta1/namespaces/default/statefulsets/cassandra
uid: 7a219483-6185-11e6-a910-42010a8a0fc0
spec:
replicas: 3
```
按下面的示例修改清单文件并保存。
```console
spec:
replicas: 4
```
这个 StatefulSet 现在将包含四个 pods。
```console
$ kubectl get statefulset cassandra
```
这个command的响应应该像这样:
```console
NAME DESIRED CURRENT AGE
cassandra 4 4 36m
```
对于 Kubernetes 1.5 发布版,beta StatefulSet 资源没有像 Deployment, ReplicaSet, Replication Controller或者 Job一样,包含 `kubectl scale` 功能,
## 步骤4:删除 Cassandra StatefulSet
删除或者缩容 StatefulSet 时不会删除与之关联的 volumes。这样做是为了优先保证安全。你的数据比其它会被自动清除的 StatefulSet 关联资源更宝贵。删除 Persistent Volume Claims 可能会导致关联的 volumes 被删除,这种行为依赖 storage class 和 reclaim policy。永远不要期望能在 claim 删除后访问一个 volume。
使用如下命令删除 StatefulSet。
```console
$ grace=$(kubectl get po cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \
&& kubectl delete statefulset -l app=cassandra \
&& echo "Sleeping $grace" \
&& sleep $grace \
&& kubectl delete pvc -l app=cassandra
```
## 步骤5:使用 Replication Controller 创建 Cassandra 节点 pods
Kubernetes _[Replication Controller](/docs/user-guide/replication-controller)_ 负责复制一个完全相同的 pods 集合。像 Service 一样,它具有一个 selector query,用来识别它的集合成员。和 Service 不一样的是,它还具有一个期望的副本数,并且会通过创建或删除 Pods来保证 Pods 的数量满足它期望的状态。
和我们刚才定义的 Service 一起,Replication Controller 能够让我们轻松的构建一个复制的、可扩展的 Cassandra 集群。
让我们创建一个具有两个初始副本的 replication controller。
```yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: cassandra
# The labels will be applied automatically
# from the labels in the pod template, if not set
# labels:
# app: cassandra
spec:
replicas: 2
# The selector will be applied automatically
# from the labels in the pod template, if not set.
# selector:
# app: cassandra
template:
metadata:
labels:
app: cassandra
spec:
containers:
- command:
- /run.sh
resources:
limits:
cpu: 0.5
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEED_PROVIDER
value: "io.k8s.cassandra.KubernetesSeedProvider"
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
image: gcr.io/google-samples/cassandra:v12
name: cassandra
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
volumeMounts:
- mountPath: /cassandra_data
name: data
volumes:
- name: data
emptyDir: {}
```
[下载示例](https://raw.githubusercontent.com/kubernetes/examples/master/cassandra-controller.yaml)
在这个描述中需要注意几件事情。
`selector` 属性包含了控制器的 selector query。它能够被显式指定,或者在没有设置时,像此处一样从 pod 模板中的 labels 中自动应用。
Pod 模板的标签 `app:cassandra` 匹配步骤1中的 Service selector。这就是 Service 如何选择 replication controller 创建的 pods 的原理。
`replicas` 属性指明了期望的副本数量,在本例中最开始为 2。我们很快将要扩容更多数量。
创建 Replication Controller
```console
$ kubectl create -f cassandra/cassandra-controller.yaml
```
你可以列出新建的 controller
```console
$ kubectl get rc -o wide
NAME DESIRED CURRENT AGE CONTAINER(S) IMAGE(S) SELECTOR
cassandra 2 2 11s cassandra gcr.io/google-samples/cassandra:v12 app=cassandra
```
现在,如果你列出集群中的 pods,并且使用 `app=cassandra` 标签过滤,你应该能够看到两个 Cassandra pods。(`wide` 参数使你能够看到 pods 被调度到了哪个 Kubernetes 节点上)
```console
$ kubectl get pods -l="app=cassandra" -o wide
NAME READY STATUS RESTARTS AGE NODE
cassandra-21qyy 1/1 Running 0 1m kubernetes-minion-b286
cassandra-q6sz7 1/1 Running 0 1m kubernetes-minion-9ye5
```
因为这些 pods 拥有 `app=cassandra` 标签,它们被映射给了我们在步骤1中创建的 service。
你可以使用下面的 service endpoint 查询命令来检查 Pods 是否对 Service 可用。
```console
$ kubectl get endpoints cassandra -o yaml
apiVersion: v1
kind: Endpoints
metadata:
creationTimestamp: 2015-06-21T22:34:12Z
labels:
app: cassandra
name: cassandra
namespace: default
resourceVersion: "944373"
selfLink: /api/v1/namespaces/default/endpoints/cassandra
uid: a3d6c25f-1865-11e5-a34e-42010af01bcc
subsets:
- addresses:
- ip: 10.244.3.15
targetRef:
kind: Pod
name: cassandra
namespace: default
resourceVersion: "944372"
uid: 9ef9895d-1865-11e5-a34e-42010af01bcc
ports:
- port: 9042
protocol: TCP
```
为了显示 `SeedProvider` 逻辑是按设想在运行,你可以使用 `nodetool` 命令来检查 Cassandra 集群的状态。为此,请使用 `kubectl exec` 命令,这样你就能在一个 Cassandra pod 上运行 `nodetool`。同样的,请替换 `cassandra-xxxxx` 为任意一个 pods的真实名字。
```console
$ kubectl exec -ti cassandra-xxxxx -- nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 10.244.0.5 74.09 KB 256 100.0% 86feda0f-f070-4a5b-bda1-2eeb0ad08b77 rack1
UN 10.244.3.3 51.28 KB 256 100.0% dafe3154-1d67-42e1-ac1d-78e7e80dce2b rack1
```
## 步骤6Cassandra集群扩容
现在,让我们把 Cassandra 集群扩展到4个 pods。我们通过告诉 Replication Controller 现在我们需要4个副本来完成。
```sh
$ kubectl scale rc cassandra --replicas=4
```
你可以看到列出了新的 pods
```console
$ kubectl get pods -l="app=cassandra" -o wide
NAME READY STATUS RESTARTS AGE NODE
cassandra-21qyy 1/1 Running 0 6m kubernetes-minion-b286
cassandra-81m2l 1/1 Running 0 47s kubernetes-minion-b286
cassandra-8qoyp 1/1 Running 0 47s kubernetes-minion-9ye5
cassandra-q6sz7 1/1 Running 0 6m kubernetes-minion-9ye5
```
一会儿你就能再次检查 Cassandra 集群的状态,你可以看到新的 pods 已经被自定义的 `SeedProvider` 检测到:
```console
$ kubectl exec -ti cassandra-xxxxx -- nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 10.244.0.6 51.67 KB 256 48.9% d07b23a5-56a1-4b0b-952d-68ab95869163 rack1
UN 10.244.1.5 84.71 KB 256 50.7% e060df1f-faa2-470c-923d-ca049b0f3f38 rack1
UN 10.244.1.6 84.71 KB 256 47.0% 83ca1580-4f3c-4ec5-9b38-75036b7a297f rack1
UN 10.244.0.5 68.2 KB 256 53.4% 72ca27e2-c72c-402a-9313-1e4b61c2f839 rack1
```
## 步骤7:删除 Replication Controller
在你开始步骤5之前, __删除__你在上面创建的 __replication controller__
```sh
$ kubectl delete rc cassandra
```
## 步骤8:使用 DaemonSet 替换 Replication Controller
在 Kubernetes中,[_Daemon Set_](/docs/admin/daemons) 能够将 pods 一对一的分布到 Kubernetes 节点上。和 _ReplicationController_ 相同的是它也有一个用于识别它的集合成员的 selector query。但和 _ReplicationController_ 不同的是,它拥有一个节点 selector,用于限制基于模板的 pods 可以调度的节点。并且 pod 的复制不是基于一个设置的数量,而是为每一个节点分配一个 pod。
示范用例:当部署到云平台时,预期情况是实例是短暂的并且随时可能终止。Cassandra 被搭建成为在各个节点间复制数据以便于实现数据冗余。这样的话,即使一个实例终止了,存储在它上面的数据却没有,并且集群会通过重新复制数据到其它运行节点来作为响应。
`DaemonSet` 设计为在 Kubernetes 集群中的每个节点上放置一个 pod。那样就会给我们带来数据冗余度。让我们创建一个 DaemonSet 来启动我们的存储集群:
```yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
labels:
name: cassandra
name: cassandra
spec:
template:
metadata:
labels:
app: cassandra
spec:
# Filter to specific nodes:
# nodeSelector:
# app: cassandra
containers:
- command:
- /run.sh
env:
- name: MAX_HEAP_SIZE
value: 512M
- name: HEAP_NEWSIZE
value: 100M
- name: CASSANDRA_SEED_PROVIDER
value: "io.k8s.cassandra.KubernetesSeedProvider"
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
image: gcr.io/google-samples/cassandra:v12
name: cassandra
ports:
- containerPort: 7000
name: intra-node
- containerPort: 7001
name: tls-intra-node
- containerPort: 7199
name: jmx
- containerPort: 9042
name: cql
# If you need it it is going away in C* 4.0
#- containerPort: 9160
# name: thrift
resources:
requests:
cpu: 0.5
volumeMounts:
- mountPath: /cassandra_data
name: data
volumes:
- name: data
emptyDir: {}
```
[下载示例](https://raw.githubusercontent.com/kubernetes/examples/master/cassandra-daemonset.yaml)
这个 DaemonSet 绝大部分的定义和上面的 ReplicationController 完全相同;它只是简单的给 daemon set 一个创建新的 Cassandra pods 的方法,并且以集群中所有的 Cassandra 节点为目标。
不同之处在于 `nodeSelector` 属性,它允许 DaemonSet 以全部节点的一个子集为目标(你可以向其他资源一样标记节点),并且没有 `replicas` 属性,因为它使用1对1的 node-pod 关系。
创建这个 DaemonSet
```console
$ kubectl create -f cassandra/cassandra-daemonset.yaml
```
你可能需要禁用配置文件检查,像这样:
```console
$ kubectl create -f cassandra/cassandra-daemonset.yaml --validate=false
```
你可以看到 DaemonSet 已经在运行:
```console
$ kubectl get daemonset
NAME DESIRED CURRENT NODE-SELECTOR
cassandra 3 3 <none>
```
现在,如果你列出集群中的 pods,并且使用 `app=cassandra` 标签过滤,你应该能够看到你的网络中的每一个节点上都有一个(且只有一个)新的 cassandra pod。
```console
$ kubectl get pods -l="app=cassandra" -o wide
NAME READY STATUS RESTARTS AGE NODE
cassandra-ico4r 1/1 Running 0 4s kubernetes-minion-rpo1
cassandra-kitfh 1/1 Running 0 1s kubernetes-minion-9ye5
cassandra-tzw89 1/1 Running 0 2s kubernetes-minion-b286
```
为了证明这是按设想的在工作,你可以再次使用 `nodetool` 命令来检查集群的状态。为此,请使用 `kubectl exec` 命令在任何一个新建的 cassandra pods 上运行 `nodetool`
```console
$ kubectl exec -ti cassandra-xxxxx -- nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 10.244.0.5 74.09 KB 256 100.0% 86feda0f-f070-4a5b-bda1-2eeb0ad08b77 rack1
UN 10.244.4.2 32.45 KB 256 100.0% 0b1be71a-6ffb-4895-ac3e-b9791299c141 rack1
UN 10.244.3.3 51.28 KB 256 100.0% dafe3154-1d67-42e1-ac1d-78e7e80dce2b rack1
```
**注意**:这个示例让你在创建 DaemonSet 前删除了 cassandra的Replication Controller。这是因为为了保持示例的简单,RC 和 DaemonSet 使用了相同的 `app=cassandra` 标签(如此它们的 pods 映射到了我们创建的 service,这样 SeedProvider 就能识别它们)。
如果我们没有预先删除 RC,这两个资源在需要运行多少 pods 上将会发生冲突。如果希望的话,我们可以使用额外的标签和 selectors 来支持同时运行它们。
## 步骤9:资源清理
当你准备删除你的资源时,按以下执行:
```console
$ kubectl delete service -l app=cassandra
$ kubectl delete daemonset cassandra
```
### 自定义 Seed Provider
我们使用了一个自定义的 [`SeedProvider`](https://svn.apache.org/repos/asf/cassandra/trunk/src/java/org/apache/cassandra/locator/SeedProvider.java) 来在 Kubernetes 之上运行 Cassandra。仅当你通过 replication control 或者 daemonset 部署 Cassandra 时才需要使用自定义的 seed provider。在 Cassandra 中,`SeedProvider` 引导 Cassandra 使用 gossip 协议来查找其它 Cassandra 节点。Seed 地址是被视为连接端点的主机。Cassandra 实例使用 seed 列表来查找彼此并学习 ring环拓扑。[`KubernetesSeedProvider`](https://github.com/kubernetes/kubernetes/blob/master/examples/storage/cassandra/java/src/main/java/io/k8s/cassandra/KubernetesSeedProvider.java) 通过 Kubernetes API 发现 Cassandra seeds IP 地址,那些 Cassandra 实例在 Cassandra Service 中定义。
请查阅自定义 seed provider 的 [README](https://git.k8s.io/examples/cassandra/java/README.md) 文档,获取 `KubernetesSeedProvider` 进阶配置。对于本示例来说,你应该不需要自定义 Seed Provider 的配置。
查看本示例的 [image](https://github.com/kubernetes/examples/tree/master/cassandra/image) 目录,了解如何构建容器的 docker 镜像及其内容。
你可能还注意到我们设置了一些 Cassandra 参数(`MAX_HEAP_SIZE``HEAP_NEWSIZE`),并且增加了关于 [namespace](/docs/user-guide/namespaces) 的信息。我们还告诉 Kubernetes 容器暴露了 `CQL``Thrift` API 端口。最后,我们告诉集群管理器我们需要 0.1 cpu(0.1 核)。
[!Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/cassandra/README.md?pixel)]()
@@ -0,0 +1,328 @@
---
title: "基于 Persistent Volumes 搭建 WordPress 和 MySQL 应用"
assignees:
- ahmetb
- jeffmendoza
---
本示例描述了如何在 Kubeernetes 上持久化安装 [WordPress](https://wordpress.org/) 和
[MySQL](https://www.mysql.com/) 。在这个安装里我们将使用官方的 [mysql](https://registry.hub.docker.com/_/mysql/) 和
[wordpress](https://registry.hub.docker.com/_/wordpress/) 镜像(WordPress 镜像包含一个 Apache 服务)。
展示的 Kubernetes 概念:
* [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) 定义持久化磁盘(磁盘生命周期不和 Pods 绑定)。
* [Services](https://kubernetes.io/docs/concepts/services-networking/service/) 使得 Pods 能够找到其它 Pods。
* [External Load Balancers](https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer) 对外暴露 Services。
* [Deployments](http://kubernetes.io/docs/user-guide/deployments/) 确保 Pods 持续运行。
* [Secrets](http://kubernetes.io/docs/user-guide/secrets/) 保存敏感密码信息。
## 快速入门
在一个名为 `password.txt` 的文件中放置你期望的 MySQL 密码,结尾不要有空行。如果你的编辑器添加了一个空行,开始的 `tr` 命令将会删除它。
**请注意:**如果你的集群强制启用 **_selinux_** 特性并且你将使用 [Host Path](#host-path) 作为存储,请遵照这个[额外步骤](#selinux)。
```shell
tr --delete '\n' <password.txt >.strippedpassword.txt && mv .strippedpassword.txt password.txt
kubectl create -f https://raw.githubusercontent.com/kubernetes/examples/master/mysql-wordpress-pd/local-volumes.yaml
kubectl create secret generic mysql-pass --from-file=password.txt
kubectl create -f https://raw.githubusercontent.com/kubernetes/examples/master/mysql-wordpress-pd/mysql-deployment.yaml
kubectl create -f https://raw.githubusercontent.com/kubernetes/examples/master/mysql-wordpress-pd/wordpress-deployment.yaml
```
## 目录
[在 Kubernetes 上持久化安装 MySQL 和 WordPress](#persistent-installation-of-mysql-and-wordpress-on-kubernetes)
- [快速入门](#quickstart)
- [目录](#table-of-contents)
- [集群要求](#cluster-requirements)
- [决定在哪里存储你的数据](#decide-where-you-will-store-your-data)
- [Host Path](#host-path)
- [SELinux](#selinux)
- [GCE Persistent Disk](#gce-persistent-disk)
- [创建 MySQL 密码 secret](#create-the-mysql-password-secret)
- [部署 MySQL](#deploy-mysql)
- [部署 WordPress](#deploy-wordpress)
- [访问你的新 WordPress 博客](#visit-your-new-wordpress-blog)
- [删除并重启你的博客](#take-down-and-restart-your-blog)
- [接下来的步骤](#next-steps)
## 集群要求
Kubernetes本质是模块化的,可以在各种环境中运行。但并不是所有集群都相同。此处是本示例的一些要求:
* 需要 1.2 版本以上的 Kubernetes,以使用更新的特性,例如 PV Claims 和 Deployments。运行 `kubectl version` 来查看你的集群版本。
* [Cluster DNS](https://github.com/kubernetes/dns) 将被用于服务发现。
* 一个 [external load balancer](https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer) 将被用于接入 WordPress。
* 使用了 [Persistent Volume Claims](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims)。你必须创建集群中需要的 Persistent Volumes。本示例将展示两种类型的 volume 的创建方法,但是任何类型的 volume 都是足够使用的。
查阅 [Getting Started Guide](http://kubernetes.io/docs/getting-started-guides/),搭建一个集群并安装 [kubectl](http://kubernetes.io/docs/user-guide/prereqs/) 命令行工具。
## 决定在哪里存储你的数据
MySQL 和 WordPress 各自使用一个 [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) 来存储自己的数据。我们将使用一个 Persistent Volume Claim 来取得一个可用的持久化存储。本示例覆盖了 HostPath 和
GCEPersistentDisk 卷类型。你可以从两者中选择一个,或者查看 [Persistent Volumes的类型](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes)。
### Host Path
Host paths 是映射到主机上目录的卷。**这种类型应该只用于测试目的或者单节点集群**。如果 pod 在一个新的节点上重建,数据将不会在节点之间移动。如果 pod 被删除并在一个新的节点上重建,数据将会丢失。
##### SELinux
在支持 selinux 的系统上,保持它为 enabled/enforcing 是最佳选择。然而,docker 容器使用 "_svirt_sandbox_file_t_" 标签类型挂载 host path,这和默认的 /tmp ("_tmp_t_") 标签类型不兼容。在 mysql 容器试图对 _/var/lib/mysql_ 执行 `chown` 时将导致权限错误。
因此,要在一个启用 selinx 的系统上使用 host path,你应该预先创建 host path 路径(/tmp/data/)并将它的 selinux 标签类型改变为 "_svirt_sandbox_file_t_",就像下面一样:
```shell
## on every node:
mkdir -p /tmp/data
chmod a+rwt /tmp/data # match /tmp permissions
chcon -Rt svirt_sandbox_file_t /tmp/data
```
继续进行 host path 配置,在 Kubernetes 中使用 [local-volumes.yaml](https://git.k8s.io/examples/mysql-wordpress-pd/local-volumes.yaml) 创建 persistent volume 对象:
```shell
export KUBE_REPO=https://raw.githubusercontent.com/kubernetes/examples/master
kubectl create -f $KUBE_REPO/mysql-wordpress-pd/local-volumes.yaml
```
### GCE Persistent Disk
如果在 [Google Compute Engine](http://kubernetes.io/docs/getting-started-guides/gce/) 上运行集群,你可以使用这个存储选项。
创建两个永久磁盘。你需要在和 Kubernetes 集群相同的 [GCE zone](https://cloud.google.com/compute/docs/zones) 中创建这些磁盘。默认的安装脚本将在 `us-central1-b` zone 中创建集群,就像你在 [config-default.sh](https://git.k8s.io/kubernetes/cluster/gce/config-default.sh) 文件中看到的。替换下面的 `<zone>` 为合适的 zone。`wordpress-1``wordpress-2` 的名字必须和 [gce-volumes.yaml](https://git.k8s.io/examples/mysql-wordpress-pd/gce-volumes.yaml) 指定的 `pdName` 字段匹配。
```shell
gcloud compute disks create --size=20GB --zone=<zone> wordpress-1
gcloud compute disks create --size=20GB --zone=<zone> wordpress-2
```
在 Kubernetes 为这些磁盘创建 persistent volume 对象:
```shell
export KUBE_REPO=https://raw.githubusercontent.com/kubernetes/examples/master
kubectl create -f $KUBE_REPO/mysql-wordpress-pd/gce-volumes.yaml
```
## 创建 MySQL 密码 Secret
使用一个 [Secret](http://kubernetes.io/docs/user-guide/secrets/) 对象存储 MySQL 密码。首先,创建一个名为 `password.txt` 的文件(和 wordpress 示例文件在相同的文件夹),并且将你的密码保存于其中。请确保密码文件的结尾没有空行。如果你的编辑器添加了一个,开始的 `tr` 命令将会删除这个空行。然后,创建这个 Secret 对象。
```shell
tr --delete '\n' <password.txt >.strippedpassword.txt && mv .strippedpassword.txt password.txt
kubectl create secret generic mysql-pass --from-file=password.txt
```
MySQL 和 WordPress pod 配置引用了这个 secret,所以这些 pods 就可以访问它。MySQL pod 会设置数据库密码,并且 WordPress 将使用这个密码来访问数据库。
## 部署 MySQL
<--
Now that the persistent disks and secrets are defined, the Kubernetes
pods can be launched. Start MySQL using
[mysql-deployment.yaml](https://git.k8s.io/examples/mysql-wordpress-pd/mysql-deployment.yaml).
-->
现在我们已经定义了永久磁盘和 secrets,可以启动 Kubernetes pods 了。使用 [mysql-deployment.yaml](https://git.k8s.io/examples/mysql-wordpress-pd/mysql-deployment.yaml) 启动 MySQL。
```shell
kubectl create -f $KUBE_REPO/mysql-wordpress-pd/mysql-deployment.yaml
```
查看 [mysql-deployment.yaml](https://git.k8s.io/examples/mysql-wordpress-pd/mysql-deployment.yaml),注意到我们定义了一个挂载到 `/var/lib/mysql` 的卷,然后创建了一个请求 20G 卷的 Persistent Volume Claim。这个要求可以被任何符合这个要求的卷满足,在我们的例子中,可以是上面创建的卷中的一个。
再看一下 `env` 一节,我们引用上面创建的 `mysql-pass` secret 来指定密码。Secrets 可以有多组键值对。我们的只有一个键 `password.txt`,它是我们用来创建 secret 的文件名。[MySQL镜像](https://hub.docker.com/_/mysql/) 使用 `MYSQL_ROOT_PASSWORD` 环境变量设置数据库密码。
在很短的时间内,新建的 pod 将达到 `Running` 状态。列出所有的 pods,查看新建的 pod 的状态。
```shell
kubectl get pods
```
```
NAME READY STATUS RESTARTS AGE
wordpress-mysql-cqcf4-9q8lo 1/1 Running 0 1m
```
Kubernetes 记录每个 pod 的 stderr 和 stdout。使用 `kubectl log` 查看一个 pod 的日志。从 `get pods` 复制 pod 名字,然后:
```shell
kubectl logs <pod-name>
```
```
...
2016-02-19 16:58:05 1 [Note] InnoDB: 128 rollback segment(s) are active.
2016-02-19 16:58:05 1 [Note] InnoDB: Waiting for purge to start
2016-02-19 16:58:05 1 [Note] InnoDB: 5.6.29 started; log sequence number 1626007
2016-02-19 16:58:05 1 [Note] Server hostname (bind-address): '*'; port: 3306
2016-02-19 16:58:05 1 [Note] IPv6 is available.
2016-02-19 16:58:05 1 [Note] - '::' resolves to '::';
2016-02-19 16:58:05 1 [Note] Server socket created on IP: '::'.
2016-02-19 16:58:05 1 [Warning] 'proxies_priv' entry '@ root@wordpress-mysql-cqcf4-9q8lo' ignored in --skip-name-resolve mode.
2016-02-19 16:58:05 1 [Note] Event Scheduler: Loaded 0 events
2016-02-19 16:58:05 1 [Note] mysqld: ready for connections.
Version: '5.6.29' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
```
我们还需要在 [mysql-deployment.yaml](https://git.k8s.io/examples/mysql-wordpress-pd/mysql-deployment.yaml) 中创建一个 service 以允许其它 pods 访问这个 mysql 示例。`wordpress-mysql` 名称被解析为这个 pod 的 IP。
到此为止,我们创建了一个 Deployment,一个 Pod,一个 PVC,一个 Service,一个 Endpoint,两个 PV 和一个 Secret,显示如下:
```shell
kubectl get deployment,pod,svc,endpoints,pvc -l app=wordpress -o wide && \
kubectl get secret mysql-pass && \
kubectl get pv
```
```shell
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/wordpress-mysql 1 1 1 1 3m
NAME READY STATUS RESTARTS AGE IP NODE
po/wordpress-mysql-3040864217-40soc 1/1 Running 0 3m 172.17.0.2 127.0.0.1
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
svc/wordpress-mysql None <none> 3306/TCP 3m app=wordpress,tier=mysql
NAME ENDPOINTS AGE
ep/wordpress-mysql 172.17.0.2:3306 3m
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
pvc/mysql-pv-claim Bound local-pv-2 20Gi RWO 3m
NAME TYPE DATA AGE
mysql-pass Opaque 1 3m
NAME CAPACITY ACCESSMODES STATUS CLAIM REASON AGE
local-pv-1 20Gi RWO Available 3m
local-pv-2 20Gi RWO Bound default/mysql-pv-claim 3m
```
## 部署 WordPress
接下来使用 [wordpress-deployment.yaml](https://git.k8s.io/examples/mysql-wordpress-pd/wordpress-deployment.yaml) 部署 WordPress
```shell
kubectl create -f $KUBE_REPO/mysql-wordpress-pd/wordpress-deployment.yaml
```
我们在这里使用了许多相同的特性,比如对 persistent storage 的 volume claim 和 password 的 secret。
[WordPress 镜像](https://hub.docker.com/_/wordpress/) 通过环境变量 `WORDPRESS_DB_HOST` 接收数据库的主机名。我们将这个环境变量值设置为我们创建的 MySQL
service 的名字:`wordpress-mysql`
WordPress service 具有 `type: LoadBalancer` 的设置。这将 wordpress service 置于一个外部 IP 之下。
找到你的 WordPress service 的外部 IP 地址。**为这个 service 分配一个外部 IP 地址可能会耗时一分钟左右,这取决于你的集群环境。**
```shell
kubectl get services wordpress
```
```
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
wordpress 10.0.0.5 1.2.3.4 80/TCP 19h
```
## 访问你的新 WordPress 博客
现在,我们可以访问这个运行的 WordPress 应用。请使用你上面获取的 service 的外部 IP 地址。
```
http://<external-ip>
```
你应该可以看到熟悉的 WordPress 初始页面。
![WordPress init page](WordPress.png "WordPress init page")
> 警告:不要在这个页面上留下你的 WordPress 设置。如果被其他用户发现,他们可能在你的实例上创建一个网站并用它来为可能有害的内容提供服务。你应该继续创建用户名密码之后的安装过程,删除你的实例,或者建立一个防火墙来限制接入。
## 删除并重启你的博客
建立你的 WordPress 博客并简单使用一下。然后删除它的 pods 并再次启动它们。由于使用了永久磁盘,你的博客的状态将被保留。
所有的资源都被标记为 `app=wordpress`,你可以使用 label selector 轻松的删除它们:
```shell
kubectl delete deployment,service -l app=wordpress
kubectl delete secret mysql-pass
```
稍后使用原来的命令重建资源,这将会选择包含原来的完整数据的磁盘。由于我们没有删除 PV Claims,在删除我们的 pods 后,集群中没有任何一个 pod 能够 claim 它们。保留 PV Claims 也保证了重建 Pods 不会导致 PD 切换 Pods。
如果你已经准备好了释放你的永久磁盘及其上的数据,请运行:
```shell
kubectl delete pvc -l app=wordpress
```
And then delete the volume objects themselves:
```shell
kubectl delete pv local-pv-1 local-pv-2
```
或者
```shell
kubectl delete pv wordpress-pv-1 wordpress-pv-2
```
## 接下来的步骤
* [Introspection and Debugging](http://kubernetes.io/docs/user-guide/introspection-and-debugging/)
* [Jobs](http://kubernetes.io/docs/user-guide/jobs/) may be useful to run SQL queries.
* [Exec](http://kubernetes.io/docs/user-guide/getting-into-containers/)
* [Port Forwarding](http://kubernetes.io/docs/user-guide/connecting-to-applications-port-forward/)
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/examples/mysql-wordpress-pd/README.md?pixel)]()
@@ -0,0 +1,27 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-1
labels:
type: local
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /tmp/data/pv-1
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv-2
labels:
type: local
spec:
capacity:
storage: 20Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /tmp/data/pv-2
@@ -0,0 +1,61 @@
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
@@ -0,0 +1,63 @@
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pv-claim
labels:
app: wordpress
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-persistent-storage
mountPath: /var/www/html
volumes:
- name: wordpress-persistent-storage
persistentVolumeClaim:
claimName: wp-pv-claim
@@ -0,0 +1,45 @@
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: gcr.io/google_containers/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
@@ -0,0 +1,45 @@
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
podManagementPolicy: "Parallel"
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: gcr.io/google_containers/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,157 @@
apiVersion: v1
kind: Service
metadata:
name: zk-headless
labels:
app: zk-headless
spec:
ports:
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zk
---
apiVersion: v1
kind: ConfigMap
metadata:
name: zk-config
data:
ensemble: "zk-0;zk-1;zk-2"
jvm.heap: "2G"
tick: "2000"
init: "10"
sync: "5"
client.cnxns: "60"
snap.retain: "3"
purge.interval: "1"
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget
metadata:
name: zk-budget
spec:
selector:
matchLabels:
app: zk
minAvailable: 2
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: zk
spec:
serviceName: zk-headless
replicas: 3
template:
metadata:
labels:
app: zk
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk-headless
topologyKey: "kubernetes.io/hostname"
containers:
- name: k8szk
imagePullPolicy: Always
image: gcr.io/google_samples/k8szk:v1
resources:
requests:
memory: "4Gi"
cpu: "1"
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
env:
- name : ZK_ENSEMBLE
valueFrom:
configMapKeyRef:
name: zk-config
key: ensemble
- name : ZK_HEAP_SIZE
valueFrom:
configMapKeyRef:
name: zk-config
key: jvm.heap
- name : ZK_TICK_TIME
valueFrom:
configMapKeyRef:
name: zk-config
key: tick
- name : ZK_INIT_LIMIT
valueFrom:
configMapKeyRef:
name: zk-config
key: init
- name : ZK_SYNC_LIMIT
valueFrom:
configMapKeyRef:
name: zk-config
key: tick
- name : ZK_MAX_CLIENT_CNXNS
valueFrom:
configMapKeyRef:
name: zk-config
key: client.cnxns
- name: ZK_SNAP_RETAIN_COUNT
valueFrom:
configMapKeyRef:
name: zk-config
key: snap.retain
- name: ZK_PURGE_INTERVAL
valueFrom:
configMapKeyRef:
name: zk-config
key: purge.interval
- name: ZK_CLIENT_PORT
value: "2181"
- name: ZK_SERVER_PORT
value: "2888"
- name: ZK_ELECTION_PORT
value: "3888"
command:
- sh
- -c
- zkGenConfig.sh && zkServer.sh start-foreground
readinessProbe:
exec:
command:
- "zkOk.sh"
initialDelaySeconds: 15
timeoutSeconds: 5
livenessProbe:
exec:
command:
- "zkOk.sh"
initialDelaySeconds: 15
timeoutSeconds: 5
volumeMounts:
- name: datadir
mountPath: /var/lib/zookeeper
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 20Gi