GitHub EnterpriseをAWSで使おう – Amazon Kinesis Firehoseを利用したログのアーカイブ

2016.12.26

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは、中山です。

GitHub Enterprise(以下GHE) on AWSシリーズの第6弾です。今回はGHEのログをAmazon Kinesis Firehose(以下Kinesis Firehose)を利用してS3にアーカイブする方法をご紹介します。Kinesis FirehoseへのプロデューサーとしてEC2インスタンス上にインストールしたawslabs/aws-fluent-plugin-kinesisを利用します。

今回動作検証した主要なソフトウェア/OSのバージョンは以下の通りです。バージョンによって設定方法が変更される可能性があります。その点ご了承ください。

  • GHE: 2.8.4
  • Amazon Linux: 2016.09.1 (HVM), SSD Volume Type - ami-9be6f38c
  • fluentd: 0.12.29
  • aws-fluent-plugin-kinesis: 1.1.2

構成

今回は以下のような構成を作成します。

image1

GHEのログをフォワーディングするためにEC2を前段に置き、そこからaws-fluent-plugin-kinesisを利用してKinesis Firehoseにログを送ります。送られたログを最終的にS3バケットにアーカイブさせます。

GHEから直接Kinesis Firehoseにログを送ればよいのではと思った方もいらっしゃるかと思います。GHE自体はUbuntuなのでaws-fluent-plugin-kinesisをインストールしてデータのプロデューサーとすることは技術的に可能です。しかし、GHEにサードパーティのソフトウェアをインストールすることは推奨されていません。また、バージョンのアップデート処理中に/data/user以外のファイルシステムをまるっと書き換えるため、用意した設定ファイルが削除されてしまいます。こういった事情もあり、今回はGHEとKinesis Firehoseの間にEC2を挟むという構成にしました。

GHE自体やVPCなどのネットワーク周りのリソースについては、GitHub社が提供しているCloudFormationテンプレートですでに構築済みとして進めさせていただきます。設定方法については以下のエントリを参照してください。

注意点として、執筆時点(2016/12/23)ではKinesis Firehoseが東京リージョンに対応していません。現在以下のリージョンにのみ対応しています。

  • 北部バージニア(us-east-1)
  • オレゴン(us-west-2)
  • アイルランド(eu-west-1)

そのため、今回は北部バージニアリージョンに環境を構築していきます。

なお、今回は弊社の下記エントリを参考に設定しています。合わせて参照いただくと理解が深まるかと思います。

やってみる

それでは早速構築してみましょう。今回はすべてのリソースをAWS CLIで作成します。繰り返しとなりますが、各種リソースを作成するリージョンはus-east-1です。 --region オプションで都度指定するのは面倒なので環境変数 AWS_DEFAULT_REGION にus-east-1を入れておきましょう。

1. EC2インスタンスの作成

まずはGHEから送られるログをKinesis Firehoseへ転送するEC2インスタンスを構築します。最初にEC2インスタンスにひも付けるIAM Roleを作成します。

# IAM Roleの作成
$ aws iam create-role \
  --role-name ec2-role \
  --assume-role-policy-document \
    "$(jo Version=2012-10-17 Statement=$(jo -a $(jo Effect=Allow Principal=$(jo Service=ec2.amazonaws.com) Action=sts:AssumeRole)))"
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "ec2.amazonaws.com"
                    }
                }
            ]
        },
        "RoleId": "*********************",
        "CreateDate": "2016-12-23T05:30:28.398Z",
        "RoleName": "ec2-role",
        "Path": "/",
        "Arn": "arn:aws:iam::************:role/ec2-role"
    }
}
# 作成したIAM RoleにKinesis Firehoseのフルアクセス権限をアタッチ
$ aws iam attach-role-policy \
  --role-name ec2-role \
  --policy-arn arn:aws:iam::aws:policy/AmazonKinesisFirehoseFullAccess
# インスタンスプロファイルの作成
$ aws iam create-instance-profile \
  --instance-profile-name ec2-profile
{
    "InstanceProfile": {
        "InstanceProfileId": "*********************",
        "Roles": [],
        "CreateDate": "2016-12-23T05:47:00.289Z",
        "InstanceProfileName": "ec2-profile",
        "Path": "/",
        "Arn": "arn:aws:iam::************:instance-profile/ec2-profile"
    }
}
# 作成したインスタンスプロファイルをIAM Roleに関連付ける
$ aws iam add-role-to-instance-profile \
  --role-name ec2-role \
  --instance-profile-name ec2-profile

続いてEC2インスタンスに関連付けるセキュリティグループを作成します。GHEからのログフォワーディングにはUDP:514を利用する点に注意してください。

# セキュリティグループの作成
$  aws ec2 create-security-group \
  --group-name ec2-sg \
  --description ec2-sg \
  --vpc-id <_YOUR_VPC_ID_>
{
    "GroupId": "sg-c61710bb"
}
# 利用するポートの開放
$ aws ec2 authorize-security-group-ingress \
  --group-name ec2-sg \
  --protocol tcp \
  --port 22 \
  --cidr "$(curl -s inet-ip.info)/32"
$ aws ec2 authorize-security-group-ingress \
  --group-name ec2-sg \
  --protocol udp \
  --port 514 \
  --cidr <_YOUR_GHE_PRIVATE_IP_>/32

最後にEC2インスタンスを作成します。

# EC2インスタンスの作成
$ aws ec2 run-instances \
  --count 1 \
  --image-id ami-9be6f38c \
  --instance-type t2.nano \
  --key-name <_YOUR_KEY_PAIR_> \
  --security-group-ids sg-c61710bb \
  --subnet-id <_YOUR_SUBNET_ID_> \
  --iam-instance-profile 'Name=ec2-profile'
<snip>

2. GHEのログフォワーディングの設定

続いてGHEのログをEC2にフォワーディングさせます。この時点ではまだKinesis Firehoseの設定をせずに、フォワーディングが正常に動作するかまでを設定/確認してみます。GHEログの取り込みにはfluentdのsyslog inputプラグインを利用します。

まずはGHE側でログフォワーディングを有効化しましょう。マネジメントコンソール( https://<_YOUR_GHE_PUBLIC_IP)/setup )にログイン後以下のように設定して「Save settings」をクリックしてください。

image2

次にEC2側の設定を行います。

# EC2にSSHログイン
$ ssh -i path/to/key ec2-user@<_YOUR_EC2_PUBLIC_IP_>
<snip>
# fluentdのインストール
[ec2-user@ip-172-31-60-61 ~]$ curl -L https://toolbelt.treasuredata.com/sh/install-redhat-td-agent2.sh | sh
<snip>
# テスト用設定ファイルの用意
[ec2-user@ip-172-31-60-61 td-agent]$ cat <<'EOT' > td-agent.conf
> <source>
>   @type syslog
>   port 514
>   bind 0.0.0.0
>   tag ghe
> </source>
> <match ghe.**>
>   type stdout
> </match>
> EOT
# fluentdの実行
[ec2-user@ip-172-31-60-61 ~]$ sudo td-agent --user root --group root -c td-agent.conf
2016-12-23 09:10:08 +0000 [info]: reading config file path="td-agent.conf"
2016-12-23 09:10:08 +0000 [info]: starting fluentd-0.12.29
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-mixin-config-placeholders' version '0.4.0'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-mixin-plaintextformatter' version '0.2.6'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-kafka' version '0.3.1'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-mongo' version '0.7.15'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-rewrite-tag-filter' version '1.5.5'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-s3' version '0.7.1'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-scribe' version '0.10.14'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-td' version '0.10.29'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-td-monitoring' version '0.2.2'
2016-12-23 09:10:08 +0000 [info]: gem 'fluent-plugin-webhdfs' version '0.4.2'
2016-12-23 09:10:08 +0000 [info]: gem 'fluentd' version '0.12.29'
2016-12-23 09:10:08 +0000 [info]: adding match pattern="**" type="stdout"
2016-12-23 09:10:08 +0000 [info]: adding source type="syslog"
2016-12-23 09:10:08 +0000 [info]: using configuration file: <ROOT>
  <source>
    @type syslog
    port 514
    bind 0.0.0.0
    tag ghe
    with_priority true
  </source>
  <match **>
    type stdout
  </match>
</ROOT>
2016-12-23 09:10:08 +0000 [info]: listening syslog socket on 0.0.0.0:514 with udp
2016-12-23 09:09:44 +0000 ghe.syslog.notice: {"host":"******************************","ident":"syslog-ng","pid":"935","message":"Syslog connection broken; fd='9', server='AF_INET(172.31.60.61:514)', time_reopen='60'"}
2016-12-23 09:09:45 +0000 ghe.user.info: {"host":"******************************","ident":"enterprise_manage_access","message":"- - [23/Dec/2016:09:09:45 +0000] \"GET / HTTP/1.0\" 302 44 \"-\" \"-\" \"-\" 0.001 0.001 ."}
2016-12-23 09:09:45 +0000 ghe.user.info: {"host":"******************************","ident":"enterprise_manage_unicorn","message":"[2016-12-23T09:09:45.219000 #7892]  INFO -- : 127.0.0.1 - - [23/Dec/2016 09:09:45] \"GET / HTTP/1.0\" 302 - 0.0005"}
2016-12-23 09:09:47 +0000 ghe.user.info: {"host":"******************************","ident":"enterprise_manage_access","message":"- - [23/Dec/2016:09:09:47 +0000] \"GET / HTTP/1.0\" 302 44 \"-\" \"-\" \"-\" 0.001 0.001 ."}
<snip>

上記のようにGHEから送られたログが標準出力に表示されれば正常にログフォワーディングが動作していると確認できます。

3. Kinesis Firehoseの作成

今度はKinesis Firehoseの設定をしていきます。まずはログ保存用S3バケットを作成しておきます。

$ aws s3 mb s3://<_YOUR_S3_BUCKET_NAME_>
make_bucket: <_YOUR_S3_BUCKET_NAME_>

続いてKinesis Firehoseにひも付けるIAM Roleを作成します。

# IAM Roleの作成
$ aws iam create-role \
  --role-name kinesis-firehose-role \
  --assume-role-policy-document \
    "$(jo Version=2012-10-17 Statement=$(jo -a $(jo Effect=Allow Principal=$(jo Service=firehose.amazonaws.com) Action=sts:AssumeRole)))"
{
    "Role": {
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "sts:AssumeRole",
                    "Effect": "Allow",
                    "Principal": {
                        "Service": "firehose.amazonaws.com"
                    }
                }
            ]
        },
        "RoleId": "*********************",
        "CreateDate": "2016-12-23T09:38:45.789Z",
        "RoleName": "kinesis-firehose-role",
        "Path": "/",
        "Arn": "arn:aws:iam::************:role/kinesis-firehose-role"
    }
}
# ポリシー用JSONファイルの作成
cat <<'EOT' > kinesis-firehose.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": [
        "s3:AbortMultipartUpload",
        "s3:GetBucketLocation",
        "s3:GetObject",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::<_YOUR_S3_BUCKET_NAME_>",
        "arn:aws:s3:::<_YOUR_S3_BUCKET_NAME_>/*"
      ]
    },
    {
      "Sid": "",
      "Effect": "Allow",
      "Action": [
        "logs:PutLogEvents"
      ],
      "Resource": [
        "arn:aws:logs:us-east-1:************:log-group:/aws/kinesisfirehose/ghe-delivery-stream:log-stream:*"
      ]
    }
  ]
}
EOT
# ポリシーの作成
$ aws iam create-policy \
  --policy-name kinesis-firehose-policy \
  --policy-document file://kinesis-firehose.json
{
    "Policy": {
        "PolicyName": "kinesis-firehose-policy",
        "CreateDate": "2016-12-23T09:45:11.647Z",
        "AttachmentCount": 0,
        "IsAttachable": true,
        "PolicyId": "*********************",
        "DefaultVersionId": "v1",
        "Path": "/",
        "Arn": "arn:aws:iam::************:policy/kinesis-firehose-policy",
        "UpdateDate": "2016-12-23T09:45:11.647Z"
    }
}
# 作成したポリシーをIAM Roleにアタッチ
$ aws iam attach-role-policy \
  --role-name kinesis-firehose-role \
  --policy-arn arn:aws:iam::************:policy/kinesis-firehose-policy

次はKinesis FirehoseのデリバリーストリームからCloudWatch Logsにログを出力するためのロググループとログストリームを作成しておきます。

# ロググループの作成
$ aws logs create-log-group \
  --log-group-name /aws/kinesisfirehose/ghe-delivery-stream
# ログストリームの作成
$ aws logs create-log-stream \
  --log-group-name /aws/kinesisfirehose/ghe-delivery-stream \
  --log-stream-name S3Delivery

最後にKinesis Firehoseのデリバリーストリームを作成します。作成には aws firehose create-delivery-stream コマンドを実行するのですが、 --extended-s3-destination-configuration オプションで設定用JSONファイルを渡す必要があります。長いので事前に作成しておきましょう。

# JSONファイルの作成
cat <<'EOT' > s3.json
{
  "RoleARN": "arn:aws:iam::************:role/kinesis-firehose-role",
  "BucketARN": "arn:aws:s3:::<_YOUR_S3_BUCKET_NAME_>",
  "BufferingHints": {
    "SizeInMBs": 5,
    "IntervalInSeconds": 300
  },
  "CompressionFormat": "UNCOMPRESSED",
  "EncryptionConfiguration": {
    "NoEncryptionConfig": "NoEncryption"
  },
  "CloudWatchLoggingOptions": {
    "Enabled": true,
    "LogGroupName": "/aws/kinesisfirehose/ghe-delivery-stream",
    "LogStreamName": "S3Delivery"
  },
  "ProcessingConfiguration": {
    "Enabled": false
  },
  "S3BackupMode": "Disabled"
}
EOT
# デリバリーストリームの作成
$ aws firehose create-delivery-stream \
  --delivery-stream-name ghe-delivery-stream \
  --extended-s3-destination-configuration file://s3.json
{
      "DeliveryStreamARN": "arn:aws:firehose:us-east-1:************:deliverystream/ghe-delivery-stream"
}
# 状態の確認
$ aws firehose describe-delivery-stream \
  --delivery-stream-name ghe-delivery-stream \
  --query 'DeliveryStreamDescription.DeliveryStreamStatus'
"ACTIVE"

fluentdからログをputさせる前に軽く動作確認をしてみます。

# テストデータのput
$ aws firehose put-record \
  --delivery-stream-name ghe-delivery-stream \
  --record '{"Data": "test"}'
{
    "RecordId": "nrzX09ZTfAhxBpuqXJ7wC6NGdWcKS/LUrPa/w7TXo7DeYdEy1ut8vvKUcm+eyErtDILigdPbIvBykfCclxWdhwICc2sCmx4cfPrcBs5pbjs4//kUuxe9YJJqw+1k5bFudKtjLfkIRzGfRHoaApXG04/eG2Rz7OiXHgu6oasDFwrnQoqiG34/Zr951n891ztAvgZ24hWeVXIgOv2exA3qZLO77KkJpiE+"
}
# S3バケットにKinesis Firehoseからログが送られていることを確認
$ aws s3 ls s3://<_YOUR_S3_BUCKET_NAME_> --recursive
2016-12-23 19:08:44          4 2016/12/23/10/ghe-delivery-stream-1-2016-12-23-10-03-42-765fa297-2401-4fd2-8272-f137cb2667b7
$ aws s3 cp s3://knakayama-kinesis-firehose-log/2016/12/23/10/ghe-delivery-stream-1-2016-12-23-10-03-42-765fa297-2401-4fd2-8272-f137cb2667b7 -
test%

上記のように標準出力にテストデータが表示されたら成功です。

4. aws-fluent-plugin-kinesisの設定

いよいよ最後の設定です。fluentdのKinesis Firehoseプラグインであるaws-fluent-plugin-kinesisをインストール/セットアップしていきます。

# EC2にSSHログイン
$ ssh -i path/to/key ec2-user@<_YOUR_EC2_PUBLIC_IP_>
# aws-fluent-plugin-kinesisのインストール
[ec2-user@ip-172-31-60-61 ~]$ sudo td-agent-gem install fluent-plugin-kinesis
<snip>
# 設定ファイルの作成
[ec2-user@ip-172-31-60-61 ~]$ cd /etc/td-agent/
[ec2-user@ip-172-31-60-61 td-agent]$ sudo mv -iv td-agent.conf td-agent.conf.orig
‘td-agent.conf’ -> ‘td-agent.conf.orig’
[ec2-user@ip-172-31-60-61 td-agent]$ cat <<'EOT' > td-agent.conf
> <source>
>   @type syslog
>   port 514
>   bind 0.0.0.0
>   tag ghe
> </source>
> <match ghe.**>
>   @type kinesis_firehose
>   region us-east-1
>   delivery_stream_name ghe-delivery-stream
> </match>
> EOT
# UDP:514でListenさせるためにtd-agent実行ユーザとグループをrootに変更
[ec2-user@ip-172-31-60-61 ~]$ sudo sed -E -i \
  -e 's/(TD_AGENT_USER=)td-agent/\1root/' \
  -e 's/(TD_AGENT_GROUP=)td-agent/\1root/' \
  /etc/init.d/td-agent
# td-agentの実行
[ec2-user@ip-172-31-60-61 ~]$ sudo service td-agent start
td-agent td-agent:                                         [  OK  ]

しばらく待った後、以下のようにS3バケットにログが出力されていれば成功です。

$ aws s3 ls s3://<_YOUR_S3_BUCKET_NAME_> --recursive
2016-12-23 19:08:44          4 2016/12/23/10/ghe-delivery-stream-1-2016-12-23-10-03-42-765fa297-2401-4fd2-8272-f137cb2667b7
2016-12-23 20:52:18      85794 2016/12/23/11/ghe-delivery-stream-1-2016-12-23-11-47-15-a73c1079-aabb-42bb-b5ce-204950f67b2c
2016-12-23 20:57:21     114530 2016/12/23/11/ghe-delivery-stream-1-2016-12-23-11-52-15-71e4a4ce-71f4-4f16-9698-56cab512141d
2016-12-23 21:03:17      84438 2016/12/23/11/ghe-delivery-stream-1-2016-12-23-11-58-15-51fc8e36-8f1c-4021-9229-f25ae6586cb2
2016-12-23 21:08:17      87351 2016/12/23/12/ghe-delivery-stream-1-2016-12-23-12-03-15-6b97c1aa-7c0c-47e1-bb21-040993b416e8
$ aws s3 cp s3://<_YOUR_S3_BUCKET_NAME_>/2016/12/23/11/ghe-delivery-stream-1-2016-12-23-11-47-15-a73c1079-aabb-42bb-b5ce-204950f67b2c -
{"host":"******************************","ident":"syslog-ng","pid":"935","message":"Syslog connection broken; fd='50', server='AF_INET(172.31.60.61:514)', time_reopen='60'"}
{"host":"******************************","ident":"haproxy","pid":"6109","message":"86.180.80.75:49232 [23/Dec/2016:11:44:58.704] ssh_protocol babeld_ssh/localhost 1/0/47116 981 -- 0/0/0/0/0 0/0"}
{"host":"******************************","ident":"enterprise_manage_access","message":"- - [23/Dec/2016:11:45:45 +0000] \"GET / HTTP/1.0\" 302 44 \"-\" \"-\" \"-\" 0.001 0.001 ."}
{"host":"******************************","ident":"enterprise_manage_unicorn","message":"[2016-12-23T11:45:45.669185 #7895]  INFO -- : 127.0.0.1 - - [23/Dec/2016 11:45:45] \"GET / HTTP/1.0\" 302 - 0.0005"}
<snip>

まとめ

いかがだったでしょうか。

Kinesis Firehoseを利用したS3へのログアーカイブ方法についてご紹介しました。GHEのマネジメントコンソールで確認すると分かりますが、ログの転送設定が推奨されています。GHEを利用していく上で有用な情報が含まれているため、しっかりとログ運用方法を検討するようにしましょう。今回は単にS3へログを保存しただけなのでfluentdのS3アウトプットプラグインでももちろん実装可能なのですが、Kinesis Firehoseはアウトプット先としてS3以外にもRedshift/Amazon Elasticsearch Serviceに対応しています。こういったサービスと連携させることでログの分析や可視化も可能となります。更に最近のアップデートでLambdaを利用したデータ変換機能に対応しました。最初はログのアーカイブだけでも良いけどいずれデータソースとして利用したい、そういったことを考慮されている場合にKinesis Firehoseを利用したログ収集について一度検討されてみるとよいのではないでしょうか。

本エントリがみなさんの参考になったら幸いに思います。