SystemdによるCloud DI実装を考えてみた

2015.08.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

藤本です。

みなさん、CDP(Cloud Design Pattern)をご存知でしょうか。私はつい先日知りました。インフラエンジニアが一度はぶつかる問題に対して、AWSによるベストプラクティス、ノウハウ集となります。 素晴らしいです。

私は中でもCloud DIパターンに感銘を受けました。

OSに依存しない外部パラメータで環境の差分を吸収できるなんて素敵すぎる! 前職でこの考え方を知っていれば。。。ってことはさておき。

概要

Systemd

について書こうとしたら既に当ブログで紹介されていました。 systemd超入門

Systemdからサービス、プロセスの取り扱いが大きく変わりました。SysVinitやUpstartのようにスクリプトによりプロセスを操作する考えではなく、設定ファイルとcgroupでプロセスのライフサイクルを管理する考えとなりました。

そこでSysVinitやUpstartではサービス起動スクリプト内で実装していたCloud DIの実装をどうすればSystemdで実現できるのか考えてみましたのでご紹介します。 もっとシンプルに実装可能な方法があればフィードバックください。

Amazon Linux、RHEL6/CentOS6(SysVinitやUpstart)の実装方法は以下のエントリーをご覧ください。 実践Cloud DIパターン – 環境ごとにApache設定ファイルを読み分ける

環境

OS : CentOS 7 AMI : CentOS 7 x86_64 (2014_09_29) (ami-89634988) Systemd : systemd-208-20.el7_1.5.x86_64 Apache : httpd-2.4.6-31.el7.centos.x86_64

想定ケースは以前のエントリーと同じ、環境に応じてEC2インスタンスのタグを割り当て、ApacheのVirtualHostを分けることとします。

# cat /etc/httpd/conf/httpd.conf
(略)
Include conf.modules.d/*.conf
Include conf.d/virtualhost/${HTTPD_ENVIRONMENT}.conf
(略)
DocumentRoot "/var/www/html"
(略)

# cat /etc/httpd/conf.d/virtualhost/dev.conf

DocumentRoot /var/www/html/dev/

# cat /etc/httpd/conf.d/virtualhost/prod.conf

DocumentRoot /var/www/html/prod/

# cat /var/www/html/index.html
index page

# cat /var/www/html/dev/index.html
development page

# cat /var/www/html/prod/index.html
production page

やってみた

一言で言うと、PreScriptでEnvironmentFileの書き換えを行うことでhttpdに動的な環境変数を渡すことでき、実現できました。 EnvironmentFileの評価がExecStartPreよりも後だということがポイントとなりました。 (サービス起動のシーケンスはまだ追えていませんが、、)

それではやってみましょう。

  1. EnvironmentFile書き換えスクリプト作成 httpdサービスはデフォルトでEnvironmentFileに/etc/sysconfig/httpdを指定しています。 EnvironmentFileは複数指定可能ですので、今回は新しく指定するファイルを作成します。 作成したファイルに環境変数を追記するスクリプトを作成します。
# touch /etc/sysconfig/httpd-env

# vi /usr/local/sbin/httpd-env.sh
〜〜〜
#!/bin/sh

ENV_FILE=/etc/sysconfig/httpd-env

INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
ENV_TAG=$(aws ec2 describe-tags \
--filters "Name=resource-id,Values=$INSTANCE_ID" "Name=key,Values=Environment" \
--query "Tags[].Value" --output text)

echo $ENV_TAG > $ENV_FILE
〜〜〜

# chmod 744 /usr/local/sbin/httpd-env.sh
  1. Unitファイルに設定追加 UnitファイルにExecStartPre、EnvironmentFileを追加します。 Unitファイルを修正する場合、/usr/lib/systemd/system/配下のファイルを/etc/systemd/system/配下にコピーした上で修正しましょう。 /etc/systemd/system/配下が優先的に読み込まれます。
# cp -a /usr/lib/systemd/system/httpd.service /etc/systemd/system/

# vi /etc/systemd/system/httpd.service
〜〜〜
[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/httpd
EnvironmentFile=/etc/sysconfig/httpd-env (追記)
ExecStartPre=/usr/local/sbin/httpd-env.sh (追記)
ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
ExecStop=/bin/kill -WINCH ${MAINPID}
# We want systemd to give httpd some time to finish gracefully, but still want
# it to kill httpd after TimeoutStopSec if something went wrong during the
# graceful stop. Normally, Systemd sends SIGTERM signal right after the
# ExecStop, which would kill httpd. We are sending useless SIGCONT here to give
# httpd time to finish.
KillSignal=SIGCONT
PrivateTmp=true

[Install]
WantedBy=multi-user.target
〜〜〜
  1. Unitファイルのリロード Unitファイルを作成、変更した際はSystemdの依存関係を再構成する必要があります。
# systemctl daemon-reload
  1. 動作確認 まずはEnvironmentタグにdevを割り当てます。 EC2_Management_Console

httpdサービスを起動します。

# systemctl start httpd

HTTPアクセスします。

# curl http://localhost/
development page

/etc/httpd/conf.d/virtualhost/dev.confに記述したVirtualHostが適用されていますね。

次にEnvirontmentタグにprodを割り当てます。 EC2_Management_Console

httpdサービスを再起動します。

# systemctl stop httpd
# systemctl start httpd

HTTPアクセスします。

# curl http://localhost/
production page

こちらも/etc/httpd/conf.d/virtualhost/prod.confに記述したVirtualHostが適用されていますね。

まとめ

いかがでしたでしょうか? 環境が変わっても同じAMIからデプロイしたEC2インスタンスを設定し直すことなく(OSにログインする必要なく)、システムを作ることができます。 仮想化ならではと言った感じではないでしょうか。

SysVinit、Upstart世代の私はまだまだSystemdの機能を追えておらず、今はまだ融通がきかないイメージが強いです。 ただSystemdは今年の4月にDebian、Ubuntuでも採用され、ほとんどのLinuxディストリビューションのデフォルトのシステム管理デーモンとなり、あまり悠長なことは言っていられない状況となりました。 Amazon Linuxに採用されるまでにはある程度使えるようになっておきたいところですね。

ちなみに色々なパターンを試してみたけどダメだったことも記載しておきます。

  • Unitファイル内のEnvironmentにコマンドを利用できない
  • ExecStartPreにexportやsourceが使えない(/から始まる必要がある)
  • ExecStartPreに/bin/shでラップして、環境変数にコマンド結果をセットしてexportしても値を取り出せず
  • 同じくExceStartを/bin/shでラップして、環境変数にコマンド結果をセットしてexportすると環境変数は適用され、プロセスは起動したが数分後にプロセスが落ちる