Ansibleとは?
対象のサーバーへ、アプリケーションを動かすために必要なモジュールやミドルウェアなどをインストールしたり、ネットワーク構築などを自動化してくれる構成管理ツールの一つ。
似たような構成管理ツールとしては、
- Chef(シェフ)
- Puppet(パペット)
- Itamae(イタマエ)
などが挙げられます。
構成管理ツールを使うことで、
- 今まで手作業やっていた部分が自動化される
- ヒューマンエラーが減る
- 似たような環境を構築する際にもスピーディーに対応可能
といった恩恵を受けることができるので、モダンな技術への感度が高い企業ではよく採用されていますね。
Ansibleを使うメリット
先に挙げた構成管理ツールに比べると、
- YAML形式で処理を書いていくため、ノンプログラマーでも入門しやすい
- ディレクトリ構成にベストプラクティスがあるため、そのレールに乗ることでプロジェクトが変わっても内容を把握しやすい
Ansibleのデメリット
- YAML形式であるため、条件分岐や繰り返し処理ができず、似たような構成を作る際にはコピペになりがち
もっとAnsibleの概念について知りたい方は
下記のスライドがよくまとまっているので、読んでみるといいです。
ansibleのインストール
ansibleはHomebrewを使ってインストールすることができます。
$ brew install ansible
インストールができたら、ansibleコマンドが使えるか確認しましょう。
ターミナル上でansible --version
を実行した際、次のような結果が表示される時は、ansibleがインストールされている証拠です。
$ ansible --version
ansible 2.8.2
config file = /Users/yuta_ushizima/yuta-ushijima-git/workspace/Ansible/qiita_clone_2019_ansible/ansible/ansible.cfg
configured module search path = [u'/Users/yuta_ushizima/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/lib/python2.7/site-packages/ansible
executable location = /usr/local/bin/ansible
python version = 2.7.15 (default, Jan 12 2019, 21:07:57) [GCC 4.2.1 Compatible Apple LLVM 10.0.0 (clang-1000.11.45.5)]
ansibleを動かすための基本的なコマンド
ここでは、ansibleを動かすときによく利用する代表的なコマンドを紹介します。
ansibleコマンド
指定したホストに対して、オプションで指定した処理を実行する時に使います。
【ansible-Working with Command Line Tools-】
https://docs.ansible.com/ansible/latest/cli/ansible.html
公式ではいくつかオプションが紹介されていますが、モジュールを指定して使うことが多いです。
$ ansible -i hosts <IPアドレス> -m <モジュール名>
ansible-playbookコマンド
作成したplaybookを実行する際のコマンドです。
【ansible-playbook-Working with Command Line Tools-】
https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html
playbook名は、公式サイトだとplaybook.ymlとなっていますが、拡張子がYAMLであれば好きな名前をつけられます。
staging.ymlやproduction.ymlといったように、環境ごとの名前で作成することが業務だと多いですね。
$ ansible-playbook -i <インベントリファイル> <playbook名>.yml
そのほかのコマンドについて
そのほかのコマンドについては、下記の公式ドキュメントにまとめられていますので、気になる方は参照してみてください。
【Working with Command Line Tools】
https://docs.ansible.com/ansible/latest/user_guide/command_line_tools.html
サンプルリポジトリ
Take off Railsの課題であるQiitaのクローンアプリを作成した際に、Ansibleを使ってEC2にモジュールのインストールなどを自動化しました。
サンプルリポジトリのディレクトリ構成
ansinle/
- roles/
- common/
- tasks/
- main.yml
- git/
- tasks/
- main.yml
- mysql/
- tasks/
- main.yml
- templates/
- my.conf.j2
- nginx/
- tasks/
- main.yml
- templates/
- nginx.conf.j2
- nodejs/
- tasks/
- main.yml
- ruby/
- files/
- .gemrc
- tasks/
- main.yml
- templates/
- rbenv_system.sh.j2
- vim/
- tasks/
- main.yml
- vars/
- common.yml
- ansible.cfg
- hosts
- production.yml
.gitignore
requirements.txt
構成については、公式サイトのベストプラクティスを参考にしながら、必要な部分に絞り込んだものにしています。
requirements.txtについて
ansibleは内部でpythonを使っているのですが、自動化を実行するためにいくつかライブラリのインストールが必要となっています。
個別にインストールしてもいいのですが、大抵はrequirements.txt
というテキストファイルが用意されていると思うので、そちらを使うと便利です。
使う際には、pip
コマンドを使って、次のように実行します。
$ pip install -r ../requirements.txt
こうすることで、requirements.txt
に書かれたライブラリを順次インストールしてくれるんですね。
ご自身でplaybookを作成する場合には、リモートリポジトリに置いておくと共同開発する際に親切かと思います。
rolesディレクトリについて
rolesディレクトリ配下にある個々のディレクトリは、インストールや構築を行うモジュール名・ミドルウェア名で切ります。
なので、すでにAnsibleで運用されているプロジェクトに参画する際、
rolesディレクトリを見れば、どんなモジュールやミドルウェアが使われているかがわかる
というわけですね。
commonディレクトリについて
commonディレクトリには、対象のサーバーで共通する設定などを扱います。
---
- name: Create app dir
become: yes
file:
path: /var/www
state: directory
owner: ec2-user
group: ec2-user
mode: 0777
この設定例では、/vars/www
というディレクトリを作成しています。
become: yes
をつけることでsudo
をサーバー上でつけて実行するのと同じ意味を持ちます。
tasksディレクトリについて
tasksディレクトリにはそれぞれのrolesに応じて、main.ymlというファイルが置かれています。
このmain.ymlには、実際にインストールやパスの反映などのような作業を自動化するための処理を記述します。
---
- name: Install packages
become: yes
yum:
name: "{{ packages }}"
vars:
packages:
- git-core
- gcc
- gcc-c++
- gdbm-devel
- libffi-devel
- libselinux-python
- libyaml-devel
- mysql
- mysql-devel
- ncurses-devel
- openssl-devel
- sqlite-devel
- readline-devel
- zlib-devel
上記はサンプルリポジトリのRubyにおけるmain.ymlです。
ここでは、yumを使ってrubyを動かすために必要なライブラリをインストールしています。
templatesディレクトリについて
temlatesディレクトリには、Jinja2というPython製のテンプレートエンジンを使って記述します。
【Jinja2】
https://jinja.palletsprojects.com/en/2.10.x/
そのため、ファイルの拡張子が.j2
となっているんですね。
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$gzip_ratio"';
access_log /var/log/nginx/access.log main;
(中略)
例えば、上記はNginxのnginx.conf.j2なのですが、記述はnginx.conf
そのままですよね。
このnginx.conf.j2
は、nginxのmain.yml
で次のように呼び出すことで、対象のサーバーへ配置されます。
(中略)
- name: Put local.conf
become: yes
template:
src: roles/nginx/templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
(中略)
templateという親keyに対して、srcとdestという子keyがあります。
src...テンプレートが置かれているpathを記述(ansibleディレクトリからみて絶対パスで)
dest...対象のサーバー内でテンプレートを配置するpathを記述
templateのsrcとdestに対してpathをvalueとして渡してあげれば、ansibleを動かした際に対象のサーバーへテンプレートに記述された内容で配置されるという仕組みです。
filesディレクトリについて
filesディレクトリは、templatesと似ているのですが、Jinja2のテンプレートを使わずに、対象のサーバーへ配置するのがポイントです。
あまり使われないので、基本的にはtemplatesを使えばOKなのですが、サンプルリポジトリ上ではrubyのroleで隠しファイルの.gemrc
を配置するようにしています。
呼び出すときは、main.ymlで次のように記述します。
(中略)
- name: Copy .gemrc to ~/.gemrc to Avoid to Install rb-docs
become: yes
copy:
src: .gemrc
dest: ~/.gemrc
(中略)
copyという親keyに対して、srcとdestという子keyがあります。(templateと同じ)
src...対象のファイルをそのまま記述(パス指定でもOK)
dest...対象のサーバー内でファイルを配置するpathを記述
filesディレクトリにあるファイルを対象のサーバーのpathへコピーすることで、ansibleを動かした際に配置されるという仕組みです。
varsディレクトリについて
varsディレクトリでは、ansible内の各roleで共通で使用する変数をcommon.ymlというファイル名でまとめます。
ruby_version: 2.6.2
bundler_version: 2.0.1
db_pass: password
db_user: root
db_name: local
local_db_user: local
public_ip: 127.0.0.1
application_name: qiita_clone_2019
サンプルリポジトリでは、上記のような形で、rubyのバージョンやMySQLのパスワードなどをまとめています。
各roleのmain.ymlで呼び出す際には、次のように指定しましょう。
(中略)
- name: Install Ruby
shell: bash -lc "RUBY_CONFIGURE_OPTS="--disable-install-rdoc" rbenv install -s {{ ruby_version }}"
(中略)
ダブルクォーテーションと{{}}
を使うことで、 文字列展開のような感じでvars/common.ymlに記載された変数を呼び出せます。
hostsファイルについて
このhostsファイルでは、対象のサーバーへの接続情報を記述します。
やり方は二つあって、
- 自分のマシンの`~/.ssh/cofig`でSSH接続に関する名前解決を行い、その情報を記述する
- 直接IPアドレスを記述し、Ansible側で接続情報を記述する
主にこの二つですね。
個人開発や勉強用であれば、前者で問題ないです。
が、業務では開発者の担当フェーズごとによる権限付与などもansibleで自動化するため、後者を選ぶことが多いですね。
今回は、後者で行います。
ansibleのディレクトリ配下に、拡張子なしでhostsというファイルを作成し、そこにIPアドレスを記述します。
[production-app]
18.182.21.236
[production-app]という記述は、どの環境に対するIPアドレスまたはホスト名(~/.ssh/configで名前解決する場合のHostName)なのかを示しています。
後述するproduction.ymlで利用します。
production.ymlについて
アプリケーションを本番環境にデプロイする際には、環境ごとにそれぞれEC2インスタンスなどを立てるのが基本です。
production.ymlのように、
<環境名>.yml
といったファイルを作成し、自動化する内容を記述していきます。
---
- name: Playbook for production
hosts: production-app
become: yes
vars_files:
- vars/common.yml
roles:
- git
- nginx
- nodejs
- mysql
- ruby
- vim
サンプルリポジトリでは、hosts
ファイルに記述した[production-app]
をhostsとして指定することで、どのサーバーに接続するかを示しています。
vars_filesのkeyに指定しているのは、利用する環境変数が書かれたファイルですね。
rolesについては、利用するroleをそれぞれリスト形式で記述しています。
Ansibleの動かし方
pingモジュールを使って、接続確認をしてみよう
playbookの準備ができたら、ローカルでansibleコマンドを用いて動かします。
が、まずは対象のサーバーに対してキチンとansibleが接続できるかどうかを確認しましょう。
任意のテキストエディタを使って、hostsファイルを次のように編集してください。
[<任意の名前>]
<IPアドレス> ansible_user=ec2-user ansible_ssh_private_key_file=<ec2にsshログインするためのpemファイル>
設定例としては、下記のような感じですね。
[production-app]
3.113.242.122 ansible_user=ec2-user ansible_ssh_private_key_file=~/.ssh/yuta-ushijima.pem
編集ができたら保存して、ansibleのpingモジュールを使って接続確認します。
$ ansible-playbook -i hosts production.yml
うまく接続ができると、ターミナルに次のような結果が表示されるはずです。
3.113.242.122 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
逆に設定項目に不備があったり、SSH接続するIPアドレスが間違っていたりした場合には、次のようなエラーが表示されます。
3.113.242.122 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ec2-user@3.113.242.122: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).",
"unreachable": true
}
unreachablke: true
というのが、対象のサーバーへのアクセスができなかったというエラーなので、このような表示がでた場合には、設定項目などを確認するようにしましょう。
ansible-playbookコマンドでモジュールのインストールを自動化しよう
pingモジュールを使って接続確認ができたら、今度はplaybookコマンドを使って動かします。
pingコマンドを使うときに編集した、hostsファイルを再び次のように編集します。
[production-app]
3.113.242.122
[production-app]
と3.113.242.122
の部分に関しては、各々の環境に応じて変更してくださいね。
保存ができたら、playbookがあるディレクトリにいることを確認して、次のコマンドを実行します。
$ ansible-playbook -i hosts staging.yml
エラーが発生した場合は、エラーメッセージをよく読み、都度対応していきましょう!