Working with services

The network service is an application that receives requests and provides a solution. Clients access the service by a network connection. They don't have to know the architecture of the service or how it runs. The only thing that clients have to verify is whether the end point of the service is contactable, and then follow its usage policy to solve problems. The Kubernetes service has similar ideas. It is not necessary to understand every pod before reaching their functionalities. For components outside the Kubernetes system, they just access the Kubernetes service with an exposed network port to communicate with running pods. It is not necessary to be aware of the containers' IPs and ports. Therefore, we can fulfill a zero downtime update for our container programs without struggling:

Working with services

The preceding image shows the basic structure of the service and realizes the following concepts:

  • As with the replication controller, the service directs the pods that have labels containing the service's selector. In other words, the pods selected by the service are based on their labels.
  • The load of requests sent to the services will distribute to four pods.
  • The replication controller ensures that the number of running pods meets its desired state. It monitors the pods for the service, making sure someone will take over duties from the service.

In this recipe, you will learn how to create services along with your pods.

Getting ready

Prior to applying services, it is important to verify whether all your nodes in the system are running kube-proxy. Daemon kube-proxy works as a network proxy in node. It helps to reflect service settings like IPs or ports on each node. To check whether kube-proxy is enabled or not, you can inspect the status of the daemon or search running processes on the node with a specific name:

// check the status of service kube-proxy 
# service kube-proxy status

or

// Check processes on each node, and focus on kube-proxy
// grep "kube-proxy" or "hyperkube proxy"
# ps aux | grep "kube-proxy"

For demonstration in later sections, you can also install a private network environment on the master node. The daemons related to the network settings are flanneld and kube-proxy. It is easier for you to do the operation and verification on a single machine. Otherwise, please verify Kubernetes services on a node, which by default, has an internal network ready.

How to do it…

We can define and create a new Kubernetes service through the CLI or a configuration file. Here we are going to explain how to deploy the services by command. The subcommands expose and describe are utilized in the following commands for various scenarios. The version of Kubernetes we used in this recipe is 1.1.3. For file-format creation, please go to Working with configuration files recipe in Chapter 3, Playing with Containers for a detailed discussion.

When creating services, there are two configurations with which we have to take care: one is the label, the other is the port. As the following image indicates, the service and pod have their own key-value pair labels and ports. Be assured to use correct tags for these settings:

How to do it…

To create a service like this one, push the following command:

# kubectl expose pod <POD_NAME> --labels="Name=Amy-log-service" --selector="App=LogParser,Owner=Amy" --port=8080 --target-port=80

The --labels tag expose in the subcommand is for labeling the services with key-value pairs. It is used to mark the services. For defining the selector of the service, use tag --selector. Without setting the selector for service, the selector would be the same as the labels of resource. In the preceding image, the selector would have an addition label: Version=1.0.

To expose the service port, we send out a port number with the tag --port in the subcommand expose. The service will take the container port number as its exposed port if no specific number is assigned. On the other hand, the tag --target-port points out the container port for service. While the target port is different from the exposed port of the container, users will get an empty response. At the same time, if we only assign the service port, the target port will copy it. Taking the previous image as an example, the traffic will be directed to container port 8080 supposing we don't use the tag --target-port, which brings out a refused connection error.

Creating services for different resources

You can attach a service to a pod, a replication controller and an endpoint outside the Kubernetes system, or even another service. We will show you these, one by one, in the next pages. The service creation is in the format: kubectl expose RESOURCE_TYPE RESOURCE_NAME [TAGS] or kubectl expose -f CONFIGURATION_FILE [TAGS]. Simply put, the resource types pod, replication controller and service are supported by the subcommand expose. So is the configuration file which follows the type limitation.

Creating a service for a pod

The pods shielded by the service need to contain labels, because the service takes this as a necessary condition based on the selector:

// Create a pod, and add labels to it for the selector of service.
# kubectl run nginx-pod --image=nginx --port=80 --restart="Never" --labels="app=nginx"
pod "nginx-pod" created
# kubectl expose pod nginx-pod --port=8000 --target-port=80 --name="service-pod"
service "service-pod" exposed

Tip

The abbreviation of Kubernetes resources

While managing resources through CLI, you can type their abbreviations instead of the full names to save time and avoid typing errors.

Resource type

Abbreviated alias

Componentstatuses

cs

Events

ev

Endpoints

ep

Horizontalpodautoscaler

hpa

Limitranges

limits

Nodes

no

Namespaces

ns

Pods

po

Persistentvolumes

pv

Persistentvolumesclaims

pvc

Resourcequotas

qotas

Replicationcontrollers

rc

Services

svc

Ingress

ing

// "svc" is the abbreviation of service
# kubectl get svc service-pod
NAME          CLUSTER_IP        EXTERNAL_IP   PORT(S)    SELECTOR    AGE
service-pod   192.168.195.195   <none>        8000/TCP   app=nginx   11s

As you see in these commands, we open a service with port 8000 exposed. The reason why we specify the container port is so that the service doesn't take 8000 as the container port, by default. To verify whether the service is workable or not, go ahead with the following command in an internal network environment (which has been installed with the Kubernetes cluster CIDR).

// accessing by services CLUSTER_IP and PORT
# curl 192.168.195.195:8000

Creating a service for the replication controller and adding an external IP

A replication controller is the ideal resource type for a service. For pods supervised by the replication controller, the Kubernetes system has a controller manager to look over the lifecycle of them. It is also helpful for updating the version or state of program by binding existing services to another replication controller:

// Create a replication controller with container port 80 exposed
# kubectl run nginx-rc --image=nginx --port=80 --replicas=2
replicationcontroller "nginx-rc" created
# kubectl expose rc nginx-rc --name="service-rc" --external-ip="<USER_SPECIFIED_IP>"
service "service-rc" exposed

In this case, we can provide the service with another IP address, which doesn't need to be inside the cluster network. The tag --external-ip of the subcommand expose can realize this static IP requirement. Be aware that the user-specified IP address could be contacted, for example, with the master node public IP:

// EXTERNAL_IP has Value shown on
# kubectl get svc service-rc
NAME         CLUSTER_IP      EXTERNAL_IP          PORT(S)     SELECTOR       AGE
service-rc   192.168.126.5   <USER_SPECIFIED_IP>  80/TCP    run=nginx-rc   4s

Now, you can verify the service by 192.168.126.5:80 or <USER_SPECIFIED_IP>:80:

// Take a look of service in details
# kubectl describe svc service-rc
Name:      service-rc
Namespace:    default
Labels:      run=nginx-rc
Selector:    run=nginx-rc
Type:      ClusterIP
IP:      192.168.126.5
Port:      <unnamed>  80/TCP
Endpoints:    192.168.45.3:80,192.168.47.2:80
Session Affinity:  None
No events.

You will find that the label and selector of a service is the default of the replication controller. In addition, there are multiple endpoints, which are replicas of the replication controller, available for dealing with requests from the service.

Creating a no-selector service for an endpoint

First, you should have an endpoint with an IP address. For example, we can generate an individual container in an instance, where it is located outside our Kubernetes system but is still contactable:

// Create an nginx server on another instance with IP address <FOREIGN_IP>
# docker run -d -p 80:80 nginx
2a17909eca39a543ca46213839fc5f47c4b5c78083f0b067b2df334013f62002 
# docker ps
CONTAINER ID        IMAGE                                  COMMAND                  CREATED             STATUS              PORTS                         NAMES
2a17909eca39        nginx                                  "nginx -g 'daemon off"   21 seconds ago      Up 20 seconds       0.0.0.0:80->80/tcp, 443/tcp   goofy_brown

Then, in the master, we can create a Kubernetes endpoint by using the configuration file. The endpoint is named service-foreign-ep. We could configure multiple IP addresses and ports in the template:

# cat nginx-ep.json
{
    "kind": "Endpoints",
    "apiVersion": "v1",
    "metadata": {
        "name": "service-foreign-ep"
    },
    "subsets": [
        {
            "addresses": [
                { "ip": "<FOREIGN_IP>" }
            ],
            "ports": [
                { "port": 80 }
            ]
        }
    ]
}
# kubectl create -f nginx-ep.json
endpoints "service-foreign-ep" created
# kubectl get ep service-foreign-ep
NAME                    ENDPOINTS                         AGE
service-foreign-ep      <FOREIGN_IP>:80                   16s

As mentioned in the previous section, we can start a service for a resource-configured template with the subcommand expose. However, the CLI is unable to support exposing an endpoint in the file format:

// Give it a try!
# kubectl expose -f nginx-ep.json
error: invalid resource provided: Endpoints, only a replication controller, service or pod is accepted

Therefore, we create the service through a configuration file:

# cat service-ep.json
{
    "kind": "Service",
    "apiVersion": "v1",
    "metadata": {
        "name": "service-foreign-ep"
    },
    "spec": {
        "ports": [
            {
                "protocol": "TCP",
                "port": 80,
                   "targetPort" : 80
            }
        ]
    }
}

The most important thing of all is that there is no selector defined in the template. This is quite reasonable since the endpoints are not in the Kubernetes system. The relationship between endpoints and service is built up by resource name. As you can see, the name of the service must be identical to the name of the endpoint:

# kubectl create -f service-ep.json
service "service-foreign-ep" created
// Check the details in service
# kubectl describe svc service-foreign-ep
Name:      service-ep
Namespace:    default
Labels:      <none>
Selector:    <none>
Type:      ClusterIP
IP:      192.168.234.21
Port:      <unnamed>  80/TCP
Endpoints:    <FOREIGN_IP>:80
Session Affinity:  None
No events.

Finally, the no-selector service is created for the external endpoint. Verify the result with <FOREIGN_IP>:80.

Creating a service with session affinity based on another service

Through the subcommand expose, we can also copy the settings of one service to another:

// Check the service we created for replication controller in previous section
# kubectl describe svc service-rc
Name:      service-rc
Namespace:    default
Labels:      run=nginx-rc
Selector:    run=nginx-rc
Type:      ClusterIP
IP:      192.168.126.5
Port:      <unnamed>  80/TCP
Endpoints:    192.168.45.3:80,192.168.47.2:80
Session Affinity:  None
No events.
//Create a new service with different name and service port
# kubectl expose svc service-rc --port=8080 --target-port=80 --name=service-2nd --session-affinity="ClientIP"
service "service-2nd" exposed

The new service named service-2nd is reset with service port 8080 and session affinity is enabled:

# kubectl describe svc service-2nd
Name:      service-2nd
Namespace:    default
Labels:      run=nginx-rc
Selector:    run=nginx-rc
Type:      ClusterIP
IP:      192.168.129.65
Port:      <unnamed>  8080/TCP
Endpoints:    192.168.45.3:80,192.168.47.2:80
Session Affinity:  ClientIP
No events.

Currently, the ClientIP is the only valued setting for the tag --session-affinity. While session affinity to the ClientIP is enabled, instead of round robin, the request of which endpoint the service should be sent to would be decided by the ClientIP. For example, if the requests from the client in the CIDR range 192.168.45.0/24 are sent to service service-2nd, they will be transferred to the endpoint 192.168.45.3:80.

Creating a service in a different type

There are three types of service: ClusterIP, NodePort and LoadBalancer:

Creating a service in a different type

By default, every service is created as a ClusterIP type. The service in the ClusterIP type would be assigned an internal IP address randomly. For the NodePort type, it covers the ClusterIP's feature, and also allows the user to expose services on each node with the same port. The LoadBalancer is on the top of the other two types. The LoadBalancer service would be exposed internally and on the node. Besides this, if your cloud provider supports external load balancing servers, you can bind the load balancer IP to the service and this will become another exposing point.

Creating a service in NodePort type

Next, we are going to show you how to create a NodePort service. The tag --type in the subcommand expose helps to define the service type:

// Create a service with type NodePort, attaching to the replication controller we created before
# kubectl expose rc nginx-rc --name=service-nodeport --type="NodePort"
service "service-nodeport" exposed
# kubectl describe svc service-nodeport
Name:      service-nodeport
Namespace:    default
Labels:      run=nginx-rc
Selector:    run=nginx-rc
Type:      NodePort
IP:      192.168.57.90
Port:      <unnamed>  80/TCP
NodePort:    <unnamed>  31841/TCP
Endpoints:    192.168.45.3:80,192.168.47.2:80
Session Affinity:  None
No events.

In the preceding case, the network port 31841 exposed on a node is randomly assigned by the system; the default port range is 30000 to 32767. Notice that the port is exposed on every node in the system, so it is fine to access the service through <NODE_IP>:31841, for example, through the domain name of a node, like kube-node1:31841.

Deleting a service

You can simply work with the subcommand delete in cases where you want to stop a service:

# kubectl delete svc <SERVICE_NAME>
service "<SERVICE_NAME>" deleted

How it works…

The main actors in the Kubernetes system that perform the service environment are flanneld and kube-proxy. Daemon flanneld builds up a cluster network by allocating a subnet lease out of a preconfigured address space, and storing the network configuration in etcd, while kube-proxy directs the endpoints of services and pods.

See also

To get the best use of services, the following recipes are suggested to be read as well:

  • Working with a replication controller
  • Working with labels and selectors
  • The Working with configuration files recipe in Chapter 3, Playing with Containers
  • The Moving monolithic to microservices recipe in Chapter 5, Building a Continuous Delivery Pipeline
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset