Ansible is an open-source software provisioning, configuration management, and application-deployment tool enabling infrastructure as code.
MySQL is an open-source relational database management system. It’s one of the popular relational management system.
Mysql is commonly installed as part of the popular LAMP or LEMP (Linux, Apache/Nginx, MySQL/MariaDB, PHP/Python/Perl) stack.
In this guide we are going to use mysql 8 on Centos 8. This guide also works on other RHEL 8 derivatives like Rocky Linux 8 and Alma Linux 8
Also check:
- How to Install and Set Up mysql 8 on Ubuntu 20.04
- Installing and Configuring Mysql Server 8 on Centos 8
- How to install and Configure Mysql Server 8 on Fedora 34/35
- Mysql Permissions – Create, Update, Delete Database Users
- Install and Setup Nginx, WordPress and Mysql 8 in Centos 8
- Create an RDS instance in terraform with a Mariadb Example
Requirements
- Internet connection
- Ansible installed locally
The hosts file
We require the hosts file that will define the servers that we are targetting:
all:
hosts:
db-server:
ansible_ssh_host: 192.168.10.2
ansible_ssh_user: centos
ansible_ssh_private_key_file: ~/.ssh/server_keyThe above yaml defines a server db-server that has the ip 192.168.10.2 and can be accessed using user centos and ~/.ssh/server_key.
Test that with:
ssh -i ~/.ssh/server_key centos@192.168.10.2The ansible playbook
Before we define our tasks, we have to tell ansible a couple of things:
- name: Install mysql server 8 and initialize
hosts: db-server
gather_facts: false
become: true
vars:
mysql_root_password: 'secure-root-pw'
ansible_python_interpreter: /usr/bin/python3Explaination:
namedefines the playbook a descriptive name of what it does, it is not compulsory to have this.hostsdefines the hosts to target as defined in thehostsorhosts.yamlfile defined above.gather_factsdefines whether we want ansible to gather os facts before processing the tasks. in our case we do not wantbecomedefines that we want to execute our tasks as rootvarsdefines the variables that we want to reuse in our tasks. We definedmysql_root_passwordandansible_python_interpreterin our case
The ansible tasks
After the section above, we now need to define our tasks. These tasks can either be added in a role or specified as tasks. In our case we will use them as tasks (check the end of this gude for the complete playbook).
Ensure required sofware is installed
Before proceeding, we want to install all the software that we would need. These includes mysql specific software mysql-server and mysql-devel and supporting software like python related software that will be used by ansible to connect to and set up the mysql server instance. We use the ansible dnf module to do this.
- name: Ensure required packages are installed
dnf:
name:
- mysql-server
- mysql-devel
- python39
- python39-devel
- python39-pip
- gcc
state: latestInstall the required pip modules
Ansible uses the PyMySQL module in python3 to connect and set up mysql server. Install it using ansible pip module
- name: Install Required pip modules
pip:
name:
- PyMySQL
state: present
executable: pip3Start and enable mysql service
Because we want to connect and perform operations in the server, lets start it with this task. Since centos 8 uses systemd to manage long running processes, lets start and enable mysqld using the ansible systemd module:
- name: Ensure mysql service is running
systemd:
name: mysqld
state: started
enabled: yesEnsure the root user can login from the local server
The mysql root user is the default admin user who has permissions to all resources in the server. A best practice would be to only enable access through this userroot in the local system when we are doing admin tasks otherwise create a dedicated user for each connection, i.e. for each app, have a user that has access to that db alone.
- name: Ensure root user can only login from localhost
mysql_user:
login_password: "{{ mysql_root_password }}"
check_implicit_admin: yes
name: root
host: "{{ item }}"
password: "{{ mysql_root_password }}"
state: present
with_items:
- localhost
- 127.0.0.1
- ::1In the above task definition:
- the
mysql_root_passwordvariable will be picked from thevarsdefined earlier - The
itemis a loop of the values defined in thewith_itemssection. - The
check_implicit_admintells ansible to try logging in without password which should work since this is a new install. As part of this, thepasswordprovided will be set for the root user
Add my.cnf to the home dir
Now that we have set the password in the above task, we would want to supply the password when doing more tasks as the root user. We can provide this in the ~/.my.cnf, a file that is checked for credentials everytime we run mysql commands.
Create a file my.cnf.j2 in the current directory:
[client]
user=root
password={{ mysql_root_password }}Then this is the task to copy to /root/.my.cnf since we are running the tasks as the root user.
- name: Add .my.cnf to user home
template:
src: my.cnf.j2
dest: /root/.my.cnfReload privileges
Run the following task to reload privileges for the changes we have made so far to apply:
- name: Reload privilege tables
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- FLUSH PRIVILEGES
changed_when: FalseRemove anonymous users
Its a good practice to remove anonymous users. Lets do it using this task:
- name: Remove anonymous users
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- DELETE FROM mysql.user WHERE User=''
changed_when: FalseDissalow root from loging in remotely
Run the following to remove entries in the mysql.user table. This ensure that the root user can only log in locally:
- name: Disallow root login remotely
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')
changed_when: FalseRemove test database and access to it
Since we do not need the test database, we can remove it with this task:
- name: Remove test database and access to it
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- DROP DATABASE IF EXISTS test
- DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'
changed_when: FalseReload privileges
To apply the changes we have made above, reload privileges with this task:
- name: Reload privilege tables
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- FLUSH PRIVILEGES
changed_when: FalseDelete the .my.cnf that we copied.
For security, lets remove the /root/.my.cnf file since it contains root access:
- name: Delete .my.conf
file:
path: /root/.my.cnf
state: absentWhole playbook
This is the whole playbook with all the tasks:
---
- name: Install mysql server 8 and initialize
hosts: db-server
gather_facts: false
become: true
vars:
mysql_root_password: 'secure-root-pw'
ansible_python_interpreter: /usr/bin/python3
tasks:
- name: Ensure required packages are installed
dnf:
name:
- mysql-server
- mysql-devel
- python39
- python39-devel
- python39-pip
- gcc
state: latest
- name: Install Required pip modules
pip:
name:
- PyMySQL
state: present
executable: pip3
- name: Ensure mysql service is running
systemd:
name: mysqld
state: restarted
enabled: yes
- name: Ensure root user can only login from localhost
mysql_user:
login_password: "{{ mysql_root_password }}"
check_implicit_admin: yes
name: root
host: "{{ item }}"
password: "{{ mysql_root_password }}"
state: present
with_items:
- localhost
- 127.0.0.1
- ::1
- name: Add .my.cnf to user home
template:
src: my.cnf.j2
dest: /root/.my.cnf
- name: Reload privilege tables
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- FLUSH PRIVILEGES
changed_when: False
- name: Remove anonymous users
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- DELETE FROM mysql.user WHERE User=''
changed_when: False
- name: Disallow root login remotely
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')
changed_when: False
- name: Remove test database and access to it
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- DROP DATABASE IF EXISTS test
- DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%'
changed_when: False
- name: Reload privilege tables
command: |
mysql -p{{ mysql_root_password }} -ne "{{ item }}"
with_items:
- FLUSH PRIVILEGES
changed_when: False
- name: Delete .my.conf
file:
path: /root/.my.cnf
state: absentTo run the playbook, you need to create the file setup-mysql.yaml with the above content and hosts.yaml with the hosts file content then use the following command to execute:
ansible-playbook -i hosts.yaml setup-mysql.yaml -vvConclusion
In this guide, we were able to use ansible to install mysql server 8 on a centos 8 host using ansible.
Ansible gives us a way to automate the process. This can be used to set up multiple instances in a predictable way using a single command.