Kubernetes made easy: Develop and deploy on a cloud cluster


Part 2: Kubernetes

Table of Contents
Part 1: Basis
Part 2: Kubernetes
Part 3: Ingress Controller
Part 4: Design & Conclusions

Kubernetes: Deploying on a cloud cluster

So far we only ran those commands locally. You could set up a dedicated server with a single Nginx reverse-proxying to various running web service and it'd already allow you to deploy exactly what you tested locally on any machine rapidly and manage the software stack. Let's see how we can deploy them on a set of machines in the cloud so that we have high availability and scalability.

Kubernetes can initially be quite daunting but especially since version 1.2 (and later) it's actually not that hard once you've got familiar with Docker. Installing it properly isn't trivial so we'll make use of Google Container Engine (GKE). You'll have to create an account and set up billing. The good news is that there is now a free tier. Also to get an idea of the actual cost if you want a beefier config, see a standard small cluster would cost about $ 70 / month):

  1. Go to Container Engine on Google Cloud
  2. Click "Create container cluster" button from withing "Container clusters" section and use any machine spec you seem fit

Congratulation you're now running one or more machines all set up for Kubernetes along with a private Docker Registry. A Docker Registry is a place where you can push/pull Docker images so that other people or machines can use them (this one is private so that you, people and machines you've authorized may access it).

Overview of Kuberentes work units

Kubernetes work units overview UML diagram

Example code

kubernetes.yml


apiVersion: v1
kind: List
items:
  - apiVersion: v1
    kind: Service
    metadata:
      name: frontend
    spec:
      type: LoadBalancer
      loadBalancerIP: 10.10.10.10
      ports:
        - port: 80
      selector:
        name: frontend-pod
  - apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: frontend-deployment
    spec:
      replicas: 2
      revisionHistoryLimit: 3  # Clean-up old Replica Sets deployed; only keep previous 3.
      # Pod template (contains same fields as a normal Pod)
      template:
        metadata:
          labels:
            name: frontend-pod
        spec:
          containers:
            - name: nginx
              image: eu.gcr.io/my-google-cloud-project-id/my-frontend-image:v1
              ports:
                - containerPort: 80
  - apiVersion: v1
    kind: Service
    metadata:
      name: db
    spec:
      ports:
        - port: 3306
      selector:
        name: mysql-pod
  - apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: mysql-deployment
    spec:
      strategy:
        # We need to kill the existing Pod before creating an new one
        # for the new one to be able to attach the persistent disk.
        type: Recreate
      replicas: 1
      revisionHistoryLimit: 3
      # Pod template (contains same fields as a normal Pod)
      template:
        metadata:
          labels:
            name: mysql-pod
        spec:
          containers:
            - name: mysql
              image: mysql:5.7.8
              env:
                - name: MYSQL_RANDOM_ROOT_PASSWORD
                  value: 'yes'
              ports:
                - containerPort: 3306
              volumeMounts:
                - name: mysql-persistent-storage
                  mountPath: /var/lib/mysql
          volumes:
            - name: mysql-persistent-storage
              gcePersistentDisk:
                pdName: my-database-disk
                fsType: ext4

Note: Instead of using --- to have the equivalent of multiple Yaml in a single file, we use kind: List which allows to use Yaml features accross the entire file.

It may look like a lot but it's not that complex; let's break it down:

  • We have two Kubernetes Deployments which is a way to ask Kubernetes to start containers that are expected to continuously execute. In Kubernetes, Docker containers run inside Pods which is a logical machine (may or not be a phyiscal machine): You’ll want to group your containers that need to be collocated by having them on the same Pod (a logical host), so they’d share:
    • Volumes (i.e. hard disk)
    • Network namespace (i.e. can connect via localhost)
    • Memory
  • We have two Kubernetes Services; they allow to connect from one or more Pods to a given set of Pods (matching labels). For example here, from within a Docker container running on your Kubernetes cluster, the host db will point to the Pod mysql-pod on which runs the MySQL container; any its port 3306 will be accessible. Also we're running two Nginx possibly on two machines, but you can access any of them that is up and running by accessing the host frontend. That frontend Service is a LoadBalancer: It'll create an external IP that resolves the service. This will be the only way to access your containers from the public internet (i.e. outside your cluster).
  • Last but not least, we have asked to mount a persistent disk which will allow us to persist the database even after upgrading. Google Compute Engine Disks (GCD) are encrypted by default and they are backed up and costs about $0.4 for 10 GB each month.

We need to create that disk before we can use it:

  1. Go to Developers Console > Compute > Compute Engine > Disk
  2. Click on "Create disk":
    1. Name: my-database-disk
    2. Zone: Same zone as your Kubernetes cluster
    3. Disk Type: Any
    4. Source type: None (blank disk)
    5. Size: Whatever your need

We also need to have a static IP so that we can set up DNS provider and have our domain point to it:

  1. Go to Developers Console > Networking > External IP addresses
  2. Click on "Reserve static address":
    1. Name: Any
    2. Type: Regional
    3. Region: Same zone as your Kubernetes cluster
    4. Attach to: None

Now note that assigned External Address and replace 10.10.10.10 above by that assigned IP.

Let's deploy everything (after installing kubectl, Kubernetes's client, and gcloud, Google Cloud's client):

curl -L https://sdk.cloud.google.com | bash
gcloud components update kubectl
gcloud auth login
gcloud config set project my-google-cloud-project-id
gcloud container clusters get-credentials cluster-name

docker tag my-frontend-image eu.gcr.io/my-google-cloud-project-id/my-frontend-image:v1
gcloud docker -- push eu.gcr.io/my-google-cloud-project-id/my-frontend-image:v1
kubectl apply -f kubernetes.yml

Checking deployment status

Most commands return immediately, you'll have to check deployment status yourself, example:

kubectl get deployment,pods,services
NAME                             DESIRED          CURRENT           UP-TO-DATE          AVAILABLE   AGE
frontend-deployment              2                2                 2                   2           3m
mysql-deployment                 1                1                 1                   1           3m
NAME                                        READY            STATUS            RESTARTS            AGE
frontend-deployment-0000000000-00001        1/1              Running           0                   3m
frontend-deployment-0000000000-00002        1/1              Running           0                   3m
mysql-deployment-0000000000-00001           1/1              Running           0                   3m
NAME                             CLUSTER-IP       EXTERNAL-IP       PORT(S)             AGE
frontend                         10.100.200.10    10.10.10.10       80/TCP              3m
mysql                            10.100.200.20                3306/TCP            3m
kubectl describe pod frontend-deployment-0000000000-00001
...
kubectl logs frontend-deployment-0000000000-00001
...
kubectl logs -p frontend-deployment-0000000000-00001
...

Deploying upgrades

Let's say some time later we changed some files locally and want to deploy a new image:

  1. Replace in kubernetes.yml the :v1 tag by :v2 (or anything else).
  2. Then run:
    docker-compose build --pull
    docker tag my-frontend-image eu.gcr.io/my-google-cloud-project-id/my-frontend-image:v2
    gcloud docker -- push eu.gcr.io/my-google-cloud-project-id/my-frontend-image:v2
    kubectl apply -f kubernetes.yml

We covered the basis. Let's see some more importants features of Kubernetes.

Next part Ingress Controller

Parts: Previous1, 2, 34Next