Ansible로 여러대의 인스턴스를 동일한 상태로 유지하기

d3fau1t·2022년 7월 29일
0

뻘짓

목록 보기
6/9

Ansible을 써야하는 이유

최근 Redis 클러스터를 구축하는 작업을 하면서 클라우드 환경에 생성된 여러대의 VM에 패키지를 설치하거나 환경을 동일하게 만들어줘야하는 상황을 마주하게되었습니다.

VM: Virtual Machine

만약 이 상황에서 AWS 사용자였다면..
한 인스턴스에 모든 환경을 구축하고 해당 인스턴스의 스냅샷 이미지를 생성해서 AMI로 여러대를 생성하는 방법을 떠올릴 수 있을겁니다.

하지만 그 이후에는요..?
업데이트를 반영하거나 변경사항이 생겨서 다른 인스턴스에 반영해야하는 상황이 생기면 생각보다 많은 피로감을 동반하는 작업이 될 수 있을 것 같다는 생각이 들었습니다.

심지어 이는 사람이 직접 수행하는 작업이다보니 휴먼에러가 동반될 수 있고, 각각의 작업결과가 다르게 나올 가능성이 항상 열릴 수 밖에 없습니다.

위와 같은 이유로 여러대의 인스턴스를 동일하게 제어할 수 있는 Ansible을 사용하기로 했습니다.

TMI

Ansible이 제공하는 대부분의 모듈들은 멱등성을 보장하는데, 이로인해 매번 같은 작업을 해도 같은 결과가 보장된다는 특성을 가집니다.

예를들면 파일이나 디렉터리를 생성 혹은 제거하는 경우 create, remove 등의 모듈을 실행하는데, when 조건절을 이용할 수 있습니다.

상태 변경시 changed_when: 혹은 failed_when 등.. 조건절을 붙여서 그 다음에 올 작업들을 수행시킬 수 있습니다.


이 문서는 Ansible을 사용하여 CentOS 7 환경에 6대의 VM에 Redis를 동일하게 설치하는 과정을 다룹니다.

설치

  • via PIP
pip install ansible
  • via Brew on MacOS
brew install ansible

위 두 방법중 선택하여 설치하면 됩니다만..
굳이 전역으로 설치할 이유는 없어보이니 python 가상환경을 생성하고 설치하는 것도 괜찮습니다.

사용환경 설정

개요

접근하려는 VM이 1대 있고, 해당 VM을 제어하려는 Host가 있다고 가정하였을 때,
해야할 작업은 아래와 같습니다.

  1. VM에 접근하기위한 앤서블 전용 사용자 생성 후 비밀번호 생성
  2. 앤서블 전용 사용자가 sudo 권한을 비밀번호 없이 사용할 수 있도록 설정
  3. Host에서 VM으로 패스워드 없이 접근할 수 있도록 SSH키 복사
  4. Host에서 인벤토리 파일 생성 후 핑테스트 진행

위의 과정이 무사히 완료되면 사용할 준비가 되었다고 판단할 수 있습니다.

관련 내용을 아래에 순서대로 작성합니다.

앤서블 전용 사용자 생성 후 비밀번호 설정

Host 환경

ssh d3fau1t@<VM IP>
d3fau1t@<VM IP>'s password:

VM 환경

# d3fau1t
sudo -s
Password:

# root
adduser ansible-user
passwd ansible-user
ansible-user 사용자의 비밀번호 변경중
새 암호:
새 암호 재입력:
passwd: 모든 인증 토큰이 성공적으로 업데이트 되었습니다.

앤서블 전용 사용자가 sudo 권한을 비밀번호 없이 사용할 수 있도록 설정

VM 환경

# root
visudo

...
## Allow root to run all commands anywhere
root            ALL=(ALL)       ALL
ansible-user    ALL=(ALL)       NOPASSWD:ALL
...

만약 이 작업을 하지 않는다면 Ansible 작업 수행시 아래와 같은 오류를 맞이할 수 있습니다.

atal: [crate-prd-001]: FAILED! => {"ansible_facts": {}, "changed": false, "failed_modules": {"ansible.legacy.setup": {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "failed": true, "module_stderr": "Shared connection to [VM IP] closed.\r\n","module_stdout": "sudo: 암호가 필요합니다\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}}, "msg": "The following modules failed to execute: ansible.legacy.setup\n"}

Host에서 VM으로 패스워드 없이 접근할 수 있도록 SSH 키 복사

Host 환경

ssh-copy-id ansible-user@<VM IP>


/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/Users/d3fau1t/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
ansible-user@<VM IP>'s password: 

Number of key(s) added:        1

Now try logging into the machine, with:   "ssh 'ansible-user@<VM IP>'"
and check to make sure that only the key(s) you wanted were added.

Host에서 인벤토리 파일 생성 후 핑테스트 진행

앤서블이 사용하는 인벤토리는 제어하려는 호스트 주소, 이름, 그룹 등의 정보를 담고 있어야 합니다.
아래의 예시와 같이 파일을 작성합니다.

인벤토리 파일 생성

hosts_group.ini

redis-001 ansible_host=<VM IP> ansible_user=ansible-user

[redis_cluster]
redis-001

핑테스트

ansible -m ping -i hosts_group.ini redis_cluster

Ansible Playbook 작성

핑테스트까지 무사히 마쳤다면, 이젠 Ansible로 VM 인스턴스를 제어할 수 있습니다.
제어하기 위해 Playbook 파일을 작성해야하고, 이는 yaml 형식으로 작성합니다.
보통 Playbook 단독으로 사용되지 않고 제어할 인스턴스 정보를 기록한 인벤토리와 함께 사용됩니다.

인벤토리 (작업 위치) + 플레이북 (해야할 작업)
위와 같은 조합으로 사용합니다.

여섯대의 인스턴스에 동일한 작업을 수행하기위해 VM 인스턴스를 추가하고, 기존에 있던 hosts_group.ini 파일을 수정했습니다.

redis-001 ansible_host=<VM IP> ansible_user=ansible-user
redis-002 ansible_host=<VM IP> ansible_user=ansible-user
redis-003 ansible_host=<VM IP> ansible_user=ansible-user
redis-004 ansible_host=<VM IP> ansible_user=ansible-user
redis-005 ansible_host=<VM IP> ansible_user=ansible-user
redis-006 ansible_host=<VM IP> ansible_user=ansible-user

[redis_cluster]
redis-001
redis-002
redis-003
redis-004
redis-005
redis-006

핑테스트도 한번 해줍니다.

ansible -m ping -i ./ansible/hosts_group.ini redis_cluster
redis-001 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
redis-002 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
redis-003 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
redis-004 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
redis-005 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
redis-006 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

redis.yaml 파일을 하나 생성합니다. 인벤토리에서 각 인스턴스를 redis_cluster로 묶어주고 타겟 hosts로 사용합니다.

---
- name: install redis
  hosts: redis_cluster
  become: true
  tasks:
    - name: install EPEL Repository
      yum:
        name: epel-release
        state: installed
    - name: install Redis package
      yum:
        name: redis
        state: installed
    - name: start Redis
      ansible.builtin.shell: systemctl start redis
    - name: set Redis automatically enabled
      ansible.builtin.shell: systemctl enable redis
    - name: ping itself
      ansible.builtin.shell: redis-cli ping

예시를 간결하게 만들기 위해 위와 같이 작성했고
실제 필요한 부분이 있다면 작성해주셔야합니다.
위와 같이 실행하면 엄청 낮은 버전의 redis가 설치됩니다.

실행

ansible-playbook -i hosts_group.ini redis.yaml
profile
웹 백엔드 합니다.

0개의 댓글