Ansible idempotent MySQL installation Playbook
je veux configurer un serveur MySQL sur AWS, en utilisant Ansible pour la gestion de la configuration.
J'utilise L'AMI par défaut D'Amazon ( ami-3275ee5b), qui utilise yum
pour la gestion des paquets.
quand le Playbook ci-dessous est exécuté, tout va bien. Mais quand je l'exécute pour la deuxième fois, la tâche Configure the root credentials
échoue, parce que L'ancien mot de passe de MySQL ne correspond plus, puisqu'il a été mis à jour la dernière fois que j'ai lancé ce Playbook.
Ce qui rend la Playbook non-idempotent, ce que je n'aime pas. Je veux être capable d'exécuter le Playbook autant de fois que je veux.
- 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
quelle serait la meilleure façon de résoudre cela, ce qui signifie faire le Playbook idempotent? Merci à l'avance!
10 réponses
j'ai posté à ce sujet sur coderwall, mais je vais reproduire l'amélioration de dennisjac dans les commentaires de mon billet original.
le truc pour le faire de façon idempo est de savoir que le module mysql_user chargera un ~/.mon Dieu.fichier cnf s'il en trouve un.
je change d'abord le mot de passe, puis je copie A. mon Dieu.fichier cnf avec le mot de passe. Lorsque vous tentez de l'exécuter une deuxième fois, le module myqsl_user ansible le trouvera .mon Dieu.cnf et utiliser le nouveau le mot de passe.
- 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
le .mon Dieu.le modèle cnf ressemble à ceci:
[client]
user=root
password={{ mysql_root_password }}
Edit: ajout de privilèges tel que recommandé par Dhananjay Nene dans les commentaires, et modification de l'interpolation des variables pour utiliser des accolades au lieu du signe dollar.
version Ansible pour une installation MySQL sécurisée.
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 }}
Références
C'est une solution alternative à celle proposée par @LorinHochStein
une de mes contraintes était de m'assurer qu'aucun mot de passe ne soit stocké dans des fichiers texte en clair n'importe où sur le serveur. Ainsi .mon Dieu.le fcen n'était pas une proposition pratique
Solution:
- 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
Et dans le fichier vars
current_password: foobar
new_password: "{{ current_password }}"
si vous ne changez pas le mot de passe mysql, lancez un playbook accessible en ligne de commande comme d'habitude.
en changeant le mot de passe mysql, ajoutez le suivez la ligne de commande. Le spécifier sur la ligne de commande permet au paramètre défini sur la ligne de commande d'avoir la priorité sur celui qui est par défaut dans le fichier vars.
$ ansible-playbook ........ --extra-vars "new_password=buzzz"
après avoir exécuté la commande, changez le fichier vars comme suit
current_password=buzzz
new_password={{ current_password }}
en ajoutant aux réponses précédentes, Je ne voulais pas d'étape manuelle avant d'exécuter la commande, c'est à dire que je veux lancer un nouveau serveur et juste exécuter le playbook sans avoir à modifier manuellement le mot de passe racine pour la première fois. Je ne crois pas que {{ mysql_password} fonctionne la première fois, quand le mot de passe root est nul, parce que mysql_password doit encore être défini quelque part (à moins que vous ne vouliez l'annuler avec-e).
alors j'ai ajouté une règle pour faire ça, qui est ignorée si elle échoue. C'est dans plus, et apparaît avant, les autres commandes ici.
- 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
Pour 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
Eh bien, c'est venu un peu compliqué. J'ai passé une journée entière sur ce et est venu avec la solution indiquée ci-dessous. Le point clé est la façon dont Ansible installe MySQL server. À partir de la documentation de mysql_user module (dernière remarque de la page):
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.
ce problème avec le mot de passe vide ou nul était un gros surprise.
Rôle:
---
- 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
Gestionnaire:
---
- name: restart mysql
service: name=mysqld state=restarted
.mon Dieu.cnf.j2:
[client]
user=root
password={{ db_root_password }}
La suite de Travailler (Insérer mon.cnf entre 2 mysql_user appels)
- 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"
je sais que c'est une vieille question, mais je partage mon manuel de travail pour ceux qui la cherchent:
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
mon.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/
racine.cnf.j2
[client]
user=root
password={{ mysql_root_pass }}
il est important de démarrer/redémarrer le serveur mysql avant de définir le mot de passe racine. Aussi, j'avais tout essayé affiché jusqu'à ce post [date] et a découvert qu'il est impératif de passer login_password
et login_user
.
(i.e.) N'importe quel jeu après avoir mis le mysql_user
user:root
et password= {{ SOMEPASSWORD }}
, vous devez vous connecter en utilisant login_password
et login_user
pour tous les jouer.
Remarque:with_items
ci-dessous est basé sur ce que les hôtes par défaut Ansible &/ MariaDB créé.
exemple de sécurisation D'un serveur 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'
j'ajoute mon propre point de vue sur les différentes approches (centos 7).
la variable mysql_root_password doit être stockée dans une voûte possible (meilleure) ou transmise sur la ligne de commande (pire)
- 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 }}