Após meses de teste, finalmente transportamos o aplicativo Ruby on Rails para produção com o cluster Kubernetes.
Neste artigo, mostrarei como configurar o roteamento baseado em caminho para um aplicativo Kubernetes Ruby on Rails com um controlador HAProxy Ingress.

Supõe-se que você tenha alguma idéia do que são pods , implantações , serviços , um mapa de configuração e o Ingress no Kubernetes.
Geralmente, em um aplicativo Rails, existem serviços como unicórnio / puma, sidekiq / trabalho atrasado / resque, soquetes da web e vários serviços API especiais. Tínhamos um serviço da web aberto através do balanceador e tudo funcionava bem. Mas o tráfego cresceu e foi necessário rotear por URL ou caminho.
O Kubernetes não possui uma solução pronta para balancear esse tipo de carga. Um controlador de entrada de alb já está sendo desenvolvido para ele, mas ainda não está no estágio alfa e para produção.
Para o roteamento baseado em caminho, era melhor usar um controlador do Ingress .
Estudamos a questão e descobrimos que o k8s tem soluções diferentes para o Ingress.
Experimentamos o nginx-ingress e o HAProxy e decidimos pelo HAProxy - é melhor para os soquetes da web Rails que usamos no projeto.
Vou mostrar passo a passo como anexar o HAProxy Ingress a um aplicativo Rails.
Configurando um aplicativo Rails com um controlador HAProxy Ingress
Aqui está o que faremos:
- Vamos criar um aplicativo Rails com diferentes serviços e implantações.
- Crie um segredo TLS para SSL.
- Crie um mapa de configuração do HAProxy Ingress.
- Crie um controlador HAProxy Ingress.
- Vamos abrir o acesso ao Ingress através de um serviço como o LoadBalancer.
- Vamos configurar aplicativos DNS para o serviço Ingress.
- Crie regras diferentes do Ingress para roteamento baseado em caminho.
- Roteamento de teste com base no caminho.
Vamos criar um manifesto para implantar um aplicativo Rails para diferentes serviços - web (unicórnio), tarefas em segundo plano (sidekiq), soquete da web (ruby thin), API (unicorn dedicado).
Aqui está nosso modelo de serviço e aplicativo da Web de implantação.
--- 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
Aqui está o modelo de serviço e implantação de aplicativos em segundo plano.
--- 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
Aqui está o soquete do aplicativo da web de implantação e o modelo de serviço.
--- 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
Aqui estão as informações de implantação e serviço da API do aplicativo.
--- `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
Vamos executar o manifesto com o 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
Depois que o aplicativo for implantado e iniciado, você precisará criar um ingresso HAProxy. Mas primeiro, vamos criar um segredo TLS com uma chave e certificado SSL.
Ele permitirá HTTPS para o URL do aplicativo e o encerrará no L7.
$ kubectl create secret tls tls-certificate --key server.key --cert server.pem
server.key
aqui é a nossa chave SSL e server.pem
é o nosso certificado SSL no formato pem.
Agora crie os recursos do controlador HAProxy.
Mapa de configuração do HAProxy
Veja todas as opções de configuração disponíveis para o HAProxy aqui .
apiVersion: v1 data: dynamic-scaling: "true" backend-server-slots-increment: "4" kind: ConfigMap metadata: name: haproxy-configmap namespace: test
Implantação do controlador HAProxy Ingress
Padrão de implantação para um controlador do Ingress com pelo menos duas réplicas para gerenciar uma implantação seqüencial.
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
Neste manifesto, estamos particularmente interessados nos argumentos transmitidos ao controlador.
--default-backend-service
é o serviço que o aplicativo usará se nenhuma regra corresponder à solicitação.
Temos esse serviço de test-production-web
, mas pode ser uma página 404 personalizada ou algo assim - você decide.
--default-ssl-certificate
é o segredo SSL que acabamos de criar. Ele encerrará o SSL no L7 e o aplicativo estará acessível externamente via HTTPS.
Serviço de ingresso HAProxy
Este é um tipo de serviço LoadBalancer
que permite o acesso do tráfego do cliente ao nosso controlador Ingress.
O LoadBalancer tem acesso à rede pública e à rede interna do Kubernetes e, no L7, direciona o tráfego para o controlador do 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
Vamos aplicar todos os manifestos 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
Quando todos os recursos estiverem em execução, especifique o terminal do 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
Mapear DNS para URL do aplicativo
Assim que especificarmos o ponto de extremidade ELB para o serviço Ingress, precisaremos mapear o DNS do serviço e o URL da solicitação (por exemplo, test-rails-app.com
).
Implementação do Ingress
A parte mais difícil está por trás, é hora de configurar as regras do Ingress e do Path.
Precisamos das seguintes regras.
As solicitações para https://test-rails-app.com serão processadas pelo serviço de test-production-web
.
As solicitações para https://test-rails-app.com/websocket serão processadas pelo serviço test-production-websocket
.
As solicitações para https://test-rails-app.com/api serão processadas pelo serviço test-production-api
.
Vamos criar um manifesto do Ingress com todas essas regras.
--- 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
No caso de alterações na configuração, temos anotações para os recursos do Ingress .
Como esperado, por padrão, nosso tráfego para /
roteado para o test-production-web
, /api
para test-production-api
e /websocket
para test-production-websocket
.
Precisávamos de roteamento baseado em caminho e terminação SSL no L7 no Kubernetes, e a implementação do Ingress resolveu esse problema.