ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Ansible] 환경구축 및 기본사용(스터디 1주차)
    Infra/Cloud 2024. 1. 10. 21:37

     

    오랜만에 돌아온 Gasida님 스터디!! 이번에는 Ansible이다.

    사용해보니 굉장히 편리하고 활용도를 높게 사용할 수 있을 거 같단 생각이 들었다(뭔가 이득본 기분...??)

    인프라를 구축하는 분들 뿐만 아니라 보안업무 측면에서도 활용도가 높을 것 같다.

    (예를 들어 굳이 힘들게 서버를 일일이 들어가서 제어하는 것이 아닌 한번에 제어가 가능하다)

     


    Ansible이란??

    - IT 자동화 도구로Ansible과 Python만 설치할 수 있다면 플레이북(yaml 형식) 작성을 통해  IT업무를 여러 환경에서 동일하게 적용 및 실행 할 수 있는 오픈소스이다.

     

     

    Ansible의 특징

    - Agentless:

    Ansible의 경우 관리노드에 Agent를 설치할 필요가 없으며 SSH를 통해 관리노드에 명령을 전달

     

    - Idempotent: 

    멱등성을 가지기 때문에 여러번 시도해도 항상 같은 결과를 낸다.

     

     

    Ansible 환경 구축

    - Requirement:

    Python, Ansible

    apt install ansible -y
    apt install python3 -y

     

     

    - Options:

    Amazon EC2, VScode

     

    AWS Management Console

    AWS Support 플랜은 AWS로 성공하는 데 도움이 되는 다양한 도구, 프로그램 및 전문 지식에 대한 액세스의 조합을 제공합니다.

    aws.amazon.com

     

    Download Visual Studio Code - Mac, Linux, Windows

    Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows. Download Visual Studio Code to experience a redefined code editor, optimized for building and debugging modern web and cloud applications.

    code.visualstudio.com

     

    => 스터디에서는 추가적으로 VScode를 통해 코드작성 환경을 구축했으며 Node의 경우 Amazon EC2 Instance를 사용했다(자신이 편한 환경으로 구축해서 사용하면 될 것 같다)

     

     

    Ansible 사용

    위의 그림에도 존재하지만 사용 시, 중요하게(?) 알아둬야할 것들이 몇 가지 존재한다.

    - SSH

    - Inventory

    - ansible.cfg(Ansible 환경설정)

    - playbook

    - etc..

     


     

    SSH(Secure Shell Protocol)

    # Create SSH Keypair
    ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa

    => 사실 SSH에 대해서는 대부분 알고 있고 많이 사용중이겠지만 환경 구축 시에 미리 설정해주는게 좋기 때문에 먼저 언급하기로 했다. 많은 분들이 이미 SSH를 사용하고 있기 때문에 어느정도 사용법은 알고 있을 것이다. SSH 사용에는 크게 ID/Passwd based와 패스워드를 굳이 입력하지 않는 방법으로 많이 알려진 Key based 방법이 있다.

     

     

    - KeyPair 생성

    => 위와 같이 keypair를 생성하고 id_rsa.pub 내용을 Ansible 명령을 받을 Managed node의 authorized_keys 파일 내에 해당 값을 추가해주면 된다. 이는 명령어를 통해 복사를 수행해주었다. 참고로 id_rsa.pub 값의 끝에는 해당 명령이 실행된 "계정"@"hostname" 형식이 작성되어있다. 

     

     

    - 각 node의 root 의 .ssh/authorized_keys 복사

    => 각 node의 authorized_keys파일에 정상적으로 id_rsa.pub 값이 복사된 것을 볼 수 있었다.


    [참고] ubuntu 계정으로 ping module 명령이 실패한 이유

    ansible -m ping web -u ubuntu

    => 이유는 ubuntu 계정의 /home/ubuntu/.ssh/authorized_keys 파일 내에 위에서 생성한 root의 id_rsa.pub값이 존재하지 않기때문이다. 때문에 ubuntu 계정으로 명령을 진행해 SSH 연결을 시도했으나 public key 값이 등록되지 않았으므로 connect에 실패하게 된다. 

     

    => SSH 관련 확인이 필요한 부분이 발견되어서 확인되는데로 추가예정(--ask-pass 옵션 시, 패스워드 미입력에도 접속됨)

     

    - root의 id_rsa.pub 값을 ubuntu 계정의 /home/ubuntu/.ssh/authorized_keys 파일에 복사

    해결방법은 당연히 간단하다.

    for i in {1..3}; do ssh-copy-id ubuntu@tnode$i; done
    for i in {1..3}; do echo ">> tnode$i <<"; ssh tnode$i cat /home/ubuntu/.ssh/authorized_keys; echo; done

    => 실패했던 "ansible -m ping web -u ubuntu" 명령이 정상적으로 성공한 것을 볼 수 있었다.

     


     

     Inventory

    - 텍스트 파일로 Ansible이 자동화 대상으로 하는 Host를 지정

    - /etc/hosts 파일에 IP와 Domain이 mapping되어있다면 Domain명으로도 지정이 가능하다.

    - 그 밖에도 inventory는 다양한 형태로 그룹을 설정할 수 있다.

    cat <<EOT > inventory
    10.10.1.11
    10.10.1.12
    10.10.1.13
    EOT
    
    ---
    
    cat <<EOT > inventory
    tnode1
    tnode2
    tnode3
    EOT
    
    ---
    
    cat <<EOT > inventory
    [web]
    tnode1
    tnode2
    
    [db]
    tnode3
    
    [all:children]
    web
    db
    EOT

     

     

     

     Inventory 구성

    - 그룹별: "[ ]" 내에 그룹명을 작성한 뒤 해당 그룹에 속하는 Host명이나 IP를 작성(하나의 Host가 여러 그룹에 속해도 무방)

    - 중첩 그룹: 기존에 정의한 Host 그룹을 다른 Host 그룹에 포함시킬 수 있으며 이때 ":children"이라는 접미사를 추가

    [default]
    # 아래의 내용을 범위로 지정 가능 -> 192.168.1.[1:3]
    192.168.1.1
    192.168.1.2
    192.168.1.3
    
    [dns]
    [a:c].dns.example.com # a.dns.example.com , b.dns.example.com , c.dns.example.com 을 의미함
    
    [dev]
    # 아래의 내용을 범위로 지정 가능 -> hi[01:02].exmple.com
    hi01.example.com
    hi02.example.com
    
    [stage]
    sleep01.example.com
    sleep02.example.com
    
    [qa]
    bye01.example.com
    bye02.example.com
    
    [prod]
    hi01.example.com
    bye02.example.com
    sleep01.example.com
    
    [test:children]
    dev
    qa
    stage

     

     

     

    - Inventory 검증

    ansible-inventory -i ./inventory --list | jq
    ansible-inventory -i ./inventory --graph

     


    ansbile.cfg

    - 매번 위의 방법처럼 -i 옵션을 이용하는 것은 불편함. 그래서 사용하는 것이 ansible.cfg라는 Ansible 환경설정 파일

    - 이 밖에도 ansible config를 적용하는 방법은 다양하다.

    - key : value 타입으로 구성됨

    # ansible.cfg 파일 생성
    cat <<EOT > ansible.cfg
    [defaults]
    inventory = ./inventory
    EOT
    
    # inventory 목록 확인
    ansible-inventory --list | jq

     

     

    [참고] ansible config 적용 방법 및 우선 순위

     

    Ansible Configuration Settings — Ansible Documentation

    ['bud-frogs', 'bunny', 'cheese', 'daemon', 'default', 'dragon', 'elephant-in-snake', 'elephant', 'eyes', 'hellokitty', 'kitty', 'luke-koala', 'meow', 'milk', 'moofasa', 'moose', 'ren', 'sheep', 'small', 'stegosaurus', 'stimpy', 'supermilker', 'three-eyes',

    docs.ansible.com

    1. ANSIBLE_CONFIG (environment variable if set)
    2. ansible.cfg (in the current directory)
    3. ~/.ansible.cfg (in the home directory)
    4. /etc/ansible/ansible.cfg

     

    - 스터디에서는 아래와 같이 ansible.cfg를 구성해서 사용했다.

    [defaults]
    inventory = ./inventory
    remote_user = root
    ask_pass = false
    
    [privilege_escalation]
    become = true
    become_method = sudo
    become_user = root
    become_ask_pass = false

     


    Ansible module을 통한 명령

    - ping 모듈

    ansible -m ping all
    ansible -m ping web
    ansible -m ping web -u ubuntu
    ansible -m ping web -u ubuntu --ask-pass

     

     

    - shell 모듈

    ansible -m shell -a 'hostname' all
    ansible -m shell -a 'pwd' all

     

    Playbook

    - 위의 방법처럼 module을 이용한 명령어 실행으로 각각의 node들을 제어할 수 도 있지만 yml 형식의 파일을 미리 작성해 사용할 수 있다. 각각의 명령 실행을 play라고 한다면 이를 책처럼 묶어서 사용하는 의미가 playbook이라고 할 수 있다.

    - 참고로 ansible cli를 사용하여 하나 이상의 managed node에 대한 단일 작업을 자동화 명령을 'ad-hoc' 이라고 한다.

    - 사용법은 ansible-playbook 명령어를 통해 작성된 playbook yml 파일 내용을 실행한다.

     

    Creating a playbook — Ansible Documentation

    © Copyright Ansible project contributors. Last updated on Jan 11, 2024.

    docs.ansible.com

    # first-playbook.yml
    ---
    - hosts: all
      tasks:
        - name: Print message
          debug:
            msg: Hello CloudNet@ Ansible Study

    => 스터디에서 진행한 기본예제이다.

     

    ansible-playbook first-playbook.yml

     

     

     

    Playbook 변수

    - 종류: 호스트, 그룹, 플레이, 추가, 작업 변수 등이 존재

    - 우선순위: 추가변수(실행 시 파라미터) > 플레이 변수 > 호스트 변수 > 그룹 변수

    [web]
    tnode1
    tnode2
    
    [db]
    tnode3 user=ansible1
    
    [all:children]
    web
    db
    
    [all:vars]
    user=ansible

     

     

    1. 그룹변수

    - inventory 파일에 선언. 현재 예시로는 all 그룹에서 user라는 변수를 사용할 수 있게됨

    [all:vars]
    user=ansible

     

     

    - playbool yml 파일에서 변수 사용(띄어쓰기 주의)

    ---
    
    - hosts: all
      tasks:
      - name: Create User {{ user }}
        ansible.builtin.user:
          name: "{{ user }}"
          state: present

    => 멱등성 특징으로인해 만약 해당 계정(ansible)이 이미 존재하더라도 에러가 아닌 최종형태가 동일한 결과로 나타남.

     

     

     

    2. 호스트 변수

    - inventory 파일내 호스트 옆에 변수 선언

    [db]
    tnode3 user=ansible1

     

     

    -  사용 시,  해당 호스트가 포함된 그룹 또는 호스트로 작성

    ---
    
    - hosts: db # <- 이 부분에 그룹 또는 호스트 
      tasks:
      - name: Create User {{ user }}
        ansible.builtin.user:
          name: "{{ user }}"
          state: present

     

     

    3. 플레이변수

    - playbook yml 내 hosts 아래에 "vars"를 추가해서 사용(file로도 가능하며 아래의 도전과제1 참고)

    ---
    
    - hosts: all
      vars:
        user: ansible_test
        
      tasks:
      - name: Create User {{ user }}
        ansible.builtin.user:
          name: "{{ user }}"
          state: present

     

     

    4. 추가변수

    - ansible-playbook 실행 시, parameter로 전달하는 변수( extra의 의미인 "-e"  옵션을 이용)

    ansible-playbook -e user=ansible create-user.yml

     

     

    5. 작업변수

    - playbook의 수행결과를 저장할 수 있는 변수로 특정 작업 후, 그 결과를 이용하여 후속작업 시, 주로 사용

    - "register: " 문구를 이용하여 사용함

    ---
    
    - hosts: db
      tasks:
      - name: Create User {{ user }}
        ansible.builtin.user:
          name: "{{ user }}"
          state: present
        register: result
      
      - ansible.builtin.debug:
          var: result

     

     


     

    Ansible Vault

    - Password, API key 등 중요한 정보 및 Credential 값이 ansible-playbook에는 text로 저장되며 접근권한이 있다면 누구나 확인이 가능함. 때문에 이를 통한 유출을 막기위해 데이터 파일을 암/복호화 할 수 있는 기능을 제공함

    ansible-vault create secret.yml

    => 해당 명령 진행 시, 해당 파일을 암호화할 때 사용할 passwd를 입력받으며, 입력이 끝나면 yml 파일 내용을 작성할 수 있음. 작성된 내용은 "cat" 명령어로는 암호화되어 확인이 불가

     

     

    암호화된 파일 보기

    ansible-vault view secret.yml

    => 위에서 암호화한 파일의 내용을 해당 명령과 Vault 패스워드를 통해 확인할 수 있음

     

     

    파일을 이용한 암복호화

    - 패스워드 파일을 만들어서 암복호화를 진행할 수도 있다.

    echo 'my-passwd' > vault-pass
    
    ansible-vault create --vault-pass-file ./vault-pass secret.yml
    ansible-vault view --vault-pass-file ./vault-pass secret.yml

    => 패스워드가 작성된 파일을 잘 관리해줘야 한다는 번거로운 점이 있을 수 있겠으나 대량의 중요파일들에 대해 패스워드를 설정하거나 변경할 때 편리할 것 같다.

     

     

    기존파일 암호화 

    - 기존에 만들어놓은 파일에 대해 암호화 및 복호화가 가능

    ansible-vault encrypt original_file.yml

    => 해당 명령 진행 시, 패스워드를 입력해주면 된다.

     

    ansible-vault decrypt var_file.yml --output=var_decrypted.yml

    => 복호화 시, 암호화된 파일은 그대로 두고 복호화한 파일만 따로 생성할 수 도 있다.

     

     

    패스워드 변경

    - 직접 입력해서 변경이 가능하며 파일로도 가능하다.

    ansible-vault rekey mysecret.yml
    ansible-vault rekey --new-vault-password-file=./vault-pass mysecret.yml

     

    playbook 진행 시, 암호화된 파일 사용법

    - 일반적인 ansible-playbook으로는 에러가 발생한다.

    - '--vault-id @promt' 옵션을 추가해주고 패스워드를 입력하면 정상적으로 동작한다.

    ansible-playbook --vault-id @prompt create-user.yml

     

     

    * 에러발생

     

     

    * 정상동작

     


    Facts

    - Ansible이 관리 호스트에서 자동으로 검색한 변수(자동 예약 변수)를 의미

    - Facts에는 아래와 같은 호스트별 정보가 담겨있음

    - 사용 표기법은 많이 알아두면 좋지만 ansible_facts 전체를 출력해서 보고 사용해도 무방할 것 같음.

    - ansible_facts.* 네임스페이스 표기법 사용을 권장

    - 사용자 지정 fact도 만들 수 있음

    • 호스트 명, 커널 버전, 네트워크 인터페이스 명, 운영체제 버전, CPU 개수, 사용 가능한 메모리, 스토리지 장치의 크기 및 여유 공간 etc...

    => 화면에는 다보이지 않지만 굉장히 많은 정보를 json 형식으로 볼 수 있다.

     

     

    * 구버전 fact 문법 비활성화

    - 구버전 사용은 권장하지 않으며 ansible.cfg 파일에 옵션을 추가하여 비활성화가 가능하다(infect_facts_as_vars=false)

    [defaults]
    inventory = ./inventory
    remote_user = root
    ask_pass = false
    inject_facts_as_vars = false
    
    [privilege_escalation]
    become = true
    become_method = sudo
    become_user = root
    become_ask_pass = false

     

     

    * facts 수집 비활성화

    - fact 수집으로 인한 호스트 부하를 줄이고 싶거나 굳이 fact 수집이 필요하지 않은 경우 사용

    - 참고로 facts 수집을 비활성화 한상태에서 해당 facts를 사용하려하면 당연히 에러가 난다)

    ---
    
    - hosts: db
      gather_facts: no # <- 수집 비활성
    
      tasks:
      - name: Print all facts
        ansible.builtin.debug:
          msg: >
            The default IPv4 address of {{ ansible_facts.hostname }}
            is {{ ansible_facts.default_ipv4.address }}

     

     

    * Manual 하게 facts 수집 비활성화

    - hosts 레벨에서 facts를 비활성화하되 tasks 레벨에서 수집할 수 있게 설정도 가능하다.

    - hosts: db
      gather_facts: no
    
      tasks:
      - name: Manually gather facts
        ansible.builtin.setup:
    
      - name: Print all facts
        ansible.builtin.debug:
          msg: >
            The default IPv4 address of {{ ansible_facts.hostname }}
            is {{ ansible_facts.default_ipv4.address }}

     

     

    * 사용자 지정 fact 만들기 & 수집

    - host의 /etc/ansible/facts.d 디렉터리 내에 *.fact 형식의 파일을 생성하여 수집하고 싶은 facts를 정의하고 수집이 가능하다.

    # 관리 호스트에 facts.d 디렉터리 생성
    mkdir /etc/ansible/facts.d
    
    # my-custom.fact 파일 생성
    cat <<EOT > /etc/ansible/facts.d/my-custom.fact
    [packages]
    web_package = httpd
    db_package = mariadb-server
    
    [users]
    user1 = ansible
    user2 = k8s-ho
    EOT

     

     

    - Playbook 예시

     

    ---
    
    - hosts: localhost
    
      tasks:
      - name: Print all facts
        ansible.builtin.debug:
          var: ansible_local

     

     


     

    도전과제1

    - 생성된 user를 ansible.builtin.user 모듈을 통해 제거해보자.

    - 참고로 Ansible의 변수선언은 다양하게 가능하며 아래와 같은 우선순위를 가진다.

    추가변수(실행 시 파라미터) > 플레이 변수 > 호스트 변수 > 그룹 변수

    => 도전과제1에서 나의 경우 별도 파일을 분리한 플레이 변수를 사용하였다.

     

     

    * 계정 생성

    ansible-playbook create-user.yml
    # var_file.yml
    user1: ansible1
    user2: ansible2
    user3: ansible3
    
    
    # create_user.yml
    ---
    
    - hosts: all
      vars_files:
        - var_file.yml
    
      tasks:
        - name: Create User {{ user1 }}
          ansible.builtin.user:
            name: "{{ user1 }}"
            state: present
        - name: Create User {{ user2 }}
          ansible.builtin.user:
            name: "{{ user2 }}"
            state: present
        - name: Create User {{ user3 }}
          ansible.builtin.user:
            name: "{{ user3 }}"
            state: present

    => 삭제를 위해 우선 계정을 생성해주었다.

     

     

    * 계정삭제

    - 변수는 이미 작성되어있으므로 삭제로만 수정진행

    ansible-playbook remove-user.yml
    # remove-user.yml
    ---
    
    - hosts: all
      vars_files:
        - var_file.yml
    
      tasks:
        - name: Remove User {{ user1 }}
          ansible.builtin.user:
            name: "{{ user1 }}"
            state: absent
        - name: Remove User {{ user2 }}
          ansible.builtin.user:
            name: "{{ user2 }}"
            state: absent
        - name: Remove User {{ user3 }}
          ansible.builtin.user:
            name: "{{ user3 }}"
            state: absent

    => 깔끔하게 삭제가 완료되었다.

     

    도전과제2

    - 관리대상에 uptime을 ansible.builtin.debug 모듈을 통해 확인해보자

    # uptime.yml
    ---
    - hosts: all
      tasks:
        - name: Get uptime to Node
          ansible.builtin.shell: /usr/bin/uptime
          register: result
    
        - name: Show uptime
          ansible.builtin.debug:
            msg: "{{ result.stdout }}"

    => 관리 node별 uptime을 확인할 수 있었다.

     

    도전과제3

    - 팩트를 사용하여 3개의 EC2 '커널버전'과 '운영체제 종류'를 출력해보자.

    # fact.yml
    ---
    
    - hosts: all
      tasks:
        - name: facts data collecting
          ansible.builtin.debug:
            #var: ansible_facts
            msg: >
              The OS of this VM is {{ ansible_facts.distribution }} {{ ansible_facts.distribution_version  }}
              and the Kernel version is {{ ansible_facts.kernel }}
    ansible-playbook fact.yml

    => 각각 VM별 OS 종류와 Kernel version을 볼 수 있었다. ansible_facts가 수집된 내용을 확인한 뒤에 필요한 데이터만 파싱해서 사용하면 된다. 

    반응형

    댓글

Designed by Tistory.