ようへいの日々精進XP

よかろうもん

最近ギョームでやったこと (6) 〜 一歩踏み込んだ Web サイトの監視について考察して仮実装してみた 〜

tl;dr

お疲れ様です. かっぱです. YAMAP に入社してもうすぐ一年が経とうとしています. あっと言う間の一年でした. この一年の振り返りを... と思いましたが, 最近の悩み事を技術で解決してみようと思って試験的に実装した内容を紹介させていただきます.

そして, この記事は, YAMAP エンジニア Advent Calendar 2019 の 25 日目の記事になる予定です.

qiita.com

悩み事

YAMAP では, yamap.com 以外にもいくつかの外部サイトを運営しています.

これらのサイトのデプロイ作業を含む各種運用に携わっていますが, これらのサイトがデプロイの度にレイアウトがデグレしてたり, 意図しないような状態で表示されたりすることが多く発生していました. 原因はいくつかありますが, コンテンツが CVS の管理外であったり, コンテンツをオンラインで更新していて CVS にマージされていなかったり人為的な手違いによるものが多く, さらに, その異常に気付くことが出来なかったりして胃が痛くなる日々が続いていました. ということで, ざっくりと悩み事をまとめると...

  • 意図しないサイトのデグレが度々発生する
  • ソースコードとコンテンツが別々に管理されている (WordPress で記事はデータベース, テーマのソースコードCVS で管理) ので変更検知は 2 箇所になるので, 最後に信じられるのは実際に動いている環境になりがち
  • コンテンツ管理者とテーマの管理者が異なる
  • HTTP ステータスコードレベルでの監視では満たされてない

こんな感じです.

どんな風に解決しようと試みたか

下図のような実装を考えて実装しました. (雑に手書きですいません.)

f:id:inokara:20191225211402j:plain

流れとしては...

こんな感じ. 上図では Puppeteer ってなっているところは, 実際には Jasmine から Puppeteer を操作してスクリーンショットを取得して, 従来のスクリーンショットとの差分を比較する処理が動きます.

作ったもの

リポジトリ

github.com

こだわり

出来るだけ手を動かすことなく CircleCI 上で動かすことを考えました.

また, Puppeteer でサイトにアクセスしてスクリーンショットを取得して比較するコアな部分は最小限の実装に スクリーンショットを S3 にアップロード, ダウンロードする部分はコアとは別に AWS CLI (実際には CircleCI の orbs) で実装しました. 以下は .circleci/config.yml の抜粋です.

version: 2.1

orbs:
  aws-s3: circleci/aws-s3@1.0.11
  slack: circleci/slack@3.4.1

executors:
  default:
    docker:
      - image: circleci/node:12.4
      - image: circleci/python:2.7

commands:
  check_prepare:
    steps:
      - aws-s3/sync:
          from: s3://${ROOT_PATH}/
          to: /home/circleci/project/${ROOT_PATH}/
          overwrite: true
          arguments: >
            --delete
  npm_install:
    steps:
      - run:
          name: Update npm
          command: sudo npm install -g npm@latest
      - restore_cache:
          name: Restore Dependencies
          keys:
            - dependencies-{{ checksum "package-lock.json" }}
            - dependencies
      - run:
          name: Install Dependencies
          command: npm install
      - save_cache:
          name: Save Dependencies
          key: dependencies-{{ checksum "package-lock.json" }}
          paths:
            - node_modules

jobs:
  check:
    executor:
      name: default
    steps:
      - checkout
      - run:
          name: Install Headless Chrome dependencies
          command: |
            sudo apt-get install -yq \
... 略 ...
      - npm_install
      - check_prepare
      - run:
          name: Run test
          command: ./node_modules/.bin/jasmine
... 略 ...
      - run:
          name: Store Images
          command: |
            aws s3 sync /home/circleci/project/${ROOT_PATH}/ s3://${ROOT_PATH}/
          when: always
      - slack/status:
          fail_only: true
          mentions: ${SLACK_MEMBER_IDS}

workflows:
  version: 2
  byhand-webpage-check:
    jobs:
      - check
  triggerd-webpage-check:
    triggers:
      - schedule:
          cron: "3 * * * *"
          filters:
            branches:
              only:
                - master
    jobs:
      - check

また, CircleCI の triggers を利用して一時間に一回定期的に実行させるようにしています.

circleci.com

さらに, S3 へのアップロードと Slack への通知は orbs を利用しています.

github.com

github.com

ちょっと痒いところに手が届かないところ (run で利用出来る when 的な定義が書けないとか) が残念でしたが, 基本的な使い方であれば全然 orbs でいけるので最高ですな.

苦労したところ

Puppeteer (1)

何よりも Puppeteer です. 全く触ったことなかったので, 見様見真似で写経 (コピペ) していました. しかし, 以下のように簡単にサイトのスクリーンショットを取得出来るのには感動しました.

//
// https://github.com/puppeteer/puppeteer#usage より引用
//
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://yamap.com');
  await page.screenshot({path: 'test.png'});

  await browser.close();
})();

これを test.js あたりで保存して node test.js とすると, 以下のようなスクリーンショットがシュッと撮れます.

f:id:inokara:20191225214856p:plain

しかし, 相手は JavaScript, 一筋縄ではいきません. asyncawait がよくわからないまま書き進めてしまい, 一応, 思ったような動作になりました... これでは, 成長が無いのでもう少し asyncawait をはじめ JavaScript について理解を深めていきたいと思います.

Puppeteer (2)

ページ全体のスクリーンショットを取得したい場合, 以下のように screenshot メソッドの引数に fullPage: true をつけてあげると取得出来ることは確認しました.

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://xxxx.xxxx.com/');
  await page.screenshot({path: 'test.png', fullPage: true});

  await browser.close();
})();

ところが, サイトによっては, 下図のように画像が一部読み込まれず悩みました.

f:id:inokara:20191225235519p:plain

現在も解決出来ていません...

差分抽出

差分抽出には Resemble.js を利用しています.

github.com

この Resemble.js も Puppeteer 同様に以下のように簡単に画像の差分を抽出することが出来ます.

        resemble('after.png').compareTo('before.png')
          .ignoreColors()
          .onComplete(function(data) {
              fse.writeFileSync(DirName + 'diff.png', data.getBuffer());
          });

動いている様子

CircleCi でのテスト結果.

f:id:inokara:20200106104657p:plain

上記のように, 対象ページに差分があり, 差分許容率がしきい値を超えるとテストが Fail となります.

f:id:inokara:20191225221726p:plain

Fail したら Slack に上図のように通知されます. 通知が届いたら, S3 バケットに保存されている画像を確認して, 意図した差分なので, そうでないかをデザイナーさん等に確認する流れになることを想定しています.

f:id:inokara:20191225221116p:plain

また, テストと合わせて差分抽出画像が生成されます. 差分抽出画像は上図のような画像になります. 前回のスクリーンショットとの差分がピンク色に着色された状態になります.

参考

以下の記事を大幅に参考にさせていただきました. Puppeteer のことを 1bit もわからない自分でもなんとなく利用することが出来ました. ありがとうございました.

qiita.com

qiita.com

ということで

まとめ

Web サイトの監視について, 対象のコンテンツ差分をチェックするというアプローチを検討して実装してみました. 意図したコンテンツの変更についても検知してアラートが飛んでくる状況ですが, これはこれで外形監視上の変更履歴として利用出来るのではと考えています.

実装にあたり, 使った技術要素を見てみると, チェックのフレームワークとして Jasmine というテストフレームワーク (と言っていいのかわかりませんが), 実際の基盤としては CircleCI と出来合いのフレームワークを組み合わせることでシュッと仮実装まで持ってくることが出来ました. 技術の進化というものは本当に素晴らしいものですね.

最後に

YAMAP に入社して一年が経とうとしていますが, エンジニアとして, 自分なりの「新しい山を作ろう」で頑張っていきたいと思います. これからも宜しくお願い致します.

2019 年 12 月 24 日 (火)

ジョギング

  • すいません, 完全に怠けました
  • 足, 痛いです
  • 明日は少し走りたい...

ギョーム

  • デプロイとか
  • デプロイした後の確認を自動化したいなーと思って, Puppeteer 等を触り始める
  • 年末なのでタスクの整理をしたり...
  • またもや WordPress 案件で冷や汗をかく

クリスマスイヴ

ちらし寿司やらお刺身やら. 奥さん自家製のローストビーフを楽しみにしていたのに... 焼き肉になっててまたのチャレンジを楽しみにしております.

#鹿児島30K 振り返り

tl;dr

フルマラソンの練習会的な位置づけで全国各所で開催されているらしい「◯◯ 30K」シリーズの鹿児島開催版である「鹿児島 30K」で 30 キロ走ってきたので振り返りたいと思います.

runners.30k-series.com

どんな大会なのか

runners.30k-series.com

30km走はマラソンの1カ月前の必須トレーニング。マラソンシーズンで「自己ベスト更新」「初完走」を狙う人にとって本番へ向けた「脚づくり」とシミュレーションにぴったりです!

上記のページに記載されている通り, レースではなく練習会的な位置づけとなるイベントのようです. とは言え, 今年はフルマラソンを一回も走っていない自分にとっては少し距離が短いマラソン大会という位置づけで挑みました. また, 後付となりますが, 雨風のレースに対して激しい苦手意識があったので完走することでその苦手意識を少しでも払拭出来ればと臨みました.

鹿児島 30K で走ってきました。

とは言え...

前日からの雨, さらにスタート直前になって強くなる風と雨...棄権したい気持ち一杯でしたが, とりあえずいけるところまで走ろうと思いスタートしました. 目標は 1 キロ 4 分ペース. 30 キロで 2 時間を切れれば最高だと思っていましたが, ひとまずフルマラソン 3 時間切りを目指す人達に用意されたペースランナーの後ろについて一周 3 キロのコースを 4 分 10 秒 /Km 程度でペースを刻みます.

f:id:inokara:20191223234710p:plain

寒い

冷たい雨と強い風が体温を奪っていきます. 普通は走れば体が温まりますが, 今回は走っても走っても体は温まらず足先は冷えてしまい接地の感覚すら無い状態で走り続けます. 動かない体を無理やり動かそうとするので呼吸は上がってきて普段のジョッグと比較すると当然ですがキツさを感じました. また, 想定していたよりもアップダウンがあり, 特に 1.5 キロ過ぎの 200m くらいの上り坂は体力を奪い, 乳酸の高まりを感じました.

途中で摂取したのは塩飴 1 個とエナジージェルを 2 つ, 給水地点でのスポーツドリンクと水. 結局, 足が攣ってしまったので 2Run を摂っておくべきだったと反省していますが, 2Run 無しで 20 キロを超える距離を走れたことは小さな自信となりました.

また, 今回のレースでは HOKA ONE ONE の RINCON で走りました.

f:id:inokara:20191224002808p:plain

履いて走った感じをもう少しレポート出来れば良いのですが, 先述の通り足先が冷たく感覚が無い状態でしたので何となくクッションが効いているなーという感覚しかありませんでした. NIKE の ZOOM FLY3 あたりで感じる反発力はあまり感じませんでした. きっとカーボンプレートの有無なのかな.

ラスト一周

本来であれば, 後半の 5 周はペースアップしていきたかったところですが, 結局ラスト一周までは体力を温存, ラスト一周をやっと目標としていた 4 分 00 秒/Km で走りきることが出来ました. 今回は 2Run を一回も摂取せずに走ったので案の定, 足はピキピキ攣りまくりながら走りました.

ということで

苦手意識のあった雨風の中で完走も出来て 30 キロを 2 時間ちょっとで走ることが出来たことは良かったと思います. また, 最後の一周は 4 分 00 秒/Km くらいまでビルドアップ出来たのは良かったと思います. しかし, フルマラソンを 2 時間 50 分切りを目指している状況からすると, 終盤の体の重さと足攣りは相変わらずで, フルマラソンの場合の残り 12 キロをどう走るのか 2 月の別大までに自分なりの答えを見つけたいと思います.

冷たい風雨の中でスタッフのように働く父。

最後に練習会という位置づけながら一生懸命応援してくれた奥さんや父, 食事面のサポートをしてくれた母, あの過酷な状況でレースをサポートしてくださったスタッフの皆さんには本当に感謝しています. ありがとうございました.

2019 年 12 月 23 日 (月)

ジョギング

  • 足がパンパンでお休み
  • いつも痛い箇所が... 痛くなる... 辛い

福岡に帰る

  • 午前中は自宅で業務, Kibana 統合をやったり, AWS の新サービス (プラン) を駆使して EC2 の利用料をさらに下げる算段をしたりしていた
  • 午後からお休みを頂いて福岡に車で帰る... きつかった〜

2019 年 12 月 22 日 (日)

ジョギング

runners.30k-series.com

鹿児島 30K で 30 キロ走ってきた. 腹が立つくらいの風雨と寒さで DNS (Did not start 棄権) も考えたけど, 雨が降っている時の苦手意識を払拭したいという思いもあったので出走. 案の定, 雨は冷たいし風は強いしで体は冷えていく一方で接地の感覚もなく完走出来るか不安になりつつもなんとか完走することが出来た.

結果は以下の通り. 目標としていた 4 分ペースで走り切ることは出来なかった (早々に諦めた) けど, 無難にサブスリー圏内で走り切ることが出来た.

f:id:inokara:20191223233217p:plain

父の PC が壊れる (2)

結局, 鹿児島のアプライドで中古デスクトップ PC を購入. Core i5 にメモリ 8GB, SSD で予算内におさめることが出来た. 早速, セットアップを行い, 難なく移行が出来たので本当に良かった.

夕飯

  • 母の鍋, お出汁が美味しかった
  • お腹一杯

2019 年 12 月 21 日 (土)

ジョギング

  • 山王公園を軽く 45 分
  • 懸垂 x 8
  • 右足の大腿裏は鍼や奥さんのマッサージでなんとか落ち着いてきたけど, 走っていて最初からなんとも言えない疲労

移動

  • 鹿児島へ移動
  • 今回はなんとなくあっという間についた感じだった
  • 途中から雨が降り出して, 明日のレースが憂鬱になった

父の PC が壊れる

  • 今回の帰省の目的の一つだった, 父 PC の確認
  • [ キーが何もしていないのに PC 起動時から勝手に入力されるので, ログオンするのにも一苦労
  • 年金暮らしの身に大変申し訳ないのであるが, 新しい PC を購入することを薦める

夕飯

明日は...

  • せめて雨がやんでくれると嬉しい

2019 年 12 月 20 日 (金)

ジョギング

  • お休み *さらに引き続き, 右太ももの裏が... 強い張りで辛い

ギョーム

  • ウィークリーレポートを書いたり
  • 来季の目標を考えたり
  • Kibana 環境を ECS + Spot Instance 化したりする為に Terraform を書いていた

Carbon X

HOKA ONE ONE の Carbon X をキャナルシティのゼビオで発見!試し履きしていい感じで一瞬グラついたけど我慢した.

2019 年 12 月 19 日 (木)

ジョギング

  • お休み
  • 引き続き, 右太ももの裏が... 強い張りで辛い

ギョーム

  • ECS の Optimized AMI の更新作業, やっぱり手作業は辛かったので自動化を検討したい
  • Terraform の aws_ssm_parameter という Data Source がすごく自分のニーズに合ってて良かった
  • Elasticsearch のシャードがノードに割り当てられていない問題...大変ごめんなさい事案だった
  • Kibana サーバーを統合する作業

YAMAP Night

スタッフとして参加した.

2019 年 12 月 18 日 (水)

ジョギング

  • お休み
  • 右太ももの裏が... 強い張りで辛い

ギョーム

  • 振り返り業務等
  • 気になっていたシステムのインスタンスの縮退等
  • 先日の Elasticsearch シャードが割り当てられていないインデックス問題に進展あり, 最悪の結果に辛い

望年会

  • 司会業務
  • 料理をほとんど食べることが出来なくて辛かった
  • それなりに盛り上がったので良かった!(と思う

Serverless Days Fukuoka 2019 の思い出 〜 愛・忘れもしません 〜 #serverlessfukuoka #ServerlessDays

tl;dr

fukuoka.serverlessdays.io

Serverless Days Fukuoka 2019 の運営に携わる機会を得たので色々と振り返りたいと思います. カンファレンス全体の詳しい内容については俺たちの清家さんの記事をご一読下さい.

blog.seike460.com

更に, どんなセッションがあったとか参加された方の詳しい記事を待ちたいと思います. 自分は運営に携わることが出来たおじさんとしてのざっくりとした内容をダラーッと書いていきたいと思います.

https://lh3.googleusercontent.com/0rf0r2bMQQeQGmy7NUZcmvmlQ0cMOJOEaKlXu_zEGQvLSZsbWK6-5TJdaKRxpTErLxNLtJU6OpfvNCTpgkHA3uxsfQl1wRSTVLNT6EvRoX36Lxxg_qsVD4T2ziQdkctb6lvVt-HnPA=w3116-h2082-no

参加してくださった皆さん, ワークショップ会場を提供してくださったエンジニアカフェさま, カンファレンス会場を提供してくださった LINE Fukuoka さま, 各スポンサーさま, 皆様のおかげで大盛況のうちに終了することが出来たと思います. 本当に有難うございました. そして, 何も文句言わずに送り出してくれた奥さん, ありがと.

そして, この記事は, YAMAP エンジニア Advent Calendar 2019 の 18 日目の記事になる予定です.

qiita.com

準備

会場

会場探しは 2019 年 6 月くらいから... 清家さんと色々と訪問させて頂いて最終的に LINE Fukuoka さんに決まった. FGN の関係者の皆さん, LINE Fukuoka のきしださんには大変お世話になりました. 色々と無理を聞いてくださり本当に感謝しかありません.

T シャツ等の準備

今回, セッションの内容もさることながら個人的にもっとも勉強になったのはカンファレンスオリジナル T シャツやタペストリー等を業者に手配する方法でした. 今までオリジナルの T シャツを作るなんて経験したこともなく, とても敷居の高いものだと思っていましたが, 実際にやってみると簡単. デザインが固まっていればインターネット経由で入稿してポチポチするだけ. でも, 初めての体験でとても勉強になりました.

https://lh3.googleusercontent.com/NAAYKHbh-1im63-8SGC0hGpgL1dasTZHyjFt5Yjrxzozh5DMvg8KwbtyHdBo8eXjf_NG4O1p-lHPKO_CcyUPPiMgTPx-BYHJH2vDrYRBKv-xy_Kwf1SMY82cYRL5iXj4cEvfe3KyvQ=w1180-h2098-no

ちょっと YAMAP のロゴが隠れてしまっていますが, 発注したタペストリー. いい感じですなあ.

スポンサー

スポンサーさんを集めるということも初体験でした. 結果として, 自分が所属する YAMAP にもコーヒースポンサーとして協力を仰ぐことになりましたが, 自分達がどのような志 (こころざし) を持ち, イベントをどのようなものにしたいのかをスポンサー企業に説明して理解して頂き協力していただく難しさを身を以て体験することが出来ました.

コーヒーの手配

LINE Fukuoka さんが入っているオフィスビルの 1 階にはみんな大好きスターバックスが営業しています. 今回はここからコーヒーを手配しました. 灯油のポリタンクサイズの入れ物に三十人分のコーヒーがあっと言う間にはけていきました. スターバックスの店員さんにはとてもよくして頂いてこれから街角でコーヒーを飲みたくなったらスターバックスを利用したいと思います.

YAMAP 印のキットカット

YAMAP はコーヒースポンサーとして協力させて頂きましたが, コーヒーのお供にヤマップロゴ入りのキットカットを提供させて頂きました.

整いました。#serverlessfukuoka #yamap55

これは, YAMAP のデザイナーである「のいさん」に協力頂いてデザインして頂いて入稿, 発注まで手配して頂きました. ありがとうございました.

本番

Day1 はワークショップ. 自分は会場担当として運営に携わりながら手を動かせるところは密かに手を動かしていました.

Day2 はコーヒースポンサーとしてコーヒーの準備, 会場設営, タイムキーパーとしてバタバタしながらもセッションに耳を傾けることが出来ました.

なんだか一年分くらいツイートしまくって充実したカンファレンスでした.

ということで

皆さん, 大変お疲れ様でした.

Serverless とは Lambda や Cloud Function を指すのではありません. 文化なんです, 文化. 〜 詠み人知らず 〜

(完)