IaC

Ansible 설치 및 실습

JAEJUNG 2021. 7. 23. 17:38

설치 환경 : Centos7

 

yum install ansible 할 때 no package ansible available 이라고 나오면

-> 기본적으로 갖고 있는 공간 내에선 ansible 설치 패키지가 들어있지 않음.

 

extra packages for enterprise linux

yum -y install epel-release
yum repolist

 

설치 다 하면 다시

yum -y install ansible

 

다른 node와 통신하기 위해 hosts 파일에 node의 ip 추가

[root@Ansible-Server ~]# tail -3 /etc/ansible/hosts
172.20.10.11
172.20.10.12
172.20.10.13

 

아래 명령어를 통해 public key 키 교환을 위해 yes를 입력

[root@Ansible-Server ~]# ansible all -m ping

 

Ansible 옵션

-i(--inventory-file) : 적용될 호스트에 대한 파일 지정

-m(--module-name) : 모듈 선택

-k(--ask-pass) : 패스워드를 물어보도록 설정

-K(--ask-become-pass) : 관리자로 권한 상승

--list-hosts : 적용되는 호스트 확인

[root@Ansible-Server ~]# ansible nginx -m ping --list-hosts
  hosts (3):
    172.20.10.11
    172.20.10.12
    172.20.10.13

#아래에서 사용하는 ping은 ICMP의 ping이 아닌 Python으로 짜여진 Module이다.(내부에서 호출)

 

그 다음부턴 -k 옵션을 통해 public key 교환을 skip하고 해당 node들과 통신이 가능

[root@Ansible-Server ~]# ansible all -m ping -k
SSH password:
172.20.10.13 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
172.20.10.12 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
172.20.10.11 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

 

별칭 사용

[]로 묶으면 아래 세 IP가 nginx라는 그룹으로 묶이게 된다.

[root@Ansible-Server ~]# tail -4 /etc/ansible/hosts 
[nginx]
172.20.10.11
172.20.10.12
172.20.10.13

 

기존 all을 통해 노드와의 통신을 확인하기 위한 방법 대신

[root@Ansible-Server ~]# ansible all -m ping -k
SSH password: 
172.20.10.13 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.20.10.12 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.20.10.11 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

 

nginx 별칭을 통한 통신 확인이 가능하다.

[root@Ansible-Server ~]# ansible nginx -m ping -k
SSH password: 
172.20.10.12 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.20.10.13 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.20.10.11 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

 

Ansible을 사용하면 각각의 계정에 접속하여 명령어를 입력하지 않아도 되는 편리함이 있고,

중앙에서 관리되어 빠른 일처리가 가능하다.

또한 명령어를 보게 되면 Readable 하기 때문에 어떤 작업인지 한 눈에  파악할 수 있다.수행하려는 작업에 맞는 Module을 설정하여 사용한다.

 

uptime

[root@Ansible-Server ~]# ansible all -i test -m shell -a "uptime" -k
SSH password: 
172.20.10.11 | CHANGED | rc=0 >>
 07:45:46 up 11 min,  2 users,  load average: 0.03, 0.04, 0.05

 

디스크 용량 확인

[root@Ansible-Server ~]# ansible all -i test -m shell -a "df -k" -k
SSH password: 
172.20.10.11 | CHANGED | rc=0 >>
Filesystem               1K-blocks    Used  Available Use% Mounted on
devtmpfs                    237348       0     237348   0% /dev
tmpfs                       249336       0     249336   0% /dev/shm
tmpfs                       249336    4664     244672   2% /run
tmpfs                       249336       0     249336   0% /sys/fs/cgroup
/dev/mapper/centos-root   52403200 1286600   51116600   3% /
/dev/mapper/centos-home 2090879492   33004 2090846488   1% /home
/dev/sda1                  1038336  140288     898048  14% /boot
tmpfs                        49868       0      49868   0% /run/user/0

 

메모리 상태 확인

[root@Ansible-Server ~]# ansible all -i test -m shell -a "free -h" -k
SSH password: 
172.20.10.11 | CHANGED | rc=0 >>
              total        used        free      shared  buff/cache   available
Mem:           486M        143M        243M        4.6M         99M        325M
Swap:          2.0G          0B        2.0G

 

사용자 유저 만들기

[root@Ansible-Server ~]# ansible all -i test -m user -a "name=Ansi password=toor" -k
SSH password: 
[WARNING]: The input password appears not to have been hashed. The 'password' argument must
be encrypted for this module to work properly.
172.20.10.11 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "comment": "", 
    "create_home": true, 
    "group": 1001, 
    "home": "/home/Ansi", 
    "name": "Ansi", 
    "password": "NOT_LOGGING_PASSWORD", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": false, 
    "uid": 1001
}
[root@node01 ~]# tail -1 /etc/passwd
Ansi:x:1001:1001::/home/Ansi:/bin/bash

 

파일 전송하기

- 특정 파일을 모든 서버에 빈번하게 전송해야 할 일이 생긴다.

[root@Ansible-Server ~]# ansible all -i test.file -m copy -a "src=./test.file dest=/tmp" -k
SSH password: 
172.20.10.11 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": true, 
    "checksum": "d036d694737d92a0c94c166b1f1a9f3dc82dec94", 
    "dest": "/tmp/test.file", 
    "gid": 0, 
    "group": "root", 
    "md5sum": "f1d0895613ff05cb7fb1af0aa7fe56e5", 
    "mode": "0644", 
    "owner": "root", 
    "secontext": "unconfined_u:object_r:admin_home_t:s0", 
    "size": 13, 
    "src": "/root/.ansible/tmp/ansible-tmp-1627041385.3-1810-235100174201201/source", 
    "state": "file", 
    "uid": 0
}
[root@Ansible-Server ~]# ssh root@172.20.10.11
root@172.20.10.11's password: 
Last login: Fri Jul 23 07:56:27 2021 from 172.20.10.10
[root@node01 ~]# cd /tmp/
[root@node01 tmp]# ls | grep test.file
test.file

 

서비스 설치

[root@Ansible-Server ~]# ansible guest -m yum -a "name=httpd state=present" -k
[root@node01 ~]# systemctl status httpd
httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:httpd(8)
           man:apachectl(8)
           
혹은

[root@Ansible-Server ~]# ansible guest -m shell -a "systemctl status httpd" -k
SSH password: 
172.20.10.11 | FAILED | rc=3 >>
httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
     Docs: man:httpd(8)
           man:apachectl(8)non-zero return code

 

Ansible PlayBook

사용자가 원하는 내용을 미리 정의해둔 것

ex) 대량의 서버에 웹 서비스를 설치하고 구동해야 할 땐?

 

1. 웹 서버용 Virtual or Real 서버에 NginX를 설치

(Apache는 점유율이 꾸준히 하락, NginX는 상승중)

2. 메인페이지의 파일 전송

3. 서비스 재시작

 

이 과정을 Ansible PlayBook에 넣어놓고 한 번에 실행 가능

이 PlayBook은 멱등성(idempotent)의 특성을 가지고 있다.

 

멱등성

연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질

예를 들어, 아래와 같이 /etc/ansible/hosts 파일에 특정 내용을 추가하는 명령어가 있다.

(-e 옵션은 뒤에 추가될 내용의 \n과 같은 문자들을 엔터로 해석한다.)

[root@Ansible-Server ~]# echo -e "[test]\n192.168.219.13" >> /etc/ansible/hosts

사용자는 ip 주소 192.168.219.13을 hosts 파일에 추가할 예정이며 같은 명령어를 여러 번 수행해도

hosts에는 해당 내용이 한번만 추가돼야 한다.

그러나 결과는 아래와 같이 계속해서 추가된다.

[root@Ansible-Server ~]# echo -e "[test]\n192.168.219.13" >> /etc/ansible/hosts 
[root@Ansible-Server ~]# echo -e "[test]\n192.168.219.13" >> /etc/ansible/hosts

[root@Ansible-Server ~]# tail -4 /etc/ansible/hosts
[test]
192.168.219.13
[test]
192.168.219.13

 

이러한 상황을 피하기 위해 Ansible PlayBook을 사용한다.

- host.yml

[root@Ansible-Server ~]# vi host.yml
---
- name: Ansible_vim
  hosts: localhost

  tasks:
    - name: Add ansible hosts
      blockinfile:
         path: /etc/ansible/hosts
         block: |
            [host]
            192.168.219.11

 

위와 같이 yaml 형식의 파일을 만들어준 후 아래 명령어를 실행한다.

[root@Ansible-Server ~]# ansible-playbook host.yml 

PLAY [Ansible_vim] **************************************************************

TASK [Gathering Facts] **********************************************************
ok: [localhost]

TASK [Add ansible hosts] ********************************************************
changed: [localhost]

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

 

이렇게 PlayBook으로 실행하면 멱등성의 성질에 따라 여러 번 수행해도 결과가 변하지 않는다.

[root@Ansible-Server ~]# tail -4 /etc/ansible/hosts 
# BEGIN ANSIBLE MANAGED BLOCK
[host]
192.168.219.11
# END ANSIBLE MANAGED BLOCK

 

PlayBook을 사용하여 3개의 노드에 nginx 설치 및 구동

 

먼저 yml 파일을 생성해준다.

- nginx.yml

[root@Ansible-Server ~]# cat nginx.yml 
---
- hosts: nginx
  remote_user: root
  tasks:
    - name: install epel-release
      yum: name=epel-release state=latest
    - name: install nginx web server
      yum: name=nginx state=present
    - name: Start nginx web server
      service: name=nginx state=started

 

ansible-playbook 명령어를 통해 nginx.yml을 실행한다.

[root@Ansible-Server ~]# ansible-playbook nginx.yml -k

아래와 유사한 에러가 발생한다면 -k 옵션을 제거하여 public key 교환을 수행한다.

Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.
[root@Ansible-Server ~]# ansible-playbook nginx.yml

PLAY [nginx] ******************************************************************************

TASK [Gathering Facts] ********************************************************************
The authenticity of host '192.168.219.12 (192.168.219.12)' can't be established.
ECDSA key fingerprint is SHA256:35/2/xEm8yMJiry/It0SW4iqnjZL28aSA8Uie7OgSl8.
ECDSA key fingerprint is MD5:d7:c7:a4:8f:86:d6:6b:11:ec:09:d3:9d:7a:34:ff:3e.
Are you sure you want to continue connecting (yes/no)? The authenticity of host '192.168.219.11 (192.168.219.11)' can't be established.
ECDSA key fingerprint is SHA256:35/2/xEm8yMJiry/It0SW4iqnjZL28aSA8Uie7OgSl8.
ECDSA key fingerprint is MD5:d7:c7:a4:8f:86:d6:6b:11:ec:09:d3:9d:7a:34:ff:3e.
Are you sure you want to continue connecting (yes/no)? The authenticity of host '192.168.219.13 (192.168.219.13)' can't be established.
ECDSA key fingerprint is SHA256:35/2/xEm8yMJiry/It0SW4iqnjZL28aSA8Uie7OgSl8.
ECDSA key fingerprint is MD5:d7:c7:a4:8f:86:d6:6b:11:ec:09:d3:9d:7a:34:ff:3e.
Are you sure you want to continue connecting (yes/no)? yes
fatal: [192.168.219.12]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.219.12' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).", "unreachable": true}
yes
fatal: [192.168.219.11]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: Warning: Permanently added '192.168.219.11' (ECDSA) to the list of known hosts.\r\nPermission denied (publickey,gssapi-keyex,gssapi-with-mic,password).", "unreachable": true}
yes

 

아래와 같이 실행된 결과를 확인한다.

(key 교환 이슈가 없었다면 changed=3이 표시되면 정상이다.)

PLAY RECAP ********************************************************************************
192.168.219.11             : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.219.12             : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.219.13             : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

shell Module을 통해 세 개의 node 모두 nginx가 구동 중인 것을 확인할 수 있다.

[root@Ansible-Server ~]# ansible nginx -m shell -a "systemctl status nginx" -k
SSH password: 
192.168.219.13 | CHANGED | rc=0 >>
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2021-07-23 14:27:33 EDT; 6min ago
...
...
192.168.219.11 | CHANGED | rc=0 >>
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2021-07-23 14:27:33 EDT; 6min ago
...
...
192.168.219.12 | CHANGED | rc=0 >>
● nginx.service - The nginx HTTP and reverse proxy server
   Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
   Active: active (running) since Fri 2021-07-23 14:27:33 EDT; 6min ago
...
...

 

nginx의 default 페이지를 확인하려 했으나 정상적으로 접속이 되지 않는다.

 

이는 방화벽에서 http 포트를 허용해주지 않았기 때문이다.

[root@node01 ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3
  sources: 
  services: dhcpv6-client ssh
  ports: 
  protocols: 
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules:

 

접속을 위해 80 포트를 방화벽에 추가시키도록 하자.

[root@node01 ~]# firewall-cmd --add-port=80/tcp
success
[root@node01 ~]# firewall-cmd --add-port=80/tcp --permanent
success

 

nginx 서버를 설치했는데 CentOS의 welcome page가 나온다.

초기화면의 경우 index.html의 값을 가져오니까 해당 디렉토리로 이동하여 확인해본다.

 

 

index.html은 아래 경로에 있으며 내용을 한번 확인해보자

[root@node01 html]# pwd
/usr/share/nginx/html
[root@node01 html]# ls -al | grep index.html
lrwxrwxrwx. 1 root root   25 Jul 23 14:25 index.html -> ../../doc/HTML/index.html

 

Welcome to nginx이 아닌 CentOS의 index.html이 들어있다.

 

우리가 보고 싶은 건 nginx의 default 페이지이기 때문에 nginx 홈페이지에서 indexl.html을 다운받는다.

먼저 기존 centos 내용이 담긴 index.html 파일은 과감히 지워버린다.

[root@node01 html]# rm -f index.html

[root@node01 html]# curl -o index.html https://www.nginx.com
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  125k    0  125k    0     0  82416      0 --:--:--  0:00:01 --:--:-- 82486

 

다시 재접속 시 원하던 nginx 페이지가 표시된다.

 

나머지 두 노드는 PlayBook을 통해 index.html을 복사해준다.

192.168.219.11 ip를 제외한 나머지 노드의 기존 index.html 파일을 삭제하기 위해 별칭을 추가한다.

- /etc/ansible/hosts

[upload]
192.168.219.12
192.168.219.13

 

yml 파일에 아래 두 줄을 추가하여 index.html을 /usr/share/nginx/html/ 디렉토리에 복사해준다.

- nginx.yml

    - name: Upload default index.html for web server
      copy: src=~/index.html dest=/usr/share/nginx/html/ mode=0644

 

현재 nginx.yml 파일은 아래와 같다.

[root@Ansible-Server ~]# cat nginx.yml 
---
- hosts: upload
  remote_user: root
  tasks:
    - name: install epel-release
      yum: name=epel-release state=latest
    - name: install nginx web server
      yum: name=nginx state=present
    - name: Upload default index.html for web server
      copy: src=~/index.html dest=/usr/share/nginx/html/ mode=0644
    - name: Start nginx web server
      service: name=nginx state=started

 

playbook 명령어를 통해 nginx.yml 파일을 적용시켜준다.

[root@Ansible-Server ~]# ansible-playbook nginx.yml -k
SSH password: 

PLAY [upload] *****************************************************************************

TASK [Gathering Facts] ********************************************************************
ok: [192.168.219.13]
ok: [192.168.219.12]
...
...
PLAY RECAP ********************************************************************************
192.168.219.12             : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.219.13             : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

 

접속 화면

 

'IaC' 카테고리의 다른 글

Terraform이란?  (0) 2022.03.25
Terraform vs Ansible  (0) 2021.09.20