728x90
반응형
SMALL
SMALL

Condition

이번엔 조건문에 대해서 알아보자. Ansible에서는 when이라는 키워드를 사용한다. 바로 예제를 보자.

- name: Example
  hosts: all
  become: true
  vars:
    users:
    - name: john
      shell: /bin/bash
      enabled: true
    - name: alice
      shell: /bin/sh
      enabled: false
    - name: claud
      shell: /bin/bash
      enabled: true
    - name: henry
      shell: /bin/sh
      enabled: false
    - name: jeremy
      shell: /bin/bash
      enabled: true
    - name: may
      shell: /bin/sh
      enabled: false
  tasks:
  - name: "Create a user if enabled in Amazon Linux"
    user:
      name: "{{ item.name }}"
      shell: "{{ item.shell }}"
      comment: "cwchoiit-ansible"
      state: "present"
    loop: "{{ users }}"
    when: item.enabled and (ansible_facts["distribution"] == "Amazon")

 

이번엔 hosts를 all로 설정했다. 그러나 모든 호스트에 대해 적용되지 않을 것이다. 왜냐하면 하단 when 키워드를 보면 조건이 있기 때문이다. 우선 loop와 {{}} 변수에 대해서는 이전 포스팅에서 다뤘기 때문에 모두 이해가 간다. 근데 유저를 생성하는데 조건이 있는데 그 조건이 현재 아이템(루프를 돌면서 하나씩 나오는 유저)의 enabled값이 true인 유저만 생성한다는 의미이다. 그리고 그 조건에 and로 이어지는 또 다른 조건이 있다. 배포판이 Amazon인 경우에만 유저를 생성한다. 나는 지금 가지고 있는 호스트 그룹이 Ubuntu와 Amazon 두 가지인데 그 중 Amazon 배포판에만 적용하겠다는 의미가 된다. 이대로 실행해보자. 다음과 같은 결과가 나온다.

우선 보다시피 Ubuntu는 모두 스킵이 됐다. Amazon에서는 enabled가 True인 것들은 Changed가 됐음을 알 수 있다. 그래서 실제로 적용됐는지 Amazon 호스트에 들어가보자. 다음과 같이 enabled가 true 유저들만 생성됐음을 확인할 수 있다.

 

 

그리고 조건은 이렇게도 AND로 사용할 수 있다. 여러개를 나열하면 이 조건들은 AND로 묶인다.

- name: "Show items between 10 and 100"
    debug:
      var: item
    loop: [ 0, 192, 154, 456, 7, 2, -1, 55, 234]
    when:
    - item >= 10
    - item <= 100

 

실행해보면 결과는 다음과 같다. 10과 100사이에 있는 loop 배열안에 숫자만 출력한다.

 

당연히 OR도 있다.

- name: "Show items not between 10 and 100"
    debug:
      var: item
    loop: [ 0, 192, 154, 456, 7, 2, -1, 55, 234]
    when:
    - (item < 10) or (item > 100)

실행해보면 10보다 작고 100보다 큰 숫자만 출력한다. 55는 스킵한다.

 

이번에는 Task에서 실행한 command 모듈의 표준출력을 변수로 받아서 그 변수를 조건으로 사용할 수도 있다.

---

- name: Example
  hosts: all
  become: true
  tasks:
  - name: "Print users"
    command: "cut -d: -f1 /etc/passwd"
    register: uus

  - name: "Is there claud"
    debug:
      msg: "There is no claud"
    when: uus.stdout.find('claud') == -1

위 코드를 보면 먼저 실행하는 Task에서 command 모듈로 유저 리스트를 가지고 온다. 저 문장을 해석해보면 /etc/passwd 명령어를 수행해서 나오는 결과를 잘라내는데(cut) 잘라내는 기준인 (delimiter)가 ":"가 되고 그 잘라낸 것 중 앞에것(-f1)만을 가져오는 명령어이다.

그래서 저 명령어를 수행하면 이러한 결과를 얻어낸다.

이 표준출력을 변수 uus에 등록하겠다는 의미가 되고, 그 다음 Task에서 그 변수의 표준출력에서 'claud'라는 값을 찾아낸다. 없다면 -1을 리턴하는데 그럴 때 메세지를 찍어주는 Task이다. 결과는 다음과 같다.

 

이렇게 조건문을 사용해서 그때 그때 조건에 맞는 작업을 수행할 수 있다.

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Ansible Part. 6 (Loop)  (0) 2024.03.18
Ansible Part. 5 (Variables)  (0) 2024.03.18
Ansible Part. 4 (Handler)  (0) 2024.03.18
Ansible Part. 3 (Playbook)  (0) 2024.03.18
Ansible Part. 2 (Adhoc)  (3) 2024.03.17
728x90
반응형
SMALL
SMALL

 

Loop

Ansible Playbook을 작성할 때도 물론 Loop를 사용할 수 있다. 그리고 Ansible에서 기존 방식과 새롭게 생겨난 방식이 있는데 둘 다 알아보겠지만 앞으로 Deprecated되고 더 이상 사용하지 않게 될 가능성도 있으니 앞으로는 새롭게 생겨난 방식으로 코드를 작성해보자.

 

with_<lookup>

아직은 Deprecated 되지 않았지만 추천하지 않는 구 방식인 with_<lookup>을 살펴보자.

- name: Playbook
  hosts: ubuntu
  become: true
  tasks:
    - name: "Create groups"
      group:
        name: "{{ item }}"
        state: "present"
      with_items:
        - backend
        - frontend
        - devops

 

위 코드를 보면 group을 만들어내는 모듈을 사용한다. 여기서 총 세번의 그룹이 만들어진다. with_items라는 키워드로 만들어 낼 그룹 3가지를 위 코드처럼 작성하면 name: "{{ item }}"에서 저 하나하나를 item이 가리키게 된다. 그래서 총 3개의 그룹이 만들어진다. 

 

실행해보자. 

ansible-playbook -i inventory playbook.yaml

 

정상적으로 실행됐으면 Ubuntu 호스트에 들어가서 다음 명령어를 입력해보자.

cat /etc/group

이렇게 총 세개의 그룹이 만들어졌음을 확인할 수 있다. 이렇게 사용하는 방법을 with_<lookup> 이라고 한다.

 

loop

이제 Ansible 공식 문서에서도 이 방법을 추천한다. 그래서 앞으로는 이것만 사용하도록 한다. 

간단하게 loop 키워드에 반복할만큼 데이터를 채워넣으면 된다.

- name: Playbook
  hosts: ubuntu
  become: true
  tasks:
    - name: "Create users"
      user:
        name: "{{ item }}"
        comment: "ansible study"
        state: "present"
      loop:
        - john
        - alice
        - cloud
        - henry
        - jeremy
        - may

데이터를 채워넣고 유저를 생성하는 모듈을 사용해서 name: "{{ item }}"을 사용하면 loop에 채워넣은 데이터를 쭉 돌게 된다.

실행해보자. 다음과 같은 결과를 확인할 수 있다.

 

근데 저렇게 loop에 직접적으로 데이터를 채워넣는 방법도 있지만, 데이터를 변수로 받을 수도 있다. 다음이 그 예시이다.

- name: Playbook
  hosts: ubuntu
  become: true
  vars:
    users:
      - jan
      - ali
      - cow
      - hazard
      - june
  tasks:
    - name: "Create a users"
      user:
        name: "{{ item }}"
        comment: "loop vars"
        state: "present"
      loop: "{{ users }}"

이렇게 작성하고 실행해보자. 다음과 같은 결과를 확인할 수 있다.

 

이제 그냥 단순 문자열 말고 Key/Value 쌍으로 이루어진 값에 대해 반복문도 가능한데, 그럴 때 많이 사용되는 builtIn Function이 'dict2items'이다. 이건 Ansible에서 공식적으로 내장하고 있는 함수인데 다음과 같이 사용할 수 있다.

- name: Playbook
  hosts: ubuntu
  become: true
  vars:
    tags:
      Name: "Debug"
      Environment: "Test"
      Owner: "cwchoiit"
  tasks:
    - name: "Debug data"
      debug:
        msg: "{{ item.key }}: {{ item.value }}"
      loop: "{{ tags | dict2items }}"

 

변수에 tags라는 값으로 Key/Value 쌍의 데이터가 이렇게 있고 Task에 사용되는 loop로 "{{ tags | dict2items }}"라고 작성한 것을 볼 수 있다. 이 구조는 {{ exp | func }}인데, exp의 값을 func 인자로 넘겨주는 표현식이다. 그래서 인자로 넘겨받으면 item이라는 객체에 key와 value에 데이터로 들어가게 된다. 그래서 위 Playbook을 실행해보면 다음과 같은 결과를 얻는다.

Ubuntu 그룹에 호스트가 2개가 있으니까 그리고 alias로 ubuntu1, ubuntu2로 선언했기 때문에 Key/Value 한 쌍이 각각의 호스트에서 한번씩 실행되는것을 알 수 있고 msg값으로 tags에 정의한 "Key: Value"로 표현됨을 알 수 있다. 

 

그리고 이렇게 사용하는 방법을 살짝만 응용하면 다음과 같이도 작성할 수 있다. 

- name: Playbook
  hosts: ubuntu
  become: true
  vars:
    users:
      - name: jan
        shell: /bin/bash
      - name: ali
        shell: /bin/sh
      - name: cow
        shell: /bin/bash
      - name: hazard
        shell: /bin/sh
      - name: june
        shell: /bin/bash
  tasks:
    - name: "Create a user"
      user:
        name: "{{ item.name }}"
        shell: "{{ item.shell }}"
        comment: "cwchoiit-ansible"
        state: "present"
      loop: "{{ users }}"

그리고 이를 실행해보면 다음과 같은 결과를 얻는다.

 

이런 여러 방법을 통해 반복문을 만들어 낼 수 있고 이 외에 문서를 참고하면 더 많은 유스케이스가 있으니 문서를 잘 참조하면 좋을 것 같다.

 

Loops — Ansible Community Documentation

Ansible Community Documentation Ansible Loops Ansible offers the loop, with_ , and until keywords to execute a task multiple times. Examples of commonly-used loops include changing ownership on several files and/or directories with the file module, creatin

docs.ansible.com

 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Ansible Part. 7 (Condition)  (0) 2024.03.18
Ansible Part. 5 (Variables)  (0) 2024.03.18
Ansible Part. 4 (Handler)  (0) 2024.03.18
Ansible Part. 3 (Playbook)  (0) 2024.03.18
Ansible Part. 2 (Adhoc)  (3) 2024.03.17
728x90
반응형
SMALL
SMALL

 

Variables

Ansible에서 변수를 사용하는 방법은 다양하다. 그 중 몇가지를 알아보는 시간을 가져보자.

 

inventory 파일

다음과 같이 inventory 파일을 작성해보자. ubuntu 그룹에 원하는 변수를 아래처럼 이어 작성하면 된다.

[amazon]
amazon1 ansible_host=43.202.58.98 ansible_user=ec2-user 
amazon2 ansible_host=3.38.182.30 ansible_user=ec2-user

[ubuntu]
ubuntu1 ansible_host=ec2-43-201-253-181.ap-northeast-2.compute.amazonaws.com ansible_user=ubuntu user_name=cwchoiit user_comment="From inventory" user_shell=/bin/bash user_uid=7777
ubuntu2 ansible_host=ec2-3-38-192-32.ap-northeast-2.compute.amazonaws.com ansible_user=ubuntu user_name=cwchoiit user_comment="From inventory" user_shell=/bin/bash user_uid=7777

[linux:children]
amazon
ubuntu

 

이 상태로 다음 playbook을 실행해보자. 참고로 Ansible에서 변수를 사용하려면 "{{}}" 이렇게 사용하면 된다.

- name: Playbook
  hosts: ubuntu
  become: true
  tasks:
    - name: "Create a user"
      user:
        name: "{{ user_name }}"
        comment: "{{ user_comment }}"
        shell: "{{ user_shell }}"
        uid: "{{ user_uid }}"

 

ansible-playbook -i with-var.inv playbook.yaml

실행이 정상적으로 됐으면 해당 리모트 호스트에 SSH를 통해 들어가보자. 들어오면 다음 명령어를 수행

cat /etc/passwd

그럼 최하단에 다음 사용자가 추가됐음을 확인할 수 있다.

 

Playbook 내 선언

이번에는 playbook 파일 내에 선언하는 방법이다. 다음과 같이 vars라는 키에 하나씩 넣어줄 수 있다.

- name: Playbook
  hosts: ubuntu
  become: true
  vars:
    user_name: "cwchoiit"
    user_comment: "from playbook vars"
    user_shell: /bin/bash
    user_uid: "7777"
  tasks:
    - name: "Create a user"
      user:
        name: "{{ user_name }}"
        comment: "{{ user_comment }}"
        shell: "{{ user_shell }}"
        uid: "{{ user_uid }}"

 

이 상태로 playbook을 실행해보자. 물론 여기서는 inventory 파일에 선언한 변수는 지워야한다. 실행한 후 리모트 호스트에 들어가서 다시 확인해보면 다음과 같은 결과를 볼 수 있다.

 

Playbook 내 파일 경로 선언

이번엔 playbook 파일 내 선언을 따로 변수를 선언한 파일 경로로 하는 방법이다.

vars.yaml

user_name: "cwchoiit"
user_comment: "from vars.yaml file"
user_shell: "/bin/bash"
user_uid: "7777"

playbook.yaml

- name: Playbook
  hosts: ubuntu
  become: true
  vars_files:
    - vars.yaml
  tasks:
    - name: "Create a user"
      user:
        name: "{{ user_name }}"
        comment: "{{ user_comment }}"
        shell: "{{ user_shell }}"
        uid: "{{ user_uid }}"

 

이 상태로 playbook을 실행해보자. 물론 여기서도 마찬가지로 inventory 파일에 선언한 변수는 지워야한다. 실행한 후 리모트 호스트에 들어가서 다시 확인해보면 다음과 같은 결과를 볼 수 있다.

 

Playbook 실행 명령어에 변수 지정

이번엔 실행하는 명령어에 변수를 추가하는 방법이다.

실행 후 리모트 호스트에 들어가서 확인해보자. 다음과 같은 결과를 볼 수 있다.

 

근데, 이렇게 쭉 나열하기는 명령어가 너무 길어져서 지저분하다면 다음과 같이 변수 파일 경로를 변수로 줄 수도 있다.

 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Ansible Part. 7 (Condition)  (0) 2024.03.18
Ansible Part. 6 (Loop)  (0) 2024.03.18
Ansible Part. 4 (Handler)  (0) 2024.03.18
Ansible Part. 3 (Playbook)  (0) 2024.03.18
Ansible Part. 2 (Adhoc)  (3) 2024.03.17
728x90
반응형
SMALL
SMALL

Handler

핸들러는 이벤트 기반으로 동작하는 Task. 예를 들어, 지금까지는 Tasks 내부에 여러 Task를 정의하고 순차적으로 실행이 됐는데, 만약 어떤 Task가 다른 Task에 의존성이 있어야 하는 경우 그 의존성을 만족하여 실행하게 하는 게 쉽지 않다. 이를 해결하는 방법 중 하나가 Handler라고 생각하면 된다. 

 

예시를 들어보자. 만약, 내가 Nginx 서버 설정을 변경했으면 서버 설정이 변경됐으니 Nginx를 재실행해야 변경 사항이 적용되는데 서버 설정을 변경하고 Ansible이 변경을 감지했을 때 재실행하게 하고 싶을 때 Handler를 사용하면 된다.

 

실습

파일 구조가 다음과 같이 되어 있다.

여기서 default 파일이 Nginx 서버 설정 파일인데 이 파일을 보자.

default

해당 파일을 보면 index에 blue.html 파일이 가장 먼저 있다. 이러면 Nginx는 세 파일 중 blue.html을 뿌려주게 된다.

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	root /var/www/html;

	index blue.html index.htm index.nginx-debian.html;

	server_name _;

	location / {
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
		try_files $uri $uri/ =404;
	}
}

 

Playbook을 실행한 후 확인해 보자.

example.yaml

Ubuntu 대상으로만 진행하는 YAML 파일이고 이 파일을 보면 하단에 "Copy nginx configuration file" Task가 있다. 이때 서버 설정 파일을 복사해서 리모트 호스트에 Nginx 설정 파일이 위치해야 하는 곳에 복사하게 된다. 

---

- name: Example
  hosts: ubuntu
  become: true
  tasks:
  # Docs: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html
  - name: "Create a user"
    user: "name=fastcampus shell=/bin/bash"

  - name: "Hello World"
    command: "echo 'Hello World!'"

  # Docs: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html
  - name: "Add DNS server to resolv.conf"
    lineinfile:
      path: /etc/resolv.conf
      line: 'nameserver 8.8.8.8'

  # Docs: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html
  - name: "Install Nginx"
    apt:
      name: nginx
      state: present
      update_cache: true

  # Docs: https://docs.ansible.com/ansible/latest/collections/ansible/posix/synchronize_module.html
  - name: "Upload web directory"
    synchronize:
      src: files/html/
      dest: /var/www/html
      archive: true
      checksum: true
      recursive: true
      delete: true

  # Docs: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html
  - name: "Copy nginx configuration file"
    copy:
      src: files/default
      dest: /etc/nginx/sites-enabled/default
      owner: "{{ ansible_user }}"
      group: "{{ ansible_user }}"
      mode: '0644'

  # Docs: https://docs.ansible.com/ansible/latest/collections/ansible/builtin/service_module.html
  - name: "Ensure nginx service started"
    service:
      name: nginx
      state: started

  handlers:
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted

 

이대로 Playbook을 실행해 보자. 

ansible-playbook -i inventory example.yaml

 

그럼 Ubuntu의 Public IP로 접속해 보면 다음과 같이 파란 화면으로 보일 것이다. 합리적이다.

 

그럼 내가 이 상태로 Nginx 설정 파일을 다음과 같이 blue.html이 아닌 red.html로 바꾸고 다시 실행하면 어떻게 될까?

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	root /var/www/html;

	index red.html index.htm index.nginx-debian.html;

	server_name _;

	location / {
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
		try_files $uri $uri/ =404;
	}
}

 

아무런 변화가 일어나지 않을 것이다. 왜냐하면 설정 파일이 변경되어도 Nginx가 재실행되지 않으면 변경 사항을 적용하지 않을 테니까. 그러면 이럴 때 핸들러를 사용하면 된다. 이런 변화가 있을 때 핸들러가 실행되도록 말이다. 그게 바로 YAML 파일에 이 부분이다.

  handlers:
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted

 

그러나, 핸들러만 이렇게 등록한다고 핸들러가 동작하는 게 아니고 핸들러라는 이벤트를 트리거해야 한다. 그건 아래처럼 작성하면 된다.

- name: "Copy nginx configuration file"
    copy:
      src: files/default
      dest: /etc/nginx/sites-enabled/default
      owner: "{{ ansible_user }}"
      group: "{{ ansible_user }}"
      mode: '0644'
    notify:
    - Restart Nginx

 

이처럼 Nginx 설정 파일을 복사하는 Task에 notify라는 키를 추가해서 핸들러의 이름을 넣어주면, 이 Task를 작업할 때 Changed라는 상태가 나타나면 핸들러가 트리거 된다. 실행해 보자. 다음과 같이 해당 Task에서 Changed가 발생했고 그에 따라 핸들러를 트리거한다. 그럼 모든 Task가 끝나고 Handler가 실행된다.

 

이제 다시 들어가서 확인해 보자. 다음과 같이 빨간 화면이 보인다. 이게 핸들러다.

 

 

핸들러를 사용할 때 알고 있어야 하는 점

  • Playbook 내에서 같은 이벤트를 여러번 호출하더라도 동일한 핸들러는 한번만 실행된다.
  • 모든 핸들러는 Playbook 내에 모든 작업이 완료된 후에 실행된다.
  • 핸들러는 이벤트 호출 순서에 따라 실행되는 것이 아니라 핸들러 정의 순서에 따라 실행된다.

나머지는 다 말 그대로이고, 마지막 문장인 핸들러 정의 순서에 따라 실행된다는 것은 무슨말이냐면 핸들러 정의를 다음과 같이했다고 가정하자.

handlers:
  - name: Restart Nginx
    service:
      name: nginx
      state: restarted
  - name: Stop Nginx
  	service:
      name: nginx
      state: stopped

두 개의 핸들러가 이러한 순서대로 정의됐을 때, 내가 만약에 Stop Nginx를 먼저 notify로 호출하고 가장 마지막 Task에서 Restart Nginx 핸들러를 호출했다고 해도 Restart Nginx -> Stop Nginx 순으로 핸들러가 실행된다는 의미이다. 그러니까 이 부분을 주의해야한다. 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Ansible Part. 6 (Loop)  (0) 2024.03.18
Ansible Part. 5 (Variables)  (0) 2024.03.18
Ansible Part. 3 (Playbook)  (0) 2024.03.18
Ansible Part. 2 (Adhoc)  (3) 2024.03.17
Ansible Part. 1 (Inventory)  (3) 2024.03.17
728x90
반응형
SMALL
SMALL

Playbook

Ansible로 무언가를 실행하고 작업할 때 여러 방법이 있다고 했는데 그 중 Adhoc 방법을 다뤄봤고 이번에 다룰 내용이 Playbook이다.

Playbook은 YAML 파일로 되어있는데 하나씩 뜯어보자.

 

우선 디렉토리 구조는 다음과 같다.

 

Inventory

이 파일은 저번 포스팅에서 다룬 인벤토리 관련 파일이다. 이번에는 딱 하나의 파일만 존재한다.

[amazon]
amazon1 ansible_host=54.180.201.128 ansible_user=ec2-user 
amazon2 ansible_host=13.124.98.155 ansible_user=ec2-user

[ubuntu]
ubuntu1 ansible_host=ec2-43-203-218-29.ap-northeast-2.compute.amazonaws.com ansible_user=ubuntu
ubuntu2 ansible_host=ec2-3-38-149-203.ap-northeast-2.compute.amazonaws.com ansible_user=ubuntu

[linux:children]
amazon
ubuntu

 

syntax.yaml

이 파일을 통해 Playbook이 어떻게 생겼는지 알아보자.

---
# This is Ansible Playbook

# Playbook: YAML로 정의. 순서대로 정렬된 플레이(작업 목록: Play) 절차.
# Play: 작업 목록(Tasks). 특정 호스트 목록에 대하여 수행
# Task: Ansible의 수행 단위. 애드혹 명령어는 한번에 단일 작업 수행.
# Module: Ansible이 실행하는 코드 단위. 작업에서 모듈을 호출함.
# Collection: 모듈의 집합.

- name: Play 1
  hosts: ubuntu
  tasks:
    - name: "Task 1: Execute command"
      command: uptime

    - name: "Task 2: Execute script"
      script: task2.sh
    
    - name: "Task 3: Install package"
      apt:
        name: nginx
        state: present
        update_cache: true

    - name: "Task 4: Start nginx service"
      service:
        name: nginx
        state: started

- name: Play 2
  hosts: localhost
  tasks:
    - name: "Task 1: Execute command"
      command: whoami

    - name: "Task 2: Execute script"
      script: task2.sh

우선, Playbook은 YAML 파일 자체라고 보면 된다. 그래서 순서대로 정의한 Play 절차를 의미하고 이 Play는 코드에서 Tasks라고 생각하면 된다. Play는 특정 호스트 목록(그룹)에 대하여 Tasks를 수행한다. Task 하나 하나는 Ansible의 수행 단위이다. Adhoc을 사용했을 땐 한번에 하나씩 수행했지만 여기선, 여러개를 tasks라는 키에 정의하여 사용가능하다. Module은 Ansible이 실행하는 코드 단위이다. 저번 포스팅에서 배운 command, ping 이런것들도 다 모듈이다.

 

install-nginx.yaml

이 파일이 실제로 Playbook을 사용해보는 첫 YAML 파일이 될 것이다. 각 호스트 그룹 Ubuntu와 Amazon에 대해서 Nginx를 설치하는 작업을 한다. 그래서 글로벌로 패키지를 설치하려면 루트 권한이 필요하기 때문에 become: true 값이 있음을 알 수 있고, Ubuntu에 대해서는 Nginx를 설치하는 Task 하나와 설치한 Nginx를 서비스로 실행하는 Task 하나가 있다. Amazon도 같은 Task가 있지만 그 전에 패키지 매니저인 amazon-linux-extras를 먼저 Enable하는 Task가 추가로 있음을 확인할 수 있다. 

---

- name: Install Nginx on Ubuntu
  hosts: ubuntu
  become: true
  tasks:
  - name: "Install Nginx"
    apt:
      name: nginx
      state: present
      update_cache: true

  - name: "Ensure nginx service started"
    service:
      name: nginx
      state: started

- name: Install Nginx on Amazon Linux
  hosts: amazon
  become: true
  tasks:
  - name: "Enable Nginx repository provided by Amazon"
    command: "amazon-linux-extras enable nginx1"

  - name: "Install Nginx"
    yum:
      name: nginx
      state: present

  - name: "Ensure nginx service started"
    service:
      name: nginx
      state: started

 

uninstall-nginx.yaml

이번엔 Uninstall이다. 그러다보니 먼저 서비스를 중지하는 Task가 먼저 나온다. 그리고 absent라는 state를 주어 해당 패키지를 삭제한다. Amazon은 거기에 더불어 Repository를 Disable하는 작업도 있다. 

---

- name: Uninstall Nginx on Ubuntu
  hosts: ubuntu
  become: true
  tasks:
  - name: "Ensure nginx service stopped"
    service:
      name: nginx
      state: stopped

  - name: "Uninstall Nginx"
    apt:
      name: nginx
      state: absent

- name: Uninstall Nginx on Amazon Linux
  hosts: amazon
  become: true
  tasks:
  - name: "Ensure nginx service stopped"
    service:
      name: nginx
      state: stopped

  - name: "Uninstall Nginx"
    yum:
      name: nginx
      state: absent

  - name: "Disable Nginx repository provided by Amazon"
    command: "amazon-linux-extras disable nginx1"

Playbook 실행 (Install Nginx)

이제 다음 명령어를 수행해보자.

ansible-playbook -i inventory install-nginx.yaml

 

이번엔 ansible-playbook이라는 명령어를 사용한다. 그래서 인벤토리를 지정하고 Playbook을 지정하면 인벤토리에 존재하는 호스트들에 대해 install-nginx.yaml 파일을 수행하라는 의미가 된다.

 

결과는 다음과 같다. (아래 사진과 백퍼센트 동일하지 않을 수 있다. ok, changed에 대해서)

 

위에서 부터 하나씩 살펴보자. 우선 순서대로 실행된다고 했기 때문에 먼저 작성한 Ubuntu에 대해서 실행을 먼저 한 모습이다.

Gathering Facts는 기본으로 실행되는 작업이다. Facts는 상세라고 표현하는데 리모트 호스트에 대한 정보를 수집하는 과정이라고 생각하면 된다.

 

그리고 Install Nginx를 실행한다. 여기서 어떤것은 ok고 어떤것은 changed라고 되어 있다. 이게 어떤 차이냐면, ok는 설치가 되어 있는 경우에 다시 설치할 필요가 없고 그 말은 변경 사항이 없다는 것을 의미하므로 OK로 표현한다. 그러나 Changed는 없던 패키지가 설치가 됐기 때문에 변경사항이 이 리모트 호스트에 있다라고 말해주기 위해 Changed로 표현한다. 그래서 같은 Playbook을 연속해서 실행하더라도 불필요하게 패키지를 계속 설치하지 않게 Ansible이 이미 이 Task를 충족하는지 확인한다. 이를 '멱등성'이라고 표현하는데 Ansible은 멱등성을 보장한다. 그리고 Service 모듈을 통해 Nginx가 실행됐는지 확인 후 실행하지 않았다면 실행하는데, Ubuntu에서는 Nginx를 설치하면 기본으로 실행을 한다. 그래서 OK로 표현한다. Amazon 부분도 같은 맥락으로 보면 될 것 같다. 

 

이제 요약부분이 마지막에 나온다. 전부 문제 없이 원하는 작업을 수행했고, 그렇지 않았다면 unreachable, failed, skipped, ignored 중 하나가 표시된다. 여기서 amazon1에 대해서만 살펴보면 OK가 총 4개고 그 중 Changed가 1개가 있다라고 생각하면 된다. OK 4개에 Changed 1개가 아니라. 

 

그리고 위에서 멱등성을 보장한다고 했다. 진짜 그런지 확인해보려면 다시 한번 실행해보면 된다. 다시 실행해보면 전부 다 OK로 나올것이다. 

전부 다 OK로 나올것을 예상했지만 딱 한군데, Amazon에서 Enable Repository 작업이 Changed가 됐다. 이는 왜 그러냐면 기본적으로 Command라는 모듈은 멱등성을 보장하지 않는다. 그도 그럴것이 커맨드를 실행하지 않은 상태에서 실행을 하는데 멱등성이란 게 있을 수가 없다. 그래서 저 부분만 Changed고 설치나 설치 후 실행 작업은 모두 이미 충족된 상태임을 알려주는 OK가 표시됐다.

 

Adhoc으로 Playbook 정상 수행 확인

Nginx를 설치했으면 잘 설치가 됐는지 확인해보자. 다음 명령어로 간단하게 확인이 가능하다.

ansible -i inventory amazon -m command -a "curl localhost"

 

인벤토리 파일에 작성된 호스트들 중 Amazon이라는 그룹에 대해서 Command 모듈을 수행하는데 localhost:80에 요청을 날리는 작업을 수행하는 것이다. Nginx가 설치됐다면 기본으로 80 포트에 Nginx 서버가 호스팅된다. 실행해보면 Nginx 기본 페이지가 보여짐을 알 수 있다.

 

 

Playbook 실행 (Uninstall Nginx)

이제 Nginx를 Uninstall 해보자. 미리 만들어둔 uninstall-nginx.yaml 파일로 Playbook을 실행하면 된다. 잘 수행됐다. 예상대로 OK와 Changed가 나올곳에 딱딱 나왔다.

 

이제 실제로 잘 삭제됐는지 확인하기 위해 같은 CURL을 수행해보자.

ansible -i inventory [amazon|ubuntu] -m command -a "curl localhost"

 

정상 응답을 받지 못하는 것을 확인할 수 있다.

 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Ansible Part. 5 (Variables)  (0) 2024.03.18
Ansible Part. 4 (Handler)  (0) 2024.03.18
Ansible Part. 2 (Adhoc)  (3) 2024.03.17
Ansible Part. 1 (Inventory)  (3) 2024.03.17
Packer Part. 5 (Post Processor)  (2) 2024.03.15
728x90
반응형
SMALL
SMALL

이전 시간에 Inventory 파일들을 만들어보고 알아보았다. 그럼 이 파일을 어떻게 수행하고 어떻게 실행해야 하는걸까?

여러 방법이 있지만 그 중 하나인 Adhoc 방법에 대해 알아보자.

 

Adhoc

쉽게 말하면, ansible이라는 명령어를 통해 수행하는 CLI 방식이라고 생각하면 된다. 형식은 다음과 같이 생겼다.

ansible [host-pattern] [-m module] [-a 'module options'] [-i inventory]
  • host-pattern: 그룹명을 의미한다. 
  • -m module: Adhoc 명령어를 통해 실행할 ansible 모듈을 의미한다.
  • -a module options: 모듈에 특정 옵션을 부여할 경우
  • -i inventory: 특정 인벤토리 파일의 경로

ping 모듈

그럼 다음 명령어 사용 방법을 기반으로 한번 아래를 수행해보자.

ansible -i amazon.inv -m ping all

참고로 위 사용 방법과 순서가 뒤죽박죽으로 되어 있다. 즉, 순서에 영향을 받지 않는다는 의미이다. 지금은 인벤토리 파일이 먼저 왔고 모듈이 그 다음에 왔고 호스트 패턴(그룹)이 제일 마지막에 왔음을 확인할 수 있다.

 

아무튼 이 명령어를 수행하면 다음과 같은 에러를 마주한다. 이전 포스팅에서 설명했지만, Ansible은 SSH로 접속할 수 있어야 하기 때문에 접속 정보가 필요한데, Amazon EC2의 유저 정보는 ec2-user이다. 그러나, 우리가 amazon.inv 파일에서는 별칭이나 ansible_user를 정의하지 않았기 때문에 현재 로컬 사용자로 접속하려고 시도한다. 당연히 없을것이다 해당 유저는. 그래서 문제가 발생한다.

 

그럼 amazon.inv 파일을 그대로 사용할 경우 유저 정보를 기입해줘야 하는데 다음과 같이 사용가능하다.

ansible -i amazon.inv -m ping all -u ec2-user

-u 옵션을 통해서 유저 정보를 줄 수 있다. 이것을 어떻게 알아냈냐면 그냥 -h 옵션으로 사용가능한 옵션을 확인해보면 된다. 이렇게 수행하면 다음과 같이 정상 결과를 돌려 받는다.

 

근데, 여기서 정상 결과를 돌려 받은 이유는 나는 ssh-agent라는 것을 사용해서 내 .pem 파일을 SSH 키 보관 장소에 보관했기 때문에 해당 EC2에 접근 가능한 Key pair 정보를 알려주지 않고도 접속이 가능한 것이다. ssh-agent를 사용하지 않는다면 --private-key라는 옵션을 주어서 .pem 파일 경로를 지정해줘야한다. ssh-agent를 사용한다면 .pem 파일을 등록해서 사용하면 굳이 SSH로 접속할 때마다 키 정보를 알려주지 않아도 되기 때문에 편리하다. 다음이 SSH-Agent가 키를 보관하는 저장소에 추가하는 방법이다.

ssh-add -K /path/to/.pem/

 

참고로 여기서 사용한 ping 모듈은 우리가 흔히 아는 ping이랑 다르다. 여기서 ping 모듈은 하는 작업이 2개다. 1. 대상 호스트에 연결 2. 파이썬 사용가능 여부 확인. 이렇게 두 가지를 해주는 모듈이다. 실제로 저 성공 결과 이미지를 보면 discovered_interpreter_python이라는 키에 파이썬 경로가 보여지는것을 알 수 있다. 이 작업을 하는 이유는 'Ansible'을 이용하게 되면 Control Node, Managed Node 이렇게 두 가지가 있다. 

Control Node는 지금 이 명령어를 수행하는 로컬 PC를 의미한다. 반면, Managed Node는 Ansible이 작업하는 서버인 AWS EC2를 의미한다. 이 예제에선 Amazon EC2를 말한다고 보면 된다. 당연히 Control Node에는 Ansible이 설치되어 있어야 하고 Managed Node는 파이썬이 설치가 되어 있어야 한다. 그래서 ping 모듈이 파이썬이 설치됐는지 확인한다.

 

command 모듈

이번엔 command라는 모듈을 사용해보자. 이는 Managed Node 내부에서 주어진 커맨드를 실행하는 모듈이다.

ansible -i vars.inv -m command -a "uptime" ubuntu

 

이번엔 vars.inv 파일을 사용해서 ansible_user도 작성이 된 파일을 사용하자. 따로 -u 옵션을 사용하기 귀찮으니까.

그리고 command 모듈을 사용해서 "uptime" 이라는 명령어를 ubuntu 그룹에만 수행하자. 

잘 수행됐음을 알 수 있다.

 

apt 모듈

이번엔 Ubuntu의 패키지 매니저인 apt-get을 사용할 수 있는 apt 모듈을 사용해서 패키지를 내려받아보자.

ansible -i vars.inv -m apt -a "name=git state=latest update_cache=yes" ubuntu

저렇게 하면 다음과 같은 에러가 발생할 것이다. 이는 무슨 에러냐면 호스트에 글로벌로 패키지를 설치하려면 기본적으로 root 권한이 있는 사용자가 설치해야 한다. 그러나 지금 vars.inv 파일에 기입된 유저는 ubuntu이므로 아래와 같은 권한 에러가 발생하는 것.

이를 해결하기 위해 다음과 같이 옵션을 붙일 수 있다.

ansible -i vars.inv -m apt -a "name=git state=latest update_cache=yes" ubuntu --become

--become 옵션은 사용자를 변경하는 옵션인데 기본적으로 root 사용자로 변경한다. 이렇게 실행해보자.

 

다음과 같이 잘 수행되었다. 이제 위에서 배운 command 모듈로 실제로 git이 잘 설치됐는지 확인해보자. 각각의 호스트에 이렇게 잘 설치됐음을 확인할 수 있다.

 

이 반대로 패키지를 설치할수도 있지만 삭제할수도 있다. 그럴땐 이렇게 작성하면 된다. state=absent는 해당 패키지를 제거한다는 의미이다. 이 명령어를 수행해보자.

ansible -i vars.inv -m apt -a "name=git state=absent update_cache=yes" ubuntu --become

 

잘 삭제됐는지 다시 한번 git 명령어를 사용해보자. 아래처럼 git을 찾을 수 없다고 나온다. 

 

 

결론

Ansible을 사용하는 방법 중 하나인 명령어로 수행하는 방식 'Adhoc' 방식을 알아보았다.

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Ansible Part. 4 (Handler)  (0) 2024.03.18
Ansible Part. 3 (Playbook)  (0) 2024.03.18
Ansible Part. 1 (Inventory)  (3) 2024.03.17
Packer Part. 5 (Post Processor)  (2) 2024.03.15
Packer Part. 4 (Data Source)  (0) 2024.03.14
728x90
반응형
SMALL
SMALL

 

이제 서버 형상 관리 도구인 "Ansible"을 사용해보자. Ansible을 사용하기에 있어 가장 먼저 이해해야 하는 개념은 Inventory이다.

 

Inventory

인벤토리란 Ansible을 사용하는 것은 특정 서버에 대해 형상 관리를 하기 위함인데 그렇다는 것은 특정 서버에 대한 정보가 필요하다. 그 정보를 관리하는 파일을 'Ansible'에서는 Inventory라고 한다.

 

이 Inventory는 그룹 기능을 지원하는데 예를 들어 내가 Linux 운영체제의 배포판 중 하나인 Ubuntu 서버를 3개를 관리하고 있다고 하면 Ubuntu라는 그룹을 만들어서 해당 그룹에 3개의 서버를 모두 할당할 수 있다. 그럼 그룹으로 실행해야 하는 명령어나 상태 체크같은 것들이 가능해지니 효율적인 관리가 될 수 있겠다.

 

이 인벤토리는 크게 두 가지 종류가 있다.

  • Static Inventory
  • Dynamic Inventory

Static inventory는 말 그대로 정적 인벤토리고 변경되지 않는 서버에 대한 관리를 할 때 사용한다고 보면 된다. 가장 기본이 되는 방식이고, Dynamic inventory는 AWS와 같은 클라우드 기반 서버의 IP는 수시로 변경될 수 있고 Auto Scaling과 같은 기능을 사용해서 없던 서버가 새로 생기는 경우도 비일비재하다. 이럴 때 동적으로 관리가 가능하게 해주는 방법이 Dynamic inventory라고 보면 되겠다.

 

Inventory 소스 작성과 이해

그러면 이제 실제로 AWS에 EC2 인스턴스 총 4개를 가지고 연습을 해보자.

우선 이전에 Terraform을 배웠을 때 Network, EC2를 생성하는 코드를 통해 총 4개의 EC2를 만들것이다.

 

이건 이전에 테라폼에 대한 포스팅에 올라와있으니 참고하면 되고 이미 생성됐다는 가정하에 진행하도록 하겠다.

4개 EC2를 테라폼을 통해 만들고 생성한 Outputs 결과는 다음과 같다.

 

이 정보들을 가지고 Inventory 파일들을 만들어보자. 우선 내 디렉토리 구조는 다음과 같이 생겼다. 참고로 인벤토리 파일의 확장자는 필요하지 않다. 그저 구분짓기 위해 작성했다고 보면 된다.

 

amazon.inv

이 파일은 amazon instance의 Public IP가 기록되어 있다.

3.38.149.218
52.78.112.88

 

ubuntu.inv

이 파일은 ubuntu instance의 Public DNS가 기록되어 있다. 이렇게 IP와 DNS를 사용하는 경우 둘 다 가능하다는 것을 보여주기 위해 작성했다.

ec2-3-34-189-200.ap-northeast-2.compute.amazonaws.com
ec2-54-180-160-242.ap-northeast-2.compute.amazonaws.com

 

simple.inv

이 파일은 위에서 말한 그룹 기능을 사용한 파일이다. 대괄호로 그룹을 지정하고 그 하위에 그룹의 대상이 되는 녀석들을 작성하면 된다.

참고로 Ansible inventory에서는 기본으로 가지고 있는 그룹이 있는데 'all' 이라는 그룹이다. 이 그룹은 inventory에 정의되어 있는 모든 그룹을 포함하고 있다고 보면 된다.

[amazon]
3.38.149.218
52.78.112.88
[ubuntu]
ec2-3-34-189-200.ap-northeast-2.compute.amazonaws.com
ec2-54-180-160-242.ap-northeast-2.compute.amazonaws.com

 

alias.inv

이 파일은 IP나 DNS값을 기억하기가 까다롭기 때문에 별칭을 사용해서 해당 값을 치환해 놓은 파일이다. 여기서 별칭은 (amazon1, amazon2, ubuntu1, ubuntu2)가 되고 이 별칭들은 당연히 Unique 값이어야 한다. 그리고 이 별칭에 대해서 ansible_host라는 변수에는 IP나 DNS값이 들어가면 된다. 그럼 이렇게 만들어 놓으면 추후에 3.38.149.218을 지칭하는 대신에 amazon1이라는 별칭으로 표현해주기 때문에 가시성에서 효율적일 수 있다.

[amazon]
amazon1 ansible_host=3.38.149.218
amazon2 ansible_host=52.78.112.88
[ubuntu]
ubuntu1 ansible_host=ec2-3-34-189-200.ap-northeast-2.compute.amazonaws.com
ubuntu2 ansible_host=ec2-54-180-160-242.ap-northeast-2.compute.amazonaws.com

 

vars.inv

이 파일은 변수를 추가하는 파일이다. 그래서 보면 없던 값인 ansible_user라는 값이 있다. 이는 무엇이냐면 Ansible은 Agentless하기 때문에 SSH나 WinRM을 통해 원격으로 명령을 수행하고 특정 작업을 진행한다. 그러려면 접속 유저 정보가 필요하다. Amazon EC2는 기본 사용자가 'ec2-user'이고 Ubuntu는 'ubuntu'이다. 그래서 이런 사용자를 지정하지 않고 만약 Ansible이 어떤 명령을 수행하기 위해 SSH접속을 할 때 로컬 PC의 현재 사용자와 동일한 사용자로 접속하고는 한다. 그러면 문제가 발생하기 때문에 이렇게 ansible_user라는 유저가 필요한 것이다.

[amazon]
amazon1 ansible_host=3.38.149.218 ansible_user=ec2-user 
amazon2 ansible_host=52.78.112.88 ansible_user=ec2-user

[ubuntu]
ubuntu1 ansible_host=ec2-3-34-189-200.ap-northeast-2.compute.amazonaws.com ansible_user=ubuntu
ubuntu2 ansible_host=ec2-54-180-160-242.ap-northeast-2.compute.amazonaws.com ansible_user=ubuntu

[linux:children]
amazon
ubuntu

 

그리고 하위에 작성한 [linux:children]이라는 표기가 있는데 이는 하위 그룹을 의미하는데 Linux라는 그룹이 amazon, ubuntu라는 그룹을 포함한다라는 의미라고 생각하면 된다.

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

Ansible Part. 3 (Playbook)  (0) 2024.03.18
Ansible Part. 2 (Adhoc)  (3) 2024.03.17
Packer Part. 5 (Post Processor)  (2) 2024.03.15
Packer Part. 4 (Data Source)  (0) 2024.03.14
Packer Part. 3 (Provisioner)  (0) 2024.03.14
728x90
반응형
SMALL
SMALL

 

Packer란

Packer는 이미지 빌더 도구이다. 여기서 이미지란 Docker나 AWS EC2 Image와 같은 그 이미지를 말한다. AMI 같은 경우 AWS에서 사용할 수 있지만 이 Packer는 이미지를 여러 플랫폼에서 사용할 수 있게 만들어준다.

 

 

Packer 설치하기

공식 문서를 참조하면 각자의 운영체제에 맞게 설치할 수 있다.

 

Install | Packer | HashiCorp Developer

Explore Packer product documentation, tutorials, and examples.

developer.hashicorp.com

 

나는 macOS 사용중이므로 다음 명령어를 통해 설치한다.

brew tap hashicorp/tap
brew install hashicorp/tap/packer

 

설치가 다 됐으면 버전 확인을 해보자.

 

 

Packer 자동 완성 기능 설정

packer -autocomplete-install

 

명령어 수행 후 본인의 쉘 설정 파일에 들어가서 최하단에 다음 문장 확인.

complete -o nospace -C /usr/local/bin/packer packer

 

쉘 다시 킨 후 "packer"를 입력하고 tab을 눌러 자동완성 기능 적용 확인.

 

 

Ansible 설치하기

다음 명령어를 수행하자.

brew install ansible

 

설치가 됐으면 버전 확인.

 

728x90
반응형
LIST

'IaC(Infrastructure as Code)' 카테고리의 다른 글

AWS + Terraform (Module)  (0) 2024.03.06
AWS + Terraform  (2) 2024.03.05
Terraform 소개 및 간단하게 다루어보기  (0) 2024.03.05
Terraform 설치 및 설정하기  (2) 2024.03.05
IaC 개요  (2) 2024.03.05

+ Recent posts