Acho que um sistema docker com nginx + ssl + nodered + grafana + mosquitto + influxdb é bastante comum. Espero que isso ajude outros.
Tenho o nginx e o ssl (certbot) funcionando perfeitamente, e se eu for para http://example.com:1880 ou http://example.com:1880/ui ou http://example.com:3000 consigo acessar perfeitamente os fluxos nodered, ui e grafana.
Mas eu preciso de "mais".
Quero ter "subpastas" como esta:
- grafana irá para example.com/grafana
- A edição do fluxo nodered deve ir para example.com/nodered
- a interface do usuário nodered (painel ou flowfuse, tanto faz) deve ir para example.com
Tentei centenas de configurações editando docker-compose.yml, nginx.conf, settings.js e grafana.ini, mas... sem sorte.
Espero que alguém possa ter algo semelhante e compartilhar conosco seus arquivos de configuração.
Estas são as minhas neste momento (eu apaguei minhas "tentativas de configuração erradas")
server {
listen 80;
listen [::]:80;
server_name example.com;
location /.well-known/acme-challenge {
allow all;
root /var/www/certbot;
}
location / {
# rewrite ^ https://$host$request_uri? permanent;
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
#STATIC index index.php index.html index.htm;
#STATIC root /var/www/html;
server_tokens off; #Disable the Nginx version in headers for security
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
#linuxserver.io
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Logs for Nginx access and errors
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~ /\.ht { deny all; }
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { log_not_found off; access_log off; allow all; }
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ { expires max; log_not_found off; }
}
Docker-compose.yml
services:
mosquitto:
image: eclipse-mosquitto:2
container_name: mosquitto
ports:
- "1883:1883"
#- "8883:8883"
#- "9001:9001" # Websocket opcional
volumes:
- ./mosquitto-data:/mosquitto
restart: unless-stopped
#environment:
# - 'TZ='Europe/Brussels'
influxdb:
image: influxdb:2.7
container_name: influxdb
ports:
- "8086:8086"
volumes:
- ./influxdb-data:/var/lib/influxdb2
- ./influxdb-config:/etc/influxdb2
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=user
- DOCKER_INFLUXDB_INIT_PASSWORD=password
- DOCKER_INFLUXDB_INIT_ORG=org
- DOCKER_INFLUXDB_INIT_BUCKET=buck
- DOCKER_INFLUXDB_INIT_RETENTION=0
restart: unless-stopped
grafana:
image: grafana/grafana:11.4.0
container_name: grafana
ports:
- "3000:3000"
volumes:
- ./grafana-data:/var/lib/grafana
- ./grafana-config:/etc/grafana
#user: "0"
environment:
- GF_SECURITY_ADMIN_USER=user
- GF_SECURITY_ADMIN_PASSWORD=password
#- GF_SERVER_ROOT_URL=https://example.com/grafana
#- GF_SERVER_DOMAIN=https://example.com/
#- GF_INSTALL_PLUGINS
restart: unless-stopped
nodered:
image: nodered/node-red:4.0
container_name: nodered
ports:
- "1880:1880"
volumes:
- ./nodered-data:/data
restart: unless-stopped
nginx:
image: nginx:1.27.3-bookworm
container_name: nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx-data:/etc/nginx/conf.d
- ./nginx-logs:/var/log/nginx
- ./letsencrypt-data:/etc/letsencrypt
- ./certbot-data:/var/www/certbot
depends_on:
- nodered
- grafana
- influxdb
restart: unless-stopped
certbot:
image: certbot/certbot
container_name: certbot
depends_on:
- nginx
volumes:
- ./letsencrypt-data:/etc/letsencrypt
- ./certbot-data:/var/www/certbot
#restart: unless-stopped
# command: certonly --webroot --webroot-path=/var/www/certbot/ --email [email protected] --agree-tos --no-eff-email --staging -d example.com
#command: certonly --webroot --webroot-path=/var/www/certbot/ --email [email protected] --agree-tos --no-eff-email --force-renewal -d example.com
volumes:
mosquitto-data:
influxdb-data:
influxdb-config:
grafana-data:
grafana-config:
nodered-data:
letsencrypt-data:
nginx-data:
certbot-data:
O que preciso alterar/adicionar a esses arquivos?:
- nodered: docker-compose, nginx e settings.js
- grafana: docker-compose, nginx e grafana.ini
Muito obrigado.
2025/01/15 19:00 (UTC): Comecei com o grafana para ir passo a passo (acho que é mais fácil) Mudei o docker-compose para:
grafana:
image: grafana/grafana:11.4.0
container_name: grafana
ports:
- "3000:3000"
volumes:
- ./grafana-data:/var/lib/grafana
- ./grafana-config:/etc/grafana
#user: "0"
environment:
- GF_SECURITY_ADMIN_USER=user
- GF_SECURITY_ADMIN_PASSWORD=password
- GF_SERVER_ROOT_URL=https://example.com/grafana/
- GF_SERVER_DOMAIN=https://example.com/
- GF_SERVER_SERVER_FORM_SUB_PATH=true
restart: unless-stopped
E nginx.conf:
# Grafana Dashboard
location /grafana/ {
proxy_set_header Host $host;
proxy_pass http://grafana:3000/;
}
# Proxy Grafana Live WebSocket connections.
location /grafana/api/live/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_pass http://grafana:3000/;
}
Não alterei o grafana.ini porque encontrei uma maneira melhor de usar variáveis de ambiente no docker-compose.
Estou entendendo "alguma coisa", pelo menos, "grafana" está respondendo:
If you're seeing this Grafana has failed to load its application files
This could be caused by your reverse proxy settings.
If you host grafana under a subpath make sure your grafana.ini root_url setting includes subpath. If not using a reverse proxy make sure to set serve_from_sub_path to true.
If you have a local dev build make sure you build frontend using: yarn start, or yarn build.
Sometimes restarting grafana-server can help.
Check if you are using a non-supported browser. For more information, refer to the list of supported browsers .
Então ainda não consigo fazê-lo funcionar.
2025/01/15 20:30 (UTC): Aqui estou testando o nodered, a configuração de "proxy" mais simples.
Adicionei isso em nginx.conf
location / {
proxy_pass http://nodered:1880/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header X-Forwarded-Proto $scheme;
# # WebSocket support
proxy_http_version 1.1;
# proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
rewrite ^/(.*) /$1 break;
}
Eu tentei várias tentativas comentando e descomentando cada linha (cada tentativa === uma linha comentada ou descomentada)
Novamente resultado parcial. Posso ver que o nodered está respondendo, mas com uma página em branco, pois várias páginas estáticas não estão carregando.
Estes são alguns dos erros que posso ver no console:
Failed to load resource: the server responded with a status of 404 ()Understand this errorAI
example.com/:1 Refused to execute script from 'https://example.com/vendor/vendor.js?v=3305aad6c0c6' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.Understand this errorAI
monaco-bootstrap.js:1
Failed to load resource: the server responded with a status of 404 ()Understand this errorAI
red.min.js:1
Failed to load resource: the server responded with a status of 404 ()Understand this errorAI
main.min.js:1
Então, novamente, estou me sentindo estúpido sobre isso. Provavelmente isso também é fácil de resolver, eu tentei todas as coisas que encontrei pesquisando no Google e usando chatgpt (por exemplo)
Mais uma vez obrigado pela sua ajuda.
2025/01/18 9:00 (UTC):
Continuo tentando coisas diferentes. Desta vez a configuração mais simples do grafana apenas para testar as configurações docker-compose e nginx. Sem sorte de novo.
# Grafana Dashboard
location / {
proxy_set_header Host $host;
proxy_pass http://grafana:3000/;
}
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
volumes:
- ./grafana-data:/var/lib/grafana
- ./grafana-config:/etc/grafana
#user: "0"
environment:
- GF_SECURITY_ADMIN_USER=user
- GF_SECURITY_ADMIN_PASSWORD=password
E grafana.ini NÃO foi modificado do arquivo padrão.
Consigo ver a página web do Grafana com erro e, no console, na aba de rede, consigo ver que o Chrome não carrega arquivos CSS e JS.
https://example.com/public/build/grafana.dark.722d809dba5a31f57d49.css
Verifiquei que dentro do contêiner grafana esse arquivo existe, então não sei por que o nginx não está redirecionando ou encontrando esse arquivo.
Mas, este arquivo foi encontrado: https://example.com/public/img/grafana_icon.svg
Então há algum problema no arquivo de configuração do nginx com arquivos js, css, ..., não é?
Se eu for (testar) em http://example.com:3000 os arquivos são carregados corretamente: http://example.com:3000/public/build/grafana.dark.722d809dba5a31f57d49.css
Então...alguma solução?
(só por precaução, o arquivo nginx.conf que estou usando para este teste "simples":)
server {
listen 80;
listen [::]:80;
server_name example.com;
location /.well-known/acme-challenge {
allow all;
root /var/www/certbot;
}
location / {
rewrite ^ https://$host$request_uri? permanent;
# return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com;
server_tokens off; #Disable the Nginx version in headers for security
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
ssl_session_tickets off;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Grafana Dashboard
location / {
proxy_set_header Host $host;
proxy_pass http://grafana:3000/;
}
# Logs for Nginx access and errors
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location ~ /\.ht { deny all; }
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { log_not_found off; access_log off; allow all; }
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ { expires max; log_not_found off; }
}