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 Label
s? 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 Pod
s 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 Pod
s. 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 ofService
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 requiredNodePort
orClusterIP
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 forkube-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 theService
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 theport
and flow to the port specified here based on the configuredprotocol
.name
: This is the name of theService
, primarily for DNS purposes.type
: This is the type mentioned earlier. If not specified, it defaults toClusterIP
.
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.