본문 바로가기

DevOps/Ansible

[A101] 6. Ansible 시스템 구축 및 환경 설정 자동화

cloudNet@ 팀의 가시다 님이 진행하는 앤서블 스터디 3주차 정리입니다.

 

 

이번 포스팅에서는 Ansible을 통해 시스템 구축 및 환경 설정 자동화 해보겠습니다.

 

✅  사용자 계정 생성하기

  • 계정 생성 하는 모듈 - user.module
  • 사용자 계정과 패스워드는 Vault를 이용해 암호화 처리 한다.

 

1. 플레이북 설계를 위해서 디렉토리를 어떻게 구분할 것인지 정해야 합니다.

 

2. 플레이북 개발 

  • 프로젝트 디렉터리 생성 및 ansible.cfg, inventory 파일 작성
#
mkdir ~/my-ansible/chapter_09.1
cd ~/my-ansible/chapter_09.1

# ansible.cfg, inventory 파일 작성
cat <<EOT > ansible.cfg
[defaults]
inventory = ./inventory
remote_user = ubuntu
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false
EOT

cat <<EOT> inventory
tnode1
tnode2
tnode3
EOT
  • 사용자 계정 정보가 정의된 변수 파일을 생성
  • vault 사용법 확인
# vault 암호는 편하게 입력
ansible-vault create vars/secret.yml
New Vault password: qwe123
Confirm New Vault password: qwe123

## 에디터 창으로 전환 : (아래 내용 복붙) user_info 변수에 userid와 userpw가 같이 있는 사전형 변수를 정의
---

user_info:
  - userid: "ansible"
    userpw: "ansiblePw1"
  - userid: "stack"
    userpw: "stackPw1"
~
~
:wq

# 변수 파일 확인
ls -l vars/secret.yml
cat vars/secret.yml

  • 사용자 계정을 생성하는 플레이북을 작성 : 모든 호스트에 동일하게 생성하며 vault로 작성된 변수 파일을 읽어 사용함
  • ~/my-ansible/chapter_09.1/create_user.yml
---

- hosts: all

  # vault로 사용자 계정 관련 변수가 정의된 파일을 임포트하여 사용
  vars_files:
    - vars/secret.yml

  tasks:
  # loop 문을 사용하여 user_info의 userid와 userpw 사용
  - name: Create user
    ansible.builtin.user:
      name: "{{ item.userid }}"
      password: "{{ item.userpw | password_hash('sha512', 'mysecret') }}"
      state: present
      shell: /bin/bash
    loop: "{{ user_info }}"
  • playbook 실행
#
ansible-playbook --ask-vault-pass create_user.yml
Vault password: qwe123

PLAY [all] **************************************************************************************************************************************************

TASK [Gathering Facts] **************************************************************************************************************************************
ok: [tnode2-ubuntu.local]
ok: [tnode3-ubuntu.local]
ok: [tnode1-ubuntu.local]

TASK [Create user] ******************************************************************************************************************************************
[DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated. The Python crypt module is deprecated and will be removed from Python 3.13. 
Install the passlib library for continued encryption functionality. This feature will be removed in version 2.17. Deprecation warnings can be disabled by 
setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated. The Python crypt module is deprecated and will be removed from Python 3.13. 
Install the passlib library for continued encryption functionality. This feature will be removed in version 2.17. Deprecation warnings can be disabled by 
setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated. The Python crypt module is deprecated and will be removed from Python 3.13. 
Install the passlib library for continued encryption functionality. This feature will be removed in version 2.17. Deprecation warnings can be disabled by 
setting deprecation_warnings=False in ansible.cfg.
changed: [tnode2-ubuntu.local] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
changed: [tnode1-ubuntu.local] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
changed: [tnode3-ubuntu.local] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
changed: [tnode1-ubuntu.local] => (item={'userid': 'stack', 'userpw': 'stackPw1'})
changed: [tnode2-ubuntu.local] => (item={'userid': 'stack', 'userpw': 'stackPw1'})
changed: [tnode3-ubuntu.local] => (item={'userid': 'stack', 'userpw': 'stackPw1'})

PLAY RECAP **************************************************************************************************************************************************
tnode1-ubuntu.local        : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode2-ubuntu.local        : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode3-ubuntu.local        : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

 

✅  사용자 계정 생성하기 - 도전과제 1

Ansible-vault 사용 시 AWS SecretManager 활용해보기

  • 암호 생성 (Pass!word)
  • create user_secret.yml 생성하기
  • ~/my-ansible/chapter_09.1/vars 밑에 json 파일생성
cat  << EOF > secret.json
{
    "password": "Password123"
}
EOF
  • Secret 매니저 
aws secretsmanager create-secret --name  secret-file2 --secret-string file://secret.json --region ap-northeast-2

### 생성되는 arn 확인
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-2:466593096201:secret:secret-file-7mClMY",
    "Name": "secret-file",
    "VersionId": "0eea3598-4a4d-4526-b1fd-82c4cf115760"
}

aws secretsmanager list-secrets --region ap-northeast-2 | jq -r .SecretList[].Name

--> secret-file

  • 저장된 secret을 사용하려면?
aws secretsmanager get-secret-value --secret-id secret-file --region ap-northeast-2 | 
jq -r '.SecretString | fromjson | .password'

# Password123
cat > get-secrets.sh << EOF
#!/bin/bash
aws secretsmanager get-secret-value --secret-id secret-file --region ap-northeast-2 | jq -r '.SecretString | fromjson | .password'
EOF

chmod +x get-secrets.sh
ansible-playbook  create-user.yaml --vault-password-file ./get-secret.sh


ubuntu@server:~/my-ansible/chapter_09.1$ ansible-playbook create-user.yml --vault-password-file ./get-secret.sh

PLAY [all] **********************************************************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************************************
ok: [tnode1]
ok: [tnode3]
ok: [tnode2]

TASK [Create user] **************************************************************************************************************************************************************************************************
[DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated. The Python crypt module is deprecated and will be removed from Python 3.13. Install the passlib library for continued encryption 
functionality. This feature will be removed in version 2.17. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated. The Python crypt module is deprecated and will be removed from Python 3.13. Install the passlib library for continued encryption 
functionality. This feature will be removed in version 2.17. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[DEPRECATION WARNING]: Encryption using the Python crypt module is deprecated. The Python crypt module is deprecated and will be removed from Python 3.13. Install the passlib library for continued encryption 
functionality. This feature will be removed in version 2.17. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
ok: [tnode2] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
ok: [tnode1] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
ok: [tnode3] => (item={'userid': 'ansible', 'userpw': 'ansiblePw1'})
ok: [tnode1] => (item={'userid': 'stack', 'userpw': 'stackPw1'})
ok: [tnode3] => (item={'userid': 'stack', 'userpw': 'stackPw1'})
ok: [tnode2] => (item={'userid': 'stack', 'userpw': 'stackPw1'})

PLAY RECAP **********************************************************************************************************************************************************************************************************
tnode1                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode2                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode3                     : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

 

✅  SSH 키 생성 및 복사하기

  • 사용자 아이디는 외부 변수로 받는다.
  • ansible-server에서 ansible 계정을 만들고 SSH 키를 생성한다. (
  • ansible-server에 생성된 SSH 공개 키를 각 tnode에 복사한다.
  • 계정을 생성할 때는 user 모듈을, SSH 공개 키를 복사할 때는 posix.authorized_key 모듈을 이용한다.

 

1. 플레이북 설계를 위해서 디렉토리를 어떻게 구분할 것인지 정해야 합니다.

  • 해당 플레이북명은 create_sshkey.yml 로 설정하고, ‘Create ssh key’ 태스크와 ‘Copy SSH Pub Key’ 라는 2개의 태스크를 갖습니다.
  • ‘Create ssh key’ 태스크는 localhost에서 실행하고, ‘Copy SSH Pub Key’ 태스크는 tnode에서 실행합니다.

 

 

2. 플레이북 개발 

  • 프로젝트 디렉터리 생성 및 inventory 파일 작성
#
mkdir ~/my-ansible/chapter_09.2
cd ~/my-ansible/chapter_09.2

# ansible.cfg, inventory 파일 작성
cp ~/my-ansible/ansible.cfg ./

# inventory 파일 수정
cat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT

# lookup 플러그인
ansible-doc -l -t lookup

 

  • SSH 키 생성하고 복사하는 플레이북을 작성 : 여기서는 태스크가 실행될 호스트별로 태스크 작성
    • localhost인 ansible-server에서 생성된 SSH 공개 키는 ansible.posix.authorized_key 모듈을 이용하여 인벤토리의 tnode 호스트 그룹의 각 서버로 복사됩니다.
    • 이때 키를 등록하기 위해 lookup 함수가 사용 : 외부 소스(파일, DB, key/value stores, APIs 등)으로 부터 데이터를 검색

~/my-ansible/chapter_09.2/create_sshkey.yml

---

- hosts: localhost
  tasks:
  - name : Create ssh key
    ansible.builtin.user:
      name: "{{ userid }}"
      generate_ssh_key: true
      ssh_key_bits: 2048
      ssh_key_file: /home/{{ userid }}/.ssh/id_rsa
      shell: /bin/bash

- hosts: tnode 
  tasks:
  - name: Copy SSH Pub key
    ansible.posix.authorized_key:
      user: "{{ userid }}"
      state: present
      key: "{{ lookup('file', '/home/{{ userid }}/.ssh/id_rsa.pub') }}"
  • 플레이북 실행
# 실행 : 외부 변수(-e)로 userid 정의하고 전달 실행 >> 실패!
ansible-playbook -e userid=ansible create_sshkey.yml
TASK [Copy SSH Pub key] ********************************************************************************
fatal: [tnode1]: FAILED! => {"msg": "The 'file' lookup had an issue accessing the file '/home/ansible/.ssh/id_rsa.pub'. file not found, use -vvvvv to see paths searched"}
fatal: [tnode2]: FAILED! => {"msg": "The 'file' lookup had an issue accessing the file '/home/ansible/.ssh/id_rsa.pub'. file not found, use -vvvvv to see paths searched"}
fatal: [tnode3]: FAILED! => {"msg": "The 'file' lookup had an issue accessing the file '/home/ansible/.ssh/id_rsa.pub'. file not found, use -vvvvv to see paths searched"}

PLAY RECAP *********************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode1                     : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
tnode2                     : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
tnode3                     : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   


# 현재 ubuntu 권한으로 실행할 경우 실패함
ls -l /home/ansible/.ssh/id_rsa.pub
sudo ls -l /home/ansible/.ssh/id_rsa.pub
  • ansible.cfg수정
[defaults]
inventory = ./inventory
remote_user = root
inject_facts_as_vars = false
  • 실행
sudo ansible-playbook -e userid=ansible create_sshkey.yml --ask-pass
SSH password: qwe123
...

TASK [Copy SSH Pub key] ********************************************************************************
changed: [tnode2]
changed: [tnode1]
changed: [tnode3]

PLAY RECAP *********************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode1                     : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode2                     : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
tnode3                     : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

 

 

 

✅  도전과제  3번 

 Ubuntu 와 CentOS에 mariadb 를 yum또는 apt를 사용하지 않고 설치하는 playbook을 작성해서 실습 

  • directory 구조 짜기
  • mariadb를 설치하는 방법 확인 
  • playbook 개발
  • 실행 후 테스트

 

1. 플레이북 설계를 위해서 디렉토리를 어떻게 구분할 것인지 정해야 합니다.

 

✔️  먼저 스터디 시간에 학습한 chrony를 설치하는 방법과 설계 구조를 유사하게 가져간다

 

 

 

2. 플레이북 설계를 위해서 디렉토리를 어떻게 구분할 것인지 정해야 합니다.

 

대략적인 설계가 끝나면 롤에 대한 상세 설계를 합니다. 마리아 디비 서비스 설치를 위한 롤에서는 변수를 정의하는 vars, 환경 설정 템플릿을 위한 templates, 태스크를 정의한 tasks, 환경 설정 후 마리아 디비 서비스를 재시작하기 위한 handlers를 사용합니다.

 

2-1. 가장 최상단 부터 플레이북(site.yml)을 작성한다.

  • 해당 부분에서는 roles을 명시해준다.
  • common에서는 공통적인 OS 세팅 또는 최적화 작업을 하는 playbook을 구성할것이고, mariadb에서는 마리아디비 설치관련한 task와 templates,vars 그리고, 핸들러 등을 구성할것이다.
---
- name: Install Mariadb
  hosts: localhost
  remote_user: root
  # remote_user: user
  # become: yes
  # become_method: sudo

  roles:
    - common
    - mariadb

 

2-2. mariadb 설치 관련한 role 

 이부분도 main > define-OS > install-{{distribution}} 순서대로 작업이 진행되게 구성 해야 한다.

2-3. handler

---
# Handler to handle DB tier notifications

- name: restart mariadb
  service: name=mariadb state=restarted

 

2-4. templates

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


# Disabling symbolic-links is recommended to prevent assorted security risks

symbolic-links=0
port={{ mysql_port }}

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file={{ __pidfile }}

 

2-5. vars

__src_tarfile: /opt/apps/mariadb-10.3.20.tar.gz
__dest_tarfile: /opt/


__mariadb_home: /usr/local/mysql
mysql_port: 13306

 

 

 

이렇게, 과거에 온프레미스 금융권 앤서블 프로젝트를 수행할때 작성했던 앤서블 코드를 리팩토링 해보면서 

다시 한번 앤서블 플레이북을 설계 하는 방법에 대해서 고민해볼 수 있는 시간을 가졌다.