07 Cluster Management Using Redis as an Example for Deployment and Access

07 Cluster Management Using Redis as an Example for Deployment and Access #

In the previous section, we have learned the basic usage of kubectl. In this section, we will use kubectl to deploy in K8S.

As we mentioned earlier, a Pod is the smallest scheduling unit in K8S, so we cannot directly run a container in K8S, but we can run a Pod that contains only one container.

Starting with kubectl run #

The basic usage of kubectl run is as follows:

Usage:
  kubectl run NAME --image=image [--env="key=value"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...] [options]

NAME and --image are required parameters. They represent the name of the deployment and the image to be used, respectively. The remaining parts will be explained later. However, in actual usage, it is recommended to write a configuration file and deploy it using kubectl create.

Using the Smallest Redis Image #

In the official Redis image list, there are many tags to choose from. Among them, the image based on Alpine Linux has the smallest size and is easy to download. We will choose the redis:alpine image for deployment.

Deployment #

Now we are only deploying one Redis instance.

➜  ~ kubectl run redis --image='redis:alpine'
deployment.apps/redis created

You can see the prompt deployment.apps/redis created, which will be explained later. Let’s use kubectl get all to see what happened.

➜  ~ kubectl get all
NAME                         READY     STATUS    RESTARTS   AGE
pod/redis-7c7545cbcb-2m6rp   1/1       Running   0          30s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   32s

NAME                    DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/redis   1         1         1            1           30s

NAME                               DESIRED   CURRENT   READY     AGE
replicaset.apps/redis-7c7545cbcb   1         1         1         30s

Among them, we can see the deployment.apps/redis that we just created with the run operation, as well as replicaset.apps/redis-7c7545cbcb, service/kubernetes, and pod/redis-7c7545cbcb-f984p.

The format of the output of kubectl get all is the type before / and the name after /.

What do these represent?

Deployment #

Deployment is a high-level abstraction that allows us to perform scaling, rolling updates, and rollbacks. We created a Deployment named redis and specified the image it uses as redis:alpine with the kubectl run redis --image='redis:alpine' command.

At the same time, K8S will add some labels (Label) to it by default. We can view it by changing the output format of get.

➜  ~ kubectl get deployment.apps/redis -o wide 
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS   IMAGES         SELECTOR
redis     1         1         1            1           40s       redis        redis:alpine   run=redis
➜  ~ kubectl get deploy redis -o wide          
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS   IMAGES         SELECTOR
redis     1         1         1            1           40s       redis        redis:alpine   run=redis

So what is the role of these Labels? They can be used as selection criteria. For example:

➜  ~ kubectl get deploy -l run=redis -o wide 
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS   IMAGES         SELECTOR
redis     1         1         1            1           11h       redis        redis:alpine   run=redis
➜  ~ kubectl get deploy -l run=test -o wide  # Since we have not created test, there is no result
No resources found.

One of the questions we always consider when deploying or updating an application is how to perform a smooth upgrade. Using Deployment, it is also easy to perform Canary deployments. This mainly depends on Label and Selector, which we will discuss in more detail later.

Creating a Deployment can be done using the mentioned method, but the recommended method is to use a configuration file in yaml format. The configuration file mainly declares an expected state, and other components are responsible for coordinating and eventually achieving this expected state. Of course, this is also one of its key functions. It delegates Pod to the ReplicaSet that will be introduced below.

ReplicaSet #

ReplicaSet is a lower-level structure that allows scaling.

As mentioned above, Deployment mainly declares an expected state and delegates Pod to ReplicaSet. ReplicaSet will check whether the current number and status of Pods meet the expectations and try to meet this expectation.

ReplicaSet can be created by ourselves, but in general, it is not recommended, because if we do so, it is actually equivalent to skipping part of the Deployment and we will not be able to use the functions or features brought by Deployment.

In addition to ReplicaSet, we have another choice named ReplicationController. The main difference between these two is more about selectors, which we will discuss later. The recommended approach now is ReplicaSet, so not much explanation is given.

ReplicaSet can be abbreviated as rs, and can be viewed with the following command:

➜  ~ kubectl get rs -o wide
NAME               DESIRED   CURRENT   READY     AGE       CONTAINERS   IMAGES         SELECTOR                           
redis-7c7545cbcb   1         1         1         11h       redis        redis:alpine   pod-template-hash=3731017676,run=redis

In the output, we notice that in addition to the run=redis label we saw earlier, there is also a pod-template-hash=3731017676 label, which is automatically added by the Deployment controller to prevent duplication, so the pod-template is hashed to serve as a unique identifier.

Service #

Service is simply to have a stable entry point to access our application service or a group of Pods. Through Service, service discovery and load balancing can be easily achieved.

➜  ~ kubectl get service -o wide
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE       SELECTOR
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   16m        <none>

Through the use of kubectl, you can see that the main display will show the name, type, IP, port, creation time, and selectors of the Service. Let’s break it down further.

Types #

Currently, there are four types of Service:

  • ClusterIP: This is the default type of Service in Kubernetes. It exposes the service on a virtual IP that is only accessible within the cluster.
  • NodePort: This exposes the service by binding a fixed port on all nodes within the cluster, allowing access to the service through <NodeIP>:<NodePort>.
  • LoadBalancer: This creates an external load balancer through the cloud provider to expose the service. It automatically creates the required NodePort or ClusterIP for routing requests to the external load balancer.
  • ExternalName: This exposes the service by forwarding it to a specified domain name using DNS CNAME. This requires support for kube-dns version 1.7 or higher.

Practice #

We have already discussed the basic types of Service, and we have also deployed a Redis service. However, we are still unable to access the service. Now, let’s expose the Redis service that we just deployed.

➜  ~ kubectl expose deploy/redis --port=6379 --protocol=TCP --target-port=6379 --name=redis-server  
service/redis-server exposed
➜  ~ kubectl get svc -o wide                                                                       
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE       SELECTOR
kubernetes     ClusterIP   10.96.0.1       <none>        443/TCP    49m       <none>
redis-server   ClusterIP   10.108.105.63   <none>        6379/TCP   4s        run=redis

By using the kubectl expose command, we expose the Redis server. Here are some explanations:

  • port: This is the port that the Service is exposed on, allowing access to the service through this port.
  • protocol: This is the protocol used. Currently, Kubernetes supports TCP/UDP protocols. Experimental support for the SCTP protocol was added in version 1.12. The default protocol is TCP.
  • target-port: This is the target port where the actual service is running. Requests enter through the port and flow to the port specified here based on the configured protocol.
  • name: This is the name of the Service, primarily for DNS purposes.
  • type: This is the type mentioned earlier. If not specified, it defaults to ClusterIP.

Currently, our Redis is using the default type, ClusterIP, so it cannot be accessed directly from outside the cluster. We can use the port-forward method to access it from outside the cluster.

➜  ~ kubectl port-forward svc/redis-server 6379:6379
Forwarding from 127.0.0.1:6379 -> 6379
Forwarding from [::1]:6379 -> 6379
Handling connection for 6379

In another local terminal, we can connect using the redis-cli tool.

➜  ~ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> ping
PONG

Of course, we can also expose the service using the NodePort method.

➜  ~ kubectl expose deploy/redis --port=6379 --protocol=TCP --target-port=6379 --name=redis-server-nodeport --type=NodePort
service/redis-server-nodeport exposed
➜  ~ kubectl get service/redis-server-nodeport -o wide 
NAME                    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE       SELECTOR
redis-server-nodeport   NodePort   10.109.248.204   <none>        6379:31913/TCP   11s       run=redis

We can connect to our Redis service using port 31913 on any node. It’s worth noting that this port range can be configured using the service-node-port-range parameter of kube-apiserver, which defaults to 30000-32767.

Pod #

In the previous section, we mentioned that a Pod is the smallest deployable unit in Kubernetes. Let’s take a look at the status of the Pods in the current cluster.

➜  ~ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
redis-7c7545cbcb-jwcf2   1/1       Running   0          8h

Let’s perform a simple scaling operation.

➜  ~ kubectl scale deploy/redis --replicas=2
deployment.extensions/redis scaled
➜  ~ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
redis-7c7545cbcb-jwcf2   1/1       Running   0          8h
redis-7c7545cbcb-wzh6w   1/1       Running   0          4s

You can see that the number of Pods has increased, and they are now in the Running state. (Of course, in a production environment, scaling a Redis service is not done this way. Actual deployment and usage practices depend on the specific scenario.)

Summary #

In this section, we used Redis as an example to learn the basics of cluster management. We learned how to deploy applications, the basic types of Service, and how to provide external access to services through methods like port-forward or NodePort.

At the same time, we learned about several types of resources that are commonly involved in application deployment, such as Deployment, ReplicaSet, Service, and Pod. Mastering these resources and understanding their relationships will greatly help with cluster maintenance and problem troubleshooting.

In the next section, we will begin learning about a crucial aspect of using Kubernetes in a production environment: access control.