In this chapter, we will set up a Kubernetes cluster, Container Registry, and local environment for deploying applications.

Deploying a cluster

Choose the way you will install Kubernetes in your operating system:

Installing and running minikube

Install/update minikube using this guide. Make sure that you have the latest version installed.

Create a new Kubernetes cluster using minikube:

# delete the existing minikube cluster (if exists)
minikube delete
# start a new minikube cluster
minikube start --driver=docker

Set the default Namespace so that you don’t have to specify it every time you invoke kubectl:

kubectl config set-context minikube --namespace=werf-guide-app

You will see the following output:

Context "minikube" modified.

If you do not have the kubectl utility installed, then install it using this guide.

Now let’s check if the new Kubernetes cluster is working by displaying the list of Pods running in it:

kubectl get --all-namespaces pod

You will see something like this:

NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-558bd4d5db-8jfng           1/1     Running   0          48s
kube-system   etcd-minikube                      1/1     Running   0          61s
kube-system   kube-apiserver-minikube            1/1     Running   0          54s
kube-system   kube-controller-manager-minikube   1/1     Running   0          54s
kube-system   kube-proxy-b87f2                   1/1     Running   0          48s
kube-system   kube-scheduler-minikube            1/1     Running   0          65s
kube-system   storage-provisioner                1/1     Running   0          56s

Your Kubernetes cluster is up and running if both statements are true:

  • in the 4th column, all the Pods from the list are Running or Completed;
  • in the 3rd column, for all Running Pods, the left (X) and right (Y) numbers are equal in the X/Y statement (i.e., Pod containers have started successfully).

Please wait a while and run the command above again to get the status of Pods, if not all of them have started successfully.

Installing NGINX Ingress Controller

Install the NGINX Ingress Controller:

minikube addons enable ingress

You will see the following output:

...
🔎  Verifying ingress addon...
🌟  The 'ingress' addon is enabled

Give it a few minutes and check if the Ingress Controller has started successfully:

kubectl -n ingress-nginx get pod

You will see something like this:

NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-qrcdg        0/1     Completed   0          8m12s
ingress-nginx-admission-patch-8pw4d         0/1     Completed   0          8m12s
ingress-nginx-controller-59b45fb494-fscgf   1/1     Running     0          8m12s

Updating the hosts file

We will use the ‘werf-guide-app’ domain for accessing the application. Let’s append the IP address/hostname to the hosts file:

echo "$(minikube ip) werf-guide-app" | sudo tee -a /etc/hosts

Installing and running minikube

Install/update minikube using this guide. Make sure that you have the latest version installed.

Create a new Kubernetes cluster using minikube:

# delete the existing minikube cluster (if exists)
minikube delete
# start a new minikube cluster
minikube start --driver=hyperkit

Set the default Namespace so that you don’t have to specify it every time you invoke kubectl:

kubectl config set-context minikube --namespace=werf-guide-app

You will see the following output:

Context "minikube" modified.

If you do not have the kubectl utility installed, then install it using this guide.

Now let’s check if the new Kubernetes cluster is working by displaying the list of Pods running in it:

kubectl get --all-namespaces pod

You will see something like this:

NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-558bd4d5db-8jfng           1/1     Running   0          48s
kube-system   etcd-minikube                      1/1     Running   0          61s
kube-system   kube-apiserver-minikube            1/1     Running   0          54s
kube-system   kube-controller-manager-minikube   1/1     Running   0          54s
kube-system   kube-proxy-b87f2                   1/1     Running   0          48s
kube-system   kube-scheduler-minikube            1/1     Running   0          65s
kube-system   storage-provisioner                1/1     Running   0          56s

Your Kubernetes cluster is up and running if both statements are true:

  • in the 4th column, all the Pods from the list are Running or Completed;
  • in the 3rd column, for all Running Pods, the left (X) and right (Y) numbers are equal in the X/Y statement (i.e., Pod containers have started successfully).

Please wait a while and run the command above again to get the status of Pods, if not all of them have started successfully.

Installing NGINX Ingress Controller

Install the NGINX Ingress Controller:

minikube addons enable ingress

You will see the following output:

...
🔎  Verifying ingress addon...
🌟  The 'ingress' addon is enabled

Give it a few minutes and check if the Ingress Controller has started successfully:

kubectl -n ingress-nginx get pod

You will see something like this:

NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-qrcdg        0/1     Completed   0          8m12s
ingress-nginx-admission-patch-8pw4d         0/1     Completed   0          8m12s
ingress-nginx-controller-59b45fb494-fscgf   1/1     Running     0          8m12s

Updating the hosts file

We will use the ‘werf-guide-app’ domain for accessing the application. Let’s append the IP address/hostname to the hosts file:

echo "$(minikube ip) werf-guide-app" | sudo tee -a /etc/hosts

Installing and running minikube

Install/update minikube using this guide. Make sure that you have the latest version installed.

Create a new Kubernetes cluster using minikube:

# delete the existing minikube cluster (if exists)
minikube delete
# start a new minikube cluster
minikube start --driver=docker

Set the default Namespace so that you don’t have to specify it every time you invoke kubectl:

kubectl config set-context minikube --namespace=werf-guide-app

You will see the following output:

Context "minikube" modified.

If you do not have the kubectl utility installed, then install it using this guide.

Now let’s check if the new Kubernetes cluster is working by displaying the list of Pods running in it:

kubectl get --all-namespaces pod

You will see something like this:

NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-558bd4d5db-8jfng           1/1     Running   0          48s
kube-system   etcd-minikube                      1/1     Running   0          61s
kube-system   kube-apiserver-minikube            1/1     Running   0          54s
kube-system   kube-controller-manager-minikube   1/1     Running   0          54s
kube-system   kube-proxy-b87f2                   1/1     Running   0          48s
kube-system   kube-scheduler-minikube            1/1     Running   0          65s
kube-system   storage-provisioner                1/1     Running   0          56s

Your Kubernetes cluster is up and running if both statements are true:

  • in the 4th column, all the Pods from the list are Running or Completed;
  • in the 3rd column, for all Running Pods, the left (X) and right (Y) numbers are equal in the X/Y statement (i.e., Pod containers have started successfully).

Please wait a while and run the command above again to get the status of Pods, if not all of them have started successfully.

Installing NGINX Ingress Controller

Install the NGINX Ingress Controller:

minikube addons enable ingress

You will see the following output:

...
🔎  Verifying ingress addon...
🌟  The 'ingress' addon is enabled

Give it a few minutes and check if the Ingress Controller has started successfully:

kubectl -n ingress-nginx get pod

You will see something like this:

NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-qrcdg        0/1     Completed   0          8m12s
ingress-nginx-admission-patch-8pw4d         0/1     Completed   0          8m12s
ingress-nginx-controller-59b45fb494-fscgf   1/1     Running     0          8m12s

Let’s make the NGINX Ingress Controller accessible on port 80 after starting the minikube tunnel:

kubectl expose service -n ingress-nginx ingress-nginx-controller --name ingress-nginx-controller-lb --type LoadBalancer --port 80 --target-port http

Now, let’s check that port 80 is available in the system. The following command should return an empty result:

netstat -anb | grep :80

If the port is busy, you will see something like this:

TCP     0.0.0.0:80        0.0.0.0:0       LISTENING
TCP     [::]:80           [::]:0          LISTENING

— in this case, you need to locate and stop the running server.

Keep in mind that minikube tunnel must be running in a separate PowerShell window. This is a prerequisite to access cluster resources via Ingress:

minikube tunnel --cleanup=true

Updating the hosts file

We will use the ‘werf-guide-app’ domain for accessing the application. To do this, update the hosts file (in PowerShell as an administrator):

Add-Content "C:\Windows\System32\drivers\etc\hosts" "`n127.0.0.1 werf-guide-app"

Testing

Execute the following command to check if the cluster is up and running:

curl http://werf-guide-app/ping

If it works, the NGINX Ingress Controller will return a 404 error:

<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

Note that if your application is running already and has an Ingress configured to use the werf-guide-app domain, you will get a response from your application instead of a 404 error.

Configuring the Container Registry

In this guide, we will use the Docker Hub Container Registry. However, you can use any other Registry that supports TLS and authentication (GitHub Container Registry, GitLab Container Registry, etc.).

Authorizing in Docker Hub

Create a Docker Hub ID and create a private repository named werf-guide-app. We will store the built images in it.

Using the docker login command, log in to the new repository by entering the username and password of the Docker Hub user:

docker login
# Enter the name of the Docker Hub user
Username: <DOCKER HUB USERNAME>
# Enter the password of the Docker Hub user
Password: <DOCKER HUB PASSWORD>

If the login is successful, you will see the following message:

Login Succeeded

Creating a namespace

Let’s create a namespace for our application:

kubectl create namespace werf-guide-app

You will see the following output:

namespace/werf-guide-app created

Creating a Secret

Now it is time to create a Secret in the cluster. Using it, our application can access the private repository:

kubectl create secret docker-registry registrysecret \
  --docker-server='https://index.docker.io/v1/' \
  --docker-username='<DOCKER HUB USERNAME>' \
  --docker-password='<DOCKER HUB PASSWORD>'

You will see the following output:

secret/registrysecret created
If you need to recreate a Secret

kubectl delete secret registrysecret

Note the --docker-server parameter. It must correspond to the address of the registry being used. For example, for GitHub Container Registry, this parameter must be set to ghcr.io, while for Docker Hub, you can omit it and use the default value.

Now the environment is ready for work.

The environment has stopped working

Is Docker running?

Let’s launch Docker Desktop. It takes some time for this application to start Docker. If there are no errors during the startup process, check that Docker is running and is properly configured:

docker run hello-world

You will see the following output if the command completes successfully:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:9f6ad537c5132bcce57f7a0a20e317228d382c3cd61edae14650eec68b2b345c
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

Should you have any problems, please refer to the Docker documentation.

Let’s launch the Docker Desktop application. It takes some time for the application to start Docker. If there are no errors during the startup process, then check that Docker is running and is properly configured:

docker run hello-world

You will see the following output if the command completes successfully:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:9f6ad537c5132bcce57f7a0a20e317228d382c3cd61edae14650eec68b2b345c
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

Should you have any problems, please refer to the Docker documentation.

Start Docker:

sudo systemctl restart docker

Make sure that Docker is running:

sudo systemctl status docker

If the Docker start is successful, you will see the following output:

● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2021-06-24 13:05:17 MSK; 13s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 2013888 (dockerd)
      Tasks: 36
     Memory: 100.3M
     CGroup: /system.slice/docker.service
             └─2013888 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

dockerd[2013888]: time="2021-06-24T13:05:16.936197880+03:00" level=warning msg="Your kernel does not support CPU realtime scheduler"
dockerd[2013888]: time="2021-06-24T13:05:16.936219851+03:00" level=warning msg="Your kernel does not support cgroup blkio weight"
dockerd[2013888]: time="2021-06-24T13:05:16.936224976+03:00" level=warning msg="Your kernel does not support cgroup blkio weight_device"
dockerd[2013888]: time="2021-06-24T13:05:16.936311001+03:00" level=info msg="Loading containers: start."
dockerd[2013888]: time="2021-06-24T13:05:17.119938367+03:00" level=info msg="Loading containers: done."
dockerd[2013888]: time="2021-06-24T13:05:17.134054120+03:00" level=info msg="Daemon has completed initialization"
systemd[1]: Started Docker Application Container Engine.
dockerd[2013888]: time="2021-06-24T13:05:17.148493957+03:00" level=info msg="API listen on /run/docker.sock"

Now let’s check if Docker is available and its configuration is correct:

docker run hello-world

You will see the following output if the command completes successfully:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:9f6ad537c5132bcce57f7a0a20e317228d382c3cd61edae14650eec68b2b345c
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

Should you have any problems, please refer to the Docker documentation.

Have you restarted the computer after setting up the environment?

Let’s start the minikube cluster we have already configured in the “Preparing the environment” chapter:

minikube start

Set the default Namespace so that you don’t have to specify it every time you invoke kubectl:

kubectl config set-context minikube --namespace=werf-guide-app

You will see the following output if the command completes successfully:

😄  minikube v1.20.0 on Ubuntu 20.04
✨  Using the docker driver based on existing profile
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
🎉  minikube 1.21.0 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.21.0
💡  To disable this notice, run: 'minikube config set WantUpdateNotification false'

🔄  Restarting existing docker container for "minikube" ...
🐳  Preparing Kubernetes v1.20.2 on Docker 20.10.6 ...
🔎  Verifying Kubernetes components...
    ▪ Using image gcr.io/google_containers/kube-registry-proxy:0.4
    ▪ Using image k8s.gcr.io/ingress-nginx/controller:v0.44.0
    ▪ Using image registry:2.7.1
    ▪ Using image docker.io/jettech/kube-webhook-certgen:v1.5.1
    ▪ Using image docker.io/jettech/kube-webhook-certgen:v1.5.1
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🔎  Verifying registry addon...
🔎  Verifying ingress addon...
🌟  Enabled addons: storage-provisioner, registry, default-storageclass, ingress
🏄  Done! kubectl is now configured to use "minikube" cluster and "werf-guide-app" namespace by default

Make sure that the command output contains the following line:

Restarting existing docker container for "minikube"

Its absence means that a new minikube cluster was created instead of using the old one. In this case, repeat all the steps required to install the environment using minikube.

Now run the command in the background PowerShell terminal (do not close its window):

minikube tunnel --cleanup=true

Let’s start the minikube cluster we have already configured in the “Preparing the environment” chapter:

minikube start --namespace werf-guide-app

Set the default Namespace so that you don’t have to specify it every time you invoke kubectl:

kubectl config set-context minikube --namespace=werf-guide-app

You will see the following output if the command completes successfully:

😄  minikube v1.20.0 on Ubuntu 20.04
✨  Using the docker driver based on existing profile
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
🎉  minikube 1.21.0 is available! Download it: https://github.com/kubernetes/minikube/releases/tag/v1.21.0
💡  To disable this notice, run: 'minikube config set WantUpdateNotification false'

🔄  Restarting existing docker container for "minikube" ...
🐳  Preparing Kubernetes v1.20.2 on Docker 20.10.6 ...
🔎  Verifying Kubernetes components...
    ▪ Using image gcr.io/google_containers/kube-registry-proxy:0.4
    ▪ Using image k8s.gcr.io/ingress-nginx/controller:v0.44.0
    ▪ Using image registry:2.7.1
    ▪ Using image docker.io/jettech/kube-webhook-certgen:v1.5.1
    ▪ Using image docker.io/jettech/kube-webhook-certgen:v1.5.1
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🔎  Verifying registry addon...
🔎  Verifying ingress addon...
🌟  Enabled addons: storage-provisioner, registry, default-storageclass, ingress
🏄  Done! kubectl is now configured to use "minikube" cluster and "werf-guide-app" namespace by default

Make sure that the command output contains the following line:

Restarting existing docker container for "minikube"

Its absence means that a new minikube cluster was created instead of using the old one. In this case, repeat all the steps required to install the environment from scratch using minikube.

Did you accidentally delete the application's Namespace?

If you have inadvertently deleted Namespace of the application, you must run the following commands to proceed with the guide:

kubectl create namespace werf-guide-app
kubectl create secret docker-registry registrysecret \
  --docker-server='https://index.docker.io/v1/' \
  --docker-username='<Docker Hub username>' \
  --docker-password='<Docker Hub password>'

You will see the following output if the command completes successfully:

namespace/werf-guide-app created
secret/registrysecret created
Nothing helps; the environment or instructions keep failing.

If nothing worked, repeat all the steps described in the “Preparing the environment” chapter and create a new environment from scratch. If creating an environment from scratch did not help either, please, tell us about your problem in our Telegram chat or create an issue on GitHub. We will be happy to help you!