長生村本郷Engineers'Blog

千葉県長生村本郷育ちのエンジニアが書いているブログ

puppeteer on Docker

概要

備忘録です。

Puppeteer をローカル環境を汚さず、 Docker 環境上で実行すべくまとめました。

サンプルスクリプトexample.comスクリーンショットを取得する、というシンプルなものです。

github.com

WEB+DB PRESS Vol.109

WEB+DB PRESS Vol.109

  • 作者: 佐藤歩,加藤賢一,原一成,加藤圭佑,大塚健司,磯部有司,村田賢太,末永恭正,久保田祐史,吉川竜太,牧大輔,ytnobody(わいとん),前田雅央,浜田真成,竹馬光太郎,池田拓司,はまちや2,竹原,原田裕介,西立野翔磨,田中孝明
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/02/23
  • メディア: 単行本
  • この商品を含むブログを見る

EC2 Instance Connect API で ssh ログインできるインタラクティブ cli tool "omssh" を作ってみました。

概要

oreno-mssh、またの名を omssh という AWS EC2 Instance Connect API を利用した ssh ログインツールを作成しました。

View post on imgur.com
imgur.com

作ろうと思った経緯

以前 EC2 Instance Connect API の登場により、EC2 Instance ID 指定で ssh ログインできる様になりました。

これにより、ssh ログイン接続するメンバーに秘密鍵を渡す、公開鍵を登録する等の作業が不要となりました。

kenzo0107.hatenablog.com

mssh を使用した場合、以下の様なコマンドで ssh ログインできます。

// Amazon Linux への ssh 
mssh <EC2 Instance ID> --profile <profile>

// Ubuntu
mssh ubuntu@<EC2 Instance ID> --profile <profile>

mssh を利用するには <EC2 Instance ID> 情報が必要で、毎回 Instance ID を調べる手間がありました。

もちろん踏み台サーバであれば、そうそう再起動され Instance ID が変更されることはないのでメモっておけば良いのでしょうが、 数が多くなると、管理が大変です。

その手間を fuzzyfinderインタラクティブに解決しようと思いました。

使用方法

README.md にもありますが、以下ステップでインストールできます。

$ git clone https://github.com/kenzo0107/omssh
$ cd omssh
$ make build && make install

今後

現在、EC2 Instance Connect を利用した運用に切り替えきれないところがあります。

理由は、EC2 インスタンスを Public Subnet に配置していないと EC2 Instance Connect API が利用できない為、踏み台までは EC2 Instance Connect API を利用し ssh ログインできたけど、その先は、秘密鍵が必要になる為です。

f:id:kenzo0107:20190804180237p:plain

おそらく近々 Private Subnet でも EC2 Instance Connect が利用できる様になるのでは?と期待しています。

そうなれば、踏み台にも omssh を置いて、鍵を意識せず、 IAM の権限だけで、 ssh 権限を管理できる様な世界が実現できます。

AWS サポートに願いを伝えておきます♪

改訂2版 みんなのGo言語

改訂2版 みんなのGo言語

EC2 Instance Connect で AWS EC2 への ssh 管理を IAM User or Group で簡単に♪

f:id:kenzo0107:20190628154100p:plain

概要

2019-06-28 に EC2 Instance Conncet が発表されました!

これによって、セキュリティグループと IAM 権限で ssh アクセス許可が可能になります。

例えば、
会社の IP からのみ、特定の IAM User Group に所属している IAM User に ssh アクセス権限を付与、
別のプロジェクトへ異動した、退職した場合は、その IAM User Group から削除で ssh アクセス権限を剥奪できます。

試験環境

macOS 10.14.3 で試しました。

事前準備

$ pip install -U awscli

$ aws s3api get-object --bucket ec2-instance-connect --key cli/ec2instanceconnectcli-latest.tar.gz ec2instanceconnectcli-latest.tar.gz

$ sudo pip install ec2instanceconnectcli-latest.tar.gz

発行した IAM User のパーミッション権限に以下を追加

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EC2InstanceConnect",
            "Action": [
                "ec2:DescribeInstances",
                "ec2-instance-connect:SendSSHPublicKey"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

この辺りは terraform 管理案件ですね。

EC2 Instance Conncect 対応 OS

  • Ubuntu>=16.04
  • AmazonLinux2>=2.0.20190618

ssh ログインする EC2側の設定

Ubuntu>=16.04

ec2-instance-connect をインストールしておく必要があります。

$ sudo apt-get update && sudo apt-get install ec2-instance-connect
$ dpkg -l | grep ec2-instance-connect

ii  ec2-instance-connect           1.1.9-0ubuntu3~18.04.1            all          Configures ssh daemon to accept EC2 Instance Connect ssh keys

AmazonLinux2>=2.0.20190618

ec2-instance-connect は設定済みです。

セキュリティグループ

ssh ログイン先となる EC2 インスタンスのセキュリティグループはアクセス元から ssh (22 port) を開けておく必要があります。

ssh ログインしてみる

local%$ mssh ubuntu@i-0f123456abcdefg --profile <profile> --region ap-northeast-1

一見、誰しもが ubuntu でログインしていて監査が不安になりますが、 CloudTrail はちゃんと誰がログインしたか見ています。

CloudTrail

f:id:kenzo0107:20190628160255p:plain
CloudTrail

以下イベントでログが残っています。

  • SendSSHPublicKey
  • DescribeInstances

SendSSHPublicKey の「イベントの表示」ボタンクリックで JSON が表示されますが、その中で、アクセス元 IP, IAM User Arn、アクセス先 インスタンスIDがわかります。

{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "ABCDEFGHIJK....",
        "arn": "arn:aws:iam::123456789012:user/hogehoge",
        "accountId": "123456789012",
        "accessKeyId": "AKIxxxxxxxxxxxxxxxx",
        "userName": "hogehoge",
        "sessionContext": {
            "attributes": {
                "mfaAuthenticated": "false",
                "creationDate": "2019-06-28T06:18:50Z"
            }
        }
    },
    "eventTime": "2019-06-28T06:18:51Z",
    "eventSource": "ec2-instance-connect.amazonaws.com",
    "eventName": "SendSSHPublicKey",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "xx.xxx.xxx.xxx",
    "userAgent": "aws-ec2-instance-connect-cli/1.0.0 Python/2.7.16 Darwin/18.2.0 Botocore/1.12.179",
    "requestParameters": {
        "instanceId": "i-0f.......",
        "osUser": "ubuntu",
        "SSHKey": {
            "publicKey": "ssh-rsa AAAAB....rHb"
        }
    },
    "responseElements": null,
    "requestID": "01234567-890a-1234-5b6d-......",
    "eventID": "f51...",
    "eventType": "AwsApiCall",
    "recipientAccountId": "123456789012"
}

こちらで EC2 インスタンスのアクセス履歴等はわかります。

まとめ

これまで ssh アカウント管理は手間でしたが、IAM 権限での管理によって非常に楽になりました♪

CloudTrail で監査もバッチリ!

Nginx IP 直アクセス不許可 & LB ヘルスチェック設定

f:id:kenzo0107:20190424103347p:plain

よく設定している Nginx の configure file のアクセス元によっての振り分け方をまとめました。

LB → Nginx → Rails

Nginx 設定

  • conf.d/default.conf
# cannot allow ip direct
server {
  listen       80;
  server_name  _;
  return       444;
}

# healthcheck from LB
server {
  listen 80 default_server;
  listen [::]:80 default_server;
  root /work/app/public;

  location = /healthcheck.html {
    access_log  off;
    proxy_pass http://puma;
  }
}

server {
  listen  80;
  server_name example.com;
  ...

IP 直アクセス禁止

server_name _ とすることで、ip 直アクセスをターゲットにしています。

server {
  listen       80;
  server_name  _;
  return       444;
}

LB からのヘルスチェック

LB からヘルスチェックを向ける先を default_sever 設定することで、この server ディレクティブを参照します。

server {
  listen 80 default_server;
  listen [::]:80 default_server;
  root /work/app/public;

  location = /healthcheck.html {
    access_log  off;
    proxy_pass http://puma;
  }
}

上記 config file は、AWS ALB のヘルスチェックパスを /healthcheck.html とし、その向け先を Rails puma にしています。

Rails 側で以下の様に gem 'ok_computer' に向けるのも良し、独自にレスポンス返すも良しです。

get 'healthcheck.html', to: 'ok_computer/ok_computer#index'

ドメイン指定

example.com でアクセスされた際にこちらの server ディレクティブを参照します。

server {
  listen  80;
  server_name example.com;
  ...

ドメイン指定の悪い例

以前は以下の様に指定し、ip 直アクセス、ヘルスチェック対応していました。

server {
    listen 80;
    server_name example.com;

    if ($host != "example.com") {
        return 444;
    }

    location = /healthcheck.html {
      access_log  off;
      proxy_pass http://puma;
    }
    ...
}

勿論これでも動作します。ですが、やや可読性が悪いです。

マルチドメインでの IP 直アクセス不許可に対応をする際にも、この if 文がどんどん長くなります。

その為、向け先の意図毎に server {} を小まめに分ける運用の方が可読性が高く、実運用していてメンテナンサビリティが高いと感じました。

以上 参考になれば幸いです。

Terraform 運用ベストプラクティス 2019 ~workspace をやめてみた等諸々~

f:id:kenzo0107:20190417103456p:plain

以前 terraform で workspace 毎に tfstate 管理する方法を執筆しましたが、実運用上いくつかの問題がありました。

結論、現在は workspace 運用をやめています。

kenzo0107.hatenablog.com

workspace 運用例

まずは実際の運用例です。

もっとうまいことやってるぞ!という話はあろうかと思いますが、まずはありがちなケースを紹介します。

例) セキュリティグループ作成

以下の要件を実現するセキュリティグループを作成するとします。

要件

  • stg では、社内で Wifi の ip からのみアクセス可
  • prd では、ip 制限なくアクセス可

サンプルコード

  • variables.tf
variable "ips" {
  type = "map"
  default = {
    stg.cidrs    = "12.145.67.89/32,22.145.67.89/32"
    prod.cidrs   = "0.0.0.0/0"
  }
}
  • security_group.tf
resource "aws_security_group" "hoge" {
  name        = "${terraform.workspace}-hoge-sg"
  vpc_id      = "${aws_vpc.vpc_main.id}"
}

resource "aws_security_group_rule" "https" {
  security_group_id = "${aws_security_group.hoge.id}"
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  cidr_blocks       = ["${split(",", lookup(var.ips, "${terraform.workspace}.cidrs"))}"]
}

resource "aws_security_group_rule" "https" {
  security_group_id = "${aws_security_group.hoge.id}"
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
}

実際に terraform を plan/apply する前にまずは terraform workspace を定義する必要があります。

terraform workspace new stg // 既に作成されていたらエラーとなります。
terraform workspace select stg

// terraform workspace = stg とした場合の tfstate をローカルのメモリ上で管理します。
terraform init 

上記のような処理があって、初めて、 variable "ips"stg.cidrs, prd.cidrs が利用できるようになります。

こちらを運用しようとしてみると以下の様な問題にぶつかりました。

実運用との相性が悪い

ステージングのみに反映させたい、という時にどう運用したら良いでしょうか。

ステージング用、本番用に設定して、プルリクエストが通って、サンプルのコードを master にマージしていたらどうでしょう?

本番にもデプロイして良さそうに見えます。

いや、むしろ反映されていなければ、混乱します。

その後に master にマージして、本番に反映させたいコードがあった時に、サンプルコードの部分は反映させたくない!と言っても反映されてしまいます。

かといって、以下の様なコードを複数リソースに入れていくのは、余計なステップ数も増え、脳内でリソースが消費されます。レビューするのも辛いです。

count = "${terraform.workspace == "stg" ? 1: 0}"

では、本番用は設定しなければいいじゃないか!と言って設定しないと、本番用はエラーを出す様になり、その他の反映が何もできなくなります。

これはステージングも本番も同じファイルを参照している為に発生しています。

また、 workspace を利用していると以下の様な問題もありました。

stg, prd 以外に新たに workspace を追加したい場合

以下要望があった場合にどうでしょうか。

  • 負荷試験をする為に本番同様の環境を用意してください」
  • 「外部 API との連携試験をしたいので環境を別途増やして欲しいです!」

例えば、 負荷試験環境を用意しようとすると、 loadtst という workspace を用意するとしたら variables.tf を以下のように修正が必要です。

variable "ips" {
  type = "map"
  default = {
    loadtst.cidrs = "12.145.67.89/32,22.145.67.89/32" // 追加
    stg.cidrs     = "12.145.67.89/32,22.145.67.89/32"
    prod.cidrs    = "0.0.0.0/0"
  }
}

上記例ですと variable "ips" に 1行加えただけで良いですが、実際は あらゆる変数に loadtst.*** = *** というコードを追加していく必要があります。

workspace が増える毎に step 数が増え、ファイルの見通しが悪くなります。

また、以下の様なコードがあると、こちらも脳内リソースを消費し、疲弊します。

lookup(var.ips, "${terraform.workspace}.cidrs")
"${terraform.workspace == "stg" ? hoge: moge}"

workspace 運用をまとめると

workspace の利用はリソースを複数環境で共有する ことで運用する想定の為に、可読性の悪化、実運用との乖離がありました。

  1. 新たに workspace 追加する際に、全ての変数 map に追加しなければならない。
    → コードの見通しが悪くなる。
    → 新規環境の構築難易度が上がる。

  2. ステージングのみに反映という時の実運用が困難
    → ステージングも本番も同じファイルを参照している為、ファイルの中でステージングの場合は?と処理を分ける必要が出てきてしまう。

  3. 今、どの workspace なのかがわかりずらく、 terraform apply する際にかなり躊躇してしまう。
    → 実際 terraform apply 実行前に terraform workspace show で workspace 確認しても、実行中で少し時間が経つと、「あれ?どっちだっけ?」と不安になり、 Terminal を遡って確認することがあったりしました。

ではどうすると良いか?

徹底的に workspace をやめます。

= DRY な設計しよう!

これに尽きます。

実際にどうしたか以下まとめました。

ディレクトリ構成は以下のようにしました。

modules/common ... stg, prd どちらの環境でも共通して同構成で作成するリソースを置きます。

modules/stg,prd ... 個々に異なる構成となるリソースを置きます。*1

.
├── README.md
├──envs/
│   ├── prd
│   │   ├── backend.tf
│   │   ├── main.tf
│   │   ├── provider.tf
│   │   ├── region.tf
│   │   ├── templates
│   │   │   └── user-data.tpl
│   │   └── variable.tf
│   └──stg/
│       ├── backend.tf
│       ├── main.tf
│       ├── provider.tf
│       ├── region.tf
│       ├── templates
│       │   └── user-data.tpl
│       └── variable.tf
│
└──modules
    ├── common
    │   ├── bastion.tf
    │   ├── bucket_logs.tf
    │   ├── bucket_static.tf
    │   ├── certificate.tf
    │   ├── cloudfront.tf
    │   ├── cloudwatch.tf
    │   ├── codebuild.tf
    │   ├── codepipeline.tf
    │   ├── network.tf
    │   ├── output.tf
    │   ├── rds.tf
    │   ├── redis.tf
    │   ├── security_group.tf
    │   └── variable.tf
    ├── prd
    │   ├── admin.tf
    │   ├── admin_autoscaling_policy.tf
    │   ├── api.tf
    │   ├── app.tf
    │   ├── ecr.tf
    │   ├── iam_ecs.tf
    │   ├── output.tf
    │   ├── variable.tf
    │   └── waf.tf
    └── stg
        ├── admin.tf
        ├── api.tf
        ├── app.tf
        ├── ecr.tf
        ├── iam_ecs.tf
        ├── output.tf
        ├── variable.tf
        └── waf.tf

前出のセキュリティグループの作成を例にするとどうなるか

以下の様になります。

  • envs/prd/variables.tf
variable "cidrs" {
  default = [
    "0.0.0.0/0",
  ]
}
  • envs/stg/variables.tf
variable "cidrs" {
  default = [
    "12.145.67.89/32",
    "22.145.67.89/32",
  ]
}
  • envs/common/security_group.tf
resource "aws_security_group" "hoge" {
  name        = "${terraform.workspace}-hoge-sg"
  vpc_id      = "${aws_vpc.vpc_main.id}"
}

resource "aws_security_group_rule" "https" {
  security_group_id = "${aws_security_group.hoge.id}"
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  cidr_blocks       = ["${var.cidrs"))}"]
}

resource "aws_security_group_rule" "https" {
  security_group_id = "${aws_security_group.hoge.id}"
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
}

もし stg だけに反映させたいセキュリティグループであれば、 envs/stg/security_group.tf に作成したいセキュリティグループを記述します。

これで stg だけ反映という実運用をカバーできます。

また、負荷試験環境 ( loadtst ) という環境を用意したい場合は、以下の様にコピーし、変数を修正すれば良いです。

  • envs/prdenvs/loadtst
  • modules/prdmodules/loadtst

多少構成に変更があろうとも、 loadtst 関連のリソースが prd, stg に影響することはない様に作成できます。

terraform コーディングルール

以下のような workspace の切り替えを利用したコードを利用しないことです。

lookup(var.ips, "${terraform.workspace}.cidrs")
"${terraform.workspace == "stg" ? hoge: moge}"

また、以下も NG とします。 stg だけ異なるのであれば、 modules/stg,prd と分けるべきです。

"${var.env == "stg" ? hoge: moge}"

terraform 実行手順

stg, prd 各環境構築は envs/stg, envs/prd ディレクトリに移動し、 以下実行します。

terraform init
terraform get -update
terraform plan
terraform apply

AWS credentials の扱い

stg, prd で同じ AWS Account を利用する場合、プロジェクトの root に direnv 等、 .envrc を置いて、運用するのが良いと思います。

stg, prd で異なる AWS Account を利用する場合、 envs/(stg,prd) 以下に .envrc をそれぞれ配置し、上記 terraform 実行手順 を実行すれば良いです。

プロジェクト毎の terraform バージョンの違いの対応

tfenv で対応します。

macOS%$ brew install tfenv

以前の執筆記事では terraform を one-off container で実行しバージョン差異を吸収する様にしていましたが、コマンドが長くなり、管理も煩雑になるので、tfenv が望ましいです。

こちらも運用してみての実感です。

その他

これはしといた方がオススメ?レベルですが、 provider で バージョン固定外した方が良かったです。

provider aws {
  version = "1.54.0"
  region  = "ap-northeast-1"
}

固定されていて、最新のリソースが利用できない時があります。*2

その時は、バージョン固定でなく、アップデートしていく方向で修正した方が、最新に追従できます。

総評

実運用をしてみて、 workspace はやめておいた方がいいかなと感じたことをまとめました。

勿論、 workspace の良さを知り尽くしてないからこういう意見になっているとも思いますので、一概に否定する意図はありません。

リポジトリの整理がついたら現段階で公開できるところをしていこうと思います!

以上 Terraform 運用されてる方の知見になりましたら幸いです。

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

*1:ECS + RDS + Redis 構成で CodePipeline からデプロイするサンプル terraform です。

*2:Aurora MySQL が作れない!と思ったら、バージョン固定してた為だったことがありました。

ProxySQL で DB の Read/Write Endpoint スイッチング

docker-compose 上で ProxySQL で primary DB と secondary DB への SQL 毎にアクセス先をスイッチングする環境を構築し、試験してみました。

github.com

ProxySQL とは?

ProxySQL はハイパフォーマンスな MySQLSQL プロキシです。

MySQLのフォークである Percona Server や MariaDB だけでなく、Galera Cluster にも対応しています。

今回やってみようと思ったのは

今回注目したのは ProxySQL の SQL プロキシの機能です。

ProxySQL は SQL によって、Read/Write エンドポイントをスイッチングしてくれます。

  • SELECT なら Read エンドポイントへ
  • INSERT, UPDATE, DELETE なら Write エンドポイントへ

という感じです。

使おうと思った経緯

Rails に関わらず、アプリケーション側の問題で、Read/Write のスイッチングができない場合があります。

  • Rails で特定の gem に依存して switch_point が効かないところがあるとか。。
  • 独自フレームワークで DB 側の処理が複雑すぎて手が出せないとか。。

なまじっか、サービスが成長していくと、アプリケーション側で DB のスイッチングができないことが、直接的に DB のボトルネックへ繋がることになりかねません。

この解決の為に ProxySQL を利用しようと思いました。

実際に試してみる。

冒頭のリポジトリgit clone して docker-compose up して頂ければ、起動します。

README の通りに実施してみてください。

  • UPDATE で primary DB へ
  • SELECT で secondary DB へ

アクセスしているのがわかります。

TODO

むしろここが肝心ですね。すでにお調べいただいている方、ご教示くださいましたら幸いです。

以上 参考になれば幸いです。

Rails (gem 'sendgrid-ruby') × SendGrid の Event Notification で API Key ごとの独自メタ情報を設定する

f:id:kenzo0107:20190313234822p:plain

SendGrid の Event Notification の使い所

SendGrid には Event Notification という Webhook を設定することでメールの送信状態をイベント情報として取得することができます。

メールを SendGrid が受信した、送信先に届いた、等の情報です。

sendgrid.kke.co.jp

例えば、未達だったメールの情報を取得したい場合等に、この Webhook を利用し、イベント情報を保存することで調査や集計が可能です。

AWS API Gateway + Lambda で構築したエンドポイントに投げ、S3 に保存し、送信失敗件数を Athena で検索集計する、ということができます。

何か問題でも?

SendGrid は 1アカウントで複数のプロジェクト毎の API Key を発行することができます。

ですが、 イベント情報にはどの API Key を利用してメール送信したか、の記録がありません。

複数の API Key がある場合に、どのプロジェクトのどの環境で送信したのか、調査や集計ができません。

これを解決する手段として、メール送信時にメタ情報を登録する方法があります。

Rails 5.2 で試してみました。

まずはセットアップ

  • Gemfile
gem 'sendgrid-ruby'
  • config/initializers/sendgrid.rb
sendgrid_api_key = Rails.application.credentials.dig(Rails.env.to_sym, :sendgrid_api_key)
ActionMailer::Base.add_delivery_method :sendgrid, Mail::SendGrid, api_key: sendgrid_api_key

api_key は credentials に設定し、そこから取得。*1

  • lib/mail/send_grid.rb
# frozen_string_literal: true

class Mail::SendGrid
  def initialize(settings)
    @settings = settings
  end

  def deliver!(mail)
    sg_mail = SendGrid::Mail.new
    sg_mail.from = SendGrid::Email.new(email: mail.from.first)
    sg_mail.subject = mail.subject
    personalization = SendGrid::Personalization.new
    personalization.add_to(SendGrid::Email.new(email: mail.to.first))
    personalization.subject = mail.subject
    sg_mail.add_personalization(personalization)
    sg_mail.add_content(SendGrid::Content.new(type: 'text/plain', value: mail.body.raw_source))

    // ここでカテゴリー情報として登録
    sg_mail.add_category(SendGrid::Category.new(name: "#{Rails.env}-#{Settings.project_name}"))

    sg = SendGrid::API.new(api_key: @settings[:api_key])
    response = sg.client.mail._('send').post(request_body: sg_mail.to_json)
    Rails.logger.info response.status_code
  end
end

#{Rails.env}-#{Settings.project_name} の部分は適宜変更してください。

メール送信してみる

rails c して

ActionMailer::Base.mail(to: "nakayama.kinnikunn@hogehoge.jp", from: "info@<sender authentication で認証したドメイン>", subject: "メールタイトル", body: "すいません、テスト送信です").deliver_now

すると、Event Notification では以下の様なイベント情報が取得できます。

{"email":"nakayama.kinnikunn@hogehoge.jp","timestamp":1551964210,"ip":"12.345.67.89","sg_event_id":"xxxxxxxxxxxxxxxx","sg_message_id":"xxxxxxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyyyyyyyy","category":["staging-kenkoboys"],"useragent":"Mozilla/5.0 (Windows NT 5.1; rv:11.0) Gecko Firefox/11.0 (via ggpht.com GoogleImageProxy)","event":"open"}

注目すべきは category":["staging-kenkoboys"] です。

add_category したカテゴリ情報が取得でき、これで どの API Key のイベント情報であるかの紐付けができます。

以上 参考になれば幸いです。

現場で使える Ruby on Rails 5速習実践ガイド

現場で使える Ruby on Rails 5速習実践ガイド

*1:RAILS_ENV=development でお試し可なので直に設定でも可。そこは自己責任で