我ながら意味不明なタイトルだ(笑)
解決したい問題
- 多数のサーバやサービスがある。
- 環境ごとの設定(db名や接続ユーザ、パスワードなど)が各サービス(リポジトリ)ごとに保存されている。
- 設定内容がバラバラに保存されてしまう。
- 時に設定値が二重管理どころか多重管理になってしまう。
- 環境ごとの設定(db名や接続ユーザ、パスワードなど)が各サービス(リポジトリ)ごとに保存されている。
- 設定値をリポジトリに保存するときに暗号化しないといけない。
- 差分管理ができず、デプロイプロセスが複雑化してしまう。
特にマイクロサービスアーキテクチャを採用している場合は、設定値が分散しがちなのでこれを何とかします。
解決方法
- 設定情報を一元管理する。
- 外部からアクセスできないローカルに設定専用gitサーバを立てて、そこのリポジトリに設定情報をplainで保存。
- これで機密性と差分更新を両立。
- 環境ごとにetcdを立てる。
- 設定値をこのetcdに投入する。
- 本番環境のetcdはクラスタリングで冗長化。
- 各サーバにはconfdを導入。
- 設定値をetcdからfetchして、各サービスごとの都合に合わせた設定ファイルを生成する。
導入方法
例として、laravelの.env
を生成するケースを考えます。
ローカルの設定専用gitサーバを立てるのは省略しますが、このgitは外部から閉じている場所に設置し、アクセスできる人は極力限定しましょう。
etcd
Ubuntuならapt一発で導入可能です。
sudo apt-get install etcd
etcdは2379/tcp
, 2380/tcp
の2つのポートを使用しますが、前者はconfdから接続する時に使用するポート、後者はetcd同士間のクラスタリングで使用するポートです。
デフォルトでは127.0.0.1
のみをlistenしてますので、必要があれば、ETCD_LISTEN_CLIENT_URLS
でlistenするIPを設定してください。
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
(この記事ではetcdのクラスタリング方法は省略します。)
データの投入は標準のetcdctl
でもできますが、いちいちキーごとにデータを投入するのは面倒なので、yamlファイルから一括投入できるruby gemを導入します。
gem install etcd-tools --no-rdoc --no-ri
このgemでインストールされる/usr/local/bin/yaml2etcd
を使ってデータを投入します。
まずは設定ファイルをyaml形式で書きましょう。
env: production
app:
name: laravel
key: base64:y/Xg+4eS2+qBoVpl9izzCSDsvfEB7CgwkQu4FBbBl5I=
debug: true
log_level: debug
url: http://example.comlocalhost
db:
connection: mysql
host: db.production.example.com
port: 3306
name: laravel
user: user
password: passowrd
redis:
host: redis.production.example.com
port: 6379
password: nulll
mail:
driver: smtp
host: smtp.production.example.com
port: 25
develop, stagingなどの環境についても、それぞれ設定ファイルも作成します。
これをリポジトリに入れて管理しましょう。
あとは、yaml2etcd
でyamlデータを一括投入するだけ。
今回は環境ごとにetcdを立てることを前提としているので、各サーバの/v1
ディレクトリにそれぞれインポートします。
yaml2etcd -u http://etcd.production.example.com:2379/ -r /v1 < /var/tmp/conf/v1/production.yml
yaml2etcd -u http://etcd.staging.example.com:2379/ -r /v1 < /var/tmp/conf/v1/staging.yml
yaml2etcd -u http://etcd.develop.example.com:2379/ -r /v1 < /var/tmp/conf/v1/development.yml
デプロイサーバ(Jenkinsなど)からインポートをするタスクを登録するとwebインターフェイスから実行できます。
投入されたデータの確認についても、標準のetcdctl
ではなくetc2yaml
を使うとyaml形式で一括ダンプができるのでこちらの方が便利だと思います。
etcd2yaml -u http://etcd.production.example.com:2379/ -r /v1
confd
さて、このままではetcdに設定データが投入されているだけです。
各サーバで設定ファイルを生成するために、confd
を導入しましょう。
confdについてはconfd を試すに詳しい解説があるので、ポイントだけ。
残念ながらUbuntuのパッケージは無いようなので、開発元からバイナリをダウンロードします。
これを/usr/loca/bin
あたりにコピーして、ファイル名変更と実行属性をつけておきます。
cd /usr/local/bin
get https://github.com/kelseyhightower/confd/releases/download/v0.11.0/confd-0.11.0-linux-amd64
mv confd-0.11.0-linux-amd64 confd
chmod 755 confd
confdの設定ファイルを置くディレクトリを生成します。
mkdir -p /etc/confd/{conf.d,templates}
ここにエントリファイルと、設定ファイルのテンプレートファイルを配置します。
[template]
prefix = "/v1"
src = "laravel.tmpl"
keys = [
"/",
]
dest = "/path/to/.env"
owner = "www"
mode = "0600"
そして、etcdからとってきたデータを使って.env
を生成する元となるテンプレートファイルを作成します。
APP_NAME={{getv "/app/name"}}
APP_ENV={{getv "/env"}}
APP_KEY={{getv "/app/key"}}
APP_DEBUG={{getv "/app/debug"}}
APP_LOG_LEVEL={{getv "/app/log_level"}}
APP_URL={{getv "/app/url"}}
(以下略)
設定ファイルにprefix="/v1"
を指定しているので、テンプレートファイル側に"/v1"を指定する必要はありません。
詳しい記述方法は本家のドキュメントを参照してください。
ここまで準備が済めば、あとはconfdを実行して設定ファイルを生成するだけです。
実行方法は2つあります。
1. confdをデーモンとして起動して、設定値が変化したら自動的に再生成する
confdに-watch
オプションを指定すると、デーモンとして起動してenvdの値が変更されたら自動的に設定ファイルを再生成してくれます。
confd -backend etcd -node http://envd.production.example.com:2379 -watch
developmentやstaging環境などではこちらが便利でしょう。(デーモンの管理はsupervisord下に置くと楽です)
新しい設定ファイルが生成された時に実行されるコマンドをエントリファイルのreload_cmd
に指定しておくと、新しい設定ファイルの反映も自動化させることも可能です。
2. confdをデーモンとして起動せず、手動で設定ファイルを生成する
confdに-onetime
オプションを指定すると、ワンショットで設定ファイルを生成することができます。
confd -backend etcd -node http://envd.production.example.com:2379 -ontime
production環境の場合、最初はこちらを利用すると安全だと思います。
Tips
設定パスはバージョニングしよう
設定のパスにAPIのURLと同じような/v1
というバージョニングを入れています。これは何故でしょうか。
システムの拡大に伴い設定値のキーを追加していくと、キー構成がグシャグシャになってしいまい、キー構成を再構成したくなる日が必ずやってきます。
設定パスにバージョニングを入れておけば、新しく/v2
というバージョンを追加することで、既存の/v1
を使っているconfdに影響を与えることなく新しいキー構成を作成することができるようになります。
その後、/v1
を参照しているconfdを/v2
を参照するように修正すれば、サービスを動作させながら新しいキー構成に移行させることが可能になります。
設定ファイルの分割
設定ファイルが巨大になってくると修正が大変になってきます。
その場合は、yaml2etcd
の-r
オプションでインポートするパスを調整すると、階層ごとにyamlファイルを分割できます。
yaml2etcd -u http://etcd.production.example.com:2379/ -r /v1 < /var/tmp/conf/v1/production/main.yml
yaml2etcd -u http://etcd.production.example.com:2379/ -r /v1/database < /var/tmp/conf/v1/production/database.yml
それでは良い設定項目デプロイライフを。