Mirantis Acquires Docker Enterprise Platform Business

LEARN MORE

Introduction to YAML, Part 2: Kubernetes Services, Ingress, and repeated nodes

In part 1 of this series, we looked at the basics behind YAML and showed you how to create basic Kubernetes objects such as Pods and Deployments using the two basic structures of YAML, Maps and Lists. Now we’re going to look at enhancing your YAML documents with repeated nodes in the context of Kubernetes Services, Endpoints, and Ingress.

Let’s start with a basic scalar value.

A simple repeated scalar value in YAML: building a Kubernetes Service

To see how we can create a simple repeated value, we’re going to look at Kubernetes Services. A complete look at Services is beyond the scope of this article, but there are three basic things you need to understand:

  1. Services are how pods communicate, either with either other or with the outside world. They do this by specifying a port for the caller to use, and a targetPort, which is the port on which the Pod itself receives the message.
  2. Services know which pods to target based on labels specified in the selector.
  3. Services come in four different types:
    1. ClusterIP: The default ServiceType, a ClusterIP service makes the service reachable from within the cluster via a cluster-internal IP.
    2. NodePort: A NodePort service makes it possible to access a Service by directing requests to a specific port on every Node, accessed via the NodeIP. (Kubernetes automatically creates a ClusterIP service to route the request.) So from outside the cluster, you’d send the request to <NodeIP>:<NodePort>.
    3. LoadBalancer: In order to use a LoadBalancer service, you have to be using a cloud provider that supports it; it’s the cloud provider that actually makes this functionality available. This service sits on top of NodePort and ClusterIP services, which Kubernetes creates automatically.
    4. ExternalName: In production situations, you will likely want to use ExternalName, which maps the service to a CNAME record such as a Fully Qualified Domain Name.

OK, with the basics under our belt, let’s take a look at actually creating one.

Repeated values with anchors and aliases

In part 1, we covered the basics of creating Kubernetes objects using YAML, and creating  a Service is no different.

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    name: http
    targetPort: 80
  - port: 443
    name: https
    targetPort: 80

As you can see, we’re creating an object just as we did in Part 1, with metadata and a spec. Metadata is the same as it was when we were dealing with Deployments, in that we are specifying information about the object and adding labels to any instances created.

As for the spec, a Service needs two basic pieces of information: a selector, which identifies Pods that it should work with (in this case, any pods with the label app=nginx) and the ports the service manages. In this case, we have two external ports, both of which get forwarded to port 80 of the actual pod.

So let’s make this more convenient.  We can create an anchor that specifies a value, then use an alias to reference that anchor.  For example:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
  - port: &target 80
    name: http
    targetPort: *target
  - port: 443
    name: https
    targetPort: *target

We create the anchor with the ampersand (&), as in &target, then reference it with the alias created with the asterisk (*), as in *target.  If we were to put this into a file and create it using kubectl, we would get a new Service, as we can see:

$ kubectl get svc
NAME         TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP  10.96.0.1     <none>        443/TCP          32d
nginx        ClusterIP  10.107.206.48 <none>        80/TCP,443/TCP   13m

If we then went on to describe the service, we could see that the values carried through:

$ kubectl describe svc nginx
Name:           nginx
Namespace:      default
Labels:         app=nginx
Annotations:    kubectl.kubernetes.io/last-applied-configuration:
                  {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx","namespace":"default"},"spec":{"p...
Selector:       app=nginx
Type:           ClusterIP
IP:             10.107.206.48
Port:           http  80/TCP
TargetPort:     80/TCP
Endpoints:      <none>
Port:           https  443/TCP
TargetPort:     80/TCP
Endpoints:      <none>
Session Affinity:  None
Events:         <none>


Now if we wanted to change that port, we could do it simply by changing the anchor:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
  - port: &target 88
    name: http
    targetPort: *target
  - port: 443
    name: https
    targetPort: *target

We can then apply the changes…

$ kubectl apply -f test.yaml
service/nginx configured

… and look at the newly configured service:

$ kubectl describe svc nginx
Name:           nginx
Namespace:      default
Labels:         app=nginx
Annotations:    kubectl.kubernetes.io/last-applied-configuration:
                  {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx"},"name":"nginx","namespace":"default"},"spec":{"p...
Selector:       app=nginx
Type:           ClusterIP
IP:             10.107.206.48
Port:           http  88/TCP
TargetPort:     88/TCP
Endpoints:      <none>
Port:           https  443/TCP
TargetPort:     88/TCP
Endpoints:      <none>
Session Affinity:  None
Events:         <none>

As you can see, all three values were changed by simply changing the anchor. Handy, but fortunately, we can also create anchors for more complicated structures.

Anchors for non-scalars: Creating Endpoints

Endpoints are, as in other applications, the target to which you’ll send your requests in order to access an application. Kubernetes creates them automatically, but you can also create them manually and link them to a specific service.  For example:

apiVersion: v1
kind: Endpoints
metadata:
  name: mytest-cluster
subsets:
  - addresses:
      - ip: 192.168.10.100
    ports:
      - name: myport
        port: 1
        protocol: TCP
  - addresses:
      - ip: 192.168.10.101
    ports:
      - name: myport
        port: 1
        protocol: TCP
  - addresses:
      - ip: 192.168.10.102
    ports:
      - name: myport
        port: 1
        protocol: TCP

As you can see, what you have here is the basic structure, only instead of a spec, we have subsets, each of which consists of one or more IP addresses and the ports to access them.

So now let’s look at creating an anchor out of one of those port definitions:

apiVersion: v1
kind: Endpoints
metadata:
  name: mytest-cluster
subsets:
  - addresses:
   - ip: 192.168.10.100
    ports: &stdport
      - name: myport
        port: 1
        protocol: TCP
  - addresses:
      - ip: 192.168.10.101
    ports: *stdport
  - addresses:
      - ip: 192.168.10.102
    ports: *stdport

If we describe the endpoints we can see that they’ve been created as we expect:

$ kubectl describe endpoints mytest-cluster
Name:      mytest-cluster
Namespace: default
Labels:    <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
             {"apiVersion":"v1","kind":"Endpoints","metadata":{"annotations":{},"name":"mytest-cluster","namespace":"default"},"subsets":[{"addresses":...
Subsets:
  Addresses:       192.168.10.100,192.168.10.101,192.168.10.102
  NotReadyAddresses:  <none>
  Ports:
Name Port  Protocol
---- ----  --------
myport  1  TCP

Events:  <none>

But when you’re using an alias for a structure such as this, you’ll often want to change a specific value and leave the rest intact.  We’ll do that next.

Changing a specific value: Kubernetes Ingress

In this final section, we’ll look at creating a Kubernetes Ingress, which makes it simpler to create access to your applications. We’ll also look at another aspect of using aliases.

In the previous section we looked at replacing entire objects with an alias, but sometimes you want to do that with slight changes.  For example, we might have an Ingress that looks like this:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
        - path: /testpath
          backend: &stdbe
            serviceName: test
            servicePort: 80
        - path: /realpath
          backend: *stdbe
        - path: /hiddenpath
          backend: *stdbe

In this case, we have three paths that all point to the same service on the same port.  But what if we want to have one path that points to another port? To do that we want to override one of the existing values, like so:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
    paths:
      - path: /testpath
        backend: &stdbe
          serviceName: test
          servicePort: 80
      - path: /realpath
        backend: *stdbe
      - path: /hiddenpath
        backend:
          << : *stdbe
          servicePort: 443

Now, a couple of things to note here. First off, the alias represents a value, so it has to have a name. We can’t use backend as the name, because we need *stdbe down one level so that we can replace servicePort.  So to reference the fact that we’re going up one level, we’re using the << notation. Then we can add another servicePort value to the same level of the hierarchy.

Now if we go ahead and apply this YAML, we can see the results:

$ kubectl apply -f test.yaml
ingress.extensions/test-ingress configured

$ kubectl describe ingress test-ingress
Name:          test-ingress
Namespace:     default
Address:     
Default backend:  default-http-backend:80 (<none>)
Rules:
  Host  Path        Backends
  ----  ----        --------
  *
        /testpath   test:80 (<none>)
        /realpath   test:80 (<none>)
        /hiddenpath test:443 (<none>)
Annotations:
  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"nginx.ingress.kubernetes.io/rewrite-target":"/"},"name":"test-ingress","namespace":"default"},"spec":{"rules":[{"http":{"paths":[{"backend":{"serviceName":"test","servicePort":80},"path":"/testpath"},{"backend":{"serviceName":"test","servicePort":80},"path":"/realpath"},{"backend":{"serviceName":"test","servicePort":443},"path":"/hiddenpath"}]}}]}}
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:                                     <none>

So that’s anchors and aliases. If you want more information on YAML, including using specific data types, feel free to check out this webinar on YAML and Kubernetes objects.

 

LIVE DEMO
How to Use Service Mesh with VMs and Containers
REGISTER