2

我正在尝试在生产 kubernetes 集群上部署 EFK 堆栈(使用 kubespray 安装),我们有 3 个节点,1 个 master + 2 个 worker,我需要使用 elasticsearch 作为 statefulset 并使用 master 节点中的本地文件夹来存储日志(本地持久性存储),我的配置是:

kind: Namespace
apiVersion: v1
metadata:
  name: kube-logging

---
kind: Service
apiVersion: v1
metadata:
  name: elasticsearch
  namespace: kube-logging
  labels:
    app: elasticsearch
spec:
  selector:
    app: elasticsearch
  clusterIP: None
  ports:
    - port: 9200
      name: rest
    - port: 9300
      name: inter-node
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
  namespace: kube-logging
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
  namespace: kube-logging
spec:
  storageClassName: local-storage
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  hostPath:
    path: /tmp/elastic
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: kube-logging
spec:
  serviceName: elasticsearch
  replicas: 2
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
        resources:
            limits:
              cpu: 1000m
              memory: 2Gi
        ports:
        - containerPort: 9200
          name: rest
          protocol: TCP
        - containerPort: 9300
          name: inter-node
          protocol: TCP
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
          - name: cluster.name
            value: k8s-logs
          - name: node.name
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: discovery.seed_hosts
            value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch"
          - name: cluster.initial_master_nodes
            value: "es-cluster-0,es-cluster-1,es-cluster-2"
          - name: ES_JAVA_OPTS
            value: "-Xms512m -Xmx512m"
      initContainers:
      - name: fix-permissions
        image: busybox
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: busybox
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
  volumeClaimTemplates:
  - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: local-storage
      resources:
        requests:
          storage: 5Gi
---

所以这是我的配置,但是当它被应用时,Elasticsearch 的两个 pod 之一仍处于待处理状态。当我对此 pod 进行 kubectl 描述时,这是我得到的错误:“1 个节点没有找到要绑定的可用持久卷”

我的配置正确吗?我必须使用 PV + storageclass + volumeClaimTemplates 吗?先感谢您。

这些是我的输出:

    [root@node1 nex]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                            STORAGECLASS    REASON   AGE
my-pv   5Gi        RWO            Retain           Bound    kube-logging/data-es-cluster-0   local-storage            24m
[root@node1 nex]# kubectl get pvc
NAME                STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS    AGE
data-es-cluster-0   Bound     my-pv    5Gi        RWO            local-storage   24m
data-es-cluster-1   Pending                                      local-storage   23m
[root@node1 nex]# kubectl describe pvc data-es-cluster-0
Name:          data-es-cluster-0
Namespace:     kube-logging
StorageClass:  local-storage
Status:        Bound
Volume:        my-pv
Labels:        app=elasticsearch
Annotations:   pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      5Gi
Access Modes:  RWO
VolumeMode:    Filesystem
Mounted By:    es-cluster-0
Events:
  Type    Reason                Age   From                         Message
  ----    ------                ----  ----                         -------
  Normal  WaitForFirstConsumer  24m   persistentvolume-controller  waiting for first consumer to be created before binding
[root@node1 nex]# kubectl describe pvc data-es-cluster-1
Name:          data-es-cluster-1
Namespace:     kube-logging
StorageClass:  local-storage
Status:        Pending
Volume:
Labels:        app=elasticsearch
Annotations:   <none>
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:
Access Modes:
VolumeMode:    Filesystem
Mounted By:    es-cluster-1
Events:
  Type    Reason                Age                   From                         Message
  ----    ------                ----                  ----                         -------
  Normal  WaitForFirstConsumer  4m12s (x82 over 24m)  persistentvolume-controller  waiting for first consumer to be created before binding
[root@node1 nex]#
4

1 回答 1

0

我的配置正确吗?我必须使用 PV + storageclass + volumeClaimTemplates 吗?先感谢您。

除了@Arghya Sadhu 在他的回答中已经提出的建议之外,我还想强调一下您当前设置中的另一件事。

如果您对 ElasticsearchPods将仅安排在一个特定节点(在您的情况下为您的主节点)这一事实感到满意,您仍然可以使用本地卷类型。但是不要将它与hostPath混淆。我在您的PV定义中注意到您使用了hostPathkey 所以很可能您并不完全了解这两个概念之间的区别。尽管它们非常相似,但本地类型比hostPath.

正如您在文档中所读到的:

本地卷表示已安装的本地存储设备,例如磁盘、分区或目录。

因此,这意味着除了特定目录之外,您还可以挂载本地磁盘或分区(/dev/sdb/dev/sdb5)。例如,它可以是具有严格定义容量的LVM分区。请记住,在挂载本地目录时,您无法强制执行可以实际使用的容量,因此即使您定义了 let's say 5Gi,即使超出此值,日志也可以写入本地目录。但情况并非如此,logical volume因为您可以定义它的容量并确保它不会使用比您给它更多的磁盘空间。

第二个区别是:

hostPath卷相比,local卷可以以持久且可移植的方式使用,而无需手动将 Pod 调度到节点,因为系统通过查看 PersistentVolume 上的节点亲和性来了解卷的节点约束。

在这种情况下,它是PersistentVolume您定义节点亲和性的地方,因此任何Pod(它可以Pod由您的管理StatefulSet)使用随后的local-storage存储类和相应PersistenVolume的将被自动安排在正确的节点上。

正如您可以进一步阅读的那样,nodeAffinity实际上是这样的必填字段PV

nodeAffinity使用本地卷时需要PersistentVolume 。它使 Kubernetes 调度程序能够使用本地卷将 Pod 正确调度到正确的节点。

据我了解,您的 kubernetes 集群是在本地/本地设置的。在这种情况下,NFS 可能是一个正确的选择。

如果您使用了一些云环境,那么您可以使用您的特定云提供商提供的持久存储,例如GCEPersistentDiskAWSElasticBlockStore. 您可以在此处找到 kubernetes 当前支持的持久卷类型的完整列表。

再说一遍,如果您担心节点级别的冗余,StatefulSet并且希望您的 2 ElasticsearchPods始终安排在不同的节点上,正如@Arghya Sadhu 已经建议的那样,请使用 NFS 或其他一些非本地存储。

但是,如果您不关心节点级冗余,并且您的 ElasticsearchPods都在同一个节点上运行(在您的情况下为主节点),您完全可以接受,请关注我 :)

正如@Arghya Sadhu 正确指出的那样:

即使已经绑定到 PVC 的 PV 有剩余容量,它也不能再次绑定到另一个 PVC,因为它是 PV 和 PVC 之间的一对一映射。

PV虽然和之间始终是一对一的映射PVC,但这并不意味着您不能PVC在多个 Pod 中使用单个。

请注意,在您StatefulSet使用的示例中,volumeClaimTemplates这基本上意味着每次创建Pod由您管理的新对象时,也会基于此模板创建StatefulSet一个新的对应对象。PersistentVolumeClaim因此,如果您已经10Gi PersistentVolume定义了例如,无论您在声明中请求全部10Gi还是只请求一半,只有 firstPVC将成功绑定到您的PV.

但是,您可以让它们使用单​​个手动volumeClaimTemplates定义PVC的. 请看以下示例:PodPVC

我们需要的第一件事是一个存储类。它看起来与您的示例中的非常相似:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

此设置与您的设置之间的第一个区别在于PV定义。而不是hostPath我们在这里使用本地卷:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /var/tmp/test ### path on your master node
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - your-master-node-name

请注意,除了定义local路径之外,我们还定义nodeAffinity了规则,以确保所有Pods获得此特定PV信息的内容都将自动安排在我们的主节点上。

然后我们手动应用PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-storage

现在PVC,所有(在您的示例 2 中)Pods管理的人都可以使用它StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # has to match .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 2 # by default is 1
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: mypd
          mountPath: /usr/share/nginx/html
      volumes:
      - name: mypd
        persistentVolumeClaim:
          claimName: myclaim

请注意,在上面的示例中,我们不再使用volumeClaimTemplates一个,而是一个PersistentVolumeClaim可供所有 Pod 使用的单个。Pod 仍然是唯一的,因为它们由 a 管理,StatefulSet但不是使用 unique PVCs,而是使用 common 。由于这种方法,两者都Pods可以同时将日志写入单个卷。

在我的示例中,我使用了 nginx 服务器来使复制尽可能简单,让每个想要快速尝试的人都可以轻松地进行复制,但我相信您可以根据自己的需要轻松调整它。

于 2020-06-09T20:57:45.253 回答