#はじめに
ラクス Advent Calendar 2017の8日目の記事です。
12月も1週間が終わり、確実にクリスマスが近づいてきていますね!
昨日はosamu_fujikakeさんの超過酷!大阪港開港記念イベントフェリーハッカソンに行ってきた!!でした。
本日はQiita初投稿の私が担当いたします。
さて本日のトピックですが、最近社内のいろんな場所で「Ansible」というワードを耳にすることが多いのですが、、、
Ansible? 構成管理ツール? バージョン管理でなく?
こんな状態でしたので、今回0から学習してみることにしました。
本記事はAnsibleをはじめる人に。に記載のハンズオンに取り組んで得た知識+色々調べたことを初学者の言葉でざっくりと残し、大枠を捉えることを目的とします。
注)対象の読者は「What's "Ansible"?」な方一択ですのであしからず・・・
#Ansibleとは
Ansibleは構成管理ツールと呼ばれているものの1種であり、類似ツールにはPuppetやChefなどが挙げられます。
構成管理ツールとは、シェルスクリプトのように「マシンの処理を自動化する」という観点ではなく、「マシンを目的の状態にする」という観点で作成されたツールのことを指します。
これだけ言われてもよくわからないなぁという感じだと思いますので、「対象のマシンにユーザを追加する」を例に違いを確認します。
「マシンの処理を自動化する」という観点では「ユーザーを追加する」という操作を実行してしまうため、既にユーザーが存在する場合、何もエラーハンドリングがなされていなければ再実行時に「既にユーザーが存在する」というエラーとなってしまいます。
対して、「マシンを目的の状態にする」という観点では「ユーザーが追加されている状態にする」ということが重要になります。「ユーザーが存在するかどうか」という判断は構成管理ツールが行ってくれるためエラーにはなりません。
どうでしょう、違いはご理解いただけたでしょうか?
エラーになるかならないかだけじゃないか?エラーをなめちゃいけません。えぇいけませんとも。
#Ansibleの特徴
Ansibleの特徴として以下のものがよく挙げられています。
- エージェントレスである
- 何度同じ操作をしても同じ状態になる(冪等性)
- 設定ファイルがシンプルに記述できる
実際にハンズオンに取り組んでみると「設定ファイルがシンプルに記述できる」の部分は多少知識を必要とはしますが、書いてある内容は**「なるほど」**となるのではないでしょうか。
#Ansibleハンズオン
Ansibleの説明が長くなってしまいましたが本題はここからです。
Ansibleをはじめる人に。で紹介されていたものを実際に試して見ました。
構成管理と言った知識を持たない私ではわからないものもありましたのではじめに登場人物を整理しましょう。
#####ハンズオンで使われているソフトウェア
- VirtualBox:言わずと知れた仮想化ソフト
- Vagrant:VirtualBoxなどの仮想化ソフトの操作を代行してくれます。使用方法までは割愛しますがOSインストール済みVMの作成、ネットワーク設定やSSH環境の整備までやってくれちゃいます。(素晴らしい)
- Ansible:本日の主人公です。ここまで律儀に読んでくださった方~~、読み飛ばした方~~には説明不要でしょう。
大きくはこの3つが登場人物です。
Vagrant+VirtualBoxでハンズオン用VMのを用意し、Ansibleを試していく。
これがハンズオンの流れとなっています。
自分のPCからはハンズオン用VMにsshで接続してハンズオンを実行していくのでなんとも手軽に試すことができました。(幸せ)
#Ansibleハンズオン用VMの準備
さて、Ansibleハンズオンを行うためにVagrantでVMの準備を行って行きます。
VagrantではBoxとVagrantfileをもちいてVM環境を作成します
BoxとはVMのベースになるイメージですハンズオンではCentOS 6.7でしたが他のものも色々用意されているようです。
Boxは以下のコマンドで用意しました。
vagrant box add centos6.7 https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box
ではもう1つのVMの構成を記述しておくVagrantfileの中身を見ていきます。
Vagrant.configure(2) do |config|
config.vm.define "controller" do |node|
node.vm.box = "centos6.7"
node.vm.hostname = "controller"
node.vm.network :private_network, ip: "192.168.100.10"
node.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2210
end
config.vm.define "target" do |node|
node.vm.box = "centos6.7"
node.vm.hostname = "target"
node.vm.network :private_network, ip: "192.168.100.20"
node.vm.network :forwarded_port, id: "ssh", guest: 22, host: 2220
end
end
なんとなく意味合いはわかりますね。
Vagrantfileの中身からも予想できるかと思いますがハンズオンではcontrollerとtargetの環境を準備します。
controller(管理用マシン)でAnsibleを実行し、target(管理対象マシン)の状態を操作するわけですね。
あとはVagrantfileと同じ階層でvagrant up
コマンドを実行しでVM環境を構築します。
ここまでが完了すると以下のような状態が出来上がっているわけです。
#Ansibleを準備する
ハンズオンの環境もできたのでAnsibleを準備していきましょう。
controllerにSSH接続してAnsibleをインストールしていきます。
Ansibleの管理用マシンには、
Python 2(バージョン2.6または2.7)またはPython 3(バージョン3.5以上)がインストールされているマシン
という要件があります。[詳細]
ですので先にPythonのバージョンを確認しましょう。
[root@controller ~]# python --version
Python 2.6.6
やっとAnsibleの出番です。
yumコマンドでインストールします。
[root@controller ~]# yum install ansible
省略
[root@controller ~]# ansible --version
ansible 2.4.1.0
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.6/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.6.6 (r266:84292, Jul 23 2015, 15:22:56) [GCC 4.4.7 20120313 (Red Hat 4.4.7-11)]
Ansibleがインストールできました。
Ansibleを使うための設定をしていきます。
AnsibleではSSH接続によって管理対象のマシンに実行していくため、パスワード無しでログインできるほうが何かと便利なためその設定(公開鍵を送り込む)をしていきます。詳しくはこちら
[root@controller ~]# ssh-keygen -t rsa
特に設定しないのでEnter
[root@controller ~]# ssh-copy-id root@192.168.100.20
#Ansibleを試す①
やっとAnsibleを試すことができる状態になりました。
後は設定ファイルを記述してコマンドを実行するだけです。
Ansibleでは、管理対象をまとめて記述しておくinventoryファイルと
管理対象の構成内容を記述しておくPlaybookという2つの設定ファイルが存在します。
まずは管理対象をまとめて記述しておくinventoryファイルから記載していきます。
[root@controller ~]# mkdir /ansible
[root@controller ~]# cd /ansible
[root@controller ansible]# mkdir inventory
[root@controller ansible]# vi inventory/hosts
[target]
192.168.100.20
今回targetが1つだけなので非常にシンプルです。
targetというグループに192.168.100.20が属しているという意味合いになります。
(本来ならグループ分けが必要だったりと簡単ではないらしいですが・・・)
次に、管理対象の構成内容を記述しておくPlaybookを作っていきます。
PlaybookはAnsibleのメイン部分と言っても過言ではないでしょう。多分!
message: "Hello Ansible !"
fruits:
apples:
amount: 10
bananas:
amount: 20
oranges:
amount: 30
targets.yml
はPlaybookではないですよ。
グループ変数を記述しているファイルになります。
YAML形式に関しては割愛します。(とにかくスペースの数が味噌です)
- hosts: targets
user: root
tasks:
- name: output message.
debug: msg="{{ message }}"
- name: output fruits
debug: msg="We want {{ item.value.amount }} {{ item.key }} !"
with_dict: "{{ fruits }}"
こちらのtest.yml
がPlaybookです。実行するグループと実行するユーザ、実行内容が記載されています。
実行結果を見た方がわかりやすいのでansible-playbook
コマンドで実行します。
[root@controller ansible]# ansible-playbook -i inventory/hosts test.yml
PLAY [targets] ****************************************************************
GATHERING FACTS ***************************************************************
ok: [192.168.100.20]
TASK: [output message.] *******************************************************
ok: [192.168.100.20] => {
"msg": "Hello Ansible !"
}
TASK: [output fruits] *********************************************************
ok: [192.168.100.20] => (item={'key': 'apples', 'value': {'amount': 10}}) => {
"item": {
"key": "apples",
"value": {
"amount": 10
}
},
"msg": "We want 10 apples !"
}
ok: [192.168.100.20] => (item={'key': 'oranges', 'value': {'amount': 30}}) => {
"item": {
"key": "oranges",
"value": {
"amount": 30
}
},
"msg": "We want 30 oranges !"
}
ok: [192.168.100.20] => (item={'key': 'bananas', 'value': {'amount': 20}}) => {
"item": {
"key": "bananas",
"value": {
"amount": 20
}
},
"msg": "We want 20 bananas !"
}
PLAY RECAP ********************************************************************
192.168.100.20 : ok=3 changed=0 unreachable=0 failed=0
おぉ、なんだか見覚えのあるものが実行されました。
実行したコマンドをみて見ます。
ansible-playbook -i inventory/hosts test.yml
引数に指定されているのは
- inventory
- Playbook
の2つです。
ただ、どう見てもtargets.yml
で記述したグループ変数が使われているようです。(不思議)
test.ymlで実行対象としてhosts: targets
というグループを指定したため、group_varsディレクトリから自動的にtargets.ymlが読み込まれ、定義した変数が反映されるのだとか・・・
この仕組みはAnsibleが持っている依存関係だそうで、決まったディレクトリなどから暗黙的に情報を取得します。(これは理解するのに骨が折れそう)
#Ansibleを試す②
先ほどの実行では出力をしただけでしたが、
次はAnsibleが持つモジュールを利用してtargetに対し
- パッケージのインストール > yamモジュール
- ジョブの設定 > cronモジュール
- ディレクトリの作成、ファイルの配置 > fileモジュール
- ファイルのコピー > copyモジュール
を行っていきます。
- hosts: targets
user: root
tasks:
- name: install packages from yum
yum: name={{ item }} state=latest
with_items:
- jq
- ruby
- httpd
- name: register cron job
cron: name="check ping" day="*/2" hour="12" minute="0" job="ping -c 3 192.168.100.10"
- name: create directories
file: path={{ item.path }} owner={{ item.owner }} group={{ item.group }} mode=0{{ item.mode }} state=directory
with_items:
- { "path":"/opt/ansible", "owner":"root", "group":"root", "mode":"755" }
- { "path":"/opt/vagrant", "owner":"vagrant", "group":"vagrant", "mode":"755" }
- name: copy files
copy: src=./files/hoge dest=/opt/ansible/hoge owner=root group=root mode=0755
このPlaybookは
- jq, ruby, httpdをインストール
- pingを実行するjobの設定
- ansible,vagrantディレクトリの作成
- hogeファイルのコピー
を行います。
[root@controller ansible]# ansible-playbook -i inventory/hosts main.yml
PLAY [targets] ****************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
ok: [192.168.100.20]
TASK [install packages from yum] **********************************************************************************************************************************************************************************
changed: [192.168.100.20] => (item=[u'jq', u'ruby', u'httpd'])
TASK [register cron job] ******************************************************************************************************************************************************************************************
changed: [192.168.100.20]
TASK [create directories] *****************************************************************************************************************************************************************************************
changed: [192.168.100.20] => (item={u'owner': u'root', u'path': u'/opt/ansible', u'group': u'root', u'mode': u'755'})
changed: [192.168.100.20] => (item={u'owner': u'vagrant', u'path': u'/opt/vagrant', u'group': u'vagrant', u'mode': u'755'})
TASK [copy files] *************************************************************************************************************************************************************************************************
changed: [192.168.100.20]
PLAY RECAP ********************************************************************************************************************************************************************************************************
192.168.100.20 : ok=5 changed=4 unreachable=0 failed=0
正常に実行されました!目的の4件の変更があったこともわかりやすいです。
#冪等性
ここまでのハンズオンに取り組むとAnsibleの特徴である
- エージェントレスである
- 設定ファイルがシンプルに記述できる
最後に**何度同じ操作をしても同じ状態になる(冪等性)**の確認をして締めましょう。
もう一度同じmain.yml
のPlaybookを実行します。
[root@controller ansible]# ansible-playbook -i inventory/hosts main.yml
PLAY [targets] ****************************************************************************************************************************************************************************************************
TASK [Gathering Facts] ********************************************************************************************************************************************************************************************
ok: [192.168.100.20]
TASK [install packages from yum] **********************************************************************************************************************************************************************************
ok: [192.168.100.20] => (item=[u'jq', u'ruby', u'httpd'])
TASK [register cron job] ******************************************************************************************************************************************************************************************
ok: [192.168.100.20]
TASK [create directories] *****************************************************************************************************************************************************************************************
ok: [192.168.100.20] => (item={u'owner': u'root', u'path': u'/opt/ansible', u'group': u'root', u'mode': u'755'})
ok: [192.168.100.20] => (item={u'owner': u'vagrant', u'path': u'/opt/vagrant', u'group': u'vagrant', u'mode': u'755'})
TASK [copy files] *************************************************************************************************************************************************************************************************
ok: [192.168.100.20]
PLAY RECAP ********************************************************************************************************************************************************************************************************
192.168.100.20 : ok=5 changed=0 unreachable=0 failed=0
1つも変更はなかったという結果が得られました。この対象のマシンがすでに目的の状態にあるときはなんの変更も行わない何度同じ操作をしても同じ状態になる
この性質が冪等性と呼ばれるものです。
パッケージのインストールもしませんし、ファイルのコピーもしません。
必要のない処理は全部スキップしてしまうのです。
#おわりに
今回一部ハンズオンの解説を省いた部分がありますが、大枠を捉えるを目的としていますのでご容赦ください。
社内で話題のツールであったというところから取り組んでみたハンズオンでしたが、全くの知識0ベースからでも気軽に取り組め、一定ラインまでは理解できたかと思います。(個人の感想です)
これもAnsibleの特徴故かと思います。
私が言うのも変な話ですが、皆様がAnsibleに触れるきっかけになれば幸いです。
#参考資料
- Ansibleをはじめる人に。
- サーバ管理者も開発者も知っておきたい構成管理ツールとAnsibleの基礎知識
- エージェントレスでシンプルな構成管理ツール「Ansible」入門
- VagrantとDockerについて名前しか知らなかったので試した
- ssh-copy-idで公開鍵を渡す