В данной статье я хотел бы рассказать об установке Kubernetes на Hetzner Cloud.

На моем рабочем компьютере установлен Ubuntu Linux 18.04 и все примеры будут подразумевать использование данной операционной системы.

Для работы с Hetzner Cloud и построения кластера Kubernetes мы будем использовать утилиту hetzner-kube. Установим ее на свой локальный компьютер.

$ 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 

Для работы утилиты hetzner-kube и ее авторизации в Hetzner Cloud необходимо создать API Token через Hetzner Cloud Console https://console.hetzner.cloud. Вверху выбираем Select a project -> Default, в левом меню выбираем пункт Access, далее переходим в раздел API tokens, нажимаем на кнопку Generate API Token.

В результате будет сгенерирован API Token и его необходимо будет указать в конфигурации утилиты hetzner-kube.

$ hetzner-kube context add k8s
Token: <PASTE TOKEN HERE>
added context 'k8s'

Далее нам необходимо сгенерировать SSH ключ, который будет использоваться для доступа к серверам в Hetzner Cloud. Для этого воспользуемся утилитой ssh-keygen:

$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which 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]-----+

В результате в вашем домашнем каталоге будет создано два файла ~/.ssh/id_rsa (приватный ключ) и ~/.ssh/id_rsa.pub (публичный ключ).

Добавим публичный ssh ключ в Hetzner Cloud:

$ hetzner-kube ssh-key add --name k8s
sshKeyAdd called
SSH key k8s(95430) created

Непосредственно построение кластера Kubernetes выполняется очень легко:

$ 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!

Данная команда автоматически создаст виртуальные сервера в Hetzner Cloud и установит на них указанное количество master/worker нод кластера Kubernetes. По-умолчанию, будут использованы CX11 виртуальные сервера.

В дальнейшем, с помощью утилиты hetzner-kube, также легко изменить конфигурацию кластера Kubernetes добавляя worker ноды. Например, добавим 2 worker ноды:

$ hetzner-kube cluster add-worker --name k8s --nodes 2

К сожалению, изменить конфигурацию master нод с помощью утилиты hetzner-kube без полного пересоздания кластера Kubernetes на данный момент времени не представляется возможным.

Для работы с Kubernetes кластером используется утилита kubectl. Подробную инструкцию по ее установке для разных операционных систем можно найти по следующей ссылке.

Для того, чтобы работать с созданным кластером Kubernetes с помощью команды kubectl, необходимо сохранить локально конфигурацию созданного кластера следующим образом:

$ hetzner-kube cluster kubeconfig k8s
create file
kubeconfig configured

Файл с конфигурацией сохраняется в ~/.kube/config.

Теперь переходим к самому интересному — конфигурированию полученного кластера Kubernetes.

Для начала создадим базовые ресурсы необходимые для будущего развертывания приложений. Более подробную информацию вы сможете найти по следующей ссылке.

$ 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

Добавляем сервис ingress-nginx, который будет обрабатывать запросы на портах 80 (http) и 443 (https) и перенаправлять их дальше на наше приложение. Вместо X.X.X.X указываем список внешних IP наших нод в кластере Kubernetes, которые будут обрабатывать запросы из Интернета (это могут быть как master, так и worker ноды, поскольку LoadBalancer в Hetzner Cloud на данный момент времени отсутствует).

Создаем файл с именем ingress-nginx.yaml и следующим содержимым:

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


Проверяем что nginx-ingress-controller и default-http-backend поды запущены.

$ 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


Добавляем А записи в ваш домен и ждем пока информация о них появится в ДНС. Например:

Type: A
Name: echo.example.com
Value: X.X.X.X 


Если в ingress-nginx.yaml вы указали несколько внешних IP адресов, то можно создать несколько одинаковых DNS записей с этими IP адресами. В этом случае запросы на ваш домен будут распределяться между этими IP адресами и будет происходить балансировка нагрузки.

В данном примере для работы https сгенерируем самоподписанный SSL сертификат.

$ 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>


Теперь добавляем наше приложение. В качестве примера выбран простой echoserver. Создаем файл с именем app.yaml и следующим содержимым:

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


На этом все )) Проверяем результат:

$ 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

Комментарии (3)


  1. de1m
    05.08.2018 23:49

    А как там со storag'oм, я как понял, общего нету?


    1. AlexeiPotocki Автор
      06.08.2018 10:04

      К сожалению, пока нету, но думаю появится в скором времени. Ну и цена конечно очень вкусная что перекрывает все остальное )


      1. de1m
        06.08.2018 13:41

        По идее можно запустить сeph в контейнерах. У нас там тоже четыре сервера с k8s стоят, но я их сам устанавливал и на них ceph стоит, гигабитной сети пока хватает.