본문 바로가기

study/Ansible101 1기

Ansible101 1기 3주차 두번째

네트워크 IP 설정하기

OS가 우분투일 경우에는 netplan 파일을 이용하여 IP를 설정한다

 - netplan은 파일이므로 사전에 netplan 파일 구조를 확인하고 jinja2 템플릿으로 작성한다.

 

OS가 CentOS/레드햇일 경우에는 nmcli 모듈을 사용하여 IP를 설정한다.

 - community.general.nmcli 모듈 - https://docs.ansible.com/ansible/latest/collections/community/general/nmcli_module.html

 

예제에서는 ethernet 타입의 네트워크 IP를 설정한다.

IP 설정 관련 정보는 메인 플레이북에서 변수로 정의한다.

변수로 정의한 네트워크 인터페이스가 실제 호스트에 존재하는지 앤서블 팩트를 통해 확인한다.

 

플레이북 설계

우분투 일 경우 netplan을 사용하는 롤과 CentOS/레드햇 일 경우 nmcli 모듈을 사용하는 롤로 구성합니다.

그리고 OS의 종류에 따라 해당 롤을 호출하는 방식으로 메인 플레이북을 설계하였습니다.

 

ansible.builtin.template 모듈 - https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.html

 

ansible.builtin.template module – Template a file out to a target host — Ansible Documentation

The permissions the resulting filesystem object should have. For those used to /usr/bin/chmod remember that modes are actually octal numbers. You must give Ansible enough information to parse them correctly. For consistent results, quote octal numbers (for

docs.ansible.com

 

플레이북 개발

1. 플레이북 개발 및 실행 : nic2를 추가하기 번거로워서 동작 적용은 하지 않음

- 프로젝트 디렉터리 생성 및 ansible.cfg, inventory 파일 작성

#
mkdir ~/ansible-project/chapter_10.1
cd ~/ansible-project/chapter_10.1

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

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

cat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT

 

2. 롤 생성

#
ansible-galaxy role init --init-path ./roles myrole.nmcli
ansible-galaxy role init --init-path ./roles myrole.netplan

# 확인
ansible-galaxy role list
tree roles -L 2

 

3. myrole.nmcli 에 태스크 파일 작성

#~/chapter_10.1/roles/myrole.nmcli/tasks/main.yml
---
# tasks file for myrole.nmcli

- name: Setup nic ip
  community.general.nmcli:
    type: ethernet
    conn_name: "{{ item.con_name }}"
    ip4: "{{ item.ip_addr }}"
    gw4: "{{ item.ip_gw }}"
    dns4: "{{ item.ip_dns }}"
    state: present
  loop: "{{ net_info }}"
  when: net_info[0].con_name in ansible_facts.interfaces

- nmcli 모듈을 사용하여 외부로부터 받은 변수로 네트워크 IP를 설정.

- 이때 변수는 배열 방식으로 받은 변수이므로 loop를 이용하였고, when 키워드를 사용하여 외부로부터 받은 인터페이스가 앤서블 팩트에 존재하는 확인

 

 

4. myrole.netplan 에 Jinna2 템플릿(파이썬 웹 프로그래밍에 주로 쓰이는 템플릿 파일) 파일 작성

touch ~/ansible-project/chapter_10.1/roles/myrole.netplan/templates/01-netplan-ansible.yaml.j2

 

#~/chapter_10.1/roles/myrole.netplan/templates/01-netplan-ansible.yaml.j2
# This is the network config written by 'ansible'
network:
  version: 2
  ethernets:
{% for item in net_info %}
    {{ item.con_name }}:
      dhcp4: no
      dhcp6: no
      addresses: [{{ item.ip_addr }}]
      gateway4: {{ item.ip_gw }}
      nameservers:
        addresses: [{{ item.ip_dns }}]
{% endfor %}

- Jinja2 템플릿을 이용하여 외부로부터 받은 배열형 변수for 문으로 하나씩 꺼내 사용할 수 있습니다.

- Jinja2 템플릿에서 제어문이나 반복문을 사용할 때는 다음과 같이 {% ~ %}를 이용합니다.

 

Jinja(진자)2 소개

- 앤서블에서 변수 확장에는 파이썬으로 작성된 Jinja2 템플릿 엔진을 사용.

- 원래 플라스크 Flask라는 파이썬의 웹 애플리케이션 프로임워크로 HTML에 동적인 값을 설정할 때 사용되는 템플릿 엔진.

- 진자의 ‘템플릿의 변수 정보를 확장하고 출력한다’ 일반적인 동작을 앤서블에서는 템플릿 모듈을 사용해 파일을 확장하는 것은 물론이고,

플레이북에 있는 변수 정보를 확장할 때도 진자2를 사용합니다.

https://cumulus.tistory.com/75

 

Ansible Template 활용하기

https://www.inflearn.com/course/ansible-%EC%8B%AC%ED%99%94/lecture/10808?tab=curriculum&speed=1.25 [심화] 앤서블(Ansible)을 깊이 있게 활용하기 - 인프런 | 학습 페이지 지식을 나누면 반드시 나에게 돌아옵니다. 인프런

cumulus.tistory.com

 

5. myrole.netplan 에 태스크 파일 작성

#~/chapter_10.1/roles/myrole.netplan/tasks/main.yml
---
# tasks file for myrole.netplan

- name: Copy netplan file
  ansible.builtin.template:
    src: 01-netplan-ansible.yaml.j2
    dest: /etc/netplan/01-netplan-ansible.yaml
  when: net_info[0].con_name in ansible_facts.interfaces
  notify: Netplan apply

 

- template 모듈을 이용하여 앞에서 생성한 템플릿 파일을 대상 호스트에 복사합니다.

- 이때 when 구문을 이용하여 외부로부터 받은 인터페이스가 앤서블 팩트에 존재하는지 확인합니다.

- 템플릿 복사가 잘 되면 notify 키워드를 사용하여 핸들러를 호출합니다.

 

6. myrole.netplan 에 핸들러 파일 작성 : 핸들러는 command 모듈을 이용하여 netplan apply 명령어를 수행

#~/chapter_10.1/roles/myrole.netplan/handlers/main.yml
---
# handlers file for myrole.netplan

- name: Netplan apply
  ansible.builtin.command: netplan apply

 

7. 마지막으로 호출메인 플레이북을 작성

- 메인 플레이북에는 롤에 전달할 변수들을 vars 섹션에 선언하고 tasks 섹센에 롤을 추가합니다.

- 이때 ansible.builtin.include_role 모듈을 이용하여 롤을 호출하면 when 구문을 함께 사용할 수 있습니다.

- 이렇게 앤서블 팩트에서 수집한 OS 버전에 따라 해당 롤을 호출할 수 있습니다.

cd ~/ansible-project/chapter_10.1
touch set_ip.yml

 

#~/chapter_10.1/set_ip.yml
---

- hosts: tnode1
  vars:
    fedora_os: 
      - CentOS
      - RedHat
    net_info:
      - con_name: ens5
        ip_addr: 10.10.1.11/24
        ip_gw: 10.10.1.1
        ip_dns: 127.0.0.53

  tasks:
  - name: Include role in CentOS and RedHat
    ansible.builtin.include_role:
      name: myrole.nmcli
    when: ansible_facts.distribution in fedora_os

  - name: Include role in Ubuntu
    ansible.builtin.include_role:
      name: myrole.netplan
    when: ansible_facts.distribution == "Ubuntu"

- hosts: tnode2
  vars:
    fedora_os: 
      - CentOS
      - RedHat
    net_info:
      - con_name: ens7
        ip_addr: 10.10.1.12/24
        ip_gw: 10.10.1.1
        ip_dns: 127.0.0.53

  tasks:
  - name: Include role in CentOS and RedHat
    ansible.builtin.include_role:
      name: myrole.nmcli
    when: ansible_facts.distribution in fedora_os

  - name: Include role in Ubuntu
    ansible.builtin.include_role:
      name: myrole.netplan
    when: ansible_facts.distribution == "Ubuntu"

 

8. 실행

실행 전 확인

# 실행 전 tnode1 정보 확인
ssh tnode1 ls /etc/netplan
ssh tnode1 cat /etc/netplan/50-cloud-init.yaml
ssh tnode1 ip -br -c addr
ssh tnode1 ip -c route
ssh tnode1 nslookup blog.cloudneta.net

#
ansible -m shell -a "cat /var/log/syslog | grep -i dhcp" tnode1
ssh tnode1 sudo dhclient -v ens5

 

실행

# 문법 체크
ansible-playbook --syntax-check set_ip.yml

# 플레이북 실행 : 우분투 OS로 롤 호출 되지만, tnode2는 enp7 NIC가 없어서 Setup nic ip 태스크가 실행되지는 않는다
ansible-playbook set_ip.yml
...

# 실행 후 tnode1 정보 확인
ssh tnode1 ls /etc/netplan
ssh tnode1 cat /etc/netplan/01-netplan-ansible.yaml
ssh tnode1 ip -br -c addr
ssh tnode1 ip -c route
ssh tnode1 nslookup blog.cloudneta.net

 

결과

 

 

 

호스트명 설정하기

앤서블로 접근하기 위한 대상 서버(tnode1~3)들은 이미 제어 노드(server)의 인벤토리에 등록되어 있다.

호스트명 설정을 하기 위해 ansible.builtin.hostname 모듈을 사용한다.

/etc/hosts 에 tnode 정보들을 등록하기 위해 필요한 정보들을 변수로 정의한다.

호스트명을 hosts 파일에 추가할 때는 ansible.builtin.lineinfile 모듈을 사용한다.

ansible.builtin.hostname 모듈 - https://docs.ansible.com/ansible/latest/collections/ansible/builtin/hostname_module.html

ansible.builtin.lineinfile 모듈 - https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html

 

1. 프로젝트 디렉터리 생성 및 ansible.cfg, inventory 파일 작성

#
mkdir ~/ansible-project/chapter_10.2
cd ~/ansible-project/chapter_10.2

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

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

cat <<EOT> inventory
[tnode]
tnode1
tnode2
tnode3
EOT

 

 

2. 변수 정의 파일 생성 : hosts 파일에 추가할 정보를 사전형 변수로 정의하여 반복문에서 사용할 수 있게

touch ~/ansible-project/chapter_10.2/vars_hosts_info.yml

####chapter_10.2/vars_hosts_info.yml
tnodes:
  - hostname: tnode1
    fqdn: tnode1.local
    net_ip: 10.10.1.11
  - hostname: tnode2
    fqdn: tnode2.local
    net_ip: 10.10.1.12
  - hostname: tnode3
    fqdn: tnode3.local
    net_ip: 10.10.1.13

 

3. 메인 플레이북 작성 : hostname은 inventory 정보를 통해서 설정하고, /etc/hosts 파일은 내용 추가는 변수 정의 파일에서 반복문을 통해 가져온다.

- regexp 는 /etc/hosts 에서 해당 값(여기서는 ip)가 있을 경우 대체(기본값 수정, state=present)하거나 혹은 삭제(state=absent).

- 매칭되는 것이 없다면 line(내용)을 추가합니다

touch ~/ansible-project/chapter_10.2/set_hostname.yml

 

chapter_10.2/set_hostname.yml

---
- hosts: tnode
  tasks: 
  - name: Set hostname from inventory
    ansible.builtin.hostname:
      name: "{{ inventory_hostname }}"

- hosts: all
  vars_files: vars_hosts_info.yml

  tasks: 
  - name: Add host ip to hosts
    ansible.builtin.lineinfile:
      path: /etc/hosts
      line: "{{ item.net_ip }}  {{ item.hostname }} {{ item.fqdn }}"
      regexp: "^{{ item.net_ip }}"
    loop: "{{ tnodes }}"

 

4. 플레이북 실행

- 실행 전 확인

ansible -m shell -a "cat /etc/hosts" tnode

 

실행 후 확인

# 문법 체크
ansible-playbook --syntax-check set_hostname.yml

# 플레이북 실행
ansible-playbook set_hostname.yml
...

# 확인
ansible -m shell -a "hostname" tnode
ansible -m shell -a "cat /etc/hosts | grep tnode" tnode

# tnode1에서 다른 노드 통신 확인
ssh tnode1 ping -c 1 tnode2
ssh tnode1 ping -c 1 tnode3.local

 

확인

 

 

NFS 서버 설치 및 NFS 스토리지

실습 환경에서는 NFS 서버를 CentOS에 구성한다

- NFS 서버가 구성되면 나머지 두 노드에는 NFS 스토리지를 마운트한다

- 플레이북 재사용을 위한 NFS 서버 및 클라이언트는 롤로 구성한다

 

NFS 서버 설치를 위한 myrole.nfs_server 롤과 NFS 스토리지 마운트를 위한 myrole.nfs_client 롤이 있습니다

 

롤 설계 : NFS 패키지를 OS에 맞게 설치하고 설치가 끝난 후 마운트할 디텍터리를 생성하고 설치한 NFS 서버의 공유 디렉터리로 마운트합니다

 

1. 프로젝트 디렉터리 생성 및 ansible.cfg, inventory 파일 작성

#chapter_10.3/..

#
mkdir ~/ansible-project/chapter_10.3
cd ~/ansible-project/chapter_10.3

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

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

cat <<EOT> inventory
[nfs_server]
tnode1

[nfs_client]
tnode2
tnode3
EOT

 

2. 롤 생성 : myrole.nfs_server, myrole.nfs_client

#
ansible-galaxy role init --init-path ./roles myrole.nfs_server
ansible-galaxy role init --init-path ./roles myrole.nfs_client

# 확인
ansible-galaxy role list
tree roles -L 2

 

 

3. myrole.nfs_server 에 변수 파일 작성 : 설치할 NFS 서버 관련 패키지

#chapter_10.3/roles/myrole.nfs_server/vars/main.yml
---
# vars file for myrole.nfs-server

nfs_packages: 
  - nfs-kernel-server

 

4. myrole.nfs_server 에 태스크 파일 작성

chapter_10.3/roles/myrole.nfs_server/tasks/main.yml

---
# tasks file for myrole.nfs_server

- name: Install NFS packages
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop: "{{ nfs_packages }}"

- name: Create NFS export directory
  ansible.builtin.file:
    path: "{{ share_path }}"
    state: directory
    owner: root
    group: root
    mode: "0755"

- name: Configure NFS exports
  ansible.builtin.lineinfile:
    path: "/etc/exports"
    line: "{{ share_path }}    *(rw,sync)"
    regexp: "^{{ share_path }}"
    state: present
    create: true
  notify: Restart NFS Service

 

5. myrole.nfs_server 에 핸들러 파일 작성

chapter_10.3/roles/myrole.nfs_server/handlers/main.yml

---
# handlers file for myrole.nfs_server

- name: Restart NFS Service
  ansible.builtin.service:
    name: nfs-server
    state: restarted

 

6. myrole.nfs_client 에 변수 파일 작성 : 설치할 NFS 패키지

chapter_10.3/roles/myrole.nfs_client/vars/main.yml

---
# vars file for myrole.nfs_client

apt_nfs_packages:
  - nfs-common
dnf_nfs_packages:
  - nfs-utils

 

7. myrole.nfs_client 에 태스크 파일 작성

chapter_10.3/roles/myrole.nfs_client/tasks/main.yml

---
# tasks file for myrole.nfs-client

- name: Install NFS Packages on Ubuntu
  ansible.builtin.apt:
    name: "{{ item }}"
    update_cache: true
    state: present
  loop: "{{ apt_nfs_packages }}"
  when: ansible_facts.distribution == "Ubuntu"

- name: Install NFS Packages on Rhel
  ansible.builtin.dnf:
    name: "{{ item }}"
    state: present
  loop: "{{ dnf_nfs_packages }}"
  when: ansible_facts.distribution == "RedHat"

- name: Create Mount Directory
  ansible.builtin.file:
    path: "{{ mount_path }}"
    state: directory

- name: Mount NFS
  ansible.posix.mount:
    src: "{{ share_server }}:{{ share_path }}"
    path: "{{ mount_path }}"
    opts: rw,sync
    state: mounted
    fstype: nfs

 

8. 메인 플레이북에서 사용할 변수를 정의 → 롤에 포함하지 않은 이유는 각각의 롤이 동일한 값의 변수를 사용하기 위함

touch ~/ansible-project/chapter_10.3/vars_share_path.yml

 

chapter_10.3/vars_share_path.yml

---

share_server: tnode1
share_path: /mnt/nfs_shares
mount_path: /mnt/nfs_data

 

9. 메인 플레이북 작성

touch ~/ansible-project/chapter_10.3/set_nfs_storage.yml

 

chapter_10.3/set_nfs_storage.yml

---

- hosts: nfs_server
  vars_files: vars_share_path.yml
  roles:
    - role: myrole.nfs_server

- hosts: nfs_client
  vars_files: vars_share_path.yml
  roles:
    - role: myrole.nfs_client

 

 

10. 플레이북 실행 및 nfs 동작 확인

# 실행 전 tnod2, tnode3 NFS 스토리지 마운트 확인
ssh tnode2 df -h --type nfs4
ssh tnode3 df -h --type ext4

 

 

 

# 문법 체크
ansible-playbook --syntax-check set_nfs_storage.yml

# 실행
ansible-playbook set_nfs_storage.yml
...

# 실행 후 tnod2, tnode3 NFS 스토리지 마운트 확인
ssh tnode2 df -h --type nfs4
ssh tnode3 df -h --type nfs4

# tnode1에 nfs-server 확인
ssh tnode1 systemctl status nfs-server
ssh tnode1 sudo exportfs -s

# (모니터링) 신규 터미널
sudo su - ubuntu
ssh tnode2 ls /mnt/nfs_data
watch -d "ssh tnode2 ls -l /mnt/nfs_data"

# tnode1 접속 후 /mnt/nfs_shares에 파일 10개 생성
ssh tnode1
for i in {1..10}; do sudo touch /mnt/nfs_shares/deleteme.$i; done;
exit

 

 

 

DB 애플리케이션 설치하기

앤서블 갤럭시에서 MySQL 설치하는 롤을 찾아보고, 해당 롤을 이용하여 테스트 진행 - Link geerlingguyGitHub 방문

https://github.com/geerlingguy/ansible-role-mysql

 

GitHub - geerlingguy/ansible-role-mysql: Ansible Role - MySQL

Ansible Role - MySQL. Contribute to geerlingguy/ansible-role-mysql development by creating an account on GitHub.

github.com

 

- MySQL을 tnode2에 설치한다

  앤서블 갤럭시에서 우분투에 설치할 수 있는 MySQL 롤을 검색하여 해당 롤을 이용한다

 

1. 프로젝트 디렉터리 생성 및 ansible.cfg, inventory 파일 작성

#
mkdir ~/ansible-project/chapter_10.4
cd ~/ansible-project/chapter_10.4

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

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

cat <<EOT> inventory
[db]
tnode2

[tnode]
tnode1
tnode2
tnode3
EOT

 

 

2. 검색한 mysql role 설치

#
ansible-galaxy role install -p ./roles geerlingguy.mysql

# 확인
ansible-galaxy role list
tree roles -L 3

 

 

3. 롤 태스트 정보 확인 : 오래된 Facts 표기법 사용하니, ansible.cfg 확인

chapter_10.4/roles/geerlingguy.mysql/tasks/main.yml

---
# Variable configuration.
- ansible.builtin.include_tasks: variables.yml

# Setup/install tasks.
- ansible.builtin.include_tasks: setup-RedHat.yml
  when: ansible_os_family == 'RedHat'

- ansible.builtin.include_tasks: setup-Debian.yml
  when: ansible_os_family == 'Debian'

- ansible.builtin.include_tasks: setup-Archlinux.yml
  when: ansible_os_family == 'Archlinux'

- name: Check if MySQL packages were installed.
  ansible.builtin.set_fact:
    mysql_install_packages: "{{ (rh_mysql_install_packages is defined and rh_mysql_install_packages.changed)
      or (deb_mysql_install_packages is defined and deb_mysql_install_packages.changed)
      or (arch_mysql_install_packages is defined and arch_mysql_install_packages.changed) }}"

# Configure MySQL.
- ansible.builtin.include_tasks: configure.yml
- ansible.builtin.include_tasks: secure-installation.yml
- ansible.builtin.include_tasks: databases.yml
- ansible.builtin.include_tasks: users.yml
- ansible.builtin.include_tasks: replication.yml

os_family 에 따라 mysql 설치 후 환경 설정 태스크들을 차례대로 호출 ⇒ 나머지 yml 파일들도 한번 내용 확인해보시기 바랍니다.

- (참고) os_family 는 간단하게 분류 : Debian에 Ubuntu도 속함 - Link

 

Conditionals — Ansible Documentation

In a playbook, you may want to execute different tasks or have different goals, depending on the value of a fact (data about the remote system), a variable, or the result of a previous task. You may want the value of some variables to depend on the value o

docs.ansible.com

 

4. 플레이북 작성

touch ~/ansible-project/chapter_10.4/install_mysql.yml

chapter_10.4/install_mysql.yml

---
- hosts: db
  roles:
    - role: geerlingguy.mysql

 

5. 실행

# 문법 체크
ansible-playbook --syntax-check install_mysql.yml

# 시뮬레이션 : Ensure MySQL is stopped after initial install 는 설치가 안되서 발생하는 거니 문제 없음
ansible-playbook --check install_mysql.yml

# 실행
ansible-playbook install_mysql.yml

# 확인
ssh tnode2 systemctl status mysql

#
ssh tnode2
sudo mysql -u root -e "show databases;"
sudo mysql -u root mysql
status
exit
exit

'study > Ansible101 1기' 카테고리의 다른 글

Ansible Semaphore  (0) 2024.02.09
Ansible101 1기 4주차 첫번째  (0) 2024.02.04
Ansible101 1기 3주차 첫번째  (0) 2024.01.28
Ansible101 1기 2주차 두번째  (0) 2024.01.14
Ansible101 1기 2주차 첫번째  (0) 2024.01.14