
お久しぶりです。
ソリューションC&I本部3年目の新井です。
前回初めて記事を書いてから早1年半が経ってしまいました...
(前に書いた記事はこちら!)
とあるサービスのリプレイスプロジェクトに参画したのですが、
そこで上がったAnsibleでの構築の話。
初めてのことに対してなかなか手が出ない個人的な課題もあり、これを機にチャレンジしてみよう...
ということで、プロジェクトで使えるようになるためにAnsibleを勉強した時の話をしたいと思います!
Ansibleについて
AnsibleはIaC(Infrastructure as Code)を実現するための構成管理ツールの一つで、
YAML型式で設定ファイルを記述することにより、インフラ構築を自動的に実行することができます。
また、構築を自動化するだけでなく、設定ファイルで管理することにより
どの環境にどんな設定がされているのか、パラメータシート的な役割も担うことができます(個人的見解)。
Ansibleを利用する場合、ちょっとした準備が必要になります。
Ansibleの実行環境を「実行端末」、Ansibleを利用して構築する環境を「ターゲット端末」とすると、
実行端末には、AnsibleとPythonのパッケージが必要となります。
また、ターゲット端末に準備するものは無いのですが、ターゲット端末がリモートである場合、
実行端末とターゲット端末がssh接続できることが前提となります。
簡単なイメージはこちら↓
準備...
インストール
前章で話した通り、Ansibleを利用するには、AnsibleとPythonのパッケージインストールが必要です。
私はこちらのサイトを参考にインストールを実施しました。
ちなみに私の実行環境はWSLのUbuntuを利用してます。
今回Ansibleを実行している環境はこちら↓です。
OS:Ubuntu(WSL)
Ansible:2.6.9
Python:2.7.17
環境セッティング
YAMLなどの設定ファイルの配置、階層は以下のような感じにしています。
親ディレクトリ
├ playbook関係のYAMLファイル
├ hosts_vars
| ┕ サーバ別パラメータ用YAMLファイル
├ group_vars
| ┕ グループ別パラメータ用YAMLファイル
├ inventory
| ┕ 接続サーバ情報用YAMLファイル
┕ roles
┕ test-setting-role
├ files
| ┕ サーバ毎のディレクトリ
| ┕ profile
| ┕ ユーザ毎のprofile
├ handlers
| ┕ main.yml
┕ tasks
┕ サーバ設定用YAMLファイル
Ansibleを触るのが初めてということもあり結構ごちゃごちゃしちゃいました...
全部を説明するのは厳しいので、以下に絞ってこれから説明していこうと思います。
- playbook関係のYAMLファイル
- rolesとtasks
playbook関係のYAMLファイル
Ansibleを動かす際、playbookというものを参照して処理をさせます。
今回、playbookには接続サーバ情報用YAMLファイルinventory
で定義した
どのホストに対して、どんな権限で、何のroleを実行するのかを記載しています。
例)
- hosts: hoge
become: True
roles:
- hoge-setting-role
それぞれの意味ですが、
- hosts :
inventory/接続サーバ情報用YAMLファイル
内で設定したホスト名を定義- 定義したホストに対して処理を実行する
- become :
Ture
時は管理者権限で処理を実行する
- roles :
roles/roleディレクトリ
を定義- 定義したrole内の処理を実行する
といった感じになります。
対象のplaybookを指定し実行することで、playbook内で定義している内容の処理を開始することができます。
rolesについて
rolesは、playbookで読み込むためのモジュールです。
前章を例にすると、playbook上でhoge-setting-role
を呼び出しています。
今回roles配下は、与えられた値から処理を実際に実行するtasks
や、サーバに配置したいコンテンツを
保管するfiles
(今回はfiles
の説明は省きます...)などで構成しています。
tasksについて
tasksでは実際にどういった処理をさせるのかを定義します。
身近なものだと、ユーザを作ったり、yumでパッケージをインストールすることができます。
Ansibleにはモジュールというものが存在し、それらを用いることでtaskを定義することができます。
例えば、yumでhttpd
をインストールしたいという場合は、↓のような感じで定義します。
- name: yum install for httpd
yum:
state: present
name: httpd
各項目の説明です。
- name(1行目) :
- こちらはタスク名になります。実際に実行した際にログ上に出力されるのでどんな処理をしているのかわかりやすくするのがおすすめです。
- yum :
- これがモジュールになります。今回は
yum
モジュールを定義していますが、どんなモジュールがあるのかはAnsibleの公式ドキュメントなどいろんなところにあるので探してみてください!! - state,name :
yum
モジュールで定義できる専用の変数みたいなものになります。state
はどんなバージョンをインストールするかを設定できます。present
の場合は最新バージョンを意味します。name
は何をインストールするかを設定できます。今回はhttpd
を対象としています。
- これがモジュールになります。今回は
文章ばかりではわかりにくいので、ここで試しに実行してみましょう。
定義はこんなかんじ。
- task-playbook.yml
- hosts: test_server
become: True
roles:
- test-setting-role
- inventory/hosts.yml
all:
children:
test:
children:
test_dev:
children:
test_dev_ap:
hosts:
test_server:
ansible_host: 対象サーバのグローバルIPアドレス
ansible_ssh_common_args: ''
- roles/test-setting-role/tasks/main.yml
- name: Main controller for common setting
debug:
msg: "Start setting {{ inventory_hostname }}"
- name: Include tasks
include_tasks:
file: "{{ item }}"
loop:
- yum_test.yml
- name: Finished tasks
debug:
msg: "Completed {{ inventory_hostname }}"
- roles/test-setting-role/tasks/yum_test.yml
- name: yum install
yum:
state: present
name: httpd
playbookを実行すると、roles/test-setting-role/tasks配下にあるmain.yml
が実行されます。
main.yml
にそのままyumの処理を直書きしても良いのですが、処理をできる限り再利用できるように
分けるようにしたいと思います。
include_tasks
モジュールを利用することで、他のtaskファイルを読み込み処理を実行することができます。
playbookの実行はansible-playbook
コマンドでできます。
sudo ansible-playbook -i inventory/hosts.yml test-playbook.yml
-i
でinventoryの指定、最後に実行するplaybookの指定をします。
実行すると...
たくさんログが出てきましたね...
主要な部分だけ説明すると
TASK [test-setting-role : yum install]
が今回httpd
をインストールしているタスクになります。
この部分でエラーが出ずにchanged
のステータスになっているので、問題なくインストールができたことを意味しています。
最後のPLAY RECAP
でもわかるようになっているので、そこで確認すればトータルで処理が問題なかったかがわかるようになっています。
対象サーバの確認もしてみましょう。
ちゃんとインストールされていましたね!
さて本番...Ansibleを動かしてみましょう
一通り基本的な動作が確認できたということで、
実際にリプレイスで使うように処理を組んでいきましょう...
全部説明するのはさすがに多いので、↓の5つに絞って説明していきます。
ファイルの中身だけ参考程度に載せたいと思います。
- main.yml
- hostname.yml
- yum.yml
- group.yml
- user.yml
- roles/test-setting-role/tasks/main.yml
- name: Main controller for common setting
debug:
msg: "Start setting {{ inventory_hostname }}"
- name: Include tasks
include_tasks:
file: "{{ item }}"
loop:
- hostname.yml
- yum.yml
- group.yml
- user.yml
- kshrc.yml
- localization.yml
- sshd.yml
- hosts.yml
- sudoers.yml
- services.yml
- profile.yml
- vimrc.yml
- name: Reboot host
debug:
msg: "Reboot {{ inventory_hostname }}"
- name: Reboot
reboot:
- name: Finished tasks
debug:
msg: "Completed {{ inventory_hostname }}"
前章のmain.yml
を改良したものになります。
include_tasks
モジュールとloop
を組み合わせることにより
他タスクを繰り返して処理させています。
また、中には再起動しないと設定が反映されないものもあるので
reboot
によりサーバの再起動を実施しています。
このモジュール、結構親切で、サーバが完全に上がり切るまで待ってくれます。
- roles/test-setting-role/tasks/hostname.yml
- name: Setting hostname
hostname:
name: "{{ inventory_hostname }}"
hostname
モジュールはその名の通りホスト名を設定することができます。
name
にホスト名を記載することで、その内容で設定を行います。
{{ inventory_hostname }}
はAnsibleの独自の変数で、inventory/host.yml
で指定した
ホスト名を表します。
今回の場合、test_server
が{{ inventory_hostname }}
に代入されます。
- roles/test-setting-role/tasks/yum.yml
- name: yum repository enable
command: yum-config-manager --enable rhel-7-server-rhui-optional-rpms
- name: yum update
yum:
name: "*"
state: latest
- name: yum install
yum:
state: present
name: "{{ item }}"
loop: "{{ yum_packages }}"
こちらも前章のものを改良したものになります。
yum
モジュールで対象を"*"
、state
をlatest
にすることで、yum update
を実施しています。
また、インストールも、対象のパッケージを変数に格納させループし、インストールするようにしています。
- roles/test-setting-role/tasks/group.yml
- name: Create groups
group:
name: "{{ item.group }}"
gid: "{{ item.gid }}"
loop: "{{ group }}"
group
モジュールを利用することでgroupの作成をすることができます。
name
でグループ名、gid
はグループIDを指定します。
ここでも同様に変数に格納したものをループで処理させるようにしています。
- roles/test-setting-role/tasks/user.yml
- name: Create users
user:
name: "{{ item.user }}"
uid: "{{ item.uid }}"
group: "{{ item.group }}"
password: "{{ item.password }}"
createhome: yes
home: "{{ item.home_directory }}"
shell: "{{ item.shell }}"
loop: "{{ user }}"
user
モジュールを利用することでuserの作成をすることができます。
それぞれの設定値についてはこんな感じです。
- name:ユーザ名
- uid:ユーザID
- group:所属グループ
- password:ユーザのパスワード
- createhome:ホームディレクトリを作成するかしないか(yesは作成し、noは作成しない)
- home:ホームディレクトリの指定
- shell:ログインシェルの指定
ここでも同様に変数に格納したものをループで処理させるようにしています。
今回のせている他の設定はこんな感じ↓
- roles/test-setting-role/tasks/kshrc.yml
/etc/kshrc
に追加したい設定をblock
配下に記述- 独自のフラグで対象ユーザに
~/.kshrc
を配置
- name: Setting /etc/kshrc
blockinfile:
path: /etc/kshrc
insertafter: 'unset -f pathmunge'
block: |
PS1="$"
[ $(whoami) = 'root' ] && PS1="#"
export PS1=[$(whoami)@$(hostname):'$PWD']$PS1" "
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ inventory_hostname }}"
backup: yes
- name: Setting ~/.kshrc
copy:
dest: "{{ item.home_directory }}/.kshrc"
owner: "{{ item.user }}"
group: "{{ item.group }}"
mode: "0644"
content: |
# .kshrc
# Source global definitions
if [ -f /etc/kshrc ]; then
. /etc/kshrc
fi
# use emacs editing mode by default
set -o emacs
# User specific aliases and functions
when: item.is_db2_user
loop: "{{ user }}"
- roles/test-setting-role/tasks/localization.yml
locale
をja_JP.UTF-8
に設定- ユーザ毎の
locale
でSHIFT_JIS
が設定できるように有効化 timezone
をAsia/Tokyo
に設定
- name: Setting locale
command: localectl set-locale LANG=ja_JP.UTF-8
- name: Add locale sjis
command: localedef -f SHIFT_JIS -i ja_JP /usr/lib/locale/ja_JP.sjis
- name: Setting timezone
timezone:
name: Asia/Tokyo
- roles/test-setting-role/tasks/sshd.yml
- パスワード認証でもssh接続できるように該当箇所の置換を実施
- name: Setting sshd
lineinfile:
path: /etc/ssh/sshd_config
state: present
backrefs: yes
regexp: "^PasswordAuthentication no"
line: "PasswordAuthentication yes"
backup: yes
notify: Restart sshd
- roles/test-setting-role/tasks/hosts.yml
- 変数に格納した内容を
/etc/hosts
の最後尾(EOF)へ追記
- 変数に格納した内容を
- name: Setting hosts
blockinfile:
path: /etc/hosts
insertafter: EOF
block: "{{ hosts }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ inventory_hostname }}"
backup: yes
- roles/test-setting-role/tasks/sudoers.yml
- 該当箇所の置換
- 最後尾(EOF)に追記
- name: Comment out '%wheel'
lineinfile:
path: /etc/sudoers
state: present
backrefs: yes
regexp: "^%wheel"
line: "#%wheel"
backup: yes
- name: Setting sudoers
blockinfile:
path: /etc/sudoers
insertafter: EOF
block: "{{ sudoers }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ inventory_hostname }}"
backup: yes
- roles/test-setting-role/tasks/services.yml
- 最後尾(EOF)に追記
- name: Setting services
blockinfile:
path: /etc/services
insertafter: EOF
block: "{{ services }}"
marker: "# {mark} ANSIBLE MANAGED BLOCK {{ inventory_hostname }}"
backup: yes
- roles/test-setting-role/tasks/profile.yml
- ちょっと複雑
copy
モジュールにより、ローカルの該当ファイルを対象のサーバへ配置- 所有者も指定可能
- name: Copy ~/.profile
copy:
src: "roles/test-setting-role/files/{{ inventory_hostname }}/profile/{{ item.user }}/"
dest: "{{ item.home_directory }}/"
owner: "{{ item.user }}"
group: "{{ item.group }}"
mode: "0644"
when: item.having_dot_profile
loop: "{{ user }}"
- roles/test-setting-role/tasks/vimrc.yml
- 新しくファイル作って
block
配下の値を投入 - 作成したユーザのうち、フラグに引っかかったもののみ処理を実施
- 新しくファイル作って
- name: Setting vimrc
blockinfile:
path: "{{ item.home_directory }}/.vimrc"
create: yes
marker: ' " # {mark} ANSIBLE MANAGED BLOCK {{ inventory_hostname }}'
block: |
set encoding=sjis
set fileencoding=cp932
set termencoding=sjis
when: item.is_db2_user
loop: "{{ user }}"
長々失礼いたしました...
ひとまず上に記載した処理を実行していきましょう...!
ちなみに今回定義している変数はこちら↓
group:
- group: test1
gid: 300
- group: test2
gid: 301
- group: test3
gid: 302
user:
- user: test1
uid: 211
group: test1
password: $6$JSTTOTPp$nnOVC337kb/H6nFCrsY0w9ilsfWvo/lLR7x4k1EO3HryW4EEBsDNXpoN8JnDZ55b4BPRYtmagoFxEhd/U6l7c.
home_directory: /home/test1
shell: /bin/bash
is_db2_user: true
having_dot_profile: true
- user: test2
uid: 221
group: test2
password: $6$I87PfjsW$UK1ytI95/PAVBwd7qndU0Qldj5v5eJLwgQd7N0zmUHp/VMsj3SaSdgbqS4TulLqlB/PhaMZ6luxKSKYOgcIGa0
home_directory: /home/test2
shell: /usr/bin/ksh
is_db2_user: true
having_dot_profile: true
- user: test3
uid: 222
group: test3
password: $6$fOdqr3sb$jGrqtEOmS..zABkW5A6VLKd2ouiTueTc7Yc.N1ZQ2nIHYuLboVonlheclK9PmRpvwdvPGIwUU52pDTflUEYUx1
home_directory: /home/test3
shell: /usr/bin/ksh
is_db2_user: true
having_dot_profile: true
sudoers: |
# Cmnd alias specification
Cmnd_Alias TEST_BIN = /bin/rm, /usr/bin/sar, /usr/bin/topas, /usr/local/bin/nmon, /usr/bin/svmon, /usr/sbin/lsof, /usr/bin/filemon, /usr/bin/fileplace, /usr/bin/netpmon, /usr/bin/trcstop
# User privilege specification
test1 ALL=(ALL) NOPASSWD:MON_BIN, IHS_BIN, WAS_BIN, DMGR_BIN, USER_CMD, LOGROT
test2 ALL=(ALL) NOPASSWD:MON_BIN
hosts: |
10.126.100.101 test1
10.126.100.102 test2
10.126.100.103 test3
10.126.100.104 test4
10.102.100.105 test5
10.126.100.106 test6
services: |
# test 60000/tcp # Ansible Test
yum_packages:
- tomcat
- mysql
- httpd
- java-1.8.0-openjdk.x86_64
ちゃんと実行できるかな...
途中までうまくいってたのにーーーーー!
どうやら、ローカルに配置してるファイルの場所が見つからずエラーになっている模様。。。
修正して再トライ!!
修正して問題なく設定が完了しました!
前回と少し違うのは、すでに設定完了しているタスクがchanged
からok
になっていること。
これはAnsibleの冪等性という性質からなっているもので、一度実行したものは二回目以降も
同じ結果になるというようになっています。
そのため、設定は変わらずそのままでOKですよーってなったということですね。
ちゃんと設定されている...?
さて、Ansibleの実行が完了しましたが、本当に設定されているのか...
実際にサーバに接続し確認していきましょう!
※全部は多いのでこちらもピックアップしてキャプチャ貼っていきますね...
- host名
- グループ
- ユーザ
- locale,timzone
- yum
ちゃんとできてそうですね!
最後に
本来であれば、プロジェクトで実際に使ったパラメータや、その時の挙動などを記事にできれば良いのですが、
公開できないものもありそうなので、今回の記事用にところどころ修正して実行しなおしました...
今回はAnsibleの基礎ということで、できるだけ身近な処理に絞り、設定ファイルやパラメータの説明をしてきましたが、実際のプロジェクトではNFSマウントやvsftpdの設定とかもしているので、Ansibleはもっといろいろなことができると思います。
実際にAnsibleを使ってみた個人的な感想としては
一度処理を書いてしまえば、後は変数を定義するだけで同じように実行できるのはとても便利だなと思いました。(その処理を書くのが結構大変なのですが...)
初めてAnsibleを触ってみたということもあり拙い記事とコードの紹介になってしまいましたが、もっと汎用性の高いものを作れるように深く勉強していきたいと思います!
せっかくなので次はterraformを使って、AWS環境の自動化もやってみたいですね!
以上、最後までお付き合いいただきありがとうございました!