ExportしたApp Service 証明書にパスフレーズをつける
管理を手伝っている、友人のサイトでApp Service 証明書を移行する必要があってちょっとハマったので備忘録。
App Service 証明書のExport
CloudShellから以下のコマンドで、Exportできます。
$ az keyvault secret download \ --file appservicecertificate.pfx \ --vault-name <key-valut-name> \ --name <保存先のシークレット名> \ --encoding base64
vault-name
には証明書作成時に設定した、キーコンテナ名を指定します。
name
には証明書が保存されている、シークレットの名前を指定します。
このコマンドでExportした証明書には空のパスフレーズで生成されます。
このあたりの詳細は公式ドキュメントにも解説があります。
証明書のImport
で、この証明書をアップロードしようとするとアップロード画面ではパスフレーズが必須になっています。
pfxファイルにパスワードをつける方法を探すのに少し手間取りましたが、以下で大丈夫でした。
# 一旦pem形式に変換 openssl pkcs12 -in appservicecertificate.pfx -out example.com.pem -nodes # 再度pfx形式に変換、この際にパスフレーズの入力プロンプトが表示されます。 openssl pkcs12 -export -out example.com.pfx -in example.com.pem
ということで無事移行できました。
CakePHP4でRoutingのテスト
最近React+TypeScriptばかりで、CakePHPのコードはあまり書いていないわたなべです。
Routingのテスト
CakePHP1の頃の新原さんのブログ(なんと2009-05-25の記事、11年前!?)でも書かれているように、routes.phpの設定変更は、思わぬバグを出す可能性があるので、UnitTestでの動作確認は必須だと思っています。
CakePHP3までは、以下のような感じでテストできていましたが、CakePHP4でRoutingがmiddleware化した影響などでそのままでは動作しません。
<?php declare(strict_types=1); ... use Cake\Network\Request; ... public function testRouting($request, $expected) { $request = new Request([ 'url' => '/api/articles.json', 'environment' => ['REQUEST_METHOD' => 'GET'] ]), $result = Router::parseRequest($request); $expected = [ 'pass' => [], 'plugin' => null, 'controller' => 'Articles', 'prefix' => 'api', '_ext' => 'json', 'action' => 'index', ]; $this->assertEquals($expected, $result); } ...
CakePHP4でのRoutingのテスト
最初書いたテストコードは以下のような感じ。
<?php declare(strict_types=1); ... use Cake\Http\ServerRequest; ... public function testRouting() { $request = new ServerRequest([ 'url' => '/api/users/login.json', 'environment' => ['REQUEST_METHOD' => 'POST'] ]); $result = Router::parseRequest($request); $expected = [ 'pass' => [], 'plugin' => null, 'controller' => 'Users', 'prefix' => 'Api', '_ext' => 'json', 'action' => 'login', '_matchedRoute' => '/api/users/login', '_method' => ['POST'], '_middleware' => ['bodies'] ]; $this->assertEquals($expected, $result); } ...
これを実行すると、以下のようなエラーになってしまいます。もちろんroutes.phpには正しく設定されていて、Web経由でのアクセスにも問題ありません。
Cake\Routing\Exception\MissingRouteException: A route matching "/api/users/login.json" could not be found.
色々調べてみたところ、Routerがmiddleware化されたため、Applicationにmiddlewareが追加された時点でrouterを初期化するようになったようです。
このため、routingテーブルが空の状態になっていました。
解決方法
ということで、Routingテーブルを初期化する処理をtraitにしました。
<?php declare(strict_types=1); namespace App\Test\Utility; use Cake\Routing\Router; trait RoutingTestTrait { protected function initializeRoute() { Router::reload(); $routes = Router::createRouteBuilder('/'); require CONFIG . 'routes.php'; } }
前出のコードをこのtraitを使うようにこんな感じに修正するとroutingのテストができます。
<?php declare(strict_types=1); ... use Cake\Http\ServerRequest; use App\Test\Utility\RoutingTestTrait; // 追加 ... class UsersControllerTest extends TestCase { use IntegrationTestTrait; use RoutingTestTrait; // 追加 ... public function testRouting() { $this->initializeRoute(); // 追加 $request = new ServerRequest([ 'url' => '/api/users/login.json', 'environment' => ['REQUEST_METHOD' => 'POST'] ]); $result = Router::parseRequest($request); $expected = [ 'pass' => [], 'plugin' => null, 'controller' => 'Users', 'prefix' => 'Api', '_ext' => 'json', 'action' => 'login', '_matchedRoute' => '/api/users/login', '_method' => ['POST'], '_middleware' => ['bodies'] ]; $this->assertEquals($expected, $result); } ...
まとめ
routingのテストを書いていたことで、routing変更時に何度も救われたことがあるので是非テストを書くことをお勧めします。 CakePHP4は、色々と改善されていい感じに進化しているので今後も使っていこうと思います。
以上、小ネタでした。
2020/6/25追記
RoutingのMiddleware化は、CakePHP3で実施されていました。具体的に何が理由でRoutingテーブルが初期化されていないかは不明。後日調べてみてわかれば追記します。
Github ActionsでMultiContainerなAzure WebApp for Containersをデプロイする
友達に頼まれて、@kunyamiさんのスライド 堅牢&運用楽々な WordPress を Azure App Service でを参考に、WordpressをMultiContainerなAzure WebApp for Containersで構築してたのですが、いくつかハマったのでメモとして残します。
基本的な設定は、@kunyamiさんの資料の通りに進めたのですが、今回は、一部Wordpress管理外の静的なページを含むため、独自コンテナをビルドする必要があったので、GitHub Actionsを使用して、コンテナイメージのビルド、Azure WebApp for Containersへのデプロイをすることにしました。
リポジトリは以下の様な構成になっています。
- Dockerfile - デプロイ用Dockerfile
- docker-compose.yml - デプロイ用 docker-composeファイル
- 静的ファイル用のディレクトリ
- その他設定ファイルなど
ベースイメージは、wordpress公式のものを使用して、静的なページは、ビルド時にコピーする様にしました
Dockerfileサンプル
FROM wordpress:latest COPY static_webpages /var/www/html/static_webpages
事前準備
Azure WebAppデプロイ用クレデンシャルの作成
いつものですね。以下のコマンドを実行すると表示されるJSONがクレデンシャルです。
$ az ad sp create-for-rbac --name "https://[WebAppの名前].azurewebsites.net" --role contributor \ --scopes /subscriptions/[サブスクリプションID]/resourceGroups/wetest \ --sdk-auth
秘匿情報の設定
Github Actions内で利用する秘匿情報は、Githubの対象リポジトリ内の Settings -> Secrets
で設定します。
今回は、ビルドしたイメージをDocker Hubに保存するので、Docker Hubアカウントパスワードと先ほど作成した、クレデンシャルをSecretsに追加します。
- AZURE_CREDENTIALS - Azureクレデンシャル
- DOCKER_PASSWORD - Docker Hubのパスワード
Github Actionsの設定
Github Actionsの設定は、リポジトリ内の .github/workfloes
ディレクトリ以下に設定ファイルを配置します。今回作成したのは以下の様なファイルです。
name: Build And Deploy env: DOCKER_USERNAME: [Docker Hubのユーザ名] DOCKER_IMAGENAME: [docker image名] AZURE_WEBAPP_NAME: [webappの名称] on: push: branches: - master jobs: build: runs-on: ubuntu-latest steps: - name: Check out code uses: actions/checkout@v2 - name: Azure authentication uses: azure/login@v1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Build & push Docker image uses: docker/build-push-action@v1 with: image: ${{ env.DOCKER_USERNAME }}/${{ env.DOCKER_IMAGENAME }} repository: ${{ env.DOCKER_USERNAME }}/${{ env.DOCKER_IMAGENAME }} dockerfile: Dockerfile username: ${{ env.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} tags: ${{ github.sha }} - name: 'Deploy to Azure Web App for Container' uses: azure/webapps-deploy@v2 with: images: ${{ env.DOCKER_USERNAME }}/${{ env.DOCKER_IMAGENAME }}:${{ github.sha }} app-name: ${{ env.AZURE_WEBAPP_NAME }} configuration-file: docker-compose-webapp.yml
yamlファイルを見れば一目瞭然ですが、今回作成したJobには以下の4つのStepが定義されています。
- Check out code - コードをチェックアウト
- Azure authentication - Azureの認証処理
- Build & push Docker image - docker imageのビルドとプッシュ
- Deploy to Azure Web App for Container - Azure WebApp for Containersへのデプロイ
docker imageのビルドとプッシュ
docker imageのビルドとプッシュには、Docker公式のGitHub Actionを使っています。 公式サイトに詳しい解説があるので、そちらを参照してください。
今回は、ビルト時のタグにgit hashを使用しているので以下の様に指定しています。
tags: ${{ github.sha }}
Azureの認証とAzure WebApp for Containersへのデプロイ
Azure WebApp for Containersへのデプロイは、Azure 公式のGitHub Actionを使っています。 こちらも、公式サイトに詳しい解説があるので、そちらを参照してください。
MultiContainerなAzure WebApp for Containersのデプロイ方法
今回は、MultiContainerなAzure WebApp for Containersを使用しているのですが、公式ドキュメントにはMultiContainerの場合のデプロイ方法は書かれていないため、GitHub Actionのソースコードを確認したところ、以下の様に設定情報を初期化しているのを発見しました。
... class ActionParameters { constructor(endpoint) { this._publishProfileContent = core.getInput('publish-profile'); this._appName = core.getInput('app-name'); this._slotName = core.getInput('slot-name'); this._packageInput = core.getInput('package'); this._images = core.getInput('images'); this._multiContainerConfigFile = core.getInput('configuration-file'); this._startupCommand = core.getInput('startup-command'); this._endpoint = endpoint; } ...
ということで、MultiContainer時のdocker-composeファイルは configuration-file
で指定できました。最終的なStepの設定が下記です。
- name: 'Deploy to Azure Web App for Container' uses: azure/webapps-deploy@v2 with: images: ${{ env.DOCKER_USERNAME }}/${{ env.DOCKER_IMAGENAME }}:${{ github.sha }} app-name: ${{ env.AZURE_WEBAPP_NAME }} configuration-file: docker-compose-webapp.yml
docker-compose.yml 内でイメージ名を指定していますが、 images
に今回ビルドしたタグを含むイメージ名を指定しています。
これもドキュメントには記載がないのですが、試行錯誤している最中に以下のエラーが発生してエラーメッセージの内容から発見したのですが、MultiContainer時にimage名を指定すると、docker-compose内のイメージ名を上書きしてくれますので、自前でdocker-composeファイルを書き換える様な処理は書かなくても大丈夫でした。
試行錯誤中に発生したエラーメッセージ
Deployment Failed with Error: Error: For single-container, just specify a valid image name. For multi-container specifying a Docker-Compose file is mandatory and specifying image names is optional. Provide image names if the tags in Docker-Compose file need to be substituted.
また、slot-name
でデプロイ対象のスロット名も指定できる様なので、B/Gデプロイもできそうですね。
まとめ
マーケットプレイスで公開されているGitHub Actionはソースコードも公開されているので、ドキュメントに記載のない内容はソースコードにあたるとすんなり解決できることも多そうです。 GitHub Actionsかなり便利なので、これからも積極的に使っていきたいです。
PCOVでコードカバレッジ取得を高速化
この記事はCakePHP Advent Calendar 2019の21日目の記事です
つい先日、ついにCakePHP 4.0がリリースされましたが、CakePHP 4.0で利用しているテスティングフレームワークはもちろんPHPUnitです。CakePHP3では、PHPUnit 6.0系を使っていましたが8.5.0に更新されています。
PHPUnitで、コードカバレッジを取得するにはXdebugを使うのが定番ですが、PHPUnit8系ではXdebug以外にPCOVを利用することができます。
PCOVは、今年(2019年)リリースされたばかりのコードカバレッジドライバーで、高速かつ省メモリで動作することが特徴です。
ということで、今回は実際にどれくらい高速化できるのかを簡単に調べてみました。
計測した環境
当初、CakePHP3で作ったサンプルアプリをCakePHP4化して試そうと思っていたのですが、いろいろな問題があり断念しCakePHP4本体のテストの一部(CakePHPのUnitTestすべてを実行するとかなり時間がかかるので、ORMのテストのみ)を利用して計測しました。
計測した環境は、macOS上のDocker Containerで、PHP公式で公開されているこちらをベースに若干拡張などを追加したものを利用しています。
それぞれのバージョンは以下のとおりです。
# php -v PHP 7.3.10 (cli) (built: Oct 10 2019 21:12:52) ( NTS ) Copyright (c) 1997-2018 The PHP Group Zend Engine v3.3.10, Copyright (c) 1998-2018 Zend Technologies with Zend OPcache v7.3.10, Copyright (c) 1999-2018, by Zend Technologies # pecl list Installed packages, channel pecl.php.net: ========================================= Package Version State apcu 5.1.17 stable pcov 1.0.6 stable redis 5.0.2 stable xdebug 2.9.0 stable
コードカバレッジなし
まずは、Xdebugを無効にしてコードカバレッジなしでの計測結果がこちら。
# ./vendor/bin/phpunit tests/TestCase/ORM/ PHPUnit 8.5.0 by Sebastian Bergmann and contributors. ............................................................. 61 / 1331 ( 4%) ............................................................. 122 / 1331 ( 9%) ... .................................................. 1331 / 1331 (100%) Time: 7.04 seconds, Memory: 36.00 MB OK, but incomplete, skipped, or risky tests! Tests: 1331, Assertions: 3917, Skipped: 3, Incomplete: 2.
Xdebug
# ./vendor/bin/phpunit tests/TestCase/ORM/ PHPUnit 8.5.0 by Sebastian Bergmann and contributors. ............................................................. 61 / 1331 ( 4%) ............................................................. 122 / 1331 ( 9%) ... .................................................. 1331 / 1331 (100%) Time: 3.68 minutes, Memory: 126.00 MB OK, but incomplete, skipped, or risky tests! Tests: 1331, Assertions: 3917, Skipped: 3, Incomplete: 2. Generating code coverage report in Clover XML format ... done [1.68 minutes] Generating code coverage report in HTML format ... done [1.8 minutes]
やはりかなり遅いですね。約31倍です。
PCOV
PCOVでの計測結果がこちら。
# php -d pcov.enabled=1 ./vendor/bin/phpunit tests/TestCase/ORM/ PHPUnit 8.5.0 by Sebastian Bergmann and contributors. ............................................................. 61 / 1331 ( 4%) ............................................................. 122 / 1331 ( 9%) ... .................................................. 1331 / 1331 (100%) Time: 24.55 seconds, Memory: 126.00 MB OK, but incomplete, skipped, or risky tests! Tests: 1331, Assertions: 3917, Skipped: 3, Incomplete: 2. Generating code coverage report in Clover XML format ... done [10.48 seconds] Generating code coverage report in HTML format ... done [46.87 seconds]
なんと、Xdebugを使った場合と比べると約10分の1の時間で完了しています!カバレッジを計測していない場合と比べると、約3.5倍。Xdebugと比べると許容範囲かなと思います。しかも、メモリ使用量もXdebugと同じという結果でした
まとめ
まとめたものがこちら
実行時間 | 使用メモリ | |
---|---|---|
カバレッジなし | 7.04 s | 36.00 MB |
Xdebug | 220.80 s | 126.00 MB |
PCOV | 24.55 s | 126.00 MB |
実際のコードで実行時間に差は出るとは思いますが、かなりの高速化が期待できそうです。
CakePHP4へのアップデートはプラグインやライブラリの対応など、まだ色々とハードルはありますが、PHPUnitのバージョンアップはかなり魅力的だと思っています。PHPUnit自体の変更については全く追えていないので今後キャッチアップしていきたいと思っています。
参考
PHPでSAS tokenを使ってAzure Blob Storageにファイルをアップロードする
現在開発中の案件で、Shared Access Signature(SAS)を使ってBlobにデータを上げる必要があって、若干ハマったのでメモ。
先日プレビュー版がリリースされた user delegation SASってのもありますが、今回はストレージアカウントのキーを使う感じで。user delegation SASに関しては、亀淵さんのブログが参考になると思います。
今回使用するバックエンドはPHPなので、SASの作成には、microsoft/azure-storage-blobを使うことにします。
ざっとコード書くてみたのがこんな感じ...
<?php ... $accountName = getenv('STORAGE_ACCOUNT_NAME'); $accountKey = getenv('STORAGE_ACCOUNT_KEY'); $containerName = getenv('CONTAINER_NAME'); $helper = new BlobSharedAccessSignatureHelper($accountName, $accountKey); $sas = $helper->generateBlobServiceSharedAccessSignatureToken( Resources::RESOURCE_TYPE_BLOB, "{$containerName}/composer.json", 'w', Chronos::now(new \DateTimeZone('UTC'))->addSeconds(60)->format(DATE_ATOM), Chronos::now(new \DateTimeZone('UTC'))->subSeconds(10)->format(DATE_ATOM), '', // リクエスト元のIPアドレス 'https' ); echo "sas: {$sas}\n";
これで試すと、 403 Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. こんなエラーがでてしまいます。
散々悩んだ挙げ句、ブラウザーで JavaScript と HTML を使用して BLOB をアップロード、一覧表示、および削除するを実際に動かして、リクエスト内容などを確認した結果ようやく原因がわかりました。
PHPの日付フォーマット DATE_ISO8601
がiso 8601準拠ではないので、 DATE_ATOM
を使っていたのですが、TimeZoneの書式が違うことが原因でした。
$ cat date.php <?php $date = new \DateTime('now', new \DateTimeZone('UTC')); echo $date->format(DATE_ISO8601)."\n"; echo $date->format(DATE_ATOM)."\n"; echo $date->format('Y-m-d\TH:i:s\Z')."\n"; ?> $ php date.php 2019-11-01T01:46:38+0000 2019-11-01T01:46:38+00:00 2019-11-01T01:46:38Z // -> この形式でないとだめ
'Y-m-d\TH:i:s\Z'
を使うことで正常にアップロードできました。めでたしめでたし。
最終的にできあがったサンプルがこちら
<?php require './vendor/autoload.php'; use Cake\Chronos\Chronos; use GuzzleHttp\Client; use MicrosoftAzure\Storage\Blob\BlobSharedAccessSignatureHelper; use MicrosoftAzure\Storage\Blob\Internal\BlobResources; use MicrosoftAzure\Storage\Common\Internal\Resources; $accountName = getenv('STORAGE_ACCOUNT_NAME'); $accountKey = getenv('STORAGE_ACCOUNT_KEY'); $containerName = getenv('CONTAINER_NAME'); $helper = new BlobSharedAccessSignatureHelper($accountName, $accountKey); $sas = $helper->generateBlobServiceSharedAccessSignatureToken( Resources::RESOURCE_TYPE_BLOB, "{$containerName}/composer.json", 'w', Chronos::now(new \DateTimeZone('UTC'))->addSeconds(60)->format('Y-m-d\TH:i:s\Z'), Chronos::now(new \DateTimeZone('UTC'))->subSeconds(10)->format('Y-m-d\TH:i:s\Z'), '', // リクエスト元のIPアドレス 'https' ); echo "sas: {$sas}\n"; try { $client = new Client(['base_uri' => sprintf('https://%s.%s/%s/', $accountName, Resources::BLOB_BASE_DNS_NAME, $containerName)]); $response = $client->put('composer.json', [ 'body' => file_get_contents('./composer.json'), 'headers' => [ 'x-ms-version' => BlobResources::STORAGE_API_LATEST_VERSION, 'x-ms-blob-type' => 'BlockBlob', ], 'query' => $sas, ]); echo sprintf("StatusCode: %d\n", $response->getStatusCode()); } catch(\Exception $e) { echo sprintf("StatusCode: %d\n", $e->getCode()); echo $e->getMessage() . "\n"; }
サンプルコードは、こちらにもあげてあります。
Azure DevOpsのself hosted agentを試してみた
暑いので快適な仕事部屋で、CodeCoverageと戦っているわたなべです...
self hosted agent試すことになった理由
現在開発中の案件では、Azure DevOpsを利用して、CI/CD環境を構築しています。バックエンドはPHPで開発していて、UnitTestを結構しっかり書いています。
開発作業に追われてなかなか手をつけられていなかった Code Coverage を見ようと設定をしたところ、以下のようなエラーが発生してしまいました。
表示されているリンクの内容を確認すると...
For 60 minutes on Microsoft-hosted agents with a private project or private repository
ということで、Microsoft-hosted agentsでプライベートリポジトリのjobを実行する場合は、60分
の実行時間制限がある様子。
Web系のアプリのCI/CDであれば、実行に60分もかかることはほとんどないと思いますが、今回はCode Coverageを収集するために Xdebug を有効にしてテストを実施しているため通常より実行にかなりの時間がかかることが原因でした。(通常のCI/CDのpipelineではCode Coverageは取得していません)
phpdbgを利用すると速度はかなり改善するのですが、こちらはメモリの非常に大量に消費します。
また、今年公開されたばかりのPCOVを使用すると実行速度、メモリ使用量共に劇的に改善できそうなのですが、PHPUnit8でないと対応していないため、今回使用しているCakePHPのアプリには現状適用できません。
ということで、 Xdebug を使う方式でなんとか CodeCoverageを取るしかなさそうです...。
そこで、今まで使ったことのなかった、self hosted agentを試すことにしました。
agentの種類
Azure DevOpsのagentには以下の2つの提供方式があります。
Microsoft-hosted agents
Microsoft-hosted agentsは、MSさんが提供しているagentで以下のような種類があります。
- Windows Server 2012 R2 with Visual Studio 2015 (Hosted)
- Windows Server 2016 with Visual Studio 2017 (Hosted VS2017)
- Windows Server 2019 with Visual Studio 2019 (Hosted Windows 2019 with VS2019)
- Windows Server Core 1803 (Hosted Windows Container)
- Ubuntu 16.04 (Hosted Ubuntu 1604)
- macOS X Mojave 10.14 (Hosted maxOS)
- macOS X High Sierra 10.13 (Hosted macOS High Sierra)
Microsoft-hosted agentsの制限事項は以下の点。
Self-hosted agents
自前のVMなどに、agentをインストールしてAzure DevOpsから利用することが出来ます。Azure DevOpsにはSelf-hosted agents 1つであれば無料で利用することができるので、VMの利用料のみで使用可能です。また、Docker上での実行もサポートされているので、k8s上で実行することもできそうです。
Self-hosted agentsの場合は、実行時間の制限はありません。
self hosted agentの設定
今回は実験ということで、VMを1台立ててagentをインストールすることにします。
dockerのインストール
今回のjobではdocker-composeを使用するのでまずは、dockerをインストールします。
$ sudo apt install -y docker docker-compose $ sudo usermod -g docker user
agent用ディレクトリを作成
実験で使用するjobの設定がhosted agentのディレクトリ構成に依存しているので以下のディレクトリを作成しました。依存しないように直したほうが良いですね...
$ cd /home $ sudo mkdir vsts $ sudo chown user.user vsts
PAT(Private access token)を生成
ここを参考に、利用するdevops organizationのPATを作成する。
作成したPATは、agentインストール時に使用します。
agentのインストール
- Azure DevOpsの Organization SettingsからAgent Poolを選択します。
- Default poolを選択して、
New Agent
ボタンを押すと以下のようなインストール手順の解説が表示されます。
ここで取得できる、agentのダウンロードURLを使用して、指示通りに設定を進めます。
$ mkdir agent $ wget https://vstsagentpackage.azureedge.net/agent/2.155.1/vsts-agent-osx-x64-2.155.1.tar.gz $ cd agent $ ./config.sh >> End User License Agreements: Building sources from a TFVC repository requires accepting the Team Explorer Everywhere End User License Agreement. This step is not required for building sources from Git repositories. A copy of the Team Explorer Everywhere license agreement can be found at: /home/kaz/agent/externals/tee/license.html Enter (Y/N) Accept the Team Explorer Everywhere license agreement now? (press enter for N) > Y >> Connect: Enter server URL > https://dev.azure.com/[your organization]/ Enter authentication type (press enter for PAT) > PAT Enter personal access token > [your personal access token] Connecting to server ... >> Register Agent: Enter agent pool (press enter for default) > Enter agent name (press enter for devopsagent) > Scanning for tool capabilities. Connecting to the server. Successfully added the agent Testing agent connection. Enter work folder (press enter for _work) > /home/vsts/work 2019-08-03 01:55:47Z: Settings Saved.
agentの実行
以下のコマンドで、agentをserviceとして登録できます。
$ cd ~/agent $ sudo ./svc.sh install ## Start $ sudo ./svc.sh start ## Stop $ sudo ./svc.sh stop ## Status $ sudo ./svc.sh status
Pipelineの設定
agentが正常に起動すると、インストール時に指定したorganizationのagent poolに表示されます。
agent が登録されたら、あとは既存のbuild pipelineをコピーするなどして、以下のように使用するagent poolを指定すれば完了です。
まとめ
self hosted agentは以外に簡単に導入できました。
Docker上での実行もサポートされているようなので、今回のプロジェクトではステージング環境のAKSクラスタ上でカバレッジ用のagentを実行できないか検討しているところです。
CIが成長してくると、ビルド時間との戦いになることはよくあることではないかと思います。 テスト、ビルド手順の見直しも必要ですが、CI環境自体をパワーアップして改善する必要があることもあります。 その際の手法として、self hosted agentはかなり有効だと思います。
参考URL
PHPでも簡単 Azure Application Insights
先月末に開催された、de:code 2019でMVP パーソナル スポンサーとしてPHP用のライブラリを公開しました。
Azure Application Insights
Application Insights は、上記のページにも書かれている通り、複数のプラットフォームで使用できる Web 開発者向けの拡張可能なアプリケーション パフォーマンス管理 (APM) サービスです。
.NETやNode.jsなどで利用する場合は、とても簡単に様々な機能を利用できるのですが、PHP用のSDKはシンプルなライブラリなので、アプリケーションに組み込むにはある程度自前での実装が必要になります。
そこで、現在、製作中の案件などで利用するために実装を進めていたものをde:code 2019での公開に向けて、公開可能な形にまとめ直したのが、Phaiです。
Phai - Application Insights PHP middleware
Phaiは、PSR-15準拠のミドルウェアです。現在、PSR-15を標準でサポートしているフレームワークはZend Expressive位であまり多くはありませんが、今後サポートする方向に進むのではないかと思います。また、各フレームワークでPSR-15をサポートするライブラリが公開されているため、問題なく利用することができます。
下記で紹介している、PSR-15 Middleware support for CakePHP
は今回Phaiを開発するにあたって、PSR-15 Middleware support for Slim Framework v3
を参考に私が開発したものです。
PSR-15対応ライブラリ
他にもありそうですが普段使っていないフレームワークに関しては探しきれていません...。他に何かあれば教えて下さいm(__)m
使用方法
Phaiには、Slim3とCakePHP3用のサンプルが同梱されていますが、以下に簡単な使い方を解説します。
Application Insightsの作成
Azure PortalからApplication Insightsの作成を選択し、利用するサブスクリプションやリソースグループ、リソース名、配置するリージョンなどを選択し Next
を押して次に進みます。
次の画面ではタグを指定できますが、今回は特になにも設定せずに Next
を押して次に進みます。
問題がなければ、 Create
を押してApplication Insightsを作成します。
しばらく待つとApplication Insightsの作成が完了し、以下のような画面を見ることができます。
概要(Overview)画面に表示されている、 Instrumentation Key
は後ほど使用しますのでメモしておいてください。
Phaiのサンプルを実行
以下の手順でPhaiをcloneして、CakePHPのサンプルの設定をします。
$ git clone git@github.com:kaz29/phai.git $ cd phai/example/cakephp $ composer install Loading composer repositories with package information Installing dependencies (including require-dev) from lock file Package operations: 86 installs, 0 updates, 0 removals - Installing cakephp/plugin-installer (1.1.0): Loading from cache - Installing aura/intl (3.0.0): Loading from cache .... Generating autoload files > Cake\Composer\Installer\PluginInstaller::postAutoloadDump > App\Console\Installer::postInstall Created `config/app.php` file Created `/Users/kaz/dev/labo/phai/examples/cakephp/logs` directory Created `/Users/kaz/dev/labo/phai/examples/cakephp/tmp` directory Created `/Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache` directory Created `/Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache/models` directory Created `/Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache/persistent` directory Created `/Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache/views` directory Created `/Users/kaz/dev/labo/phai/examples/cakephp/tmp/sessions` directory Created `/Users/kaz/dev/labo/phai/examples/cakephp/tmp/tests` directory Set Folder Permissions ? (Default to Y) [Y,n]? y Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache/models Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache/persistent Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/tmp/cache/views Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/tmp/sessions Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/tmp/tests Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/tmp Permissions set on /Users/kaz/dev/labo/phai/examples/cakephp/logs Updated Security.salt value in config/app.php
以下のように、先程作成したApplication InsightsのInstrumentation Key を指定してサンプルアプリを実行します。
Your application insights "Instrumentation Key"
の部分にご自身の環境のInstrumentation Keyを指定してください。
$ env APPLICATION_INSIGHTS_KEY='Your application insights "Instrumentation Key"' ./bin/cake server Welcome to CakePHP v3.7.7 Console --------------------------------------------------------------- App : src Path: /Users/kaz/dev/labo/phai/examples/cakephp/src/ DocumentRoot: /Users/kaz/dev/labo/phai/examples/cakephp/webroot Ini Path: --------------------------------------------------------------- built-in server is running in http://localhost:8765/ You can exit with `CTRL-C`
正常に実行できたら、表示されているURL(http://localhost:8765/)にアクセスすると、テレメトリ情報がApplication Insightsに送信されます。
Application Insightsでのログの確認
Application Insightsの Search
メニューを選択し、Click here
をクリックします。
以下のように、アクセスログが、レスポンスタイム付きで表示されます。 ポータル上に反映されるのに数分かかりますので、少しお時間をおいてから確認してください。
ここで、http://localhost:8765/pages/hoge
のような存在しないページにアクセスしてみます。
すると下記のような例外のログがApplication Insightsに送信されてエラーの内容を確認することができます。
ログレベルの設定
Phaiに含まれる、標準のTelemetry_Client
をラップした、\kaz29\Phai\ApplicationInsights\Telemetry_Client
を使用し、以下のように指定することで、指定したログレベル以下のものはApplication Insightsへの転送は行われません。
環境変数などを利用して、実行環境ごとに、ログレベルを設定したり一時的にデバッグログを有効にするなどの切り替えが簡単にできるようになっています。
$client = new \kaz29\Phai\ApplicationInsights\Telemetry_Client( null, null, \ApplicationInsights\Channel\Contracts\Severity_Level::Warning );
サンプルコードの簡単な解説
config/bootstrap.phpの末尾で、環境変数をもとに設定情報を保存しています。
/** * Initialize Phai */ $client = new \ApplicationInsights\Telemetry_Client(); \kaz29\Phai\Phai::initialize($client, env('APPLICATION_INSIGHTS_KEY')); Configure::write('Phai.client', $client);
src/Application.php内で以下のように、Phaiを登録しています。
public function middleware($middlewareQueue) { $middlewareQueue // Catch any exceptions in the lower layers, // and make an error page/response ->add(new ErrorHandlerMiddleware(null, Configure::read('Error'))) // ** ここで Phaiを指定している ->add(new PsrMiddleware(new ApplicationInsightsMiddleware(Configure::read('Phai.client')))) // Handle plugin/theme assets like CakePHP normally does. ->add(new AssetMiddleware([ 'cacheTime' => Configure::read('Asset.cacheTime') ])) // Add routing middleware. // Routes collection cache enabled by default, to disable route caching // pass null as cacheConfig, example: `new RoutingMiddleware($this)` // you might want to disable this cache in case your routing is extremely simple ->add(new RoutingMiddleware($this, '_cake_routes_')); return $middlewareQueue; }
まとめ
いかがでしたか?とても簡単にApplication Insightsを導入できることがご理解いただけたのではないかと思います。 現状は、まだ以下のような基本的な機能のみしか利用できませんが、今後も開発を続けてもっと便利にApplication Insightsを利用できるようにしていく予定です。
- リクエストログ
- 例外ログ
- PSR-3 Logger Interfaceに準拠したロガー