Ansible 노드 구성

  • 암호로 인증을 하겠다는 옵션
[root@control_node ~]# ansible all -m ping -k

# -k(= --ask-pass): 암호로 인증을 하겠다는 옵션

# 연결성을 테스트하는 모듈: icmp 패킷을 보내는 프로토콜이 아니라 ping 요청에 대해 pong을 반환하는 모듈

[root@control_node ~]# vi /etc/ansible/hosts
[m1] 211.183.3.210
[m2] 211.183.3.220


yum 모듈

대상 서버에서 패키지관리 명령(yum)을 내리는 모듈

[root@control_node ~]# ansible m1 -m yum -a "name=httpd state=present" -k

# m1: 기본 인벤토리의 [m1]서버

# -a: 인자 전달
# name: 패키지 이름

# state: 원하는 상태(desired)

# present: 존재

# -k: 암호인증

# 내가 원하는 상태(state)가 존재(present)하는 상태였기 때문에 httpd라는 패키지가 설치안된 서버에 설치됨

[root@m1 ~]# systemctl status httpd

# 실제로 그런지 m1에 접속하여 확인
# 한번더 치면 변화없음


멱등성

  • 바람(Desired)과 현재 상태를 확인해 대조한 후 같으면 명령 수행X

# 바람(Desired state, 원하는 상태)과 현재상태(status)를 대조해서 ‘현재상태’가 ‘바램’ 수렴하게끔 함

[root@control_node ~]# ansible 211.183.3.210 -m service -a "name=httpd state=started" -k

# service: 모듈(= systemctl 비슷)

# started: 시작된 상태

[root@m1 ~]# systemctl status httpd | grep -i active

# m1서버의 httpd라는 패키지가 started가 됐음


https://docs.ansible.com/ansible/latest/collections/index_module.html

# 다양한 모듈들의 사용법을 알 수 있는 공식 문서
# 여기에 나와있는 모듈들 중에서도 특정한 모듈 이해하면 좋음

실습)

m2에 httpd라는 데몬이 잘 동작하고, 재부팅후에도 동작할 수 있도록 ansible 명령을 수행해보세요.
[root@control_node ~]# ansible m2 -m yum -a "name=httpd state=present" -k
[root@control_node ~]# ansible m2 -m service -a "name=httpd state=started" -k
[root@control_node ~]# ansible m2 -m service -a "name=httpd enabled=yes" -k

# enabled=true(= yes)

# m2에서 잘 동작하고 있음 확인

앤서블로 m2의 방화벽을 켜보세요.
[root@control_node ~]# ansible m2 -m service -a "name=firewalld state=started" -k


user 모듈

사용자 관리하는 모듈

  • m1 서버에서 newuser1이라는 사용자 생성
[root@control_node ~]# ansible m1 -m user -a "name=newuser1" -k

# m1 서버에서 확인해보면 잘 생성된걸 확인 가능

실습)

m3 서버를 한대 추가해서 m1,m2는 seoul 서버로 묶고 m3서버는 busan 서버로 묶어보세요.
  • 1. vi /etc/ansible/hosts로 추가

# 인벤토리의 서버목록은 중복 가능

[seoul]
211.183.3.210
211.183.3.220
[busan]
211.183.3.230
  • 2. 따로 인벤토리 파일을 생성
[root@control_node ~]# vi myinven

 

[root@control_node ~]# ansible busan -i myinven -m service -a "name=firewalld state=started"
yes
[root@control_node ~]# ansible busan -i myinven -m service -a "name=firewalld state=started" -k

# -i: 내가 임의로 만든 인벤토리를 사용하기 위해 사용하는 옵션으로 해당 인벤토리 파일을 지정


키페어 생성

계속 암호를 치는게 불편하니까, ssh 인증을 암호가 아닌 키페어를 사용(public-key 방식)

[root@control_node ~]# ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ""

# -b: bit

# -t: 암호화 타입

# -f: 파일 위치

# -q: 메세지X

# -N: passphrase(= 프라이빗키 암호 = 암호X)

# 사실상 ssh-keygen한것과 크게 다른건 없지만, 엔터를 3번 치지 않고 한번만 명령을 입력해도 키페어가 생성

  • 대상서버(root@211.183.3.210 = m1)의  authorized_keys에 내 퍼블릭키 등록
[root@control_node ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@211.183.3.210
  • m1에서 authorized_keys 확인
[root@m1 ~]# vi ~/.ssh/authorized_keys

# control_node의 퍼블릭키가 잘 등록됨

  • m2, m3의  authorized_keys에 내 퍼블릭키 등록
[root@control_node ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@211.183.3.220
[root@control_node ~]# ssh-copy-id -i ~/.ssh/id_rsa.pub root@211.183.3.230
  • 퍼블릭키를 심은 후 따로 암호로 인증(-k)하지 않아도 명령이 잘 전달됨
[root@control_node ~]# ansible all -i myinven -m ping


플레이북

태스트를 모아놓은 명세표, 문서

  • 모듈 : 도구 (ex. 장도리)
  • 태스크 : 작업 (ex. 못박기, 못뽑기)
  • 인벤토리 : managed node들의 목록
  1. 내가 수행하고자 하는 작업(task) = httpd라는 패키지가 설치되면 좋겠다.
    → yum이라는 도구를 사용해 state를 present로 만들면 된다.
  2. 내가 수행하고자 하는 작업(task) = httpd라는 패키지가 없었으면 좋겠다.
    → yum이라는 도구를 사용해 state를 absent로 만들면 된다.
[root@control_node ~]# mkdir /ans
[root@control_node ~]# cd /ans/

yaml 파일

확장자를 일반적으로 yaml, yml로 사용

JSON파일과 호환되는 <key>:<value> 형태로 구성된 파일

파이썬의 자료형 중 리스트와 딕셔너리를 통해 구성

  • 리스트
    동물 = [“고양이”,”강아지”]
  • 딕셔너리
    서버 = {“cpu”: 2, “ram” : 2048}
  • ex. state: started
    state = key값 : started = value값

플레이북은 결국 다양한 태스크들을 모아놓은 파일인데, 수행할 task가 없는 플레이북 제작해보자!

  • 플레이북 제작
[root@control_node ans]# vi playbook.yml
- name: playbook-name
  hosts: all
  tasks:

  • 기본 인벤토리의 모든 서버들한테 playbook.yml이라는 플레이북 파일을 수행
[root@control_node ans]# ansible-playbook playbook.yml

  • 아까 /root 경로에 만든 인벤토리 파일을 /ans로 복사
[root@control_node ans]# cp ~/myinven .
  • myinven라는 인벤토리를 대상으로 playbook.yml 수행
[root@control_node ans]# ansible-playbook playbook.yml -i myinven

 

플레이북에 httpd라는 패키지를 설치하는 태스크를 추가해보자!


# 2칸 들여쓰기

[root@control_node ans]# vi playbook.yml
- name: playbook-name
  hosts: all
  tasks:
  - name: install_httpd # httpd라는 패키지를 설치하는 테스트
    yum:
      name: httpd
      state: present
  • myinven라는 인벤토리를 대상으로 playbook.yml 수행
[root@control_node ans]# ansible-playbook playbook.yml -i myinven

# m1, m2는 이미 설치되어있었기 때문에 설치 안됐고 m3만 설치

실습)

nginx.yml이라는 플레이북을 만들어서 busan 서버에 nginx를 설치하고 동작시켜서 접속되는 것까지 확인해보세요

풀이1)

  • 플레이북 제작
[root@control_node ans]# vi nginx.yml
- name: nginx-name
  hosts: busan
  become: yes # root 권한 사용
  tasks:
  - name: Install nginx # nginx 설치
    yum:
      name: nginx
      state: present

  - name: Start and enable nginx service # nginx 시작 및 활성화
    systemd:
        name: nginx
        state: started
        enabled: yes

# httpd가 inactive가 아니라면 80 port로 겹치기 때문에 실행되지 않음

[root@control_node ans]# ansible-playbook nginx.yml -i myinven
[root@control_node ans]# ansible busan -i myinven -m service -a  "name=firewalld state=stopped"

# busan server의 m3(211.183.3.230)에 접속 잘되는 것 확인

풀이2)

  • m3에 가서 일단 nginx를 설치

# 패키지가 없음

[root@m3 ~]# yum install -y epel-release
[root@m3 ~]# yum install -y nginx
[root@m3 ~]# systemctl restart nginx

[root@m3 ~]# yum remove httpd -y

# httpd를 제거하고 nginx를 재시작하면 잘 동작함

  • 플레이북을 구성 (대상 = busan 서버)
플레이북
  httpd의 status를 absent로 하는 태스크(필요모듈=yum)
  firewalld 끄는 태스크(service모듈)
  epel-release 설치하는 태스크
  nginx 동작시키는 태스크
  • 플레이북 제작
[root@control_node ans]# vi nginx.yml
- name: nginx-pb
  hosts: busan
  tasks:
  - name: remove_httpd
    yum:
      name: httpd
      state: removed

  - name: firewalld_stopped
    service:
      name: firewalld
      state: stopped
      enabled: false # systemctl disable firewalld

  - name: install_epel
    yum:
      name: epel-release
      state: present

  - name: install_nginx
    yum:
      name: nginx
      state: present

  - name: start_nginx
    service:
      name: nginx
      state: started
      enabled: true
[root@control_node ans]# ansible-playbook nginx.yml -i myinven

여러개의 패키지를 설치하는 플레이북

[root@control_node ~]# cd /ans/
[root@control_node ans]# vi multi.yml
- name: multi_install_pb
  hosts: busan
  tasks:
  - name: multi_install_task
    yum:
      name: "{{ item }}"
      state: present
    with_items:
    - "httpd"
    - "net-tools"
    - "wget"
[root@control_node ans]# ansible-playbook multi.yml -i myinven

# 경고가 뜨긴함

  • busan 서버(m3)에 ifconfig명령이 존재함 →  net-tools가 잘 설치됨 확인


copy 모듈

컨트롤 노드에 존재하는 파일을 매니지드 서버에 복사

물론 매니지드-매니지드 복사 가능(remote_src)

내가 원하는 상태 = 웹서버를 설치 및 동작 시키고, index.html 복사

  • 넣어줄 index.html 파일 생성
[root@control_node ans]# echo test_copy > index.html
  • centos7에 nginx를 설치하면 웹루트디렉토리가 /usr/share/nginx/html

# m1서버에 아까 httpd를 제거하고, nginx를 설치했기때문에 /var/www/html 경로가 삭제됐음

  • 플레이북 생성
[root@control_node ans]# vi copy.yml
- name: copy_index_pb
  hosts: seoul
  tasks:
  - name: install_nginx
    yum:
      name: ['epel-release','nginx']
      state: present
  - name: start_nginx
    service:
      name: nginx
      state: started
      enabled: true

  - name: copy_file_task
    copy:
      src: /ans/index.html
      dest: /usr/share/nginx/html/index.html
[root@control_node ans]# ansible-playbook copy.yml -i myinven

# index.html 파일이 잘 복사된걸 확인 가능


lineinfile 모듈

[root@control_node ans]# vi lineinfile.yml
- name: lineinfile_pb
  hosts: busan
  tasks:
  - name: lineinfile
    lineinfile:
      path: /usr/share/nginx/html/index.html
      line: "line in file test"
[root@control_node ans]# ansible-playbook lineinfile.yml -i myinven
  • index.html 파일을 삭제
[root@m3 ~]# rm /usr/share/nginx/html/index.html
  • create 옵션으로 파일을 생성 후 내용 추가
create: true

 

# 파일 생성후 내용 추가

[root@control_node ans]# ansible-playbook lineinfile.yml -i myinven


file 모듈

파일 생성 및 권한 부여

[root@control_node ans]# vi file.yml
- name: make_file_pb
  hosts: busan
  tasks:
  - name: make_file_task
    file:
      path: /touch-test.txt
      state: touch
      mode: '0777' # 777앞의 0은 8진수를 의미
[root@control_node ans]# ansible-playbook file.yml -i myinven

# m3의 최상위 디렉토리에 파일 생성 및 권한부여가 잘된것 확인 가능

실습)

rapa.inven이라는 인벤토리파일을 하나 만든후 [web] 목록을 하나 만든다,
[web]에 속하는 서버는 IP가 211.183.3.150,211.183.3.160인 서버이며 여기에 간단한 index.html 파일을 넣으려는데,
해당 파일은 웹상의 https://www.w3.org/TR/PNG/iso_8859-1.txt 이 파일을 받아오고자 한다.
노드에 따로 파일을 다운받아서 넣지 말고, wget 같은 모듈을 찾아서 한번 해보세요. 
  • 서버 두개 생성해서 퍼블릭키를 넣기
ssh-copy-id -i ~/.ssh/id_rsa.pub root@211.183.3.150
ssh-copy-id -i ~/.ssh/id_rsa.pub root@211.183.3.160
  • 인벤토리 생성
[root@control_node ans]# vi rapa.inven
[web]
211.183.3.150
211.183.3.160
  • 플레이북 생성
[root@control_node ~]# cd /ans/
[root@control_node ans]# vi web.yml
- name: deploy_index_pb
  hosts: web
  tasks:
  - name: firewalld_stopped
    service:
      name: firewalld
      state: stopped
      enabled: false # systemctl disable firewalld

  - name: install_epel
    yum:
      name: epel-release
      state: present

  - name: install_nginx
    yum:
      name: nginx
      state: present

  - name: start_nginx
    service:
      name: nginx
      state: started
      enabled: true

  - name: download_index_task
    get_url:
      url: https://www.w3.org/TR/PNG/iso_8859-1.txt
      dest: /usr/share/nginx/html/index.html
      force: yes  # 기존 파일을 덮어쓰기
[root@control_node ans]# ansible-playbook filedown.yml -i rapa.inven


shell 모듈

명령을 수행하는 shell 모듈의 경우, 단순히 명령을 수행하기때문에 멱등성이 보장되지 않는다.

shell 모듈은 멱등성이 보장되지 않기때문에 만약 대체 가능한 모듈이 있다면 그 모듈을 사용하는게 좋

[root@control_node ans]# vi shell.yml
- name: shell_test_pb
  hosts: web
  tasks:
  - name: shell_test_task
    shell: "{{ item }}"
    with_items:
    - "mkdir /shelltest"
    - "cp /root/anaconda-ks.cfg /shelltest"
    - "ls -al /shelltest"
  • 플레이북 수행
[root@control_node ans]# ansible-playbook shell.yml -i rapa.inven

# 플레이북에서 shell 명령을 잘 수행했다.

 

실습1)

인벤토리의 [web] 서버들을 대상으로 하여 프리템플릿(고양이)을 배포해보세요.
[root@control_node ans]# vi tem.yml
- name: deploy_tem_pb
  hosts: web
  tasks:
  - name: firewalld_stopped
    service:
      name: firewalld
      state: stopped
      enabled: false # systemctl disable firewalld
  - name: install_epel
    yum:
      name: epel-release
      state: present
  - name: install_nginx
    yum:
      name: nginx
      state: present
  - name: start_nginx
    service:
      name: nginx
      state: started
      enabled: true

  - name: get_free_tem
    get_url:
      url: https://www.free-css.com/assets/files/free-css-templates/download/page169/the-cat.zip
      dest: /root/tem.zip

  - name: install_unzip
    yum:
      name: unzip
      state: present

  - name: unarchive
    unarchive:
      src: /root/tem.zip
      remote_src: yes
      dest: /usr/share/nginx/html
[root@control_node ans]# ansible-playbook tem.yml -i web

실습2)

앤서블 플레이북을 통해 m3는 nfs-server로, m1은 nfs-client로 구성해보세요.
  • 인벤토리 생성
[root@control_node ans]# vi /root/nfsinven
[nfs_server]
211.183.3.230

[nfs_client]
211.183.3.210
  • 인벤토리 복사
[root@control_node ans]# cp ~/nfsinven .
  • 플레이북 생성
[root@control_node ans]# vi nfs.yml
- name: nfs_pb
  hosts: all
  become: yes
  tasks:

  # ✅ NFS 서버 설정 (m3)
  - name: Install NFS server packages (Ubuntu/Debian)
    apt:
      name: nfs-kernel-server
      state: present
    when: ansible_os_family == "Debian"

  - name: Install NFS server packages (CentOS/RHEL)
    yum:
      name: nfs-utils
      state: present
    when: ansible_os_family == "RedHat"

  - name: Create NFS shared directory
    file:
      path: /srv/nfs_share
      state: directory
      mode: '0777'
    when: inventory_hostname in groups['nfs_server']

  - name: Configure NFS export
    lineinfile:
      path: /etc/exports
      line: "/srv/nfs_share 211.183.3.210(rw,sync,no_root_squash)"
      create: yes
    when: inventory_hostname in groups['nfs_server']

  - name: Restart NFS server (Ubuntu/Debian)
    service:
      name: nfs-kernel-server
      state: restarted
    when: ansible_os_family == "Debian"

  - name: Restart NFS server (CentOS/RHEL)
    service:
      name: nfs-server
      state: restarted
    when: ansible_os_family == "RedHat"

  # ✅ NFS 클라이언트 설정 (m1)
  - name: Install NFS client packages (Ubuntu/Debian)
    apt:
      name: nfs-common
      state: present
    when: ansible_os_family == "Debian"

  - name: Install NFS client packages (CentOS/RHEL)
    yum:
      name: nfs-utils
      state: present
    when: ansible_os_family == "RedHat"

  - name: Create mount directory
    file:
      path: /mnt/nfs_share
      state: directory
      mode: '0755'
    when: inventory_hostname in groups['nfs_client']

  - name: Mount NFS share
    mount:
      path: /mnt/nfs_share
      src: "211.183.3.230:/srv/nfs_share"
      fstype: nfs
      opts: defaults
      state: mounted
    when: inventory_hostname in groups['nfs_client']
  • 플레이북 실행
[root@control_node ans]# ansible-playbook nfs.yml -i nfsinven

  • nfs_server(m3) 확인

  • nfs_client(m1) 확인

  • NFS 공유 디렉터리에서 파일 테스트

풀이)

  • nfs-client
[root@m4 html]# vi nfs-srv.yml
- name: nfs-srv_pb
  hosts: nfs-srv
  tasks:
  - name: disable_selinux
    selinux:
      state: disabled
  - name: stop_firewalld
    service:
      name: firewalld
      state: stopped
      enabled: false

  - name: install_nfs-utils
    yum:
      name: nfs-utils
      state: present

  - name: mkdir_shared
    file:
      path: /shared
      state: directory
      mode: '0777'

  - name: edit_exports
    lineinfile:
      path: /etc/exports
      line: "/shared *(rw,no_root_squash)"

  - name: nfs_start
    service:
      name: nfs-server
      state: restarted

# 인벤토리에 추가

[root@control_node ans]# ansible-playbook nfs-srv.yml -i rapa.inven

# nfs-cli 서버 말고 아무 다른 서버에서 nfs-srv가 잘 구성됐는지 확인
# 마운트 확인

  • nfs-client
[root@control_node ans]# vi nfs-cli.yml
- name: nfs-cli_pb
  hosts: nfs-cli
  tasks:
  - name: disable_selinux
    selinux:
      state: disabled
  - name: stop_firewalld
    service:
      name: firewalld
      state: stopped
      enabled: false
      
  - name: install_nfs-utils
    yum:
      name: nfs-utils
      state: present

  - name: mkdir_remote
    file:
      path: /remote
      state: directory
      mode: '0777'

  - name: mount_directory
    mount:
      name: /remote
      src: '211.183.3.150:/shared'
      fstype: nfs
      state: mounted
[root@control_node ans]# ansible-playbook nfs-cli.yml -i rapa.inven