Discourse работает с jwilder/nginx proxy и acme-companion

Привет, ребята,

Я уже несколько часов застрял на этой проблеме и прошу помощи, так как не могу найти рабочее решение.

Поскольку у меня несколько контейнеров, на которых размещены веб-сервисы, мне нужно запустить свой собственный Nginx для проксирования трафика на соответствующие контейнеры. (Я мог бы просто перенести Discourse на отдельный сервер, но это крайняя мера, если мне не удастся заставить это работать).

В настоящее время я использую следующую конфигурацию:

Хорошую схему того, как это всё работает вместе, можно найти на следующей странице:

Я использовал следующие руководства, которые были полезны, однако успеха я всё ещё не добился.

  1. How to Install Multiple Discourse Forums on the Same Server
  2. Use Nginx-Proxy and LetsEncrypt Companion to Host Multiple Websites - Singular Aspect

Предыдущие темы на Discourse, которые я нашёл (более 2 лет назад), включают:

Мой файл app.yml для Discourse содержит следующее (домен и адрес электронной почты удалены):

docker_args:
  - "--net nginx-proxy"
  VIRTUAL_HOST: {domain}
  VIRTUAL_PORT: 80
  LETSENCRYPT_HOST: {domain}
  LETSENCRYPT_EMAIL: {email address}

Я закомментировал пробрасываемый порт в примере ниже. Я пробовал множество вариаций и видел много разных решений. Например, комментарии в этом посте: Discourse with jwilder/nginx-proxy howtos?, или в других случаях я видел, что это закомментировано и указано как VIRTUAL_PORT в переменных Docker. Я немного запутался в этой части.

#expose:
#  - "80"

Все мои контейнеры находятся в одной сети, например:
docker network inspect nginx-proxy

[
    {
        "Name": "nginx-proxy",
        "Id": "d2715f513771f002711521838340b879bb9106ef50118fb29be6b0cf2d5f25e7",
        "Created": "2022-02-01T20:32:10.021632263Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "0b6296ccf7e31b67caf0d31ccc5485e30bd6f1746ceee7e6ce29617b892178cd": {
                "Name": "app",
                "EndpointID": "1cea3782ef9dea3cf0066add142121f569682c74ff8faa6b19813035eff8998c",
                "MacAddress": "02:72:f8:ee:03:32",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            "975ea34d56a65b722aa5e83b99f71039fd687b1aee4e8d4df3cb192b1d35c043": {
                "Name": "nginx-proxy",
                "EndpointID": "bc7a0b4602614be516ee0ceb1c4edf923b91bf7a6528c013157180a98d435807",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "e299a734584a765dbc10102a5bc9b0d0c98ac6096b6ae75dd1169105893e40af": {
                "Name": "letsencrypt-proxy",
                "EndpointID": "6b3588359ac4eec02618d3a7438d2750cb6e1c3d1cb6e1a8ce6378c568358a50",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

Мой сертификат успешно сгенерирован, как вы можете видеть здесь, однако я сейчас получаю ошибку 502.

При попытке отладки путём просмотра логов Nginx на контейнере Discourse файлы логов создаются, но они пусты:

root@ubuntu-vm-dev-app:/var/log/nginx# ls -lt
total 692
-rw-r--r-- 1 www-data www-data      0 Feb  4 07:23 access.letsencrypt.log
-rw-r--r-- 1 www-data www-data      0 Feb  4 07:23 error.letsencrypt.log
-rw-r--r-- 1 www-data www-data      0 Feb  4 07:23 error.log
-rw-r--r-- 1 www-data www-data      0 Feb  1 23:04 access.log
-rw-r--r-- 1 www-data www-data 700917 Jan 31 23:48 error.log.1
-rw-r--r-- 1 www-data www-data      0 Jan 31 22:55 access.letsencrypt.log.1
-rw-r--r-- 1 www-data www-data      0 Jan 31 22:55 error.letsencrypt.log.1

Мои логи Docker показывают следующее, что пока кажется наиболее полезной информацией (я удалил публичный IP и имя домена):

nginx-proxy          | nginx.1     | 2022/02/04 08:35:04 [error] 206#206: *93 connect() failed (111: Connection refused) while connecting to upstream, client: {REDACTED_PUBLIC_IP}, server: {MY_FORUM_DOMAIN}.com, request: "GET / HTTP/2.0", upstream: "http://172.18.0.4:80/", host: "{MY_FORUM_DOMAIN}.com"
nginx-proxy          | nginx.1     | {MY_FORUM_DOMAIN}.com {REDACTED_PUBLIC_IP} - - [04/Feb/2022:08:35:04 +0000] "GET / HTTP/2.0" 502 559 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36" "172.18.0.4:80"
nginx-proxy          | nginx.1     | 2022/02/04 08:35:06 [error] 206#206: *93 connect() failed (111: Connection refused) while connecting to upstream, client:{REDACTED_PUBLIC_IP}, server: {MY_FORUM_DOMAIN}.com, request: "GET /service-worker.js HTTP/2.0", upstream: "http://172.18.0.4:80/service-worker.js", host: "{MY_FORUM_DOMAIN}.com", referrer: "https://{MY_FORUM_DOMAIN}.com/service-worker.js"
nginx-proxy          | nginx.1     | {MY_FORUM_DOMAIN}.com {REDACTED_PUBLIC_IP} - - [04/Feb/2022:08:35:06 +0000] "GET /service-worker.js HTTP/2.0" 502 559 "https://{MY_FORUM_DOMAIN}.com/service-worker.js" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36" "172.18.0.4:80"
nginx-proxy          | nginx.1     | 2022/02/04 08:35:19 [error] 206#206: *93 connect() failed (111: Connection refused) while connecting to upstream, client:{REDACTED_PUBLIC_IP}, server: {MY_FORUM_DOMAIN}.com, request: "GET /service-worker.js HTTP/2.0", upstream: "http://172.18.0.4:80/service-worker.js", host: "{MY_FORUM_DOMAIN}.com", referrer: "https://{MY_FORUM_DOMAIN}.com/service-worker.js"
nginx-proxy          | nginx.1     | {MY_FORUM_DOMAIN}.com{REDACTED_PUBLIC_IP} - - [04/Feb/2022:08:35:19 +0000] "GET /service-worker.js HTTP/2.0" 502 559 "https://{MY_FORUM_DOMAIN}.com/service-worker.js" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36" "172.18.0.4:80"

Я подтвердил, что связь с контейнером Discourse через указанный порт в файле containers/app.yml работает. Например, я зашел в контейнер Discourse и запустил netcat на порту 80 перед выполнением запроса из браузера.

root@ubuntu-vm-dev-app:/var/log/nginx# netcat -lnvp 80
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::80
Ncat: Listening on 0.0.0.0:80
Ncat: Connection from 172.18.0.2.
Ncat: Connection from 172.18.0.2:48776.
GET / HTTP/1.1
Host: {REDACTED_HOST}.com
Connection: close
X-Real-IP: {REDACTED_PUBLIC-IP}
X-Forwarded-For: {REDACTED_PUBLIC-IP}
X-Forwarded-Proto: https
X-Forwarded-Ssl: on
X-Forwarded-Port: 443
cache-control: max-age=0
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
sec-fetch-site: none
sec-fetch-mode: navigate
sec-fetch-user: ?1
sec-fetch-dest: document
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
cookie: _t=azl2WkpyN1hgbENOd3pyZ1d6V3ZXUT....

root@ubuntu-vm-dev-app:/var/log/nginx# 

Пример сгенерированной конфигурации Nginx из контейнера nginx-proxy выглядит следующим образом:

root@975ea34d56a6:/etc/nginx/conf.d# cat default.conf 
# nginx-proxy version : 0.10.0-11-g42c8b0c
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
  default $http_x_forwarded_proto;
  ''      $scheme;
}
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
  default $http_x_forwarded_port;
  ''      $server_port;
}
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
# Connection header that may have been passed to this server
map $http_upgrade $proxy_connection {
  default upgrade;
  '' close;
}
# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Default dhparam
ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
# Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto
map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
  default off;
  https on;
}
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
                 '"$request" $status $body_bytes_sent '
                 '"$http_referer" "$http_user_agent" '
                 '"$upstream_addr"';
access_log off;
		ssl_protocols TLSv1.2 TLSv1.3;
		ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
		ssl_prefer_server_ciphers off;
resolver 127.0.0.11;
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_buffering off;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
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 $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
server {
	server_name _; # This is just an invalid value which will never trigger on a real hostname.
	server_tokens off;
	listen 80;
	access_log /var/log/nginx/access.log vhost;
	return 503;
}
server {
	server_name _; # This is just an invalid value which will never trigger on a real hostname.
	server_tokens off;
	listen 443 ssl http2;
	access_log /var/log/nginx/access.log vhost;
	return 503;
	ssl_session_cache shared:SSL:50m;
	ssl_session_tickets off;
	ssl_certificate /etc/nginx/certs/default.crt;
	ssl_certificate_key /etc/nginx/certs/default.key;
}
# {REDACTED_DOMAIN}.com
upstream {REDACTED_DOMAIN}.com {
	## Can be connected with "nginx-proxy" network
	# app
	server 172.18.0.4:80;
}
server {
	server_name {REDACTED_DOMAIN}.com;
	listen 80 ;
	access_log /var/log/nginx/access.log vhost;
	# Do not HTTPS redirect Let'sEncrypt ACME challenge
	location ^~ /.well-known/acme-challenge/ {
		auth_basic off;
		auth_request off;
		allow all;
		root /usr/share/nginx/html;
		try_files $uri =404;
		break;
	}
	location / {
		return 301 https://$host$request_uri;
	}
}
server {
	server_name {REDACTED_DOMAIN}.com;
	listen 443 ssl http2 ;
	access_log /var/log/nginx/access.log vhost;
	ssl_session_timeout 5m;
	ssl_session_cache shared:SSL:50m;
	ssl_session_tickets off;
	ssl_certificate /etc/nginx/certs/{REDACTED_DOMAIN}.com.crt;
	ssl_certificate_key /etc/nginx/certs/{REDACTED_DOMAIN}.com.key;
	ssl_dhparam /etc/nginx/certs/{REDACTED_DOMAIN}.com.dhparam.pem;
	ssl_stapling on;
	ssl_stapling_verify on;
	ssl_trusted_certificate /etc/nginx/certs/{REDACTED_DOMAIN}.com.chain.pem;
	add_header Strict-Transport-Security "max-age=31536000" always;
	include /etc/nginx/vhost.d/default;
	location / {
		proxy_pass http://{REDACTED_DOMAIN}.com;
	}
}

Меня просто интересует, удалось ли кому-нибудь заставить это работать? Или есть какие-то идеи по методам отладки ошибки 502, которую я получаю? Я знаю, что ошибка может быть вызвана множеством переменных, но если вы сталкивались с этим при настройке Discourse или подобных конфигураций, ваше решение может помочь.

Спасибо.

Пожалуйста, ознакомьтесь с инструкциями по отладке установки Discourse с участием нескольких прокси-серверов:
Как отладить установку Discourse в подпапке

Похоже, ваша конфигурация прокси работает, но Discourse — нет. Скорее всего, проблема в том, что он не может получить доступ к базе данных? Вы можете посмотреть (что-то вроде)

/var/discourse/shared/standalone/logs/rails/production.log

"GET /server-worker.js HTTP/2.0" **502** […]
Для меня это выглядит как ошибка 502 при получении статического файла — прокси, похоже, не работает.

Ох. Это из вашего прокси-сервера nginx, а не из nginx для Discourse. Извините.

В том, что вы описали, я не вижу ничего явно неправильного, но задействовано множество мелких компонентов. Возможно, Discourse находится не в той сети Docker (хотя, кажется, вы уже пытались это исправить).

В течение последних нескольких месяцев я собирался разобраться, как сделать именно то, о чём вы говорите, но люди постоянно нанимают меня для выполнения других задач. Может быть, в ближайшие неделю-две, но я уже говорю это несколько месяцев.

[…] "https://… […]

Попробуйте настроить ваш внешний прокси на подключение к http-URL на Nginx для Discourse, а не к https.

Привет @rrit @pfaffman, спасибо за ответы и предложения.

Сегодня я ещё раз попробовал разобраться с этим, дело заняло немало времени, но наконец-то всё заработало. Я предоставлю свою конфигурацию и несколько методов отладки на случай, если это поможет кому-то ещё.

Примечание: Пожалуйста, обратитесь к моему первоначальному сообщению для информации о том, как контейнеры nginx, acme-companion и Discourse «склеиваются» вместе и автоматизируют выдачу сертификатов и работу обратного прокси.

Если вас не интересуют методы отладки, переходите сразу к концу, где опубликованы мои решения (файлы docker-compose и app.yml)

Основная ошибка, выявленная в моей конфигурации

Похоже, одна из ошибок, с которой я столкнулся, была моей собственной виной из-за слепого следования множеству руководств, что привело к путанице. Если вы обратили внимание на моё первоначальное сообщение, я мог прослушивать порт 80 контейнера приложения Discourse, используя команду «netcat -lnvp 80». Однако это не имеет смысла, если я хочу, чтобы обратный прокси отправлял трафик на прослушиваемый порт контейнера Discourse; этот порт не должен был быть доступен для прослушивания через netcat… он должен был быть «занят».

Проблема заключалась в том, что я использовал следующий шаблон: web.socketed.template.yml. Это означало, что nginx внутри контейнера Discourse использовал Unix-сокеты, а не слушал TCP/IP-порты. Пример шаблона и конфигурации, которую он применяет к nginx, приведён ниже:

ubuntu@ubuntu-vm-dev:/var/discourse/templates$ cat web.socketed.template.yml 
run:
  - file:
     path: /etc/runit/1.d/remove-old-socket
     chmod: "+x"
     contents: |
        #!/bin/bash
        rm -f /shared/nginx.http*.sock
  - file:
     path: /etc/runit/3.d/remove-old-socket
     chmod: "+x"
     contents: |
        #!/bin/bash
        rm -rf /shared/nginx.http*.sock
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 80;/
     to: |
       listen unix:/shared/nginx.http.sock;
       set_real_ip_from unix:;
  - replace:
     filename: "/etc/nginx/conf.d/discourse.conf"
     from: /listen 443 ssl http2;/
     to: |
       listen unix:/shared/nginx.https.sock ssl http2;
       set_real_ip_from unix:;

Проблема заключалась в том, что nginx из моего контейнера nginx-proxy успешно пересылал трафик в контейнер Discourse, но пересылал его на порт 80, вместо использования общего Unix-сокета.

Как видно из документации nginx-proxy, порт по умолчанию — 80, если не указан виртуальный порт.
Скриншот из: GitHub - nginx-proxy/nginx-proxy: Automated Nginx Reverse Proxy for Docker · GitHub

Вместо того чтобы возиться с настройкой контейнера nginx-proxy, я просто удалил web.socketed.template.yml. После этого я убрал все порты, которые экспонировал мой контейнер Discourse, так как это было не нужно. Важно, чтобы контейнер Discourse находился в правильной сети Docker (той же сети, что и nginx-proxy и acme-companion), тогда всё должно работать корректно.

Советы по отладке

1. Отладка сети

1.1 Сеть Docker
Пример того, как можно проверить успешность сети, приведён ниже (после запуска вашего приложения Docker и контейнеров nginx/acme).

ubuntu@ubuntu-vm-dev:/var/discourse$ docker network inspect nginx-proxy
 {
        "Name": "nginx-proxy",
        "Id": "d2715f513771f002711521838340b879bb9106ef50118fb29be6b0cf2d5f25e7",
        "Created": "2022-02-01T20:32:10.021632263Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "205772214b77a6bf49a76d082eef216aa5fc4ca15a39ae2c8fefc0b3eeb61e00": {
                "Name": "nginx-test_webserver2_1",
                "EndpointID": "ee3096663b1a90443906652ff3a192243fa1f60081e5bbee190a23f1d23393f7",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "3050831bf600ae02c18e5045549267b583ec732d6f1550f869ab651dfc7d8dca": {
                "Name": "nginx-test_nginx_1",
                "EndpointID": "a7f95f2a7589ca3823da01b9d28244bbd81815d6f5cc1f2f89804a488ef2148d",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "696ce8d129a62ce37ae8373ed4f603fd5d731f5f29166bd6fa889631ef0ae606": {
                "Name": "nginx-proxy",
                "EndpointID": "99d90539668b97652ffca7e3301aa83c40fe7b31ca8eea2a8a4d9bf7afe19ac0",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            },
            "d3faf6489ca6617dceda4f2907ee6c055a1d81e3590c3eab2768601dfc0b60d7": {
                "Name": "app",
                "EndpointID": "aed6bfcc61e439e2206615a1569355c529a63a21c356e54da378f630d44fd3bc",
                "MacAddress": "02:72:f8:ee:03:32",
                "IPv4Address": "172.18.0.6/16",
                "IPv6Address": ""
            },
            "e04f2125001d5f346255312f10cdd0ab176388cddb0819b0022c5988a23303eb": {
                "Name": "letsencrypt-proxy",
                "EndpointID": "0088bcd942aed41c9351b935ed7b115e49046cc3a8308ca5a8d3828a194ecfee",
                "MacAddress": "02:42:ac:12:00:05",
                "IPv4Address": "172.18.0.5/16",
                "IPv6Address": ""
            }
        },

Примечание: измените имя сети на то, которое вы используете (если только вы тоже не используете имя nginx-proxy)

После этого вы должны увидеть эти IP-адреса из сети Docker в конфигурации контейнера nginx-proxy, к которой можно получить доступ и просмотреть её через:

ubuntu@ubuntu-vm-dev:/var/discourse$ docker exec -it nginx-proxy /bin/bash
root@696ce8d129a6:/app# cd /etc/nginx/conf.d/
root@696ce8d129a6:/etc/nginx/conf.d# cat default.conf
... Конфигурация NGINX будет здесь ... 

Чтобы проверить успешное сетевое взаимодействие, попробуйте взаимодействовать с IP-адресом, указанным в одном из upstream-расположений. Например, из контейнера nginx-proxy попробуйте взаимодействовать с контейнером Discourse через назначенные IP-адреса сети Docker.

Например, мой файл конфигурации содержит:

upstream my_forum_domain.com {
	## Может быть подключён к сети "nginx-proxy"
	# app
	server 172.18.0.6:80;
}

Так как upstream-IP для моего контейнера Discourse — 172.18.0.6, я должен увидеть успешный вывод при выполнении curl против моего контейнера Discourse.

root@696ce8d129a6:/etc/nginx/conf.d# curl 172.18.0.6
... Вывод запроса Curl ... 

1.2. Инструменты устранения неполадок сети (Netstat, lsof, netcat)
Следующие инструменты помогут в устранении неполадок: ‘netstat’, ‘lsof’, ‘netcat’. Поскольку контейнер Discourse не поставляется с этими инструментами предустановленными, установите их через:

root@ubuntu-vm-dev-app:/# apt-get install lsof
root@ubuntu-vm-dev-app:/# apt-get install net-tools
root@ubuntu-vm-dev-app:/# apt-get install netcat

lsof
Вы должны увидеть процесс nginx, слушающий порт 80, через lsof.

root@ubuntu-vm-dev-app:/# lsof -i :80
COMMAND PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
nginx    55 root    6u  IPv4 254317      0t0  TCP *:http (LISTEN)
root@ubuntu-vm-dev-app:/# 

netstat
Следующая команда netstat также полезна для проверки использования правильных процессов/портов. Например, вы можете увидеть порты, связанные с HTTP (80), redis (6379), PostgreSQL (5432), Unicorn (3000) и т. д.

root@ubuntu-vm-dev-app:/# netstat -peanut
Активные интернет-соединения (серверы и установленные)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       User       Inode      PID/Program name    
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      0          254317     55/nginx: master pr 
tcp        0      0 127.0.0.1:3000          0.0.0.0:*               LISTEN      1000       255560     -                   
tcp        0      0 0.0.0.0:5432            0.0.0.0:*               LISTEN      105        255137     -                   
tcp        0      0 127.0.0.11:46697        0.0.0.0:*               LISTEN      0          254194     -                   
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN      106        255126     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37600         ESTABLISHED 106        255583     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37608         ESTABLISHED 106        255610     -                   
tcp        0      0 127.0.0.1:37632         127.0.0.1:6379          ESTABLISHED 1000       254766     -                   
tcp        0      0 127.0.0.1:37626         127.0.0.1:6379          ESTABLISHED 1000       254745     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37634         ESTABLISHED 106        254773     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37650         ESTABLISHED 106        255950     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37668         ESTABLISHED 106        255965     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37612         ESTABLISHED 106        255618     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37602         ESTABLISHED 106        255585     -                   
tcp        0      0 127.0.0.1:37624         127.0.0.1:6379          ESTABLISHED 1000       254739     -                   
tcp        0      0 127.0.0.1:37594         127.0.0.1:6379          ESTABLISHED 1000       255578     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37594         ESTABLISHED 106        254527     -                   
tcp        0      0 127.0.0.1:37618         127.0.0.1:6379          ESTABLISHED 1000       255635     -                   
tcp        0      0 127.0.0.1:37586         127.0.0.1:6379          ESTABLISHED 1000       255168     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37622         ESTABLISHED 106        255645     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37624         ESTABLISHED 106        254740     -                   
tcp        0      0 127.0.0.1:37600         127.0.0.1:6379          ESTABLISHED 1000       254537     -                   
tcp        0      0 127.0.0.1:37610         127.0.0.1:6379          ESTABLISHED 1000       254563     -                   
tcp        0      0 127.0.0.1:3000          127.0.0.1:36678         TIME_WAIT   0          0          -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37620         ESTABLISHED 106        255642     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37636         ESTABLISHED 106        254779     -                   
tcp        0      0 127.0.0.1:37628         127.0.0.1:6379          ESTABLISHED 1000       254751     -                   
tcp        0      0 127.0.0.1:37590         127.0.0.1:6379          ESTABLISHED 1000       254361     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37626         ESTABLISHED 106        254746     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37586         ESTABLISHED 106        255169     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37590         ESTABLISHED 106        255173     -                   
tcp        0      0 127.0.0.1:37668         127.0.0.1:6379          ESTABLISHED 1000       254849     -                   
tcp        0      0 127.0.0.1:37622         127.0.0.1:6379          ESTABLISHED 1000       254582     -                   
tcp        0      0 127.0.0.1:37634         127.0.0.1:6379          ESTABLISHED 1000       254772     -                   
tcp        0      0 127.0.0.1:37602         127.0.0.1:6379          ESTABLISHED 1000       254541     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37630         ESTABLISHED 106        254760     -                   
tcp        0      0 127.0.0.1:37612         127.0.0.1:6379          ESTABLISHED 1000       255617     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37628         ESTABLISHED 106        254752     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37610         ESTABLISHED 106        255612     -                   
tcp        0      0 127.0.0.1:3000          127.0.0.1:36682         TIME_WAIT   0          0          -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37632         ESTABLISHED 106        254767     -                   
tcp        0      0 127.0.0.1:37650         127.0.0.1:6379          ESTABLISHED 1000       254826     -                   
tcp        0      0 127.0.0.1:37608         127.0.0.1:6379          ESTABLISHED 1000       254559     -                   
tcp        0      0 127.0.0.1:37620         127.0.0.1:6379          ESTABLISHED 1000       255641     -                   
tcp        0      0 127.0.0.1:37636         127.0.0.1:6379          ESTABLISHED 1000       254778     -                   
tcp        0      0 127.0.0.1:37630         127.0.0.1:6379          ESTABLISHED 1000       254759     -                   
tcp        0      0 127.0.0.1:6379          127.0.0.1:37618         ESTABLISHED 106        255636     -                   
tcp6       0      0 :::5432                 :::*                    LISTEN      105        255138     -                   
tcp6       0      0 :::6379                 :::*                    LISTEN      106        255127     -                   
udp        0      0 127.0.0.1:54569         127.0.0.1:54569         ESTABLISHED 105        255145     -                   
udp        0      0 127.0.0.11:44019        0.0.0.0:*                           0          254193     -                   
root@ubuntu-vm-dev-app:/# 

nc
nc для просмотра прослушиваемых портов, чтобы проверить, настроены ли они и слушают ли они правильно (хотя это должно отображаться в netstat).

root@ubuntu-vm-dev-app:/# nc -zv localhost 1-10000 2>&1 | grep -i  succeeded 
Connection to localhost (127.0.0.1) 80 port [tcp/http] succeeded!
Connection to localhost (127.0.0.1) 3000 port [tcp/*] succeeded!
Connection to localhost (127.0.0.1) 5432 port [tcp/postgresql] succeeded!
Connection to localhost (127.0.0.1) 6379 port [tcp/redis] succeeded!

1.3 Процесс
Ещё одна полезная команда: ps aux

root@ubuntu-vm-dev-app:/# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.0   6772  3252 pts/0    Ss+  15:20   0:00 /bin/bash /sbin/boot
root          41  0.0  0.0   2340   636 pts/0    S+   15:20   0:00 /usr/bin/runsvdir -P /etc/service
root          42  0.0  0.0   2188   696 ?        Ss   15:20   0:00 runsv cron
root          43  0.0  0.0   2188   636 ?        Ss   15:20   0:00 runsv rsyslog
root          44  0.0  0.0   2188   632 ?        Ss   15:20   0:00 runsv nginx
root          45  0.0  0.0   2188   636 ?        Ss   15:20   0:00 runsv postgres
root          46  0.0  0.0   2188   636 ?        Ss   15:20   0:00 runsv redis
root          47  0.0  0.0   2188   636 ?        Ss   15:20   0:00 runsv unicorn
root          48  0.0  0.0 151068  3868 ?        Sl   15:20   0:00 rsyslogd -n
root          49  0.0  0.0   2336   700 ?        S    15:20   0:00 svlogd /var/log/redis
root          50  0.0  0.0   6620  2768 ?        S    15:20   0:00 cron -f
redis         51  0.1  0.0  54180  5828 ?        Sl   15:20   0:02 /usr/bin/redis-server *:6379
root          52  0.0  0.0   2336   636 ?        S    15:20   0:00 svlogd /var/log/postgres
postgres      53  0.0  0.3 213196 28676 ?        S    15:20   0:00 /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main
discour+      54  0.0  0.0  15256  3924 ?        S    15:20   0:00 /bin/bash config/unicorn_launcher -E production -c config/unicorn.conf.rb
root          55  0.0  0.0  21280  7128 ?        S    15:20   0:00 nginx: master process /usr/sbin/nginx
www-data      69  0.0  0.0  22156  5004 ?        S    15:20   0:00 nginx: worker process
www-data      70  0.0  0.0  22124  4524 ?        S    15:20   0:00 nginx: worker process
www-data      71  0.0  0.0  21656  3768 ?        S    15:20   0:00 nginx: cache manager process
postgres      75  0.0  0.0 213296  6344 ?        Ss   15:20   0:00 postgres: 13/main: checkpointer 
postgres      76  0.0  0.0 213196  5896 ?        Ss   15:20   0:00 postgres: 13/main: background writer 
postgres      77  0.0  0.1 213196 10028 ?        Ss   15:20   0:00 postgres: 13/main: walwriter 
postgres      78  0.0  0.1 213736  8492 ?        Ss   15:20   0:00 postgres: 13/main: autovacuum launcher 
postgres      79  0.0  0.0  67960  5548 ?        Ss   15:20   0:00 postgres: 13/main: stats collector 
postgres      80  0.0  0.0 213752  6868 ?        Ss   15:20   0:00 postgres: 13/main: logical replication launcher 
discour+      81  0.4  3.1 436276 253572 ?       Sl   15:20   0:08 unicorn master -E production -c config/unicorn.conf.rb
postgres      99  0.0  0.3 220252 30612 ?        Ss   15:20   0:00 postgres: 13/main: discourse discourse [local] idle
discour+     116  0.6  3.7 831804 306708 ?       SNl  15:20   0:12 sidekiq 6.4.1 discourse [0 of 5 busy]
discour+     125  0.4  3.6 750356 297108 ?       Sl   15:20   0:07 unicorn worker[0] -E production -c config/unicorn.conf.rb
discour+     133  0.3  3.5 737812 289896 ?       Sl   15:20   0:07 unicorn worker[1] -E production -c config/unicorn.conf.rb
postgres     147  0.0  0.3 219372 27792 ?        Ss   15:20   0:00 postgres: 13/main: discourse discourse [local] idle
root        1858  0.0  0.0   7036  3828 pts/1    Ss   15:46   0:00 /bin/bash
postgres    1982  0.0  0.2 217012 24008 ?        Ss   15:47   0:00 postgres: 13/main: discourse discourse [local] idle
discour+    2236  0.0  0.0  13760  2216 ?        S    15:51   0:00 sleep 1
root        2237  0.0  0.0   9636  3292 pts/1    R+   15:51   0:00 ps aux
root@ubuntu-vm-dev-app:/# 

Примечание: Причина предоставления вышеуказанных выводов — помочь в выявлении любых аномалий или отличий от вашего развёртывания и моего. Вместо этого вы можете запустить собственное рабочее развёртывание (без обратного прокси) и сравнить такие вещи, как:

  • Сеть
  • Процессы
  • Создание/изменение файлов
  • Вывод логов

Мои рабочие файлы конфигурации

1. Пример вывода логов при ./launcher rebuild app

+ /usr/bin/docker run --shm-size=512m -d --restart=always -e LANG=en_US.UTF-8 -e RAILS_ENV=production -e UNICORN_WORKERS=2 -e UNICORN_SIDEKIQS=1 -e RUBY_GLOBAL_METHOD_CACHE_SIZE=131072 -e RUBY_GC_HEAP_GROWTH_MAX_SLOTS=40000 -e RUBY_GC_HEAP_INIT_SLOTS=400000 -e RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=1.5 -e DISCOURSE_DB_SOCKET=/var/run/postgresql -e DISCOURSE_DB_HOST= -e DISCOURSE_DB_PORT= -e VIRTUAL_HOST={your_domain}.com -e LETSENCRYPT_HOST={your_domain}.com -e LETSENCRYPT_EMAIL=steve@{your_email_domain}.com -e LC_ALL=en_US.UTF-8 -e LANGUAGE=en_US.UTF-8 -e EMBER_CLI_PROD_ASSETS=1 -e DISCOURSE_HOSTNAME={your_domain}.com -e DISCOURSE_DEVELOPER_EMAILS=steve@{your_email_domain}.com -e DISCOURSE_SMTP_ADDRESS=smtp.mailgun.org -e DISCOURSE_SMTP_PORT=587 -e DISCOURSE_SMTP_USER_NAME=noreply@mail.{your_domain}.com -e DISCOURSE_SMTP_PASSWORD={your_password} -e DISCOURSE_SMTP_DOMAIN=mail.{your_domain}.com -e DISCOURSE_NOTIFICATION_EMAIL=noreply@mail.{your_domain}.com -h ubuntu-vm-dev-app -e DOCKER_HOST_IP=172.17.0.1 --name app -t -v /var/discourse/shared/standalone:/shared -v /var/discourse/shared/standalone/log/var-log:/var/log --mac-address 02:72:f8:ee:03:32 --network nginx-proxy local_discourse/app /sbin/boot
d3faf6489ca6617dceda4f2907ee6c055a1d81e3590c3eab2768601dfc0b60d7
ubuntu@ubuntu-vm-dev:/var/discourse$ 

2. Мой файл docker-compose

ubuntu@ubuntu-vm-dev:~/nginx-test$ cat docker-compose.yml 
version: '3'
services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      - letsencrypt-certs:/etc/nginx/certs
      - letsencrypt-vhost-d:/etc/nginx/vhost.d
      - letsencrypt-html:/usr/share/nginx/html
  letsencrypt-proxy:
      image: jrcs/letsencrypt-nginx-proxy-companion
      container_name: letsencrypt-proxy
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock:ro
        - letsencrypt-certs:/etc/nginx/certs
        - letsencrypt-vhost-d:/etc/nginx/vhost.d
        - letsencrypt-html:/usr/share/nginx/html
      environment:
        - DEFAULT_EMAIL=steve@{your_email_domain}.com
        - NGINX_PROXY_CONTAINER=nginx-proxy

networks:
  default:
    external:
      name: nginx-proxy
 
volumes:
  letsencrypt-certs:
  letsencrypt-vhost-d:
  letsencrypt-html:

3. Мой файл app.yml для Discourse

ubuntu@ubuntu-vm-dev:/var/discourse$ cat containers/app.yml 
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/sshd.template.yml"
  - "templates/web.template.yml"

docker_args:
  - "--network nginx-proxy"

params:
  db_default_text_search_config: "pg_catalog.english"

  db_shared_buffers: "128MB"

env:
  VIRTUAL_HOST: {your_domain}.com
  LETSENCRYPT_HOST: example.com
  LETSENCRYPT_EMAIL: steve@{your_email_domain}.com
  LC_ALL: en_US.UTF-8
  LANG: en_US.UTF-8
  LANGUAGE: en_US.UTF-8
  EMBER_CLI_PROD_ASSETS: 1
  # DISCOURSE_DEFAULT_LOCALE: en

  UNICORN_WORKERS: 2
  
  DISCOURSE_HOSTNAME: example.com
  
  DISCOURSE_DEVELOPER_EMAILS: 'steve@{email_domain}.com'

  DISCOURSE_SMTP_ADDRESS: smtp.mailgun.org
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: noreply@mail.{domain.com}.com
  DISCOURSE_SMTP_PASSWORD: "{password}"
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (опционально, по умолчанию true)
  DISCOURSE_SMTP_DOMAIN: mail.{domain}.com
  DISCOURSE_NOTIFICATION_EMAIL: noreply@mail.{domain}.com

volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-spoiler-alert.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/discourse/discourse-cakeday.git

run:
  - exec: echo "Начало пользовательских команд"
  - exec: echo "Конец пользовательских команд"

Надеюсь, вышеизложенное поможет кому-то ещё, кто тоже застрял на этом :slight_smile: