Ansible for the Absolute Beginner 勉強メモ
はじめに
Ansible for the Absolute Beginner の勉強メモ。自分用
ansibleのきそ
「誰に」「何をするか」を規定する。
- inventory ファイル(「誰に」)
- 管理するサーバ達の接続先の設定を入れる。ansible版のhostsに近い。複数サーバひとまとめに扱える点が先進的
- デフォルトでは
/etc/ansible/hosts
だが、-i <inventory name>
で任意に指定可能
- playbook(「何をする」)
例えば・・・
# inventory ファイル localhost server1.company.com server2.company.com [mail] server3.company.com server4.company.com [db] server5.company.com server6.company.com
# httpdを立ち上げるplaybook - name: Play 1 hosts: localhost # inventoryファイルにlocalhostが記載されている必要あり。 tasks: - name: Execute command 'date' command: date - name: Execute script on server script: test_script.sh - name: Play 2 hosts: localhosts tasks: - name: Install httpd service yum: name: httpd state: present - name: Start web server service: name: httpd status: started
- taskはリストなので、順序がある。上から順に実行される
# 全てのサーバにpingするplaybook - name: Test connectivity to target servers hosts: all tasks: - name: Ping test ping: # pingモジュールには属性が定義されていないのでこれだけでOK
module
- 上記の
command
,script
,yum
,service
に相当するもの。 例えばhttpdをインストールしたい場合、
- 従来叩くようなコマンド(
yum install httpd
)を記載する代わりに、 - 「どうあって欲しいか」を表現するyamlを書く。
- 従来叩くようなコマンド(
tasks: - name: Install httpd service yum: name: httpd state: present
moduleのyaml記法は別途redhatが定義しているため、残念ながら覚えるか調べる必要がある。
ansible-doc <module name>
で調べられる。ansible-doc ping
など
playbookの実行
ansible-playbook playbook.yml # で実行 (インベントリファイルは/etc/ansible/hostsを利用) ansible-playbook playbook.yml -i inventory.txt # インベントリファイルを明示的に指定したい場合 ansible-playbook --help # でヘルプ確認
ansibleの実行方法
ansible
コマンドを使うやり方と、ansible-playbook
コマンドを使うやり方がある。ansible
コマンドの方がプリミティブなコマンド。- 命令的記法。(
kubectl run busybox --image=busybox
と同じアイデア)
- 命令的記法。(
# ansibleコマンドの例 ansible <hosts> -a <command> -i inventory.txt ansible all -a "/sbin/reboot" -i inventory.txt # allはいつでも利用可能。インベントリファイルに含まれる全てのサーバを表す ansible <hosts> -m <module> -i inventory.txt ansible target1 -m ping -i inventory.txt
ansible-playbook
コマンド- 宣言的記法。こちらが、ansbile的には王道の書き方。
kubectl create -f hoge.yaml
と同じアイデア
# ansible-playbookコマンドの例 ansible-playbook <playbook name> ansible-playbook playbook-webserver.yaml
modules
modulesいろいろ
- system
- commands
- command, expect, raw,script, shell...
- expect :対話的な処理
- script:ローカルホスト上のscriptを直接実行できる
- files
- acl, archive, copy, file, find, replace, template...
- database
- mysql, postgresql...
- cloud
- windows
more...
command
- name: Play 1 hosts: localhost tasks: - name: Execute command 'date' command: date - name: Display reslov.conf content command: cat /etc/resolv.conf - name: Display reslov.conf content command: cat resolv.conf chdir=/etc - name: Make directory # createとあるが、ifNotExistの意味らしい command: mkdir /folder create=/folder
- command moduleは
free form
なので、適当にcat /etc/hosts
とか書いても全体をstringと解釈してくれるが、他moduleでは普通ではないことに注意。 script
ansibleのローカルマシン上のスクリプトを実行する
- いちいちローカル→リモートにコピーしなくていいので便利
- name: Play 1 hosts: localhost tasks: - name: Run a script on remote server script: /some/local/script.sh -arg1 -arg2
service
サービスのstart, stop, restartなど
どちらの記法でもOK
- name: Play 1 hosts: localhost tasks: - name: start the database service service: name=postgresql status=started
- name: Play 1 hosts: localhost tasks: - name: start the database service service: name: postgresql status: started
- name: Play 1 hosts: localhost tasks: - name: add nameserver lineinfile: path: /etc/resolv.conf line: 'nameserver 8.8.8.8'
variable
- inventoryにもplaybookにも別ファイルにも定義できる。
- inventoryのansible_host, ansible_connectionも実はvariable。
- playbookに変数を記載する例
- name: Add DNS server to resolve.conf hosts: localhost vars: # 変数はここで定義 dns_server=8.8.8.8 tasks: - lineinfile: path: /etc/resolv.conf line: 'nameserver {{ dns_server }}' # 2重カッコで括って変数を展開
- inventoryに変数を記載する例
# sample inventory file Web http_port=8081
# playbook - name: ser firewall config hosts: web tasks: - firewalld: service: https permanent: true state: enabled - firewalld: port '{{ http_port }}'/tcp parmanent: true state: disabled
- 別ファイルに変数を記載する例
# sample variable fiel - web.yml http_port: 8081 snmp_port: 161-162
- 2重かっこで変数を展開する方式は、Jinja2 templating と呼ばれている。
source: {{ http_port }} # NG source: '{{ http_port }}' # OK。基本は''で囲む source: Someting{{ http_port }}something # OK。文中のみ''は不要。
conditions (条件文)
- name: install NGINX hosts: all tasks: - name: install NGINX on debian apt: name: nginx state: present when: ansible_os_family == "Debian" and ansible_distribution_version == "16.04" # ビルトイン変数を利用 - name: install NGINX on redhat yum: name: nginx state: present when: ansible_os_family == "RedHat" or ansible_os_family == "SUSE"
- loop とwhenの併用例
- name: Install softwares hosts: all vars: packages: - name: nginx required: True - name: mysql required: True - name: required: False tasks: - name: install '{{item.name}}' on debian # リストのアイテムは文字通りitemで呼び出す。属性の呼び方はjqと同じ apt: name: '{{item.name}}' state: present when: item.required == True # whenと併用も可能。この場合nginxとmysqlだけがインストールされる loop: '{{packages}}' #変数で定義したリストを展開可能。イテレータ。
- registerの利用例 (前に実行したタスクの実行結果を引き継ぐ)
- name: check status of a service and email if its down hosts: localhost tasks: - command: service httpd status register: result # commandタスクの結果をresult変数(?)に格納。 - mail: to: aaa@gmail.com subject: service alert body: httpd service is down when: result.stdout.find('down') != -1 # findは「見つからなければ-1を返す」ので、downが見つかるとtrue
Role
ansibleの宣言的定義は、各サーバに対して、仕事・役割を振っていると考えることができる
その意味で、
role
という単位でtasksをまとめておくことで、mysqlとして動作してほしいサーバにはmysql roleを渡すだけ、webAPlサーバとして動作してほしいサーバんはhttpd roleを渡すだけ、とする。「君(サーバ)にやって欲しいこと」の単位でまとめているのでroleと呼ぶ
ansible galaxyに沢山roleが公開されているので見てみると良い
ansible-galaxy install geerlingguy.mysql
でインストール可能。ansibleが共通で参照するディレクトリ/etc/ansible/roles
に配置される。
- name: install and configure mysql hosts: db-server roles: - geerlingguy.mysql # 普通に使える
roleを作りたいときはansible-galaxy init
role名
でディレクトリを自動生成してくれる
[ec2-user@ip-172-31-3-27 ~]$ ansible-galaxy init mysql - Role mysql was created successfully [ec2-user@ip-172-31-3-27 ~]$ tree mysql mysql ・defaults ・main.yml ・files ・handlers ・main.yml ・meta ・main.yml ・README.md ・tasks ・main.yml ・templates ・tests ・inventory ・test.yml ・vars ・main.yml 8 directories, 8 files
my-playbook ・playbook.yaml ・roles ・mysql ・...
あるいは、
/etc/ansible/roles
に配置しておけば共通にみることができる/etc/ansible/ansible.cfg
のroles_path
から変更も可能- ansible-galaxy上のroleをインストールするとここに置かれる。
advanced topics
- windowsマシンにansibleから指示を出したい場合、windowsマシンでwinRMを有効化しないと使えないことに注意。
- ホスト名には
Host*
とかワイルドカードも利用可能 - custom modules : 自分でpythonで書ける。
Ex
inventory
# Sample Inventory File # ansible内で使用する別名を定義できる。ansible版のhosts。hostsで定義したホスト名を更に上書きで出来るので、注意深く定義しないと後々面倒なことになりそう。 # linuxサーバはansible_connection=ssh, windowsサーバはansible_connection=winrm # linuxサーバのパスワードはansible_ssh_pass=Password123!, windowsサーバのパスワードはansible_password=Password123! # db servers sql_db1 ansible_host=sql01.xyz.com ansible_connection=ssh ansible_user=root ansible_ssh_pass=Lin$Pass sql_db2 ansible_host=sql02.xyz.com ansible_connection=ssh ansible_user=root ansible_ssh_pass=Lin$Pass # web servers web_node1 ansible_host=web01.xyz.com ansible_connection=winrm ansible_user=administrator ansible_password=Win$Pass web_node2 ansible_host=web02.xyz.com ansible_connection=winrm ansible_user=administrator ansible_password=Win$Pass web_node3 ansible_host=web03.xyz.com ansible_connection=winrm ansible_user=administrator ansible_password=Win$Pass # サーバのグループを定義 [db_nodes] sql_db1 sql_db2 [web_nodes] web_node1 web_node2 web_node3 # 1つのサーバーが複数のグループに所属してもよい。 [boston_nodes] sql_db1 web_node1 [dallas_nodes] sql_db2 web_node2 web_node3 # グループのグループも作成可能。 [us_nodes:children] # ただし:childrenが必要。 boston_nodes dallas_nodes
playbook
- localhostsで
date
、cat /etc/hosts
を実行するplaybook
- name: 'Execute two commands on localhost' hosts: localhost tasks: - name: 'Execute a date command' command: date - name: 'Execute a command to display hosts file' command: cat /etc/hosts
module
- lineinfile, user, script, serviceを使った。
- name: 'Execute a script on all web server nodes and start httpd service' hosts: web_nodes tasks: - name: 'Update entry into /etc/resolv.conf' lineinfile: path: /etc/resolv.conf line: 'nameserver 10.1.250.10' - name: add user user: name: web_user uid: 1040 group: developers - name: 'Execute a script' script: /tmp/install_script.sh - name: 'Start httpd service' service: name: httpd state: present
variables
- name: 'Update nameserver entry into resolv.conf file on localhost' hosts: localhost vars: car_model: BMW M3 country_name: USA title: Systems Engineer tasks: - name: 'Print my car model' command: 'echo "My car''s model is {{car_model}}"' - name: 'Print my country' command: 'echo "I live in the {{country_name}}"' - name: 'Print my title' command: 'echo "I work as a {{title}}"'
conditions
- name: 'Execute a script on all web server nodes' hosts: all_servers tasks: - service: 'name=mysql state=started' when: ansible_host == "server4.company.com" # db1とかで指定したいところ。できそうだが
- name: 'Am I an Adult or a Child?' hosts: localhost vars: age: 25 tasks: - command: 'echo "I am a Child"' when: age < 18 # これは変数展開しないのか・・・ - command: 'echo "I am an Adult"' when: age >= 18 #elseがないのが微妙
- register
- name: 'Add name server entry if not already entered' hosts: localhost tasks: - shell: 'cat /etc/resolv.conf' register: command_output - shell: 'echo "nameserver 10.0.250.10" >> /etc/resolv.conf' when: 'command_output.stdout.find("10.0.250.10") == -1'
loop
- name: 'Print list of fruits' hosts: localhost vars: fruits: - Apple - Banana - Grapes - Orange tasks: - command: 'echo "{{item}}"' with_items: '{{fruits}}' # loopではなくwith_itemsというディレクティブもある。アイテム名を直接使いたいとき用?
tips
ssh fingerprintを無視する方法
atomのremote sync を使うと、
- ローカルのIDEで開発
- 任意のタイミングで内容をansible controllerに同期 できる
- shell モジュールとcommand モジュールはよく似ているが、 shellは専用のシェルが払い出されて、環境変数を使うことができ、コマンドにリダイレクトを含むこともできる。
- 変数の展開がよくわからん・・・・→jinja2 templateを学ぶべき
- デフォルトではamazon linux にはansibleを入れることが出来ない。
amazon-linux-extras | grep ansible # ansible2の設定項目があることを確認。 sudo amazon-linux-extras enable ansible2 sudo yum install -y ansible