Ansible이란?
리눅스에서 환경을 구성하기 위해 사용하는 방법은 무엇이 있을까? 도커나 가상환경 등 여러 방법이 있지만, 기초적으로 쉘 스크립트를 기반으로 환경을 구성하게 된다. 하지만 이러한 방법으로는 다수의 서버에 동시에 동일한 환경을 배포해야 하는 상황에서는 쉽지 않다는 것이다.
이를 위해 고안된 것이 바로 IaC(Infrastructure as Code)이다. IaC는 환경의 배포와 구성을 규격화된 코드로 정의해 사용하는 것을 의미한다. 이 개념을 기반으로 하는 환경 자동화 도구의 대표적인 예시가 바로 Ansible이다.
이전의 방법론과 다르게 Ansible은 Agent가 필요하지 않는다는 장점이 있다. 즉, 따로 노드에 Ansible로 명령하거나 관리하기 위해 별도의 설치 없이 사용할 수 있다. (물론, Ansible 접속을 위한 간단한 설정은 진행해야한다.)
Ansible Architecture
우선 가장 중요한 개념부터 살펴보자.
- Host : 관리하는 대상
- Inventory : 호스트의 목록 또는 그룹이 지정되어 있는 파일
- Playbook : 작업에 필요한 명령어를 스크립트로 만들어 둔 파일
그 밖에도 편의성과 다양한 목적을 위해 이미 배포되어 있는 다양한 플러그인과 모듈을 사용할 수 있다. 필요에 따라서는 직접 제작하여, 사용하는 것도 가능하다.
Ansible Setting
Make Connection to Host
관리자가 사용할 PC에 Ansible을 설치한다. 별도로 Host에는 Ansible을 설치하지 않아도 되지만, 편의성을 위해 약간의 설정은 필요하다.
1. Ansible 설치
다음 명령어를 실행하여, ansible을 설치한다.
$ deb <http://ppa.launchpad.net/ansible/ansible/ubuntu> trusty main
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
$ sudo apt update
$ sudo apt install ansible
설치에 성공하면, 다음 명령어를 실행하여 버전 확인.
$ ansible --version
ansible [core 2.15.6]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/username/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/username/miniconda3/lib/python3.11/site-packages/ansible
ansible collection location = /home/username/.ansible/collections:/usr/share/ansible/collections
executable location = /home/username/miniconda3/bin/ansible
python version = 3.11.5 (main, Sep 11 2023, 13:54:46) [GCC 11.2.0] (/home/username/miniconda3/bin/python)
jinja version = 3.1.2
libyaml = True
2. 접속 관련 권한 설정
visudo 설정 (호스트별로)
$ sudo su
$ visudo
~~
username ALL=(ALL) NOPASSWD:ALL # 사용할 ID를 통일해서 만들어야 한다
host의 ip 주소와 hostname 설정
$ sudo vi /etc/hosts
~~
192.168.10.201 ansible-1 # 서버 환경에 따라서 수정 및 추가
192.168.10.202 ansible-2
ssh 설정
$ ssh-keygen
$ ssh-copy-id ansible-1 # 앞서 지정한 hostname
$ ssh-copy-id ansible-2
방화벽 설정 해제 (호스트별로)
$ sudo ufw disable
3. Inventory 파일 작성
/etc/ansible/hosts
localhost ### ungrouped host
[ansible_test_server] ## host group name
ansible-1 ### host name
ansible-2 ### host name
ansible-3 ansible_host=192.168.10.203 ### host name
기존 파일 하단에 추가하거나 혹은 새롭게 작성해도 무관하다. 호스트를 미리 설정 해두었다면, 호스트명으로도 설정 가능하다. 그 외의 경우 직접 주소를 입력할 수 도 있다.
호스트가 잘 연결되었는지 확인하기 위해서, 다음 명령어를 실행한다.
$ ansible all -m ping
ansible-1 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
localhost | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ansible-2 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
ansible-3 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
추가적으로 그룹을 확인하기 위해서, 다음 명령어를 실행한다.
$ ansible-inventory --graph
@all:
|--@ungrouped:
| |--localhost
|--@ansible_test_server:
| |--ansible-1
| |--ansible-2
| |--ansible-3
Task Deploy to Hosts
Git에 배포한 코드를 각 호스트에 배포하고 필요한 작업을 수행하는 시나리오를 생각하고 직접 구현해보자. 우선은 간단하게 Docker 보다는 Conda 가상환경을 이용하여, 환경을 구성한다고 가정한다.
Ansible Playbook의 특징 두가지는 무상태(Stateless)와 멱등성(Idempotency)으로 각 실행이 독립적으로 실행되며, 여러 번 실행해도 동일한 결과가 유지되는 것을 의미한다. 이러한 특성을 고려하여 작성에 유의해야한다. 이와 관련해서, Conda 가상환경을 사용하기 위해서는 링크를 참고하자.
1. Playbook 파일 작성
Ansible Playbook은 YAML 형식으로 작성된다. 실행될 Host를 정의하고 수행할 작업을 명시한다. Ansible은 non-interactive shell에서 실행되며, 기존 사용하던 환경변수를 사용할 수 없다.
추가적으로 source ~/.bashrc 를 사용하는 방법은 불가능하다..bashrc 내부에, non-interactive 실행을 방지하기 위한 조건문이 포함되어 있다.
Example)
- name: Ansible Playbook Sample
hosts: ansible_test_server # 작업을 수행할 Host 정의
vars: # 필요한 변수 정의
work_dir: /home/username/Desktop/ansible_sample
git_user_id: user name
git_user_pwd: password
tasks: # 수행할 작업 명시
- name: Clone git repository
git:
repo: http://{{ git_user_id }}:{{ git_user_pwd }}@git_repository_url
dest: "{{ work_dir }}" # work_dir 경로에 별도의 폴더 없이 복사된다
- name: Create a Conda environment
shell: /home/username/miniconda3/bin/conda create -n test python=3.10 -y
args:
executable: /bin/bash
다음과 같이 환경변수를 사용할 수 없기 때문에, 직접 경로를 명시하여 사용해야한다. 다만, 호스트 측에서 miniconda와 anaconda 같이 환경변수는 동일하지만 혼용되는 경우 구분이 필요하다. 이 경우에는 다음과 같은 방법으로 진행할 수 있다.
- name: Ansible Playbook Sample
hosts: ansible_test_server # 작업을 수행할 Host 정의
vars: # 필요한 변수 정의
work_dir: /home/username/Desktop/ansible_sample
git_user_id: user name
git_user_pwd: password
conda_paths:
ansible-1: "anaconda3"
ansible-2: "miniconda3"
ansible-3: "anaconda3"
tasks: # 수행할 작업 명시
- name: Clone git repository
git:
repo: http://{{ git_user_id }}:{{ git_user_pwd }}@git_repository_url
dest: "{{ work_dir }}" # work_dir 경로에 별도의 폴더 없이 복사된다
- name: Create a conda environment
shell: /home/username/{{ conda_paths[inventory_hostname] }}/bin/conda create --name ansible_test python=3.10 -y
args:
executable: /bin/bash
2. 작업 실행
작성한 .yaml 파일에는 이미 실행할 작업과 호스트가 명시되어있다. ansible-playbook sample.yaml 로 실행이 가능하다. 코드를 실행하면, 다음과 같이 진행 상황을 정리하여 보여준다.
PLAY [Ansible Playbook Sample] ********************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************
ok: [ansible-1]
ok: [ansible-2]
ok: [ansible-3]
TASK [Clone git repository] ***********************************************************************************************************
changed: [ansible-3]
changed: [ansible-1]
changed: [ansible-2]
TASK [Create a conda environment] *****************************************************************************************************
changed: [ansible-3]
changed: [ansible-1]
changed: [ansible-2]
PLAY RECAP ****************************************************************************************************************************
ansible-1 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-2 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-3 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
작업이 완료될 때까지 기다려야하기 때문에 작업시간이 긴 경우에는 비동기적으로 실행하면 편리하다.
작업시간이 긴 작업을 실행하기 위한 명령어
Async
- async: 이 파라미터는 작업이 완료되기를 기다리는 최대 시간(초 단위)을 지정합니다. 이 시간이 지나면 작업이 종료됩니다.
- 예를 들어, 어떤 작업이 30분(1800초) 이상 걸릴 것으로 예상되면, async: 1800과 같이 설정할 수 있습니다.
Poll
- poll: 이 파라미터는 Ansible이 작업의 상태를 얼마나 자주 확인할지를 결정합니다. 기본값은 10초마다 한 번씩 상태를 확인하는 것입니다.
- poll: 0으로 설정하면, Ansible은 작업을 시작한 후 즉시 다음 작업으로 넘어갑니다. 이 경우 나중에 async_status 모듈을 사용하여 작업 상태를 수동으로 확인해야 합니다.
- 반면에 poll: 15와 같이 설정하면, Ansible은 15초마다 작업 상태를 확인합니다.
예시이 예시에서, long_running_script.sh는 최대 1시간 동안 실행될 수 있으며, Ansible은 이 작업을 시작한 후 바로 다음 작업으로 넘어갑니다. 작업의 상태는 나중에 수동으로 확인할 수 있습니다.
- name: Check status of the long running task
async_status:
jid: "{{ ansible_job_id }}"
register: job_result
until: job_result.finished
retries: 30
delay: 60 # Check every minute
이 코드는 비동기 작업의 상태를 확인합니다. async_status 모듈은 ansible_job_id를 사용하여 특정 작업의 상태를 확인하며, 여기서는 매 분마다 상태를 확인합니다(delay: 60).
- async와 poll을 사용하면 시간이 오래 걸리는 작업을 효율적으로 관리할 수 있으며, 플레이북의 나머지 부분이 불필요하게 지연되지 않도록 할 수 있습니다.
- 비동기 작업의 상태 확인:
- - name: Long running task example command: /path/to/long_running_script.sh async: 3600 # 1 hour poll: 0 # Fire and forget
- Ansible에서 async와 poll은 장시간 실행되는 작업을 관리하는 데 사용됩니다. 이들은 작업을 비동기적으로 실행하고 완료 상태를 확인하는 데 도움이 됩니다. 이를 통해 다른 작업들이 오래 실행되는 작업에 의해 차단되지 않도록 할 수 있습니다.
3. 멱등성 검사
실행했던 .yaml 파일은 다시 실행해도 항상 동일한 결과가 나와야한다. 실제로 다시 실행해보면 다음과 같은 결과를 확인할 수 있다.
PLAY [Ansible Playbook Sample] ********************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************************
ok: [ansible-1]
ok: [ansible-2]
ok: [ansible-3]
TASK [Clone git repository] ***********************************************************************************************************
ok: [ansible-3]
ok: [ansible-1]
ok: [ansible-2]
TASK [Create a conda environment] *****************************************************************************************************
changed: [ansible-3]
changed: [ansible-1]
changed: [ansible-2]
PLAY RECAP ****************************************************************************************************************************
ansible-1 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-2 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ansible-3 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
다시 실행했지만, ‘changed’ 상태가 확인된다. 위에서 작성한 예시에 문제가 있다는 뜻이다. 확인해보니, conda 가상환경의 이름이 중복되면 지우고 다시 설치가 진행되는 것을 확인했다. 이를 다음과 같이 수정할 수 있다.
- name: Ansible Playbook Sample
hosts: ansible_test_server # 작업을 수행할 Host 정의
vars: # 필요한 변수 정의
work_dir: /home/username/Desktop/ansible_sample
git_user_id: user name
git_user_pwd: password
conda_paths:
ansible-1: "anaconda3"
ansible-2: "miniconda3"
ansible-3: "anaconda3"
conda_env_name: ansible_test
tasks: # 수행할 작업 명시
- name: Clone git repository
git:
repo: http://{{ git_user_id }}:{{ git_user_pwd }}@git_repository_url
dest: "{{ work_dir }}" # work_dir 경로에 별도의 폴더 없이 복사된다
- name: Check if conda environment exists
shell: /home/username/{{ conda_paths[inventory_hostname] }}/bin/conda info --envs | grep '{{ conda_env_name }}'
register: conda_env_exists
ignore_errors: yes
changed_when: false
- name: Create a conda environment
shell: /home/username/{{ conda_paths[inventory_hostname] }}/bin/conda create --name {{ conda_env_name }} python=3.10 -y
when: conda_env_exists.rc != 0
args:
executable: /bin/bash
이제 다시 실행해도 항상 동일한 결과를 확인할 수 있다.
'Study > MLOps' 카테고리의 다른 글
AWS Container Support Service (2/3) (0) | 2024.03.14 |
---|---|
AWS Container Support Service (1/3) (0) | 2024.03.07 |
Slurm과 Kubernetes 정답은? (0) | 2023.12.06 |
도대체 MLOps는 무엇일까? (1) | 2023.11.21 |
pydantic을 사용하여, 안정성 높이기 (0) | 2023.01.26 |