AnsibleとServerspecの実行をRakeタスクにまとめる

17 Dec 2015, 19:25 By mosuke5

こんにちはもーすけです。
私が趣味で開発しているシステムでは、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.cfgsudo_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

まとめ

タスクによってその実行方法が異なることは運用上とても不便です。 とくに慣れている言語でタスクが整理されていることによって以下のような恩恵を受けれるようになります。

  • テストの実行し忘れがなくなる
  • ドキュメントも残しやすくなる
  • 慣れている言語で整理されることによりメンテしやすくなる。
  • チームメンバーへの運用方法の伝授も楽になる

保守性を考えて、慣れている言語やアプリケーションのスタックに合わせてタスクを統一することを選択肢の一つに入れるといいかもしれません。

フィードバック・相談(β)

本記事に対して、執筆者にフィードバックや聞きたいことがあればこちらのフォームからご記入ください。執筆者に問い合わせてみる

このエントリーをはてなブックマークに追加
comments powered by Disqus