読者です 読者をやめる 読者になる 読者になる

オールアバウトTech Blog

株式会社オールアバウトのエンジニアブログです。

SeleniumでE2Eテストを始めよう

selenium テスト

オールアバウトのsankameです。

突然ですが、Webブラウザのテストってやりずらいですよね…

ブラウザ毎に挙動が違ったり、結局、担当者頼みになって、その人が代わったらまた障害が起きて…と。

エンジニアならこういう問題はさっさと自動化してスマートに解決したいものです。

※このアプリのソースコードはページ末尾のリンクからご利用下さい。

…とは言え、「なる早でリリースを」という場面ではテストは後回しにされがちで、さらに第2フェーズが始まると尚更テストに時間を割くのが難しくなります。(※1)

オールアバウトではユニットテストがまだ全アプリに入っていない状況ですが、とにかくハードルを下げつつ半年ほど前からE2Eテスト(※2)を実施しています。(※3)

(※1) オールアバウトには専任のQAチーム (Quality Assurance)がないため、開発エンジニアがそれを担当しています。

(※2) End to End Test (ネットワーク終端ホストによるテスト)。Webブラウザによる利用者視点でのテストと解釈。

(※3) Google社によると、本来は[ユニットテスト数] > [E2Eテスト数]とのこと…参考

ハードルを下げる

テストのハードルが高いのは、プロダクションコードと合わせてテストコードを書くのでコード量が倍になる点だと思います。

その負担を減らすため、まずはJavaScript (以下JS)エラーをチェックすることに特化したテストアプリを作成し、毎日動かすことにしました。GitHub URL

プラットフォーム

下記の理由でE2EテストにはSelenium + Javaを採用しています。

  • ネット各社の導入事例など、利用実績が多い。

  • Seleniumは十数年の歴史がありながら、最近でも比較的活発にメンテされている。参考

  • SeleniumW3Cで標準化が検討されている。参考

システム構成

準備

  • CentosWindowsで予めSeleniumサーバーを起動しておきます。

    (パスやバージョンは適宜読み替えて下さい)

    $ java -jar /usr/local/src/selenium-server-standalone-2.41.0.jar -role hub

    C:\apps\selenium>java -jar selenium-server-standalone-2.42.2.jar -role webdriver -hub http://【CentosのIP】:4444/grid/register

構成図

f:id:allabout-techblog:20160811023523j:plain

※図中のロゴは右のサイトより使用。 Selenium HQ さくらのナレッジ

処理フロー

○数字は上記の図に対応

  • ① アプリがテスト対象のURLを読み込みます。

  • ② アプリからWebDriver APIをコール。

  • ③ 2つのSeleniumサーバーを中継します。

  • Windows側のSeleniumサーバーがブラウザを起動し、テスト対象のURLにアクセス。

  • ⑤ アプリは発生したJSエラーをWebDriverから受け取り、メールで通知します。(JSエラー収集はライブラリを使用)

特記事項

  • 将来的にテスト対象のブラウザを増やしたり、テストの並列化ができるように、③でSeleniumサーバーをhubとnodeの関係にしました。

  • アプリはJenkinsで定期実行していましたが、Wercker+Deployerに移行しつつあるため、現在は暫定でRundeckから行っています。

  • WebDriverが対応しているブラウザのバージョンは決まっているため、敢えてブラウザの自動アップデートは止めています。

エラー修正

上記⑤のメールは開発者のメーリングリストに送信しています。

メールで送信するだけだと、そのエラーに誰が対応していてどんな状態なのか管理しずらいので、Google Apps Scriptを使ってGmailからGoogleスプレッドシートに自動で読み込ませるようにしています。

f:id:allabout-techblog:20160811234215j:plain

このシートをもとに、毎週の定例会議で「修正担当者をアサイン」→「進捗確認」を繰り返しています。

まとめ

次のステップとしては、リンクの飛び先や画面要素のチェックなど、Selenium本来の機能を活かしたテストコードを書いていく予定です。

また、現在は1日1回のテストを定時に実行しているだけなので、リグレッションテストという意味ではまだ不十分です。

将来的にはCIツールと連携して、ステージングにWebアプリをデプロイしたタイミングでE2Eテストを回したいと考えています。

ソースコード

プルリクなど頂ければ幸いです。

github.comGitHub - allabout/js-e2e-test: Selenium WebDriverを用いてJavaScriptの自動テストを行います。

Wercker+deployerで始める新リリース生活

リリースフロー wercker deployer DevOps

@takkyです。
前回私が執筆した記事では、オールアバウトのリリースフローの変遷について説明しました。

allabout-tech.hatenablog.com

FTP手動アップロードの時代からJenkinsやWerckerを使用したフローへと今では変わっています。
前回の記事では、WerckerやDeployerについての説明や実際の運用方法などは触れていなかったため今回の記事で紹介します。

Werckerとは

wercker.com

WerckerはCIツールの一種です。buildやテスト・デプロイを自動で行ってくれるものです。
よく他社で使われているツールにはcircleciTravis CIなどがあると思いますが、それと同様のツールだと捉えてください。
ちょうど検証が終わったころにBitbucketもBitbucket PipelinesというCIツールを出しましたが、 機能が少なく導入は見送っています。

bitbucket.org

Werckerの特徴的な機能

wercker.ymlで動作設定

Wercker.ymlというファイルをアプリケーションのリポジトリに含むことで、そのアプリケーションに対してどういうことを実行するか設定することができます。

コンテナベース

今流行のDockerコンテナをベースにしています。
本番環境と同じ環境でテストを回すことが可能です。

Wercker Workflows

CIで実行する項目をpipelineという単位で作ることができ、 そのpipelineを直列や並列につなげて実行することができるようになります。 例えばPHPLintでPHPの構文チェックを行うpipelineの後にPHP UNITでテストを行うpipelineをつなげることができます。 今までのWerckerではPHPUnit単体でのRetryはできなかったのですが、Workflowsにより、各pipeline毎にRetryができるようになっています。

Wercker Workflowsの日本語ではこちらのQiitaが詳しいです。

qiita.com

豊富な拡張機能

Wercker RegistryというWerckerのpipelineで使用できる便利なメソッドストアのようなものが用意されています。
例えばS3にファイルをアップロードするstep(pipelineを構成する最小要素)が用意されており、これを使用することで簡単にS3にアップロードすることが可能です。

Werckerのいいところ

何よりBitbucketに対応しているところ
弊社ではBitbucketを使用しているのですがBitbucketと連携ができるCIツールは少ないです。
その中でもわりと機能が豊富で無料で使えるためWerckerを採用しています。

Werckerの悪いところ

全世界でキューを共通利用しているようでたまに重くなることが有ります。
課金すれば解決できるようですが...
また、ユーザの管理の仕方もちょっと貧弱だとは思います。

とはいえ、無料で使えますし、Bitbucketに対応していてコンテナベースである。ということで採用をしています。

Deployerとは

DeployerとはPHP製のデプロイツールです。

CapistranoFabricPHP版という認識でOKです。
なお、Twitterが提供しているモバイル向けSDKFabricという名前なので注意が必要です。

Deployerの特徴

・taskというリモートサーバで実行したい機能単位で実装がされています
PHPで書かれています。PHPを書いたことがあるエンジニアであれば、機能の追加が容易です。
・基本的なフレームワークはレシピという形でデフォルトでありますし、rsyncのレシピなども容易に追加が可能です。

Deployerのいいところ

PHPで書かれているので、開発者がリモートサーバで実行したいタスクを書くことができる点。
いままでは、インフラのリリーススクリプト作業待ちで止まることもあったのですが、開発者が準備することができます。
・リリース毎にシンボリックリンクが貼り変わるデプロイが可能です。
シンボリックリンクの張替えのみで済むのでRollBackが高速に行えます。
(以下はLaravelをシンボリックリンクデプロイした例です)
f:id:allabout-techblog:20160704110653p:plain ・書き込み権限を与えるディレクトリや、共有ディレクトリとしたいディレクトリの設定が容易であり、configで追加すれば設定が完了となります。

Deployerの悪いところ

・あまり情報がないところ。 Deployerという名前自体が一般化されているからか検索で引っかかってくる情報が少ないです。
PHPで書かれているのでDeployer自体のソースを読めばいいんでしょうけど。

オールアバウトでの Wercker + Deployerの運用方法

Wercker+Deployerでのフローでの開発チームとインフラチームの役割分担は以下のようになっています。 開発チーム: アプリケーションの開発、WerckerとDeployerの設定、リリース作業
インフラチーム: サーバの準備、鍵交換

以下がかなり簡略化したWercker+Deployer+アプリケーションの関係図になります。

f:id:allabout-techblog:20160704114941p:plain

オールアバウトではGCEにDeployerサーバを構築して、各Applicationのサーバと鍵交換をしてssh接続ができるようにしておきます。
インフラチームは各サーバの準備及び鍵交換をしておきます。 (必要あればVPN等で異なるクラウド上接続できるようにしておきます) 。
開発チームはWerckerとDeployerの設定をしアプリケーションをリリースすれば良いので、どのサーバにリリースすればよいのか悩む必要がありません。

Werckerの設定

社内のボイラープレート集に wercker.ymlを追加しておきます。
開発者はそのファイルをベースにwercker.ymlに設定を加えていきます。
基本的にはボイラープレートのwercker.ymlをそのまま使えるようにしてあります。
その後プルリクエスト、マージの流れになります。

Deployerの設定

Deployerの設定用のリポジトリを用意しておきます。
オールアバウトで使用しているメインのフレームワークはCodeIgniterとLaravelがあり、それぞれに対応したサンプル設定を用意しておきます。
開発者はサンプルをコピーして、リリースのテストを行い、その後プルリクエスト、マージの流れになります。

インフラチームは面倒なアプリケーションリリースの設定を行う必要がなくなりますし、開発者はインフラチームの作業待ちがなくなります。
また、リリース先のサーバがDeployerの設定で決定しているためリリース先サーバに悩むこともありません。

DeployerのTIPS

rsyncデプロイ

f:id:allabout-techblog:20160620171628p:plain オールアバウトのリリースフローの変遷 - オールアバウトTech Blog 上の図や前回の説明を見ていただくと分かる通り、rsyncでデプロイを行っています。

Deployerのレシピ はrsyncのレシピを提供しています。
しかし、このrsyncのレシピはDeployerサーバ上のファイルと対象サーバのファイルをrsyncするレシピになっています。
弊社ではPF環境と本番環境のファイルをrsyncしたく、PF環境とDeployerサーバは別サーバのため工夫が必要になります。

そこで、rsyncをremoteのファイルを対象にできるようにtaskを追加して対応しました。

// 指定したサーバでrsyncする。
task('rsync_remote', function() {
    $config = get('rsync');
    $src = env('rsync_src');
    $dst = env('rsync_dest');
    $server = \Deployer\Task\Context::get()->getServer()->getConfiguration();
    $host = $server->getHost();
    $port = $server->getPort() ? ' -p' . $server->getPort() : '';
    $identityFile = $server->getPrivateKey() ? ' -i ' . $server->getPrivateKey() : '';
    $user = !$server->getUser() ? '' : $server->getUser() . '@';
    $source = input()->getArgument('source');
    $current = $src;

    $source_server = \Deployer\Deployer::get()->servers->get($source);
    run_remote("rsync -{$config['flags']} -e 'ssh$port$identityFile' {{rsync_options}}{{rsync_excludes}}{{rsync_includes}}{{rsync_filter}} '$current/' '$user$host:$dst/'", $source_server);
})->desc('Rsync remote->remote');

run_remote

/**
 * Run command on remote server.
 * 指定したremote serverでコマンド実行する。
 * depoyerであるrunコマンドに引数追加で作成。
 * @param string $command
 * @return Result
 */
function run_remote($command, $server)
{
    $command = env()->parse($command);
    $workingPath = workingPath();

    if (!empty($workingPath)) {
        $command = "cd $workingPath && $command";
    }

    if (isVeryVerbose()) {
        writeln("<fg=red>></fg=red> $command");
    }
    $output = $server->run($command);
    if (isDebug() && !empty($output)) {
        output()->writeln(array_map(function ($line) {
            return "\033[1;30m< $line\033[0m";
        }, explode("\n", $output)), \Symfony\component\Console\Output\OutputInterface::OUTPUT_RAW);
    }

    return new \Deployer\Type\Result($output);
}

deployerにもともとあったrunを使用してrsyncをしようとすると、deployerサーバとデプロイ先のサーバでrsyncをしてしまいます。
今回はサーバAとサーバBでデプロイを行いたいために、指定したremoteサーバでrunができるような関数を実装した後、 rsyncするときもrun_remoteでリモートサーバ同士でrsyncできるようにしています。

Laravelの.envをデプロイされる環境ごとに変更したい。

同一アプリをAWSとGCEに展開して、環境変数AWS/GCE毎に変更したいということがあると思います。
Laravel4では、 .env.{環境名}でできたのですが、Laravel5以上ではどうやら行けないようです。
Deployerで、特定のスクリプトを叩くようなtaskを追加して、そのシェルスクリプトで.envのシンボリックリンクを張り替えるようにしました。シェルスクリプトは以下のような感じでさくっと書いています。

envの配置

   .env -> .env.AWS
   .env.AWS
   .env.GCE
#!/bin/sh
cd $1/current
echo "########## make symlink #########"

HOSTNAME=`hostname`
# env存在チェック
if [ ! -e .env ]; then
    case $HOSTNAME in
        AWS* ) ln -s env.AWS .env
        echo "make symlink to env.AWS";

        # それ以外
        * ) ln -s env.GCS .env
        echo "make symlink to env.GCS";
    esac
    exit 0;
else
    echo "Could not make symlink"
    exit 1;
fi

deployerで特定のシェルスクリプトを叩くのに以下のようにtaskを定義します。

// envを作成
task('make:symlink', function() {
    run("sh {{deploy_path}}/current/symlink.sh {{deploy_path}}");
})->desc('make_symlink');

その後 main taskにmake:symlinkを追加します。

task('deploy', [
    'deploy:prepare',
    'deploy:release',
    'make:symlink',
])->desc('Deploy your project');

まとめ

DeployerはPHPで書かれているので開発者が容易に拡張可能です。
また、設定ファイルをgitを使用していれば属人化しにくくなると思います。

ただ最近の悩みとしてWercker含むSaasを数多く使っているのですが、 Web上で設定したものの使い回しやバックアップができないという点があります。
Werckerのweb上で設定したものをxmlなどからもimportできるともっと使いやすくなるのですが・・・

レガシーな広告配信システムをリプレイスした話

広告

こんにちは。オールアバウトの@naga1460です。

オールアバウトの代表的なメディアである「All About」の広告配信システムは、 ここ1年で大きく変わりました。

All About初期からのレガシーシステムをどうリプレイスしたのか、紹介したいと思います。

All Aboutの主な広告

まず前提知識として、All Aboutで扱っている代表的な広告を簡単に説明します。

※ここに挙げたもの以外の種類もありますが、今回は割愛します。

純広告

All About内の特定のページの特定の枠に配信する広告です。

リンク先は基本的には広告主様(以下クライアントと表記)のサイトとなります。

ネットワーク広告

こちらに関しては説明不要かと思います。

All Aboutでは、純広告が配信されていないページにネットワーク広告を配信しています。

タイアップ広告 + 誘導広告

タイアップ広告は、商品・サービスの情報を取り上げた記事風広告です。

All About内の他のページからタイアップ広告ページへ誘導する広告の配信も同時に行います。

※誘導広告用の枠と、純広告・ネットワーク広告用の枠は全く別の配置となっています。

All About初期の広告配信システム

初期システム構成図

f:id:allabout-techblog:20160725190501p:plain

初期システム概要

All About初期の頃は、図のように旧自社製アドサーバーが全ての広告を配信していました。

このアドサーバーはつい1年程前まで現役で使い続けていたのですが、 15年以上前に設計、構築されたシステムということもあり、問題点を多く抱えていました。

広告配信の所要時間が膨大

広告の配信開始日に、純広告、タイアップ広告 + 誘導広告全てを一斉にオンプレファイルサーバーへアップロードしていたため、 1回の配信に1時間以上かかることも少なくありませんでした。

クリエイティブの履歴管理が手作業

旧アドサーバーでは、クリエイティブの差し替えは行えるのですが、 いつどのクリエイティブを配信していたのか、といった履歴がシステム上で管理されていませんでした。

よって、入稿者が手作業でフォルダ管理する運用となっていました。

拡張性ゼロ

ASPという言語でフレームワークも使わずに構築されていたので、システムの拡張性はほぼありませんでした。

その他にも...

  • プレビュー環境と本番環境に差があり、正確なプレビューが不可能。
  • 仕様をきちんと把握している人がおらず、障害対応の難易度が高い。
  • 15年以上をかけて継ぎ接ぎなシステムとなってしまっている。
  • 不要となっている機能も多い。

等、挙げるとキリが無いのでこのあたりで止めておきます。

現在の広告配信システム

現システム構成図

f:id:allabout-techblog:20160725181050p:plain

現システム概要

流石に初期のシステムをこのまま使い続けるには限界がある、ということで、ここ1年程かけてアドサーバー全面リプレイスを決行しました。

現在は上記の図のような構成となっています。

初期システムから大きく変わった点としては、今まではアドサーバー1つで全ての広告配信を行っていたところを、

  • 純広告:DFP*1を利用して配信。
  • タイアップ広告 + 誘導広告:専用に新規構築したアドサーバーで配信。

というようにシステムを切り分けたところです。

それぞれのシステムについて詳細を説明していきます。

DFP(純広告 / ネットワーク広告)

純広告の配信システムとして、DFPを導入しました。

また、今まではフロントアプリ側に実装していたネットワーク広告配信処理もDFP側で巻き取ることとなりました。

DFPを導入した理由は、

  • フルスクラッチ開発よりもコストが低い。
  • 想定IMPがDFPの無料範囲内に収まる。
  • Googleのプラットフォームを使うことによるネットワーク広告収益最大化を狙う。

等です。

実際、DFP導入によりネットワーク広告収益増加の恩恵を受けられているため、当初の思惑通りとなっています。

(実際には、自社製案件管理システムとDFPの間に自社製連携用アプリがあるのですが、今回は説明を簡単にするため省略しています)

新自社製アドサーバー(タイアップ広告 + 誘導広告)

タイアップ広告 + 誘導広告の配信システムは、純広告側とは違い全てフルスクラッチで構築しました。

こちら側にDFPやその他既存のアドサーバーを導入せずフルスクラッチとした理由は、

  • タイアップ広告 + 誘導広告のIMPを合わせるとDFPの無料範囲を超えてしまう。
  • タイアップ広告と誘導広告はセットで管理したい。
  • 独自の配信ロジックを使いたい。

等です。

新規構築したアドサーバーには大きく分けて2つの機能があります。

  1. タイアップ広告の入稿・公開
  2. 誘導広告の入稿・配信

実際には、誘導広告の配信部分は単体の配信システムとして切り出されており、 機械学習を使ってタイアップ広告への誘導を最適化しています。

具体的には、誘導広告のクリエイティブの実績CTR等を元にクリエイティブと配信先ページの相性を判別し、クリエイティブと相性の良いページに絞って配信、といったロジックになっています。

このあたりは次回以降の記事で詳しく紹介したいと思います。

新自社製アドサーバーでは今までの問題点の解決、最適化を下記の通り実現しました。

広告配信の所要時間が従来の20%以下に

広告クリエイティブを入稿の時点でS3*2にアップすることにより、 配信開始日の一斉アップロードが無くなり、配信の所要時間が従来の20%以下となりました。

(純広告の配信をDFP側に切り分けていることも、高速化の要因の一つとなっています)

クリエイティブの履歴管理が可能

新システムではクリエイティブにバージョンの概念を導入し、 いつ、どのクリエイティブを配信したのかをシステム上で管理可能となりました。

これにより、入稿者が手作業でファイル管理をする必要が無くなりました。

拡張性が高い

拡張性を考慮し、PHPのモダンフレームワーク(Laravel 5)を採用して構築しました。

おかげで新アドサーバーのリリース以降、レガシーな旧システムではやりにくかった拡張が、次々と行えるようになりました。

リプレイスしてみて

15年以上使っていた広告配信システムのリプレイスは非常に大変でした。

初期システムの仕様をきちんと把握している人がいない中で仕様調査や関係者へのヒアリングを進め、 新システムへの要望と突き合わせた上で必要な機能、不要な機能を洗い出していきました。

また、システム構成図にあるような社内システムとの連携も要件として含まれており、 単なる新規構築よりも難易度の高い案件でした。

結果として、レガシーシステム群の中で広告配信システム部分のみ切り離す形となりました。

ただ、オールアバウトでは全体としてレガシーシステムをリプレイスする方向で動いており、今回の広告配信システムはその一環でもありました。

このリプレイスを通して、1つ1つ地道にレガシーシステムを改善していき、新しい施策に対応できるようなシステムにしておくことが、 メディアの成長において重要であると実感しました。

*1:Googleが提供するアドサーバー。

*2:Amazon Web Servicesが提供するオンラインストレージサービス。

第1回 開発合宿@伊東に行ってきました

開発合宿

f:id:allabout-techblog:20160722095433j:plain

こんにちは!オールアバウトの@C058です。
7/9(土)-7/10(日)にかけて行った開発合宿に行ってきました!

開発合宿に参加したメンバーは私含め12名。
「合宿目標を明確にする」「2人1組チームを編成する」「事前準備をしっかりする」という3つを意識したところ、
初の開発合宿は無事成功となりました。
今回は、予想以上に良い結果となった開発合宿の様子と成果・まとめを書きます。

続きを読む

AMPでサクサク記事表示

AMP

オールアバウトのsankameです。

AMP(※1)で快適に記事を読んでほしい!ということで、最近始めたAll About NEWSというメディアでAMP対応してみました。

(※1)スマホページを高速化するためのHTMLサブセットで、Google社などが推進。

メリット

AMP対応することで下記のメリットを享受できます。

  • ユーザーは高速化されたWebページで記事を読むことができる

  • メディアはAMP用のカルーセルから自社サイトへの流入を見込める

    (Googleで特定のニュース系キーワードを検索するとAMP用カルーセルが表示されます)

方針検討

実現方法を検討します。

外部サービス利用?

外部のクラウドサービスでAMP用ページを生成してくれるものもありましたが、自社で細かい表示制御をしたかったので、今回は見送りました。

CMS or フロント?

f:id:allabout-techblog:20160711004941j:plain

All Aboutの記事データは過去からの経緯でHTMLタグが含まれています。

AMPで使用できるHTMLタグは制限があるため、CMSからAMP用の記事を再入稿する案もありましたが、改修・運用コストを考慮し、今回はフロント側のみで対応することにしました。(Web Serverの手前にCache Serverがあるため、ある程度のフロント処理は許容)

AMP用に上図 (a)、(b) の処理を追加しました。

リクエストURLのドメイン名を参照して「通常記事」と「AMP記事」のどちらを返すか決定します。

実装

最初、下記ページなどに従って、HTMLタグを変換する処理を入れては、都度バリデーターでチェックしていきました。

そのうちHTMLタグの種類が多く、この方法では厳しいと分かったため、「必要最低限のものだけ残して、HTMLタグを全部取る」という方針に変えました。

//表示許可しているタグだけ残す
//app.amp_permit_tagには"<iframe><div><h1><h2><h3><p><a><b><br><img><li><ul>"が入る

$amp_content = strip_tags($amp_content,config('app.amp_permit_tag'));

さらに許可したHTMLタグでも特定の属性はエラーになったため、下記の表に従って細かい変換処理を入れました。

f:id:allabout-techblog:20160711012521j:plain

リリース

バリデーターでエラーが出なくなったら本番サーバーへデプロイします。

AMP記事ページをGoogleドメイン経由で表示できれば成功です。

Googleドメイン経由で見て分かったのですが、<amp-img>の後に自動で</amp-img>が差し込まれ、レイアウト崩れが発生しました。

<amp-img><div>ここはamp-imgタグで囲まれたくない</div></amp-img>

予め</amp-img>を付けておくことで想定外の箇所に差し込まれなくなりました。

<amp-img></amp-img><div>ここはamp-imgタグで囲まれたくない</div>

SEO対策

さらに、GoogleにAMP記事を知らせるため、下記2つの対策を行いました。

構造化データ

WordPressプラグインを参考にして、記事の構造化データをNewsArticleに変更しました。

All About NEWSの場合、</body>タグ直前にJSON-LD形式で下記のように定義しています。

(例)

<script type="application/ld+json">
{
    "@context": "http://schema.org",
    "@type": "NewsArticle",
    "mainEntityOfPage": "http://news.allabout.co.jp/articles/c/464568",
    "headline": "韓国の大人気かき氷が日本初上陸!「ソルビン原宿」",
    "datePublished": "2016-06-29 10:48:15",
    "dateModified": "2016-06-29 10:48:15",
    "author": {
        "@type": "Person",
        "name": "下井 美奈子"
    },
    "publisher": {
        "@type": "Organization",
        "name": "All About NEWS",
        "logo": {
            "@type": "ImageObject",
            "url": "http://img.aacdn.jp/app/aa-news-front/images/news_logo_400_45.png",
            "width": 400,
            "height": 45
        }
    },
    "image": {
        "@type": "ImageObject",
        "url": "http://imgcp.aacdn.jp/img-a/1200/900/aa/gm/article/4/6/4/5/6/8/201606271819/topimg_original.jpg",
        "width": 1062,
        "height": 900
    }
}
</script>

参考:AMPページとして…

linkタグ

通常記事(元の記事)とAMP記事の関係をlinkタグで表します。

  • 元記事側
<!-- AMP記事があることを知らせる -->
<link rel="amphtml" href="http://amp-news.allabout.co.jp/articles/c/464568/">
  • AMP記事側
<!-- 元記事のURLを知らせる -->
<link rel="canonical" href="http://news.allabout.co.jp/articles/c/464568/">

まとめ

All About NEWSはまだカルーセルに載る頻度も少ないため、今後は記事の速報性を上げるなどさらなる施策が必要だと考えています。

その他

Digipotさんのフリー素材を使わせて頂きました。ありがとうございました。

Facebook Instant Articles を実装から運用まで紹介

citrus Instant Articles

こんにちは、オールアバウトの@k_shiotaです。

今回はcitrusサイトを Facebook Instant Articles に対応しましたのでご紹介します。
Instant Articlesへの記事反映はAPIRSSの2種類の方法がありますが、今回はRSSで反映しています。

そもそも Facebook Instant Articles とは

ニュースサイトなどの記事コンテンツを直接ニュースフィードに配信するサービスです。
サイトに飛ばさずFacebookアプリ内に表示されるので表示は早いです。ただしPCは対象ではありません。
Facebook側で記事情報を持っていることになるので、基本的には同じ記事が2か所で管理されていることになります。

利用開始までの流れ

Facebook Instant Articles を使えるページにする

まずは下記のページの「Sign up」から、記事を選択してインスタント記事の利用を開始します。

instantarticles.fb.com

ドメイン登録

「投稿ツール」ページで左カラムの「インスタント記事/構成」を選択
「ツール」のブロックで「Connect Your Site」に記載のmetaタグを<head>内に追加
URL欄にmetaタグを取得できるURLを入力して「URLの取得」をクリックして、ドメインが許可されたかを確認しましょう

f:id:allabout-techblog:20160704153246p:plain

プロダクション・開発RSSフィード

ここには作成したRSSが設置されているURLを入力します。(上記のドメイン登録で登録したドメイン以下のURL)
取得自体は5分に1回程度で行われているようです。(7/1現在)
RSSの中に使われていURLもドメインのチェックをしております。
canonical」のドメインが登録されているドメインでないと、記事が登録されないので注意しましょう。

f:id:allabout-techblog:20160704154451p:plain

スタイル

最低限ロゴ画像を登録しなければなりません。
初期スタイルの「default」をクリックしてロゴをアップロードしましょう。

f:id:allabout-techblog:20160704154522p:plain f:id:allabout-techblog:20160704154532p:plain

確認方法

RSSで登録された記事はFacebook page managerアプリで確認ができます。

f:id:allabout-techblog:20160704160214p:plain
※右下の「・・・」をタップして「インスタント記事」を選択することで確認ができます。

RSSの構成

基本は下記の構成でRSSを作成することで、記事が登録されます。

developers.facebook.com

今回はRSS作成時に気づいたことやつまずいたことを記載します。

canonicalドメインが登録されているドメインと違うと反映されない

これはテストしている最中になかなか反映されない現象があり気づいたのですが、
テストで作成したドメインと本番で運用するドメインが違う場合にあるかも知れないので注意が必要です。

関連記事について

RSSで関連する記事も指定はできますが、指定しなかった場合はFacebook側が関連する記事を持ってきてくれます。
関連する記事は、Instant Articles以外の投稿も含まれます。

広告の自動配置

広告は自動配置と手動配置があり、自動配置にするとFacebook側で記事の長さに応じて広告表示位置を決めてくれます。

Instant Articlesの投稿

Instant Articlesの編集が完了して「公開」の流れになりますが、Instant Articles側で「公開」しても記事を投稿しないといけません。

記事が更新される判定

RSS上の<op-modified>の指定が更新されていた場合に、記事は更新されます。
Instant Articles側のみで編集した場合はすべて上書きされてしまうので注意が必要です。

記事のタグをそのままRSSに入れられない

利用できるタグが限られているため、記事中のタグを特定のタグに置換しなければならず、この部分で導入の敷居が高くなっていると思われます。
最低限10件の審査が通過すれば、Facebookページ管理から新しく「作成」や「編集」が可能ですので、自動化はできなくともとりあえず運用することは可能です。

PHPでしたら下記のSDKが利用できるかもしれません。 github.com

実際に運用してみて

上記の気になることの中にも出てきましたが、広告についてはFacebook側が自動で挿入してくれる仕組みもあります。ですが記事自体が長くなった場合広告数を制御できないため、広告の数が多いと感じる場合があります。
そのためcitrusでは広告タグを表示位置に書き出しています。

タグやデザインに制限があるものの、広告やGAタグ・その他独自の集計タグも入れられるのでサイトにアクセスする事とあまり変わりがありません。
表示される速度も段違いでInstant Articlesは早いので、表示が遅いなどのストレスなく記事を読めます。

現在はFacebookアプリにのみ対応しているので、スマホからのアクセスが多いサイトは導入してみてはいかがでしょうか?

関連リンク

https://hyper-text.org/archives/2016/04/facebook_instant_articles_quick_start.shtml
http://qiita.com/kay/items/bbc6c2e35db3370b787b
http://jp.techcrunch.com/2015/05/13/20150512facebook-instant-articles/

オールアバウトのリリースフローの変遷

インフラ リリースフロー DevOps

オールアバウトの@takkyです。

オールアバウトはサイト開設15年とWebメディアとしては長い歴史を持っています。 そのため、リリースフローも時代によって様変わりしてきました。
今回はオールアバウトのリリースフローがどのように変わってきたか
今後どのように変えていくかについて書いていきたいと思います。

リリースフローの変遷

2014年以前は私の入社前なので伝聞や社内資料を漁ったものなので若干正確じゃないところでもあります。
また、図はリリースフローをかなり簡素化したものになってます。
詳しいフローは本記事の後半に記載しています。

~2011年頃

本番リリースは手動でFTPアップロードというリリースを行っていたようです。
また、ソース管理もディレクトリコピー管理で行っていたようです。
そのような背景もありこの時代は古いソースで上書きリリースをしてしまうなど事故が多かったようです。
f:id:allabout-techblog:20160620170802p:plain

一部のプロジェクトでは、VSSを導入していたものもあるようです。
Microsoft Visual SourceSafe - Wikipedia

2011年頃

この時期にSubversionを導入しはじめたようです。
ディレクトリ管理からSubversion管理にすることでソース管理自体はシンプルにできるようになりました。
しかし、大型のリニューアル時のマージ漏れなどで事故はあったようです。

2011〜2013年頃?

インフラチームが本番サーバに入りリリース用のスクリプトを実行していたようです。
インフラチームがリリーススクリプトを実行しないといけないという問題はありましたが、
FTPアップロードよりはだいぶマシになってきたようです。
f:id:allabout-techblog:20160620170859p:plain

2013年秋頃?

Jenkinsを導入しはじめました。
インフラチームが本番サーバに入る必要がなくスクリプトの実行ができていました。
しかし、リリース作業は開発者からの依頼によりインフラチームが行っていました。
ロールバックがあった場合、インフラチームが対応しなければいけないため、開発者・インフラ担当者と2倍の工数がかかっていたようです。
f:id:allabout-techblog:20160620170957p:plain

2014年夏

ちょうど新卒入社半年後だったこともあり、
Subversion -> Git(bitbucket)への移行の担当者として、bitbucketへの移行を行いました。
これにより、いまでは当たり前となっているgitを利用した開発ができるようになりました。
移行直後はgitのフローに慣れていないこともあり、masterへの直pushや誤マージなどで、事故になりそうなこともありました。
最近はgitのフローに慣れてきたからかgitが起因の事故もほぼなくなってきています。

2014年冬

Jenkinsによる開発者のリリースが開放され、開発者がリリースを行えるようになりました。
これによりリリース時のインフラチームがリリース作業から解放され負担が少なくなりました。
また、デプロイの効率も良くなり日別のデプロイ数が約3倍になりました。
f:id:allabout-techblog:20160627150231p:plain

2016年春

Wercker+DeployerによるCIを導入し、 CIツールであるWerckerを用いてテストの自動化やデプロイツールであるDeployerを用いて、
確認環境や本番環境への自動化を行うことができました。
Jenkins時代はインフラチームがリリースの設定を行うという点でインフラチームの負担となっていました。
また、Jenkins時代がブラックボックス化してきており、後々技術的負債になってしまうのではないかという懸念点もありました。
Wercker+Deployerによるリリースフローでは開発者がリリースの設定も行うことによりもっと早くデプロイが可能になっていくと考えられます。

f:id:allabout-techblog:20160620171200p:plain

このようにソース管理はディレクトリ管理 -> Git管理
リリースはFTP手動アップロードから開発者がリリースへと変わってきました。

上記ではかなりざっくりリリースフローを紹介しましたが、以下で2014年冬頃のJenkinsを利用した開発フローと、
今年はじめたWercker+Deployerでのリリースフローについて記載したいと思います。

2014冬Jenkinsを利用した開発フロー

基本的にはGitHub-flowで行っています。
Gitブランチを使いこなすgit-flow/GitHub Flow入門(終):プルリクエスト/レビューを取り込んだ、よりシンプルなGitHub Flowの運用を図解する (1/2) - @IT f:id:allabout-techblog:20160620171539p:plain masterへマージ後手動でstaging環境にてgit pull origin masterを行い、デプロイします。
必要があればcomposer updateなど行います。
staging環境でのブラウザテストなどを行います。
次にPrimary Front: PF環境(本番と環境がほぼ同一なリリース事前確認環境)にrsyncでstagingのソースを展開します。
PF環境では本番環境のDBを参照しているため、本番と同一データで問題ないかのチェックを行います。

PF環境でのチェックが終わり次第Jenkins経由でリリーススクリプトを動作させ本番環境へデプロイします。
リリーススクリプトは、pf環境にあるファイルをtarで圧縮し各サーバで展開していました。

このフローの欠点は3つあり、
1. stg環境/pf環境に入ってリリース準備を行うのが手間であること。
2. stg環境でテスト時にソースを直接変更してしまい、それがPF、本番へデプロイされて事故が起きてしまうこと。
3. Jenkinsはインフラチームが管理しており、開発者側から見るとブラックボックス化してしまっている。また、ソース管理されていなかったこと。
がありました。

上記の欠点を解消するためにオールアバウトではWerckerとDeployerを用いてのリリースフローに徐々に切り替えています。

WerckerとDeployerを用いてのリリースフロー

WerckerやDeployerについては、次回に詳しく書くこととしてWercker+Deployerを用いたリリースフローについて記載します。

リリースフロー

f:id:allabout-techblog:20160620171628p:plain 青矢印がstaging環境へのリリースフロー
赤矢印がpf環境へのリリースフロー
黒矢印が本番環境へのリリースフロー

GitHub-flowを若干拡張したような、Git-flowを簡易化したようなフローを使っています。

1.stg用のbranchにマージされたタイミングでWerckerが自動実行され、staging環境へ自動デプロイされます。
staging環境への自動デプロイはWercker上からDeployerサーバに接続してDeployerを実行しています。

2.master branchへマージされたタイミングでWerckerが自動実行されpf環境へ自動デプロイされます。

3.Wercker上から本番反映ボタンを押すと、Wercker上からDeployerサーバに接続してDeployerを実行しrsyncでデプロイをしています。
Deployerにはrsyncのレシピが用意されているので特に大きく設定を加えずにデプロイスクリプトを使用することができます。

これにより、開発者はSTG/PF環境に入らずデプロイが可能になりました。
Werckerの設定はWeb上で行えるため、容易に開発者が設定できます。
DeployerのスクリプトPHPで書かれているためこちらも準備することが簡単にできます。
定常的なリリース作業や新規アプリケーションの構築時に素早くできることを期待しています。

まとめ

ここ4〜5年のCIツールやソース管理のシステムの発達により、オールアバウトでのソース管理やリリースフローも変化してきました。
まだまだ、CIツールによる自動テストなどは行えていないので発展の余地があるとは思いますが
さらなるDevOpsの実現に向けて推進していきたいと思います。