Kubernetes: знакомство, часть 3 — обзор AWS EKS и ручное создание кластера

Автор: | 08/15/2019
 

Продолжаем знакомство с Kubernetes.

Предыдущие части:

Следующие:

В этой части перейдём уже непосредственно к EKS — кратко его рассмотрим, потом создадим Kubernetes Control Plane, CloudFormation стек с Worker Nodes, запустим веб-сервис в новом кластере, и добавим LoadBalancer.

Elastic Kubernetes Service — обзор

AWS EKS (Elastic Kubernetes Service) представляет собой Kubernetes-сервис, где пользователю не надо заниматься созданием и управлением ядра Kubernetes — его Control Plane.

  • Control Plane: управляется самим Амазоном, состоит из трёх EC2 в различных Availability Zones, управляются самим AWS
  • Worker Nodes: обычные ЕС2 в пользовательской VPC, управляются пользователем

Архитектура сети:

Для сети используется плагин amazon-vpc-cni-k8s, который позволяет подам кластера использовать ENI (Elastic Network Interface) и сетевое пространство VPC и подсетей в AWS.

Для авториазации используется плагин  aws-iam-authenticator, который позволяет выполнять авторизацию кластера через AWS IAM роли и политики (см. Managing Users or IAM Roles for your Cluster)

AWS также сам выполняется минорные обновления, например с 1.11.5 to 1.11.8, но мажорные апгрейды на совести пользователя.

Подготовка AWS

Для создания кластера нам нужно поднять свою VPC, создать сети, настроить роутинг, и добавить IAM роль, которая будет использоваться кластером для авторизации.

IAM роль

Переходим в IAM, создаём новую роль, тип EKS:

Permissions AWS выберет сам:

Сохраняем:

VPC

Далее потребуется создать VPC и 4 подсети — две публичные, для Load Balancer, и две приватные — для Worker-нод.

Создаём VPC:

SecurityGroup

Переходим в SecurityGroups, создаём SG для кластера:

Добавляем правила:

Internet Gateway

Создаём IGW, через который будет ходить трафик из публичных подсетей:

Подключаем его к VPC:

Subnets

Pod-ы будут получать IP из подсетей (см. amazon-vpc-cni-k8s) — сделайте достаточно адресов.

Создаём первую публичную сеть с блоком 10.0.0.0/18 (всего-то 16384 адресов):

Вторую публичную — 10.0.64.0/18:

В публичных адреса — включаем добавление публичного IP инстансам:

И аналогично добавляем две приватные подсети:

NAT Gateway

В публичной подсети создаём NAT Gateway — через него будет ходить трафик из приватных подсетей:

(раньше не было кнопки Create EIP 😐 )

Сразу настраиваем маршрутизацию:

Route tables

Тут требуется создать две таблицы маршрутизации — одна для публичных подсетей, и вторую — для приватных.

Public route table

Создаём для публичной:

Редактируем маршруты — задаём 0.0.0.0/0 через IGW:

Переключаемся во вкладку Subnet association — подключаем две публичные подсети:

Private route table

Аналогично создаём вторую таблицу, для приватных подсетей:

Добавляем маршрут к 0.0.0.0/0 через NAT GW:

Возвращаемся к подсетям, подключаем к ним созданные таблицы маршрутизации — Edit route table association:

К приватным подсетям подключаем, соответственно, приватную RTB, с маршрутами через NAT:

К публичным — RTB с маршрутом через IGW:

Проверка

Для проверки — запустим две ЕС2, сначала в публичной подсети:

Указываем SG:

Проверяем сеть:

[setevoy@setevoy-arch-work ~] $ ssh -i setevoy-testing-eu-west-2.pem ubuntu@35.178.171.252 'ping -c 1 8.8.8.8'
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=51 time=1.33 ms
--- 8.8.8.8 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.331/1.331/1.331/0.000 ms

Добавляем ещё один ЕС2, в приватной подсети:

Не забываем SG.

И пингуем его с первого инстанса:

[setevoy@setevoy-arch-work ~] $ ssh -i setevoy-testing-eu-west-2.pem ubuntu@35.178.171.252 'ping -c 1 10.0.184.21'
PING 10.0.184.21 (10.0.184.21) 56(84) bytes of data.
64 bytes from 10.0.184.21: icmp_seq=1 ttl=64 time=0.357 ms
--- 10.0.184.21 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.357/0.357/0.357/0.000 ms

Если пинг не проходит — проверьте настройки Security Group, подключенной к инстансу.

На этом с подготовкой закончили  — переходим к самому EKS.

EKS

Создание Control Plane

Создаём мастер-ноды — кликаем Create cluster:

Задаём имя, выбираем IAM-роль, созданную в самом начале:

В подсетях — выбираем только приватные и указываем нашу SecurityGroup:

Примечание: На самом деле, хотя EKS пишет про «подсети для ваших Worker Nodes — эти же подсети будут использоваться в случае использования, например, AWS Load Balancer, для которого нужны публичные сети. Поэтому — можно выбрать все: EKS будет исопльзовать приватные для ЕС2, и публичные — для ALB.

Можно сразу тут включить и логи:

Запускаем создание:

Создание Worker Nodes 

Пока создаётся Control Plane — создадим CloudFormation стек для Worker Nodes.

Можно взять уже готовый шаблон, например — https://amazon-eks.s3-us-west-2.amazonaws.com/cloudformation/2019-02-11/amazon-eks-nodegroup.yaml.

Переходим в CloudFormation > Create stack:

Т.к. наши рабочие ноды будут в приватных подсетях — открываем файл в редакторе:

Находим AssociatePublicIpAddress, меняем значеине с true на false:

Кликаем Create stack:

Задаём имя стека — любое, имя кластера — как мы задали выше, в данном примере eks-cluster-manual, выбираем нашу SecurityGroup, заполняем настройки AutoScale-группы:

Выбираем NodeImageId, а зависимости от зоны (см. актуальные AM ID в документации):

Region Amazon EKS-optimized AMI with GPU support
US East (Ohio) (us-east-2) ami-0485258c2d1c3608f ami-0ccac9d9b57864000
US East (N. Virginia) (us-east-1) ami-0f2e8e5663e16b436 ami-0017d945a10387606
US West (Oregon) (us-west-2) ami-03a55127c613349a7 ami-08335952e837d087b
Asia Pacific (Hong Kong) (ap-east-1) ami-032850771ac6f8ae2 N/A*
Asia Pacific (Mumbai) (ap-south-1) ami-0a9b1c1807b1a40ab ami-005b754faac73f0cc
Asia Pacific (Tokyo) (ap-northeast-1) ami-0fde798d17145fae1 ami-04cf69bbd6c0fae0b
Asia Pacific (Seoul) (ap-northeast-2) ami-07fd7609df6c8e39b ami-0730e699ed0118737
Asia Pacific (Singapore) (ap-southeast-1) ami-0361e14efd56a71c7 ami-07be5e97a529cd146
Asia Pacific (Sydney) (ap-southeast-2) ami-0237d87bc27daba65 ami-0a2f4c3aeb596aa7e
EU (Frankfurt) (eu-central-1) ami-0b7127e7a2a38802a ami-0fbbd205f797ecccd
EU (Ireland) (eu-west-1) ami-00ac2e6b3cb38a9b9 ami-0f9571a3e65dc4e20
EU (London) (eu-west-2) ami-0147919d2ff9a6ad5 ami-032348bd69c5dd665
EU (Paris) (eu-west-3) ami-0537ee9329c1628a2 ami-053962359d6859fec
EU (Stockholm) (eu-north-1) ami-0fd05922165907b85 ami-0641def7f02a4cac5

В данном случае регион London/eu-west-2, GPU не нужен, значит — ami-0147919d2ff9a6ad5 (Amazon Linux).

Указываем этот AMI, выбираем нашу VPC, и в ней — две приватные подсети:

Кликаем Next, на следующей странице можно ничего не менять, и запускаем создание стека:

После создания стека — проверяем AutoScaling groups:

Установка kubectl

Тем временем — сам EKS-кластер поднялся, можно установить kubectl.

На рабочую машину загружаем исполняемый файл:

[setevoy@setevoy-arch-work ~] $ curl -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.13.7/2019-06-11/bin/linux/amd64/kubectl
[setevoy@setevoy-arch-work ~] $ chmod +x kubectl
[setevoy@setevoy-arch-work ~] $ sudo mv kubectl /usr/local/bin/

Проверяем:

[setevoy@setevoy-arch-work ~] $ kubectl version --short --client
Client Version: v1.13.7-eks-fa4c70

Для создания файла его настроек — используем AWS CLI:

[setevoy@setevoy-arch-work ~] $ aws eks --region eu-west-2 --profile arseniy update-kubeconfig --name eks-cluster-manual
Added new context arn:aws:eks:eu-west-2:534***385:cluster/eks-cluster-manual to /home/setevoy/.kube/config

Добавляем алиас для удобства:

[setevoy@setevoy-arch-work ~] $ echo "alias kk=\"kubectl\"" >> ~/.bashrc
[setevoy@setevoy-arch-work ~] $ bash

Проверяем:

[setevoy@setevoy-arch-work ~] $ kk get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   172.20.0.1   <none>        443/TCP   20m

AWS authenticator

Хотя CloudFormation стек создан, и ЕС2 подняты — но Worker Nodes ещё не добавлены в сам Kubernetes -кластер:

[setevoy@setevoy-arch-work ~] $ kk get node
No resources found.

Загружаем AWS authenticator:

[setevoy@setevoy-arch-work ~] $ cd Temp/
[setevoy@setevoy-arch-work ~] $ curl -so aws-auth-cm.yaml https://amazon-eks.s3-us-west-2.amazonaws.com/cloudformation/2019-02-11/aws-auth-cm.yaml

Переходим в IAM  > Roles, находим ARN (Amazon Resource Name) роли (NodeInstanceRole) (или находим в Outputs CloudFormation стека для worker nodes):

Редактируем aws-auth-cm.yaml, задаём rolearn:

Создаём ConfigMap:

[setevoy@setevoy-arch-work ~/Temp]  $ kk apply -f aws-auth-cm.yaml
configmap/aws-auth created

Проверяем:

[setevoy@setevoy-arch-work ~/Temp]  $ kubectl get nodes -o wide
NAME                                         STATUS   ROLES    AGE   VERSION              INTERNAL-IP    EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
ip-10-0-153-7.eu-west-2.compute.internal     Ready    <none>   47s   v1.13.7-eks-c57ff8   10.0.153.7     <none>        Amazon Linux 2   4.14.128-112.105.amzn2.x86_64   docker://18.6.1
ip-10-0-196-123.eu-west-2.compute.internal   Ready    <none>   50s   v1.13.7-eks-c57ff8   10.0.196.123   <none>        Amazon Linux 2   4.14.128-112.105.amzn2.x86_64   docker://18.6.1
ip-10-0-204-190.eu-west-2.compute.internal   Ready    <none>   52s   v1.13.7-eks-c57ff8   10.0.204.190   <none>        Amazon Linux 2   4.14.128-112.105.amzn2.x86_64   docker://18.6.1

Ноды появились, всё отлично.

Web-app && LoadBalancer

И для проверки — создадим простой веб-сервис, пусть будет просто NGINX, как в предыдущем примере.

Для доступа к NGINX — добавим AWS и Kubernetes LoadBalancer, который будет проксировать запросы к рабочим нодам:

kind: Service
apiVersion: v1
metadata:
  name: eks-cluster-manual-elb
spec:
  type: LoadBalancer
  selector:
    app: eks-cluster-manual-pod
  ports:
    - name: http
      protocol: TCP
      # ELB's port
      port: 80
      # container's port
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: eks-cluster-manual-deploy
spec:
  # ReplicaSet pods config
  replicas: 1
  # pods selector
  selector:
    matchLabels:
      app: eks-cluster-manual-pod
  # Pod template
  template:
    metadata:
      # a pod's labeles
      labels:
        app: eks-cluster-manual-pod
    spec:
      containers:
        - name: eks-cluster-manual-app
          image: nginx

Применяем их:

[setevoy@setevoy-arch-work ~/Temp]  $ kk apply -f eks-cluster-manual-elb-nginx.yml
service/eks-cluster-manual-elb created
deployment.apps/eks-cluster-manual-deploy created

Проверяем сервисы:

[setevoy@setevoy-arch-work ~/Temp]  $ kk get svc
NAME                     TYPE           CLUSTER-IP     EXTERNAL-IP                                                               PORT(S)        AGE
eks-cluster-manual-elb   LoadBalancer   172.20.17.42   a05***405.eu-west-2.elb.amazonaws.com   80:32680/TCP   5m23s

Под:

kk get po -o wide -l app=eks-cluster-manual-pod
NAME                                         READY   STATUS    RESTARTS   AGE     IP            NODE                                       NOMINATED NODE   READINESS GATES
eks-cluster-manual-deploy-698b8f6df7-jg55x   1/1     Running   0          6m17s   10.0.130.54   ip-10-0-153-7.eu-west-2.compute.internal   <none>           <none>

И ConfigMap:

kubectl describe configmap -n kube-system aws-auth
Name:         aws-auth
Namespace:    kube-system
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","data":{"mapRoles":"- rolearn: arn:aws:iam::534***385:role/eks-cluster-manual-workers-stack-NodeInstanceRole-12DRN98...
Data
====
mapRoles:
----
- rolearn: arn:aws:iam::534***385:role/eks-cluster-manual-workers-stack-NodeInstanceRole-12DRN987QYB34
username: system:node:{{EC2PrivateDNSName}}
groups:
- system:bootstrappers
- system:nodes
Events:  <none>

LoadBalancer в самом AWS (потребуется минут 5, что бы завелись поды и инстансы подключились к ELB):

Его теги:

Проверяем URL:

[setevoy@setevoy-arch-work ~/Temp]  $ curl a05***405.eu-west-2.elb.amazonaws.com
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Готово.

Ссылки по теме