it-swarm.com.ru

Ansible идемпотентная установка MySQL Playbook

Я хочу настроить сервер MySQL на AWS, используя Ansible для управления конфигурацией ... Я использую AMI по умолчанию от Amazon ( AMI-3275ee5b ), который использует yum для управления пакетами.

Когда Playbook ниже выполняется, все идет хорошо. Но когда я запускаю его во второй раз, задача Configure the root credentials не выполняется, потому что старый пароль MySQL больше не совпадает, так как он был обновлен при последнем запуске этого Playbook.

Это делает Playbook неидемпотентным, что мне не нравится. Я хочу иметь возможность запускать Playbook столько раз, сколько захочу.

- 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

Как лучше всего решить эту проблему, а значит сделать идемпотент Playbook? Заранее спасибо!

40
ndequeker

Ansible версия для безопасной установки 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

шаблоны/корень/my.cnf.j2

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

Рекомендации

29
ndequeker

Я написал об этом на coderwall , но я воспроизвожу улучшение dennisjac в комментариях к моему оригинальному сообщению.

Хитрость в том, чтобы сделать это идемпотентно, это знать, что модуль mysql_user загрузит файл ~/.my.cnf, если найдет его.

Сначала я изменяю пароль, затем копирую файл .my.cnf с учетными данными пароля. Когда вы попытаетесь запустить его во второй раз, модуль ansq myqsl_user найдет .my.cnf и использует новый пароль.

- 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

Шаблон .my.cnf выглядит следующим образом:

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

Edit: добавлены привилегии в соответствии с рекомендациями Dhananjay Nene в комментариях и изменена интерполяция переменных для использования фигурных скобок вместо знака доллара.

35
Lorin Hochstein

Это альтернативное решение, предложенное @LorinHochStein

Одно из моих ограничений заключалось в том, чтобы пароли не сохранялись в текстовых файлах где-либо на сервере. Таким образом .my.cnf не было практическим предложением

Решение :

- 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

И в файле Vars

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

Если пароль mysql не изменен, запустите ansible playbook из командной строки, как обычно.

При изменении пароля mysql добавьте следующее в командную строку. Указание его в командной строке позволяет параметру в командной строке иметь приоритет над параметром по умолчанию в файле vars.

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

После запуска команды измените файл vars следующим образом

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

Добавляя к предыдущим ответам, я не хотел выполнять шаг вручную перед запуском команды, то есть я хочу раскрутить новый сервер и просто запустить playbook без необходимости сначала вручную изменять пароль root. Я не верю, что {{mysql_password}} сработает в первый раз, когда пароль root равен null, потому что mysql_password все еще должен быть где-то определен (если вы не хотите переопределить его с помощью -e).

Поэтому я добавил для этого правило, которое игнорируется в случае сбоя. Это является дополнением к любой из других команд здесь и появляется перед ней.

- 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

Для 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

Ну, это оказалось немного сложным. Я потратил на это целый день и нашел решение, указанное ниже. Ключевым моментом является то, как Ansible устанавливает сервер MySQL. Из документов mysql_user модуля (последняя заметка на странице):

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.

Эта проблема с пустым или нулевым паролем была большой неожиданностью.

Роль:

---

- 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

Обработчик:

---

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

.my.cnf.j2:

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

Следующее будет работать (вставьте my.cnf между 2 вызовами 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"
1
smuniyappa

Важно запустить/перезапустить сервер MySQL перед установкой пароля root. Кроме того, я перепробовал все, что было опубликовано до этой записи [дата], и обнаружил, что обязательно передавать login_password и login_user.

(т.е.). Любое Воспроизведение после установки mysql_useruser:root и password= {{ SOMEPASSWORD }}, вы должны подключиться, используя login_password и login_user для любого последующего воспроизведения.

Примечание: with_items ниже основан на том, что хосты Ansible и/MariaDB по умолчанию создали.

Пример для защиты сервера 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'
0
user742030

Мы потратили много времени на эту проблему. Для MySQL 5.7 и выше мы пришли к выводу, что проще просто игнорировать учетную запись root и устанавливать разрешения для обычного пользователя MySQL.

Причины

  1. Установить пароль root сложно
  2. unix_socket плагин auth конфликтует со стандартным плагином auth
  3. Надежное изменение пароля root после отключения плагина unix_socket практически невозможно
  4. Ansible не очень подходит для атомарного изменения пароля root за один шаг
  5. Использование обычной учетной записи в целом работает хорошо

Если вы откажетесь от идемпотентности, то сможете заставить ее работать нормально. Однако, поскольку ценностное предложение в том, что идемпотентность возможна, мы обнаруживаем, что разработчики тратят время с неверным предположением.

Само существование опции взлома, такой как check_implicit_admin, начинает нам намекать, что детерминированная установка MySQL не так проста. Если это на самом деле детерминированный, не должно быть «проверки», должно быть только «делать».

0
Ryan

Я знаю, что это старый вопрос, но я делюсь своей рабочей тетрадью для тех, кто ее ищет:

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

Я добавляю свой взгляд на различные подходы (centos 7).

Переменная mysql_root_password должна храниться в хранилище ansible (лучше) или передаваться в командной строке (хуже)

- 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