Отсутствуют строки журнала при записи в cloudwatch из контейнеров ECS Docker

(Контейнер Docker на AWS-ECS завершается до того, как все журналы будут напечатаны в CloudWatch Logs) Почему некоторые потоки группы журналов CloudWatch не завершены (т. Е. Контейнер Docker Fargate завершается успешно, но журналы перестают обновляться внезапно)? Это время от времени наблюдается почти во всех группах журналов, однако не при каждом запуске потока журналов / задач. Я использую версию 1.3.0


Описание:
Dockerfile запускает скрипты node.js или Python с помощью команды CMD.

Это не серверы / длительные процессы, и в моем случае требуется, чтобы контейнеры закрывались после завершения задачи.

Пример файла Dockerfile:

FROM node:6
WORKDIR /path/to/app/
COPY package*.json ./
RUN npm install
COPY . .
CMD [ "node", "run-this-script.js" ]


Все журналы правильно распечатываются на stdout / stderr моего терминала, когда эта команда запускается на терминале локально с docker run.
Чтобы запустить их как задачи ECS на Fargate, драйвер журнала для установлен как awslogs из шаблона CloudFormation.

...
LogConfiguration:
   LogDriver: 'awslogs'
     Options:
        awslogs-group: !Sub '/ecs/ecs-tasks-${TaskName}'
        awslogs-region: !Ref AWS::Region
        awslogs-stream-prefix: ecs
...

Видя, что иногда вывод журналов cloduwatch бывает неполным, я провел тесты и проверил все ограничения из CW Logs Limits, и я уверен, что проблема не в этом.
Сначала я думал, что это проблема с асинхронным выходом node js до того, как console.log() будет сброшен, или что процесс завершается слишком рано, но та же проблема возникает, когда я использую другой язык - что заставляет меня думать, что это не проблема с кодом, а именно с облачными часами.
У меня не получилось вызвать задержки в коде путем добавления таймера сна.

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

образцы журналов: неполный поток:

{ "message": "configs to run", "data": {"dailyConfigs":"filename.json"]}}
running for filename

завершенный поток журнала:

{ "message": "configs to run", "data": {"dailyConfigs":"filename.json"]}}
running for filename

stdout: entered query_script
... <more log lines>
stderr:
real 0m23.394s
user 0m0.008s
sys 0m0.004s
(node:1) DeprecationWarning: PG.end is deprecated - please see the upgrade guide at https://node-postgres.com/guides/upgrading

comment
Что произойдет, если вы закидываете логи в саму систему, а не в Cloudwatch? Вы тогда получаете полные логи?   -  person deosha    schedule 12.02.2019
comment
@deosha да, я получаю полные логи, если они есть в системе   -  person tanvi    schedule 12.02.2019
comment
Можете ли вы публиковать журналы Cloudwatch, а также полные журналы?   -  person deosha    schedule 12.02.2019
comment
@deosha обновил вопрос, чтобы включить его, но содержимое фактических журналов не имеет ничего общего с ошибкой, поскольку я вижу это в целом ряде различных задач.   -  person tanvi    schedule 12.02.2019
comment
Трудно сказать, что такое драйвер ведения журнала CloudWatch, но я могу порекомендовать взглянуть на альтернативный collectord.io, который основан на драйвер регистрации json-файлов и пересылки из файлов. Collectord хранит ссылки на файлы и всегда пересылает их CloudWatch до конца.   -  person outcoldman    schedule 27.03.2019
comment
Основная ошибка AWS теперь кажется исправленной, поэтому нет необходимости реализовывать обходной путь для получения всех журналов. Я обновил свой ответ.   -  person asavoy    schedule 12.07.2019


Ответы (4)


ОБНОВЛЕНИЕ: похоже, теперь это исправлено, поэтому нет необходимости применять описанный ниже обходной путь.


Я видел такое же поведение при использовании контейнеров ECS Fargate для запуска скриптов Python - и в результате испытал такое же разочарование!

Я думаю, это связано с тем, что агент CloudWatch Logs Agent публикует события журнала партиями:

Как группируются события журнала?

Пакет становится полным и публикуется при выполнении любого из следующих условий:

  1. buffer_duration количество времени прошло с момента добавления первого события журнала.

  2. Накоплено менее batch_size событий журнала, но добавление нового события журнала превышает batch_size.

  3. Количество событий журнала достигло batch_count.

  4. События журнала из пакета не охватывают более 24 часов, но добавление нового события журнала превышает 24-часовое ограничение.

(Ссылка: https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html)

Таким образом, возможное объяснение состоит в том, что события журнала буферизуются агентом, но еще не публикуются, когда задача ECS остановлена. (И если так, то это похоже на проблему ECS - любые инженеры AWS ECS, желающие высказать свое мнение по этому поводу ...?)

Кажется, что нет прямого способа обеспечить публикацию журналов, но он предполагает, что можно подождать не менее buffer_duration секунд (по умолчанию 5 секунд), и все предыдущие журналы должны быть опубликованы.

После небольшого тестирования, которое я опишу ниже, я нашел обходной путь. Сценарий оболочки run_then_wait.sh обертывает команду для запуска сценария Python, чтобы добавить сон после завершения сценария.

Dockerfile

FROM python:3.7-alpine
ADD run_then_wait.sh .
ADD main.py .

# The original command
# ENTRYPOINT ["python", "main.py"]

# To run the original command and then wait
ENTRYPOINT ["sh", "run_then_wait.sh", "python", "main.py"]

run_then_wait.sh

#!/bin/sh
set -e

# Wait 10 seconds on exit: twice the `buffer_duration` default of 5 seconds
trap 'echo "Waiting for logs to flush to CloudWatch Logs..."; sleep 10' EXIT

# Run the given command
"$@"

main.py

import logging
import time

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()

if __name__ == "__main__":
    # After testing some random values, had most luck to induce the
    # issue by sleeping 9 seconds here; would occur ~30% of the time
    time.sleep(9)
    logger.info("Hello world")

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

Трудно доказать, что предложенное объяснение является точным, поэтому я использовал приведенный выше код, чтобы проверить эффективность обходного пути. Тестом была исходная команда vs. run_then_wait.sh, по 30 запусков каждая. В результате проблема наблюдалась в 30% случаев против 0% соответственно. Надеюсь, это так же эффективно для вас!

person asavoy    schedule 26.03.2019
comment
Спасибо за обновление ответа, только что посмотрел на страницу форума, там написано - теперь это должно быть решено для любых новых запущенных задач. Означает ли это, что мне нужно повторно развернуть все старые задачи, чтобы это исправление вступило в силу? - person tanvi; 12.07.2019

Только что обратился в службу поддержки AWS по поводу этой проблемы, и вот их ответ:

...

Основываясь на этом случае, я вижу, что это происходит для контейнеров в задаче Fargate, которая быстро завершается после вывода в stdout / stderr. Похоже, это связано с тем, как работает драйвер awslogs и как Docker в Fargate взаимодействует с конечной точкой CW.

Глядя на наши внутренние заявки на то же самое, я вижу, что наша служба поддержки все еще работает над постоянным решением этой обнаруженной ошибки. К сожалению, пока неизвестно, когда будет установлено исправление. Однако я воспользовался этой возможностью, чтобы добавить этот случай во внутренний тикет, чтобы сообщить команде о подобном и попытаться ускорить процесс.

Между тем, этого можно избежать, увеличив время жизни выходящего контейнера, добавив задержку (~> 10 секунд) между выходом журнала приложения и выходом из процесса (выход из контейнера).

...

Обновление: обратились в AWS примерно 1 августа 2019 г. и сказали, что проблема устранена.

person Zhenya    schedule 24.05.2019

Я тоже это заметил. Это должно быть ошибка ECS?

Мое обходное решение (Python 3.7):

import atexit
from time import sleep

atexit.register(finalizer)

def finalizer():
    logger.info("All tasks have finished. Exiting.")
    # Workaround:
    # Fargate will exit and final batch of CloudWatch logs will be lost
    sleep(10)

person davegallant    schedule 01.05.2019
comment
Я пытался вызвать явный сон, но в моем случае это, похоже, не помогло - я добавил несколько ожиданий в скрипт, который запускает другие скрипты в контейнере, но журналы не печатаются даже тогда. Я предполагаю, что это проблема с очисткой журнала, а не время - person tanvi; 01.05.2019
comment
то же самое, добавил засыпания по сценарию, и все равно получить половину журнала - person pelos; 24.06.2021

У меня была такая же проблема с сбросом логов в CloudWatch.

После ответа asavoy я переключился с формы exec на форму оболочки ENTRYPOINT и добавил в конце 10-секундный сон.

До:

ENTRYPOINT ["java","-jar","/app.jar"]

После:

ENTRYPOINT java -jar /app.jar; sleep 10
person Marcin Piela    schedule 04.05.2019