이 연재글은 Ansible 알아보기의 3번째 글입니다.

이번장에서는 ansible을 이용하여 centos7기반에 database 설치를 자동화 해보겠습니다. centos7부터는 기본 database가 mariadb로 변경되었습니다. 따라서 yum install mysql 명령을 통해 database를 설치해도 mariadb가 설치됩니다. 사용자 입장에서는 mysql과 mariadb는 거의 차이가 없으므로 동일한 db로 봐도 무방합니다. 실습에서도 mysql과 mariadb를 따로 구분하지 않고 playbook을 작성해 보겠습니다.

엔서블 서버에 접속

$ vagrant ssh ansible-server

ansible-galaxy로 mysql role 생성

ansible-galaxy명령으로 ansible 기본 디렉토리 구조를 생성합니다.

$ cd roles
$ ang init mysql
- mysql was created successfully
$ tree mysql
mysql
|-- README.md
|-- defaults
|   `-- main.yml
|-- files
|-- handlers
|   `-- main.yml
|-- meta
|   `-- main.yml
|-- tasks
|   `-- main.yml
|-- templates
|-- tests
|   |-- inventory
|   `-- test.yml
`-- vars
    `-- main.yml

db 설치 playbook 작성

$ vi /roles/mysql/install_db.yml

dbservers에 속한 서버들에 mysql 설치 명령을 수행합니다. vars 하위의 변수정보와 install.yml task를 조합하여 실행되도록 아래와 같이 작성합니다. 이때 명령이 sudo권한으로 실행되도록 become:yes를 설정합니다.

---
   - hosts: dbservers
     become: yes
     vars_files:
        - vars/main.yml
     tasks:
        - include: tasks/install.yml

설치 환경 변수 작성

mysql 설치시 사용할 변수 정보를 아래와 같이 작성합니다. 설정한 변수 정보는 playbook 작성시 또는 jinja2 template파일 작성시 참조하여 사용할 수 있습니다.

$ vi /roles/mysql/vars/main.yml
---
# vars file for mysql
mysql_collation: utf8mb4_unicode_ci
mysql_encoding: utf8mb4
mysql_bind_address: 0.0.0.0
mysql_max_connections: 200
mysql_root_password: cjstk1
mysql_user_db_name: wordpress
mysql_user: wordpress
mysql_user_password: wordpress123!

DB 설치 task 작성

  • epel 저장소 업데이트
  • mariadb-server 설치
  • mysql support library 설치
  • my.cnf 파일 템플릿 적용
  • mariadb 시작
  • root 패스워드 및 권한 수정
  • test database 삭제
  • 신규 database 생성
  • 익명 user 삭제
  • 신규 user 작성 및 권한 수정

task에서 mysql 설치시 이미 정의된 모듈을 사용하였는데 자세한 사용법은 아래 링크에서 확인 가능합니다.

mysql_db module

https://docs.ansible.com/ansible/latest/modules/mysql_db_module.html

state 종류
  • present – DB를 설치합니다.
  • absent – DB를 삭제합니다.
  • dump – DB 데이터를 dump합니다.
  • import – DB 데이터를 import합니다.

mysql_user module

https://docs.ansible.com/ansible/latest/modules/mysql_user_module.html

state 종류
  • present – user를 추가합니다.
  • absent – user를 삭제합니다.
$ vi /roles/mysql/tasks/install.yml
---
    - name: install epel-release
      action: "{{ ansible_pkg_mgr }} name=epel-release state=latest"

    - name: install mysql server
      action: "{{ ansible_pkg_mgr }} name=mariadb-server state=present"

    - name: install python mysql support library
      action: "{{ ansible_pkg_mgr }} name=MySQL-python state=latest"

    - name: copy my.cnf file
      template:
        src=my.cnf.j2 dest=/etc/my.cnf mode=0600

    - name: start mysql server
      action: service name=mariadb state=started enabled=true

    - name: update mysql root password
      mysql_user:
         login_user: root
         login_password: "{{ mysql_root_password }}"
         name: root
         host: "{{ item }}"
         password: "{{ mysql_root_password }}"
         check_implicit_admin: yes
         priv: "*.*:ALL,GRANT"
      with_items:
         - "{{ ansible_hostname }}"
         - 127.0.0.1
         - ::1
         - localhost

    - name: remove test db
      mysql_db:
          login_user: root
          login_password: "{{ mysql_root_password }}"
          db: test
          state: absent

    - name: create a new db
      mysql_db:
          login_user: root
          login_password: "{{ mysql_root_password }}"
          name: "{{ mysql_user_db_name }}"
          collation: "{{ mysql_collation }}"
          encoding: "{{ mysql_encoding }}"
          state: present

    - name: delete anonymouse user
      mysql_user:
          login_user: root
          login_password: "{{ mysql_root_password }}"
          name: ""
          host_all: yes
          state: absent

    - name: create a new user
      mysql_user:
          login_user: root
          login_password: "{{ mysql_root_password }}"
          name: "{{ mysql_user }}"
          password: "{{ mysql_user_password }}"
          priv: "{{ mysql_user_db_name }}.*:ALL,GRANT"
          host: "%"
          state: present

.my.cnf jinja 파일 작성

실습에서는 my.cnf 기본 설정을 거의 변경없이 사용합니다. 예제에서는 bind-address, max-connection 값을 유동적으로 설정해 보기 위해 변수 처리하였습니다.

$ vi /roles/mysql/templates/my.cnf.j2
[client]
port		= 3306
socket		= /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket		= /var/run/mysqld/mysqld.sock
nice		= 0

[mysqld]
user		= mysql
pid-file	= /var/run/mysqld/mysqld.pid
socket		= /var/run/mysqld/mysqld.sock
port		= 3306
basedir		= /usr
datadir		= /var/lib/mysql
tmpdir		= /tmp
lc_messages_dir	= /usr/share/mysql
lc_messages	= en_US
skip-external-locking

bind-address		= {{ mysql_bind_address }}
max_connections		= {{ mysql_max_connections }}
connect_timeout		= 5
wait_timeout		= 600
max_allowed_packet	= 16M
thread_cache_size       = 128
sort_buffer_size	= 4M
bulk_insert_buffer_size	= 16M
tmp_table_size		= 32M
max_heap_table_size	= 32M

# * InnoDB
default_storage_engine	= InnoDB
# you can't just change log file size, requires special procedure
#innodb_log_file_size	= 50M
innodb_buffer_pool_size	= 256M
innodb_log_buffer_size	= 8M
innodb_file_per_table	= 1
innodb_open_files	= 400
innodb_io_capacity	= 400
innodb_flush_method	= O_DIRECT

playbook 실행

$ cd /roles/mysql
$ anp install_db.yml

PLAY [dbservers] ***********************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************
ok: [192.168.1.2]

TASK [install epel-release] ************************************************************************************************************************************
changed: [192.168.1.2]

TASK [install mysql server] ************************************************************************************************************************************
changed: [192.168.1.2]

TASK [install python mysql support library] ********************************************************************************************************************
changed: [192.168.1.2]

TASK [copy my.cnf file] ****************************************************************************************************************************************
changed: [192.168.1.2]

TASK [start mysql server] **************************************************************************************************************************************
changed: [192.168.1.2]

TASK [update mysql root password] ******************************************************************************************************************************
changed: [192.168.1.2] => (item=ansible-client002)
changed: [192.168.1.2] => (item=127.0.0.1)
changed: [192.168.1.2] => (item=::1)
changed: [192.168.1.2] => (item=localhost)

TASK [remove test db] ******************************************************************************************************************************************
changed: [192.168.1.2]

TASK [create a new db] *****************************************************************************************************************************************
changed: [192.168.1.2]

TASK [delete anonymouse user] **********************************************************************************************************************************
changed: [192.168.1.2]

TASK [create a new user] ***************************************************************************************************************************************
changed: [192.168.1.2]

PLAY RECAP *****************************************************************************************************************************************************
192.168.1.2                : ok=11   changed=10   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

mariadb 설치 확인

client서버에 접속하여 mysql 프로세스를 확인하고 root 계정으로 로그인하면 생성한 wordpress 계정 및 신규 database를 확인할 수 있습니다. database 정보에서는 test database가 삭제되었고 user 정보에서는 anonymous 유저가 삭제된것을 확인 할수 있습니다. 또한 root유저로는 외부에서 db접속이 불가능하고 신규로 생성한 wordpress유저만 외부에서 접근 가능하도록 세팅된것을 확인할 수 있습니다.

이렇게 설정하는 이유는 root유저의 경우 database의 모든 권한을 가지고 있기 때문에 외부에 노출되면 해킹이나 권한 남용의 위험성이 있어서입니다. 이를 막기 위해 제한된 권한을 가진 user를 새로 생성하여 서비스에서 사용하도록 설정한 것입니다.

$ vagrant ssh ansible-client002
$ ps -ef | grep mysql
mysql     8591     1  0 03:19 ?        00:00:00 /bin/sh /usr/bin/mysqld_safe --basedir=/usr
mysql     8753  8591  0 03:19 ?        00:00:00 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --pid-file=/var/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock
$ mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 17
Server version: 5.5.64-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| wordpress          |
+--------------------+
4 rows in set (0.00 sec)

MariaDB [(none)]> use mysql
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> select user, host from user;
+-----------+-------------------+
| user      | host              |
+-----------+-------------------+
| wordpress | %                 |
| root      | 127.0.0.1         |
| root      | ::1               |
| root      | ansible-client002 |
| root      | localhost         |
+-----------+-------------------+
6 rows in set (0.00 sec)

mysql 삭제 playbook 작성

dbservers에 속한 서버들에 mysql 삭제 명령을 수행합니다. vars하위의 변수정보와 remove.yml task를 조합하여 작업을 수행합니다. 이때 명령어는 sudo권한으로 실행됩니다.( become:yes )

$ vi /roles/mysql/remove_db.yml
---
   - hosts: dbservers
     become: yes
     vars_files:
        - vars/main.yml
     tasks:
        - include: tasks/remove.yml

DB삭제 task 작성

  • epel 저장소 업데이트
  • 생성한 databases 삭제
  • mariadb-server 삭제
  • mysql support library 삭제
$ vi /roles/mysql/tasks/remove.yml
---
    - name: install epel-release
      action: "{{ ansible_pkg_mgr }} name=epel-release state=latest"

    - name: delete databases
      mysql_db:
          login_user: root
          login_password: "{{ mysql_root_password }}"
          name: "{{ mysql_user_db_name }}"
          state: absent

    - name: remove mysql server
      action: "{{ ansible_pkg_mgr }} name=mariadb-server state=absent"

    - name: remove python mysql support library
      action: "{{ ansible_pkg_mgr }} name=MySQL-python state=absent"

playbook 실행

$ cd /roles/mysql
$ anp remove_db.yml
$ anp remove_db.yml

PLAY [dbservers] ***********************************************************************************************************************************************

TASK [Gathering Facts] *****************************************************************************************************************************************
ok: [192.168.1.2]

TASK [install epel-release] ************************************************************************************************************************************
changed: [192.168.1.2]

TASK [delete databases] ****************************************************************************************************************************************
changed: [192.168.1.2]

TASK [remove mysql server] *************************************************************************************************************************************
changed: [192.168.1.2]

TASK [remove python mysql support library] *********************************************************************************************************************
changed: [192.168.1.2]

PLAY RECAP *****************************************************************************************************************************************************
192.168.1.2                : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

mysql 삭제 확인

vagrant로 클라이언트 서버에 접속하여 mysql 프로세스를 확인하면 프로세스가 존재하지 않는것을 확인 할 수 있습니다.

$ vagrant ssh ansible-client002
$ ps -ef | grep mysql
vagrant   1982  6332  0 08:07 pts/0    00:00:00 grep --color=auto mysql

외부 접속 테스트

Vagrantfile 수정

Host PC의 13306 port를 통해 mysql서버의 3306 port에 접근할수 있도록 port forwarding을 설정해 보겠습니다. 아래와 같이 Vagrant파일의 forwarded_port정보를 수정합니다. 실습에서는 두개의 Port를 다르게 했지만 Host PC에 충돌하는 port가 없다면 동일하게 설정해도 무방합니다.

#ansible-client002
config.vm.define "ansible-client002" do |cfg|
    cfg.vm.box = "centos/7"
    cfg.vm.provider "virtualbox" do |vb|
        vb.name = "ansible-client002"
    end
    cfg.vm.host_name = "ansible-client002"
    cfg.vm.network "public_network", ip: "192.168.1.2"
    cfg.vm.network "forwarded_port", guest: 3306, host: 13306, auto_correct: true
    cfg.vm.synced_folder "../shared_data", "/shared_data", disabled: true
    cfg.vm.provision "shell", path: "enable_ssh_password_auth.sh"
end

수정 내용을 반영하기 위해 VM을 재시작 ( vagrant halt => vagrant up ) 합니다.

$ vagrant halt ansible-client002
==> ansible-client002: Attempting graceful shutdown of VM...
$ vagrant up ansible-client002
Bringing machine 'ansible-client002' up with 'virtualbox' provider...
==> ansible-client002: Checking if box 'centos/7' version '1905.1' is up to date...
==> ansible-client002: Clearing any previously set forwarded ports...
... 생략

Mysql WorkBench 접속 테스트

아래와 같이 설정하여 접속 테스트를 합니다. 접속정보는 로컬의 vm이므로 127.0.0.1을 입력하고 port는 위에서 설정한 13306 port를 그리고 계정정보에는 ansible에서 새로 생성한 계정인 wordpress / wordpress123!을 입력합니다.

접속이 정상적으로 이루어지면 화면을 통해 wordpress database가 노출됨을 확인할 수 있습니다. root 유저가 아니므로 권한이 설정된 wordpress database만 보여지게 됩니다.

ansible을 이용하여 mysql을 설치해보았습니다. 생각보다 복잡한 작업이지만 playbook에 작성된 내용을 통해 작업이 진행되는 상황을 쉽게 파악하고 수정할 수 있게 되었습니다. 또한 다수의 서버에 동일한 환경을 빠르게 구축할 수 있게 되었습니다.

실습시 사용한 소스는 아래 github에서 확인할 수 있습니다.

https://github.com/codej99/ansible-web-application.git

연재글 이동[이전글] Ansible을 이용한 시스템 구성관리(2) – ansible로 nginx 설치 – roles, handler, template, vars
[다음글] Ansible을 이용한 시스템 구성관리(4) – wordpress (nginx+mariadb+php) 설치 자동화