Ansible で EC2インスタンスのWebサーバ(CentOS6+Apache)を1台構築してみました。
自宅からAWSに接続することを前提にしています。
Ansible では以下のタスクを行います。
- CentOS公式AMIからEC2インスタンスの作成
- ルートパーティションの拡張(8GB ⇒ 10GB)
- CentOS6の基本設定
- SElinux無効化
- ipv6無効化
- ファイヤーウォール無効化
- 日本語(ja_JP.UTF-8)設定
- タイムゾーン(Asia/Tokyo)設定
- Apache導入
AWS側は、事前に以下のリソースを用意します。
- サブネット(パブリック)
- セキュリティグループ(SSH、HTTPを許可)
- キーペア
Ansibleのインストール
Ansible のインストールは以下のとおり。
libselinux-python は、SELinuxの設定で使用します。
python-boto は EC2 の作成で使用します。
libselinux-python は、SELinuxの設定で使用します。
python-boto は EC2 の作成で使用します。
[root@node01 ~]# yum install ansible libselinux-python python-boto
Ansible のバージョンは以下のとおり
[root@node01 ~]# ansible --version ansible 1.9.4 configured module search path = None
Ansible プレイブックのディレクトリ構成
今回、ansible は 一般ユーザの infraユーザで実行することにしました。
Ansible のプレイブックは、ベストプラクティスを参考に以下のように配置しました。
[infra@node01 ansible]$ tree . . ├── aws.yml ├── group_vars ├── host_vars ├── key.pem ├── production ├── roles │ ├── apache │ │ └── tasks │ │ └── main.yml │ ├── common │ │ ├── defaults │ │ ├── files │ │ │ └── disable-ipv6.conf │ │ ├── handlers │ │ ├── meta │ │ ├── tasks │ │ │ └── main.yml │ │ ├── templates │ │ └── vars │ │ └── main.yml │ ├── ec2 │ │ ├── tasks │ │ │ └── main.yml │ │ └── vars │ │ └── main.yml │ └── growpart │ ├── tasks │ │ └── main.yml │ └── vars │ └── main.yml ├── site.yml └── webservers.yml 19 directories, 13 files
Ansible プレイブックの内容
各ファイルの内容は以下のとおり。
key.pem はAWSで作成したキーペアの秘密鍵です。
CentOS6の公式AMで作成したEC2インスタンスには、この秘密鍵を使用して centos ユーザでログインします。
centosユーザはパスワードなしの sudo が使用できるようになっています。
[infra@node01 ansible]$ more * | cat - :::::::::::::: aws.yml :::::::::::::: - hosts: localhost connection: local gather_facts: False roles: - ec2 - hosts: webservers gather_facts: True sudo: yes roles: - growpart :::::::::::::: key.pem :::::::::::::: -----BEGIN RSA PRIVATE KEY----- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -----END RSA PRIVATE KEY----- :::::::::::::: production :::::::::::::: [local] localhost [webservers:vars] ansible_ssh_user=centos ansible_ssh_private_key_file=/home/infra/ansible/key.pem :::::::::::::: site.yml :::::::::::::: - include: aws.yml - include: webservers.yml :::::::::::::: webservers.yml :::::::::::::: - hosts: webservers gather_facts: True sudo: yes roles: - common - apache [infra@node01 ansible]$ more */*/*/* | cat - :::::::::::::: roles/apache/tasks/main.yml :::::::::::::: - name: Apache のインストール yum: name=httpd state=latest tags: - apache - name: Apache の自動起動ON service: name=httpd enabled=yes tags: - apache - name: Apache の起動 service: name=httpd state=started tags: - apache :::::::::::::: roles/common/files/disable-ipv6.conf :::::::::::::: options ipv6 disable=1 :::::::::::::: roles/common/tasks/main.yml :::::::::::::: #----------------------- # OS 基本設定 #----------------------- # SElinux off # yum install libselinux-python - name: SELinux を無効にする selinux: state=disabled tags: - os_basic - selinux # ipv6 off - name: ipv6 を無効にする(/etc/sysconfig/network) lineinfile: > dest=/etc/sysconfig/network state=present backup=yes backrefs=no insertafter=EOF regexp='^NETWORKING_IPV6=' line='NETWORKING_IPV6=no' tags: - os_basic - ipv6 - name: ipv6 を無効にする(/etc/modprobe.d/) copy: > src=disable-ipv6.conf dest=/etc/modprobe.d/disable-ipv6.conf group=root owner=root mode=0644 backup=yes tags: - os_basic - ipv6 # Firewall off - name: Firewall を無効にする service: name={{ item }} enabled=no with_items: firewall_srv tags: - os_basic - firewall # LANG=ja_JP.UTF-8 - name: 日本語(ja_JP.UTF-8) にする lineinfile: > dest=/etc/sysconfig/i18n state=present backup=yes backrefs=no insertafter=EOF regexp='^LANG=' line='LANG=ja_JP.UTF-8' tags: - os_basic - lang # TIMEZONE=Asia/Tokyo - name: タイムゾーンを Asia/Tokyo にする lineinfile: > dest=/etc/sysconfig/clock state=present backup=yes backrefs=no insertafter=EOF regexp='^ZONE=' line='ZONE=Asia/Tokyo' tags: - os_basic - zone - name: ローカルタイムを Asia/Tokyo にする file: > src=/usr/share/zoneinfo/Asia/Tokyo dest=/etc/localtime state=link force=yes owner=root group=root tags: - os_basic - zone #----------------------- # OS パッケージの最新化 #----------------------- - name: yum アップデート yum: name=* enablerepo=epel state=latest tags: - yum_update - name: reboot! command: shutdown -r now tags: - yum_update - name: wait for SSH port down local_action: wait_for host={{ inventory_hostname }} port=22 state=stopped sudo: no tags: - yum_update - name: wait for SSH port up local_action: wait_for host={{ inventory_hostname }} port=22 state=started timeout=300 sudo: no tags: - yum_update :::::::::::::: roles/common/vars/main.yml :::::::::::::: firewall_srv: - iptables - ip6tables :::::::::::::: roles/ec2/tasks/main.yml :::::::::::::: # yum install python-boto - name: EC2インスタンスを作成 ec2: aws_access_key: "{{ aws_access_key }}" aws_secret_key: "{{ aws_secret_key }}" assign_public_ip: "{{ assign_public_ip }}" region: "{{region}}" zone: "{{zone}}" image: "{{ ami_image }}" instance_type: "{{ instance_type }}" key_name: "{{ key_name }}" group_id: "{{ sg_id }}" private_ip: "{{ ip }}" vpc_subnet_id: "{{ vpc_subnet_id }}" instance_tags: Name: "{{ hostname }}" System: "Test" wait: yes wait_timeout: 300 volumes: - device_name: "{{ device_name }}" volume_size: "{{ volume_size }}" delete_on_termination: yes register: ec2 tags: - ec2 #- debug: var=ec2 - name: SSHで接続できるようになるまで待機 local_action: wait_for port=22 host={{ item.public_dns_name }} timeout=300 state=started with_items: ec2.instances when: ec2.changed == true tags: - ec2 - name: 作成したEC2インスタンスをインベントリに追加 local_action: add_host hostname="{{ item.public_ip }}" groupname="webservers" with_items: ec2.instances when: ec2.changed == true tags: - ec2 :::::::::::::: roles/ec2/vars/main.yml :::::::::::::: # EC2 region: us-east-1 zone: us-east-1a aws_access_key: xxxxxxxxxxxxx aws_secret_key: xxxxxxxxxxxxx assign_public_ip: yes ami_image: ami-57cd8732 key_name: keypaire hostname: apache01 ip: 10.0.0.11 vpc_subnet_id: "subnet-xxxx" sg_id: [ "sg-xxxx", "sg-xxxx" ] instance_type: "t2.micro" device_name: "/dev/sda1" volume_size: 10 :::::::::::::: roles/growpart/tasks/main.yml :::::::::::::: # ルートパーティション拡張 - name: epel レポジトリのインストール yum: name=epel-release state=latest tags: - growpart - name: yum パッケージのインストール yum: name={{ item }} enablerepo=epel state=latest with_items: grow_part_pkg tags: - growpart - name: パーティション拡張の指示 shell: "dracut --force --add growroot /boot/initramfs-$(uname -r).img" register: dracut ignore_errors: true changed_when: false tags: - growpart - name: reboot! command: shutdown -r now tags: - growpart - name: wait for SSH port down local_action: wait_for host={{ inventory_hostname }} port=22 state=stopped sudo: no tags: - growpart - name: wait for SSH port up local_action: wait_for host={{ inventory_hostname }} port=22 state=started timeout=300 sudo: no tags: - growpart :::::::::::::: roles/growpart/vars/main.yml :::::::::::::: grow_part_pkg: - cloud-utils-growpart - dracut-modules-growroot [infra@node01 ansible]$
イベントリ(puroduction)の webservers グループには、ホスト(IPアドレス)を指定していませんが、EC2作成時に自動的にIPアドレスが追加されるようにしています。
プレイブック実行時に、作成したEC2インスタンスのパブリックIPがwebsrversグループに追加され、後続の処理では、追加されたIPアドレスに対してタスクを実行します。
プレイブック実行時に、作成したEC2インスタンスのパブリックIPがwebsrversグループに追加され、後続の処理では、追加されたIPアドレスに対してタスクを実行します。
Ansibleプレイブックのタスク一覧
このプレイブックが実施するタスクは以下のとおり。
上から順番に実行されます。
[infra@node01 ansible]$ ansible-playbook site.yml -i production --list-tasks playbook: site.yml play #1 (localhost): TAGS: [] EC2インスタンスを作成 TAGS: [ec2] SSHで接続できるようになるまで待機 TAGS: [ec2] 作成したEC2インスタンスをインベントリに追加 TAGS: [ec2] play #2 (webservers): TAGS: [] epel レポジトリのインストール TAGS: [growpart] yum パッケージのインストール TAGS: [growpart] パーティション拡張の指示 TAGS: [growpart] reboot! TAGS: [growpart] wait for SSH port down TAGS: [growpart] wait for SSH port up TAGS: [growpart] play #3 (webservers): TAGS: [] SELinux を無効にする TAGS: [os_basic, selinux] ipv6 を無効にする(/etc/sysconfig/network) TAGS: [ipv6, os_basic] ipv6 を無効にする(/etc/modprobe.d/) TAGS: [ipv6, os_basic] Firewall を無効にする TAGS: [firewall, os_basic] 日本語(ja_JP.UTF-8) にする TAGS: [lang, os_basic] タイムゾーンを Asia/Tokyo にする TAGS: [os_basic, zone] ローカルタイムを Asia/Tokyo にする TAGS: [os_basic, zone] yum アップデート TAGS: [yum_update] reboot! TAGS: [yum_update] wait for SSH port down TAGS: [yum_update] wait for SSH port up TAGS: [yum_update] Apache のインストール TAGS: [apache] Apache の自動起動ON TAGS: [apache] Apache の起動 TAGS: [apache]
Ansible実行(checkモード)
実際に変更は行わないようにして実行して、動きを確認してみます。
[infra@node01 ansible]$ ansible-playbook site.yml -i production --check PLAY [localhost] ************************************************************** TASK: [ec2 | EC2インスタンスを作成] ********************************** skipping: [localhost] ok: [localhost] TASK: [ec2 | SSHで接続できるようになるまで待機] **************** skipping: [localhost] => (item=ec2.instances) TASK: [ec2 | 作成したEC2インスタンスをインベントリに追加] *** skipping: [localhost] => (item=ec2.instances) PLAY [webservers] ************************************************************* skipping: no hosts matched PLAY [webservers] ************************************************************* skipping: no hosts matched PLAY RECAP ******************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 [infra@node01 ansible]$
Ansible実行
今度は、実際に変更を行います。
途中、EC2インスタンスに初めてSSHログインするタイミングで、接続確認入力を求められるので "yes" を入力します。
[infra@node01 ansible]$ ansible-playbook site.yml -i production PLAY [localhost] ************************************************************** TASK: [ec2 | EC2インスタンスを作成] ********************************** changed: [localhost] TASK: [ec2 | SSHで接続できるようになるまで待機] **************** ok: [localhost -> 127.0.0.1] => (item={u'ramdisk': None, u'kernel': None, u'root_device_type': u'ebs', u'private_dns_name': u'ip-10-0-0-11.ec2.internal', u'tags': {}, u'key_name': u'virginia_key', u'public_ip': u'52.23.158.7', u'image_id': u'ami-57cd8732', u'tenancy': u'default', u'private_ip': u'10.0.0.11', u'groups': {u'sg-bbf176df': u'default', u'sg-01237c67': u'webgroup'}, u'public_dns_name': u'ec2-52-23-158-7.compute-1.amazonaws.com', u'state_code': 16, u'id': u'i-a6f60810', u'placement': u'us-east-1a', u'ami_launch_index': u'0', u'dns_name': u'ec2-52-23-158-7.compute-1.amazonaws.com', u'region': u'us-east-1', u'ebs_optimized': False, u'launch_time': u'2015-11-29T04:24:40.000Z', u'instance_type': u't2.micro', u'state': u'running', u'root_device_name': u'/dev/sda1', u'hypervisor': u'xen', u'virtualization_type': u'hvm', u'architecture': u'x86_64'}) TASK: [ec2 | 作成したEC2インスタンスをインベントリに追加] *** ok: [localhost -> 127.0.0.1] => (item={u'ramdisk': None, u'kernel': None, u'root_device_type': u'ebs', u'private_dns_name': u'ip-10-0-0-11.ec2.internal', u'tags': {}, u'key_name': u'virginia_key', u'public_ip': u'52.23.158.7', u'image_id': u'ami-57cd8732', u'tenancy': u'default', u'private_ip': u'10.0.0.11', u'groups': {u'sg-bbf176df': u'default', u'sg-01237c67': u'webgroup'}, u'public_dns_name': u'ec2-52-23-158-7.compute-1.amazonaws.com', u'state_code': 16, u'id': u'i-a6f60810', u'placement': u'us-east-1a', u'ami_launch_index': u'0', u'dns_name': u'ec2-52-23-158-7.compute-1.amazonaws.com', u'region': u'us-east-1', u'ebs_optimized': False, u'launch_time': u'2015-11-29T04:24:40.000Z', u'instance_type': u't2.micro', u'state': u'running', u'root_device_name': u'/dev/sda1', u'hypervisor': u'xen', u'virtualization_type': u'hvm', u'architecture': u'x86_64'}) PLAY [webservers] ************************************************************* GATHERING FACTS *************************************************************** The authenticity of host '52.23.158.7 (52.23.158.7)' can't be established. RSA key fingerprint is d8:8c:b2:3d:dd:d8:c3:7b:29:ec:41:72:29:ca:da:bf. Are you sure you want to continue connecting (yes/no)? yes ok: [52.23.158.7] TASK: [growpart | epel レポジトリのインストール] ****************** changed: [52.23.158.7] TASK: [growpart | yum パッケージのインストール] ******************* changed: [52.23.158.7] => (item=cloud-utils-growpart,dracut-modules-growroot) TASK: [growpart | パーティション拡張の指示] *********************** ok: [52.23.158.7] TASK: [growpart | reboot!] **************************************************** changed: [52.23.158.7] TASK: [growpart | wait for SSH port down] ************************************* ok: [52.23.158.7 -> 127.0.0.1] TASK: [growpart | wait for SSH port up] *************************************** ok: [52.23.158.7 -> 127.0.0.1] PLAY [webservers] ************************************************************* GATHERING FACTS *************************************************************** ok: [52.23.158.7] TASK: [common | SELinux を無効にする] *********************************** changed: [52.23.158.7] TASK: [common | ipv6 を無効にする(/etc/sysconfig/network)] ************** changed: [52.23.158.7] TASK: [common | ipv6 を無効にする(/etc/modprobe.d/)] ******************** changed: [52.23.158.7] TASK: [common | Firewall を無効にする] ********************************** changed: [52.23.158.7] => (item=iptables) changed: [52.23.158.7] => (item=ip6tables) TASK: [common | 日本語(ja_JP.UTF-8) にする] ***************************** changed: [52.23.158.7] TASK: [common | タイムゾーンを Asia/Tokyo にする] ******************* changed: [52.23.158.7] TASK: [common | ローカルタイムを Asia/Tokyo にする] **************** changed: [52.23.158.7] TASK: [common | yum アップデート] *************************************** changed: [52.23.158.7] TASK: [common | reboot!] ****************************************************** changed: [52.23.158.7] TASK: [common | wait for SSH port down] *************************************** ok: [52.23.158.7 -> 127.0.0.1] TASK: [common | wait for SSH port up] ***************************************** ok: [52.23.158.7 -> 127.0.0.1] TASK: [apache | Apache のインストール] ********************************* changed: [52.23.158.7] TASK: [apache | Apache の自動起動ON] ************************************* changed: [52.23.158.7] TASK: [apache | Apache の起動] ********************************************* changed: [52.23.158.7] PLAY RECAP ******************************************************************** 52.23.158.7 : ok=22 changed=15 unreachable=0 failed=0 localhost : ok=3 changed=1 unreachable=0 failed=0 [infra@node01 ansible]$
動作確認
EC2インスタンスにログインして、パーティションの拡張や、OS設定が成功しているか確認します。
[centos@ip-10-0-0-11 ~]$ getenforce Disabled [centos@ip-10-0-0-11 ~]$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT xvda 202:0 0 10G 0 disk └─xvda1 202:1 0 10G 0 part / [centos@ip-10-0-0-11 ~]$ df -Th Filesystem Type Size Used Avail Use% Mounted on /dev/xvda1 ext4 9.8G 867M 8.4G 10% / tmpfs tmpfs 498M 0 498M 0% /dev/shm [centos@ip-10-0-0-11 ~]$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP qlen 1000 link/ether 0a:7f:41:05:7d:b5 brd ff:ff:ff:ff:ff:ff inet 10.0.0.11/24 brd 10.0.0.255 scope global eth0 [centos@ip-10-0-0-11 ~]$ date 2015年 11月 29日 日曜日 13:40:06 JST [centos@ip-10-0-0-11 ~]$ chkconfig | grep iptables iptables 0:off 1:off 2:off 3:off 4:off 5:off 6:off [centos@ip-10-0-0-11 ~]$ chkconfig | grep ip6tables ip6tables 0:off 1:off 2:off 3:off 4:off 5:off 6:off [centos@ip-10-0-0-11 ~]$ chkconfig | grep httpd httpd 0:off 1:off 2:on 3:on 4:on 5:on 6:off [centos@ip-10-0-0-11 ~]$ curl -I http://localhost HTTP/1.1 403 Forbidden Date: Sun, 29 Nov 2015 04:40:39 GMT Server: Apache/2.2.15 (CentOS) Accept-Ranges: bytes Content-Length: 4961 Connection: close Content-Type: text/html; charset=UTF-8 [centos@ip-10-0-0-11 ~]$
パーティションも10GBに拡張され、OS設定も成功しています。
◆ 補足
あとで、Webサーバの変更をしたい場合は、イベントリ(production)の webserversグループにEC2インスタンスのIPアドレスを記載します。
以下のように webservers グループだけ指定したり、タグを指定したりして一部のタスクのみ実行できます。
◆ 補足
あとで、Webサーバの変更をしたい場合は、イベントリ(production)の webserversグループにEC2インスタンスのIPアドレスを記載します。
以下のように webservers グループだけ指定したり、タグを指定したりして一部のタスクのみ実行できます。
[infra@node01 ansible]$ ansible-playbook site.yml -i production -l webservers
[infra@node01 ansible]$ ansible-playbook site.yml -i production --tags os_basic