こんにちはもーすけです。
私が趣味で開発しているシステムでは、Ansibleを使って、サーバ構築からアプリケーションのデプロイまで実行できるように管理しています。
そして、serverspecを使って、そのサーバに対するインフラテストも行っています。しかし、その運用にいくつか課題点がありました。
その課題点についてと、課題点へ対策したことについて書きます。
課題だったこと
(課題1) デプロイとテストをそれぞれ実行していた
Ansibleでのデプロイとserverspecのテストはそれぞれ別のコマンドで実行していました。
$ ansible-playbook site.yml -i hosts
$ bundle exec rake serverspec
2つ実行することが面倒であり、面倒であるがゆえにserverspecの実行を怠ったりしていました。 これではテストの効果があまり発揮できませんね。 CIで自動実行すればいいのでは?というのがありますが、本番環境への適応はそれでいいのですが、 このシステムは開発環境はローカルPCのVagrant上に構築しており、手元でのプロビジョニングには、手動でのAnsible実行を必要としている状況でした。
(課題2) sudoパスワードをうまく管理できなかった
上のような課題1について、真っ先に以下の様にコマンドを続けることを思いつきました。
$ ansible -playbook site.yml -i hosts; bundle exec rake serverspec
しかし、この方法ではansible実行終了後にserverspecを実行する際にsudoパスワードが再度聞かれるため、
コマンドを打ったまま「放置」ができませんでした。
※もちろん、sudoパスワードを要求しないようにユーザ設定をすればできますが、多くの場合ではセキュリティ上難しかったりすると思います。ssh接続は鍵認証、sudoには必ずパスワードを要求するようにしています。
また、Ansibleもserverspecにもコマンド実行時にsudoパスワードを記述する方法があります。
Ansibleでは、ansible.cfg
にsudo_password
を記述、あるいはコマンド実行時に--extra-args
でsudoパスワードを指定できます。
serverspecでも環境変数でSUDO_PASSWORD
が指定できます。
ansible -playbook site.yml -i --extra-args='ansible_sudo_pass=xxxxxxxx'
bundle exec rake serverspec SUDO_PASSWORD=xxxxxxxx
ですが、おわかりの通り、コマンドの履歴にもパスワードが残ります。 あまり良い方法ではないと思っています。
(課題3) タスクの実行方法がバラバラ
デプロイはansibleコマンドで実行、テストはrakeで実行、他のタスクはシェルスクリプト。 といったように、タスクによって実行方法が異なってしまう状況になっていました。 運用的にとても不便でしたので、1つに統一したいと思っていました。
アプリケーションの運用に必要なことはRakeタスクで実行する
上で述べたような課題点をクリアするように、下記の要件を満たすように工夫をしました。
- デプロイ、テストが同じ形式で実行できる
- sudoパスワードをベタ書きすることなく実行できる
- sudoパスワードの入力を一回だけにする
結論は、すべてRakeタスクで実行できるようにしました。 タスク一覧を見ると以下の様な感じになりました。(※実行結果は例であり実際の内容とは少し異なる。)
$ bundle exec rake -T
rake deploy:development # Deploy to development server
rake deploy:production # Deploy to production server
rake serverspec # Run serverspec to all hosts
rake serverspec:app # Run serverspec to app server
rake serverspec:db # Run serverspec to db server
Rakefileの実装例(一部省略)
desc "Deploy and Test"
namespace :deploy do
require "io/console"
require "open3"
STDOUT.sync = true
desc "Deploy to development server"
task :development do
# sudoパスワードははじめに1回だけ聞くようにします。
sudo_password = ask_sudo_password
# デプロイとテストを同時に実行します。
deploy_and_test('development', sudo_password)
end
# デプロイとテストの同時実行関数
def deploy_and_test(env, sudo_password)
deploy_cmd = "ansible-playbook -i #{env} site.yml --extra-vars 'ansible_sudo_pass=#{sudo_password}'"
test_cmd = "bundle exec rake serverspec ENVIRONMENT=#{env} SUDO_PASSWORD=#{sudo_password}"
Open3.pipeline("#{deploy_cmd}; #{test_cmd}")
end
# sudoパスワードを要求関数
def ask_sudo_password
print "SUDO Password: "
sudo_password = STDIN.noecho &:gets
end
end
まとめ
タスクによってその実行方法が異なることは運用上とても不便です。 とくに慣れている言語でタスクが整理されていることによって以下のような恩恵を受けれるようになります。
- テストの実行し忘れがなくなる
- ドキュメントも残しやすくなる
- 慣れている言語で整理されることによりメンテしやすくなる。
- チームメンバーへの運用方法の伝授も楽になる
保守性を考えて、慣れている言語やアプリケーションのスタックに合わせてタスクを統一することを選択肢の一つに入れるといいかもしれません。