web-dev-qa-db-de.com

Ansible idempotent MySQL-Installations-Playbook

Ich möchte einen MySQL-Server unter AWS einrichten und Ansible für die Konfigurationsverwaltung verwenden. __ Ich verwende die Standard-AMI von Amazon ( AMI-3275ee5b ), die yum für die Paketverwaltung verwendet.

Wenn das untenstehende Playbook ausgeführt wird, ist alles gut. Wenn ich es jedoch ein zweites Mal starte, schlägt die Aufgabe Configure the root credentials fehl, da das alte Passwort von MySQL nicht mehr übereinstimmt, da es beim letzten Ausführen dieses Playbooks aktualisiert wurde.

Dies macht das Playbook nicht idempotent, was ich nicht mag. Ich möchte das Playbook beliebig oft ausführen können.

- 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

Was wäre der beste Weg, das zu lösen, was das Playbook idempotent macht? Danke im Voraus!

40
ndequeker

Ansible-Version für eine sichere MySQL-Installation.

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

vorlagen/root/my.cnf.j2

[client]
user=root
password={{ mysql_root_password }}

Verweise

29
ndequeker

Ich habe dies auf coderwall gepostet, aber ich werde die Verbesserung von dennisjac in den Kommentaren meines ursprünglichen Posts wiedergeben.

Der Trick, es idempotent zu tun, ist zu wissen, dass das Modul mysql_user eine ~/.my.cnf -Datei lädt, wenn es eine findet.

Ich ändere zunächst das Passwort und kopiere dann eine .my.cnf-Datei mit den Passwort-Zugangsdaten. Wenn Sie versuchen, es ein zweites Mal auszuführen, findet das ansible-Modul myqsl_user die Datei .my.cnf und verwendet das neue Kennwort.

- 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

Die .my.cnf-Vorlage sieht folgendermaßen aus:

[client]
user=root
password={{ mysql_root_password }}

Bearbeiten: In den Kommentaren wurden die von Dhananjay Nene empfohlenen Berechtigungen hinzugefügt und die Interpolation der Variablen so geändert, dass Klammern anstelle von Dollarzeichen verwendet werden.

35
Lorin Hochstein

Dies ist eine alternative Lösung zu der von @LorinHochStein vorgeschlagenen

Eine meiner Einschränkungen bestand darin, sicherzustellen, dass keine Passwörter in Klartextdateien auf dem Server gespeichert werden. Somit war .my.cnf kein praktischer Vorschlag

Lösung:

- 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

Und in der VAR-Datei

current_password: foobar
new_password: "{{ current_password }}"

Wenn Sie das MySQL-Kennwort nicht ändern, führen Sie das Ansible Playbook wie üblich in der Befehlszeile aus.

Wenn Sie das mysql-Kennwort ändern, fügen Sie der Befehlszeile Folgendes hinzu. Durch die Angabe in der Befehlszeile kann der Parametersatz in der Befehlszeile Vorrang vor demjenigen haben, der standardmäßig in der VARS-Datei verwendet wird.

$ ansible-playbook ........ --extra-vars "new_password=buzzz"

Nachdem Sie den Befehl ausgeführt haben, ändern Sie die vars-Datei wie folgt

current_password=buzzz
new_password={{ current_password }}
5
Dhananjay Nene

Zusätzlich zu den vorherigen Antworten wollte ich vor dem Ausführen des Befehls keinen manuellen Schritt, dh ich möchte einen neuen Server hochfahren und das Playbook ausführen, ohne das Root-Passwort zuerst manuell ändern zu müssen. Ich glaube nicht, dass {{mysql_password}} das erste Mal funktioniert, wenn das root-Passwort null ist, da mysql_password noch irgendwo definiert werden muss (es sei denn, Sie möchten es mit -e überschreiben).

Also habe ich eine Regel dazu hinzugefügt, die ignoriert wird, wenn sie fehlschlägt. Dies ist zusätzlich zu den anderen Befehlen hier und wird auch vorher angezeigt.

- 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
5
mahemoff

Für 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
4
anneb

Nun, das war etwas kompliziert. Ich habe einen ganzen Tag damit verbracht und die unten aufgelistete Lösung gefunden. Der Schlüsselpunkt ist, wie Ansible den MySQL-Server installiert. Aus den Dokumenten von mysql_user module (letzter Hinweis auf Seite):

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.

Dieses Problem mit einem leeren oder leeren Passwort war eine große Überraschung.

Rolle:

---

- 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

Handler:

---

- name: restart mysql
  service: name=mysqld state=restarted

.my.cnf.j2:

[client]
user=root
password={{ db_root_password }}
3
oblalex

Folgendes wird funktionieren (my.cnf zwischen 2 Aufrufen von mysql_user einfügen)


- 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"
1
smuniyappa

Es ist wichtig, den mysql-Server vor dem Festlegen des root-Passworts neu zu starten. Ich hatte auch alles ausprobiert, was bis zu diesem Beitrag [Datum] gepostet wurde, und stellte fest, dass es unbedingt erforderlich ist, login_password und login_user zu übergeben.

(d. h.) Bei jeder Wiedergabe, nachdem Sie mysql_useruser:root und password= {{ SOMEPASSWORD }} eingestellt haben, müssen Sie sich für die nachfolgende Wiedergabe mit login_password und login_user verbinden.

Hinweis: Der with_items basiert auf den von Ansible &/MariaDB erstellten Standardhosts.

Beispiel für das Sichern eines MariaDB-Servers:

---
# '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'
0
user742030

Wir haben viel Zeit mit diesem Thema verbracht. Für MySQL 5.7 und höher schlussfolgerten wir, dass es einfacher ist, das root-Konto einfach zu ignorieren und Berechtigungen für einen normalen MySQL-Benutzer festzulegen.

Gründe dafür

  1. Das Einstellen des Root-Passworts ist schwierig
  2. unix_socket auth plugin steht in Konflikt mit dem standard auth plugin
  3. Das zuverlässige Ändern des root-Passworts nach dem Deaktivieren des unix_socket-Plugins ist nahezu unmöglich
  4. Ansible ist nicht geeignet, das root-Passwort in einem Schritt atomar zu ändern
  5. Die Verwendung eines normalen Kontos funktioniert allgemein gut

Wenn Sie Idempotenz aufgeben, können Sie es gut funktionieren lassen. Da jedoch der mögliche Wert des Ideals lautet, dass Idempotenz möglich ist, stellen wir fest, dass Entwickler Zeit mit der falschen Annahme verschwenden.

Die bloße Existenz einer Hack-Option wie check_implicit_admin weist darauf hin, dass deterministisches MySQL-Setup nicht so einfach ist. Wenn es tatsächlich deterministisch ist, sollte es keine "Prüfung" geben, es sollte nur "tun" geben.

0
Ryan

Ich weiß, das ist eine alte Frage, aber ich teile mein Arbeitsbuch für diejenigen, die es suchen:

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 }}
0
Arbab Nazar

Ich füge meine eigenen Ansichten zu den verschiedenen Ansätzen hinzu (Centos 7).

Die Variable mysql_root_password sollte in einem Ansible-Vault (besser) gespeichert oder in der Befehlszeile (schlechter) übergeben werden.

- 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 [email protected] 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 }}
0
Ben