programing

책임감 있는 MySQL 설치 플레이북

yellowcard 2023. 8. 23. 21:42
반응형

책임감 있는 MySQL 설치 플레이북

구성 관리를 위해 Ansible을 사용하여 AWS에 MySQL 서버를 설정하고 싶습니다.나는 아마존의 기본 AMI(ami-3275ee5b)를 사용하고 있으며, 이는yum패키지 관리를 위해.

아래 Playbook을 실행하면 모든 것이 잘 진행됩니다.하지만 두 번째로 실행하면 작업을 수행합니다.Configure the root credentialsMySQL의 이전 암호가 마지막으로 이 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을 동일하게 만드는 것입니다.잘 부탁드립니다!

나는 이것에 대해 코더월에 게시했지만, 나는 원래 게시물의 댓글에서 데니스잭의 개선점을 재현할 것입니다.

mysql_user 모듈이 파일을 찾으면 ~/.my.cnf 파일을 로드한다는 것을 아는 것이 이를 잠재적으로 수행하는 비결입니다.

먼저 암호를 변경한 다음 암호 자격 증명이 있는 .my.cnf 파일을 복사합니다.두 번째 실행을 시도하면 myqsl_user answible 모듈이 .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 : 댓글에 Dhanjay Nene님의 추천으로 권한을 추가하였으며, 변수 보간법을 달러 기호 대신 중괄호를 사용하도록 변경하였습니다.

안전한 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 }}

레퍼런스

이것은 @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 암호를 변경하지 않으면 평소와 같이 명령줄에서 건전한 플레이북을 실행합니다.

mysql 암호를 변경할 때 명령줄에 다음을 추가합니다.명령줄에 이 매개 변수를 지정하면 명령줄에 설정된 매개 변수가 vars 파일에서 기본값으로 설정된 매개 변수보다 우선할 수 있습니다.

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

명령을 실행한 후 다음과 같이 vars 파일을 변경합니다.

current_password=buzzz
new_password={{ current_password }}

이전 답변에 추가하여 명령을 실행하기 전에 수동 단계를 수행할 필요가 없었습니다. 즉, 새 서버를 스핀업하여 루트 암호를 처음에 수동으로 변경하지 않고 플레이북만 실행하려고 합니다.루트 암호가 null인 경우 {{mysql_password }}이(가) 처음 작동하지 않을 것으로 생각합니다. 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

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

음, 이것은 좀 복잡했습니다.저는 하루 종일 이 문제에 몰두했고 아래 나열된 해결책을 생각해냈습니다.핵심은 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.

빈 암호 또는 null 암호에 대한 문제는 큰 놀라움이었습니다.

역할:

---

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

다음 항목이 작동합니다(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"

타당한 플레이북을 사용하여 mysql 설치에 사용해 보십시오.

호스트: slave.server.com 가 됨: 예

작업:

  • 이름: "Repo 설치 중" 셸: sudo rpm -Uvh http://dev.mysql.com/get/mysql57-community-release-el6-7.noarch.rpm ignore_filename: 예

  • 이름: "MySQL 5.7 설치" 패키지: name=syslog-community-server state=present

  • name: MySQL 서비스 시작: name: mysqld state: started enabled: yes

  • shell: "": "MySQL": "echo"grep 'temporary.*root@localhost' /var/log/mysqld.log | sed 's/.*root@localhost: //' 파일: mysql_root_pass

이것이 오래된 질문이라는 것을 알지만, 저는 그것을 찾고 있는 사람들을 위해 제 작업 전략을 공유합니다.

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

루트 암호를 설정하기 전에 mysql 서버를 시작/재시작하는 것이 중요합니다.또한, 저는 이 게시물[날짜]까지 게시된 모든 것을 시도했고 합격하는 것이 필수적이라는 것을 알게 되었습니다.login_password그리고.login_user.

(예:) 의 모든 Playsmysql_user user:root그리고.password= {{ SOMEPASSWORD }}다음을 사용하여 연결해야 합니다.login_password그리고.login_user다음 플레이를 위해.

참고: 아래는 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'

저는 다양한 접근법(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 }}

우리는 이 문제에 많은 시간을 보냈습니다.MySQL 5.7 이상의 경우 단순히 루트 계정을 무시하고 일반 MySQL 사용자에게 권한을 설정하는 것이 더 쉽다는 결론을 내렸습니다.

이유들

  1. 루트 암호 설정이 어렵습니다.
  2. unix_socketauth 플러그인이 표준 auth 플러그인과 충돌합니다.
  3. 사용하지 않도록 설정한 후 루트 암호를 안정적으로 변경unix_socket플러그인이 거의 불가능합니다.
  4. Ansible은 루트 암호를 한 번에 원자적으로 변경하는 데 적합하지 않습니다.
  5. 일반 계정을 광범위하게 사용하는 것이 효과적입니다.

만약 당신이 동일한 능력을 포기한다면, 당신은 그것을 잘 작동시킬 수 있습니다.그러나 타당한 가치 제안은 동일성이 가능하다는 것이기 때문에, 우리는 개발자들이 잘못된 가정으로 시간을 낭비한다는 것을 발견했습니다.

해킹 옵션이 존재하는 것만으로도check_implicit_admin결정론적인 MySQL 설정이 그렇게 쉽지 않다는 것을 암시하기 시작합니다.만약 그것이 실제로 결정론적이라면, "체크"는 없어야 하고, "실행"만 있어야 합니다.

저는 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

템플릿/root-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

defaults/main.yml

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

defaults/secret.yml

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

이 플레이북을 두 번 실행한 후에는 현재 암호(current_passwd_mysql_root)와 설정할 루트 암호(passwd_mysql_root)로 이 secret.yml 파일을 업데이트해야 합니다.

언급URL : https://stackoverflow.com/questions/16444306/ansible-idempotent-mysql-installation-playbook

반응형