Ansible idempotent Instalación de MySQL Playbook
command-line amazon-web-services (10)
Quiero configurar un servidor MySQL en AWS, usando Ansible para la gestión de la configuración. Estoy usando el AMI predeterminado de Amazon ( ami-3275ee5b ), que usa yum
para la administración de paquetes.
Cuando se ejecuta la Playbook a continuación, todo va bien. Pero cuando lo ejecuto por segunda vez, la tarea Configure the root credentials
falla, porque la contraseña anterior de MySQL ya no coincide, ya que se actualizó la última vez que ejecuté esta Playbook.
Esto hace que el Playbook no sea idempotente, lo que no me gusta. Quiero poder ejecutar el Playbook todas las veces que quiera.
- hosts: staging_mysql
user: ec2-user
sudo: yes
tasks:
- name: Install MySQL
action: yum name=$item
with_items:
- MySQL-python
- mysql
- mysql-server
- name: Start the MySQL service
action: service name=mysqld state=started
- name: Configure the root credentials
action: command mysqladmin -u root -p $mysql_root_password
¿Cuál sería la mejor manera de resolver esto, lo que significa que el Playbook es idempotente? ¡Gracias por adelantado!
Versión de Ansible para una instalación segura de MySQL.
mysql_secure_installation.yml
- hosts: staging_mysql
user: ec2-user
sudo: yes
tasks:
- name: Install MySQL
action: yum name={{ item }}
with_items:
- MySQL-python
- mysql
- mysql-server
- name: Start the MySQL service
action: service name=mysqld state=started
# ''localhost'' needs to be the last item for idempotency, see
# http://ansible.cc/docs/modules.html#mysql-user
- name: update mysql root password for all root accounts
mysql_user: name=root host={{ item }} password={{ mysql_root_password }}
with_items:
- "{{ ansible_hostname }}"
- 127.0.0.1
- ::1
- localhost
- name: copy .my.cnf file with root password credentials
template: src=templates/root/my.cnf.j2 dest=/root/.my.cnf owner=root mode=0600
- name: delete anonymous MySQL server user for $server_hostname
action: mysql_user user="" host="{{ server_hostname }}" state="absent"
- name: delete anonymous MySQL server user for localhost
action: mysql_user user="" state="absent"
- name: remove the MySQL test database
action: mysql_db db=test state=absent
templates / root / my.cnf.j2
[client]
user=root
password={{ mysql_root_password }}
Referencias
- La respuesta original de Lorin Hochstein
- https://github.com/gaspaio/ansible-devbox/blob/master/roles/mysql/tasks/server.yml
Agregando a las respuestas anteriores, no quería un paso manual antes de ejecutar el comando, es decir, quiero hacer girar un nuevo servidor y simplemente ejecutar el libro de jugadas sin tener que cambiar manualmente la contraseña de root la primera vez. No creo que {{mysql_password}} funcione la primera vez, cuando la contraseña de root sea nula, porque mysql_password aún tiene que definirse en algún lugar (a menos que desee sobrescribirlo con -e).
Así que agregué una regla para hacer eso, que se ignora si falla. Esto es además de, y aparece antes, cualquiera de los otros comandos aquí.
- name: Change root user password on first run
mysql_user: login_user=root
login_password=''''
name=root
password={{ mysql_root_password }}
priv=*.*:ALL,GRANT
host={{ item }}
with_items:
- $ansible_hostname
- 127.0.0.1
- ::1
- localhost
ignore_errors: true
Bueno, esto fue un poco complicado. He pasado un día entero sobre esto y se me ocurrió la solución que se detalla a continuación. El punto clave es cómo Ansible instala el servidor MySQL. De los documentos del módulo mysql_user (última nota en la página):
MySQL server installs with default login_user of ‘root’ and no password. To secure this user as part of an idempotent playbook, you must create at least two tasks: the first must change the root user’s password, without providing any login_user/login_password details. The second must drop a ~/.my.cnf file containing the new root credentials. Subsequent runs of the playbook will then succeed by reading the new credentials from the file.
Ese problema con la contraseña en blanco o nulo fue una gran sorpresa.
Papel :
---
- name: Install MySQL packages
sudo: yes
yum: name={{ item }} state=present
with_items:
- mysql
- mysql-server
- MySQL-python
- name: Start MySQL service
sudo: yes
service: name=mysqld state=started enabled=true
- name: Update MySQL root password for root account
sudo: yes
mysql_user: name=root password={{ db_root_password }} priv=*.*:ALL,GRANT
- name: Create .my.cnf file with root password credentials
sudo: yes
template: src=.my.cnf.j2 dest=/root/.my.cnf owner=root group=root mode=0600
notify:
- restart mysql
- name: Create a database
sudo: yes
mysql_db: name={{ db_name }}
collation=utf8_general_ci
encoding=utf8
state=present
- name: Create a database user
sudo: yes
mysql_user: name={{ db_user }}
password={{ db_user_password }}
priv="{{ db_name }}.*:ALL"
host=localhost
state=present
Controlador :
---
- name: restart mysql
service: name=mysqld state=restarted
.my.cnf.j2 :
[client]
user=root
password={{ db_root_password }}
Es importante iniciar / reiniciar el servidor mysql antes de configurar la contraseña de root. Además, había intentado todo lo publicado en esta publicación [fecha] y descubrí que es imprescindible pasar login_password
y login_user
.
(es decir) Cualquier Reproducción después de configurar el user:root
mysql_user
user:root
y password= {{ SOMEPASSWORD }}
, debe conectarse usando login_password
y login_user
para cualquier jugada posterior.
Nota: los elementos con los with_items
continuación se basan en lo que crearon los hosts predeterminados de Ansible y / MariaDB .
Ejemplo para asegurar un servidor MariaDB:
---
# ''secure_mariadb.yml''
- name: ''Ensure MariaDB server is started and enabled on boot''
service: name={{ mariadb_service_name }} state=started enabled=yes
# localhost needs to be the last item for idempotency, see
# http://ansible.cc/docs/modules.html#mysql-user
- name: ''Update Mysql Root Password''
mysql_user: name=root
host={{ item }}
password={{ root_db_password }}
priv=*.*:ALL,GRANT
state=present
with_items:
- 127.0.0.1
- ::1
- instance-1 # Created by MariaDB to prevent conflicts between port and sockets if multi-instances running on the same computer.
- localhost
- name: ''Create MariaDB main configuration file''
template: >
src=my.cnf.j2
dest=/etc/mysql/my.cnf
owner=root
group=root
mode=0600
- name: ''Ensure anonymous users are not in the database''
mysql_user: login_user=root
login_password={{ root_db_password }}
name=''''
host={{ item }}
state=absent
with_items:
- 127.0.0.1
- localhost
- name: ''Remove the test database''
mysql_db: login_user=root
login_password={{ root_db_password }}
name=test
state=absent
- name: ''Reload privilege tables''
command: ''mysql -ne "{{ item }}"''
with_items:
- FLUSH PRIVILEGES
changed_when: False
- name: ''Ensure MariaDB server is started and enabled on boot''
service: name={{ mariadb_service_name }} state=started enabled=yes
# ''End Of File''
Esta es una solución alternativa a la propuesta por @LorinHochStein
Una de mis limitaciones era garantizar que no se almacenaran contraseñas en archivos de texto plano en ninguna parte del servidor. Por lo tanto .my.cnf no era una propuesta práctica
Solución:
- name: update mysql root password for all root accounts from local servers
mysql_user: login_user=root
login_password={{ current_password }}
name=root
host=$item
password={{ new_password }}
priv=*.*:ALL,GRANT
with_items:
- $ansible_hostname
- 127.0.0.1
- ::1
- localhost
Y en el archivo vars
current_password: foobar
new_password: "{{ current_password }}"
Cuando no se cambia la contraseña de mysql, ejecute el libro de jugadas ansible en la línea de comandos como de costumbre.
Al cambiar la contraseña de mysql, agregue lo siguiente a la línea de comando. Especificarlo en la línea de comandos permite que el parámetro establecido en la línea de comando tenga prioridad sobre el predeterminado en el archivo vars.
$ ansible-playbook ........ --extra-vars "new_password=buzzz"
Después de ejecutar el comando, cambie el archivo vars de la siguiente manera
current_password=buzzz
new_password={{ current_password }}
Estoy agregando mi propia opinión sobre los diversos enfoques (centos 7).
La variable mysql_root_password debe almacenarse en una ansible-vault (mejor) o pasarla en la línea de comando (peor)
- name: "Ensure mariadb packages are installed"
yum: name={{ item }} state="present"
with_items:
- mariadb
- mariadb-server
- name: "Ensure mariadb is running and configured to start at boot"
service: name=mariadb state=started enabled=yes
# idempotently ensure secure mariadb installation --
# - attempts to connect as root user with no password and then set the root@ mysql password for each mysql root user mode.
# - ignore_errors is true because this task will always fail on subsequent runs (as the root user password has been changed from "")
- name: Change root user password on first run, this will only succeed (and only needs to succeed) on first playbook run
mysql_user: login_user=root
login_password=''''
name=root
password={{ mysql_root_password }}
priv=*.*:ALL,GRANT
host={{ item }}
with_items:
- "{{ ansible_hostname }}"
- 127.0.0.1
- ::1
- localhost
ignore_errors: true
- name: Ensure the anonymous mysql user ""@{{ansible_hostname}} is deleted
action: mysql_user user="" host="{{ ansible_hostname }}" state="absent" login_user=root login_password={{ mysql_root_password }}
- name: Ensure the anonymous mysql user ""@localhost is deleted
action: mysql_user user="" state="absent" login_user=root login_password={{ sts_ad_password }}
- name: Ensure the mysql test database is deleted
action: mysql_db db=test state=absent login_user=root login_password={{ mysql_root_password }}
Lo siguiente funcionará (Inserte my.cnf entre 2 llamadas a mysql_user)
- name: ''Install MySQL''
yum: name={{ item }} state=present
with_items:
- MySQL-python
- mysql
- mysql-server
notify:
- restart-mysql
- name: ''Start Mysql Service''
action: service name=mysqld state=started enabled=yes
- name: ''Update Mysql Root Password''
mysql_user: name=root host=localhost password={{ mysql_root_password }} state=present
- name: ''Copy Conf file with root password credentials''
template: src=../templates/my.cnf.j2 dest=/root/.my.cnf owner=root mode=0600
- name: ''Update Rest-Mysql Root Password''
mysql_user: name=root host={{ item }} password={{ mysql_root_password }} state=present
with_items:
- "{{ ansible_hostname }}"
- "{{ ansible_eth0.ipv4.address }}"
- 127.0.0.1
- ::1
- name: ''Delete anonymous MySQL server user from server''
mysql_user: name="" host={{ ansible_hostname }} state="absent"
Para ansible 1.3+:
- name: ensure mysql local root password is zwx123
mysql_user: check_implicit_admin=True login_user=root login_password="zwx123" name=root password="zwx123" state=present
Sé que esta es una vieja pregunta, pero estoy compartiendo mi manual de trabajo para aquellos que lo están buscando:
mysql.yml
---
- name: Install the MySQL packages
apt: name={{ item }} state=installed update_cache=yes
with_items:
- mysql-server-5.6
- mysql-client-5.6
- python-mysqldb
- libmysqlclient-dev
- name: Copy the configuration file (my.cnf)
template: src=my.cnf.j2 dest=/etc/mysql/my.cnf
notify:
- Restart MySQL
- name: Update MySQL root password for all root accounts
mysql_user: name=root host={{ item }} password={{ mysql_root_pass }} state=present
with_items:
- "{{ ansible_hostname }}"
- 127.0.0.1
- ::1
- localhost
- name: Copy the root credentials as .my.cnf file
template: src=root.cnf.j2 dest=~/.my.cnf mode=0600
- name: Ensure Anonymous user(s) are not in the database
mysql_user: name='''' host={{ item }} state=absent
with_items:
- localhost
- "{{ ansible_hostname }}"
- name: Remove the test database
mysql_db: name=test state=absent
notify:
- Restart MySQL
vars.yml
---
mysql_port: 3306 #Default is 3306, please change it if you are using non-standard
mysql_bind_address: "127.0.0.1" #Change it to "0.0.0.0",if you want to listen everywhere
mysql_root_pass: mypassword #MySQL Root Password
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 = {{ mysql_port }}
basedir = /usr
datadir = /var/lib/mysql
tmpdir = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address = {{ mysql_bind_address }}
key_buffer = 16M
max_allowed_packet = 64M
thread_stack = 192K
thread_cache_size = 8
myisam-recover = BACKUP
query_cache_limit = 1M
query_cache_size = 16M
log_error = /var/log/mysql/error.log
expire_logs_days = 10
max_binlog_size = 100M
[mysqldump]
quick
quote-names
max_allowed_packet = 64M
[mysql]
[isamchk]
key_buffer = 16M
!includedir /etc/mysql/conf.d/
root.cnf.j2
[client]
user=root
password={{ mysql_root_pass }}
coderwall sobre esto en coderwall , pero reproduciré la mejora de dennisjac en los comentarios de mi publicación original.
El truco para hacerlo idempotentemente es saber que el módulo mysql_user cargará un archivo ~ / .my.cnf si encuentra uno.
Primero cambio la contraseña y luego copio un archivo .my.cnf con las credenciales de la contraseña. Cuando intente ejecutarlo por segunda vez, el módulo ansible myqsl_user encontrará .my.cnf y usará la nueva contraseña.
- hosts: staging_mysql
user: ec2-user
sudo: yes
tasks:
- name: Install MySQL
action: yum name={{ item }}
with_items:
- MySQL-python
- mysql
- mysql-server
- name: Start the MySQL service
action: service name=mysqld state=started
# ''localhost'' needs to be the last item for idempotency, see
# http://ansible.cc/docs/modules.html#mysql-user
- name: update mysql root password for all root accounts
mysql_user: name=root host={{ item }} password={{ mysql_root_password }} priv=*.*:ALL,GRANT
with_items:
- "{{ ansible_hostname }}"
- 127.0.0.1
- ::1
- localhost
- name: copy .my.cnf file with root password credentials
template: src=templates/root/.my.cnf dest=/root/.my.cnf owner=root mode=0600
La plantilla .my.cnf se ve así:
[client]
user=root
password={{ mysql_root_password }}
Editar: se agregaron los privilegios recomendados por Dhananjay Nene en los comentarios, y se cambió la interpolación variable para usar llaves en lugar de un signo de dólar .