Cómo implementar una aplicación Ruby on Rails con HAProxy Ingress, unicornio / puma y sockets web

Después de meses de pruebas, finalmente trasladamos la aplicación Ruby on Rails a producción con el clúster Kubernetes.


En este artículo, le mostraré cómo configurar el enrutamiento basado en la ruta para una aplicación Ruby on Rails en Kubernetes con un controlador HAProxy Ingress.


imagen


Se supone que tiene alguna idea de qué pods , implementaciones , servicios , un mapa de configuración e Ingress en Kubernetes son.


Por lo general, en una aplicación Rails hay servicios como unicornio / puma, sidekiq / trabajo retrasado / resque, sockets web y varios servicios API especiales. Tuvimos un servicio web abierto a través del equilibrador, y todo funcionó bien. Pero el tráfico creció y fue necesario enrutarlo por URL o ruta.


Kubernetes no tiene una solución llave en mano para equilibrar este tipo de carga. Ya se está desarrollando un controlador de ingreso alb para él, pero todavía no está en la etapa alfa y para la producción.


Para el enrutamiento basado en la ruta, era mejor usar un controlador Ingress .


Estudiamos el problema y descubrimos que k8s tiene diferentes soluciones para Ingress.



Experimentamos con nginx-ingress y HAProxy y nos decidimos por HAProxy: es mejor para los zócalos web Rails que utilizamos en el proyecto.


Le mostraré paso a paso cómo adjuntar HAProxy Ingress a una aplicación Rails.


Configurar una aplicación Rails con un controlador HAProxy Ingress


Esto es lo que haremos:


  • Creemos una aplicación Rails con diferentes servicios e implementaciones.
  • Crea un secreto TLS para SSL.
  • Cree un mapa de configuración HAProxy Ingress.
  • Cree un controlador de ingreso HAProxy.
  • Abramos el acceso a Ingress a través de un servicio como LoadBalancer.
  • Configuremos las aplicaciones DNS para el servicio Ingress.
  • Cree diferentes reglas de entrada para el enrutamiento basado en la ruta.
  • Prueba de enrutamiento basado en ruta.

Creemos un manifiesto para implementar una aplicación Rails para diferentes servicios: web (unicornio), tareas en segundo plano (sidekiq), socket web (ruby thin), API (unicorn dedicado).


Aquí está nuestra aplicación web de despliegue y plantilla de servicio.


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

Aquí está el despliegue de la aplicación en segundo plano y la plantilla de servicio.


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

Aquí está el socket de la aplicación web y la plantilla de servicio.


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

Aquí está la implementación de la API de la aplicación y la información del servicio.


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

kubectl apply el manifiesto con el 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 

Una vez que la aplicación se implementa y se inicia, deberá crear un ingreso HAProxy. Pero primero, creemos un secreto TLS con una clave SSL y un certificado.


Permitirá HTTPS para la URL de la aplicación y la terminará en L7.


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


server.key aquí es nuestra clave SSL, y server.pem es nuestro certificado SSL en formato pem.


Ahora cree los recursos del controlador HAProxy.


Mapa de configuración de HAProxy


Vea todas las opciones de configuración disponibles para HAProxy aquí .


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

Implementación del controlador de ingreso HAProxy


Patrón de implementación para un controlador Ingress con al menos dos réplicas para administrar una implementación secuencial.


 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 

En este manifiesto, estamos particularmente interesados ​​en los argumentos pasados ​​al controlador.
--default-backend-service es el servicio que usará la aplicación si no hay reglas que coincidan con la solicitud.


Tenemos este test-production-web , pero puede ser una página 404 personalizada o algo así, usted decide.


--default-ssl-certificate es el secreto SSL que acabamos de crear. Terminará SSL en L7, y la aplicación será accesible externamente a través de HTTPS.


Servicio de ingreso de HAProxy


Este es un tipo de servicio LoadBalancer que permite el acceso del tráfico del cliente a nuestro controlador Ingress.


LoadBalancer tiene acceso a la red pública y a la red interna de Kubernetes, y en L7 enruta el tráfico al controlador Ingress.


 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 

Apliquemos todos los manifiestos HAProxy.


 $ 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 

Cuando se estén ejecutando todos los recursos, especifique el punto final de LoadBalancer.


 $ 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 

Asignar DNS a la URL de la aplicación


Tan pronto como especifiquemos el punto final ELB para el servicio Ingress, necesitaremos asignar el DNS del servicio y la URL de solicitud (por ejemplo, test-rails-app.com ).


Implementación de entrada


La parte más difícil está detrás, es hora de configurar las reglas basadas en Ingress y Path.


Necesitamos las siguientes reglas.


Las solicitudes a https://test-rails-app.com serán procesadas por el test-production-web .


Las solicitudes a https://test-rails-app.com/websocket serán procesadas por el servicio test-production-websocket .


El test-production-api procesará las solicitudes a https://test-rails-app.com/api .


Creemos un manifiesto de Ingress con todas estas reglas.


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

En caso de cambios de configuración, tenemos anotaciones para los recursos de Ingress .


Como se esperaba, de manera predeterminada, nuestro tráfico a / enruta al test-production-web , /api a test-production-api , y /websocket a test-production-websocket .


Necesitábamos enrutamiento basado en Path y terminación SSL en L7 en Kubernetes, y la implementación de Ingress resolvió este problema.

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


All Articles