Dockerで作る開発環境:xhguiの導入

スポンサーリンク
Dockerで作る開発環境:xhguiの導入 ノウハウ
Dockerで作る開発環境:xhguiの導入
この記事は約21分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

今日は、dockerで構築した開発環境において、xhguiを導入した手順をご紹介します。

スポンサーリンク

実行環境

この記事では、下記の記事で構築している開発環境を前提にしています。もし環境構築等のハンズオンに興味がありましたら、下記の記事を参考に環境構築をお願いします。

https://yossi-note.com/fix_apache_health_check_path_to_simple_file/

ただ、修正内容が積み重なったいるので、下記のタグをローカルに持ってきて、参照するといいと思います。この記事では、このタグに対しての修正内容を解説しています。

背景

これまで、xhprofの解析内容は、「http://localhost:8080/xhprof/?dir=/var/log/xhprof」にアクセスして参照していました。このやり方だと、各webサーバでxhprofを参照することになるので、一箇所にまとめて参照したいと思い、xhguiを利用してxhprof参照用の専用サーバを立てました。

ディレクトリ構造

ディレクトリ構造は下記のようになっています。赤字の箇所が新規&更新、削除になります。

.
├── LICENSE
├── Makefile
├── README.md
└── app
    ├── apache-exporter
    ├── compose.yml
    ├── flyway
    ├── grafana
    ├── influxdb
    ├── k6
    ├── mongo
    │   ├── Dockerfile
    │   └── init.d
    │       └── xhgui.js
    ├── mysql
    ├── mysqld-exporter
    ├── prometheus
    ├── schemaspy
    ├── web
    │   ├── CodeIgniter-3.1.13
    │   │   ├── application
    │   │   │   ├── config
    │   │   │   │   ├── hooks.php
    │   │   │   │   └── xhgui.php 
    │   │   │   └── hooks
    │   │   │         └── xhgui.php
    │   │   └── composer.json
    │   └── Dockerfile
    └── xhgui
        ├── Dockerfile
        ├── config
        │   └── config.default.php
        └── nginx.conf

compose.yml

下記の内容をファイル(compose.yml) 174 – 154 行目に追記します。

  xhgui:
    build: ./xhgui
    container_name: xhgui
    restart: always
    volumes:
      - ./xhgui/config:/var/www/xhgui/config
      - ./xhgui/nginx.conf:/etc/nginx/http.d/default.conf:ro
    environment:
      - XHGUI_MONGO_HOSTNAME=mongo
      - XHGUI_MONGO_DATABASE=xhprof
    ports:
      - "8142:80"
    networks:
      - net

  mongo:
    build: ./mongo
    container_name: mongo
    platform: linux/x86_64
    # (case sensitive) engine: mmapv1, rocksdb, wiredTiger, inMemory
    command: --storageEngine=wiredTiger
    restart: always
    environment:
      - MONGO_INITDB_DATABASE=xhprof
    volumes:
      - ./mongo/init.d:/docker-entrypoint-initdb.d
    ports:
      - "27017:27017"
    networks:
      - net

mongo

mongo/Dockerfile

下記の内容で新規ファイル(mongo/Dockerfile) を作成します。

FROM percona/percona-server-mongodb:3.6

mongo/init.d/xhgui.js

下記の内容で新規ファイル(mongo/init.d/xhgui.js) を作成します。

db.results.ensureIndex( { 'meta.SERVER.REQUEST_TIME' : -1 } );
db.results.ensureIndex( { 'profile.main().wt' : -1 } );
db.results.ensureIndex( { 'profile.main().mu' : -1 } );
db.results.ensureIndex( { 'profile.main().cpu' : -1 } );
db.results.ensureIndex( { 'meta.url' : 1 } );
db.results.ensureIndex( { 'meta.SERVER.SERVER_NAME' : 1 } );

xhgui

xhgui/Dockerfile

下記の内容で新規ファイル(xhgui/Dockerfile) を作成します。

FROM xhgui/xhgui:latest

xhgui/nginx.conf

下記の内容で新規ファイル(xhgui/nginx.conf) を作成します。

# see https://github.com/perftools/xhgui#configuration

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    root   /var/www/xhgui/webroot;
    index  index.php;

    client_max_body_size 5M;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        try_files $uri =404;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass    xhgui:9000;
        fastcgi_index   index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        client_body_buffer_size 1M;
    }
}

xhgui/config/config.default.php

下記の内容で新規ファイル(xhgui/config/config.default.php) を作成します。

<?php
/**
 * Default configuration for XHGui.
 *
 * To change these, create a file called `config.php` file in the same directory
 * and return an array from there with your overriding settings.
 */

return [
    // Which backend to use for Xhgui_Saver.
    // Must be one of 'mongodb', or 'pdo'.
    'save.handler' => getenv('XHGUI_SAVE_HANDLER') ?: 'mongodb',

    // Database options for PDO.
    'pdo' => [
        'dsn' => getenv('XHGUI_PDO_DSN') ?: null,
        'user' => getenv('XHGUI_PDO_USER') ?: null,
        'pass' => getenv('XHGUI_PDO_PASS') ?: null,
        'table' => getenv('XHGUI_PDO_TABLE') ?: 'results',
        'tableWatch' => getenv('XHGUI_PDO_TABLE_WATCHES') ?: 'watches',
    ],

    // Database options for MongoDB.
    'mongodb' => [
        // 'hostname' and 'port' are used to build DSN for MongoClient
        'hostname' => getenv('XHGUI_MONGO_HOSTNAME') ?: '127.0.0.1',
        'port' => getenv('XHGUI_MONGO_PORT') ?: 27017,
        // The database name
        'database' => getenv('XHGUI_MONGO_DATABASE') ?: 'xhprof',
        // Additional options for the MongoClient constructor,
        // for example 'username', 'password', or 'replicaSet'.
        // See <https://www.php.net/mongoclient_construct#options>.
        'options' => [
            /*
            'username' => getenv('XHGUI_MONGO_USERNAME') ?: null,
            'password' => getenv('XHGUI_MONGO_PASSWORD') ?: null,
            */
        ],
        // An array of options for the MongoDB driver.
        // Options include setting connection context options for SSL or logging callbacks.
        // See <https://www.php.net/mongoclient_construct#options>.
        'driverOptions' => [],
    ],

    'run.view.filter.names' => [
        'Zend*',
        'Composer*',
    ],

    // If defined, add imports via upload (/run/import) must pass token parameter with this value
    'upload.token' => getenv('XHGUI_UPLOAD_TOKEN') ?: '',

    // Add this path prefix to all links and resources
    // If this is not defined, auto-detection will try to find it itself
    // Example:
    // - prefix=null: use auto-detection from request
    // - prefix='': use '' for prefix
    // - prefix='/xhgui': use '/xhgui'
    'path.prefix' => null,

    // Setup timezone for date formatting
    // Example: 'UTC', 'Europe/Tallinn'
    // If left empty, php default will be used (php.ini or compiled in default)
    'timezone' => '',

    // Date format used when browsing XHGui pages.
    //
    // Must be a format supported by the PHP date() function.
    // See <https://secure.php.net/date>.
    'date.format' => 'M jS H:i:s',

    // The number of items to show in "Top lists" with functions
    // using the most time or memory resources, on XHGui Run pages.
    'detail.count' => 6,

    // The number of items to show per page, on XHGui list pages.
    'page.limit' => 25,
];

web/Dockerfile

下記の赤字内容でファイル(web/Dockerfile) 13 – 14 行目を更新します。

RUN yum update -y \
 && yum install -y \
        systemd \
        httpd \
        wget \
        git \
        graphviz \
        unzip

web/CodeIgniter-3.1.13/application/config/hooks.php

下記の内容でファイル(web/CodeIgniter-3.1.13/application/config/hooks.php) 15行目以降を更新します。

$hook['pre_controller'] = array(
    'class'    => 'MyXhgui',
    'function' => 'start',
    'filename' => 'xhgui.php',
    'filepath' => 'hooks',
    'params'   => array()
);

web/CodeIgniter-3.1.13/application/config/xhgui.php

下記の内容でファイル(web/CodeIgniter-3.1.13/application/config/xhgui.php) を作成します。

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

use Xhgui\Profiler\Profiler;
use Xhgui\Profiler\ProfilingFlags;

require __DIR__ . '/../../vendor/autoload.php';

$xhgui_config = [

    // If defined, use specific profiler
    // otherwise use any profiler that's found
    'profiler' => Profiler::PROFILER_TIDEWAYS_XHPROF,

    // This allows to configure, what profiling data to capture
    'profiler.flags' => array(
        ProfilingFlags::CPU,
        ProfilingFlags::MEMORY,
        ProfilingFlags::NO_BUILTINS,
        ProfilingFlags::NO_SPANS,
    ),

    // Saver to use.
    // Please note that 'pdo' and 'mongo' savers are deprecated
    // Prefer 'upload' or 'file' saver.
    'save.handler' => Profiler::SAVER_UPLOAD,

    // Saving profile data by upload is only recommended with HTTPS
    // endpoints that have IP whitelists applied.
    // https://github.com/perftools/php-profiler#upload-saver
    'save.handler.upload' => array(
        'url' => 'http://xhgui/run/import',
        // The timeout option is in seconds and defaults to 3 if unspecified.
        'timeout' => 3,
        // the token must match 'upload.token' config in XHGui
        'token' => 'token',
    ),

    // https://github.com/perftools/php-profiler#file-saver
    'save.handler.file' => array(
        // Appends jsonlines formatted data to this path
        'filename' => '/tmp/xhgui.data.jsonl',
    ),

    // https://github.com/perftools/php-profiler#stack-saver
    'save.handler.stack' => array(
        'savers' => array(
            Profiler::SAVER_UPLOAD,
            Profiler::SAVER_FILE,
        ),
        // if saveAll=false, break the chain on successful save
        'saveAll' => false,
    ),

    // Environment variables to exclude from profiling data
    'profiler.exclude-env' => array(),
    'profiler.options' => array(),

    /**
     * Determine whether the profiler should run.
     * This default implementation just disables the profiler.
     * Override this with your custom logic in your config
     * @return bool
     */
    'profiler.enable' => function () {
        return true;
    },

    /**
     * Creates a simplified URL given a standard URL.
     * Does the following transformations:
     *
     * - Remove numeric values after "=" in query string.
     *
     * @param string $url
     * @return string
     */
    'profiler.simple_url' => function ($url) {
        return preg_replace('/=\d+/', '', $url);
    },

    /**
     * Enable this to clean up the url before submitting it to XHGui.
     * This way it is possible to remove sensitive data or discard any other data.
     *
     * The URL argument is the `REQUEST_URI` or `argv` value.
     *
     * @param string $url
     * @return string
     */
    'profiler.replace_url' => function ($url) {
        return str_replace('token', '', $url);
    },

];

web/CodeIgniter-3.1.13/application/hooks/xhgui.php

下記の内容でファイル(web/CodeIgniter-3.1.13/application/hooks/xhgui.php) を作成します。

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class MyXhgui {

	public function start()
	{

		include(APPPATH.'config/xhgui.php');
		// Add this block inside some bootstrapper or other "early central point in execution"
		try {

			/**
			 * The constructor will throw an exception if the environment
			 * isn't fit for profiling (extensions missing, other problems)
			 */
			$profiler = new \Xhgui\Profiler\Profiler($xhgui_config);

			// The profiler itself checks whether it should be enabled
			// for request (executes lambda function from config)
			$profiler->start();
		} catch (Exception $e){
			// throw away or log error about profiling instantiation failure
		}
	}

}

web/CodeIgniter-3.1.13/composer.json

下記の内容でファイル(web/CodeIgniter-3.1.13/composer.json) 14-15行目を更新します。

	"require": {
		"php": ">=5.3.7",
		"perftools/php-profiler": "^1.1"
	},

動作確認

下記のコマンドを実行して、サービスを起動します。

make setup

ブラウザで「http://localhost:8142/」にアクセスして、下図のように表示されていれば成功です。

解説

xhguiとは

xhguiは、PHPアプリケーションのパフォーマンスを測定するためのツールであり、XHProfというPHP拡張機能を使用してプロファイルデータを収集します。xhguiは、これらのデータを収集して、グラフィカルに表示することができます。これにより、開発者はアプリケーションのボトルネックを特定し、最適化することができます。

xhguiは、Webインターフェースを介してデータを表示するため、結果を直感的に理解することができます。Webインターフェースには、プロファイリングデータを分析するための様々な機能があります。たとえば、特定の関数がどの程度CPU時間を消費しているか、また、どの関数が最も頻繁に呼び出されているかを確認することができます。

xhguiは、GitHubでオープンソースで利用できます。PHP 5.4以上をサポートしています。xhguiは、Composerを使用してインストールすることができます。また、xhguiは、Dockerコンテナとしても提供されています。

xhguiの利用には、XHProfというPHP拡張機能が必要です。XHProfは、Facebookによって開発されたツールで、PHPアプリケーションのパフォーマンスを測定するためのものです。XHProfは、PHP拡張機能として提供されており、GitHubでオープンソースで利用できます。XHProfは、PHP 5.2以上をサポートしています。

xhguiは、開発者にとって非常に便利なツールであり、アプリケーションのパフォーマンス改善に役立ちます。ただし、xhguiを使用するには、PHP拡張機能のインストールや設定など、いくつかの手順が必要となります。

おわりに

今日は、dockerで構築した開発環境にxhguiを導入しました。これで見やすくなったのかなと思います。複数サーバあるときにメリットを享受しやすいのかなと思いました。

今回使用したファイルは下記のGitHubにタグ付けしています。

もし、質問等あればコメント頂ければ確認後、ご連絡いたしますので、ご入用の方はコメント下さい。

よっしー
よっしー

また明日お会いしましょう!

コメント

タイトルとURLをコピーしました