kikukawa's diary

都内で活動するシステムエンジニアが書いてます。 興味を持った技術やハマったポイント、自分用メモをつけてます。 最近はweb中心

fargateのログをdatadogへ送る

fargateのログはclowdwatchを経由してdatadogへ送る方法がありますが、
datadogが推奨しているfirelensを使って直接datadogへ送る方法についてメモです。
といってもやることは単純でアプリケーション用のコンテナにsidecar構成でfluentbitのコンテナを起動させるだけです。

コンテナ定義のサンプル

{
    "family": "foo",
    "taskRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/foo_ecs_task_iam_role",
    "executionRoleArn": "arn:aws:iam::XXXXXXXXXXXX:role/foo_ecs_task_execution_role",
    "containerDefinitions": [
        {
             "essential": true,
             "image": "amazon/aws-for-fluent-bit:latest",
             "name": "log-router",
             "firelensConfiguration":{
                 "type":"fluentbit",
                 "options":{
                     "enable-ecs-log-metadata":"true"
                 }
             },
             "memoryReservation": 50
        },
        {
             "essential": true,
             "image": "XXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/foo-application:latest",
             "name": "app",
             "logConfiguration": {
                 "logDriver":"awsfirelens",
                 "options": {
                    "Name": "datadog",
                    "Host": "http-intake.logs.datadoghq.com",
                    "TLS": "on",
                    "dd_service": "foo-service",
                    "dd_source": "foo-middleware",
                    "dd_tags": "foo:foo-value,bar:bar-value",
                    "provider": "ecs"
                },
                "secretOptions": [{
                    "name": "apikey",
                    "valueFrom": "/DATADOG_API_KEY"
                }]
            },
            "memoryReservation": 100
        }
    ]
}

1つ目のコンテナ定義がfluentbit用です。
こちらは基本的に固定で大丈夫です。
2つ目のコンテナ定義が自分のアプリケーション用です。

自分で設定するのは

  • logConfiguration.opions.dd_service
  • logConfiguration.opions.dd_source
  • logConfiguration.opions.dd_tags
  • logConfiguration.secretOptions.valueFrom

くらいだと思います。

logConfiguration.opions.dd_service

datadog上でserviceに値する項目です。
ご自身の環境で決めてよいかと思います。
特に入れるものがなければアプリケーション名など。

logConfiguration.opions.dd_source

datadog上でsourceに値する項目です。
ここはミドルウェアの名前を入れるのが一般的みたいです。
httpd,redis,mysqlなど
複数構成したコンテナであればwebなどでも良いかもしれません。

logConfiguration.opions.dd_tags

datadog上でタグとして展開されます。
コロン区切りでkeyとvalueを定義して、複数指定したいときはカンマで区切ります。

logConfiguration.secretOptions.valueFrom

datadogのapiキーを指定します。
logConfiguration.opions 側に定義しても良いのですが、それだとベタ書きになります。
上記では、 awsのパラメーターストアから取得する方法で記述しています。

まとめ

clowdwatch側には当然ログは流れなくなるのでログの確認はdatadogで行うことになります。
fluentbitのコンテナを追加で定義して
アプリケーション側のコンテナのlogConfigurationをちょっと修正するだけで
datdogにログを送れるようになるので便利です。

コンテナ監視と合わせて設定しておくとよいかと思います。

kkkw.hatenablog.jp

参考

phpunit8のwarning対応

phpunit8を使っているとphpunit9でなくなるメソッドなどのwarningが発生します。
その対処方法いくつか備忘録として残しておきます。

assertArraySubset

結構重宝していたのでなくなるのは辛いメソッドです。
代替手段としては、 https://packagist.org/packages/dms/phpunit-arraysubset-asserts があります。
本家phpunitのissue でも紹介されているのでメンテナンスの心配は少なそうです。

phpunit8では、 v0.1系を使う必要があります。 執筆時点での最新は v0.2系です。

assertAttributeSame

こちらは代替となるパッケージがありませんでしたので自作になります。
本家はclassとobject両方に対応していましたが、下記はobjectのみです。
また、objectのプロパティにアクセスするという簡単なことが実はいろいろ考えると複雑な処理になるので
symfony/property-access を使って楽をしてます。

use PHPUnit\Framework\InvalidArgumentException;
use Symfony\Component\PropertyAccess\PropertyAccess;

trait Test_Concern_AssertObjectAttribute
{
    /**
     * @param $expected
     * @param string $actualAttributeName
     * @param $actualObject
     * @param string $message
     */
    public function assertObjectAttribute($expected, string $actualAttributeName, $actualObject, string $message = '')
    {

        if (!is_string($actualAttributeName)) {
            throw InvalidArgumentException::create(2, 'attribute name');
        }
        if (!is_object($actualObject)) {
            throw InvalidArgumentException::create(3, 'object');
        }
        $propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
        ->disableExceptionOnInvalidPropertyPath()
        ->getPropertyAccessor();

        $prop = $propertyAccessor->getValue($actualObject, $actualAttributeName);
        /** @noinspection PhpUndefinedMethodInspection */
        self::assertSame(
            $expected,
            $prop,
            $message
        );
    }
}

assertContains

代わりに assertStringContainsString を使います。

expectException

アノテーションを使ったExceptionのアサーションではなく、メソッド形式に変更します。
Exceptionを発生させる処理の前に、expectExceptionなどを呼び出しておくだけです。

/**
 * @expectedException ErrorException
 * @expectedExceptionMessage process is timeout.
 */
public function test_foo()
{
    trigger_exception();
}
public function test_foo()
{
    self::expectException(ErrorException::class);
    self::expectExceptionMessage('process is timeout.');
    trigger_exception();
)

This test did not perform any assertions

phpunitアサーションが一つもないテストに表示されます。
dbunitやmockeryなど別の手段でアサーションしている場合は、 @doesNotPerformAssertions でリスキーではないとマークして対処できます。

参考

datadogでfargateのコンテナ監視をする

datadogでコンテナ内の詳細なメトリクスが取得できるようになります。

上記2つを参考に進めていけばあっさりと設定できます。

コンテナ定義

[
  {
      "cpu": 0,
      "memoryReservation": 1024,
      "name": "foo-app",
      "image": "xxxxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/foo-app-repository:latest",
      "essential": true,
      "dockerLabels": {
          "com.datadoghq.tags.env": "prd",
          "com.datadoghq.tags.service": "foo-app"
      },
      "logConfiguration": {
          "logDriver": "awslogs",
          "options": {
              "awslogs-group": "foo-app-log-group",
              "awslogs-region": "ap-northeast-1",
              "awslogs-stream-prefix": "ecs"
          }
      },
      "secrets": [
          {
          "name": "DB_HOST",
          "valueFrom": "/DB_HOST"
          },
          {
          "name": "DB_USER",
          "valueFrom": "/DB_USER"
          },
          {
          "name": "DB_PASS",
          "valueFrom": "/DB_PASS"
          },
          {
          "name": "DB_NAME",
          "valueFrom": "/DB_NAME"
          }
      ]
  },
  {
      "cpu": 10,
      "memoryReservation": 256,
      "name": "datadog-agent",
      "image": "datadog/agent:latest",
      "essential": true,
      "logConfiguration": {
          "logDriver": "awslogs",
          "options": {
              "awslogs-group": "datadog-agent-log-group",
              "awslogs-region": "ap-northeast-1",
              "awslogs-stream-prefix": "ecs"
          }
      },
      "secrets": [
          {
          "name": "DD_API_KEY",
          "valueFrom": "/DD_API_KEY"
          }
      ],
      "environment": [
          {
          "name": "ECS_FARGATE",
          "value": "true"
          }
      ]
  }
]

アプリケーション側の dockerLabels

datadogのタグ付けを行っています。
DD_DOCKER_LABELS_AS_TAGSDD_DOCKER_ENV_AS_TAGS を使うこともできるようですが、
datadogとしては下記の方法を推奨しているようです。
https://docs.datadoghq.com/ja/getting_started/tagging/unified_service_tagging/?tab=ecs

今回はアプリケーション側にenvがないので、 部分的なコンフィギュレーション で設定しています。

追加のインテグレーションのメトリクス収集

こちらで

インテグレーションメトリクスの収集の詳細については、ECS Fargate のインテグレーションセットアップを参照してください。

とあるのですが、最初なんのことか分からず困りました。
要はアプリケーション側がdatadogが対応しているインテグレーション(ミドルウェアなど)であれば、
追加でさらに詳細なメトリクスを収集できますよ。
ということのようです。

参考

aws cliからfargateのタスクを実行する

すでにタスク定義が存在するfargateタスクをaws cliから実行する方法です。
いろいろ探しましたが、fargateではなくインスタンスであったり、サービス前提のタスクだったりで
私の欲しい物がなかったので覚書を残します。

前提

  • fargateで動かす
  • タスク定義はすでに存在する
  • サービスでは動かさない
  • タスク定義はバッチのような動きをする

iam周り

aws cliを叩くときに指定するiamには以下のアクションを許可しています。

  • ecs:ListTaskDefinitions
  • ecs:ListTasks
  • ecs:DescribeTasks
  • ecs:RunTask
  • iam:PassRole

ecs:ListTaskDefinitions , ecs:ListTasks , ecs:DescribeTasks はタスク実行前、実行中に
list-task-definitions , list-tasks , wait tasks-stopped などを使うためです。
iam:PassRole はタスク定義で指定しているタスク実行ロールにわたすためです。

コマンド

$ aws ecs list-task-definitions #タスク定義のarnを確認する
$ aws ecs run-task \
    --cluster foo-cluster \
    --task-definition arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/foo-task-definision:9 \ #確認したarnをリビジョン含めて指定する
    --launch-type FARGATE \
    --network-configuration 'awsvpcConfiguration={subnets=[subnet-xxxx],securityGroups=[sg-xxxxxxxx],assignPublicIp=DISABLED}' #ネットワーク周りの設定を指定する
$ aws ecs list-tasks --cluster foo-cluster #タスク一覧で確認する
$ aws ecs wait tasks-stopped \ #タスクが終わるのを待つ
    --cluster foo-cluster \
    --tasks arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx #run-taskで返ってきたarnを指定する

参考

embulkでgoogle analyticsからデータを抽出する

embulkのinputプラグインである embulk-input-google_analytics を使ってgoogle analyticsからデータを抽出したので覚書です。

GCP ConsoleとGAのつなぎこみ

embulk-input-google_analytics では、GAの認証をGCPのサービスアカウントで認証します。
そのためにはGCPとGAをつなぎこまなければなりません。

手順としては、

  1. GCP側でサービスアカウントを作成する
  2. 作成したサービスアカウントのメールアドレスで、GA側にユーザーを作る

というだけです。

実際の作業は このサイト が分かりやすいです。

dimensions,metricsに指定するもの

ymlに設定するdimensions,metricsは Dimensions & Metrics Explorer にあるものが指定できます。 GAのreporting apiのパラメーターと同じものです。

embulk-output-bigqueryと一緒に使うとエラーになる場合

BigQueryに出力するのが流れとしては自然かと思いますが、 embulk-output-bigquery をしようするとエラーがでます。
こちら に同様の質問がありました。
その場合は、 embulk-input-google_analytics の v0.1.24以降を指定すると解決することがあります。

直接の原因はterratailにあるようにGemごとのバージョン依存関係なのですが https://github.com/treasure-data/embulk-input-google_analytics/pull/46 がで解決されてv0.1.24で取り込まれてます。

ssh-keygenよく使うもの

自分用メモ

鍵ペアを作る

$ ssh-keygen -t ed25519 -C "" -m PEM
$ ssh-keygen -t rsa -b 4096 -C "" -m PEM

強度の強い ed25519 が使えればそれを使い
使えない場合は、 rsa4096 を使います
-C は公開鍵に記載されるコメントなので不要なら空文字を指定しておきます
-m はフォーマットの指定。なしだとopenssh形式になります。PEM形式にしておきたいので指定します。
パスフレーズをなしにしたい場合は、聞かれたときに何も入力せずエンターでなしにできます。

パスレーズを変更する

$ ssh-keygen -p -f path/to/secret-key

秘密鍵から公開鍵を作る

$ ssh-keygen -y -f path/to/secret-key

パスフレーズがある場合は入力を求められます。
入力を求められるかどうかでパスフレーズが設定されているかどうかの確認にも使えます。

参考

sftpコマンドまとめ

毎回検索しているので自分用メモ

接続

$ sftp -oPort="2222" foo@localhost #ポート指定
$ sftp -oPort="2222" -oIdentityFile=~/ssh/sftp_rsa foo@localhost #ポートと鍵指定

ディレクトリの移動、確認

リモート側は普通にコマンドを打つだけ

$ pwd
$ ls
$ cd path/to

ローカルは接頭辞に l をつける

$ lpwd
$ lls
$ lcd path/to

get

リモート側からホスト側にファイルを引っ張る

$ get foo.txt
$ mget *.txt #メタ文字を使うときはmget

put

リモート側にホスト側からファイルを送る

$ put foo.txt
$ mput *.txt #メタ文字を使うときはmput

パスの補完

Macのデフォルトのsftpコマンドではパスの補完が出来ないが、opensshのsftpならばできる

brew install openssh

参考

https://cns-guide.sfc.keio.ac.jp/2004/2/6/3.html