So stellen Sie eine Ruby on Rails-Anwendung mit HAProxy Ingress, Unicorn / Puma und Web-Sockets bereit

Nach monatelangen Tests haben wir die Ruby on Rails-Anwendung schließlich mit dem Kubernetes-Cluster auf die Produktion portiert.


In diesem Artikel werde ich Ihnen zeigen, wie Sie das pfadbasierte Routing für eine Ruby on Rails-Anwendung in Kubernetes mit einem HAProxy Ingress-Controller konfigurieren.


Bild


Es wird davon ausgegangen, dass Sie eine Vorstellung davon haben, was Pods , Bereitstellungen , Dienste , eine Konfigurationszuordnung und Ingress in Kubernetes sind.


Normalerweise gibt es in einer Rails-Anwendung Dienste wie Unicorn / Puma, Sidekiq / Delayed Job / Resque, Web Sockets und mehrere spezielle API-Dienste. Wir hatten einen Webdienst über den Balancer geöffnet, und alles hat gut funktioniert. Aber der Verkehr wuchs und es war notwendig, ihn per URL oder Pfad weiterzuleiten.


Kubernetes hat keine schlüsselfertige Lösung, um diese Art von Last auszugleichen. Ein Alb-Ingress-Controller wird bereits dafür entwickelt, befindet sich jedoch noch nicht im Alpha-Stadium und für die Produktion.


Für das pfadbasierte Routing ist es am besten, einen Ingress-Controller zu verwenden .


Wir haben das Problem untersucht und festgestellt, dass k8s unterschiedliche Lösungen für Ingress bietet.



Wir haben mit Nginx-Ingress und HAProxy experimentiert und uns für HAProxy entschieden - es ist besser für die Rails-Web-Sockets, die wir im Projekt verwendet haben.


Ich zeige Ihnen Schritt für Schritt, wie Sie HAProxy Ingress an eine Rails-Anwendung anhängen.


Einrichten einer Rails-Anwendung mit einem HAProxy Ingress-Controller


Folgendes werden wir tun:


  • Erstellen wir eine Rails-Anwendung mit verschiedenen Diensten und Bereitstellungen.
  • Erstellen Sie ein TLS-Geheimnis für SSL.
  • Erstellen Sie eine HAProxy Ingress-Konfigurationszuordnung.
  • Erstellen Sie einen HAProxy Ingress-Controller.
  • Lassen Sie uns den Zugriff auf Ingress über einen Dienst wie LoadBalancer öffnen.
  • Lassen Sie uns DNS-Anwendungen für den Ingress-Dienst konfigurieren.
  • Erstellen Sie verschiedene Ingress-Regeln für das pfadbasierte Routing.
  • Testen Sie das Routing basierend auf dem Pfad.

Erstellen wir ein Manifest für die Bereitstellung einer Rails-Anwendung für verschiedene Dienste - Web (Einhorn), Hintergrundaufgaben (Sidekiq), Web-Socket (Ruby Thin), API (Einhorn dediziert).


Hier ist unsere Vorlage für die Bereitstellung von Webanwendungen und Diensten.


--- apiVersion: v1 kind: Deployment metadata: name: test-production-web labels: app: test-production-web namespace: test spec: template: metadata: labels: app: test-production-web spec: containers: - image: <your-repo>/<your-image-name>:latest name: test-production imagePullPolicy: Always env: - name: POSTGRES_HOST value: test-production-postgres - name: REDIS_HOST value: test-production-redis - name: APP_ENV value: production - name: APP_TYPE value: web - name: CLIENT value: test ports: - containerPort: 80 imagePullSecrets: - name: registrykey --- apiVersion: v1 kind: Service metadata: name: test-production-web labels: app: test-production-web namespace: test spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: test-production-web 

Hier ist die Hintergrundvorlage für die Bereitstellung und den Dienst der App.


 --- apiVersion: v1 kind: Deployment metadata: name: test-production-background labels: app: test-production-background namespace: test spec: template: metadata: labels: app: test-production-background spec: containers: - image: <your-repo>/<your-image-name>:latest name: test-production imagePullPolicy: Always env: - name: POSTGRES_HOST value: test-production-postgres - name: REDIS_HOST value: test-production-redis - name: APP_ENV value: production - name: APP_TYPE value: background - name: CLIENT value: test ports: - containerPort: 80 imagePullSecrets: - name: registrykey --- apiVersion: v1 kind: Service metadata: name: test-production-background labels: app: test-production-background namespace: test spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: test-production-background 

Hier finden Sie den Socket und die Dienstvorlage für die Bereitstellung von Webanwendungen.


 --- apiVersion: v1 kind: Deployment metadata: name: test-production-websocket labels: app: test-production-websocket namespace: test spec: template: metadata: labels: app: test-production-websocket spec: containers: - image: <your-repo>/<your-image-name>:latest name: test-production imagePullPolicy: Always env: - name: POSTGRES_HOST value: test-production-postgres - name: REDIS_HOST value: test-production-redis - name: APP_ENV value: production - name: APP_TYPE value: websocket - name: CLIENT value: test ports: - containerPort: 80 imagePullSecrets: - name: registrykey --- apiVersion: v1 kind: Service metadata: name: test-production-websocket labels: app: test-production-websocket namespace: test spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: test-production-websocket 

Hier finden Sie Informationen zur Bereitstellung und zum Dienst der Anwendungs-API.


 --- `apiVersion: v1 kind: Deployment metadata: name: test-production-api labels: app: test-production-api namespace: test spec: template: metadata: labels: app: test-production-api spec: containers: - image: <your-repo>/<your-image-name>:latest name: test-production imagePullPolicy: Always env: - name: POSTGRES_HOST value: test-production-postgres - name: REDIS_HOST value: test-production-redis - name: APP_ENV value: production - name: APP_TYPE value: api - name: CLIENT value: test ports: - containerPort: 80 imagePullSecrets: - name: registrykey --- apiVersion: v1 kind: Service metadata: name: test-production-api labels: app: test-production-api namespace: test spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: test-production-api 

Lassen Sie uns das Manifest mit dem kubectl apply .


 $ kubectl apply -f test-web.yml -f test-background.yml -f test-websocket.yml -f test-api.yml deployment "test-production-web" created service "test-production-web" created deployment "test-production-background" created service "test-production-background" created deployment "test-production-websocket" created service "test-production-websocket" created deployment "test-production-api" created service "test-production-api" created 

Sobald die Anwendung bereitgestellt und gestartet wurde, müssen Sie einen HAProxy Ingress erstellen. Aber zuerst erstellen wir ein TLS-Geheimnis mit einem SSL-Schlüssel und einem Zertifikat.


Er lässt HTTPS für die Anwendungs-URL zu und beendet sie auf L7.


$ kubectl create secret tls tls-certificate --key server.key --cert server.pem


server.key hier ist unser SSL-Schlüssel und server.pem ist unser SSL-Zertifikat im PEM-Format.


Erstellen Sie nun die Ressourcen des HAProxy-Controllers.


HAProxy-Konfigurationskarte


Alle verfügbaren Konfigurationsoptionen für HAProxy finden Sie hier .


 apiVersion: v1 data: dynamic-scaling: "true" backend-server-slots-increment: "4" kind: ConfigMap metadata: name: haproxy-configmap namespace: test 

HAProxy Ingress Controller-Bereitstellung


Bereitstellungsmuster für einen Ingress-Controller mit mindestens zwei Replikaten zum Verwalten einer sequentiellen Bereitstellung.


 apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: run: haproxy-ingress name: haproxy-ingress namespace: test spec: replicas: 2 selector: matchLabels: run: haproxy-ingress template: metadata: labels: run: haproxy-ingress spec: containers: - name: haproxy-ingress image: quay.io/jcmoraisjr/haproxy-ingress:v0.5-beta.1 args: - --default-backend-service=$(POD_NAMESPACE)/test-production-web - --default-ssl-certificate=$(POD_NAMESPACE)/tls-certificate - --configmap=$(POD_NAMESPACE)/haproxy-configmap - --ingress-class=haproxy ports: - name: http containerPort: 80 - name: https containerPort: 443 - name: stat containerPort: 1936 env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace 

In diesem Manifest interessieren uns insbesondere die Argumente, die an den Controller übergeben werden.
--default-backend-service ist der Service, den die Anwendung verwendet, wenn keine Regeln mit der Anforderung übereinstimmen.


Wir haben diesen test-production-web , aber es kann sich um eine benutzerdefinierte 404-Seite oder ähnliches handeln - Sie entscheiden.


--default-ssl-certificate ist das gerade erstellte SSL-Geheimnis. Es wird SSL auf L7 beenden und die Anwendung wird extern über HTTPS zugänglich sein.


HAProxy Ingress Service


Dies ist eine Art LoadBalancer Dienst, der den Zugriff des LoadBalancer auf unseren Ingress-Controller ermöglicht.


Der LoadBalancer hat Zugriff auf das öffentliche Netzwerk und das interne Netzwerk von Kubernetes und leitet auf L7 den Datenverkehr an den Ingress-Controller weiter.


 apiVersion: v1 kind: Service metadata: labels: run: haproxy-ingress name: haproxy-ingress namespace: test spec: type: LoadBalancer ports: - name: http port: 80 protocol: TCP targetPort: 80 - name: https port: 443 protocol: TCP targetPort: 443 - name: stat port: 1936 protocol: TCP targetPort: 1936 selector: run: haproxy-ingress 

Wenden wir alle HAProxy-Manifeste an.


 $ kubectl apply -f haproxy-configmap.yml -f haproxy-deployment.yml -f haproxy-service.yml configmap "haproxy-configmap" created deployment "haproxy-ingress" created service "haproxy-ingress" created 

Wenn alle Ressourcen ausgeführt werden, geben Sie den Endpunkt des LoadBalancer an.


 $ kubectl -n test get svc haproxy-ingress -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR haproxy-ingress LoadBalancer 100.67.194.186 a694abcdefghi11e8bc3b0af2eb5c5d8-806901662.us-east-1.elb.amazonaws.com 80:31788/TCP,443:32274/TCP,1936:32157/TCP 2m run=ingress 

Ordnen Sie DNS der Anwendungs-URL zu


Sobald wir den ELB-Endpunkt für den Ingress-Dienst angeben, müssen wir den DNS des Dienstes und die Anforderungs-URL (z. B. test-rails-app.com ) test-rails-app.com .


Ingress-Implementierung


Der schwierigste Teil liegt dahinter, es ist Zeit, Ingress- und Path-basierte Regeln zu konfigurieren.


Wir brauchen die folgenden Regeln.


Anfragen an https://test-rails-app.com werden vom test-production-web bearbeitet.


Anfragen an https://test-rails-app.com/websocket werden vom test-production-websocket bearbeitet.


Anfragen an https://test-rails-app.com/api werden vom test-production-api bearbeitet.


Erstellen wir ein Ingress-Manifest mit all diesen Regeln.


 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress namespace: test spec: tls: - hosts: - test-rails-app.com secretName: tls-certificate rules: - host: test-rails-app.com http: paths: - path: / backend: serviceName: test-production-web servicePort: 80 - path: /api backend: serviceName: test-production-api servicePort: 80 - path: /websocket backend: serviceName: test-production-websocket servicePort: 80 

Im Falle von Konfigurationsänderungen haben wir Anmerkungen für Ingress-Ressourcen .


Wie erwartet wird unser Datenverkehr standardmäßig an / an den test-production-web , /api an test-production-api und /websocket an test-production-websocket .


Wir brauchten Pfad-basiertes Routing und SSL-Terminierung auf L7 in Kubernetes, und die Ingress-Implementierung löste dieses Problem.

Source: https://habr.com/ru/post/de424789/


All Articles