47

我想在 AWS 上设置一个 MySQL 服务器,使用 Ansible 进行配置管理。我正在使用来自 Amazon ( ami-3275ee5b ) 的默认 AMI,它yum用于包管理。

当执行下面的剧本时,一切顺利。但是当我第二次运行它时,任务Configure the root credentials失败了,因为 MySQL 的旧密码不再匹配,因为我上次运行这个 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 具有幂等性?提前致谢!

4

13 回答 13

37

我在coderwall上发布了有关此内容,但我将在我原始帖子的评论中重现 dennisjac 的改进。

做到这一点的诀窍是知道 mysql_user 模块如果找到一个 ~/.my.cnf 文件就会加载它。

我首先更改密码,然后复制带有密码凭据的 .my.cnf 文件。当您尝试第二次运行它时,myqsl_user ansible 模块将找到 .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 }}

编辑:添加了 Dhananjay Nene 在评论中推荐的权限,并将变量插值更改为使用大括号而不是美元符号

于 2013-05-08T16:56:11.487 回答
33

用于安全 MySQL 安装的 Ansible 版本。

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 }}

参考

于 2013-05-20T15:14:46.860 回答
5

添加到前面的答案中,我不想在运行命令之前执行手动步骤,即我想启动一个新服务器并运行 playbook,而无需第一次手动更改 root 密码。我不相信 {{ mysql_password }} 第一次会起作用,当 root 密码为空时,因为 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
于 2014-03-04T01:49:46.737 回答
5

这是@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 }}
于 2013-11-15T09:24:05.883 回答
4

For 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
于 2014-08-29T22:48:13.723 回答
3

嗯,这有点复杂。我花了一整天的时间想出了下面列出的解决方案。关键是 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 }}
于 2014-09-23T15:32:24.580 回答
1

以下将起作用(在 2 个 mysql_user 调用之间插入 my.cnf)


- 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"
于 2014-01-29T23:24:06.013 回答
1

请尝试使用 ansible playbook 安装 mysql

主机:slave.server.com 变为:是

任务:

  • 名称:“安装回购”外壳:sudo rpm -Uvh http://dev.mysql.com/get/mysql57-community-release-el6-7.noarch.rpm ignore_errors:是

  • 名称:“安装 MySQL 5.7”包:名称=mysql-community-server state=present

  • name:启动 MySQL 服务 service:name:mysqld state:started enabled:yes

  • 名称:查找 MySQL root 密码 shell:“echo grep 'temporary.*root@localhost' /var/log/mysqld.log | sed 's/.*root@localhost: //'” 注册:mysql_root_pass

于 2021-03-05T17:04:20.753 回答
0

我知道这是一个老问题,但我正在为那些正在寻找它的人分享我的工作手册:

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

变量.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

我的.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/

根.cnf.j2

[client]
user=root
password={{ mysql_root_pass }}
于 2015-05-26T01:19:02.100 回答
0

在设置 root 密码之前启动/重新启动 mysql 服务器很重要。此外,我已经尝试了发布到这篇文章 [日期] 的所有内容,并发现必须通过login_passwordlogin_user.

(ie) 设置mysql_user user: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'
于 2015-06-24T22:05:07.243 回答
0

我们在这个问题上花了很多时间。对于 MySQL 5.7 及更高版本,我们得出的结论是,简单地忽略 root 帐户,并在常规 MySQL 用户上设置权限更容易。

原因

  1. 设置root密码很困难
  2. unix_socketauth 插件与标准 auth 插件冲突
  3. 禁用unix_socket插件后可靠地更改 root 密码几乎是不可能的
  4. Ansible 不太适合一步原子地更改 root 密码
  5. 使用普通帐户效果很好

如果你放弃幂等性,那么你可以让它正常工作。然而,由于 ansible 的价值主张是幂等性是可能的,我们发现开发人员在错误的假设上浪费了时间。

仅仅存在类似 hack 选项check_implicit_admin就开始向我们暗示,确定性 MySQL 设置并不容易。如果它实际上是确定性的,就不应该有“检查”,而应该只有“做”。

于 2019-02-05T05:11:51.757 回答
0

我正在添加我自己对各种方法的看法(centos 7)。

变量 mysql_root_password 应该存储在 ansible-vault 中(更好)或在命令行上传递(更糟)

- 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 }}
于 2015-09-04T18:27:36.840 回答
0

我正在使用 Ansible 2.9.20 并为 8.0.26 版本的 mysql 安装创建了剧本。此 mysql 版本安装有一些更改,因此在此处添加对我有用的解决方案。

MySQL.yml

---
# tasks file for mysql_setup
- name: Upgrade all packages
  yum:
    name: "*"
    state: latest

- name: Install MySQL repository
  yum:
    name: "https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm"
    state: present

- name: Install MySQL
  yum:
    name: ['mysql-community-devel*', 'mysql-community-server*', 'MySQL-python']
    state: present

- name: copy my.cnf
  copy:
    src: ../files/etc/my.cnf
    dest: /etc/my.cnf
    mode: 0644

- name: Enable the MySQL service
  service:
    name: mysqld
    state: restarted
    enabled: true

- name: Read secret file
  include_vars: "defaults/secret.yml"

- name: get root password
  shell: "grep 'A temporary password is generated for root@localhost' /var/log/mysqld.log | awk -F ' ' '{print $(NF)}'"
  register: root_password

- name: Ensure root can login into MySQL localhost using temporary password
  shell: "mysql -uroot -p'{{ root_password.stdout }}' --connect-expired-password"
  with_items:
    - 127.0.0.1
    - ::1
    - localhost
  register: root_login_tmp_pass
  ignore_errors: yes

- name: update expired root user password
  command: mysql --user root --password={{ root_password.stdout }} --connect-expired-password --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '{{ secret.passwd_mysql_root }}';"
  when: root_login_tmp_pass is succeeded

- name: update root user password
  command: mysql --user root --password={{ secret.current_passwd_mysql_root }} --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '{{ secret.passwd_mysql_root }}';"
  when: root_login_tmp_pass is failed

- name: Copy root .my.cnf file
  template:
    src: ../templates/root-my.cnf.j2
    dest: /root/.my.cnf
    owner: root
    group: root
    mode: 0600

- name: Create a database
  mysql_db: name={{ db_name }}
            collation=utf8_general_ci
            encoding=utf8
            state=present

- name: Create a database user
  mysql_user: name={{ db_user }}
              password={{ secret.db_user_password }}
              priv="{{ db_name }}.*:ALL"
              host=localhost
              state=present

模板/根-my.cnf.j2

[client]
user=root
password={{ secret.passwd_mysql_root }}

文件/etc/my.cnf

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
default-authentication-plugin=mysql_native_password

默认值/main.yml

---
# defaults file for mysql_setup
db_name: mydb
db_user: iamuser

默认值/secret.yml

secret:
  passwd_mysql_root: RootPassword2!3
  db_user_password: iamdbpassword
  current_passwd_mysql_root: currRootPass2!3

运行此 playbook 两次后,您必须使用当前密码 (current_passwd_mysql_root) 和要设置的 root 密码 (passwd_mysql_root) 更新此 secret.yml 文件。

于 2021-08-15T12:41:59.923 回答