Kubernetes: запуск push-сервера Gorush в EKS за AWS LoadBalancer

Автор: | 06/02/2020
 

Gorush – сервер, написанный на Go, для отправки пуш-уведомлений на мобильные.

Запускать будем в AWS EKS, в отдельном namespace, при этом сервис должен быть доступен толкьо внутри VPC, поэтому используем AWS Internal Application Load Balancer.

Запуск Gorush

Namespace

Клонируем репозиторий:

[simterm]

$ git clone https://github.com/appleboy/gorush
$ cd gorush/k8s/

[/simterm]

Создаём пространство имён gorush и confgiMap, который будет использоваться сервисом для получения настроек подключения к Redis-сервверу:

[simterm]

$ kubectl apply -f gorush-namespace.yaml
namespace/gorush created
$ kubectl apply -f gorush-configmap.yaml 
configmap/gorush-config created

[/simterm]

Позже можем использовать этот configMap для передачи в Gorush своего файла настроек.

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

[simterm]

$ kubectl -n gorush get cm
NAME            DATA   AGE
gorush-config   2      20s

[/simterm]

Redis

Запускаем Redis сервер:

[simterm]

$ kubectl apply -f gorush-redis-deployment.yaml 
deployment.extensions/redis created
$ kubectl apply -f gorush-redis-service.yaml 
service/redis created

[/simterm]

Gorush

Для того, что бы проверить работу Gorush после запуска – добавим ещё один под с Debian.

Обновляем файл gorush-deployment.yaml, добавляем контейнер с именем Bastion:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: gorush
  namespace: gorush
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: gorush
        tier: frontend
    spec:
      containers:
      - image: appleboy/gorush
        name: gorush
        imagePullPolicy: Always
        ports:
        - containerPort: 8088
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8088
          initialDelaySeconds: 3
          periodSeconds: 3
        env:
        - name: GORUSH_STAT_ENGINE
          valueFrom:
            configMapKeyRef:
              name: gorush-config
              key: stat.engine
        - name: GORUSH_STAT_REDIS_ADDR
          valueFrom:
            configMapKeyRef:
              name: gorush-config
              key: stat.redis.host
      - image: debian
        name: bastion
        command: ["sleep"]
        args: ["600"]

Создаём деплоймент:

[simterm]

$ kubectl apply -f gorush-deployment.yaml
deployment.extensions/gorush created

[/simterm]

Проверяем поды:

[simterm]

$ kubectl -n gorush get po
NAME                      READY   STATUS    RESTARTS   AGE
gorush-59bd9dd4fc-dzm47   2/2     Running   0          8s
gorush-59bd9dd4fc-fkrhw   2/2     Running   0          8s
gorush-59bd9dd4fc-klsbz   2/2     Running   0          8s
redis-7d5844c58d-7j5jp    1/1     Running   0          3m1s

[/simterm]

AWS Internal Application Load Balancer

Далее, требуется создать Service типа NodePort и сервис Ingress, который затриггерит  alb-ingress-controller  в Kubernetes кластере на создание AWS Load Balancer.

Обновляем gorush-service.yaml, раскомментируем строку с NodePort и комментириуем LoadBalancer:

...
#type: LoadBalancer
type: NodePort
...

Полностью файл теперь выглядит так:

apiVersion: v1
kind: Service
metadata:
  name: gorush
  namespace: gorush
  labels:
    app: gorush
    tier: frontend
spec:
  selector:
    app: gorush
    tier: frontend
  # if your cluster supports it, uncomment the following to automatically create
  # an external load-balanced IP for the frontend service.
  #type: LoadBalancer
  type: NodePort
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8088

Запускаем сервис:

[simterm]

$ kubectl apply -f gorush-service.yaml
service/gorush created

[/simterm]

Настраиваем ALB.

Редактируем файл gorush-aws-alb-ingress.yaml, добавляем в него kubernetes.io/ingress.class: alb для триггера alb-ingress-controller, меняем scheme на internal задаём значения подсетей и SecurityGroup для балансировщика (используя публичные подсети VPC кластера).

Кроме того, обновляем значение servicePort с 8088 на 80, так как мы используем ALB и NodePort Service:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: gorush
  namespace: gorush
  annotations:
    # Kubernetes Ingress Controller for AWS ALB
    # https://github.com/coreos/alb-ingress-controller
    #alb.ingress.kubernetes.io/scheme: internet-facing
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internal
    alb.ingress.kubernetes.io/subnets: subnet-010f9918532f52c6d, subnet-0f6dbde36b6669f48
    alb.ingress.kubernetes.io/security-groups: sg-0f1df776a767a2589
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          serviceName: gorush
          servicePort: 80

Создаём ALB:

[simterm]

$ kubectl apply -f gorush-aws-alb-ingress.yaml
ingress.extensions/gorush created

[/simterm]

Проверяем логи alb-ingress-controller:

[simterm]

$ kubectl logs -f -n kube-system $(kubectl get po -n kube-system | egrep -o 'alb-ingress[a-zA-Z0-9-]+')
...
E0206 11:08:52.664554       1 controller.go:217] kubebuilder/controller "msg"="Reconciler error" "error"="no object matching key \"gorush/gorush\" in local store"  "controller"="alb-ingress-controller" "request"={"Namespace":"gorush","Name":"gorush"}
I0206 11:08:53.731420       1 loadbalancer.go:191] gorush/gorush: creating LoadBalancer 3407a88c-gorush-gorush-f66a
I0206 11:08:54.372743       1 loadbalancer.go:208] gorush/gorush: LoadBalancer 3407a88c-gorush-gorush-f66a created, ARN: arn:aws:elasticloadbalancing:us-east-2:534***385:loadbalancer/app/3407a88c-gorush-gorush-f66a/d31b461c65a278f0
I0206 11:08:54.516572       1 targetgroup.go:119] gorush/gorush: creating target group 3407a88c-e50bdf2f1d39b9db54c
I0206 11:08:54.627177       1 targetgroup.go:138] gorush/gorush: target group 3407a88c-e50bdf2f1d39b9db54c created: arn:aws:elasticloadbalancing:us-east-2:534***385:targetgroup/3407a88c-e50bdf2f1d39b9db54c/e1fdcb752a01b891
I0206 11:08:54.643263       1 tags.go:43] gorush/gorush: modifying tags {  kubernetes.io/service-name: "gorush",  kubernetes.io/service-port: "80",  ingress.k8s.aws/stack: "gorush/gorush",  kubernetes.io/cluster/eksctl-bttrm-eks-production-1: "owned",  kubernetes.io/namespace: "gorush",  kubernetes.io/ingress-name: "gorush",  ingress.k8s.aws/cluster: "eksctl-bttrm-eks-production-1",  ingress.k8s.aws/resource: "gorush/gorush-gorush:80"} on arn:aws:elasticloadbalancing:us-east-2:534***385:targetgroup/3407a88c-e50bdf2f1d39b9db54c/e1fdcb752a01b891
I0206 11:08:54.978243       1 targets.go:80] gorush/gorush: Adding targets to arn:aws:elasticloadbalancing:us-east-2:534***385:targetgroup/3407a88c-e50bdf2f1d39b9db54c/e1fdcb752a01b891: i-05fbfa35e00ade9b3:31472, i-08ecf579e0dd8b5a0:31472, i-098d672bbc222d424:31472, i-0712f941381a624b8:31472
I0206 11:08:55.196629       1 listener.go:110] gorush/gorush: creating listener 80
E0206 11:08:55.247492       1 controller.go:217] kubebuilder/controller "msg"="Reconciler error" "error"="failed to reconcile listeners due to failed to reconcile rules due to ListenerNotFound: One or more listeners not found\n\tstatus code: 400, request id: 9adc08a0-d25d-499a-b347-2170489429d7"  "controller"="alb-ingress-controller" "request"={"Namespace":"gorush","Name":"gorush"}
I0206 11:08:56.610223       1 rules.go:60] gorush/gorush: creating rule 1 on arn:aws:elasticloadbalancing:us-east-2:534***385:listener/app/3407a88c-gorush-gorush-f66a/d31b461c65a278f0/6248c53e3077174d
I0206 11:08:56.648485       1 rules.go:77] gorush/gorush: rule 1 created with conditions [{    Field: "path-pattern",    Values: ["/*"]  }]
I0206 11:08:57.174774       1 rules.go:82] gorush/gorush: modifying rule 1 on arn:aws:elasticloadbalancing:us-east-2:534***385:listener/app/3407a88c-gorush-gorush-f66a/d31b461c65a278f0/6248c53e3077174d
I0206 11:08:57.215907       1 rules.go:98] gorush/gorush: rule 1 modified with conditions [{    Field: "path-pattern",    Values: ["/*"]  }]

[/simterm]

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

[simterm]

$ kubectl -n gorush get ingress
NAME     HOSTS   ADDRESS                                                                     PORTS   AGE
gorush   *       internal-3407a88c-gorush-gorush-f66a-***.us-east-2.elb.amazonaws.com   80      3m57s

[/simterm]

Проверка API

Подключаемся на наш Bastion хост, устанавливаем dnsutils, dnsping и curl.

Что бы быстро получить под для подключения можно выполнить:

[simterm]

$ kubectl -n gorush get pod | grep gorush | cut -d" " -f 1 | tail -1
gorush-59bd9dd4fc-klsbz

[/simterm]

И для подключения в контейнер Бастиона:

[simterm]

$ kubectl -n gorush exec -ti $(kubectl -n gorush get pod | grep gorush | cut -d" " -f 1 | tail -1) -c bastion bash
root@gorush-59bd9dd4fc-klsbz:/#

[/simterm]

Устанавливаем пакеты, проверяем доступ к пуш-серверу:

[simterm]

root@gorush-59bd9dd4fc-klsbz:/# apt update && apt -y install dnsutils curl dnsdiag
root@gorush-59bd9dd4fc-klsbz:/# curl dualstack.internal-3407a88c-gorush-gorush-f66a-***.us-east-2.elb.amazonaws.com
{"text":"Welcome to notification server."}

[/simterm]

Всё работает.

Route53

Проверяем, что URL нашего Internal ALB резолвится на приватные IP:

[simterm]

root@gorush-59bd9dd4fc-klsbz:/# dig +short dualstack.internal-3407a88c-gorush-gorush-f66a-***.us-east-2.elb.amazonaws.com
10.0.15.167
10.0.23.138

[/simterm]

Отлично.

Для работы мы хотим использовать собственный домен.

Переходим в AWS Route53, добавляем запись типа A и Alias:

Выполняем GET с Бастиона, что бы запрос пошёл внутри VPC используя её DNS, что бы разрезолвить имя на приватные IP:

[simterm]

root@gorush-59bd9dd4fc-klsbz:/# dig push.example.com +short
10.0.23.138
10.0.15.167

[/simterm]

И проверяем сервис:

[simterm]

root@gorush-59bd9dd4fc-klsbz:/# curl push.example.com
{"text":"Welcome to notification server."}

[/simterm]

Готово.