1 - 示例:使用 Persistent Volumes 部署 WordPress 和 MySQL

本示例描述了如何通过 Minikube 在 Kubernetes 上安装 WordPress 和 MySQL。这两个应用都使用 PersistentVolumes 和 PersistentVolumeClaims 保存数据。

PersistentVolume(PV)是一块集群里由管理员手动提供,或 kubernetes 通过 StorageClass 动态创建的存储。 PersistentVolumeClaim(PVC)是一个满足对 PV 存储需要的请求。PersistentVolumes 和 PersistentVolumeClaims 是独立于 Pod 生命周期而在 Pod 重启,重新调度甚至删除过程中保存数据。

Objectives

  • 创建 PersistentVolumeClaims 和 PersistentVolumes
  • 创建 kustomization.yaml 使用
    • Secret 生成器
    • MySQL 资源配置
    • WordPress 资源配置
  • 应用整个 kustomization 目录 kubectl apply -k ./
  • 清理

Before you begin

你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

To check the version, enter kubectl version.

此例在kubectl 1.14 或者更高版本有效。

下载下面的配置文件:

  1. mysql-deployment.yaml

  2. wordpress-deployment.yaml

创建 PersistentVolumeClaims 和 PersistentVolumes

MySQL 和 Wordpress 都需要一个 PersistentVolume 来存储数据。他们的 PersistentVolumeClaims 将在部署步骤中创建。

许多群集环境都安装了默认的 StorageClass。如果在 PersistentVolumeClaim 中未指定 StorageClass,则使用群集的默认 StorageClass。

创建 PersistentVolumeClaim 时,将根据 StorageClass 配置动态设置 PersistentVolume。

创建 kustomization.yaml

创建 Secret 生成器

A Secret 是存储诸如密码或密钥之类的敏感数据的对象。从 1.14 开始,kubectl支持使用 kustomization 文件管理 Kubernetes 对象。您可以通过kustomization.yaml中的生成器创建一个 Secret。

通过以下命令在kustomization.yaml中添加一个 Secret 生成器。您需要用您要使用的密码替换YOUR_PASSWORD

cat <<EOF >./kustomization.yaml
secretGenerator:
- name: mysql-pass
  literals:
  - password=YOUR_PASSWORD
EOF

补充 MySQL 和 WordPress 的资源配置

以下 manifest 文件描述了单实例 MySQL 部署。MySQL 容器将 PersistentVolume 挂载在/var/lib/mysqlMYSQL_ROOT_PASSWORD环境变量设置来自 Secret 的数据库密码。

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: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  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

以下 manifest 文件描述了单实例 WordPress 部署。WordPress 容器将网站数据文件位于/var/www/html的 PersistentVolume。WORDPRESS_DB_HOST环境变量集上面定义的 MySQL Service 的名称,WordPress 将通过 Service 访问数据库。WORDPRESS_DB_PASSWORD环境变量设置从 Secret kustomize 生成的数据库密码。

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: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  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

  1. 下载 MySQL deployment 配置文件。

    curl -LO https://k8s.io/examples/application/wordpress/mysql-deployment.yaml
    
  2. 下载 WordPress 配置文件。

    curl -LO https://k8s.io/examples/application/wordpress/wordpress-deployment.yaml
    
  3. 补充到 kustomization.yaml 文件。

    cat <<EOF >>./kustomization.yaml
    resources:
      - mysql-deployment.yaml
      - wordpress-deployment.yaml
    EOF
    

应用和验证

kustomization.yaml包含用于部署 WordPress 网站的所有资源以及 MySQL 数据库。您可以通过以下方式应用目录

kubectl apply -k ./

现在,您可以验证所有对象是否存在。

  1. 通过运行以下命令验证 Secret 是否存在:

    kubectl get secrets
    

    响应应如下所示:

    NAME                    TYPE                                  DATA   AGE
    mysql-pass-c57bb4t7mf   Opaque                                1      9s
    
  2. 验证是否已动态配置 PersistentVolume:

    kubectl get pvc
    

    响应应如下所示:

    NAME             STATUS    VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS       AGE
    mysql-pv-claim   Bound     pvc-8cbd7b2e-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    wp-pv-claim      Bound     pvc-8cd0df54-4044-11e9-b2bb-42010a800002   20Gi       RWO            standard           77s
    
  3. 通过运行以下命令来验证 Pod 是否正在运行:

    kubectl get pods
    

    响应应如下所示:

    NAME                               READY     STATUS    RESTARTS   AGE
    wordpress-mysql-1894417608-x5dzt   1/1       Running   0          40s
    
  4. 通过运行以下命令来验证 Service 是否正在运行:

    kubectl get services wordpress
    

    响应应如下所示:

    NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
    wordpress   ClusterIP   10.0.0.89    <pending>     80:32406/TCP   4m
    
  5. 运行以下命令以获取 WordPress 服务的 IP 地址:

    minikube service wordpress --url
    

    响应应如下所示:

    http://1.2.3.4:32406
    
  6. 复制 IP 地址,然后将页面加载到浏览器中来查看您的站点。

    您应该看到类似于以下屏幕截图的 WordPress 设置页面。

    wordpress-init

Cleaning up

  1. 运行一下命令删除您的 Secret,Deployments,Services and PersistentVolumeClaims:

    kubectl delete -k ./
    

What's next

2 - 示例:使用 StatefulSet 部署 Cassandra

本教程描述拉如何在 Kubernetes 上运行 Apache Cassandra。 数据库 Cassandra 需要永久性存储提供数据持久性(应用“状态”)。 在此示例中,自定义 Cassandra seed provider 使数据库在加入 Cassandra 集群时发现新的 Cassandra 实例。

使用"StatefulSets"可以更轻松地将有状态的应用程序部署到你的 Kubernetes 集群中。 有关本教程中使用的功能的更多信息, 参阅 StatefulSet

Objectives

  • 创建并验证 Cassandra 无头(headless)Service..
  • 使用 StatefulSet 创建一个 Cassandra ring。
  • 验证 StatefulSet。
  • 修改 StatefulSet。
  • 删除 StatefulSet 及其 Pod.

Before you begin

你必须拥有一个 Kubernetes 的集群,同时你的 Kubernetes 集群必须带有 kubectl 命令行工具。 建议在至少有两个节点的集群上运行本教程,且这些节点不作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面任意一个 Kubernetes 工具构建:

要完成本教程,你应该已经熟悉 PodServiceStatefulSet

为 Cassandra 创建无头(headless) Services

在 Kubernetes 中,一个 Service 描述了一组执行相同任务的 Pod

以下 Service 用于在 Cassandra Pod 和集群中的客户端之间进行 DNS 查找:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: cassandra
  name: cassandra
spec:
  clusterIP: None
  ports:
  - port: 9042
  selector:
    app: cassandra

创建一个 Service 来跟踪 cassandra-service.yaml 文件中的所有 Cassandra StatefulSet:

kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml

验证(可选)

获取 Cassandra Service。

kubectl get svc cassandra

响应是:

NAME        TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
cassandra   ClusterIP   None         <none>        9042/TCP   45s

如果没有看到名为 cassandra 的服务,则表示创建失败。 请阅读调试服务,以解决常见问题。

使用 StatefulSet 创建 Cassandra Ring

下面包含的 StatefulSet 清单创建了一个由三个 Pod 组成的 Cassandra ring。

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: cassandra
  labels:
    app: cassandra
spec:
  serviceName: cassandra
  replicas: 3
  selector:
    matchLabels:
      app: cassandra
  template:
    metadata:
      labels:
        app: cassandra
    spec:
      terminationGracePeriodSeconds: 1800
      containers:
      - name: cassandra
        image: gcr.io/google-samples/cassandra:v13
        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
              - nodetool drain
        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: 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
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: fast
      resources:
        requests:
          storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: fast
provisioner: k8s.io/minikube-hostpath
parameters:
  type: pd-ssd

使用 cassandra-statefulset.yaml 文件创建 Cassandra StatefulSet :

# 如果你能未经修改地 apply cassandra-statefulset.yaml,请使用此命令
kubectl apply -f https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml

如果你为了适合你的集群需要修改 cassandra-statefulset.yaml, 下载 https://k8s.io/examples/application/cassandra/cassandra-statefulset.yaml, 然后 apply 修改后的清单。

# 如果使用本地的 cassandra-statefulset.yaml ,请使用此命令
kubectl apply -f cassandra-statefulset.yaml

验证 Cassandra StatefulSet

  1. 获取 Cassandra StatefulSet:

    kubectl get statefulset cassandra
    

    响应应该与此类似:

    NAME        DESIRED   CURRENT   AGE
    cassandra   3         0         13s
    

    StatefulSet 资源会按顺序部署 Pod。

  1. 获取 Pod 查看已排序的创建状态:

    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
    

    这三个 Pod 要花几分钟的时间才能部署。部署之后,相同的命令将返回类似于以下的输出:

    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
    
  1. 运行第一个 Pod 中的 Cassandra nodetool, 以显示 ring 的状态。

    kubectl exec -it cassandra-0 -- nodetool status
    

    响应应该与此类似:

    Datacenter: DC1-K8Demo
    ======================
    Status=Up/Down
    |/ State=Normal/Leaving/Joining/Moving
    --  Address     Load       Tokens       Owns (effective)  Host ID                               Rack
    UN  172.17.0.5  83.57 KiB  32           74.0%             e2dd09e6-d9d3-477e-96c5-45094c08db0f  Rack1-K8Demo
    UN  172.17.0.4  101.04 KiB  32           58.8%             f89d6835-3a42-4419-92b3-0e62cae1479c  Rack1-K8Demo
    UN  172.17.0.6  84.74 KiB  32           67.1%             a6a1e8c2-3dc5-4417-b1a0-26507af2aaad  Rack1-K8Demo
    

修改 Cassandra StatefulSet

使用 kubectl edit 修改 Cassandra StatefulSet 的大小。

  1. 运行以下命令:

    kubectl edit statefulset cassandra
    

    此命令你的终端中打开一个编辑器。需要更改的是 replicas 字段。下面是 StatefulSet 文件的片段示例:

    # 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/v1
    kind: StatefulSet
    metadata:
      creationTimestamp: 2016-08-13T18:40:58Z
      generation: 1
      labels:
      app: cassandra
      name: cassandra
      namespace: default
      resourceVersion: "323"
      uid: 7a219483-6185-11e6-a910-42010a8a0fc0
    spec:
      replicas: 3
    
  1. 将副本数(replicas)更改为 4,然后保存清单。

    StatefulSet 现在可以扩展到运行 4 个 Pod。

  2. 获取 Cassandra StatefulSet 验证更改:

    kubectl get statefulset cassandra
    

    响应应该与此类似:

    NAME        DESIRED   CURRENT   AGE
    cassandra   4         4         36m
    

Cleaning up

删除或缩小 StatefulSet 不会删除与 StatefulSet 关联的卷。 这个设置是出于安全考虑,因为你的数据比自动清除所有相关的 StatefulSet 资源更有价值。

  1. 运行以下命令(连在一起成为一个单独的命令)删除 Cassandra StatefulSet 中的所有内容:

    grace=$(kubectl get pod cassandra-0 -o=jsonpath='{.spec.terminationGracePeriodSeconds}') \
      && kubectl delete statefulset -l app=cassandra \
      && echo "Sleeping ${grace} seconds" 1>&2 \
      && sleep $grace \
      && kubectl delete persistentvolumeclaim -l app=cassandra
    
  1. 运行以下命令,删除你为 Cassandra 设置的 Service:

    kubectl delete service -l app=cassandra
    

Cassandra 容器环境变量

本教程中的 Pod 使用来自 Google 容器镜像库gcr.io/google-samples/cassandra:v13 镜像。上面的 Docker 镜像基于 debian-base, 并且包含 OpenJDK 8。

该映像包括来自 Apache Debian 存储库的标准 Cassandra 安装。 通过使用环境变量,您可以更改插入到 cassandra.yaml 中的值。

环境变量 默认值
CASSANDRA_CLUSTER_NAME 'Test Cluster'
CASSANDRA_NUM_TOKENS 32
CASSANDRA_RPC_ADDRESS 0.0.0.0

What's next