Installing Kubernetes on Hetzner Cloud
In this article, I would like to talk about installing Kubernetes on the Hetzner Cloud.
Ubuntu Linux 18.04 is installed on my work computer and all the examples will imply the use of this operating system.
To work with Hetzner Cloud and build Kubernetes cluster, we will use the hetzner-kube utility . Install it on your local computer.
To use the hetzner-kube utility and its authorization in the Hetzner Cloud, you must create the Token API through the Hetzner Cloud Console https://console.hetzner.cloud . At the top, select Select a project -> Default, select the Access item in the left menu, then go to the API tokens section, click on the Generate API Token button.
As a result, the Token API will be generated and it will need to be specified in the configuration of the hetzner-kube utility.
Next, we need to generate an SSH key that will be used to access the servers in the Hetzner Cloud. To do this, use the ssh-keygen utility:
As a result, two files ~ / .ssh / id_rsa (private key) and ~ / .ssh / id_rsa.pub (public key) will be created in your home directory.
Add a public ssh key to Hetzner Cloud:
Building the Kubernetes cluster directly is very easy:
This command will automatically create virtual servers in Hetzner Cloud and install the specified number of master / worker nodes of the Kubernetes cluster on them. By default, CX11 virtual servers will be used.
Later, using the hetzner-kube utility, it is also easy to change the Kubernetes cluster configuration by adding a worker node. For example, add 2 worker nodes:
Unfortunately, it is not possible to change the configuration of the master node using the hetzner-kube utility without completely re-creating the Kubernetes cluster at this time.
The kubectl utility is used to work with the Kubernetes cluster. Detailed instructions for installing it for different operating systems can be found at the following link .
In order to work with the created Kubernetes cluster using the kubectl command, you must save the configuration of the created cluster locally as follows:
The configuration file is saved in ~ / .kube / config.
Now we come to the most interesting part - configuring the resulting Kubernetes cluster.
To begin with, let's create the basic resources needed for future application deployment. More information can be found at the following link .
We add the ingress-nginx service, which will process requests on ports 80 (http) and 443 (https) and redirect them further to our application. Instead of XXXX, we specify the list of external IPs of our nodes in the Kubernetes cluster, which will process requests from the Internet (these can be either master or worker nodes, since LoadBalancer is not available in Hetzner Cloud at this time).
Create a file called ingress-nginx.yaml and the following contents:
We check that nginx-ingress-controller and default-http-backend are running.
Add A record to your domain and wait until the information about them appears in DNS. For example:
If you entered several external IP addresses in ingress-nginx.yaml, you can create several identical DNS records with these IP addresses. In this case, requests for your domain will be distributed between these IP addresses and load balancing will occur.
In this example, to work with https, we will generate a self-signed SSL certificate.
Now add our application. The simple echoserver is chosen as an example. Create a file called app.yaml and the following contents:
That's all)) Check the result:
Ubuntu Linux 18.04 is installed on my work computer and all the examples will imply the use of this operating system.
To work with Hetzner Cloud and build Kubernetes cluster, we will use the hetzner-kube utility . Install it on your local computer.
$ wget https://github.com/xetys/hetzner-kube/releases/download/0.3.1/hetzner-kube-linux-amd64
$ chmod a+x ./hetzner-kube-linux-amd64
$ sudo mv ./hetzner-kube-linux-amd64 /usr/local/bin/hetzner-kube
To use the hetzner-kube utility and its authorization in the Hetzner Cloud, you must create the Token API through the Hetzner Cloud Console https://console.hetzner.cloud . At the top, select Select a project -> Default, select the Access item in the left menu, then go to the API tokens section, click on the Generate API Token button.
As a result, the Token API will be generated and it will need to be specified in the configuration of the hetzner-kube utility.
$ hetzner-kube context add k8s
Token: <PASTE TOKEN HERE>
added context 'k8s'
Next, we need to generate an SSH key that will be used to access the servers in the Hetzner Cloud. To do this, use the ssh-keygen utility:
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file inwhich to save the key (~/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ~/.ssh/id_rsa.
Your public key has been saved in ~/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:1bwptZ8lPiAhtA37/2U1G7HsC+aE7qMVCtVIfN3OLzk lx4241@LX4241-LINUX
The key's randomart image is:
+---[RSA 2048]----+
| +. . . |
| ..*o+ . . |
| +o=.+ o. |
| .+ o +.oo|
| .S +.= .*+|
| . .+o+E+*|
| . o.+==o|
| o.+..+.|
| .oo.... |
+----[SHA256]-----+
As a result, two files ~ / .ssh / id_rsa (private key) and ~ / .ssh / id_rsa.pub (public key) will be created in your home directory.
Add a public ssh key to Hetzner Cloud:
$ hetzner-kube ssh-key add --name k8s
sshKeyAdd called
SSH key k8s(95430) created
Building the Kubernetes cluster directly is very easy:
$ hetzner-kube cluster create --name k8s --ssh-key k8s --master-count 1 --worker-count 1
2018/08/02 13:57:57 Creating new cluster
NAME:k8s
MASTERS: 1
WORKERS: 1
ETCD NODES: 0
HA: false
ISOLATED ETCD: false
2018/08/02 13:57:58 creating server 'k8s-master-01'...
--- [======================================] 100%
2018/08/02 13:58:18 Created node 'k8s-master-01' with IP 159.69.54.228
2018/08/02 13:58:18 creating server 'k8s-worker-01'...
--- [======================================] 100%
2018/08/02 13:58:37 Created node 'k8s-worker-01' with IP 159.69.51.140
2018/08/02 13:58:37 sleep for 10s...
k8s-master-01 : complete! 100.0% [==============]
k8s-worker-01 : complete! 100.0% [==============]
2018/08/02 14:02:50 Cluster successfully created!
This command will automatically create virtual servers in Hetzner Cloud and install the specified number of master / worker nodes of the Kubernetes cluster on them. By default, CX11 virtual servers will be used.
Later, using the hetzner-kube utility, it is also easy to change the Kubernetes cluster configuration by adding a worker node. For example, add 2 worker nodes:
$ hetzner-kube cluster add-worker --name k8s --nodes 2
Unfortunately, it is not possible to change the configuration of the master node using the hetzner-kube utility without completely re-creating the Kubernetes cluster at this time.
The kubectl utility is used to work with the Kubernetes cluster. Detailed instructions for installing it for different operating systems can be found at the following link .
In order to work with the created Kubernetes cluster using the kubectl command, you must save the configuration of the created cluster locally as follows:
$ hetzner-kube cluster kubeconfig k8s
create file
kubeconfig configured
The configuration file is saved in ~ / .kube / config.
Now we come to the most interesting part - configuring the resulting Kubernetes cluster.
To begin with, let's create the basic resources needed for future application deployment. More information can be found at the following link .
$ curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml | kubectl apply -f -
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 6170 100 6170 0 0 13987 0 --:--:-- --:--:-- --:--:-- 14022
namespace "ingress-nginx" created
deployment "default-http-backend" created
service "default-http-backend" created
configmap "nginx-configuration" created
configmap "tcp-services" created
configmap "udp-services" created
serviceaccount "nginx-ingress-serviceaccount" created
clusterrole "nginx-ingress-clusterrole" created
role "nginx-ingress-role" created
rolebinding "nginx-ingress-role-nisa-binding" created
clusterrolebinding "nginx-ingress-clusterrole-nisa-binding" created
deployment "nginx-ingress-controller" created
We add the ingress-nginx service, which will process requests on ports 80 (http) and 443 (https) and redirect them further to our application. Instead of XXXX, we specify the list of external IPs of our nodes in the Kubernetes cluster, which will process requests from the Internet (these can be either master or worker nodes, since LoadBalancer is not available in Hetzner Cloud at this time).
Create a file called ingress-nginx.yaml and the following contents:
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type:
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app: ingress-nginx
externalIPs:
- X.X.X.X
- X.X.X.X
$ kubectl apply -f ingress-nginx.yaml
service "ingress-nginx" configured
We check that nginx-ingress-controller and default-http-backend are running.
$ kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
default-http-backend-55c6c69b88-hvl4x 1/1 Running 0 51m
nginx-ingress-controller-6658c97f58-d6jkg 1/1 Running 0 51m
Add A record to your domain and wait until the information about them appears in DNS. For example:
Type: A
Name: echo.example.com
Value: X.X.X.X
If you entered several external IP addresses in ingress-nginx.yaml, you can create several identical DNS records with these IP addresses. In this case, requests for your domain will be distributed between these IP addresses and load balancing will occur.
In this example, to work with https, we will generate a self-signed SSL certificate.
$ openssl req -newkey rsa:2048 -nodes -keyout echo.example.com.key -x509 -days 365 -out echo.example.com.crt
Generating a 2048 bit RSA private key
..+++
.............+++
writing new private key to 'echo.example.com.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:UA
State or Province Name (full name) [Some-State]:Kyiv
Locality Name (eg, city) []:Kyiv
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Super Company Ltd
Organizational Unit Name (eg, section) []:echo.example.com
Common Name (e.g. server FQDN or YOUR name) []:echo.example.com
Email Address []:info@echo.example.com
$ cat echo.example.com.key | base64 | tr -d '\n'
<YOUR PRIVATE KEY>
$ cat echo.example.com.crt | base64 | tr -d '\n'
<YOUR CERTIFICATE>
Now add our application. The simple echoserver is chosen as an example. Create a file called app.yaml and the following contents:
apiVersion: v1
kind: Namespace
metadata:
name: echoserver
---
kind: Secret
metadata:
name: echo.example.com-tls
namespace: echoserver
type: kubernetes.io/tls
data:
tls.crt: <YOUR CERTIFICATE>
tls.key: <YOUR PRIVATE KEY>
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: echoserver
namespace: echoserver
spec:
replicas: 1
template:
metadata:
labels:
app: echoserver
spec:
containers:
- image: gcr.io/google_containers/echoserver:1.0
imagePullPolicy: Always
name: echoserver
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: echoserver
namespace: echoserver
spec:
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
selector:
app: echoserver
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: echoserver
namespace: echoserver
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- echo.example.com
secretName: echo.example.com-tls
rules:
- host: echo.example.com
http:
paths:
- path: /
backend:
serviceName: echoserver
servicePort: 80
$ kubectl apply -f app.yaml
namespace "echoserver" configured
deployment "echoserver" unchanged
service "echoserver" configured
ingress "echoserver" unchanged
That's all)) Check the result:
$ curl https://echo.example.com/
CLIENT VALUES:
client_address=('10.244.3.2', 32860) (10.244.3.2)
command=GET
path=/
real path=/
query=
request_version=HTTP/1.1
SERVER VALUES:
server_version=BaseHTTP/0.6
sys_version=Python/3.5.0
protocol_version=HTTP/1.0
HEADERS RECEIVED:
Accept=*/*
Connection=close
Host=echo.example.com
User-Agent=curl/7.58.0
X-Forwarded-For=10.244.0.0
X-Forwarded-Host=echo.example.com
X-Forwarded-Port=80
X-Forwarded-Proto=http
X-Original-URI=/
X-Real-IP=10.244.0.0
X-Request-ID=7a4f4aabf9a0043ea2b1ca91bd1a3adf
X-Scheme=http