Executando verificação de segurança...
1

(DÚVIDAS) Docker Swarm e NGINX

Tenho uma aplicação utilizando node pro server, vue pro client, rabbitmq, postgre e nginx.
No momento estou tentando fazer o deploy de tudo isso com Swarm no GCP em uma VM (Compute Engine).
Atualmente só tenho 1 máquina manager e configurada.

No GCP tenho acesso normal a porta 80, então no firewall eu liberei o acesso as portas 15672 (rabbit management) e 3000 (api). Teria alguma maneira "correta" de fazer isso, ou seria só liberar essas portas mesmo?

Também gostaria de saber, o correto é criar um load balancer pra tudo isso (ai aponta o dominio pro ip do loadbalancer)?

Segue o docker-compose.yml pra criar a stack de serviços:

version: "3.8"

services:
  postgres:
    container_name: postgres
    image: postgres:16
    restart: always
    volumes:
      - postgres_volume:/var/lib/postgresql/data
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: default
    networks:
      - default
      
  rabbitmq:
    container_name: rabbitmq
    image: rabbitmq:3.13-management
    restart: always
    ports:
      - 5672:5672
      - 15672:15672
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
    volumes:
    - rabbitmq_data:/var/lib/rabbitmq
    networks:
      - default

  backend:
    container_name: backend
    image: <BACKEND-IMAGE>
    restart: always
    ports:
      - 3000:3000
    depends_on:
      - postgres
    environment:
      PORT: 3000
      HOST: 0.0.0.0
      NODE_ENV: development
      APP_KEY: ''
      APP_TITLE: 'Teste'
      DRIVE_DISK: gcs
      SERVER_URL: 'http://<IP-DA-VM>:3000'
      CLIENT_URL: 'http://<IP-DA-VM>'
      HASH_DRIVER: scrypt
      SESSION_DRIVER: cookie
      # Database
      DB_CONNECTION: pg
      PG_HOST: application_postgres
      PG_PORT: 5432
      PG_USER: postgres
      PG_PASSWORD: postgres
      PG_DB_NAME: default
      # Bucket
      GCS_BUCKET: ''
      GCS_PROJECT_ID: ''
      GCS_KEY: '{}'
      #Rabbit
      RABBITMQ_HOSTNAME: application_rabbitmq
      RABBITMQ_USER: guest
      RABBITMQ_PASSWORD: guest
      RABBITMQ_PORT: 5672
      RABBITMQ_PROTOCOL: amqp://
    networks:
      - default

  frontend:
    container_name: frontend
    image: <FRONT-IMAGE>
    restart: always
    environment:
      VITE_APP_TITLE: "Teste"
      VITE_APP_ENV: production
      VITE_API_URL: 'http://<IP-DA-VM>/api'
      VITE_SERVER_URL: 'http://<IP-DA-VM>:3000'
      VITE_GCS_BUCKET: ''
    ports:
      - 80:80
    depends_on:
      - backend
    networks:
      - default

volumes:
  postgres_volume:
  rabbitmq_data:

networks:
  default:

Configurações do NGINX (como o nome da stack no swarm é application, ele cria os serviços com o prefixo application_, então no proxy_pass passei o nome dos serviços, está correto?):

server {
    listen 80;
    listen [::]:80;
    server_name <DOMINIO/IP-DA-VM>;

    root /usr/share/nginx/html;

    large_client_header_buffers 4 32k;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    charset utf-8;

    # Location client
    location / {
        proxy_pass https://application_frontend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_cache_bypass $http_upgrade;
    }

    # Location websocket
    location /socket.io/ {
        proxy_pass https://application_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_cache_bypass $http_upgrade;
    }

    # Location backend
    location /api/ {
        proxy_pass https://application_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_cache_bypass $http_upgrade;
    }
}

Agradeço!

Carregando publicação patrocinada...
2

Salve! Não sou o mais expert em Docker Swarm mas tive uma experiência considerável, vou tentar ajudar.

No GCP tenho acesso normal a porta 80, então no firewall eu liberei o acesso as portas 15672 (rabbit management) e 3000 (api). Teria alguma maneira "correta" de fazer isso, ou seria só liberar essas portas mesmo?

Lembre-se que a magia do Docker Swarm seria montar um cluster com vários nós, pelo que me lembro a porta em que os nós se "conversam" é a 2377 (TCP), essa tem que ser liberada. Um adendo é a porta do RabbitMQ, o correto seria só a porta da aplicação web (Nginx no caso), para casos de desenvolvimento é tranquilo, mas o mínimo de portas que você expor, melhor.

Também gostaria de saber, o correto é criar um load balancer pra tudo isso (ai aponta o dominio pro ip do loadbalancer)?

Ao criar um cluster Docker Swarm, o próprio cluster funciona como um Load Balancer, o que eu utilizava era um Load Balancer em outro servidor sem ser Docker Swarm que fazia um RoundRobin diretamente nos servidores, faz um booom tempo que não mexo nisso (nem estou na empresa mais), mas configurei um Traefik como rede interna do Docker também, é relativamente complexo de você precisa pesquisar bem.

Leitura recomendada: How is load balancing done in Docker-Swarm mode

Outra coisa que você precisa se atentar é sobre a persistência dos dados, vamos ao caso do PostgreSQL:

    ...
    volumes:
      - postgres_volume:/var/lib/postgresql/data
    ...

Ao utilizar um Cluster, caso você reinicie a aplicação e não especifique em qual nó ela irá subir, o Docker escolherá algum nó utilizando alguma política (por exemplo, para equilibrar o uso dos recursos) e poderá subir uma instância em um nó onde não tinha o volume, criando inconsistência nos dados, você pode utilizar um storage em rede como o GlusterFS, veja aqui. Lembrando que há várias abordagens para estes casos.

Configurações do NGINX (como o nome da stack no swarm é application, ele cria os serviços com o prefixo application_, então no proxy_pass passei o nome dos serviços, está correto?):

Sobre a abordagem, creio que esteja correto assim, depende muito como você quer fazer isso, sobre o application_<serviço> você pode especificar o hostname no manifesto da stack, o Docker internamente resolve o hostname, nâo o container_name, novamente se atentando ao utilizar replicas, já que ele criaria algo do tipo aplicacao-1, aplicacao-2...


Espero ter ajudado e nâo complicado mais as coisas, o Docker Swarm é muito massa e dá uma experiência bacana sobre escalabilidade, além de ter uma curva de aprendizado menor em comparação ao Kubernetes.

Divirta-se! 😄

1

Top Marlon, muito obrigado por essas dicas!

Alterei o firewall (deixando apenas a porta 80 por enquanto) e o restante (como por exemplo portainer na porta 9000 e rabbitmq na porta 15672) no nginx fazendo proxy reverso pro serviço do swarm.

Ainda vou aprender a questão do load balancer, tenho apenas a máquina manager rodando tudo.

No caso do nginx, eu preciso redirecionar pro nome do serviço (por exemplo backend) e não application_backend (onde application_ é a stack, então é necessário ignora-lá).