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]
Готово.