Building K8S cluster of EMQ X MQTT server from scratch

Keywords: Kubernetes network DNS Docker

EMQ X Team provides Helm chart for users to deploy EMQ X on kubernetes cluster with one click MQTT server This is the most recommended method of deploying EMQ X MQTT server on kubernetes or k3s cluster by EMQ X Team. This paper will use the method of handwritten yaml file to deploy a K8S cluster of EMQ X MQTT server from scratch, and analyze the details and skills in the deployment, so as to facilitate users to use flexibly in the actual deployment.

Reading this article requires users to understand the basic concepts of kubernetes and have an operational kubernetes cluster.

Deploying a single EMQ X MQTT server node on K8S

Deploying EMQ X Broker directly with Pod

In Kubernetes, the smallest management elements are not individual containers, but Pod Pod is the basic unit of execution of Kubernetes applications, that is, it is the smallest and simplest unit created or deployed in the Kubernetes object model. Pod means in the colony The process running on.

EMQ X Broker in docker hub It is convenient to deploy EMQ X Broker on a single Pod. Use the kubectl run command to create a Pod running EMQ X Broker

$ kubectl run emqx --image=emqx/emqx:v4.1-rc.1  --generator=run-pod/v1
pod/emqx created

To view the status of EMQ X Broker:

$ kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE
emqx   1/1     Running   0          3m13s

$ kubectl exec emqx -- emqx_ctl status
Node 'emqx@192.168.77.108' is started
emqx 4.1-rc.1 is running

Delete Pod:

$ kubectl delete pods emqx
pod "emqx" deleted

Pod is not designed as a persistent resource. It will not survive scheduling failure, node crash, or other recycling (such as due to lack of resources or other maintenance). Therefore, a controller is needed to manage the pod.

Deploying Pod with Deoloyment

Deployment A declarative method is provided for Pod and ReplicaSet to replace the previous methods ReplicationController To facilitate the management of applications. Typical application scenarios include:

  • Define Deployment to create pods and replicasets
  • Rolling upgrade and roll back application
  • Expansion and reduction
  • Pause and resume Deployment

Deploy an EMQ X Broker Pod with Deployment:

  • Define Deployment:

    $ cat deployment.yaml
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: emqx-deployment
    labels:
      app: emqx
    spec:
    replicas: 1
    selector:
      matchLabels:
        app: emqx
    template:
      metadata:
        labels:
          app: emqx
      spec:
        containers:
        - name: emqx
          image: emqx/emqx:v4.1-rc.1
          ports:
          - name: mqtt
            containerPort: 1883
          - name: mqttssl
            containerPort: 8883
          - name: mgmt
            containerPort: 8081
          - name: ws
            containerPort: 8083
          - name: wss
            containerPort: 8084
          - name: dashboard
            containerPort: 18083
  • Deployment:

    $  kubectl apply -f deployment.yaml
    deployment.apps/emqx-deployment created
  • To view the deployment:

    $ kubectl get deployment
    NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/emqx-deployment   3/3     3            3           74s
    
    $ kubectl get pods
    NAME                                  READY   STATUS    RESTARTS   AGE
    pod/emqx-deployment-7c44dbd68-8j77l   1/1     Running   0          74s
    
    $ kubectl exec pod/emqx-deployment-7c44dbd68-8j77l -- emqx_ctl status
    Node 'emqx-deployment-7c44dbd68-8j77l@192.168.77.117' is started
    emqx 4.1-rc.1 is running
  • Try to delete Pod manually

    $ kubectl delete pods emqx-deployment-7c44dbd68-8j77l
    pod "emqx-deployment-7c44dbd68-8j77l" deleted
    
    $ kubectl get pods
    NAME                              READY   STATUS    RESTARTS   AGE
    emqx-deployment-68fcb4bfd6-2nhh6   1/1     Running   0          59s

    The output results show that the EMQ X Broker Pod is successfully deployed with Deployment. Even if the Pod is terminated unexpectedly, Deployment will create a new Pod again.

Exposing EMQ X Broker Pod service with Services

Kubernetes Pods There is a life cycle. They can be created, and destruction will not start again. If used Deployment To run the application, it can dynamically create and destroy pods.

Each Pod has its own IP address, but in Deployment, the set of pods that run at the same time may be different from the set of pods that run the application later.

This leads to a problem: if the EMQ X Broker Pod is used to serve MQTT clients, how should clients find and track the IP addresses to connect to so that clients can use the EMQ X Broker service?

The answer is: Service

Service will run in a group Pods Applications on are exposed as abstract methods for network services.

Using Service to expose EMQ X Broker Pod as network Service:

  • Define Service:

    $cat service.yaml
    
    apiVersion: v1
    kind: Service
    metadata:
    name: emqx-service
    spec:
    selector:
      app: emqx
    ports:
      - name: mqtt
        port: 1883
        protocol: TCP
        targetPort: mqtt
      - name: mqttssl
        port: 8883
        protocol: TCP
        targetPort: mqttssl
      - name: mgmt
        port: 8081
        protocol: TCP
        targetPort: mgmt
      - name: ws
        port: 8083
        protocol: TCP
        targetPort: ws
      - name: wss
        port: 8084
        protocol: TCP
        targetPort: wss
      - name: dashboard
        port: 18083
        protocol: TCP
        targetPort: dashboard
    
  • Deploy Service:

    $ kubectl apply -f service.yaml
    service/emqx-service created
  • View deployment

    $ kubectl get svc
    NAME           TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                                        AGE
    emqx-service   ClusterIP   10.96.54.205   <none>        1883/TCP,8883/TCP,8081/TCP,8083/TCP,8084/TCP,18083/TCP   58s
  • Use the IP provided by Service to view the API of EMQ X Broker

    $ curl 10.96.54.205:8081/status
    Node emqx-deployment-68fcb4bfd6-2nhh6@192.168.77.120 is started
    emqx is running

At this point, a single EMQ X Broker node is deployed on kubernetes, and the EMQ X Broker Pod is managed through Deployment, and the EMQ X Broker Service is exposed through Service.

Cluster EMQ X MQTT server automatically through kubernetes

As mentioned above, a single EMQ X Broker Pod is deployed through Deployment. It is very convenient to expand the number of pods through Deployment. Execute kubectl scale Deployment ${Deployment_ Name} -- replicas ${number} command can expand the number of pods. Next, expand EMQ x broker pods to 3:

$ kubectl scale deployment emqx-deployment --replicas 3
deployment.apps/emqx-deployment scaled

$ kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
emqx-deployment-68fcb4bfd6-2nhh6   1/1     Running   0          18m
emqx-deployment-68fcb4bfd6-mpvch   1/1     Running   0          6s
emqx-deployment-68fcb4bfd6-mx55q   1/1     Running   0          6s

$ kubectl exec emqx-deployment-68fcb4bfd6-2nhh6 -- emqx_ctl status
Node 'emqx-deployment-68fcb4bfd6-2nhh6@192.168.77.120' is started
emqx 4.1-rc.1 is running

$ kubectl exec emqx-deployment-68fcb4bfd6-2nhh6 -- emqx_ctl cluster status
Cluster status: #{running_nodes =>
                      ['emqx-deployment-68fcb4bfd6-2nhh6@192.168.77.120'],
                  stopped_nodes => []}

You can see that the number of EMQ x broker pods has been expanded to three, but each Pod is independent and there is no cluster. Next, try to cluster EMQ x broker pods automatically through kubernetes.

Modify the configuration of EMQ X Broker

Check the EMQ X Broker documentation for Automatic clustering You can see that the configuration of EMQ X Broker needs to be modified

cluster.discovery = kubernetes
cluster.kubernetes.apiserver = http://10.110.111.204:8080
cluster.kubernetes.service_name = ekka
cluster.kubernetes.address_type = ip
cluster.kubernetes.app_name = ekka

Among them cluster.kubernetes.apiserver Is the address of kubernetes apiserver, which can be obtained through the kubectl cluster info command, cluster.kubernetes.service_name is the name of the Service mentioned above, cluster.kubernetes.app_ The name is EMQ X Broker's node.name Therefore, it is also necessary to set the EMQ X Broker in the cluster to unified node.name Prefix of.

The docker image of EMQ X Broker provides the function of modifying the configuration through environment variables. You can view the details docker hub or Github.

  • Modify the yaml file of Deployment and add environment variables

    $ cat deployment.yaml
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: emqx-deployment
    labels:
      app: emqx
    spec:
    replicas: 3
    selector:
      matchLabels:
        app: emqx
    template:
      metadata:
        labels:
          app: emqx
      spec:
        containers:
        - name: emqx
          image: emqx/emqx:v4.1-rc.1
          ports:
          - name: mqtt
            containerPort: 1883
          - name: mqttssl
            containerPort: 8883
          - name: mgmt
            containerPort: 8081
          - name: ws
            containerPort: 8083
          - name: wss
            containerPort: 8084
          - name: dashboard
            containerPort: 18083
          env:
          - name: EMQX_NAME
            value: emqx
          - name: EMQX_CLUSTER__DISCOVERY
            value: k8s
          - name: EMQX_CLUSTER__K8S__APP_NAME
            value: emqx
          - name: EMQX_CLUSTER__K8S__SERVICE_NAME
            value: emqx-service
          - name: EMQX_CLUSTER__K8S__APISERVER
            value: "https://kubernetes.default.svc:443"
          - name: EMQX_CLUSTER__K8S__NAMESPACE
            value: default

    Because ` kubectl scale deployment ${deployment_ The name} -- replicas ${number} command does not modify yaml files, so you need to set the spec.replicas: 3 .

    The DNS rule of kubernetes is built into Pod, so https://kubernetes.default.svc:443 will be resolved to the address of kubernetes apiserver.

  • Delete the previous Deployment and re deploy:

    $ kubectl delete deployment emqx-deployment
    deployment.apps "emqx-deployment" deleted
    
    $ kubectl apply -f deployment.yaml
    deployment.apps/emqx-deployment created

Granting Pod access to kubernetes apiserver

After deploying Deployment, check the status of the EMQ X Broker. You can see that although the EMQ X Broker is successfully started, the cluster is still not successful. Check the log of the EMQ X Broker Pod:

$ kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
emqx-deployment-5c8cfc4d75-67lmt   1/1     Running   0          5s
emqx-deployment-5c8cfc4d75-r6jgb   1/1     Running   0          5s
emqx-deployment-5c8cfc4d75-wv2hj   1/1     Running   0          5s

$ kubectl exec emqx-deployment-5c8cfc4d75-67lmt -- emqx_ctl status
Node 'emqx@192.168.87.150' is started
emqx 4.1-rc.1 is running

$ kubectl exec emqx-deployment-5c8cfc4d75-67lmt -- emqx_ctl cluster status
Cluster status: #{running_nodes => ['emqx@192.168.87.150'],
                  stopped_nodes => []}

$ kubectl logs emqx-deployment-76f6895c46-4684f

···
(emqx@192.168.87.150)1> 2020-05-20 01:48:39.726 [error] Ekka(AutoCluster): Discovery error: {403,
                                     "{\"kind\":\"Status\",\"apiVersion\":\"v1\",\"metadata\":{},\"status\":\"Failure\",\"message\":\"endpoints \\\"emqx-service\\\" is forbidden: User \\\"system:serviceaccount:default:default\\\" cannot get resource \\\"endpoints\\\" in API group \\\"\\\" in the namespace \\\"default\\\"\",\"reason\":\"Forbidden\",\"details\":{\"name\":\"emqx-service\",\"kind\":\"endpoints\"},\"code\":403}\n"}
···

Pod was denied access to kubernetes apiserver due to permission problem, and returned HTTP 403, so the cluster failed.

Ordinary Pod can't access kubernetes apiserver. There are two ways to solve this problem. One is to open the http interface of kubernetes apiserver, but this method has some security risks. The other is to configure RBAC authentication through ServiceAccount, Role and RoleBinding.

  • Define ServiceAccount, Role, and RoleBinding:

    $ cat rbac.yaml
    
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    namespace: default
    name: emqx
    ---
    kind: Role
    apiVersion: rbac.authorization.kubernetes.io/v1beta1
    metadata:
    namespace: default
    name: emqx
    rules:
    - apiGroups:
    - ""
    resources:
    - endpoints 
    verbs: 
    - get
    - watch
    - list
    ---
    kind: RoleBinding
    apiVersion: rbac.authorization.kubernetes.io/v1beta1
    metadata:
    namespace: default
    name: emqx
    subjects:
    - kind: ServiceAccount
      name: emqx
      namespace: default
    roleRef:
    kind: Role
    name: emqx
    apiGroup: rbac.authorization.kubernetes.io
  • Deploy the appropriate resources:

    $ kubectl apply -f rbac.yaml
    serviceaccount/emqx created
    role.rbac.authorization.kubernetes.io/emqx created
    rolebinding.rbac.authorization.kubernetes.io/emqx created
  • Modify the yaml file of Deployment and add spec.template.spec.serviceAccountName and redeploy:

    $cat deployment.yaml
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: emqx-deployment
    labels:
      app: emqx
    spec:
    replicas: 3
    selector:
      matchLabels:
        app: emqx
    template:
      metadata:
        labels:
          app: emqx
      spec:
        serviceAccountName: emqx
        containers:
        - name: emqx
          image: emqx/emqx:v4.1-rc.1
          ports:
          - name: mqtt
            containerPort: 1883
          - name: mqttssl
            containerPort: 8883
          - name: mgmt
            containerPort: 8081
          - name: ws
            containerPort: 8083
          - name: wss
            containerPort: 8084
          - name: dashboard
            containerPort: 18083
          env:
          - name: EMQX_NAME
            value: emqx
          - name: EMQX_CLUSTER__DISCOVERY
            value: kubernetes
          - name: EMQX_CLUSTER__K8S__APP_NAME
            value: emqx
          - name: EMQX_CLUSTER__K8S__SERVICE_NAME
            value: emqx-service
          - name: EMQX_CLUSTER__K8S__APISERVER
            value: "https://kubernetes.default.svc:443"
          - name: EMQX_CLUSTER__K8S__NAMESPACE
            value: default
    
    $ kubectl delete deployment emqx-deployment
    deployment.apps "emqx-deployment" deleted
    
    $ kubectl apply -f deployment.yaml
    deployment.apps/emqx-deployment created
  • View status:

    $ kubectl get pods
    NAME                              READY   STATUS    RESTARTS   AGE
    emqx-deployment-6b854486c-dhd7p   1/1     Running   0          10s
    emqx-deployment-6b854486c-psv2r   1/1     Running   0          10s
    emqx-deployment-6b854486c-tdzld   1/1     Running   0          10s
    
    $ kubectl exec emqx-deployment-6b854486c-dhd7p  -- emqx_ctl status
    Node 'emqx@192.168.77.92' is started
    emqx 4.1-rc.1 is running
    
    $ kubectl exec emqx-deployment-6b854486c-dhd7p  -- emqx_ctl cluster status
    Cluster status: #{running_nodes =>
                        ['emqx@192.168.77.115','emqx@192.168.77.92',
                         'emqx@192.168.87.157'],
                    stopped_nodes => []}
  • Abort a Pod:

    $ kubectl delete pods emqx-deployment-6b854486c-dhd7p
    pod "emqx-deployment-6b854486c-dhd7p" deleted
    
    $ kubectl get pods
    NAME                              READY   STATUS    RESTARTS   AGE
    emqx-deployment-6b854486c-846v7   1/1     Running   0          56s
    emqx-deployment-6b854486c-psv2r   1/1     Running   0          3m50s
    emqx-deployment-6b854486c-tdzld   1/1     Running   0          3m50s
    
    $ kubectl exec emqx-deployment-6b854486c-846v7 -- emqx_ctl cluster status
    Cluster status: #{running_nodes =>
                        ['emqx@192.168.77.115','emqx@192.168.77.84',
                         'emqx@192.168.87.157'],
                    stopped_nodes => ['emqx@192.168.77.92']}

    The output results show that EMQ X Broker will display the stopped pods correctly and add the newly created Pod of Deployment to the cluster.

So far, EMQ X Broker successfully established the cluster on kubernetes.

Persistent EMQ X Broker cluster

The Deployment used above is used to manage the Pod, but the network of the Pod is constantly changing. Moreover, when the Pod is destroyed and rebuilt, the data and configuration stored in the EMQ X Broker will disappear. This is unacceptable in production. Next, try to persist the cluster of the EMQ X Broker. Even if the Pod is destroyed and rebuilt, the EMQ X Broker will be destroyed The data can still be saved.

ConfigMap

ConfigMap configMap is an API object used to save non confidential data to key value pairs. When used, it can be used as an environment variable, a command-line parameter, or a configuration file in a storage volume.

ConfigMap combines your environment configuration information with Container image Decoupling, easy to modify the application configuration.

ConfigMap does not provide privacy or encryption. If the data you want to store is confidential, use Secret , or use other third-party tools to keep your data private, rather than using ConfigMap.

Next, use ConfigMap to record the configuration of EMQ X Broker and import them into Deployment as environment variables.

  • Define Configmap and deploy:

    $cat configmap.yaml
    
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: emqx-config
    data:
    EMQX_CLUSTER__K8S__ADDRESS_TYPE: "hostname"
    EMQX_CLUSTER__K8S__APISERVER: "https://kubernetes.default.svc:443"
    EMQX_CLUSTER__K8S__SUFFIX: "svc.cluster.local"
    
    $ kubectl apply -f configmap.yaml
    configmap/emqx-config created
  • Configure Deployment to use Configmap

    $cat deployment.yaml
    
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: emqx-deployment
    labels:
      app: emqx
    spec:
    replicas: 3
    selector:
      matchLabels:
        app: emqx
    template:
      metadata:
        labels:
          app: emqx
      spec:
        serviceAccountName: emqx
        containers:
        - name: emqx
          image: emqx/emqx:v4.1-rc.1
          ports:
          - name: mqtt
            containerPort: 1883
          - name: mqttssl
            containerPort: 8883
          - name: mgmt
            containerPort: 8081
          - name: ws
            containerPort: 8083
          - name: wss
            containerPort: 8084
          - name: dashboard
            containerPort: 18083
          envFrom:
            - configMapRef:
                name: emqx-config
  • Redeploy the Deployment and view the status

    $ kubectl delete -f deployment.yaml
    deployment.apps "emqx-deployment" deleted
    
    $ kubectl apply -f deployment.yaml
    deployment.apps/emqx-deployment created
    
    $ kubectl get pods
    NAME                               READY   STATUS    RESTARTS   AGE
    emqx-deployment-5c7696b5d7-k9lzj   1/1     Running   0          3s
    emqx-deployment-5c7696b5d7-mdwkt   1/1     Running   0          3s
    emqx-deployment-5c7696b5d7-z57z7   1/1     Running   0          3s
    
    $ kubectl exec emqx-deployment-5c7696b5d7-k9lzj -- emqx_ctl status
    Node 'emqx@192.168.87.149' is started
    emqx 4.1-rc.1 is running
    
    $ kubectl exec emqx-deployment-5c7696b5d7-k9lzj -- emqx_ctl cluster status
    Cluster status: #{running_nodes =>
                        ['emqx@192.168.77.106','emqx@192.168.77.107',
                         'emqx@192.168.87.149'],
                    stopped_nodes => []}

The configuration file of EMQ X Broker has been decoupled into Configmap. If necessary, you can configure one or more configmaps freely and import them into Pod as environment variables or files.

StatefulSet

StatefulSet It is designed to solve the problem of state and no state for applications

  • Stable persistent storage, that is, the same persistent data can be accessed after Pod rescheduling, which is based on PVC
  • The stable network flag, that is, after the Pod is rescheduled, its PodName and HostName remain unchanged, which is based on the Headless Service (i.e. the Service without Cluster IP)
  • Orderly deployment, orderly expansion, that is, pods are sequential. When deploying or expanding, they should be carried out in sequence according to the defined order (i.e. from 0 to N-1, all previous pods must be in Running and Ready status before the next Pod is run), which is implemented based on init containers
  • Orderly shrink, orderly delete (from N-1 to 0)

From the above application scenario, we can see that StatefulSet is composed of the following parts:

  • Headless Service for defining DNS domain
  • volumeClaimTemplates for creating PersistentVolumes
  • Define StatefulSet for specific application

The DNS format of each Pod in StatefulSet is statefulsetname - {0.. n-1} serviceName.namespace.svc . cluster.local In which

  • serviceName is the name of the Headless Service
  • 0..N-1 is the sequence number of Pod, starting from 0 to N-1
  • statefulSetName is the name of StatefulSet
  • The namespace is the namespace where the service is located, and the headless service and stateful set must be in the same namespace
  • . cluster.local Cluster Domain

Next, use StatefulSet instead of Deployment to manage pods.

  • Delete Deployment:

    $ kubectl delete deployment emqx-deployment
    deployment.apps "emqx-deployment" deleted
  • Define StatefulSet:

    $cat statefulset.yaml
    
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
    name: emqx-statefulset
    labels:
      app: emqx
    spec:
    serviceName: emqx-headless
    updateStrategy:
      type: RollingUpdate
    replicas: 3
    selector:
      matchLabels:
        app: emqx
    template:
      metadata:
        labels:
          app: emqx
      spec:
        serviceAccountName: emqx
        containers:
        - name: emqx
          image: emqx/emqx:v4.1-rc.1
          ports:
          - name: mqtt
            containerPort: 1883
          - name: mqttssl
            containerPort: 8883
          - name: mgmt
            containerPort: 8081
          - name: ws
            containerPort: 8083
          - name: wss
            containerPort: 8084
          - name: dashboard
            containerPort: 18083
          envFrom:
            - configMapRef:
                name: emqx-config

    Note that stateful set requires Headless Service to implement stable network flag, so a Service needs to be defined

    $cat headless.yaml
    
    apiVersion: v1
    kind: Service
    metadata:
    name: emqx-headless
    spec:
    type: ClusterIP
    clusterIP: None
    selector:
      app: emqx
    ports:
    - name: mqtt
      port: 1883
      protocol: TCP
      targetPort: 1883
    - name: mqttssl
      port: 8883
      protocol: TCP
      targetPort: 8883
    - name: mgmt
      port: 8081
      protocol: TCP
      targetPort: 8081
    - name: websocket
      port: 8083
      protocol: TCP
      targetPort: 8083
    - name: wss
      port: 8084
      protocol: TCP
      targetPort: 8084
    - name: dashboard
      port: 18083
      protocol: TCP
      targetPort: 18083

    Because the Headless Service does not require IP, clusterIP: None is configured.

  • Deploy the appropriate resources:

    $ kubectl apply -f headless-service.yaml
    service/emqx-headless created
    
    $ kubectl apply -f statefulset.yaml
    statefulset.apps/emqx-deployment created
    
    $ kubectl get pods
    NAME                               READY   STATUS    RESTARTS   AGE
    emqx-statefulset-0                 1/1     Running   0          2m59s
    emqx-statefulset-1                 1/1     Running   0          2m57s
    emqx-statefulset-2                 1/1     Running   0          2m54s
    
    $ kubectl exec emqx-statefulset-0 -- emqx_ctl cluster status
    Cluster status: #{running_nodes =>
                        ['emqx@192.168.77.105','emqx@192.168.87.153',
                         'emqx@192.168.87.155'],
                    stopped_nodes => []}
  • Update Configmap:

    StatefulSet provides a stable network flag. EMQ X Broker supports the use of hostname and dns rules to implement cluster on behalf of IP. Taking hostname as an example, it needs to be modified emqx.conf :

    cluster.kubernetes.address_type = hostname
    cluster.kubernetes.suffix = "svc.cluster.local"

    The DNS rules of Pod in kubernetes cluster can be customized by users. EMQ X Broker provides cluster.kubernetes.suffix This paper uses the default DNS rule: statefulsetname - {0.. n-1} serviceName.namespace.svc . cluster.local , the servicename in the DNS rule is the headless used by StatefulSet Service, so you also need to cluster.kubernetes.service_name is changed to Headless Service Name.

    To convert a configuration item into an environment variable, you need to configure it in Configmap

    EMQX_CLUSTER__K8S__ADDRESS_TYPE: "hostname"
    EMQX_CLUSTER__K8S__SUFFIX: "svc.cluster.local"
    EMQX_CLUSTER__K8S__SERVICE_NAME: emqx-headless

    Configmap provides the hot update function. Execute $kubectl edit configmap emqx config to hot update configmap.

  • Redeploy StatefulSet:

    After the Configmap update, the Pod will not restart. We need to manually update the StatefulSet

    $ kubectl delete statefulset emqx-statefulset
    statefulset.apps "emqx-statefulset" deleted
    
    $ kubectl apply -f statefulset.yaml
    statefulset.apps/emqx-statefulset created
    
    $ kubectl get pods
    NAME                 READY   STATUS    RESTARTS   AGE
    emqx-statefulset-0   1/1     Running   0          115s
    emqx-statefulset-1   1/1     Running   0          112s
    emqx-statefulset-2   1/1     Running   0          110s
    
    $ kubectl exec emqx-statefulset-2 -- emqx_ctl cluster status
    Cluster status: #{running_nodes =>
                        ['emqx@emqx-statefulset-0.emqx-headless.default.svc.cluster.local',
                         'emqx@emqx-statefulset-1.emqx-headless.default.svc.cluster.local',
                         'emqx@emqx-statefulset-2.emqx-headless.default.svc.cluster.local'],
                    stopped_nodes => []}

    You can see that the new EMQ X Broker cluster has been successfully established.

  • Abort a Pod:

    After the Pod in StatefulSet is rescheduled, its PodName and HostName remain unchanged. Here's a try:

    $ kubectl get pods
    kuNAME                 READY   STATUS    RESTARTS   AGE
    emqx-statefulset-0   1/1     Running   0          6m20s
    emqx-statefulset-1   1/1     Running   0          6m17s
    emqx-statefulset-2   1/1     Running   0          6m15s
    
    $ kubectl delete pod emqx-statefulset-0
    pod "emqx-statefulset-0" deleted
    
    $ kubectl get pods
    NAME                 READY   STATUS    RESTARTS   AGE
    emqx-statefulset-0   1/1     Running   0          27s
    emqx-statefulset-1   1/1     Running   0          9m45s
    emqx-statefulset-2   1/1     Running   0          9m43s
    
    $ kubectl exec emqx-statefulset-2 -- emqx_ctl cluster status
    Cluster status: #{running_nodes =>
                        ['emqx@emqx-statefulset-0.emqx-headless.default.svc.cluster.local',
                         'emqx@emqx-statefulset-1.emqx-headless.default.svc.cluster.local',
                         'emqx@emqx-statefulset-2.emqx-headless.default.svc.cluster.local'],
                    stopped_nodes => []}

    As expected, StatefulSet reschedules a Pod with the same network flag, and the EMQ X Broker in the Pod successfully joins the cluster.

StorageClasses, PersistentVolume, and PersistentVolumeClaim

A persistent Volume (PV) is a storage set up by an administrator and is part of a cluster. Just as nodes are resources in a cluster, PV is also a resource in a cluster. PV is a Volume plug-in such as Volume, but has a lifecycle independent of the Pod that uses PV. This API object contains the details of the storage implementation, that is, NFS, iSCSI, or cloud vendor specific storage systems.

Persistent volume claim (PVC) is a user stored request. It's similar to pod. Pod consumes node resources and PVC consumes PV resources. Pods can request specific levels of resources (CPU and memory). Declarations can request specific sizes and access modes (for example, they can be mounted in read / write once or read-only multiple times).

StorageClass provides administrators with a way to describe a storage "class.". Different classes may map to different quality of service levels or backup policies, or to any policy determined by the Cluster Administrator. Kubernetes itself does not know what the various classes represent. This concept is sometimes referred to as a "configuration file" in other storage systems.

When deploying EMQ X Broker, PV or StorageClass can be created in advance, and then the directory / opt/emqx/data/mnesia of EMQ X Broker can be mounted by using PVC. After Pods are rescheduled, EMQ X will obtain data from / opt/emqx/data/mnesia directory and recover, so as to realize the persistence of EMQ X Broker.

  • Define StatefulSet

    $cat statefulset.yaml
    
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
    name: emqx-statefulset
    labels:
      app: emqx
    spec:
    replicas: 3
    serviceName: emqx-headless
    updateStrategy:
      type: RollingUpdate
    selector:
      matchLabels:
        app: emqx
    template:
      metadata:
        labels:
          app: emqx
      spec:
        volumes:
        - name: emqx-data
          persistentVolumeClaim:
            claimName: emqx-pvc
        serviceAccountName: emqx
        containers:
        - name: emqx
          image: emqx/emqx:v4.1-rc.1
          ports:
          - name: mqtt
            containerPort: 1883
          - name: mqttssl
            containerPort: 8883
          - name: mgmt
            containerPort: 8081
          - name: ws
            containerPort: 8083
          - name: wss
            containerPort: 8084
          - name: dashboard
            containerPort: 18083
          envFrom:
            - configMapRef:
                name: emqx-config
          volumeMounts:
          - name: emqx-data
            mountPath: "/opt/emqx/data/mnesia"
    volumeClaimTemplates:
    - metadata:
        name: emqx-pvc
        annotations:
          volume.alpha.kubernetes.io/storage-class: manual
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 1Gi

    The file first specifies the storage class with the name of StorageClass as manual through volumeClaimTemplates to create a PVC resource named emqx PVC. The read-write mode of the PVC resource is ReadWriteOnce, which requires 1Gi of space. Then, the PVC is defined as the volumes with the name of emqx data, and the volumes are mounted in the / opt/emqx/data/mnesia directory in Pod.

  • Deployment resources:

    Before deploying StatefulSet, users or kubernetes cluster administrators need to create their own storage classes.

    $ kubectl apply -f statefulset.yaml
    statefulset.apps/emqx-statefulset created
    
    $ kubectl get pods
    NAME                 READY   STATUS    RESTARTS   AGE
    emqx-statefulset-0   1/1     Running   0          27s
    emqx-statefulset-1   1/1     Running   0          9m45s
    emqx-statefulset-2   1/1     Running   0          9m43s
    
    $ kubectl get pvc
    NAME                                 STATUS    VOLUME                                 CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    emqx-data-emqx-statefulset-0   Bound     pvc-8094cd75-adb5-11e9-80cc-0697b59e8064   1Gi        RWO            gp2            2m11s
    emqx-data-emqx-statefulset-0   Bound     pvc-9325441d-adb5-11e9-80cc-0697b59e8064   1Gi        RWO            gp2            99s
    emqx-data-emqx-statefulset-0   Bound     pvc-ad425e9d-adb5-11e9-80cc-0697b59e8064   1Gi        RWO            gp2            56s

    The output results show that the state of the PVC is Bound, and the PVC storage has been successfully established. When the Pod is rescheduled, EMQ X Broker will read the data mounted in the PVC to achieve persistence.

EMQ X Broker establishes a persistent cluster on kubernetes. This article omits some details. The deployment process is also a simple Demo. Users can read it by themselves kubernetes documentation With EMQ X Team Helm chart source code To continue in-depth research, of course, we welcome the contribution of issue, pull requests and start in Github.

Posted by chacha102 on Mon, 29 Jun 2020 22:46:35 -0700