In this document, I try to demonstrate a use case of kubernetes to deploy a simple static web page. 

The network in kubernetes  usually will have a topology like the figure below. In this example, we will start from the bottom, which are pods, and will build up the network.


Example network topology (image from this article)


Step 0: have a webserver docker image ready with the contents of your web page

  • There are various ways to deploy web server on the k8s. Here we assume that you have a docker image of the web server and all the contents of your pages. 
  • We will use "registry.kasi.re.kr/uwife/uwife_www" that I (Jae-Joon Lee) have personally created.
  • The image is based on nginx web server and the copy of the web pages I will serve.
  • The Dockerfile and all its contents are available at https://data.kasi.re.kr/gitlab/leejjoon/uwife-www

Step 1: Deployment

  • The "deployment" resource is often used when you want scalable number of pods.


deployment_uwife_www.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: uwife-www
spec:
  replicas: 1
  selector:
    matchLabels:
      app: uwife-www
  template:
    metadata:
      labels:
        app: uwife-www
    spec:
      containers:
      - name: uwife-www
        image: registry.kasi.re.kr/uwife/uwife_www
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get pods  
No resources found in default namespace.
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get deployments
No resources found in default namespace.
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k apply -f deployment_uwife_www.yaml
deployment.apps/uwife-www created
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get deployments
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
uwife-www   0/1     1            0           7s
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get pods
NAME                         READY   STATUS    RESTARTS   AGE
uwife-www-79b5cc8898-l585l   1/1     Running   0          17s
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get deployments
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
uwife-www   1/1     1            1           20s


  • We see that it has created a pod with an internal ip of 10.100.95.194.
  • The ip is only accessible inside the k8s cluster (including, master and worker nodes).
  • To test, lets login to the master node.
  • Note that to login to the master (or worker nodes), you need to set the ssh-key properly while creating the cluster. You can 

## Let's find out the ip address of the master node and ssh in to it.

ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get nodes -o wide|grep master
spherex-k8s-10-n2fw4cjrwsza-master-0   Ready    master   24h     v1.18.2   10.0.1.125    <none>        Fedora CoreOS 32.20201004.3.0   5.8.12-200.fc32.x86_64   docker://19.3.11
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ ssh core@10.0.1.125
Enter passphrase for key '/home/ubuntu/.ssh/id_rsa':  
Fedora CoreOS 32.20201004.3.0
Tracker: https://github.com/coreos/fedora-coreos-tracker
Discuss: https://discussion.fedoraproject.org/c/server/coreos/

Last login: Wed Jun  8 09:38:15 2022 from 10.0.1.58


## You are now logged into the master node, and you can check the connection to the pod with a simple curl command.

 
[core@spherex-k8s-10-n2fw4cjrwsza-master-0 ~]$ curl -I 10.100.95.194
HTTP/1.1 200 OK
Server: nginx/1.22.0
Date: Wed, 08 Jun 2022 10:44:05 GMT
Content-Type: text/html
Content-Length: 35
Last-Modified: Fri, 30 May 2014 06:14:51 GMT
Connection: keep-alive
ETag: "538821db-23"
Accept-Ranges: bytes

Step 2: Create a service for external connection.

  • To access the pod from outside, you need to create a service resource for it.
  • Here we will use "NodePort" type service. With the "NodePort" service, you will create a connection from a port in any of the worker nodes to the pod.


service_uwife_www.yaml
apiVersion: v1
kind: Service
metadata:
  name: uwife-www
spec:
  type: NodePort
  selector:
    app: uwife-www
  ports:
  - port: 80
    targetPort: 80

ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.254.0.1   <none>        443/TCP   24h
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k apply -f service_uwife_www.yaml  
service/uwife-www created
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.254.0.1       <none>        443/TCP        24h
uwife-www    NodePort    10.254.132.132   <none>        80:31349/TCP   4s


  • Now, if you can reach any of the k8s nodes (only the worker nodes), you can reach the pod with the port number 31349
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get nodes              
NAME                                   STATUS   ROLES    AGE     VERSION
spherex-k8s-10-n2fw4cjrwsza-master-0   Ready    master   24h     v1.18.2
spherex-k8s-10-n2fw4cjrwsza-node-0     Ready    <none>   24h     v1.18.2
spherex-k8s-10-n2fw4cjrwsza-node-1     Ready    <none>   24h     v1.18.2
spherex-k8s-10-n2fw4cjrwsza-node-2     Ready    <none>   24h     v1.18.2
spherex-k8s-10-n2fw4cjrwsza-node-3     Ready    <none>   7h14m   v1.18.2
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ curl -I 10.0.1.223:31349
HTTP/1.1 200 OK
Server: nginx/1.22.0
Date: Wed, 08 Jun 2022 11:01:17 GMT
Content-Type: text/html
Content-Length: 35
Last-Modified: Fri, 30 May 2014 06:14:51 GMT
Connection: keep-alive
ETag: "538821db-23"
Accept-Ranges: bytes

  • Similarly, you can assign a virtual ip by creating a "LoadBalancer" service.
  • But, for now, the loadbalancer requires floating ip assigned to it, which can limit its usage.
  • Instead, you can use Ingress resources. It can create a internal vip and/or use same ip for different services.

Step 3: Create am Ingress for external connection with routing.


ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: default-internal-ingress
  annotations:
    kubernetes.io/ingress.class: "openstack"
    octavia.ingress.kubernetes.io/internal: "true"
spec:
 rules:
 - host: k8s-10.gems0.org
   http:
     paths:
     - path: /
       pathType: Prefix
       backend:
         serviceName: uwife-www
         servicePort: 80

  • Note that on line 7, we specify "internal" as true. Thus it won't try to allocate external floating ip.
  • We also says the if the incoming traffic haas a target Host name of "k8s-10.gems0.org", the traffic will be forwarded to the "uwife-www" service.
  • We are using "octavia" ingress controller for openstack, and it only allows NodePort service as a backend.
  • Once you apply that yaml file to create the ingress object, you will see a ip address will be assigned (this can take several seconds). Note that this will automatically create an loadbalancer object in the openstack.
  • And your web page accessible with the allocated ip (10.0.1.156 in this case), but the Header should specify the host name of k8s-10.gems0.org.
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get ing
NAME                       CLASS    HOSTS              ADDRESS          PORTS   AGE
default-internal-ingress   <none>   k8s-10.gems0.org   10.0.1.156       80      87m
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ curl -I -H "Host: k8s-10.gems0.org" 10.0.1.156
HTTP/1.1 200 OK
Server: nginx/1.22.0
Date: Wed, 08 Jun 2022 11:29:10 GMT
Content-Type: text/html
Content-Length: 35
Last-Modified: Fri, 30 May 2014 06:14:51 GMT
ETag: "538821db-23"
Accept-Ranges: bytes


  • Similarly, you can create an ingress object with an external floating ip. However, in this case, you can access multiple services by having different rules for different services (e.g., different host name)
ingress-external
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: default-external-ingress
  annotations:
    kubernetes.io/ingress.class: "openstack"
    octavia.ingress.kubernetes.io/internal: "false"
spec:
 rules:
 - host: k8s-10.gems0.org
   http:
     paths:
     - path: /
       pathType: Prefix
       backend:
         serviceName: uwife-www
         servicePort: 80

  • The only change we made (other than the name) is t set "internal" as false in line 7. Thus it will try to allocate the floating ip.
  • In this case, the floating ip of "210.219.33.217" is assigned to this ingress object.
  • Note that you can add multiple rules to the above yaml file and reapply to have this ingress object to forward your traffic to multiple services.


ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get ing
NAME                       CLASS    HOSTS              ADDRESS          PORTS   AGE
default-external-ingress   <none>   k8s-10.gems0.org   210.219.33.217   80      88m
default-internal-ingress   <none>   k8s-10.gems0.org   10.0.1.156       80      100m
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ curl -I -H "Host: k8s-10.gems0.org" 210.219.33.217
HTTP/1.1 200 OK
Server: nginx/1.22.0
Date: Wed, 08 Jun 2022 11:36:33 GMT
Content-Type: text/html
Content-Length: 35
Last-Modified: Fri, 30 May 2014 06:14:51 GMT
ETag: "538821db-23"
Accept-Ranges: bytes

  • Now, change your DNS seeting so that the host name of "k8s-10.gems0.org" to the ip of 210.219.33.217.
  • We assume you have your own domain you can manipulate. If not, you may test it with nip hostname (https://nip.io/). In this case, duplicate the host rule in the yaml with the host name replaced with "210-219-33-217.nip.io"
  • Once the DNS is set, you can access the side with the host name.


ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ nslookup k8s-10.gems0.org
Server:         127.0.0.53
Address:        127.0.0.53#53

Non-authoritative answer:
k8s-10.gems0.org        canonical name = k8s-ing217.gems0.org.
Name:   k8s-ing217.gems0.org
Address: 210.219.33.217

ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ curl -I k8s-10.gems0.org
HTTP/1.1 200 OK
Server: nginx/1.22.0
Date: Wed, 08 Jun 2022 11:46:32 GMT
Content-Type: text/html
Content-Length: 35
Last-Modified: Fri, 30 May 2014 06:14:51 GMT
ETag: "538821db-23"
Accept-Ranges: bytes

  • And now you can open the page in your web browser.
  • Of course, the site is only accessible from the KAS intranet because of the KASAI firewall. If you want allow public access, you need to talk to the KASI computer management team to allow inbound traffic on that ip address.

  • You can setup a ssl certificate for your site and make HTTPS connection accessible, but this won't be covered here.


Step 4: Scale out your web page.

  • If your web page become super popular, a single web server cannot handle your traffic.
  • The virtue of kubernetes is its ease of scaling out.
  • You simply change the "replicas" number (line 6) and reapply. And you will have multiple pods serving your web page.


deployment_uwife_www.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: uwife-www
spec:
  replicas: 3
  selector:
    matchLabels:
      app: uwife-www
  template:
    metadata:
      labels:
        app: uwife-www
    spec:
      containers:
      - name: uwife-www
        image: registry.kasi.re.kr/uwife/uwife_www

# We start with a single pod.

ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get pods
NAME                         READY   STATUS    RESTARTS   AGE
uwife-www-79b5cc8898-l585l   1/1     Running   0          77m
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ ls
deployment_uwife_www.yaml  ingress-flaoting-ip.yaml  ingress.yaml  service_uwife_www.yaml
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ vim deployment_uwife_www.yaml


## Edit the YAML file to increase the replica number and reapply.

 
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k apply -f deployment_uwife_www.yaml
deployment.apps/uwife-www configured


## Wait for several seconds for the pods to start up.


ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get deployment
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
uwife-www   2/3     3            2           77m
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get deployment
NAME        READY   UP-TO-DATE   AVAILABLE   AGE
uwife-www   3/3     3            3           77m
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get pods
NAME                         READY   STATUS    RESTARTS   AGE
uwife-www-79b5cc8898-2sdkj   1/1     Running   0          14s
uwife-www-79b5cc8898-b67gb   1/1     Running   0          14s
uwife-www-79b5cc8898-l585l   1/1     Running   0          77m

## You now have 3 pods serving your web pages.

## The ingress and service objects you created will automatically divide the traffic to each pod.


  • Note that the service object you created will automatically divide the traffic to each pod.
  • In the example below, note the uwife-www service now have 3 endpoints pointing each pods.
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k get pods -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE                                 NOMINATED NODE   READINESS GATES
uwife-www-79b5cc8898-2sdkj   1/1     Running   0          19m   10.100.64.131    spherex-k8s-10-n2fw4cjrwsza-node-2   <none>           <none>
uwife-www-79b5cc8898-b67gb   1/1     Running   0          19m   10.100.205.195   spherex-k8s-10-n2fw4cjrwsza-node-1   <none>           <none>
uwife-www-79b5cc8898-l585l   1/1     Running   0          96m   10.100.95.194    spherex-k8s-10-n2fw4cjrwsza-node-3   <none>           <none>
ubuntu@spherex-gw:~/git/k8s-leejjoon-test/simple_webserver_uwife$ k describe svc uwife-www
Name:                     uwife-www
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=uwife-www
Type:                     NodePort
IP Families:              <none>
IP:                       10.254.132.132
IPs:                      <none>
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  31349/TCP
Endpoints:                10.100.205.195:80,10.100.64.131:80,10.100.95.194:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

  • No labels