TECH PLAY

Wedding Park/ウエディングパーク

Wedding Park/ウエディングパーク の技術ブログ

206

こんにちは、SREチーム エンジニアの綿引です。 今回は技術ネタではなく、チームビルディングの話です。 先日行われた Scrum Fest Osaka 2020@ONLINE の一つのセッションで、「目標設定にピア・レビューを取り入れている」 という話を聞いて SRE チームでも導入してみました。 弊社では四半期毎に目標設定を行っており、SRE チームの目標設定は、今まで 「自分 + マネージャー」 で行っていたのですが、実際にピア・レビューをやってみた結果、メリットも多いと感じたので共有したいと思います。 ピア・レビューとは ピア・レビューとは、各成果物に対してチームメンバーや同僚がレビューしあうというものです。 効果としては以下があげられます。 作成者では気づかなかった間違いが発見できてより良い成果物ができる ピア・レビューの過程でお互いの経験やノウハウを共有できる 導入 導入に関しては、既存の目標設定のプロセスの 「マネージャー1次レビュー」 の前に入れ込む形にしました。 目標に対するマネージャーとの認識合わせ 目標を作成 チーム内でピア・レビュー マネージャー1次レビュー 目標を修正 マネージャー最終レビュー 目標が決定 ここで気をつけたのが、 会社・組織に期待される目標が、ピア・レビューによってズレないかです。 ピア・レビューを行うと経営目線より、どうしてもメンバー目線のアドバイスが多くなります。 その結果、会社・組織の方針と異なる目標になってしまうことは避けたいので、「アドバイス」 レベルであることを意識しながら行うと良いと思います。 やってみた結果 個人的には以下のようなメリット・デメリットがあると思います。 メリット 人の目標を見ることが刺激となりモチベーションが上がる 「新しい観点」 の目標を立てられ、業務中それを意識することができる 「上手い人」の目標のたて方・書き方などが学べて、特に若いエンジニアにはいい機会になる ここでの 「新しい観点」 の例だと、「技術目線」 の目標ばかりたてていたが、「経営目線」 も目標も入れるようになったなどです。 デメリット ミーティング分、参加メンバーやマネージャーの工数を使ってしまう パーソナルな情報のため恥ずかしい人もいる メンバーの感想 実施後にメンバーに感想を聞いた結果、 「先輩の目標が見れて自分もいいと思ったことを参考にできた」 などの声が聞けたので、初回としては良かったと思っています。 最後に 今回は初めて技術ネタではないチームビルディングのお話をさせて頂きました。 既に実施しているチームもあるかと思いますが、今回 SRE チームで初導入した感触は上々でした。 やってみたい!と思った方は、一度やってみるのもありではないでしょうか。 個人的には今後も技術だけでなく、こういったチームビルディングのお話もしていければと思います。 エンジニア募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 株式会社ウエディングパークwantedly
アバター
こんにちは、SREチーム エンジニアの綿引です。 Route 53 に大量のレコードを登録する時、ホストゾーンに作成したレコードがなければ 「ゾーンファイルのインポート」 が使えますが、1レコードでも作ってしまうと使えないため、大量のレコードを泣く泣く手で登録したという経験がある方もいらっしゃるのではないでしょうか? そこで Route 53 に cli53 とシェルを使って複数のレコードを登録してみました。 既存のホストゾーンに大量のレコードを追加したいが手動は嫌だ! という方のご参考になればと思います。 cli53 とは cli53 は GitHub で公開されている Route 53 へのレコード登録・削除・更新ができるツールです。 他にもホストゾーンの作成・削除・一覧表示や、フェイルオーバー、加重レコード など細かい設定もできるようです。 検証準備 検証の準備として以下を用意します。 実際に使用する際はホストゾーンを対象のものに読み替えていただければと思います。 ホストゾーンはテスト用に 「test.example」 を作成 自端末(Mac)に AWS CLI をインストールし credentials の設定も行う cli53 インストール方法 では cli53 を Mac にインストールしていきます。 Linux、Mac、Windows で使えるようですが、今回は Mac にインストールしたいと思います。 $ brew install cli53 Mac から Route 53 に対して更新を行うので認証情報設定ファイルも用意しておきます。 $ cat ~/.aws/credentials [default] aws_access_key_id = XXXXXXXXXXXXXXXXXXXX aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX レコード登録 追加コマンドの書式は以下です。 $ cli53 rrcreate {ホストゾーン} '{Name} {TTL} {Type} {Value}' では実際に1レコード追加してみましょう。これが追加前のホストゾーンです。 以下のコマンドで追加を行います。 $ cli53 rrcreate test.example 'test1 60 A 127.0.0.0' 想定のレコードが追加されました。 シェルで複数レコードを登録してみる 次はシェルを使って複数のレコードを登録していきます。 今回はサブドメインだけ異なる Value は同じレコードを 5 つほど登録します。 実行ファイルのシェルとは別に、登録するサブドメインに関しては conf ファイルを別で作成し read で読み込む形にします。 まずは実行ファイルである cli53_test.sh を Mac に作成します。 cli53_test.sh #!/bin/sh while read line do cli53 rrcreate test.example "$line 60 A 127.0.0.0" done < name.conf exit 次にサブドメインを一覧にした conf ファイルを作成します。 name.conf test2 test3 test4 test5 あとは、シェルに実行権限を付与し、実行します。 $ ./cli53_test.sh Created record: 'test2.test.example. 60 IN A 127.0.0.0' Created record: 'test3.test.example. 60 IN A 127.0.0.0' Created record: 'test4.test.example. 60 IN A 127.0.0.0' Created record: 'test5.test.example. 60 IN A 127.0.0.0' 完了しました。 実際に対象のレコードが追加されました。 name.conf にサブドメインを追記していけば、大量のレコード追加も可能です。 最後に 今回は cli53 とシェルを使ってレコードを追加してみましたが、レコードをさくっと追加できていいツールでした。 シェルで Value 部分も変えたい場合は、read する conf ファイルを分けたり、cut などで必要なフィールドを取得すれば問題なさそうです。 エンジニア募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 株式会社ウエディングパークwantedly
アバター
こんにちは、インフラエンジニアの綿引です。 「CloudWatch で監視したいけどメモリ・ディスク使用率の監視項目がないじゃない、、」 と悩まれた方もいらっしゃるのではないでしょうか? そこで今回は CloudWatch で EC2 のメモリ・ディスク使用率を監視する、ということをやってみたいと思います。 対象の方は以下のような方でしょうか。   監視運用を導入するまでアラートをウォッチしてしておきたい コストを抑えるためスモールスタートで監視を実施したい   また以下をご参考にいただければ、アラームも Slack に飛ばせるので、一緒に見て頂けると嬉しいです。 参考 : Amazon CloudWatch + Amazon SNS + AWS Chatbot を使ってアラームを Slack に通知してみる 目次 1. CloudWatch エージェント について  1.1. CloudWatch エージェント のできること  1.2. CloudWatch Logs について 2. CloudWatch エージェントのインストール  2.1. IAM ロールを作成する   2.1.1. CloudWatchAgentServerRole の作成   2.1.2. CloudWatchAgentAdminRole の作成  2.2. インストールおよび設定   2.2.1. SSM エージェントのインストール   2.2.2. CloudWatch エージェントのインストール   2.2.3. CloudWatch エージェントの設定ファイル作成  2.3. エージェントの起動 3. CloudWatch メトリクスの確認 4. エージェント起動時のエラーについて 5. 最後に   1. 統合 CloudWatch エージェント について まず 統合 CloudWatch エージェント についてです。 統合 CloudWatch エージェントとは、 サーバにインストールする CloudWatch のエージェント (パッケージ) のことです。 これをインストールすることでカスタムメトリクスが取得できるようになったり、CloudWatch Logs にログを送信できるようになります。   1.1. CloudWatch エージェント のできること CloudWatch エージェント のできることとしては以下になります。   EC2 インスタンスから標準メトリクスより多くのメトリクスを収集できるようになる サーバのログを収集できる オンプレミスなどのAWS によって管理されていないサーバからもメトリクスを収集できる   また収集できるメトリクスの詳細は以下に記載してあります。 参考 : CloudWatch エージェントにより収集されるメトリクス   1.2. CloudWatch Logs について 統合 CloudWatch エージェント をインストールすると、対象サーバから CloudWatch Logs に対してログを送信できるようになります。 CloudWatch Logs についてはまた別の機会に紹介できればと思いますが、システムログや Webサーバ (Apache・Nginx) のログも監視できる強力な監視ツールです。   2. CloudWatch エージェントのインストール では早速、CloudWatch エージェントを導入していきます。 まず CloudWatch エージェントをインストールするには、以下の3種類の方法があります。   コマンドラインを使用する AWS Systems Manager を使用する AWS CloudFormation を使用する   参考 : CloudWatch エージェントのインストール またサポートされる OS などは以下に記載されているため、実施する前にご確認いただけると混乱せずに進められると思います。 参考 : CloudWatch エージェントを使用して Amazon EC2 インスタンスとオンプレミスサーバーからメトリクスとログを収集する 今回は Systems Manager からインストールしてみたいと思います。 使用するサービスと用途は以下です。 EC2 (ホスト名 : cloudwatch-test) – CloudWatch エージェントをインストールするサーバ IAM – EC2 から CloudWatch にメトリクスの書き込みを行うためのロールを作成 – AWS Systems Manager のパラメータストアに書き込みを行うためのロールを作成 AWS Systems Manager – Run Command により CloudWatch エージェントのインストール・起動を実施 – CloudWatch エージェントの設定ファイルをパラメータストアに保存   2.1. IAM ロールを作成する まずは以下のマニュアルに沿って EC2 にアタッチする IAM ロールを作成していきます。 参考 : CloudWatch エージェントで使用する IAM ロールおよびユーザーを作成する 今回は以下の2種類のロールを作成します。   CloudWatchAgentServerRole CloudWatchAgentAdminRole   どちらのロールも EC2 にアタッチする CloudWatch に書き込み許可を与えるためのロールですが、2つ目の CloudWatchAgentAdminRole に関しては、 CloudWatch エージェントの設定ファイルをパラメータストアに書き込む権限を持っています。 使い分けとしては基本は1つ目の CloudWatchAgentServerRole をアタッチしておくが、パラメータストアへの書き込みが必要な時は2つ目の CloudWatchAgentAdminRole を使用するといった形です。 1台管理用のサーバをたてて、それだけ CloudWatchAgentAdminRole を付与するというやり方も良いかと思います。 詳細は以下をご確認頂けると幸いです。 参考 : AWS Systems Manager パラメータストア   2.1.1. CloudWatchAgentServerRole の作成 まずは1つ目の CloudWatchAgentServerRole から作成していきます。 マネジメントコンソールから IAM を選択し、ロールを選択した後、 「ロールの作成」 を押下します。 「信頼されたエンティティの種類を選択」 にて 「AWS サービス」 を選択、「ユースケースの選択」 にて 「EC2」 を選択して 「次のステップ: アクセス権限」 を押下します。 次にポリシーの選択ですが、以下を選択し、 「次のステップ: タグ」 を押下します。   CloudWatchAgentServerPolicy AmazonSSMManagedInstanceCore   次ページのタグは任意です。 ロール名はユーザーガイドに沿って 「CloudWatchAgentServerRole」 にします。 後は設定したポリシーを確認して、最後に 「ロールの作成」 を押下します。 次に CloudWatchAgentAdminRole の作成です。   2.1.2. CloudWatchAgentAdminRole の作成 作成の仕方は1つ目とほぼ変わらないので上記を参考作っていただきたいのですが、ポリシーの 「CloudWatchAgentServerPolicy」 を 「CloudWatchAgentAdminPolicy」 に変更します。 CloudWatchAgentAdminRole のポリシーとしては以下になります。   CloudWatchAgentAdminPolicy AmazonSSMManagedInstanceCore   ロール名は 「CloudWatchAgentAdminRole」 にしました。 最後に対象の EC2 に上記のロールをアタッチします。 今回は前述した通りパラメータストアに書き込みを行うため 「CloudWatchAgentAdminRole」 をアタッチします。 EC2 のコンソール画面を開き、対象のインスタンスのチェックボックスを選択、 「アクション」 → 「インスタンスの設定」 → 「IAM ロールの割り当て/置き換え」 を押下します。 s 先ほど作成した 「CloudWatchAgentAdminRole」 を選択し、最後に 「適用」 を押下します。 これで IAM の作業は完了です。   2.2. インストールおよび設定 次は CloudWatch エージェント の設定に移ります。 前述の通り AWS Systems Manager にて CloudWatch エージェントをインストールするのですが、Systems Manager でインストールするには、対象サーバに SSM エージェントのインストール が必要となります。 SSM エージェント (AWS Systems Manager エージェント) とは、CloudWatch エージェントとは別の、AWS Systems Manager のエージェントのことです。 参考 : SSM エージェント について この SSM エージェントをサーバにインストールすると、EC2・オンプレミスに関わらず AWS Systems Manager の コンソールから、情報を監視したり、リモートでコマンドを実行することが可能になります。 CloudWatch エージェントを使用するには SSM のバージョンが 2.2.93.0 以降 である必要があったり、OS によってはデフォルトでインストールされてたりするので、以下から事前に導入されているかご確認いただいた方が宜しいかと思います。 参考 : Linux の EC2 インスタンスで SSM エージェント をインストールして設定する   2.2.1. SSM エージェントのインストール それでは SSM エージェントをインストールしていきます。手順としては対象サーバにログインし、 “yum” でインストールします。 以下のコマンドを実行します。   $ sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm 次にプロセスが立ち上がっているかを確認します。   $ sudo systemctl status amazon-ssm-agent 最後に自動起動も有効にしておきましょう。   $ sudo systemctl enable amazon-ssm-agent これで SSM エージェントのインストールは完了です。   2.2.2. CloudWatch エージェントのインストール SSM エージェントをインストールしたので、 Systems Manager から操作ができるようになりました。 続いて、CloudWatch エージェント をインストールしていきましょう。 まずは 「AWS Systems Manager」 から 「Run Command」 を選択します。 次に 「コマンドを実行する」 を押下します。 | 次に 「AWS-ConfigureAWSPackage」 を検索し、表示された項目にチェックをつけます。 そのまま下に移動し 「コマンドのパラメータ」 の項目で以下を設定します。 ・Action : Install ・Name : AmazonCloudWatchAgent 次に 「ターゲット」 の項目にて 「インスタンスを手動で選択する」 を選択し、 対象のインスタンスにチェックをつけます。 最後に 「実行」 を押下します。 無事成功しました。 これで CloudWatch エージェントのインストールは完了です。   2.2.3. CloudWatch エージェントの設定ファイル作成 インストールが完了したので、CloudWatch エージェントを起動させていきたい所ですが、起動には、前述の通り CloudWatch エージェントの設定ファイルを作成する必要があります。 設定ファイルの作成の仕方は以下の2種類の方法があります。 1. ウィザードを使用して CloudWatch エージェント設定ファイルを作成する 2. CloudWatch エージェント設定ファイルを手動で作成または編集する 今回は 1. のウィザードを使用して作成していきます。 対象サーバにログインし、以下コマンドを実行します。   $ sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard ここからは対話型になりますので、一つずつ確認していきます。 まずは OS を選択します。今回は Linux なので、 「1」 にします。 尚、何も入力せずに 「Enter」 を押すと 「default choice」 の設定になりますので 「default choice」 と設定が一緒であれば、何も入力せず 「Enter」 を押下で OK です。   ============================================================= = Welcome to the AWS CloudWatch Agent Configuration Manager = ============================================================= On which OS are you planning to use the agent? 1. linux 2. windows default choice: [1]: 1 次は、EC2 か オンプレミスかです。 今回は EC2 なので 「1」 にします。   Trying to fetch the default region based on ec2 metadata... Are you using EC2 or On-Premises hosts? 1. EC2 2. On-Premises default choice: [1]: 1 これは CloudWatch エージェントを何のユーザで起動させるかです。 今回はデフォルトの root にするため、 「1」 にします。   Which user are you planning to run the agent? 1. root 2. cwagent 3. others default choice: [1]: 1 StatsD デーモンをオンにするかなので、 「1」 を選択します。 尚、 StatsD デーモンはデータ収集用のツール で、 カスタムメトリクス用のデータを収集するために使われているようです。   Do you want to turn on StatsD daemon? 1. yes 2. no default choice: [1]: 1 StatsD デーモンのポート指定です。 デフォルトの 「8125」 にします。   Which port do you want StatsD daemon to listen to? default choice: [8125] 8125 StatsD デーモンの収集間隔の指定です。 デフォルトの 「1」 にします。   What is the collect interval for StatsD daemon? 1. 10s 2. 30s 3. 60s default choice: [1]: 1 StatsD デーモンによって収集されるデータの集約間隔です。 これもデフォルトの 「4」 にします。   What is the aggregation interval for metrics collected by StatsD daemon? 1. Do not aggregate 2. 10s 3. 30s 4. 60s default choice: [4]: 4 CollectD を使ってメトリクス収集するかです。 これは yes にするため 「1」 にします。 尚、 CollectD も StatsD デーモンと同様、データ収集用のツール で、 メモリ使用量、稼働時間の統計などを取得しているようです。   Do you want to monitor metrics from CollectD? 1. yes 2. no default choice: [1]: 1 CPU やメモリも監視するか、なので 「1」 にします。   Do you want to monitor any host metrics? e.g. CPU, memory, etc. 1. yes 2. no default choice: [1]: 1 CPU のコアごとに監視するかです。 監視はそこそこしたいですが、追加料金が適用される場合があるようです。 詳細はわかりませんでしたが、コアごとにメトリクスが収集されるため、 その分メトリクス数が多くなるからということでしょうか? 正確にはわかりませんが、今回は “no” の 「2」 を選択します。   Do you want to monitor cpu metrics per core? Additional CloudWatch charges may apply. 1. yes 2. no default choice: [1]: 2 EC2 の イメージID や インスタンスID なども項目に追加したいかです。 デフォルトの 「1」 にします。   Do you want to add ec2 dimensions (ImageId, InstanceId, InstanceType, AutoScalingGroupName) into all of your metrics if the info is available? 1. yes 2. no default choice: [1]: 1 メトリクスを何秒間隔で収集するかです。 デフォルトの 「4」 にします。   Would you like to collect your metrics at high resolution (sub-minute resolution)? This enables sub-minute resolution for all metrics, but you can customize for specific metrics in the output json file. 1. 1s 2. 10s 3. 30s 4. 60s default choice: [4]: 4 これは AWS 側で事前定義された 「メトリクスセット」 を選択します。 今回は “Advanced” にしてみようと思いますので、 「3」 を指定します。 注意点としては各メトリクスセットで収集されるメトリクスが異なるので、 事前に以下をチェックして頂けると宜しいかと思います。 参考 : CloudWatch エージェントの事前定義されたメトリクスセット   Which default metrics config do you want? 1. Basic 2. Standard 3. Advanced 4. None default choice: [1]: 3 今までの設定が出力され、問題ないかと聞かれますので “yes” の 「1」 を指定します。   Current config as follows: { "agent": { "metrics_collection_interval": 60, "run_as_user": "root" }, "metrics": { "append_dimensions": { "AutoScalingGroupName": "${aws:AutoScalingGroupName}", "ImageId": "${aws:ImageId}", "InstanceId": "${aws:InstanceId}", "InstanceType": "${aws:InstanceType}" }, "metrics_collected": { "collectd": { "metrics_aggregation_interval": 60 }, "cpu": { "measurement": [ "cpu_usage_idle", "cpu_usage_iowait", "cpu_usage_user", "cpu_usage_system" ], "metrics_collection_interval": 60, "totalcpu": false }, "disk": { "measurement": [ "used_percent", "inodes_free" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "diskio": { "measurement": [ "io_time", "write_bytes", "read_bytes", "writes", "reads" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "mem": { "measurement": [ "mem_used_percent" ], "metrics_collection_interval": 60 }, "netstat": { "measurement": [ "tcp_established", "tcp_time_wait" ], "metrics_collection_interval": 60 }, "statsd": { "metrics_aggregation_interval": 60, "metrics_collection_interval": 10, "service_address": ":8125" }, "swap": { "measurement": [ "swap_used_percent" ], "metrics_collection_interval": 60 } } } } Are you satisfied with the above config? Note: it can be manually customized after the wizard completes to add additional items. 1. yes 2. no default choice: [1]: 1 次は “CloudWatch Log Agent の設定ファイルはあるか” です。 対象サーバに “CloudWatch Logs エージェント” をインストールしている状態であれば、 設定ファイルをインポートできるようです。 CloudWatch Logs エージェントに関しては以下をご確認いただきたいですが、 廃止予定なので、もしご使用であればタイミングで移行したいですね。 参考 : CloudWatch Logs の使用開始 今回は設定がないため 「2」 を指定します。   Do you have any existing CloudWatch Log Agent (http://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AgentReference.html) configuration file to import for migration? 1. yes 2. no default choice: [2]: 2 ログファイルを監視するかなので、今回は 「2」 を指定します。 CloudWatch Logs にログを送りたい場合はこちらで設定します。   Do you want to monitor any log files? 1. yes 2. no default choice: [1]: 2 また設定内容が出力され、本設定ファイルを SSM パラメータストアに保存するかの問いです。 前述の通り、パラメータストアに保存するので 「1」 を指定します。   Saved config file to /opt/aws/amazon-cloudwatch-agent/bin/config.json successfully. Current config as follows: { "agent": { "metrics_collection_interval": 60, "run_as_user": "root" }, "metrics": { "append_dimensions": { "AutoScalingGroupName": "${aws:AutoScalingGroupName}", "ImageId": "${aws:ImageId}", "InstanceId": "${aws:InstanceId}", "InstanceType": "${aws:InstanceType}" }, "metrics_collected": { "collectd": { "metrics_aggregation_interval": 60 }, "cpu": { "measurement": [ "cpu_usage_idle", "cpu_usage_iowait", "cpu_usage_user", "cpu_usage_system" ], "metrics_collection_interval": 60, "totalcpu": false }, "disk": { "measurement": [ "used_percent", "inodes_free" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "diskio": { "measurement": [ "io_time", "write_bytes", "read_bytes", "writes", "reads" ], "metrics_collection_interval": 60, "resources": [ "*" ] }, "mem": { "measurement": [ "mem_used_percent" ], "metrics_collection_interval": 60 }, "netstat": { "measurement": [ "tcp_established", "tcp_time_wait" ], "metrics_collection_interval": 60 }, "statsd": { "metrics_aggregation_interval": 60, "metrics_collection_interval": 10, "service_address": ":8125" }, "swap": { "measurement": [ "swap_used_percent" ], "metrics_collection_interval": 60 } } } } Please check the above content of the config. The config file is also located at /opt/aws/amazon-cloudwatch-agent/bin/config.json. Edit it manually if needed. Do you want to store the config in the SSM parameter store? 1. yes 2. no default choice: [1]: 1 パラメータストアに保存する際の、”パラメータストアの名前”を指定します。 デフォルトの 「AmazonCloudWatch-linux」 にします。   What parameter store name do you want to use to store your config? (Use 'AmazonCloudWatch-' prefix if you use our managed AWS policy) default choice: [AmazonCloudWatch-linux] AmazonCloudWatch-linux 設定ファイルをパラメータストアのどのリージョンの保存するかです。 「ap-northeast-1」 にします。   Trying to fetch the default region based on ec2 metadata... Which region do you want to store the config in the parameter store? default choice: [ap-northeast-1] ap-northeast-1 最後にどの IAM ユーザ・ロールを使用するかです。 ここで パラメータストアへの書き込み権限 がなければエラーとなります。 既にパラメータストアへの書き込み権限があるロールを EC2 にアタッチ済みのため、 デフォルトの 「1」 にします。   Which AWS credential should be used to send json config to parameter store? 1. XXXXXXXXXXXXXXXX(From SDK) ← マスクしております 2. Other default choice: [1]: 1 Successfully put config to parameter store AmazonCloudWatch-linux. Program exits now. これでパラメータストアに CloudWatch エージェントの設定ファイルを保存できました。 正常に終了すれば AWS Systems Manager のパラメータストアに表示されます。 パラメータストアへの書き込みが完了したので、 EC2 にアタッチしている IAM ロールを セキュリティ強化のため 「CloudWatchAgentServerRole」 に変更しておきましょう。 これで CloudWatch エージェントの設定ファイルの作成が完了しました。   2.3. エージェントの起動 CloudWatch エージェントの設定ファイル作成が完了したので、 エージェントの起動を行なっていきます。 AWS Systems Manager から 「Run Command」 を選択し、 検索窓に 「AmazonCloudWatch-ManageAgent」 と入力後、 表示された項目にチェックをつけます。 そのまま下に移動し 「コマンドのパラメータ」 の項目で、 先ほどパラメータストアに保存したファイル名を入力します。 ・Optional Configuration Location : AmazonCloudWatch-linux 次に 「ターゲット」 の項目にて 「インスタンスを手動で選択する」 を選択し、 対象のインスタンスにチェックをつけます。 最後に 「実行」 を押下します。 これで起動が完了しました。   3. CloudWatch メトリクスの確認 これまでの作業で対象サーバの “メモリ・ディスク使用率” が CloudWatch に送信されているはずなので確認してみましょう。 コンソールから CloudWatch を選択し、 「メトリクス」 を押下します。 すると、これまでなかった 「CWAgent」 という項目が表示されていますので、これを選択します。 「ImageId,InstanceId,InstanceType」 を選択します。 メモリ使用率である 「mem_used_percent」 がありました。 ディスク使用率も「ImageId,InstanceId,InstanceType,device,fstype,path」に存在します。 これで当初の目的であるメモリ・ディスク使用率を監視することができそうです。   4. エージェント起動時のエラーについて 本検証の 「CloudWatch エージェントの開始」 を実施している際に、 エラーを引いたので共有させていただきます。 Systems Manager の 「Run Command」 から 「CloudWatch エージェントの開始」 を行った所、 以下のように失敗しました。 詳細を確認した所、以下のエラーメッセージが出力されておりました。   ======== Error Log ======== 2020/04/28 01:28:37 I! AmazonCloudWatchAgent Version 1.237768.0. 2020/04/28 01:28:37 E! Error parsing /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.toml, open /usr/share/collectd/types.db: no such file or directory 確認した所、パッケージ 「collectd」 がサーバにインストールされていないことが原因でした。 以下 のコマンドで 「collectd」 をインストールしたら、エージェントの起動が上手くいきました。   $ sudo yum install collectd   5. 最後に 今回は 「統合 CloudWatch エージェントを導入してメモリ・ディスク使用率を監視する」 をやってみましたが、なかなか骨が折れました。 ただこれで EC2 のモニタリングに必要な情報はそこそこ揃いましたし、 前回のブログと合わせてアラームも設定できるので、より使いやすくはなったと思います。 今後に関しては、CloudWatch Logs を用いたログ監視をやってみたいのと、 アラーム・ダッシュボードの設定もやっていきたいです。 エンジニア募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 株式会社ウエディングパークwantedly
アバター
こんにちは、インフラエンジニアの綿引です。 今回は Amazon CloudWatch と Amazon SNS 、そして AWS Chatbot を使って、 CloudWatch アラーム を Slack に通知する。ということをやってみたいと思います。 CloudWatch のことを有効活用できていなかった私としては、 個人的にとてもいい勉強になったので、アウトプットさせていただきたいと思います。 用途としては、監視運用を導入するまでアラートをウォッチしてしておきたいといった場合や、 コストを抑えるためスモールスタートで監視を実施したいといった場合に使えるのでないでしょうか。 なお、AWS Chatbot は現時点でベータ版のため、ご認識のほどお願いいたします。 それではまず、CloudWatch の概要から記載していきたいと思います。 CloudWatch とは AWS の各種リソースをモニタリングできるサービス です。 その CloudWatch の中でもいくつかのカテゴリに分かれていますので、 以下に各項目の概要を記載していきます。 • CloudWatch Metrics 各 AWS サービスから取得した性能データ(メトリクス)を過去分からグラフで参照できる セットアップ不要。各サービス作成後、自動的に CloudWatch にデータを送信 (標準メトリクス) メトリクスの例としては EC2 の CPU 使用率 や、ALB の 500 エラー など 標準メトリクスにないデータをモニタリングしたい際には、独自のカスタムメトリクスを作成できる • CloudWatch Alarms CloudWatch Metrics をモニタリングして、アラームを発行できる 連携サービスは Amazon SNS、EC2、AWS Auto Scaling の 3つ アラームをトリガーに、SNS から Lambda を実行したり、EC2 の再起動などが可能 • CloudWatch Logs CloudWatch エージェント経由でログを CloudWatch へ転送 エージェントはサーバにインストールする必要あり CloudWatch に転送したログは、その後 Lambda や Amazon Kinesis への転送も可能 • CloudWatch Logs Insights CloudWatch Logs をより見やすくしてくれる機能 簡単な専用クエリを使って、インタラクティブに検索できる 可視化の機能もあり • CloudWatch Dashboards CloudWatch Metrics のメトリクスを組み合わせてダッシュボートとして見れる • CloudWatch Events AWS リソースの変更をトリガーに、ターゲットへの操作が可能 トリガーの種類も豊富であり、AWS Batch や、CodePipeline 、ECS など色々存在 CloudWatch の料金 料金 に関しては、メトリクスの数や API の数で変わったり、 項目の数も多いため、他のサービスに比べて少し複雑です。 リンク先の公式サイトがわかりやすいので、そちらをご参照いただければと思います。 無料枠もあるので上手く使いたいですね。 使ってみる では実際に試していきたいと思います。 今回使用するサービスは以下です。 1. CloudWatch – CloudWatch アラームを使用、SNS と連携 2. Amazon SNS – CloudWatch アラーム と AWS Chatbot の間に配置 3. AWS Chatbot – SNS の通知を受け、Slack にアラームを転送 4. EC2 (ホスト名 : cloudwatch-test) – アラームを起こすため、テスト用に構築 ① Amazon SNS の設定 まずは CloudWatch と Chatbot に設定する SNS トピックを作成していきます。 メールでも通知を受け取りたいため、サブスクリプションにはメールを設定します。 マネジメントコンソールから Amazon SNS を選択し、「トピックの作成」を選択します。 「名前」 と 「表示名 – オプション」 は cloudwatch-test-sns にしました。 入力後、右下の「トピックの作成」を選択します。 これで SNS トピックの作成ができました。 次はサブスクリプションの設定を行います。 画面右の「サブスクリプションの作成」を選択します。 「プロトコル」に Eメール を選択し、エンドポイントに送信したいアドレスを入力後、 「サブスクリプションの作成」を選択します。 設定したメールアドレス宛てに、 「AWS Notification – Subscription Confirmation」 という件名でメールが来ますので、 「Confirm subscription」 を押下すると、のサブスクリプションのステータスが 確認済み になります。 これで SNS の設定が完了しました。 次に、CloudWatch アラーム の設定を行っていきます。 ② CloudWatch アラームの設定 CloudWatch アラームの設定では、 テスト用に作成した EC2 の CPU 使用率 (CPUUtilization) に対しアラームを設定します。 マネジメントコンソールから CloudWatch を選択し、 左側の「アラーム」を選択後、「アラームの作成」を押下します。 次画面にて「メトリクスの選択」を押下します。 このメトリクスの設定画面で、EC2 の CPUUtilization を選択して行きます。 検索窓に 【インスタンスID】 と 【”CPU” という文字列】を入れ、対象のメトリクスを検索します。 対象インスタンスの CPUUtilization の項目が表示されたらボックスにチェックを入れ、 右下の「メトリクスの選択」を押下します。 次はアラームの条件を指定します。 今回は 【5分間の間に CPU 使用率が 10% を超えたら通知】 されるよう、以下で設定しています。 設定項目 設定値 統計 最大 期間 5分 しきい値の種類 静的 CPUUtilization が次の時… より大きい … よりも 10 設定が終わったら、「次へ」を押下します 次は通知先の設定です。 ここで先ほど作成した SNS トピック を指定します。 設定は以下です。 設定項目 設定値 アラーム状態トリガー アラーム状態 SNS トピックの選択 既存の SNS トピックを選択 通知の送信先 ※ 先ほど作成した SNS トピック (cloudwatch-test-sns) 最後にアラーム名をつけ、「次へ」を選択後、「アラームの作成」を押下します。 これで CloudWatch アラームの設定ができました。 この時点でアラームが指定のメールアドレスに来るようになっています。 そして次は Slack に通知が来るよう、 AWS Chatbot の設定を行っていきます。 ③ AWS Chatbot の設定 まずはマネジメントコンソールから AWS Chatbot を選択し、 チャットクライアントの設定を行います。 右側にある 「チャットクライアント」 のプルダウンから 「Slack」 を選び、 「クラアントの設定」を押下します。 「クラアントの設定」を押下すると、slack ワークスペースのサインイン画面に遷移するので、 アラームの通知を受けたいワークスペースにサインインします。 メールアドレスとパスワードを入力すると、 AWS Chatbot と slack の連携 を許可する画面になるので「許可する」を選択します。 これでクライアントの設定は完了したので、次はチャネルの作成を行います。 Chatbot の画面から、「新しいチャネルを設定」を選択します。 設定名は任意です。 ここでは cloudwatch_test としました。 次に slack チャネル の設定です。 チャネルタイプは「パブリック」、パブリックチャネルは「random」にしました。 パブリックチャネル で選択したチャネルに対し、アラーム通知が来る形になってますので、 アラーム通知用のチャネルを作っておくと便利かと思います。 次のアクセス許可では、Chatbot が CloudWatch と連携できるようにロールを設定します。 必要なポリシーは以下のようなので、事前にロールを作成しておいても良いですが、 今回は画面から新規作成できるので、そちらで作成していきます。 ・ cloudwatch:Describe* ・ cloudwatch:Get* ・ cloudwatch:List* IAM ロールには、「テンプレートを使用して IAM ロールを作成する」を選択、 ロール名は任意で設定します。 ポリシーテンプレートには 「通知のアクセス許可」を選択します。 通知の項目では、SNS トピックが存在するリージョンを指定し、 最初に作成した SNS トピックを選択しましょう。 これで AWS Chatbot の設定が完了しました。 では CloudWatch アラームが Slack に通知されるかテストしてみましょう。 ④ CloudWatch アラームのテスト 早速、対象サーバの CPU 使用率をあげます。 今回はコマンドを打鍵し、あげております。 その後、CloudWatch メトリクスにて CPUUtilization が上昇しているか確認します。 CloudWatch メトリクス側で CPU 使用率上昇の確認ができました。 またCloudWatch アラームの履歴でも action の履歴が表示されました。 その後すぐにメールと、Slack の通知が届きました。 以下は slack のキャプチャです。 画像もついていてわかりやすいですね。 これで検証完了です。 最後に 今回は CloudWatch と SNS 、AWS Chacbot を使い、 CloudWatch アラームの通知を Slack で受けとるという検証をしましたが、 使いやすいし、色々出来ることが増えるなというのが個人的な印象です。 設定も簡易ですし、特に CloudWatch と SNS は Lambda や他の AWS サービスと連携しているので、 アラームを通知するだけでなく、再起動や、他の細かな対応まで出来るのは嬉しいなと思いました。 今後に関しては、CloudWatch を使って監視がもっと精度高くできるようにしていきたいですが、 今回使った標準メトリクスだけでは、メモリ使用率などがとれないので、 次回は CloudWatch エージェントのお話でもしたいです。 エンジニア募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 株式会社ウエディングパークwantedly
アバター
こんにちは、インフラエンジニアの綿引です。 今回は AWS Backup を使って EC2 と RDS のバックアップを取得してみました。 なかなか面白かったので、AWS Backup の概要も含めてアプトプットさせていただきます。 AWS Backup とは AWS の各サービスのバックアップを一元的に管理できるサービス です。 今までのバックアップは各サービス毎に取得するしかなかったので、 一元管理できるのは嬉しいですね。 AWS Backup で現在までにサポートされているサービスとリソースは以下となっています。 サービス リソース Amazon EFS Amazon EFS ファイルシステム Amazon DynamoDB DynamoDB テーブル Amazon EBS Amazon EBS ボリューム Amazon RDS Amazon RDS データベース AWS Storage Gateway AWS Storage Gateway ボリューム RDS に関して、基本的に全てのエンジンがサポートされておりますが、 Amazon Aurora だけは対象外ですのでご注意ください。 AWS Backup の料金 料金 に関しては、 バックアップにて取得されたストレージ量に対して月額費用 がかかります。 また復元した場合にも別料金が発生します。 料金 ※ アジアパシフィック (東京) リージョン リソースタイプ 料金 Amazon EFS File System バックアップ 0.06 USD/月 (GB あたり) Amazon EBS ボリュームスナップショット 0.05 USD/月 (GB あたり) Amazon RDS データベーススナップショット 0.095 USD/月 (GB あたり) Amazon DynamoDB テーブルバックアップ 0.114 USD/月 (GB あたり) AWS Storage Gateway ボリュームバックアップ 0.05 USD/月 (GB あたり) また EFS のみ、上記のウォームストレージへのバックアップとは別に [コールドストレージへの移行] が可能で、 有効にした場合に別で 0.012 USD/月(GB あたり) の料金が発生します。 使用する場合、コールドストレージに移行されたバックアップの保存期間は最低で 90 日間のため 90 日が経過する前にバックアップを削除しても、残りのストレージ料金が日割りで請求される。 とのことなので、予想外のコストがかからないように注意が必要です。 使ってみる では実際に試してみたいと思います。 今回バックアップを取得するサービスは以下です。 ・EC2 – aws-backup-test-ec2 ・RDS – aws-backup-test-rds まずは [バックアッププランを管理] を選択します。 次に [バックアッププランを作成] を選択します。 起動オプション では [新しいプランを立てる] を選択し、 [バックアッププラン名] を入力します。 ここでは aws-backup-test としました。 続いてはバックアップルールの作成です。 ルール名 は今回 aws-backup-test-rule にしました。 スケジュール の 頻度 に関しては 12時間ごと や 毎日 などから選択できますが、 今回はデフォルトの [毎日] で進めていきます。 なお、個人的には カスタム cron 式 が嬉しかったです。 バックアップウィンドウ については時間帯をこちらで決めたいため、 [バックアップウィンドウをカスタマイズ] を選択しました。 バックアップウィンドウの開始時間 は AM 2:00 で 1時間以内 にしました。 UTC なので日本時間だと AM 11:00 から 1時間以内にバックアップが開始される想定です。 続いて、 ライフサイクル ですが、これはコールドストレージの設定です。 いつコールドストレージに移行されるか、いつ有効期限が切れるかを定義できますが、 上記でも記載した通り、現在は EFS のみ有効のため設定せずに進みます。 バックアップボールト はバックアップを整理するためのものです。 バックアップボールトの管理画面に行けば、 今まで取得したバックアップの一覧がわかりやすく見れたり、 それに対しての復元などが簡単に行えます。 設定に関して、今回はデフォルトの [Default] を使用しますが、 バックアップボールトを新規作成することで、 暗号化キーやアクセスポリシーを制御を自由に設定できます。 なお、 リージョンにコピー はデフォルトです。 バックアッププランに追加されたタグ も設定せず進み、 最後に [プランの作成] を押下します。 これで バックアッププラン の作成は完了です。 次に何をバックアップするかの バックアップリソースの追加 を行います。 バックアッププランの画面から、 [リソースの割り当て] を選択します。 リソース割り当て名 は aws-backup-test として、 IAM ロールは [デフォルトのロール] を選択しました。 リソースの割り当て単位 は、 リソースID か タグ を使ってでできるのですが、 今回は リソースID にしています。 EC2 はインスタンスID で、RDS はデータベース名で表示されます。 最後に [リソースを割り当てる] を押下します。 これでバックアッププランが完成しました。 後は バックアップウィンドウ で指定した時間になるとジョブが動き出します。 今回は AM 11:00 から 1時間以内 に指定しましたが、実際に動き出したのは AM 11:30 ぐらいでした。 完了するとステータスが更新されます。 最後に EC2 と RDS のコンソールからバックアップを確認してみます。 [EC2] [RDS] ちゃんと取得できているようです。 RDS のバックアップが失敗する 今回、RDS のバックアップ時に一度だけエラーに出くわしたので共有させていただきます。 AWS Backup ジョブが失敗し、以下のエラーが出力されました。 Can’t start a backup now. RDS DB Instance is close to enter RDS automated backup window. の内容通り、 RDS の自動バックアップを有効にしていたため AWS Backup の時間と被ってしまったようです。 今後気をつけたいと思います。 最後に Amazon Aurora がサポートされていないのは残念でしたが、 バックアップの一元管理という点では使いやすかったかなと思います。 機会があれば運用に入れてみようかと思いました。 エンジニア募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 株式会社ウエディングパークwantedly
アバター
はじめまして。セキュリティエンジニアの奥野(@okuken3)です。 弊社では2018年12月にプロダクトセキュリティチームを立ち上げ、脆弱性診断の一部内製化を中心とした各種セキュリティ施策に取り組んでいます。 本ブログ上でも、プロダクトセキュリティチームの取り組みについて定期的に発信していきますので、お楽しみいただければと思います! なお、弊社におけるプロダクトセキュリティチームとは、「ウエディングパーク」をはじめとする弊社が開発運営しているWebサービス群のセキュリティを推進するチームになります。 ですので、これからセキュリティに本腰を入れようと考えているアプリケーションエンジニアの方や、セキュリティベンダーでWeb脆弱性診断担当をされていてユーザー企業の具体的な取り組みを知りたい/アドバイスしてあげたい方などに、参考にしていただければ嬉しく思います。 さて、定期発信の初回テーマですが、ずばり「プロダクトセキュリティチームの立ち上げ」です。 チーム立ち上げの経緯から始め、どのようにロードマップを描いて走り出したのかについて、当時を振り返りつつご紹介します。 経緯 弊社では、会社規模の拡大に合わせてディフェンス面の強化が叫ばれていました。 プロダクトセキュリティについても、技術責任者が兼務したり親会社に頼ったりする状況から脱却すべく、セキュリティに注力できる人材の確保をはじめていました。 弊社が求めたセキュリティエンジニアは、単に脆弱性診断ができるセキュリティ専門家のイメージではなく、プロダクトセキュリティチームの立ち上げを任せられ、かつカルチャーマッチするエンジニアでした。 そんな中、縁あって2018年12月に私、奥野が入社し、それと同時にプロダクトセキュリティチームを立ち上げました。 しかし、奥野はセキュリティエンジニアとしては完全なる卵だったのです。 常識を知る 情報系の修士卒、社会に出てから10年ほどITエンジニアをしてきましたが、ことセキュリティに関してはずぶの素人。 そんな私が最初に行ったのは、セキュリティの常識を知ることでした。 社内では、「プロダクトセキュリティチームはゼロイチなので大変でしょう?」とよく言われました。 もちろん社内では初の取り組みですが、世間を見渡せばいくらでも事例がありますし、公的機関等が発行している資料も豊富です。 それらを吸収し、後ろ盾としつつ、効果的な施策を実施していこうと考えました。 主要な資料取得元 IPA 独立行政法人 情報処理推進機構:情報セキュリティ NPO日本ネットワークセキュリティ協会 OWASP Japan JPCERT コーディネーションセンター それと同時に、セキュリティ関連書籍を読み漁りつつ、セキュリティと名の付く社外勉強会に片っ端から参加することで、セキュリティエンジニアとしてのベース作りや潮流の把握を進めました。 なお、セキュリティ系勉強会は大小様々ありますが、初心者向けの小規模勉強会では講師や他の参加者との距離が近く、勉強会終わりで飲みに行くことも多いため密度の濃いコミュニケーションがとり易く、個人的な相談もし易いです。反対に、玄人も対象とした中規模以上の勉強会では、界隈の著名人によるレクチャーや最先端の話題を聞くことができ、目指すべき方向性を確認するのに役立ちます。 ご参考まで、奥野が2019年1月~9月に参加した勉強会のうち、主要なものを記載します。(日付順) WASNight 2019 Kick-Off = OWASP x WASForum Night 第24回 ゼロから始めるセキュリティ入門 勉強会 Burp Suite Japan LT Carnival (ISC)² Night Tokyo 第22回 セキュリティ共有勉強会 enjoy!インハウス #03|エンジニア主体のチーム立ち上げ(技術責任者/QA/セキュリティ)【登壇】 Vuls祭り#5【登壇】 WASNight 2019 Summer = OWASP x WASForum Night【登壇】 Security JAWS 第14回 勉強会 第53回 ゆるいハッキング大会 in TOKYO ロードマップの作成 入社時に技術責任者から受けたリクエストは、脆弱性診断の一部内製化と、脆弱性情報の収集でした。 それらを満たすのはもちろんですが、プロダクトセキュリティチームを立ち上げて活動していく上で、中長期的なロードマップが欲しくなりました。 ロードマップの作成に当っては、ISMS(Information Security Management System)の考え方や、下記の資料を参考にしました。 セキュリティというと「防御」「検知」のイメージが強いですが、サイバーセキュリティフレームワークでいうところの「特定」「対応」の部分の重要さを改めて認識し、優先度を勘案した上でロードマップに組み込みました。 主要な参考資料 重要インフラのサイバーセキュリティを改善するためのフレームワーク(セキュリティ関連NIST文書:IPA 独立行政法人 情報処理推進機構) CISO ハンドブック(CISO支援WG) | NPO日本ネットワークセキュリティ協会 サイバーセキュリティ経営ガイドライン(METI/経済産業省) 初版のロードマップを作成した後も、実際に取り組んでみて得た知見や、各種セキュリティ系セミナーの内容、勉強会で知り合ったセキュリティ専門家の方々からの影響を受けつつ、軌道修正しながら進めています。 そして、プロダクトセキュリティチームが走り出した ロードマップの役員決裁も無事に終え、プロダクトセキュリティチームが走り出しました! セキュリティ初心者からのスタートで、得も言われぬプレッシャーとの闘いではありましたが、その分フレッシュな気持ちで全身全霊をかけて取り組むことができ、個人的にはとても充実した日々を送ることができました。 さて、そんなプロダクトセキュリティチームが立ち上げから約10ヶ月で行ってきたことですが、主要トピックスベースで下記になります。 脆弱性診断の一部内製化 脆弱性情報収集基盤の構築、情報連携体制の整備 新卒向けプロダクトセキュリティ研修の実施 セキュアコーディングガイドの作成 リスクアセスメント 次回以降の投稿では、各々のトピックについて掘り下げてご紹介したいと考えています。 もちろん、これらの運用を継続しつつ新たな施策も打ち続けますので、そちらの内容も追々ご紹介していれけばと思います。 次回予告 次回のプロダクトセキュリティチームからの投稿は、「脆弱性診断の一部内製化(仮)」を予定しています!乞うご期待!
アバター
こんにちは!DBAを目指しているエンジニアの高嶋(@__1016t)です。 当社でダッシュボードツールである「Redash」を導入するときに課題となったこと、 それをどのように解決したのかを書きたいと思います。 Redash導入の背景と目的 ウエディングパークでは施策を考えたり、効果検証をするときのデータを 都度SQLを書いて抽出しています。 データの抽出はエンジニアが行うことになっているので、 ディレクターに必要な項目、抽出する条件をまとめて依頼をしてもらい、 担当エンジニアがヒアリングしながらSQLを書いています。 この運用でずっとやってきたのですが、施策や人が増えていく中で 以下のような課題が出てきました。 ・たくさんの依頼によるエンジニアの工数圧迫 ・工数を圧迫する懸念から、ディレクターが依頼をする心理的ハードルが高い ・ディレクターがほしいとおもったタイミングでデータを見ることができない これらの課題を解決するため、Redashの導入が決まりました。 導入にあたってSuperset と Redashを比較した記事もあるので、ぜひご覧ください! Redash と Superset を比較検証してみた 導入の課題 データベースには機密情報も含まれているため、 閲覧・編集権限を適切に割り当てる必要がありました。 ですが、Redashは画面から設定できる権限が少ないことが課題になり、 どうすれば適切に権限を付与できるのかとても悩みました。 解決方法 まず、エンジニア、ディレクターの役割をそれぞれ整理し、 エンジニアがSQLの作成・編集、ディレクターが抽出したデータを閲覧するため、 それぞれ以下のような権限をつけて管理しようと考えました。 エンジニア クエリ、ダッシュボードを新規作成・編集することができる ディレクター ダッシュボードで抽出したデータをみることができる クエリを作成・編集できないようにする そこで、画面から設定できる「data source permission」と コマンドで設定する「action permission」をつかって上記の権限を実現させました。 設定手順 1. エンジニア、ディレクター の グループ を作成 2. 開発環境、本番環境の データソース を登録 3. data source permission を設定 設定からグループ・データソースごとに権限を設定することができます。 Full Access : クエリ実行・データ閲覧可能 View Only  : データ閲覧のみ可能 抽出したデータを見るだけのディレクターははじめ View Only にしたところ、 動的なSQL(日付の条件などを自由入力)を実行できなくなってしまったため、 エンジニア、ディレクターどちらにも Full Access の権限を与えました。 4. action permission を設定 コマンドでグループごとに細かい権限を設定することができます。 create_dashboard:ダッシュボード作成 create_query:SQL登録 edit_dashboard:ダッシュボード編集 edit_query:SQL編集 execute_query:SQL実行 list_alerts:アラート一覧閲覧 list_dashboards:ダッシュボード一覧閲覧 list_data_sources:接続DB一覧閲覧 list_users:ユーザー一覧閲覧 schedule_query:定期実行設定 view_query:SQL一覧閲覧 view_source:SQL文閲覧 たたくコマンドは以下の記事を参考にさせていただき、設定しました! Re:dashの権限設定について ▼ エンジニア create_dashboard, create_query, edit_dashboard, edit_query, view_query, view_source, execute_query, list_users, schedule_query, list_dashboards, list_alerts, list_data_sources ▼ ディレクター view_query, list_dashboards, execute_query ※ 動的なSQL(日付の条件などを自由入力)を実行できるようにするため、ディレクターにも   execute_query をつけていますが、 edit_query がないためSQLの編集はできません 導入してよかったこと 定期的に実行したり、条件の日付を変えるだけの依頼を都度対応する必要がないので、 エンジニアの工数の削減につながりました。 それ以外にも、 ・ディレクターが一度抽出したデータを自由に見れるようになった ・同じような依頼があったときに、以前の抽出内容を見つけやすい ・クエリを動的にしたり、Redashの機能を積極的につかってくれるメンバーがいる などなど嬉しいことがたくさんありました! 現在は、クエリの実行結果がそのままダッシュボードにのっている状態なので、 今後はグラフ化などRedashならではの+αな価値を出していきたいと思っています。 おわりに Redashを導入するときに、今までの運用での役割をどう実現したのかについて書かせていただきました。適切な権限設定などまだ模索中なところもありますが、たくさんのメリットがあり導入してよかったと思っています。 これから導入を検討している方への第一歩になれたらとてもうれしいです。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにお越しください! 株式会社ウエディングパークwantedly
アバター
はじめに エンジニアの久保です。 最近プライベートで海外に行きました。 街中の無料wifiを使うとき、情報検索程度ならよいのですが個人情報を入力するときはセキュリティ面で少し心配です。 また、海外に行った場合は、国内の回線からインターネットに接続したくなることもあります。 そんなときのために、個人的に利用できるVPN(Virtual Private Network)を用意しました。 今回は AWS CLI を使って、Amazon Lightsail 上にVPNサーバをサクッと構築してみます。 利用したもの Amazon Lightsail AWSが提供するVPS(Virtual Private Server)サービスです。 同社の Amazon EC2 に対して次の特徴があります。 ストレージやネットワーク(ロードバランサ、DNSなど)の設定がパッケージされている 月額固定料金 LightsailはEC2に対して、必要な設定が最初からパッケージとして含まれているため、初心者にとっては使いやすいサービスです。 他方で、高度な設定はできないため柔軟な設定を行いたい場合はEC2を使った方がよいでしょう。 個人的な用途などでカジュアルにサーバ構築をしたい場合は、Lightsail で十分といえるでしょう。 https://aws.amazon.com/jp/lightsail IPsec VPN Server Auto Setup Scripts ワンライナーで IPsec VPN server を構築できる IPsec VPN Server Auto Setup Scripts を利用しました。 https://github.com/hwdsl2/setup-ipsec-vpn/ 手順 インスタンスの作成とVPSサーバの構築 Lightsail インスタンスの作成とVPNサーバのセットアップを行います。 $ aws lightsail create-instances \ --instance-names kubovpn \ --blueprint-id ubuntu_18_04 \ --bundle-id nano_2_0 \ --region ap-northeast-1 \ --availability-zone ap-northeast-1a \ --user-data "wget https://git.io/vpnsetup -O vpnsetup.sh && VPN_IPSEC_PSK='kubosecret' VPN_USER='kubo' VPN_PASSWORD='kubopass' sh vpnsetup.sh" ここで、 --instance-names は、インスタンス名です。 --user-data は、インスタンス作成時に実行するスクリプトを指定できます。ここにVPNサーバをセットアップするためのスクリプトを入力しました。 VPN_IPSEC_PSK , VPN_USER , VPN_PASSWORD はそれぞれVPNサーバの事前共有鍵(pre-shared key)、ユーザ名、パスワードです。 ご自身の環境に合わせて変更してください。 インスタンスイメージには、Ubuntu 18.04 を利用しました。ほかにどんなイメージを利用できるかは次のコマンドで確認できます。 $ aws lightsail get-blueprints --region ap-northeast-1 インスタンスプランは、最低限の Nano を選択しています。ほかにどのようなタイプを選択できるかは次のコマンドで確認できます。 $ aws lightsail get-bundles --region ap-northeast-1 では、インスタンスの作成が終わったら、インスタンスが起動しているか確認してみましょう。 $ aws lightsail get-instance-state \ --instance-name kubovpn \ --region ap-northeast-1 { "state": { "code": 16, "name": "running" } } これでインスタンスのセットアップとVPNサーバのセットアップが終わりました(簡単ですね)。 次に、外部のネットワークからVPNサーバに接続できるようにポートの開放を行います。 ポートの開放 現在解放されているポートを確認してみましょう。 $ aws lightsail get-instance \ --instance-name kubovpn \ --region ap-northeast-1 \ --query 'instance.networking.ports' [ { "protocol": "tcp", "accessType": "public", "commonName": "", "accessFrom": "Anywhere (0.0.0.0/0)", "fromPort": 80, "accessDirection": "inbound", "toPort": 80 }, { "protocol": "tcp", "accessType": "public", "commonName": "", "accessFrom": "Anywhere (0.0.0.0/0)", "fromPort": 22, "accessDirection": "inbound", "toPort": 22 } ] 既にTCP80(HTTP)とTCP22(SSH)が解放されています。 では、IPsec VPN Server で利用するポートのUDP4500とUDP500を解放します。 また今回はSSH接続やHTTP接続をする必要がないので、TCP22とTCP80を閉じることにしました。 # ポートの開放(UPD500) $ aws lightsail open-instance-public-ports \ --instance-name kubovpn \ --region ap-northeast-1 \ --port-info fromPort=500,toPort=500,protocol=UDP # ポートの開放(UDP4500) aws lightsail open-instance-public-ports \ --instance-name kubovpn \ --region ap-northeast-1 \ --port-info fromPort=4500,toPort=4500,protocol=UDP # ポートの閉鎖(TCP22) aws lightsail close-instance-public-ports \ --instance-name kubovpn \ --region ap-northeast-1 \ --port-info fromPort=22,toPort=22,protocol=TCP # ポートの閉鎖(TCP80) aws lightsail close-instance-public-ports \ --instance-name kubovpn \ --region ap-northeast-1 \ --port-info fromPort=80,toPort=80,protocol=TCP 終わったら反映されているか確認しておきます。 $ aws lightsail get-instance \ --instance-name kubovpn \ --region ap-northeast-1 \ --query 'instance.networking.ports' [ { "protocol": "udp", "accessType": "public", "commonName": "", "accessFrom": "Anywhere (0.0.0.0/0)", "fromPort": 4500, "accessDirection": "inbound", "toPort": 4500 }, { "protocol": "udp", "accessType": "public", "commonName": "", "accessFrom": "Anywhere (0.0.0.0/0)", "fromPort": 500, "accessDirection": "inbound", "toPort": 500 } ] 以上でVPNサーバに接続できるようになりました(ここまでの所要時間は5分くらいでした)。 IPアドレスの確認 VPNサーバに接続するために、インスタンスのIPアドレスを確認しておきます。 $ aws lightsail get-instance \ --instance-name kubovpn \ --region ap-northeast-1 \ --query 'instance.publicIpAddress' "13.114.XXX.XXX" スマートフォンへの設定 VPNサーバが完成しました。手元のスマートフォンからVPNに接続出来るように設定してみます。 iOS12の場合、「設定」→「一般」→「VPN」→「VPN構成を追加…」と進みます。 タイプは「IPsec」を選択し、その他の項目は先ほど設定した内容を入力します。 入力が完了したら、「設定」に戻り、VPNをONにしましょう。 以上で完了です。 インスタンスの削除 インスタンスが不要になったら削除しましょう。 $ aws lightsail delete-instance \ --instance-name kubovpn \ --region ap-northeast-1 おわりに 今回は、AWS CLI と LightSail を使って、VPNサーバを構築してみました。 いままでは、VPNルータやRaspberry Pi、他社のVPSサービスを使ってVPNサーバを構築したことがありました。 これらの方法では構築手順を忘れてしまったり設定が煩わしく思っていましたが、今回の方法なら手軽に構築できて良さそうです。 また、Lightsail はユーザフレンドリーなWebコンソールも用意されています。 今回はVPNサーバの構築でしたが、Webサービスの構築なども容易に行えそうです(こちらは今度試してみます)。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにお越しください! 株式会社ウエディングパークwantedly 参考文献 https://docs.aws.amazon.com/cli/latest/reference/lightsail/ https://dev.classmethod.jp/cloud/amazon-lightsail-iphone-ipsec-vpn/
アバター
こんにちは。ウエディングパーク新卒2年目エンジニアの永井( @__south__373 )です。 今年のエンジニア新卒研修を担当させていただいたので、ウエディングパークの新卒研修ではどんなことをしているのか、どうやって進めたのかについて書きたいと思います。 エンジニア研修の概要 ウエディングパークでは毎年4月から6月まで3ヶ月間のエンジニア研修を行い、7月から本配属となります。 3ヶ月間でウエディングパークの開発に必要な基礎知識を学びます。 ディレクターは、エンジニアの仕事内容を知ることにより 開発ディレクション時にスムーズにコミュニケーションが取れるようになるので、 4月のみ一緒にエンジニア研修を受け、5月から本配属となります。 同様にディレクター研修において、エンジニアにも必要な項目は一緒に受けています。 3月中旬頃に研修担当として、私(新卒2年目)と中途1年目の先輩がアサインされました。 そこから半月ほど準備を進め、3ヶ月間の研修を迎えることとなります。 研修の目的・ゴール 1. 7月のアサイン時にサポートありで開発ができること 弊社は7月に各チームに本配属されます。 そこからすぐに実際の案件にアサインされていきますので、 1人でもスムーズに開発に取り組めるように1人前のエンジニアになっていることが最終ゴールです。 2. 社会人として必要なことを身につける 研修期間は社会人として身につけてほしい常識、マナー、生活態度を学ぶ時間でもあります。 ホウレンソウがきちんとできる 開発する上でのコミュニケーションがきちんととれる ことはもちろん 情報収集を習慣づける 自分で考える わからないところを聞いて解決する などができるようになって欲しいという思いで、普段のサポートをしていました。 研修を組み立てる上でのポイント 今のウエディングパークに必要なものを考える ここ数年、座学では毎年同じ項目を研修として取り入れていましたが、項目を選定し直した結果、追加で必要な項目が見えてきました。 今年の研修を決める時に考えたのが、弊社には各分野のプロがいるのだから、その知識を存分に伝えてもらおうというものです。 1人で学んでいくには少しハードルの高いものであっても、 研修として一緒に手を動かしたものであれば今後も学習や挑戦がしやすく、 様々な分野に精通するエンジニアへの第一歩になると考えました。 ここ最近ではセキュリティエンジニアの入社や設計、パフォーマンスの強化という背景もあり、 新たにプロ編座学として取り入れることに決めました。 他にもアサイン直後から即戦力となれるように、実践的な実案件OJTを取り入れました。 エンジニア、ディレクター社員を巻き込む 研修担当は2人ですが、新入社員は全員で育てていくものだと思いますし、弊社にはその風土が強いように感じます。 みんなで新卒をサポートし、達成や成功を一緒に喜ぶ。 昨年実際に研修を受けていた時、研修のあいだから同じチームの一員であるような雰囲気があり、 モチベーションを高く保てた記憶があります。 今年もその雰囲気が作れるように、 みんなで育てていく方針のもと、座学では専門領域に特化した「プロ」のメンバーに講師をお願いしたり、 開発フェーズのレビュワーには実際に普段からレビューしていただいている方をアサインしたり、 座学の進捗や点数の進捗を全社での朝会や部署での定例ミーティングで都度共有していました。 研修全体の流れ どんな研修をやったのかざっくりと。 メイン研修 基礎座学 HTML/CSS インフラ JavaScript SQL PHP/Laravel Golang QA 設計 プロ編座学 パフォーマンスチューニング SEO プロダクトセキュリティ 自動テスト 個人開発 実案件OJT サブ研修 ディレクター研修 新聞読み会 輪読会 どうやったのか(メイン研修編) 座学 エンジニアとして知っておいて欲しい基本的な技術についての研修です。 一方的に話を聞き続けるのではなく、ハンズオン形式をとっています。 また講義ごとに目的・ゴールを設定しており、講義を受けた結果そのゴールに到達しているかテストを行います。 テストは講義に応じて変えており、筆記テストもあれば、プログラミングテストもあり、 満点を目指して講義を受けてもらうことで、理解を深めてもらうようにしています。 講師 先にも述べている通り、講義内容ごとに専門領域に特化した「プロ」のメンバーへお願いをしました。 中には昨年と同じ講義項目ながらも、本業務の傍ら内容を一新してくれる社員もいました。 場所 座学は Cスタ で開催。 たくさんの人とコミュニケーションを取りながら進めてもらいたかったので、講義以外の時間は自席で開発してもらいました。 個人開発 実際の案件を想定して企画を考え、それをもとに設計・開発・テストを行います。 各フェーズごとに先輩のレビューを設け、最終的に社長や統括を含めた社員の前で発表を行い、優勝者を決めました。 ここでは ウエディングパークというメディアについて知ること 自分のアイディアを形にする流れを知ること を目的としています。 上記に付随して、 ウエディングパークのサイトを観察したり、GoogleAnalyticsを使って分析をしたりと自ら情報を集める力や、 自分の考えを正しく人に伝える力、 周りの社員に相談をして巻き込んでいく力も養われると思っています。 実案件OJT ウエディングパークのリリースフローを学びながら実際にリリースする案件を開発します。 これが最後にやる研修なのですが、目標は7月から1人前になること!(もちろん引き続きサポートはします) 各チームが持っている軽めの案件からいくつか選択し、そこにアサインをしました。 これまでの研修では1人での作業がメインでしたが、 ディレクターと一緒に案件をやることでチームで案件を進めるために必要なことを学んでいきます。 どうやったのか(サブ研修編) 新聞読み会 2ヶ月間毎朝新聞を読む会です。 読むだけでは面白くないので、メディア開発本部長とともに記事について議論する時間があります。 経営層の視点を学ぶ良い機会となっています。 輪読会 Web技術の基本 をチョイスしました。 Webとはそもそもなんかのか、イラストつきで分かりやすい本です。 1章ごと読んできて、新たな発見やわからなかった部分について新卒と研修担当とで議論します。 どうやったのか(サポート編) メンター制度 新卒1人に先輩社員1人がメンターとしてつきます。 基本的には新卒2年目の社員がつき、技術的なサポートとメンタル的なサポートをします。 人によってやり方は異なり、毎日15-30分ほど話すペアや週に1回話すペアがあり、2人でやりやすいように決めています。 研修担当との1on1 研修自体は基本的に他の社員に講師やレビュワーをお願いしているので、1日の最後に新卒社員と研修担当(私)が会話できる時間を作りました。 1日研修を受けてどうだったか、モチベーションや不安点をヒヤリングし、活かせるものは次の日から変えていきました。 振り返り 新卒・社員全員にまず目的とゴールを伝える それぞれの研修をなぜ行うのか、なにを目指せばいいのかを事前に共有することが大事だと実感しました。 全員が同じ方向を向いていないと、お互いどう頑張って良いのか、どうサポートすれば良いのか曖昧になってしまうので 必要な人へは必要以上にしっかり伝えるようにすると良いと思います。 こまめな情報キャッチアップと調整 全体的に新卒研修担当の私がつきっきりで指導するのではなく、 それぞれ担当社員にお任せをしているので、 今どんな状況なのかを常に把握するようにしていました。 具体的には各研修の進捗状況や新卒、メンター、講師など関係者の不安点を把握し、 必要であればマネージャーとの交渉や、次の日からの改善事項として調整を行いました。 社員みんなで育てていくという方針のもと、たくさんの方にご協力をいただきましたが、 各担当に任せて終わりではなく、 都度調整していくことが1番難しく、1番大事なことだったと思います。 最後に 2年目にも関わらず、新卒を指導するカリキュラムを作ることはとてもプレッシャーでしたが、 自分が昨年受けた研修をより良いものにして後輩に返すことが ウエディングパークのエンジニアグループの成長に繋がると思い、最後までやりきることができました。 これから新卒研修を推進していく方の参考になれば幸いです。 エンジニア募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 株式会社ウエディングパークwantedly
アバター
こんにちは,新卒2年目サーバーサイドエンジニアの福間(@r_fukuma)です。 技術的負債を返済する文化を作るため,技術的負債返済イベント「1UP DAY」を開催しました。 開催に至った経緯と学んだことについて書いていきたいと思います。 当日の様子についてはこちらのブログを参照! → クリエイターチームの「1UP DAY」(技術的負債返済DAY)を開催しました! 技術的負債返済イベント「1UP DAY」を開催したきっかけ 開発現場で”よく聞くフレーズ”が気になった 最初の1年間,僕は運用開発のチームでサイトのKPIに関わる案件を多くこなしてきました。 その過程の中で「本当はこれもっとこうしたいんだけど…」とか「ここ修正したいってのは前々から話があがってるんだけど…」みたいな話が浮かんでは消えていくのをよく見るようになりました。 「やりたいけど,優先度は低いので今はできそうにない…」といった状況です。 こういう状況をよく目にしていたので,なんとかできないもんかなーとフワフワ考えていました。 技術提案を行うHack Dayという社内イベントが始まった そんなことを考えていたら,会社で新しい取り組みが始まりました。 Hack Day という,クリエイターが自ら技術提案を行い,採用されれば正式に工数をもらって取り組めるといったもの。 弊社は,普段から技術提案はできる環境ではあるのですが,それをもっと活性化することが狙いのイベントでした。 僕自身,この第1回Hack Dayに参加したのですが,技術的負債返済の提案はせずに,普通に技術提案をしました。 ウエディングパークマガジンをAMP化しましょう,みたいな提案です。 結果は惨敗でしたが (笑) まずは今の課題を片付けないと…でも優先度は低い…じゃあイベントにしちゃえ! 「技術的負債をみんなで返済していきましょうよ!」という提案をしたのは2回目のHack Dayでした。 Hack Dayでは非常に多くの技術提案が行われるのですが,懸念として上がることが多いのが 現状のシステム課題 とのバッティング。 新しいことに取り組みたいのに既存の課題が邪魔をする。 そんな環境で本当にクリエイターがワクワクして働けるのかなと疑問に思いました。 「じゃあまず既存の負債を解決していかないと…。でも普段は優先度が低くて見送られる。 だったらいっそのこと課題解決をイベント化してしまって,短期間で取り組んでみたらどうだろうか?」と思い,1UP DAYの提案をしました。 結果として提案が通り,イベント開催のために半日の工数をもらうことができました。 1UP DAYについて (※ ポジティブなイメージを持たせたかった。自作ロゴ) 1UP DAYの定義 普段は優先度が低いタスクに全員で集中して取り組む日 ウエディングパークにおける技術的負債の定義 定義はいろいろあると思うのですが 中長期的に障害を起こす原因になりうる課題 開発の生産性を低下させるもの を負債として定義しました。 自動化やツールの導入,ドキュメント化などにも取り組みます。 運用方法 技術的負債をGitLabのissueに書いてストックする スプレッドシートにリストアップし,優先度をつけて「やる・やらない」を判断する 金曜午後の時間を使ってISSUEに取り組み, 最後にチームごと成果発表を行う やって良かったこと 1. 技術的負債の把握・管理ができるようになった これは意外だったのですが,実際にISSUEを書いてもらうと「その負債,知らなかった〜!」というものがけっこうな数あがってきます。 逆に言うと,技術的負債の存在自体が属人化していたということですね。 クリエイター全員がシステム課題を俯瞰して把握できるようになり,普段の運用開発にISSUEを取り込んだ例もありました。 実際に取り組むかは別にして,今ある課題を全員が把握できるだけでも価値のあることだと思いました。 2.「これ1UP DAYでやろう」という会話が増えた 普段のコードレビューや開発中のディレクターとのやり取り等で「工数増えるけど,本当はこう実装してほしい」「この処理は共通化してほしい」といった会話があったりします。 これまでは,そうした要望も優先度の関係で流れがちでしたが,1UP DAYを開催してからは「これ次の1UP DAYでやろう」という会話が生まれるようになりました。 クリエイター的にはあとでやる!と決めておくことで,普段は業務に集中できるので精神的にも良いです。 1UP DAYを通して負債返済の意識がクリエイターにつくことで, 負債を見つけたら早めに返済する という流れが生まれてきたなと感じました。 3. 生産性高く,負債を返済することができる 第1回目で取り組んだISSUEは20個になりますが,そのうちのほとんどを実際に完了まで持っていくことができました。 不要な画像やCSSの削除 Jenkinsのジョブ作成による開発効率UP 情報が古くなっていた社内Wikiのアップデート など,幅広いジャンルのISSUEに取り組めました。 当日の夕方には成果発表会が控えており,自然と短時間で集中して取り組む形になるので, 生産性も非常に高くなるという思わぬ発見もありました! 4. 全員で同じ時間で取り組むのは,楽しい・学びも多い! 負債返済というワード自体はネガティブなものですが, 同じ時間に全員で取り組むと,自然とワイワイとした空気が出て 予想以上に楽しい時間になりました。(チームを組み,優勝制度も設けたのも大きかったかも) 同じISSUEに複数人で取り組むことでお互いの知識を共有できたり,モブプロ的な要素での学びもあるので,個人・チームの技術力向上にも繋がります。 最後に 技術的負債イベント「1UP DAY」の開催した経緯と,良かったことについて書きました。 実はこの記事を執筆中に,第2回も開催されました!(前回以上に成果の出る会となりました) 技術的負債はどのチームにも存在するものだと思いますので,悩んでいる方がいらっしゃいましたら,ぜひ1UP DAYの取り組みを参考にしてみてください! 最後までお読みいただき,ありがとうございました! エンジニア募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 株式会社ウエディングパークwantedly 参考 イベント開催のヒントとして,Wantedly様の 負債返済日の取り組み のスライドを参考にさせていただきました。ありがとうございます。
アバター
こんにちは。フォトウエディングの撮影スタジオ検索サイト「Photorait(フォトレイト)」を担当しているエンジニアの武田(@takedajs)です。 Photorait(フォトレイト)では入稿された画像をリサイズしてから保存し、公開側に合った大きさの画像を表示しています。動的に画像をリサイズする機能をまだ導入していないので、検証も兼ねて実装してみました。 Lambda、API Gateway、ImageMagickを利用した画像リサイズ機能を書いた記事が過去に投稿されていますが、今回の記事では、Lambda@Edgeとsharpを利用した画像リサイズ機能の実装について書きました。 やってみよう!AWSでWEBサーバー環境構築(Lambda|API Gateway|シリーズ第4回) 実装したもの このような構成で画像リサイズ機能を実装しました。 クライアントがwidthとheightのパラメーターをつけた画像URLにアクセスすると、それに合ったサイズにリサイズした画像を返します。 例: http://xxx.net/cat.jpg?width=500&height=300 Lambda@Edgeについて Lambda@Edgeを利用することで、CloudFrontのイベントに合わせてLambda関数を実行できるようになります。詳しくは公式ページに書いてあるのでそちらを参照してください。 Lambda@Edge – AWS Lambda 画像リサイズ処理を実装 画像リサイズで利用するsharpの取得と、実際にリサイズ処理を行う実装コードを説明します。 sharp取得 sharpをLambda関数で利用するためには、Amazon Linux上でinstallしたsharpを利用する必要があります。今回は、Dockerで環境を作りその上で取得します。 作業ディレクトリにDockerfileを作成します。 FROM amazonlinux:latest MAINTAINER takedajs RUN ["/bin/bash", "-c", "curl -s https://rpm.nodesource.com/setup_8.x | bash -"] RUN ["/bin/bash", "-c", "yum install -y gcc-c++ nodejs"] CMD ["npm", "install", "--only=production"] package.jsonを作成します。 { "dependencies": { "sharp": "^0.21.3" } } Dockerイメージを作成します。 $ docker build -t amazonlinux/nodejs . Dockerコンテナを作成します。 $ docker run --rm -v "$(pwd)":/opt -w /opt amazonlinux/nodejs:latest 作業ディレクトリ配下が以下のような構成になり、node_modules配下にAmazon Linux上でinstallしたsharpが取得されます。 % tree -L 1 . ├── package.json └── node_modules 実装コード 画像リサイズ処理のコードを書くindex.jsを作業ディレクトリ配下に作成します。 % tree -L 1 . ├── package.json └── node_modules └── index.js index.jsでは、CloudFrontのオリジンレスポンスを受け取り、S3から取得した画像を指定されたサイズにリサイズした結果を返しています。 'use strict'; const AWS = require('aws-sdk'); const querystring = require('querystring'); const sharp = require('sharp'); const S3 = new AWS.S3(); const BUCKET = 's3_bucket_name'; exports.handler = (event, context, callback) => { let request = event.Records[0].cf.request; let response = event.Records[0].cf.response; if (response.status == 304 || response.status == 404 ) { callback(null, response); return; } let s3_params = { Bucket: BUCKET, Key: request.uri.substring(1) }; let query_params = querystring.parse(request.querystring); let width = parseInt(query_params.width, 10); let height = parseInt(query_params.height, 10); S3.getObject(s3_params).promise() .then(data => sharp(data.Body) .resize(width, height, { fit: sharp.fit.inside }) .toFormat('jpeg') .toBuffer() ) .then(buffer => { response.status = '200'; response.body = buffer.toString('base64'); response.bodyEncoding = 'base64'; response.headers['content-type'] = [{ key: 'Content-Type', value: 'image/jpeg'}]; callback(null, response); }) .catch(err => { console.log(err); }); }; Lambda関数作成 実装したコードをLambdaにアップするために、Lambda関数を作成します。 ロール作成 Lambda関数作成に利用するロールをIAMで作成します。 使用するサービスにLambdaを選択します。 S3から画像を取得する権限を付与します。 作成したロールの信頼関係に、edgelambda.amazonaws.comを追記します。 これでロールの作成完了です。 Lambda関数作成 Lambda関数作成ページで、以下を選択してください。 Lambda@EdgeはNode.js 6.10 も利用できますが、今回は8系を利用しています。 ・ランタイムにNode.js8.10 ・既存ロールに先程作成したロール 作業ディレクトリにあるindex.js、node_modulesをzip化します。 作成したzipファイルをLambdaの関数パッケージにアップロードして保存します。 ハンドラはindex.handlerを記述してください。 これでLambda関数が作成できました。 Lambda関数のテスト アップロードした関数パッケージが正しく動くかLambda上でテストします。 テストイベント作成ページで、CloudFrontのイベントテンプレートを選択し、uriとquerystringを修正追加したイベントを作成します。 作成したイベントを選択してテストを実行すると実行結果が表示されます。 bodyに画像情報が表示され、問題なく動作していることが分かります。 Lambda@Edgeにデプロイ CloudFrontのイベントでLambda関数を実行させるために、Lambda関数をLambda@Edgeにデプロイします。 まず、Lambda関数にCloudFrontのトリガーを追加します。 下の方にLambda@Edgeへのデプロイがあるので選択し、追加ボタンを押下します。 オリジンにS3を指定したCloudFrontを事前に作成しておき、ディストリビューションで作成したCloudFrontを選択します。 イベントにオリジンレスポンスを選択して、デプロイを押下します。 デプロイには数十分かかるのでTwitterでも見て時間を潰してください。 実装結果 紐づけたCloudFrontのstatusがDeployedになればデプロイ完了です。 URLのwidthとheightを指定してアクセスすると、数値に合わせて画像がリサイズされており、正常に動作していることが分かります。 今回の処理では縦横比を固定しています。また、同じURLにアクセスするとキャッシュを返します。 猫ってやっぱり可愛いですね。猫飼いたいけど、ペット不可のマンションなんですよね。。。 最後に 最低限のリサイズ処理と設定で画像リサイズ機能を実装しました。 本番運用時には、正方形で切り取りと背景透過などの実装や、設定周りのチューニングをする必要がありそうです。 また、Lambda@Edgeへのデプロイに時間がかかるので、本番環境で問題が起きた時の対応をどうするかなど検討しなければいけないことが多々ありそうです。 実際に本番導入してそこで得た知見を書いた記事が上がっているので、そちらを参考にさせて頂きたいと思います。 エンジニア大募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集! 参考 Lambda@Edge – AWS Lambda Amazon CloudFront & Lambda@Edge で画像をリサイズする | Amazon Web Services ブログ AWS Lambda@Edge で画像をリアルタイムにリサイズ&WebP形式へ変換する – クックパッド開発者ブログ Node.jsのNative ModuleをAmazon Linux on Dockerでコンパイルする | DevelopersIO
アバター
こんにちは。サーバーサイドエンジニアの杉浦です。 私がGoogle Ads Scripts(旧:GoogleAdwords Script)の開発を始めてから、約半年が経ちました。 今回は、これからGoogle Ads Scriptsの開発をされる方向けに、設計に必要なTipsをご紹介しようと思います。 開発時に考慮すべきこと どのアカウントの情報を取得・操作するか Google Ads Scriptsは、MCCアカウントに対して設定することも出来ますし、各アカウントそれぞれに設定することも出来ます。 そのため、 1つのアカウントの情報取得したい のか、 複数のアカウントの情報を取得したい のかを開発の前に決める必要があります。 例えば、このような構造をしたアカウントを運用していたとしましょう。 このとき、 1. アカウントA~Eのデータを取得したい⇒ MCCアカウント①にscriptを設定 2. アカウントC~Eのデータを取得したい⇒ MCCアカウント②にscriptを設定 3. アカウントAのデータだけを取得したい⇒ アカウントAにscriptを設定 4. アカウントA・C・Eのデータを取得したい⇒ MCCアカウント①にscriptを設定 かつ 特定のアカウントが判断できる要素を設定 となります。 特定のアカウントを指定するためには、アカウントIDを指定したり、ラベルで判断することが出来ます。アカウントIDを指定する場合、対象のアカウントが変更される度にScriptを修正しなくてはなりません。個人的にはアカウントにラベルを設定し、そのラベルが貼られているアカウントをデータ抽出対応とすることをお薦めいたします。 スクリプト30分の壁 これは私が開発を進めていくうえで躓いた点なのですが、1つのスクリプトの実行時間は 30分 と決まっております。そして30分以内に実行が終わらない場合、「タイムアウトで停止しました」というステータスで実行が停止されてしまいます。 実行レスポンスが遅くなる原因として、 ログの量が多い スプレッドシートに出力するデータ量が多い が挙げられます。 どうしても30分超えてしまうという場合は、アカウントをID順にソートしてn件ずつにスクリプトを分けるなどの工夫が必要です。 開発を進めてからタイムアウトで悩まないように、データ数を考慮した設計を心がけてください。 運用を考えたシステム設計 この記事を読まれている方は恐らくエンジニアの方だと思いますが、スクリプトの運用をする方もエンジニアでしょうか?? 弊社では、広告運用チームのメンバーがスクリプトの運用を行っております。 そのため「データ抽出対象日を手動で設定したいのだけれど、スクリプトの変更はしたくない… 」 という状況が生まれました。 そこで スプレッドシートの固定のセルにデータ抽出対象日を記入してもらい、スクリプトはそのセルの値を参照する というロジックに変更しました。 スクリプトはデータを出力することだけでなく、指定したURLのスプレッドシートを読み込むことができます。 スクリプト実行ごとに変更する条件はあるか? 履歴を残しておく必要はあるか? データ出力する場合、スプレッドシートは毎回新規で作成するのか?特定のスプレッドシートを繰り返し使うのか? どんな運用を行うかを事前に想定した設計をしていただくと、後々にロジックを変更する事態を避けられるのではないかと思います。 最後に 私が初めてGoogle Ads Scriptsの開発を行った時はまだ知識も浅く、開発を進めてから「こうしておけばよかった」と思うことが何度かありました。 このブログを読んでらっしゃる方は、ぜひ手戻りのない設計になっているかを一度チェックしてみてください。 *シリーズ* Google Ads Scripts入門① Google Ads Scripts入門② Google Ads Scripts入門③ エンジニア大募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
アバター
こんにちは。サーバーサイドエンジニアの杉浦です。 前回 は、Google広告(旧:GoogleAdWords)のデータをキャンペーンパフォーマンスレポートを使って取得する方法をご紹介しました。 今回は、アカウントパフォーマンスレポートから、前日にCVの上がったアカウント一覧をメールで送信する方法をご紹介します。 前提 Google広告を運用しているGoogleアカウントがあること 入門内容 アカウントパフォーマンスレポートより、下記項目を取得し、HTMLメールを送信することを目標とします。 Google広告画面でのイメージとしては、対象MCCアカウントの「アカウント>掲載結果」に表示される項目を取得します。 アカウントID アカウント名 コンバージョン 表示回数 クリック数 費用 CTR CPC コード /** * 前日にCVが上がったアカウント情報をメールで送信します */ // メールアドレス ★変更してください★ var MAIL_ADDRESS = ['AAAAAAAAAA@AAA.co.jp' ,'BBBBBBBBBB@BBB.co.jp']; // レポート出力対象CustomerID ★変更してください★ var MANAGER_CUSTOMER_ID = '1234567890'; function main() { // データを取得 var allData = []; procAccountDataToMail(function(account) { // アカウントレポートデータ取得 var data = getAccountData(account); // 全データをため込む allData.push(data); }); // データをHTMLに加工 var mail_date = shapeDataToHtml(allData); // メール送信 sendmail(mail_date); } function procAccountDataToMail(callback) { // アカウント情報を取得 var accountSelector = MccApp.accounts() .withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'") .withCondition("Conversions > 0") .forDateRange("YESTERDAY") .orderBy("ExternalCustomerId ASC"); var accountIterator = accountSelector.get(); Logger.log('アカウント件数 : ' + accountIterator.totalNumEntities()); // アカウント単位でデータ抽出 while(accountIterator.hasNext()) { var account = accountIterator.next(); // MCC内に存在する対象のアカウント情報を取得 MccApp.select(account); // main()内にて宣言したfunctionを実行する callback(account); } } /** * アカウントレポートデータ取得 */ function getAccountData() { var data = []; var rows = AdWordsApp.report( 'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, Ctr, Conversions' + ' FROM ACCOUNT_PERFORMANCE_REPORT ' + ' DURING YESTERDAY' ).rows(); while (rows.hasNext()) { var row = rows.next(); try { data['ExternalCustomerId'] = row['ExternalCustomerId']; data['CustomerDescriptiveName'] = row['CustomerDescriptiveName']; data['Impressions'] = row['Impressions']; data['Clicks'] = row['Clicks']; data['Cost'] = row['Cost']; data['Ctr'] = row['Ctr']; data['Conversions'] = row['Conversions']; } catch (ex) { Logger.log('Exception:'+ex); } } return data; } /** * 受け取ったデータをHTML形式にして返します * @param {attay} data */ function shapeDataToHtml(data){ // 日付取得 var d = new Date(); d.setDate(d.getDate() - 1); // 件名設定 var subject = '【CVあり】アカウントレポート_' + Utilities.formatDate(d, 'JST', 'yyyy年MM月dd日'); var body = []; body.push(' 昨日のCVが1以上のアカウント一覧 '); body.push(' '); // タイトル表示 body.push(' \ '); body.push('td width="80" style="background: #a3daff">アカウントID '); body.push(' アカウント名 '); body.push(' コンバージョン '); body.push(' 表示回数 '); body.push(' クリック数 '); body.push(' 費用 '); body.push(' CTR '); body.push(' CPC '); body.push(' '); // データ表示 for(var i = 0; i < data.length; i++) { body.push(' '); body.push(' '+data[i]['ExternalCustomerId']+' '); body.push(' '+data[i]['CustomerDescriptiveName']+' '); body.push(' '+data[i]['Conversions']+' '); body.push(' '+data[i]['Impressions']+' '); body.push(' '+data[i]['Clicks']+' '); body.push(' '+data[i]['Cost']+' '); body.push(' '+data[i]['Ctr']+' '); body.push(' '+(Math.round(getRatio(toInteger(data[i]['Cost']), toInteger(data[i]['Clicks']), false)))+' '); body.push(' '); } body.push(" "); return { "subject": subject, "body": body.join('\n') }; } /** * メール送信 * @param {array} mail_date */ function sendmail(mail_date) { MailApp.sendEmail(MAIL_ADDRESS ,mail_date["subject"] ,'' , {htmlBody: mail_date["body"]} ); Logger.log('メールを送信しました。 件名:' + mail_date["subject"]); } /** * 文字列を受け取り、数値で返します * @param {string} value 数字の文字列 */ function toInteger(value) { //undefineの場合 if(value === void 0){ return 0; } try { return parseInt(value.split(',').join(''), 10) } catch (ex) { Logger.log('[ERROR] function:toInteger value='+value+', ex='+ex); return 0; } } /** * 割り算した結果を返す * 分子または分母が0の場合は、0を返します * @param {int} bunshi 割られる数 * @param {int} bunbo 割る数 * @param {boolean} isPercent true:%表記で返す/false:100を掛けない元データで返す */ function getRatio(bunshi, bunbo, isPercent) { if (bunshi == 0 || bunbo == 0) { return 0; } if (isPercent) { return bunshi / bunbo * 100; } return bunshi / bunbo; } 解説 アカウント情報を取得 33行目にて、対象となるアカウント情報を取得しています。 var accountSelector = MccApp.accounts() .withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'") .withCondition("Conversions > 0") .forDateRange("YESTERDAY") .orderBy("ExternalCustomerId ASC"); var accountIterator = accountSelector.get(); withCondition は複数設定することが出来ます。 例えば表示回数が100回以上のデータを取得する際には、 .withCondition(“Impressions >= 100”) と表記します。 .forDateRange(“YESTERDAY”) は他にも .forDateRange(“LAST_MONTH”) で先月を指定したり、 .forDateRange(“20180101,20181231”) で期間指定することも出来ます。 セレクタに関する詳細は、 公式ガイド をご覧ください。 アカウントレポートを取得 56行目から始まるfunctionにて、1アカウントずつのアカウントレポート情報を取得しています。 私が初めに躓いたのは、 取得する項目の組み合わせによって1つのクエリで取得できないことがある 、というものでした。 例えば今回のクエリに、1つ項目を足してみましょう。 var rows = AdWordsApp.report( 'SELECT ExternalCustomerId, CustomerDescriptiveName, Impressions, Clicks, Cost, ' 'Ctr, Conversions, ConversionTypeName' //★ConversionTypeNameを足しました + ' FROM ACCOUNT_PERFORMANCE_REPORT ' + ' DURING YESTERDAY' ).rows(); 実行すると、エラー INVALID_FIELD_NAME_FOR_REPORT が発生してしまいます。 これは、ImpressionsやClicksと同時にConversionTypeNameを取得することが出来ないためです。 Google広告画面にてMCCアカウントから「アカウント>掲載結果」の表を出してみてください。 ConversionTypeNameを足す前のクエリは、この画面にて分割表示をせず、コンバージョンでフィルターを掛けた結果と同等です。 ですが「分割>コンバージョン>コンバージョンアクション」を設定すると、各コンバージョン(ConversionTypeName)単位でのコンバージョン数が表示され、その行のImpressionsやClicksには「-」が表示されています。 つまり、Google広告画面上でコンバージョン単位でのImpressionsやClicksが表示できないように、クエリでもImpressionsやClicksと同時にConversionTypeNameを取得することが出来ないのです。 広告運用をしたことが無い私にとって、この理解はなかなか難しかったです。 データをHTML形式に整形 85行目からのfunctionでは、全アカウントのデータをHTML形式に整形しています。 今回はタイトルだけに背景色を設定いたしましたが、 例えばコンバージョンが50以上のセルに背景色を設定すれば特定のデータが見やすくなりますし、 全アカウントのデータを表示回数順に並び替えてランキング形式で表示することも可能です。 ぜひ色々と工夫してみてください。 メール送信 130行目からのfunctionでは、HTML形式に整形したデータをメール送信しています。 Ads Scriptには MailApp.sendEmail が用意されていますので、引数にメールアドレスと件名、本文を設定するだけでメールが送信されます。 メールアドレスは複数設定することが出来ますので、各担当者へ同じメールを送信することが可能です。 ▼スクリプト実行後には、このようなメールが届くはずです。(値はマスクさせていただきました) 最後に タイトルに入門とつけておりますが、かなり実用的な内容になっております。 スクリプトの自動実行を早朝に設定し昨日の実績をメールで送信したり、 一定期間内のCVが0のアカウントをアラートとしてメール通知することも出来ます。 ぜひ皆様の業務に合った内容に工夫してみてください。 *シリーズ* Google Ads Scripts入門① Google Ads Scripts入門② エンジニア大募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
アバター
こんにちは。エンジニアの阿久津です。 今回はMySQLの mysqldump と mysqlpump という2つのコマンドでバックアップを実施しどちらが速く処理するのか比較してみましたので、それを記事にしたいと思います。 mysqldumpとは 従来のMySQLデータベースのバックアップ/リストアに用いられるコマンドです。 mysqlpumpとは MySQL5.7から追加された新機能で、 mysqldump の次世代となるコマンドです。 機能紹介 1. 並列処理(並列ダンプ) 複数のデータベース・テーブルを並列処理でバックアップすることができます。 MySQLサーバからデータを収集するために複数のスレッドを作成し、そのスレッドがそれぞれMySQLサーバへ接続してデータをまとめるためにキューに取得したデータを挿入する仕組みになっています。 オプションを何も利用せずにデフォルトで mysqlpump を利用すると、スレッド数は2つ、キュー数は1つでダンプ処理が走ります。 –parallel-schemas オプションと –default-parallelism を使うと、スレッド数とキュー数を調整しながら、ダンプを実施できるみたいです。 2. 圧縮出力 –compress オプションを使えば、クライアント・サーバ間で送信されるデータを圧縮することができます。また –compress-output オプションを使うと、ダンプデータを圧縮できるようです。※ZLIBとLZ4をサポート。 せっかくなので –compress-output を使ってみました。 内容としては、mysql_testというデータベースを圧縮なし、圧縮ありのパターンで比較してみます。 圧縮なし $ mysqlpump --single-transaction -uroot -pXXXXXXXXX mysql_test > ~/tmp_default.sql 圧縮あり $ mysqlpump --single-transaction --compress-output=LZ4 -uroot -pXXXXXXXXX mysql_test > ~/tmp_compress.sql 結果 $ du -hs ~/tmp* 177M tmp_default.sql 129M tmp_compress.sql 圧縮されてる!これは便利ですね! 3. ダンプ処理の進捗出力 mysqlpump には、ダンプ処理の進捗を確認できる機能が備わっています。 これは実際に見たほうが早いと思いますのでやってみました。 結果 $ mysqlpump --single-transaction -uroot -pXXXXXXXXX mysql_test > ~/tmp.sql mysqlpump: [Warning] Using a password on the command line interface can be insecure. Dump progress: 0/1 tables, 250/2145122 rows Dump progress: 0/1 tables, 226000/2145122 rows Dump progress: 0/1 tables, 522250/2145122 rows Dump progress: 0/1 tables, 804000/2145122 rows Dump progress: 0/1 tables, 1075500/2145122 rows Dump progress: 0/1 tables, 1360000/2145122 rows Dump progress: 0/1 tables, 1625000/2145122 rows Dump progress: 0/1 tables, 1885250/2145122 rows Dump completed in 8510 milliseconds 上記のように、ダンプ処理の進捗を確認できます。 mysqldump にはない機能なので、便利ですね。 他にも新機能はあると思いますが、紹介はこのくらいにして 今回の検証に移りたいと思います。 検証環境 Vagrant 1.9.5 CentOS Linux release 7.1.1503 (Core) MySQL 5.7.23 検証内容 以下の条件で mysqldump と mysqlpump どちらが速くバックアップできるのか。 ※コマンドにはオプションを付けずに、デフォルトのまま実行しています。 ① 1テーブル(約130MB)でバックアップする場合 ② 1テーブル(約500MB)でバックアップする場合 ③ 3テーブルでバックアップする場合 ④ 5テーブルをバックアップする場合 検証結果 ① 1テーブル(約130MB)でバックアップする場合 1テーブル 行数 : 1048576 サイズ : 約130MB mysqldumpコマンド $ time mysqldump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 > ~/tmp.sql mysqlpumpコマンド $ time mysqlpump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 > ~/tmp.sql 結果 約0.6秒 ほど mysqldump が速い結果となりました。 ② 1テーブル(約500MB)でバックアップする場合 1テーブル 行数 : 4194304 サイズ : 約500MB mysqldumpコマンド $ time mysqldump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 > ~/tmp.sql mysqlpumpコマンド $ time mysqlpump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 > ~/tmp.sql 結果 約2.2秒 ほど mysqldump が速い結果となりました。 ③ 2テーブルでバックアップする場合 1テーブル 行数 : 33556096 サイズ : 約800MB mysqldumpコマンド $ time mysqldump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 test2 > ~/tmp.sql mysqlpumpコマンド $ time mysqlpump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 test2 > ~/tmp.sql 結果 約5.5秒 ほど mysqlpump が速い結果となりました。 ④ 3テーブルでバックアップする場合 1テーブル 行数 : 33556096 サイズ : 約800MB mysqldumpコマンド $ time mysqldump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 test2 test3 > ~/tmp.sql mysqlpumpコマンド $ time mysqlpump --single-transaction -uroot -pXXXXXXXXX mysql_test test1 test2 test3 > ~/tmp.sql 結果 約7.1秒 ほど mysqlpump が速い結果となりました。 まとめ 今回はシンプルに mysqldump と mysqlpump どちらが速くバックアップできるか比較しました。 結果としては、1テーブルをバックアップする場合は mysqldump 、 複数テーブルをバックアップする場合は mysqlpump の方が速い結果となりました。 mysqlpump には並列ダンプ機能があるため、複数テーブルのバックアップにはやはり強いようです。 今回は少ないテーブルをバックアップしましたが、データベースやテーブルが膨大な場合は mysqlpump を使った方が速くバックアップができそうです。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 IT×ブライダルで業界を変える!プロフェッショナルなWEBエンジニア募集!
アバター
こんにちは。サーバーサイドエンジニアの杉浦です。 前回は、Google広告(旧:GoogleAdWords)のデータを Google Ads Scripts (旧:Google AdWords Scripts)を使ってスプレッドシートに出力する方法をご紹介いたしました。 今回はGoogleAdWordsAPIを使って、キャンペーンパフォーマンスレポートから取得したデータを出力する方法をご紹介します。 前提 Google広告を運用しているGoogleアカウントがあること 入門内容 キャンペーンパフォーマンスレポートより、下記項目を取得することを目標とします。 アカウントID アカウント名 表示回数 クリック数 コンバージョンアクションが「購入」のコンバージョン数 実際に私が直面した問題 Google広告画面で1度に表示できる項目が、AdWordsAPIだと1度に取得できない 例えばGoogle広告画面のキャンペーンにて、コンバージョンアクションで分割すると、クリック数や表示回数、費用といった項目はキャンペーンの行にのみ表示され、分割したコンバージョンアクションの行には表示されません。 キャンペーン、表示回数、クリック数、コンバージョンアクション単位でのコンバージョンを同一の表に出力する場合、キャンペーンを単位とした一覧と、コンバージョンアクションを単位とした一覧をそれぞれ取得し、script上で結合させる必要があります。 Google Ads Scriptsを作成してみよう ①.スプレッドシートを作成する 前回はscriptからスプレッドシートを作成しましたが、今回は作成したスプレッドシートに出力することにします。 そのため、下記の通りの新しいスプレッドシートを作成してください。 タイトルはなんでもOK シート名は「シート1」のまま ②.Google Ads Scriptsを作成する script作成方法は、前回のブログをご参照下さい。 なお、このscriptはMCCアカウントに作成してください。 ③.ソースコードを設定する 新しいscript作成が完了したら、「function main(){ }」を削除し、下記コードをコピーして貼り付けてください。 ※2行目:MANAGER_CUSTOMER_IDの値を、ご自身で登録しているMCCアカウントのIDに変更してください。 ※5行目:SPREADSHEET_URL の値を、作成したスプレッドシートのURLに変更してください。 //レポート出力対象CustomerID ★変更してください★ var MANAGER_CUSTOMER_ID = '1234567890'; //出力先スプレッドシートのURL ★変更してください★ var SPREADSHEET_URL = 'https://docs.google.com/spreadsheets/d/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/edit#gid=0'; //出力スプレッド行 var row = 1; function main() { //------------------------ // スプレッドシートを用意 //------------------------ //スプレッドシートを展開 var spreadSheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL); //出力先シートを宣言 var sheet = spreadSheet.getSheetByName('シート1'); //タイトルを表示 setTitle(sheet); //---------------------------------------------- // Google広告データを取得・スプレッドシートに出力 //---------------------------------------------- createReport(function(account) { //メインデータ取得 var mainData = getMainData(); //CVデータ取得 var cvData = getCvData(); //スプレッドに出力 viewAllData(mainData, cvData, sheet); }); } /** * アカウント情報取得 * @param {*} callback */ function createReport(callback) { //MCCidを指定してアカウント情報を取得 var accountIterator = MccApp.accounts() .withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'") .get(); Logger.log('アカウント件数 : ' + accountIterator.totalNumEntities()); //アカウント単位でデータ抽出 while(accountIterator.hasNext()) { var account = accountIterator.next(); //MCC内に存在する対象のアカウント情報を取得 MccApp.select(account); //main()内にて宣言したfunctionを実行する callback(account); } } /** * タイトル表示 * @param {object} sheet スプレッドシートオブジェクト */ function setTitle(sheet){ sheet.getRange(row, 1).setValue('アカウントID'); sheet.getRange(row, 2).setValue('アカウント名'); sheet.getRange(row, 3).setValue('表示回数'); sheet.getRange(row, 4).setValue('クリック数'); sheet.getRange(row, 5).setValue('購入CV'); row++; } /** * メインデータ取得 */ function getMainData() { var data = []; var rows = AdWordsApp.report( 'SELECT ExternalCustomerId, CustomerDescriptiveName, Clicks, Impressions' + ' FROM ACCOUNT_PERFORMANCE_REPORT ' + ' DURING LAST_MONTH' ).rows(); while (rows.hasNext()) { var row = rows.next(); try { data['ExternalCustomerId'] = row['ExternalCustomerId']; data['CustomerDescriptiveName'] = row['CustomerDescriptiveName']; data['Clicks'] = row['Clicks']; data['Impressions'] = row['Impressions']; } catch (ex) { Logger.log('Exception:'+ex); } } return data; } /** * CVデータ取得 */ function getCvData() { var data = []; var rows = AdWordsApp.report( 'SELECT ExternalCustomerId, CustomerDescriptiveName, ConversionTypeName, Conversions' + ' FROM ACCOUNT_PERFORMANCE_REPORT ' + ' DURING LAST_MONTH' ).rows(); while (rows.hasNext()) { var row = rows.next(); // コンバージョンアクションが「購入」であるか if(row['ConversionTypeName']!='購入') continue; try { data['ExternalCustomerId'] = row['ExternalCustomerId']; data['CustomerDescriptiveName'] = row['CustomerDescriptiveName']; data['ConversionTypeName'] = row['ConversionTypeName']; data['Conversions'] = row['Conversions']; break; } catch (ex) { Logger.log('Exception:'+ex); } } return data; } /** * スプレッドシートにデータ出力 * @param {array} mainData メインデータ * @param {array} cvData CVデータ * @param {object} sheet スプレッドシートオブジェクト */ function viewAllData(mainData, cvData, sheet){ //メインデータがundefinedの場合 if(mainData['ExternalCustomerId'] === void 0) return; sheet.getRange(row, 1).setValue(mainData['ExternalCustomerId']); sheet.getRange(row, 2).setValue(mainData['CustomerDescriptiveName']); sheet.getRange(row, 3).setValue(mainData['Clicks']); sheet.getRange(row, 4).setValue(mainData['Impressions']); //CVデータがundefinedの場合 if(cvData['Conversions'] === void 0){ sheet.getRange(row, 5).setValue(0); }else{ sheet.getRange(row, 5).setValue(cvData['Conversions']); } row++; } ④.実行・結果確認 コードの貼り付けが完了したら、画面右下の「プレビュー」をクリックしてください。 作成したスプレッドシートを確認すると、データが出力されていることが確認できました。 今回は Account Performance Report からデータを取得しましたが、取得できる項目はたくさんありますので、ぜひ他の項目も取得してみてください。 補足 アカウント取得方法 アカウントは、下記のように取得することも出来ます。 //アカウント情報を取得 var accountSelector = MccApp.accounts() .withCondition("ManagerCustomerId = 'MCCアカウントID'") .withCondition("LabelNames CONTAINS 'ラベル名'") .forDateRange('20180401, 20180930') .orderBy("ExternalCustomerId ASC"); var accountIterator = accountSelector.get(); MccApp.accounts().withCondition()は複数設定することが出来ます。 そのため、CVが〇件以上のデータだけを抽出する、といった操作も可能です。 便利ツール AdWordsAPIを利用するうえで欠かせないツールが、 AWQL.me です。 サンプルソースではAWQL(AdWords クエリ言語)を使ってデータを取得していますが、このAWQLだけを実行することが出来るサイトです。 AWQLに関しては、 公式サイト をご確認ください。 最後に 取得できる項目は他にも多くありますので、項目を増やしたり、特定のコンバージョンタイプのCPAやCVRを計算して出力することも出来ます。 ぜひ色々試してみてください。 エンジニア大募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
アバター
こんにちは。サーバーサイドエンジニアの杉浦です。 今回は、Google広告(旧:GoogleAdWords)のデータを Google Ads Scripts (旧Google AdWords Scripts)を使ってスプレッドシートに出力する方法をご紹介したいと思います。 Google Ads Scriptsとは Google Ads Scriptsはブラウザベースの IDE で、シンプルな JavaScript を使うことで Google広告 データを取得したり、制御することが出来ます。 Google広告のデータをエクセルでレポートにしている方が多いかと思いますが、Google Ads Scriptsを使うとレポートの作成を自動化することも可能です。 詳細は 公式HP をご参照ください。 前提 Google広告を運用しているGoogleアカウントがあること 入門内容 Google Ads Scriptsを作成してみよう では実際に、Google Ads Scriptsを作成してみましょう。 Google広告にログインし、MCCアカウントにいる状態で画面右上にある[ツール]から、一括操作の[スクリプト]を選択します。 [+]のマークをクリックして、新規スクリプトを作成します。 スクリプトの承認を要求されるので、[承認]リンクをクリックします。 どのアカウントに対して承認するかを選択し、[Allow]をクリックします。 承認が完了したら、「function main(){ }」を削除し、下記コードをコピーして貼り付けてください。 ※3行目:MANAGER_CUSTOMER_IDの値を、ご自身で登録しているMCCアカウントのIDに変更してください。 function main() { //レポート出力対象MCC-ID var MANAGER_CUSTOMER_ID = '1234567890'; // 日付取得 var now = new Date(); var yyyymmdd = now.getFullYear()+ ( "0"+( now.getMonth()+1 ) ).slice(-2)+ ( "0"+now.getDate() ).slice(-2); // スプレッドシート作成 var spreadsheet = SpreadsheetApp.create('WeddingparkSampleReport_' + yyyymmdd); // デフォルトで作成されるシートを取得 var sheet = spreadsheet.getSheetByName('シート1'); // シート名を変更 sheet.setName('アカウント一覧'); // MCC-IDを指定してアカウント情報を取得 var accountIterator = MccApp.accounts().withCondition("ManagerCustomerId = '"+MANAGER_CUSTOMER_ID+"'").get(); Logger.log('アカウント件数 : ' + accountIterator.totalNumEntities()); // 1行目にタイトルを出力 sheet.getRange(1, 1).setValue('アカウントID'); sheet.getRange(1, 2).setValue('アカウント名'); sheet.getRange(1, 3).setValue('タイムゾーン'); sheet.getRange(1, 4).setValue('通貨コード'); // データは2行目から出力 for (var i = 2; accountIterator.hasNext(); i++) { // アカウント毎にデータ取得 var account = accountIterator.next(); // MCC内に存在する対象のアカウント情報を取得 MccApp.select(account); // アカウントデータを出力 sheet.getRange(i, 1).setValue(account.getCustomerId()); // アカウントID sheet.getRange(i, 2).setValue(account.getName()); // アカウント名 sheet.getRange(i, 3).setValue(account.getTimeZone()); // タイムゾーン sheet.getRange(i, 4).setValue(account.getCurrencyCode()); // 通貨コード } } コードの貼り付けが完了したら、画面右下の「プレビュー」をクリックしてください。 スクリプトが実行され、「ログ」タブにログが出力されます。 実行時にエラーが発生した場合も、「ログ」タブにエラーが出力されます。 さて。Google Ads Scriptsを実行したGoogleアカウントで、スプレッドシートが出力されているかを確認しましょう。 「WeddingparkSampleReport_YYYYMMDD」というタイトルで、スプレッドシートが出力されています。 最後に 今回はスプレッドシートを作成しましたが、URLを指定していつも同じスプレッドシートにデータを出力することも出来ます。 またアカウントを取得する際にMCCアカウントのIDではなくラベルでフィルターを掛けたり、条件を複数設定することも可能です。 次回はAdWordsAPIを利用したレポート取得方法をご紹介します。 エンジニア大募集中 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
アバター
デザイナーの山下です。 ___∧______________ みなさんは日々どのようにデザインをブラッシュアップしていますか? 師匠や先輩にチェックしてもらう ディレクターにチェックしてもう デザイナー同士でチェックし合う 自力でがんばる と、方法は様々あるかと思います。 ウエディングパークのデザイナーチーム(通称:デザチ)は、 非常にフラットなチームなので、 この人がゼッタイ!!という概念がありません。 そこで選択したのが、 デザイナー同士でチェックし合う です。 この取り組みを社内ではデザインレビューと呼んでいます。 デザインレビューを始めて半年以上経ち、 生産性の向上 デザインスキルの向上 と良い面が沢山でてきたので、このナイスな取り組みをご紹介します★ __________________ デザインレビューってどうやっているのか ■レビューに使用しているアプリケーション: Adobe XD フィードバックが非常に簡単で使いやすく、無料で使用できる点もナイスです。 ■レビューの管理: Googleスプレッドシート デザインのコンセプト、レビュー期限、担当者、などが書いてあります。 ■ルール 原則デザイナー全員がレビューを提出する 原則デザイナー全員がレビューを確認する 気になった点を指摘、もしくは改善提案する(良いと思った点も積極的に★) あくまでアドバイスであり、指摘内容の取捨選択は担当デザイナーに一任する 生産性が向上したよ! 弊社の開発体制は、大体デザイナーとディレクターが各1名ずつアサインされます。 オリエン後に、まずはデザイナーがデザイン制作を担当します。 作ったデザインは最終的に責任者からOKをいただかなくてはいけません。 ですが、デザインの相談をする場がなかったため かなりのやり取りがディレクターとの間で発生していました。 最後の責任者確認での戻しも多々あり、ちょっとした問題になることもしばしばありました。 今まで… デザインレビュー導入後 レビュー導入後ディレクターとのやり取りが大幅に減り、一気に生産性があがりました★ デザイナースキルの向上 今まで デザイナー間でのコミュニケーション不足により以下の課題がありました。 他のデザイナーがどんな事やってるか把握しづらい デザインに対して意見を言うタイミングがない デザインレビュー導入後 導入後は以下のスキルアップが図れています。 影響範囲の把握 :他のデザイナーがどんなデザインをしているか、自分のデザインに影響が出そうかよく分かる デザインの最適化 :デザインアドバイスによって、各自のデザインがブラッシュアップできる フォロー力 :レギュレーションや仕様を理解したリードデザイナーのフォローが入る 言語化 :レビューに出す方も、コメントする方も言語化して伝えるスキルが身に付く どれもインハウスデザイナーにはとっても大切★ まとめ デザインレビューはデザイナーがチームとして機能する際に、 また、育成をしていく場合でも、 非常に有効な手段だなと感じております。 へぇ〜もっと話聞いてみたい! デザイナーのチーム作りに興味のある! うちもやってるんだけど、こんな課題ない?などなど ぜひ一度気軽にオフィスに遊びにいらしてください。 お待ちしています★ チームの企画を、自分でデザイン~コーディングまでしたいデザイナー募集!
アバター
デザイナーの山下です。 ___∧______________ 「デザイナーにどう指示だしたらいいかわからないから、参考になる本ない?」 「ディレクターだし、デザインの勉強もしたいんだけどいい本ないかな?」 「デザイナーになりたけど、何から読んだらいいのかな?」 などなど日頃こんなご相談をいただくのですが、 その際におすすめしている本をご紹介します。 __________________ 美味しいとこだけ知っちゃおう! なるほどデザイン 超おすすめ★ 専門用語を控えめにしてくれているので、 誰が読んでもわかりやすいです。ステキ! 抑えておくべきデザインの基礎がここにわかりやす〜〜〜く詰まっております。 図のデザインに関しては、デザイナー以外の人も用途が多いのでぜひ参考にしてほしいです。 配色アイデア手帖 めくって見つける新しいデザインの本[完全保存版] 色が与えるイメージを様々なパターンで紹介してれます。 色だけではなく、展開場合のデザイン・パターン・イラストもあるので、 より具体的にイメージできます。 今回のデザインどうしようかなぁ〜って時にパラパラめくるのがオススメです。 もっと専門的な知識を細かく知りたい!(用語も含め) デザイナーがノンデザイナーに教えなくちゃいけない時などに大活躍します。 もちろんデザインの勉強本としても◎ デザイン入門教室[特別講義] 確かな力を身に付けられる ~学び、考え、作る授業~ (Design&IDEA) 参考デザインが日本のものなのでわかりやすい! ノンデザイナーズ・デザインブック [第4版] 超職人系なあなたにピッタリ★ マニアックなまでに細部まで知りたかったら 最後に 簡単ではありますがご紹介させていただきました。 インプットしたらアウトプットすることがとっても大切です。 弊社ではおすすめしたい本をご紹介する機会が多々あるので、 わたしもいい本があったらどんどんアウトプットしていきます! Wedding Parkでは一緒に技術のウエディングパークを創っていくデザイナーを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらしてください。 お待ちしています★ チームの企画を、自分でデザイン~コーディングまでしたいデザイナー募集!
アバター
Redis4.0のUNLINKを使ってみる こんにちは、サーバーサイドエンジニアの菅原です。 今回は今更ながらRedis4系から追加されたUNLINKコマンドについて調べて検証してみました。 背景 Webアプリケーションのパフォーマンスを向上しようとするときRedisは強力なツールです。 ですがRedisを運用する注意点としてRedisの容量の懸念が見込まれます。 キーがどんどん積み上がるとRedisの容量を大きく圧迫し、そうなるとRedisはキーの検索に時間を要するのとキーを削除するときもサーバーに負荷がかかりサーバーダウンしてしまうことも考えられます。 setした時に設定したexpireが切れたキーも完全に消えることは確証されなく、 ゴミのデータが残る場合があるのでDELコマンドで定期的に掃除が必要になります。 しかしRedis3までのDELコマンドは一度に指定されたキーを全てアクセスして削除しようとするのでその間はRedisは他の処理を受け付けないため Redisに膨大なサーバー負荷がかかり、Redisが落ちてしまったり、クラスターダウンの影響にも繋がります。 今回はその問題を解決するためにRedis4系で新たに追加されたUNLINKコマンドについて紹介します。 Redis4とは Redis4は2017年7月14日にリリースされました。現在は4.0.10までのバージョンが出ています。 4系で追加された仕様の中でもっとも気になったのはモジュール機能とUNLINKコマンドの追加です。 モジュール機能はRedisの型やコマンドなどを拡張できる機能で UNLINKコマンドは非同期にキーのデータを削除してくれることで スレッドの待ち時間なしにデータを順に削除することができる機能です。 今回は実際にUNLINKコマンドを扱って挙動を確認してみましょう。 今回の目標とゴール Redis Clusterを構築する Redis Clusterにデータをセットする UNLINKコマンドで容量が大きいキーTOP10を削除するシェル作成 実際に登録したデータが反映されているか確認する 環境 Mac OS redis-4.0.9 Redis Clusterを構築する まずは公式HPから自分のマシンにRedis 4をダウンロードして解凍します。 https://redis.io/download 以前に環境構築した記事で細かい設定などを執筆してあるのでそちらも参考にしてください。 初めてRedisを使うための環境構築 $ wget http://download.redis.io/releases/redis-4.0.9.tar.gz $ tar xzf redis-4.0.9.tar.gz $ cd redis-4.0.9 $ make $ sudo make install 上記インストールが完了したら redis-serverを立ち上げましょう(brew)など/usr配下のredisを起動したい場合はそのパスに合わせてredis-serverコマンドを入力します。 $ src/redis-server redisが立ち上がったらredisのコマンドラインを立ち上げます $ src/redis-cli 無事立ち上げることができたら成功です。デフォルトでは6379のポート番号でredisが立ち上がっていることがわかります。 そして早速Redis Clusterを構築するためのCluster構成を設計しましょう 今回はClusterを構成するための最低限のCluster構成(master: 3台, slave: 3台)で設計して構築します。 図で表すとこんな感じです。 Port(7000~7005)までのRedisでクラスターを構築していきます。 まずは先ほど立ち上げたredisサーバーを閉じてcluster-testディレクトリを作成します。 作成が完了したらその中に(7000~7005)までのディレクトリの部屋を用意してあげます。 用意ができたらそれぞれの部屋にRedisの設定ファイル(redis.conf)を用意してあげます。ポート番号は各自変更してください。 シェルで配布もできそうなのでそこらへんもまた作っていけたらと思います。 port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes 各ディレクトリに配布が完了したら各Redisを立ち上げていきましょう。 #!/bin/sh /usr/local/bin/redis-server ./7000/redis.conf --pidfile ./7000/redis.pid --logfile ./7000/redis.log --cluster-config-file ./7000/node.conf& /usr/local/bin/redis-server ./7001/redis.conf --pidfile ./7001/redis.pid --logfile ./7001/redis.log --cluster-config-file ./7001/node.conf& /usr/local/bin/redis-server ./7002/redis.conf --pidfile ./7002/redis.pid --logfile ./7002/redis.log --cluster-config-file ./7002/node.conf& /usr/local/bin/redis-server ./7003/redis.conf --pidfile ./7003/redis.pid --logfile ./7003/redis.log --cluster-config-file ./7003/node.conf& /usr/local/bin/redis-server ./7004/redis.conf --pidfile ./7004/redis.pid --logfile ./7004/redis.log --cluster-config-file ./7004/node.conf& /usr/local/bin/redis-server ./7005/redis.conf --pidfile ./7005/redis.pid --logfile ./7005/redis.log --cluster-config-file ./7005/node.conf& 無事立ち上がったら次にクラスターで各nodeをclusterとして連携させていきます。 今回は redis-trib.rb というrubyで作られたクラスター管理ツールで構築していきます。 (あらかじめgemなどでインストールしておきます) $ gem install redis4.0.1 clusterは3系以降しか構築できないためgemでインストールするredisは3系以上のインストールをお願いします。 redis-trib.rb のバイナリが入ることがわかるので実行していきましょう。 redis-tribはクラスターを構築したり、nodeを追加したり、クラスターやノードの状態をチェックすることもできます。 今回は各MASTERノードにSLAVEを1 対 1 で構築してあげたいのでreplicas 1のコマンドをコマンド内に入れて実行していきましょう $ ./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 実行が完了するとコンバートしたよとクラスターの構成ができたことがわかります。 [OK] All 16384 slots covered 実際にクラスターの状態を確認します。 $ redis-cli -c -p 7000 cluster nodes e75b961fb9d1520d017d6b344d20e47e095ae51d 127.0.0.1:7000@17000 myself,master - 0 1529857410000 1 connected 0-5460 87db09e5e5f7cc3cd6a0c80d335f0cc5fbd71fee 127.0.0.1:7002@17002 master - 0 1529857411571 3 connected 10923-16383 4dcf420b2674f6b4608b5677ee61d6a8c5b0d74e 127.0.0.1:7001@17001 master - 0 1529857412091 8 connected 5461-10922 5e25fd42ea7ca7cb920db0cc5f16ff6c6a0f1e7b 127.0.0.1:7004@17004 slave 4dcf420b2674f6b4608b5677ee61d6a8c5b0d74e 0 1529857411000 8 connected 4bd60fc5c721be1e560119bad7f4c358ae4050c3 127.0.0.1:7005@17005 slave 87db09e5e5f7cc3cd6a0c80d335f0cc5fbd71fee 0 1529857411467 6 connected b615c32f67585687139a99d199560d3a57716657 127.0.0.1:7003@17003 slave e75b961fb9d1520d017d6b344d20e47e095ae51d 0 1529857412000 11 connected これでクラスターが構築されたことがわかります。 Redisにデータをセットする それでは構築したClusterにデータを投入していきましょう $ redis-cli -c -p 7000 127.0.0.1:7000 > set wp-cluster1 test1 OK 127.0.0.1:7000 > set wp-cluster2 test1 -> Redirected to slot [9505] located at 127.0.0.1:7004 OK 127.0.0.1:7004 > 最初に設定したwp-cluster1のキーバリューは7000(master)のredisに格納され、 そのあとのwp-cluster2はポート7004のredis(master)にリダイレクトで格納されて分散されていることがわかります。 次に先ほど自動的に格納されて切り替わったポート7004のコマンドラインから7000に格納したキー(wp-cluster1)の値を取り出してみます。 127.0.0.1:7004> GET wp-cluster1 -> Redirected to slot [5442] located at 127.0.0.1:7000 "test1" 127.0.0.1:7000> 7000ポートのredisを参照しに行ってることがわかります!これでredis clusterへのデータの投入ができました。 しかし、上記方法でデータの追加をしてUNLINKを扱うにはかなり面倒です。 Redisにはダミーデータを登録するコマンドがあるのでそれを使いましょう。 ダミーデータはDEBUGコマンドから投入することができます。 それでは1000件のダミーデータを投入していきましょう。 *Clusterではこのダミーデータはslaveへの同期が行われないので注意 127.0.0.1:7000 > debug populate 1000 OK 登録が完了できたら実際に投入されているか確認します。 127.0.0.1:7000 > dbsize (integer) 1000 1000件のデータが投入されていることがわかります。 それでは実際にこのキーの中でサイズが大きいTOP10を出力してUNLINKするためのシェルを書いていきます。 UNLINKコマンドで容量が大きいキーTOP10を削除するシェル作成 unlink_scan_big_keys.shファイルを作成し、以下のシェルを記載します。 #!/bin/bash BEGIN=0 TOPNUM=10 TMPFILE="./scan.out" RESULTFILE="./result.out" > $RESULTFILE redis-cli -c -p 7000 --raw SCAN $BEGIN > $TMPFILE while true do for key in `sed '1d' $TMPFILE` do echo -n $key" " >> $RESULTFILE redis-cli -c -p 7000 --raw DEBUG OBJECT $key | awk '{print $NF}' | cut -d":" -f 2 >> $RESULTFILE done CURSOR=`head -n1 $TMPFILE` if [[ $CURSOR -eq $BEGIN ]] then echo "Scan ended!" echo "The Top $TOPNUM key in your Redis is" cat $RESULTFILE | sort -nrk2 | head -n$TOPNUM ./unlink-keys.sh exit fi redis-cli -c -p 7000 --raw SCAN $CURSOR > $TMPFILE done #!/bin/bash DATA=`cat ./result.out | sort -nrk2 | head -n10 | awk '{ sub(" .*", ""); print $0; }'` while read keyline do redis-cli -c -p 7000 UNLINK $keyline done << FILE $DATA FILE コマンド一つ一つの意味は解説しませんが 7000のredisサーバにあるキーバリューをSCANで読み出し、読み出したデータの情報を加工して result.outファイルに吐き出します。 result.outに記載しているキーのデータサイズを昇順に並び替え、そのトップ10をUNLINKで削除します。 unlink-keys.sh は別シェルで叩いてますが同一シェル上で問題ありません。 実際に完成したらこのシェルを実行してあげましょう $ ./unlink_scan_big_keys.sh Scan ended! The Top 10 key in your Redis is key:895 7408 key:477 7408 key:192 7408 key:963 7407 key:953 7407 key:949 7407 key:9 7407 key:899 7407 key:890 7407 key:876 7407 (integer) 0 (integer) 0 (integer) 1 (integer) 0 (integer) 0 (integer) 0 (integer) 0 (integer) 0 (integer) 0 (integer) 0 消す対象に上がったキーのバックアップファイルを参照してそのキーが本当に消されているのか確認します。 GET keys key:192 nil nilが返ってきたのでデータが削除されていることがわかります。 今回はトップ10のデータをunlinkで削除しました。 実際にDELコマンドで削除するよりも非同期で安全に削除することができるため バッチのように定期的に取り扱うことも簡単にできそうです。 まとめ Redis4系のUNLINKが気になったので軽く触ってみました。 大量にデータをUNLINKしてる場合にも別ターミナルからRedisへデータを投入なども可能で DELではできなかった操作でデータ量と頻度によってはUNLINKがおすすめであることがわかりました! redis4.0ではさらに可用性で追加された仕様もあるので皆さんも是非使ってみてください。
アバター
こんにちは、サーバーサイドエンジニアの菅原です。 今回はElasticStack6について学んでみたのでその知見を共有したいと思い記事にしました。 はじめに 検索エンジンElasticsearchの検証をしている中でELKと呼ばれるElasticStackの製品について学んでみましたのでそのELKの実行までの流れを記載していきます。 環境 Mac OS Elasticsearch 6.2.4 Kibana 6.2.3 Logstash 6.2.3 入門内容 ゴール:ELKの使い方、及び導入からビジュアライズまでの流れを確認する Logstashでデータを扱う LogstashからElasticsearchにデータを加工し投入する KibanaでElasticsearchで投入したデータを可視化する 1 . Logstashでデータを扱う Logstashをインストールします。 ドキュメント通りにインストールしても問題ないですが今回はhomebrewでインストールしました。 logstashがローカルの /usr/local/bin/logstash にインストールされたことがわかります。 https://www.elastic.co/jp/downloads/logstash $ brew install logstash $ which logstash /usr/local/bin/logstash では Logstashを実際に起動させて動作を確認していきましょう Logstashを扱うためにはLogstashにどんなデータに対して(input)どのように加工し(filter)どう出力(output)するかを設定するためのconfファイルが必要になるのでファイルを作りましょう。 ファイルはどこでも大丈夫なのですがここでは /usr/local/Cellar/logstash/6.2.4/bin 配下に logstash.conf を配置しました。 // どんなデータに対して input { stdin {} } // どのように加工するか filter {} // どう出力するか output { stdout { codec => rubydebug } } 上記を配置しますが今はlogstash.confの雛形をおいただけなので特に設定などは記していません。 実際に起動して確認していきましょう $ /usr/local/bin/logstash -f logstash.conf 成功すると最終行にこのような出力でコマンドラインが止まることを確認します。 Pipelines running {:count=>1, :pipelines=>["main"]} こちらの状態で文字列を入力してみます。 Pipelines running {:count=>1, :pipelines=>["main"]} Hello! { "host" => "host", "@timestamp" => 2018-06-17T11:38:44.370Z, "message" => "Hello!", "@version" => "1" } Logstash側から上記のレスポンスが返ってくることが確認できます。 Hello! というメッセージが messageのフィールドに存在していることがわかります。 それではlogstash.confでデータを加工してみましょう データの加工にはfilterを使います。 filterにも様々な加工を施すAPIが用意されております。 https://www.elastic.co/guide/en/logstash/current/filter-plugins.html この中で grok と呼ばれるapacheなどのログをフィルタリングするときに便利なfilterを使ってみます。 logstash.confのfilter内にgrokフィールドを追加します。 input { stdin {} } filter { grok { match => { "message" => '%{HTTPDATE:date} %{IP:ip} ' } } } output { stdout { codec => rubydebug } } 上記の設定で保存したらひとまず上記を実行してみます。 $ /usr/local/bin/logstash -f logstash.conf 立ち上がったことが確認できたら以下を入力します。 18/July/2018:19:38:00 -0700 183.60.215.50 18/July/2018:19:38:00 -0700 183.60.215.50 { "@timestamp" => 2018-06-17T15:31:18.991Z, "message" => "18/July/2018:19:38:00 183.60.215.50 ", "msg" => "Hello!", "ip" => "183.60.215.50", "@version" => "1", "date" => "18/July/2018:19:38:00 -0700", "host" => "WP3N-219" } レスポンスをみるとフィールドが増えていることがわかります。 入力したログをfilterで確認してデータを分割してくれているのです。 HTTPDATEには日付、IPにはipアドレス、DATAにはメッセージデータが入っていることがわかります。 このようにしてElasticsearchにデータを送る際に事前に加工することによってKibanaで可視化しやすくしたり Elasticsearchのマッピングに対応した値を投入することができます。 マッピングについては後ほど。。 2. LogstashからElasticsearchにデータを加工し投入する Logstashでデータを加工して操作することができました。次に加工したデータをElasticsearch側に投入していく準備をします。 Logstashにはデータを扱うためのAPIがデフォルトで様々なファイル形式やサービスに対して扱うことができます。 https://www.elastic.co/guide/en/logstash/current/input-plugins.html 今回はCSV形式のファイルでLogstashからElasticsearch側にデータを送る準備をしましょう 商品情報が入った product.csv というcsvを事前に用意します。 中身は id, title, description, manufacturer, priceのセルの列を持ったデータを用意します。 logstash.confを以下のように編集します // fileAPIを使ってpathのcsvを指定します。 // 起動したタイミングで読み込みたいのでbegining (デフォルトはend) // どこまで投入できたかどうかを覚えておくログを吐き出すパス(今回は設定しない) input { file { path => "~/Desktop/products.csv" start_position => "beginning" sincedb_path => "/dev/null" } } // 「,」でくぎられたところでカラムには一列目のセルの名前を入れます // mutateでカラムの型のタイプを指定したり、加工したり、フィールドに入るデータを制御したりすることができます。 // 今回は必要ないfieldを抜いて対応しています。 filter { csv { separator => "," columns => ["id","title","description","manufacturer","price"] } mutate { remove_field => ["@version","path","host", "tags", "message"] } } // ローカルの9200ポートでelasticsearchを起動しているのでhostsを向けます // indexにはRDBMSでいうDB名を指定し // document_typeにはRDBMSでいうテーブル名を指定します output { elasticsearch { hosts => "http://localhost:9200" index => "wp_products" document_type => "products" } stdout {} } 上記の設定ができたらデータを投入していきましょう! 先ほどと同じようにlogstash.confの設定を元にlogstashを起動してあげます。 $ /usr/local/bin/logstash -f logstash.conf するとたくさんのデータが投入されていることがわかります。 記事では一部データが投入されているところを記載しております。 ~~中略~~ { "title" => "kensington contour pro 17 notebook carrying case (62340)", "manufacturer" => "kensington", "price" => "89.99", "id" => "b0000dbmhr", "@timestamp" => 2018-06-17T10:38:45.141Z, "description" => "kensington 62340a contour pro 17 notebook carrying case - the contour pro 17 offers premium protection and exceptional comfort in one outstanding case. not only does its snugfit? protection system keep your notebook computer safe its contour panel and weight distribution system helps reduce weight impact by 35% so the case feels lighter. shock-absorbing contour shoulder strap is secure and comfortable convenient compartment for on-the-go access to aircraft tickets et al 1680 denier ballistic nylon protects against abrasions punctures and tears front pockets laid out for easy organization and access padded rear compartment keeps peripherals from poking through contour pro 17 notebook carrying case is backed by the kensington lifetime warranty." } { "title" => "now up-to-date & contact v5.0 (mac)", "manufacturer" => "now software", "price" => "0", "id" => "b000a7q6n8", "@timestamp" => 2018-06-17T10:38:45.142Z, "description" => "now up-to-date & contact is the only solution that addresses all of the problems that businesses and power users face. productivity is a lot like the weather. everyone talks about it but nobody does anything about it. staying organized is a continual struggle for most people. now you can manage all your appointments contacts notes and information. it's the solution for your busy modern life. best of all it's fully cross-platform -- it doesn't matter if you use macs or windows it looks and acts the same on both. professional rapid response via online support system automated faq and free email support" } ~~中略~~ 投入が終わり次第Elasticsearchでデータができているかの確認をします。 curl -XGET "http://localhost:9200/wp_products/products/_search?pretty" すると約1363件のデータが入っていることが確認できます。 { "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1363, "max_score" : 1.0, "hits" : [ { "_index" : "wp_products", "_type" : "products", "_id" : "cwtMDWQBLzsARdUAhfb1", "_score" : 1.0, "_source" : { "title" : "clickart 950 000 - premier image pack (dvd-rom)", "price" : "0", "id" : "b000jz4hqo", "description" : null, "manufacturer" : "broderbund" } }, 確認できたらKibanaでデータを可視化していきましょう 3. KibanaでElasticsearchで投入したデータを可視化する それではKibanaでデータを可視化していきましょう(5601ポートでKibanaを起動しています) 先ほど追加したindexの「wp_products」をManagementバーからindex_patternsに追加してあげます。 追加ができたらサイドバーのDiscoverから「wp_products」のインデックスが登録されていることがわかります。 登録が確認できたら早速ビジュアライズしていきましょう サイドバーのVisualizeを押してビジュアライズしたい図・表を選択します。 今回はPieチャートを選択します。 選択したらPieチャートが表示されていることがわかります。 このPieチャートに条件をつけていきます。 Priceの範囲(range)で分けてみよう Bucketsのsplit sliceからRangeでグルーピングして対象フィールドを「Price」にし範囲を設定します。 するとこのようにPieに色がつくことがわかります。 Priceに対してどのような商品があるのかTermsでIDを使って可視化してみます 先ほどのrangeに追加で商品ID(Terms)を追加してorderで件数を指定して実行してみましょう どのような商品が入っているかビジュアライズできたことがわかります。 このように様々な条件で図・表をビジュアライズ化し、ダッシュボードを作成することもできます。 x-packのKibana Canvasを用いるとリアルタイムでダッシュボードが更新されていくためこちらも今後は触ってみたいです。 以上でELKの流れを知ることができました。 補足(マッピングについて) LogstashからElasticsearch側にデータを投入する際にElasticsearch側では何もせずにデータを投入しました。 しかし、実際の業務に使うにはすごく危険です。 というのも wp_products のマッピングと呼ばれる(RDBMSでいうテーブル設計)を事前に定義しておかないと期待した挙動をしてくれないことがあるからです。 今回のデータでいうとpriceが当てはまります。 今回priceにはドルの単位の小数点を含んだ数字が入ります。 実際にマッピングを定義しないで投入したマッピングをみてみます。 $ curl -XGET "http://localhost:9200/wp_products/_mapping?pretty" "wp_products" : { "mappings" : { "products" : { "properties" : { "@timestamp" : { "type" : "date" }, ~~ 中略 ~~ "price" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256 } } }, ~~ 中略 ~~ } } するとpriceフィールドの型がtextになっていることがわかります。 これでは検索クエリでpriceのrangeを絞った場合に意図しない挙動を招きかねません。 そういう事態にならないためにも事前にテーブル定義のようにマッピングを作る必要があります。 小数点がつくマッピングにはpriceに対してfloatの型を指定してあげる必要があります。 今回はmappingに対して新しく追加された型 scaled_float を指定しています。 これはfloatで入ったデータに対してscaling_factorに記述している100を掛け合わせることで 整数値integerに変更するようにしています。これによってコンピュータがfloatにかかる検索処理時間を短縮し かつdisk容量も減らせることができるので検索時にもパフォーマンスがよくなるからです。 { "settings": { "number_of_shards": 1, "number_of_replicas": 0 }, "mappings": { "products": { "properties": { "id": { "type": "keyword" }, "title": { "type": "text" }, "description": { "type": "text" }, "manufacturer": { "type": "text", "fields": { "raw": { "type": "keyword" } } }, "price": { "type": "scaled_float", "scaling_factor": 100 } } } } } まとめ いかがでしたか?今回はElasticStack6のELKの基本操作の入門をしました。 プログラミング的要素は多くなく、とっかかりやすく理解もしやすい印象でした。 ElasticStack製品は設定がかなり細かくでき仕様が深くバージョンアップの頻度も高いため どんなデータに対しても対応策がかなりあり良い反面、知っていないと使えないツールなのでドキュメントを通じて今後も学んでアウトプットしていきたいです。 この度は、ご清覧頂きありがとうございました。 Wedding Parkでは一緒に技術のウエディングパークを創っていくエンジニアを募集しています。 興味のある方はぜひ一度気軽にオフィスに遊びにいらして頂ければと思います。 ブライダル業界のデジタルシフトを加速させるリードエンジニア候補募集!
アバター