После последнего обновления почта не отправляется

В последнее время вышло много обновлений. Одно из них сломало тему письма (теперь в ней не указывается категория), и теперь письма вообще не отправляются. Пользователи очень недовольны. Я не знаю, с чего начать отладку этой проблемы. Где можно найти логи ошибок? Я помню, что где-то была страница о очередях Sidekiq и так далее, но не могу её найти. Любая помощь будет крайне признательна.

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

Причиной этого может быть то, что Sidekiq не обрабатывает запланированные задания, когда должен.

Мы обнаружили ту же проблему сегодня ранее на наших CD-сайтах. Убедитесь, что у вас как минимум этот коммит:

(Кажется, это именно этот коммит, но я не уверен на 100%)

Чтобы проверить, та же ли это проблема, просмотрите запланированные задания в /sidekiq и убедитесь, что нет заданий с истёкшим сроком.

Да, мы столкнулись с этим. Обновление всё исправило.

Я подтверждаю сотни неудачных заданий Sidekiq в latest-release +103

исправлено в latest-release +153

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

ОШИБКА - достигнут конец файла

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

Трассировка
Сообщение (5 сообщений в отчёте)

Исключение задания: достигнут конец файла

Трассировка

/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:237:in `rbuf_fill'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:199:in `readuntil'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-protocol-0.2.2/lib/net/protocol.rb:209:in `readline'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:1017:in `recv_response'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:676:in `block in do_start'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:1027:in `critical'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:676:in `do_start'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/net-smtp-0.5.1/lib/net/smtp.rb:642:in `start'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:154:in `start_smtp_session'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/network/delivery_methods/smtp.rb:108:in `deliver!'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/mail-2.9.0/lib/mail/message.rb:269:in `deliver!'
/usr/local/lib/ruby/3.3.0/delegate.rb:87:in `method_missing'
/var/www/discourse/lib/email/sender.rb:296:in `send'
/var/www/discourse/app/jobs/regular/download_backup_email.rb:19:in `execute'
/var/www/discourse/app/jobs/base.rb:318:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management.rb:17:in `with_connection'
/var/www/discourse/app/jobs/base.rb:305:in `block in perform'
/var/www/discourse/app/jobs/base.rb:301:in `each'
/var/www/discourse/app/jobs/base.rb:301:in `perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:220:in `execute_job'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:185:in `block (4 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:180:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/suppress_user_email_errors.rb:6:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/discourse_event.rb:6:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/pausable.rb:131:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job/interrupt_handler.rb:9:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:26:in `track'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:134:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:173:in `invoke'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:184:in `block (3 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:145:in `block (6 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:118:in `local'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:144:in `block (5 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/config.rb:39:in `block in <class:Config>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:139:in `block (4 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:281:in `stats'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:134:in `block (3 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:133:in `block (2 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:85:in `global'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:132:in `block in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:40:in `prepare'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:131:in `dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:183:in `block (2 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `block in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:86:in `process_one'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:76:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:10:in `watchdog'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:19:in `block in safe_thread'

Только что снова попробовал отправить тестовое письмо с ноутбука и получил следующее:

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

А в консоли JavaScript:

Возможно, у меня другая проблема — я проверю это через Mailgun.

Редактирование: Я увидел две ошибки в повторных попытках и удалил их. К сожалению, при тестировании ошибка всё ещё появляется. Я также проверил SMTP-сервер, и он, похоже, работает корректно.

Привет, Тобиас!

Твоя проблема отличается — соединение зависает в ожидании ответа вскоре после успешного начального подключения.

Я бы предположил, что ты пытаешься использовать неправильный протокол для неправильного порта… какие настройки ты используешь?

Показывает ли задача rake emails:test (с недавно обновлённой логикой и сообщениями об ошибках) какое-либо другое сообщение об ошибке?

Привет, Майкл! Спасибо за ответ. Я так сильно скучаю по вам! :smiling_face_with_three_hearts:

Хм… Я недавно перенёс свой сайт с DO на Hetzner, и всё работало отлично в течение пары недель. Мой другой сайт тоже работает без проблем. Это загадка. Где-то неделю назад всё перестало работать, и когда я начал разбираться, то увидел ошибки. Я обратился в поддержку Hetzner (они отказались помогать) и в Mailgun. По словам Mailgun:

Спасибо за ваш ответ. Последнее принятое аутентифицированное событие, которое мы видим, было 11 января и было отправлено через SMTP.

Пожалуйста, подтвердите, не были ли внесены какие-либо изменения? Предоставьте скриншот конфигурации вашего приложения для отправки писем для нашего рассмотрения, а также любые соответствующие ошибки из логов вашего приложения для отправки/SMTP.

Я просто сменил пароль Mailgun на случай, если дело в этом, и попробовал снова, но безрезультатно.

Вывод команды rake emails:test:

root@ubuntu-4gb-nbg1-1-app:/var/www/discourse# rake emails:test

Тестирование отправки через smtp.mailgun.org:587, имя пользователя: postmaster@domain с обычной аутентификацией.

====================================================================================== ОШИБКА =======================================================================================

НЕИЗВЕСТНАЯ ОШИБКА!

EOFError: достигнут конец файла

===================================================================================== РЕШЕНИЕ =====================================================================================

Это не распространённая ошибка. Нет рекомендованного решения!

Пожалуйста, сообщите точное сообщение об ошибке выше на https://meta.discourse.org/

(И решение, если вы его найдёте!)

====================================================================================================================================================================================

Похоже, ошибка возникает еще до попытки входа.

Чтобы исключить Discourse как причину, попробуйте выполнить проверку как с хоста, так и внутри контейнера:

$ openssl s_client -connect smtp.mailgun.org:587 -starttls smtp

Вы должны увидеть множество строк вывода, после чего сможете попытаться выполнить аутентификацию:

○ → openssl s_client -connect smtp.mailgun.org:587 -starttls smtp
Connecting to 34.160.63.108
CONNECTED(00000003)
…
SSL-Session:
   …
---
read R BLOCK
EHLO localhost
250-2ed1d46f4d7dec773e2a97b59f3a3bf8a2d6db54f94eead5dcf49e3ea1caac18
250-AUTH PLAIN LOGIN
250-SIZE 52428800
250-8BITMIME
250-SMTPUTF8
250 PIPELINING
AUTH PLAIN bWljaGFlbABtaWNoYWVsAHBhc3N3b3Jk
501 Username used for auth is not valid email address
535 Authentication failed
closed

Строки, которые нужно ввести:

EHLO localhost
AUTH PLAIN bWljaGFlbABtaWNoYWVsAHBhc3N3b3Jk

(эта строка содержит учётные данные michael/password, поэтому она, разумеется, не сработает, но вы можете посмотреть этот пост, чтобы узнать, как сформировать строку для ваших реальных учётных данных, если захотите попробовать вручную)

Надеюсь, что наглядное наблюдение за тем, что работает, а что нет, поможет.

Также можно попробовать использовать утилиту swaks, если она доступна — скорее всего, это пакет ОС, который можно установить.

Это немного проще, и вы можете, например, выполнить:

swaks --to frodo@shire.net --from bilbo@shire.net --auth PLAIN --auth-user bilbo --auth-password ring --server smtp.mailgun.org:587 --tls

только с вашими реальными учётными данными.

Вывод этой команды также может помочь выявить проблему.

Я попробовал использовать swaks и получил следующее:

=== Trying smtp.mailgun.org:587...
=== Connected to smtp.mailgun.org.
*** Remote host closed connection unexpectedly.

Это заставило меня проверить работу с другого сервера, где swaks показал «Great success» — сообщение довольно милое!

<~  250 Great success
 ~> QUIT
<~  221 See you later. Yours truly, Mailgun
=== Connection closed with remote host.

Так что проблема либо в том, что Mailgun блокирует мой сервер, либо мой сервер как-то неправильно настроен. Я свяжусь с Mailgun, а если дело не в этом, то уничтожу и пересоздам свой сервер.

Имеет смысл; это по сути та же ошибка, что и

Как вы и предполагаете, наиболее вероятная причина — что-то внешнее, что мешает соединению.

У меня есть подозрение, что вам нужно использовать порт 2525 вместо 587

Hetzner прямо указывает, что они не блокируют порт 587.

Кроме того, если бы они это делали, это, вероятно, проявилось бы как ошибка при установлении соединения.

Это почти исключает проблемы с конфигурацией Discourse и Mailgun.

На данном этапе наиболее полезной диагностической проверкой будет попытка использовать альтернативный порт отправки Mailgun с затронутого сервера:

openssl s_client -connect smtp.mailgun.org:2525 -starttls smtp

(или тот же тест с swaks).

Mailgun поддерживает порт 2525 специально для сред, где порт 587 блокируется фаерволами, фильтрацией исходящего трафика или сетевыми правилами на уровне провайдера.

Если:

  • 2525 работает, а 587 нет → очень вероятно вмешательство сети/IP/маршрутизации
  • оба не работают → ещё более убедительное указание на то, что что-то внешнее блокирует или прерывает соединение

В любом случае поведение гораздо больше соответствует «внешнему вмешательству в соединение», чем регрессии в Discourse или Sidekiq.

От Mailgun до сих пор нет ответа. Если они не смогут помочь, я планирую просто начать заново с нового сервера Hetzner.

Я также пробовал использовать swaks с другими портами. Интересно, что с портом 2525 я получил ту же ошибку, и она появилась мгновенно, без задержки.

=== Trying smtp.mailgun.org:2525...
=== Connected to smtp.mailgun.org.
*** Remote host closed connection unexpectedly.

Однако с портами 25 и 465 ошибка была другой, и ответ приходил через несколько секунд.

=== Trying smtp.mailgun.org:25...
*** Error connecting to smtp.mailgun.org:25:
*** Connection timed out
=== Trying smtp.mailgun.org:465...
*** Error connecting to smtp.mailgun.org:465:
*** Connection timed out

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

Это намеренно и ожидаемо.

Hetzner по умолчанию блокирует эти порты.

Логично… но интересно, что порты 25 и 465 дольше других не отвечали. В любом случае, это не срочно, и я подожду ответа от Mailgun. Это для семейного сайта, поэтому я просто предлагаю всем зайти и проверить уведомления, а не ждать только письма!

Завтра еду в Германию — навестить семью и присутствовать на похоронах. На следующей неделе я снова займусь этим вопросом и, скорее всего, просто начну заново с нового сервера.

Спасибо за всю помощь! Я многому научился в процессе. Мне очень понравился инструмент swaks.

Данные о времени действительно могут быть полезны.

Когда порт (или, в более общем смысле, трафик) блокируется, это может происходить двумя способами: «тихим» отбрасыванием (молчаливый отказ) или явным отказом (отправляется сообщение о недостижимости, например, «порт недоступен»).

Тихое отбрасывание — это то, что вы наблюдали с портами 25/465: попытка подключения выполняется, но ничего не происходит до истечения времени ожидания.

Явный отказ приводит к появлению сообщения, например, «Подключение отклонено» или «Порт недоступен».

Это указывает на ожидаемое поведение: подключение устанавливается, а затем немедленно разрывается.


Поскольку вы изменили источник, можно также попробовать изменить назначение. Попробуйте выполнить ту же команду, например, против smtp.gmail.com или smtp.office365.com. Вы должны получить ошибку аутентификации, что является сильным признаком того, что именно Mailgun блокирует именно вас.

То объяснение от @supermathie абсолютно точное :+1:
Разница во времени на самом деле является полезным сигналом, а не шумом.

Если подытожить то, что вы наблюдаете:

  • Таймаут для портов 25 / 465 → тихое отбрасывание (блокировка на уровне политики Hetzner, ожидаемое поведение)
  • Подключение к порту 2525 с немедленным закрытием → TCP-путь в порядке, но удаленная сторона разрывает сеанс

Последний пункт — ключевая деталь.

Потому что:

  • рукопожатие TCP успешно
  • согласование TLS по сути не начинается
  • и всё происходит мгновенно

…это сильно указывает на то, что Mailgun отклоняет соединение на раннем этапе, а не Discourse, Sidekiq или Ruby.

Это согласуется с несколькими вещами, которые мы наблюдали недавно:

  • фильтрация по репутации IP-адресов / региону
  • новые диапазоны IP-адресов Hetzner ещё не доверены
  • или проверки политик на стороне Mailgun до обмена SMTP-баннером

Ничто в Discourse не могло бы вызвать такое немедленное закрытие удаленного сокета — Sidekiq просто передает сообщение в Net::SMTP и ждет.

Если позже вы захотите получить ещё одно очень надежное подтверждение (никакой срочности):

openssl s_client -connect smtp.gmail.com:587 -starttls smtp

Вы должны увидеть корректный SMTP-баннер, а затем ошибку аутентификации — это в основном докажет, что исходящий SMTP сам по себе работает нормально, и только Mailgun ведет себя иначе.


И я полностью понимаю, что вы решили приостановить работу над этим — особенно учитывая обстоятельства.
Мне очень жаль, что вам приходится сталкиваться с этим на фоне всего остального :heart:

Если Mailgun ответит чем-то полезным — отлично. Но, честно говоря, начать заново или сменить провайдера (Brevo, Postmark и т. д.) часто быстрее, чем ждать исправления репутации SMTP.

Вы уже отладили это абсолютно правильно — ничего здесь не указывает на то, что вы что-то упустили или неправильно настроили Discourse.

Удачной дороги в Германию и берегите себя.