Проблема с фаерволом при запуске нескольких контейнеров после обновления

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

Однако по какой-то причине первый форум больше недоступен: Safari сообщает, что сервер неожиданно разорвал соединение. Пересборка, похоже, проходит успешно, но форум недоступен. Я могу зайти в консоль приложения и Rails, и база данных выглядит целой.

Единственные предупреждения, которые я вижу в логах пересборки и которые могут иметь отношение к делу:

168:M 31 Jan 2021 21:39:22.459 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
168:M 31 Jan 2021 21:39:22.459 # Server initialized
168:M 31 Jan 2021 21:39:22.459 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
168:M 31 Jan 2021 21:39:22.459 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled (set to 'madvise' or 'never').
168:M 31 Jan 2021 21:39:22.459 * Loading RDB produced by version 6.0.9
168:M 31 Jan 2021 21:39:22.459 * RDB age 21 seconds
168:M 31 Jan 2021 21:39:22.459 * RDB memory usage when created 4.03 Mb
168:M 31 Jan 2021 21:39:22.466 * DB loaded from disk: 0.006 seconds
168:M 31 Jan 2021 21:39:22.466 * Ready to accept connections

production.log:


Job exception: Error connecting to Redis on localhost:6379 (Errno::ENETUNREACH)

Error connecting to Redis on localhost:6379 (Errno::ENETUNREACH) subscribe failed, reconnecting in 1 second. Call stack /var/www/discourse/vendor/bundle/ruby/2.7.0/gems/redis-4.2.5/lib/redis/client.rb:367:in `rescue in establish_connection'

Аналогичные сообщения появляются в unicorn.stderr.log и unicorn.stdout.log.

При входе в контейнер и выполнении команды redis-cli ping я получаю ответ PONG. Redis работает на сервере (но не в отдельных контейнерах — насколько мне известно, так было всегда).

Есть ли у кого-нибудь идеи, что может происходить?

(Я также перезагрузил сервер и создал новый сертификат letsencrypt для этого домена, чтобы перестраховаться, но проблема остаётся прежней.)

Похоже, что всё должно работать… Пробовали ли вы открыть сайт в другом браузере или очистить кэш? Если это не поможет, пожалуйста, опубликуйте результат выполнения команды:

curl -vv -o /dev/null <ссылка на форум>

Я пробовал в нескольких браузерах, но получаю ту же ошибку Michael. Вот вывод этой команды:

~$ curl -vv -o /dev/null https://metaruby.com
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 78.46.110.60...
* TCP_NODELAY set
* Connected to metaruby.com (78.46.110.60) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [226 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [93 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [2473 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [333 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [70 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: CN=metaruby.com
*  start date: Jan 31 03:33:05 2021 GMT
*  expire date: May  1 03:33:05 2021 GMT
*  subjectAltName: host "metaruby.com" matched cert's "metaruby.com"
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: metaruby.com
> User-Agent: curl/7.64.1
> Accept: */*
> 
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0* TLSv1.2 (IN), TLS alert, close notify (256):
{ [2 bytes data]
* Empty reply from server
  0     0    0     0    0     0      0      0 --:--:--  0:00:01 --:--:--     0
* Connection #0 to host metaruby.com left intact
curl: (52) Empty reply from server
* Closing connection 0

Пожалуйста, помогите разобраться.

Вот несколько возможных причин ошибки пустого ответа:

  1. Сервер находится в VPN, и доступ к порту отсутствует.
  2. Если на одном сервере развернуто несколько экземпляров Discourse, предположительно перед ними работает обратный прокси. Убедитесь, что он указывает на контейнер Discourse (возможно, потребуется его перезапуск).
  3. На сервере недостаточно свободного места (проверьте командой df -hT /).

Я бы сначала проверил свободное место на диске (пункт 3).

Использование диска показывало 31%, но я всё равно выполнил ./launcher cleanup:

docker container ls 
(Чтобы убедиться, что все три контейнера форума запущены)

./launcher cleanup

ВНИМАНИЕ! Это удалит все остановленные контейнеры.
Вы уверены, что хотите продолжить? [y/N] y
Всего освобождено места: 0B
ВНИМАНИЕ! Это удалит все образы, у которых нет хотя бы одного связанного с ними контейнера.
Вы уверены, что хотите продолжить? [y/N] y
Удалённые образы:
...
Всего освобождено места: 32.56 ГБ

Мы используем HAProxy, я проверил его (и перезапустил), и он работает (мы также перенаправляем трафик с http на https через него, и это тоже работает корректно для домена, так что, думаю, проблема не там — к тому же всё работало до этого обновления).

Я всё ещё могу войти в контейнер и получить доступ к консоли Rails, база данных там же и подключена к контейнеру — так что это просто крайне странно — у кого-нибудь есть другие идеи или шаги для устранения этой проблемы?

Если вы не смогли отладить проблему, одним из вариантов может быть создание резервной копии через командную строку и восстановление её на новом чистом сайте, работающем на PG13. Альтернативно, если вам необходимо быстро вернуть сайт в рабочее состояние, вы можете откатить версию до PG12, переместить существующую директорию shared/postgres_data_old обратно в shared/postgres_data и выполнить пересборку. Однако мы рекомендуем попробовать вариант с резервным копированием и восстановлением, так как проблема, похоже, не связана с самим обновлением базы данных.

Вы немного вышли за рамки стандартной поддерживаемой установки. :slight_smile:

У каждого экземпляра Discourse есть свой собственный Postgres, или у вас один Postgres для всех трёх?

Если у вас один контейнер Postgres/данных, то перед попыткой обновления Postgres необходимо ОСТАНОВИТЬ все экземпляры Discourse.

HaProxy не имеет никакого отношения к Postgres, поэтому, думаю, это не имеет значения.

Если у вас есть другие идеи для расследования, я с радостью их опробую, Майкл. К счастью, это не критично, что форум недоступен, так как он всё равно работал в режиме только для чтения (его заменил другой форум).

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

Честно говоря, это немного насторожило меня в плане перевода остальных моих форумов на Discourse, и понимание того, что пошло не так, могло бы быть полезно для всех нас.

Это стандартная установка с несколькими контейнерами, где у каждого форума есть свой файл app.yml и набор контейнеров, основанный на host: discourse/shared-site-name/standalone и host: discourse/shared-site-name/standalone/log/var-log (как в вопросах, которые я задавал, и в сообщениях на этом форуме).

При входе в каждый контейнер и запуске psql (sudo -u postgres psql discourse) с командой \l+ видно, что в каждом контейнере есть только одна база данных discourse (и все они имеют разный размер), поэтому я предполагаю, что это независимые экземпляры Discourse.

Есть ли у вас ссылка на «стандартный» способ запуска нескольких независимых форумов Discourse на одном сервере? Я могу проверить, совпадает ли это с тем, что у меня здесь, хотя я почти уверен, что моя конфигурация основана на сообщениях и рекомендациях команды Discourse.

Вы запускаете nginx внутри контейнера? Следующий шаг — проследить, куда направляются запросы. Правильно ли я понимаю, что HAProxy выполняет терминацию SSL, а затем проксирует запросы в соответствующие контейнеры?

А, понятно. Значит, для каждого из них нужно выполнить команду ./launcher rebuild YOUR-APP-NAME дважды. Я не думаю, что это можно сделать через веб-интерфейс.

И в контейнерах yml все шаблоны SSL и Let’s Encrypt закомментированы (или удалены)?

Насколько мне известно, сами контейнеры являются «стандартными» (то есть, как я понимаю, в каждом из них запущен nginx), и да, HAProxy обрабатывает весь SSL и направляет запросы в каждый контейнер.

Моя конфигурация соответствует описанию здесь: Set up Discourse on a server with existing Apache sites (с SSL-версией конфигурации HAProxy здесь).

Была одна проблема с конфигурацией HAProxy:

backend main_apache_sites
  server server1 127.0.0.1:8080 cookie A check
  cookie JSESSIONID prefix nocache

backend discourse_docker
  server server2 127.0.0.1:8888 cookie A check
  cookie JSESSIONID prefix nocache

backend discourse_docker_2
  server server2 127.0.0.1:8889 cookie A check
  cookie JSESSIONID prefix nocache

backend discourse_docker_3
  server server2 127.0.0.1:8890 cookie A check
  cookie JSESSIONID prefix nocache

backend letsencrypt-backend
  server letsencrypt 127.0.0.1:54321

По какой-то причине во всех бэкендах для Discourse во второй строке было указано server2 — вчера я изменил их на server2, server3 и так далее, но это не дало никаких результатов (и ранее всё работало нормально в таком виде).

Есть ли какие-то конкретные файлы журналов, которые я мог бы просмотреть и которые могли бы дать дополнительные подсказки? Возможно, логи Docker?

Да, они закомментированы:

templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
## Раскомментируйте эти две строки, если хотите добавить Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

Логи nginx внутри контейнеров приложения должны подтвердить, что запросы доходят до приложения. Можете ли вы их проверить? Nginx в контейнере проксирует запросы на 127.0.0.1:3000, что соответствует процессу Unicorn.

Просмотрев /var/log/nginx и /shared/log/rails, я не обнаружил ничего подозрительного. Фактически, ни один из журналов не обновлялся сегодня (4-го), за исключением /shared/log/rails/production.log, в котором есть лишь несколько заданий, подобных этому:

Журналы Rails:

Журналы Nginx:

Я также изменил порт в HAProxy и, как и ожидалось, получил ошибку «сервер не найден». Затем я обновил контейнер, вернув тот же порт, и поведение вернулось к прежнему (так что, думаю, это исключает проблему с HAProxy).

Есть ли какие-либо логи Docker, которые стоит проверить? Или я могу сохранить/экспортировать этот контейнер и отправить его вам, чтобы вы могли его осмотреть? Думаю, вы так же, как и я, задаетесь вопросом, что пошло не так :blush:

На самом деле я только что посмотрел ещё раз (то, что выше, было с прошлой ночи), и теперь в:

unicorn.stderr.log

(Извините, система не позволяет мне скопировать текст)

Сегодня в логи nginx ничего не записывалось, хотя в последнем логе от 30 января видно: 7: limiting requests by zone “flood” client: my.ip.address, POST /mini-profiler-resources — ошибка типа.

Редактирование: не уверен, поможет ли это, но при запуске docker logs APP:

Для неработающего форума:

# docker logs metaruby
run-parts: executing /etc/runit/1.d/00-ensure-links
run-parts: executing /etc/runit/1.d/00-fix-var-logs
run-parts: executing /etc/runit/1.d/01-cleanup-web-pids
run-parts: executing /etc/runit/1.d/anacron
run-parts: executing /etc/runit/1.d/cleanup-pids
Cleaning stale PID files
run-parts: executing /etc/runit/1.d/copy-env
Started runsvdir, PID is 43
ok: run: redis: (pid 55) 0s
ok: run: postgres: (pid 56) 0s
chgrp: invalid group: 'syslog'
supervisor pid: 50 unicorn pid: 89

Для форума 2 (работает нормально):

# docker logs f2

run-parts: executing /etc/runit/1.d/00-ensure-links

run-parts: executing /etc/runit/1.d/00-fix-var-logs

run-parts: executing /etc/runit/1.d/01-cleanup-web-pids

run-parts: executing /etc/runit/1.d/anacron

run-parts: executing /etc/runit/1.d/cleanup-pids

Cleaning stale PID files

run-parts: executing /etc/runit/1.d/copy-env

Started runsvdir, PID is 42

ok: run: redis: (pid 55) 0s

ok: run: postgres: (pid 54) 0s

chgrp: invalid group: 'syslog'

supervisor pid: 51 unicorn pid: 82

(51) Reopening logs

(51) Reopening logs

(51) Reopening logs

(51) Stopping Sidekiq

(51) Reloading unicorn (82)

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid... 22039

(51) Old pid is: 82 New pid is: 22039

(51) Stopping Sidekiq

(51) Reloading unicorn (22039)

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid...

(51) Waiting for new unicorn master pid... 23358

(51) Old pid is: 22039 New pid is: 23358

(51) Reopening logs

(51) Reopening logs

Для форума три (тоже работает нормально):

# docker logs f3

run-parts: executing /etc/runit/1.d/00-ensure-links

run-parts: executing /etc/runit/1.d/00-fix-var-logs

run-parts: executing /etc/runit/1.d/01-cleanup-web-pids

run-parts: executing /etc/runit/1.d/anacron

run-parts: executing /etc/runit/1.d/cleanup-pids

Cleaning stale PID files

run-parts: executing /etc/runit/1.d/copy-env

Started runsvdir, PID is 42

ok: run: redis: (pid 54) 0s

chgrp: invalid group: 'syslog'

ok: run: postgres: (pid 55) 0s

supervisor pid: 56 unicorn pid: 88

(56) Reopening logs

(56) Reopening logs

(56) Reopening logs

(56) Reopening logs

(56) Reopening logs

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

Вы упомянули, что Redis работает на сервере, но не в отдельных контейнерах. Не могли бы вы уточнить, что именно вы имеете в виду?

С такой конфигурацией Redis будет работать внутри контейнера (это видно в логах Docker).

Кстати, когда вы переходите по URL неработающего сайта, что отображается в логах nginx? Файл error.log должен быть пустым, а access.log — заполнен различными HTTP-запросами. Просто пытаюсь определить, на каком этапе возникает проблема.

Извините, я перепутал. На самом деле Redis работает в каждом контейнере, что подтверждается выполнением следующих команд на самом сервере и в каждом из трёх контейнеров Discourse с одинаковым результатом:

$ redis-cli ping
PONG
$ redis-server
# Создание TCP-сокета сервера для прослушивания *:6379: bind: Адрес уже используется (означает, что он уже запущен)
$ redis-cli
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> get mykey
(nil)
127.0.0.1:6379> set mykey somevalue
OK
127.0.0.1:6379> get mykey
"somevalue"

То же самое верно для всех трёх (примечательно: первый запрос get mykey всегда возвращает nil), поэтому можно с уверенностью сказать, что Redis запущен и работает независимо во всех контейнерах.

Он пуст, и в этой директории сегодня ничего не записывалось:

drwxr-xr-x 2 www-data www-data  4096 Feb  4 21:26 .
drwxrwxr-x 9 root     root      4096 Feb  2 08:03 ..
-rw-r--r-- 1 www-data www-data     0 Feb  3 07:38 access.log
-rw-r--r-- 1 www-data www-data     0 Feb  2 08:03 access.log.1
-rw-r--r-- 1 www-data www-data   294 Feb  1 09:43 access.log.2.gz
-rw-r--r-- 1 www-data www-data 37598 Jan 30 23:56 access.log.3.gz
-rw-r--r-- 1 www-data www-data 58059 Jan 30 07:36 access.log.4.gz
-rw-r--r-- 1 www-data www-data 55988 Jan 29 07:34 access.log.5.gz
-rw-r--r-- 1 www-data www-data 73964 Jan 28 07:49 access.log.6.gz
-rw-r--r-- 1 www-data www-data 78069 Jan 27 07:53 access.log.7.gz
-rw-r--r-- 1 www-data www-data     0 Feb  3 07:38 error.log
-rw-r--r-- 1 www-data www-data     0 Feb  2 08:03 error.log.1
-rw-r--r-- 1 www-data www-data    20 Feb  1 00:31 error.log.2.gz
-rw-r--r-- 1 www-data www-data   632 Jan 30 23:46 error.log.3.gz
-rw-r--r-- 1 www-data www-data   265 Jan 29 09:07 error.log.4.gz
-rw-r--r-- 1 www-data www-data    20 Jan 28 07:50 error.log.5.gz
-rw-r--r-- 1 www-data www-data  3107 Jan 28 07:41 error.log.6.gz
-rw-r--r-- 1 www-data www-data    20 Jan 26 07:53 error.log.7.gz

Проверил логи доступа для другого контейнера — там всё в порядке, значит проблема только в этом.

Похоже, что HAProxy передаёт запрос, но контейнер не может его обработать или принять. Неужели нет возможности что-то сбросить там? (Хотя я думал, что пересборка контейнера уже должна это делать?)

Это действительно похоже на то. Можете ли вы подтвердить, какие привязки портов присутствуют для каждого контейнера при запуске docker ps на хосте?

Конечно:

IMAGE                   COMMAND             CREATED             STATUS              PORTS                                     
local_discourse/1      	"/sbin/boot"        20 часов назад        Запущен 20 часов назад         0.0.0.0:2225->22/tcp, 0.0.0.0:8892->80/tcp
local_discourse/2   	"/sbin/boot"        4 дня назад          Запущен 4 дня назад           0.0.0.0:2223->22/tcp, 0.0.0.0:8889->80/tcp
local_discourse/3       "/sbin/boot"        4 дня назад          Запущен 4 дня назад           0.0.0.0:2224->22/tcp, 0.0.0.0:8890->80/tcp

Мне кажется, это связано с неудачной попыткой обновления через панель управления. Обычно при обновлении PostgreSQL или крупных версиях панель управления сообщает, что необходимо выполнить пересборку, и обновление через неё отключено. Но по какой-то причине этого не произошло (возможно, потому что я давно не обновлял этот форум — отсюда и мысль, что стоит сначала попробовать через панель управления). Либо, возможно, контейнер не был корректно остановлен или запущен перед выполнением пересборки :confused:

В конфигурации HAProxy я вижу, что бэкенды настроены на перенаправление на порты 8888, 8889 и 8890:

Однако контейнеры приложения прослушивают порты 8892, 8889, 8890 — похоже на несоответствие для backend discourse_docker. Вы что-то обновляли в конфигурации с момента публикации этого сообщения?

Да, порты HAProxy соответствуют правильным портам контейнера :smiley: Я почти уверен, что это не связано с этим, так как всё работало нормально — проблема возникла именно после этого обновления/пересборки.

Вход в контейнер, открытие статистики Top и переход на сайт тоже не дают никакого результата. На всякий случай, если это поможет, вот скриншот:

Если вам так будет удобнее, я могу «сохранить» контейнер и отправить его вам (это вообще возможно с Docker-контейнерами? ха-ха!) :slight_smile: