Posted at

etcdとconfdでらくらく大規模設定項目デプロイライフ

More than 1 year has passed since last update.

我ながら意味不明なタイトルだ(笑)


解決したい問題


  • 多数のサーバやサービスがある。


    • 環境ごとの設定(db名や接続ユーザ、パスワードなど)が各サービス(リポジトリ)ごとに保存されている。


      • 設定内容がバラバラに保存されてしまう。

      • 時に設定値が二重管理どころか多重管理になってしまう。





  • 設定値をリポジトリに保存するときに暗号化しないといけない。


    • 差分管理ができず、デプロイプロセスが複雑化してしまう。



特にマイクロサービスアーキテクチャを採用している場合は、設定値が分散しがちなのでこれを何とかします。


解決方法


  • 設定情報を一元管理する。

  • 外部からアクセスできないローカルに設定専用gitサーバを立てて、そこのリポジトリに設定情報をplainで保存。


    • これで機密性と差分更新を両立。



  • 環境ごとにetcdを立てる。


    • 設定値をこのetcdに投入する。

    • 本番環境のetcdはクラスタリングで冗長化。



  • 各サーバにはconfdを導入。


    • 設定値をetcdからfetchして、各サービスごとの都合に合わせた設定ファイルを生成する。



スクリーンショット 2017-04-22 17.41.00.png


導入方法

例として、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を設定してください。


/etc/default/etcd

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形式で書きましょう。


/var/tmp/conf/v1/production.yml

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}

ここにエントリファイルと、設定ファイルのテンプレートファイルを配置します。


/etc/confd/conf.d/laravel.toml

[template]

prefix = "/v1"
src = "laravel.tmpl"
keys = [
"/",
]
dest = "/path/to/.env"
owner = "www"
mode = "0600"

そして、etcdからとってきたデータを使って.envを生成する元となるテンプレートファイルを作成します。


/etc/confd/templates/laravel.tmpl

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に影響を与えることなく新しいキー構成を作成することができるようになります。

スクリーンショット 2017-04-22 18.06.45.png

その後、/v1を参照しているconfdを/v2を参照するように修正すれば、サービスを動作させながら新しいキー構成に移行させることが可能になります。

スクリーンショット 2017-04-22 18.06.48.png


設定ファイルの分割

設定ファイルが巨大になってくると修正が大変になってきます。

その場合は、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

それでは良い設定項目デプロイライフを。