Je souhaite 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 packages.
Lorsque le Playbook ci-dessous est exécuté, tout va bien. Mais lorsque je l'exécute une seconde fois, la tâche Configure the root credentials
échoue, car l'ancien mot de passe de MySQL ne correspond plus, car il a été mis à jour la dernière fois que j'ai exécuté ce Playbook.
Cela rend le Playbook non-idempotent, ce que je n'aime pas. Je veux pouvoir exécuter le Playbook autant de fois que je le souhaite.
- 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 ce problème, ce qui signifie que le Playbook serait idempotent? Merci d'avance!
- 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
[client]
user=root
password={{ mysql_root_password }}
J'ai posté à ce sujet sur coderwall , mais je vais reproduire l'amélioration de dennisjac dans les commentaires de mon message d'origine.
L'astuce pour le faire de façon idoti- cace est de savoir que le module mysql_user chargera un fichier ~/.my.cnf s'il en trouve un.
Je change d'abord le mot de passe, puis copie un fichier .my.cnf avec les informations d'identification du mot de passe. Lorsque vous essayez de l'exécuter une seconde fois, le module myqsl_user ansible trouvera le fichier .my.cnf et utilisera le nouveau 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 modèle .my.cnf ressemble à ceci:
[client]
user=root
password={{ mysql_root_password }}
Edit: Ajout des privilèges recommandés par Dhananjay Nene dans les commentaires et modification de l'interpolation des variables pour utiliser des accolades au lieu du signe dollar.
C'est une solution alternative à celle proposée par @LorinHochStein
L'une de mes contraintes était de m'assurer qu'aucun mot de passe ne soit stocké dans des fichiers de texte en clair, où que ce soit sur le serveur. Donc. Mon.cnf 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 }}"
Lorsque vous ne modifiez pas le mot de passe mysql, lancez ansible playbook en ligne de commande, comme d’habitude.
Lorsque vous modifiez le mot de passe mysql, ajoutez ce qui suit à la ligne de commande. En le spécifiant sur la ligne de commande, le paramètre défini sur la ligne de commande a priorité sur celui par défaut du 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 souhaitais créer un nouveau serveur et lancer le playbook sans avoir à modifier manuellement le mot de passe root pour la première fois. Je ne crois pas que {{mysql_password}} fonctionnera la première fois, lorsque le mot de passe root est nul, car mysql_password doit encore être défini quelque part (à moins que vous ne souhaitiez le remplacer par -e).
J'ai donc ajouté une règle pour le faire, qui est ignorée si elle échoue. Ceci est en plus de, et apparaît avant, l'une des 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, cela est venu un peu compliqué. J'ai passé une journée entière sur ce sujet et j'ai proposé la solution ci-dessous. Le point clé est la façon dont Ansible installe le serveur MySQL. Depuis la documentation de mysql_user module (dernière note 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 un mot de passe vide ou nul était une grosse 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
.my.cnf.j2:
[client]
user=root
password={{ db_root_password }}
Ce qui suit fonctionnera (insérez my.cnf entre 2 appels 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"
Il est important de démarrer/redémarrer le serveur mysql avant de définir le mot de passe root. De plus, j'avais tout essayé jusqu'à la poste [date] et j'avais découvert qu'il était impératif de transmettre login_password
et login_user
.
(i.e.) Toute lecture après avoir défini mysql_user
user:root
et password= {{ SOMEPASSWORD }}
, vous devez vous connecter en utilisant login_password
et login_user
pour toute lecture ultérieure.
Remarque: Le with_items
ci-dessous est basé sur ce que les hôtes par défaut d'Ansible &/MariaDB ont 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'
Nous avons passé beaucoup de temps sur cette question. Pour MySQL 5.7 et les versions ultérieures, nous avons conclu qu'il était plus simple d'ignorer le compte root et de définir les autorisations sur un utilisateur MySQL standard.
unix_socket
auth plugin est en conflit avec le plugin auth standardunix_socket
est presque impossibleSi vous abandonnez idempotency, vous pourrez alors le faire fonctionner correctement. Cependant, comme la proposition de valeur ansible est que l’idempotence est possible, nous constatons que les développeurs perdent du temps avec la fausse hypothèse.
La simple existence d’une option de type hack telle que check_implicit_admin
commence à nous faire comprendre que la configuration déterministe de MySQL n’est pas si facile. Si c'est réellement déterministe, il ne devrait pas y avoir de "vérification", il devrait y avoir seulement "faire".
Je sais que c'est une vieille question, mais je partage mon livre de travail avec ceux qui le cherchent
---
- 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
---
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
[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/
[client]
user=root
password={{ mysql_root_pass }}
J'ajoute ma propre vision des différentes approches (centos 7).
La variable mysql_root_password devrait être stockée dans un ansible-vault (meilleur) ou passée 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 }}