🍰

Elastic Beanstalk 環境で CakePHP のログを CloudWatch Logs で収集する

5 min read

はじめに

統合された CloudWatch エージェントを使ったサンプルが少なかったので備忘録。

戦略

postdeployフックを使用して既存の設定を上書き、エージェントを再起動させる力技。

.platform/hooks/postdeploy
├── 000-cakephp.sh 
├── 001-fix_cloudwatch_agent_conf.php
└── 002-restart_cloudwatch_agent.sh

CakePHP の対応

  • デプロイ対象のディレクトリ外に logs, tmp 用のディレクトリを作成し、デフォルトのパスにシンボリックリンクを貼る。
  • キャッシュのクリア
000-cakephp.sh
#!/bin/bash
set -eu

LOGS_DIR="/var/app/cake/logs"
LOGS_LINK_DIR="/var/app/current/logs"

TMP_DIR="/var/app/cake/tmp"
TMP_LINK_DIR="/var/app/current/tmp"

# logs
if [ ! -d "${LOGS_DIR}" ]; then
  mkdir -p "${LOGS_DIR}"
  chown -R webapp:webapp "${LOGS_DIR}"
  chmod -R 0777 "${LOGS_DIR}"
  logger -t eb-cake "mkdir ${LOGS_DIR}"
fi

if [ -d "${LOGS_LINK_DIR}" ] && [ ! -L "${LOGS_LINK_DIR}" ]; then
  rm -rf "${LOGS_LINK_DIR}"
  logger -t eb-cake "rm ${LOGS_LINK_DIR}"
fi

ln -s "$LOGS_DIR" "$LOGS_LINK_DIR"
logger -t eb-cake "ln ${LOGS_DIR} ${LOGS_LINK_DIR}"

# tmp
if [ ! -d "${TMP_DIR}" ]; then
  mkdir -p "${TMP_DIR}/sessions"
  mkdir -p "${TMP_DIR}/cache"
  mkdir -p "${TMP_DIR}/cache/models"
  mkdir -p "${TMP_DIR}/cache/persistent"
  mkdir -p "${TMP_DIR}/cache/views"
  chown -R webapp:webapp "${TMP_DIR}"
  chmod -R 0777 "${TMP_DIR}"
  logger -t eb-cake "mkdir ${TMP_DIR}"
fi

if [ -d "${TMP_LINK_DIR}" ] && [ ! -L "${TMP_LINK_DIR}" ]; then
  rm -rf "${TMP_LINK_DIR}"
  logger -t eb-cake "rm ${TMP_LINK_DIR}"
fi

ln -s "$TMP_DIR" "$TMP_LINK_DIR"
logger -t eb-cake "ln ${TMP_DIR} ${TMP_LINK_DIR}"

# cache
/var/app/current/bin/cake cache clear_all
logger -t eb-cake "clear cache"

既存の設定を上書き

以下の理由から、既存の設定は上書きした方が良さげ。

  • timestamp_formatが設定されてないので CloudWatch Logs のタイムスタンプと大きくズレる可能性がある。
  • multi_line_start_patternが未設定のまま、複数行のログが流れて悲しい思いをする。
    eb-engine.log とか。

httpd(ログのフォーマットはデフォルトのまま)を使用する想定。

001-fix_cloudwatch_agent_conf.php
#!/usr/bin/env php

<?php
define('TARGET', '/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_beanstalk.json');
define('HAS_FIXED', '/root/.fixed_cloudwatch_agent_conf');

openlog('eb-cw-agent-conf', LOG_PID, LOG_USER);

if (file_exists(HAS_FIXED)) {
    syslog(LOG_INFO, 'already fixed config');
    exit;
}

$list = [
    [
        'file_path' => '/var/log/eb-engine.log',
        'log_group_name' => '/aws/elasticbeanstalk/example-env/var/log/eb-engine.log',
        'log_stream_name' => '{instance_id}',
        'timestamp_format' => '%Y/%m/%d %H:%M:%S.%f',
        'multi_line_start_pattern' => '{timestamp_format}',
    ],
    [
        'file_path' => '/var/log/eb-hooks.log',
        'log_group_name' => '/aws/elasticbeanstalk/example-env/var/log/eb-hooks.log',
        'log_stream_name' => '{instance_id}',
        'timestamp_format' => '%Y/%m/%d %H:%M:%S.%f',
        'multi_line_start_pattern' => '{timestamp_format}',
    ],
    [
        'file_path' => '/var/log/httpd/access_log',
        'log_group_name' => '/aws/elasticbeanstalk/example-env/var/log/httpd/access_log',
        'log_stream_name' => '{instance_id}',
        'timestamp_format' => '[%d/%b/%Y:%H:%M:%S %z]',
    ],
    [
        'file_path' => '/var/log/httpd/error_log',
        'log_group_name' => '/aws/elasticbeanstalk/example-env/var/log/httpd/error_log',
        'log_stream_name' => '{instance_id}',
        'timestamp_format' => '[%a %b %d %H:%M:%S.%f %Y]',
    ],
    [
        'file_path' => '/var/app/cake/logs/error.log',
        'log_group_name' => '/aws/elasticbeanstalk/example-env/var/app/cake/error_log',
        'log_stream_name' => '{instance_id}',
        'timestamp_format' => '%Y-%m-%d %H:%M:%S',
        'multi_line_start_pattern' => '{timestamp_format}',
    ],
];

$tmp = [
    'logs' => [
        'logs_collected' => [
            'files' => [
                'collect_list' => $list,
            ],
        ],
    ],
];

$json = json_encode($tmp, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
file_put_contents(TARGET, $json, LOCK_EX);
syslog(LOG_INFO, 'fixed config');

エージェントの再起動

002-restart_cloudwatch_agent.sh
#!/bin/bash
set -eu

HAS_FIXED="/root/.fixed_cloudwatch_agent_conf"

if [ -e "${HAS_FIXED}" ]; then
  logger -t eb-cw-agent "already fixed config"
  exit 0
fi

systemctl restart amazon-cloudwatch-agent
logger -t eb-cw-agent "restart agent"

touch "${HAS_FIXED}"
logger -t eb-cw-agent "touch fixed config"

注意点

削除するとデプロイに失敗するので、以下のグループは CloudWatch ロググループから削除しない。

  • /xxxx/xxx/eb-engine.log
  • /xxxx/xxx/eb-hooks.log
  • /xxxx/xxx/httpd/access_log
  • /xxxx/xxx/httpd/error_log
  • /xxxx/xxx/nginx/access.log
  • /xxxx/xxx/nginx/error.log

まとめ

相変わらずの力技なので、もっとスマートな方法があるはず。。。

Discussion

ログインするとコメントできます