1台のサーバに異なる設定でApache Traffic Serverを複数立ち上げるためのビルド設定

はじめに

Apache Traffic ServerにはHierarchical Cachingという機能があって、キャッシュを親と子の2階層にすることが出来ます。

CentOSで1つのサーバに親と子の2つのTraffic Server 6.1.1を異なる設定で起動するような構成にしたかったのですが、本家のrpmでは出来ないようでした。 ソースを見ていたらconfigureオプションをうまく指定すれば可能だとわかり、カスタムrpmを作りました。

rpmのspecファイルはapache-traffic-server-rpm/trafficserver.spec、ビルドしたrpmは hnakamur/apache-traffic-server-6 Copr で公開しています。

起動オプションではやりたいことは出来なさそうでした

カスタムrpmを作る前に、本家のrpmを使いつつコマンドラインオプションや環境変数の設定によってやりたいことが実現できないか調べてみたのですが、出来なさそうでした。

バージョン6.1.1のソースを見た時のメモです。

まず、 traffic_server コマンドには -conf_dir というオプションがあります。ソースは proxy/Main.cc です。traffic_serverのドキュメントには記載がありません。

一方、 traffic_manager コマンドには -tsArgs というオプションがあります。 ソースは cmd/traffic_manager/traffic_manager.cctraffic_managerのドキュメント にも説明はありませんが載っています。

しかし、 traffic_cop コマンドが traffic_manager コマンドを起動する際には -tsArgs オプションは指定していません。ソースは cmd/traffic_cop/traffic_cop.cc です。 traffic_cop のドキュメントを見ても traffic_manager にオプションを渡すためのオプションは無いようです。

rpmでインストールされるサービス起動スクリプトだと traffic_cop → traffic_manger → traffic_sever という呼び出し関係になるので、こ traffic_server-conf_dir オプションを渡すことは出来なさそうです。

TS_ROOTという環境変数を発見

lib/ts/Layout.ccTS_ROOT という環境変数を参照しているのを見つけました。

Layout::Layout(const char *_prefix)
{
  if (_prefix) {
    prefix = ats_strdup(_prefix);
  } else {
    char *env_path;
    char path[PATH_NAME_MAX];
    int len;

    if ((env_path = getenv("TS_ROOT"))) {
      len = strlen(env_path);
      if ((len + 1) > PATH_NAME_MAX) {
        ink_error("TS_ROOT environment variable is too big: %d, max %d\n", len, PATH_NAME_MAX - 1);
        return;
      }
      ink_strlcpy(path, env_path, sizeof(path));
      while (len > 1 && path[len - 1] == '/') {
        path[len - 1] = '\0';
        --len;
      }
    } else {
      // Use compile time --prefix
      ink_strlcpy(path, TS_BUILD_PREFIX, sizeof(path));
    }

    prefix = ats_strdup(path);
  }
  exec_prefix = layout_relative(prefix, TS_BUILD_EXEC_PREFIX);
  bindir = layout_relative(prefix, TS_BUILD_BINDIR);
  sbindir = layout_relative(prefix, TS_BUILD_SBINDIR);
  sysconfdir = layout_relative(prefix, TS_BUILD_SYSCONFDIR);
  datadir = layout_relative(prefix, TS_BUILD_DATADIR);
  includedir = layout_relative(prefix, TS_BUILD_INCLUDEDIR);
  libdir = layout_relative(prefix, TS_BUILD_LIBDIR);
  libexecdir = layout_relative(prefix, TS_BUILD_LIBEXECDIR);
  localstatedir = layout_relative(prefix, TS_BUILD_LOCALSTATEDIR);
  runtimedir = layout_relative(prefix, TS_BUILD_RUNTIMEDIR);
  logdir = layout_relative(prefix, TS_BUILD_LOGDIR);
  mandir = layout_relative(prefix, TS_BUILD_MANDIR);
  infodir = layout_relative(prefix, TS_BUILD_INFODIR);
  cachedir = layout_relative(prefix, TS_BUILD_CACHEDIR);
}

layout_relative関数の定義ink_filepath_merge関数の定義 を見ると、 layout_relative の第2引数が / で始まっていると第2引数がそのまま使われ、 / で始まっていないと第1引数と第2引数を必要に応じて / を挟んで連結した値になることがわかりました。

TS_BUILD_SYSCONFDIR などはtrafficserver/ink_config.h.in で定義されていました。

/* Various "build" defines */
#define TS_BUILD_PREFIX "@prefix@"
#define TS_BUILD_EXEC_PREFIX "@rel_exec_prefix@"
#define TS_BUILD_BINDIR "@rel_bindir@"
#define TS_BUILD_SBINDIR "@rel_sbindir@"
#define TS_BUILD_SYSCONFDIR "@rel_sysconfdir@"
#define TS_BUILD_DATADIR "@rel_datadir@"
#define TS_BUILD_INCLUDEDIR "@rel_includedir@"
#define TS_BUILD_LIBDIR "@rel_libdir@"
#define TS_BUILD_LIBEXECDIR "@rel_libexecdir@"
#define TS_BUILD_LOCALSTATEDIR "@rel_localstatedir@"
#define TS_BUILD_RUNTIMEDIR "@rel_runtimedir@"
#define TS_BUILD_LOGDIR "@rel_logdir@"
#define TS_BUILD_MANDIR "@rel_mandir@"
#define TS_BUILD_CACHEDIR "@rel_cachedir@"
#define TS_BUILD_INFODIR "@rel_infodir@"

rel_* という値は configure 実行時にbuild/common.m4の TS_SUBST_LAYOUT_PATH で設定されるようです。

dnl
dnl TS_SUBST_LAYOUT_PATH
dnl Export (via TS_SUBST) the various path-related variables that
dnl trafficserver will use while generating scripts and
dnl the default config file.
AC_DEFUN([TS_SUBST_LAYOUT_PATH], [
  TS_EXPAND_VAR(exp_$1, [$]$1)
  TS_PATH_RELATIVE(rel_$1, [$]exp_$1, ${prefix})
  TS_SUBST(exp_$1)
  TS_SUBST(rel_$1)
  TS_SUBST($1)
])

ここから呼ばれる TS_PATH_RELATIVE で実際の値が作られます。

dnl
dnl Removes the value of $3 from the string in $2, strips of any leading
dnl slashes, and returns the value in $1.
dnl
dnl Example:
dnl orig_path="${prefix}/bar"
dnl TS_PATH_RELATIVE(final_path, $orig_path, $prefix)
dnl    $final_path now contains "bar"
AC_DEFUN([TS_PATH_RELATIVE], [
ats_stripped=`echo $2 | sed -e "s#^$3##"`
# check if the stripping was successful
if test "x$2" != "x${ats_stripped}"; then
# it was, so strip of any leading slashes
    $1="`echo ${ats_stripped} | sed -e 's#^/*##'`"
else
# it wasn't so return the original
    $1="$2"
fi
])

ということで、例えば sysconfdir の値が prefix の値で始まっていれば rel_sysconfdirprefix からの相対パスになり、そうでなければ sysconfdir そのままになるということがわかりました。

configureオプションの指定方法

上記を踏まえて、私が作成した /trafficserver.spec では 1行目

%define _prefix /opt/trafficserver

と設定し、 85〜94行目 で以下のような configure オプションを指定しています。

%configure \
  --enable-layout=opt \
  --sysconfdir=%{_prefix}%{_sysconfdir} \
  --localstatedir=%{_prefix}%{_localstatedir} \
  --libexecdir=%{_prefix}/%{_lib}/plugins \
  --with-tcl=/usr/%{_lib} \
  --enable-luajit \
  --with-user=ats --with-group=ats \
  --disable-silent-rules \
  --enable-experimental-plugins

これでビルドしたtrafficserverを実行する際に、環境変数TS_ROOTを設定することで以下のようなディレクトリを参照することが出来ました。

私が使っているディレクトリ構成

実際には以下のようなシンボリックリンクを貼って使っています。

コマンド実行時の環境変数指定

コマンドを実行するときはPATHを通すかフルパスで指定するだけではなく、 TS_ROOT 環境変数も指定する必要があります。

例えば、1段目のキャッシュを全クリアするときは Clearing the Cache の説明では traffic_server -Cclear ですが、このrpmの場合は

TS_ROOT=/opt/trafficserver-first /opt/trafficserver-first/bin/traffic_server -Cclear

と実行する必要があります。