Ansible Playbook 심화 (vault, Facts, include_tasks, 조건문)


vagrantfile의 ansible server 부분에 

cfg.vm.provision "shell", inline: "yum -y install epel-release" 추가

그리고 이제 window는 주석처리

=begin 부터 =end까지 한번에 주석 가능

더보기
# --- Ansible Server ---

Vagrant.configure("2") do |config|
	config.vm.define "my-ansible-server" do |cfg|
		cfg.vm.box = "centos/7" 
		cfg.vm.provider "virtualbox" do |vb|
			vb.name = "my-ansible-server"
			vb.cpus = 2
			vb.memory = 4096
			vb.gui = false
		end
		cfg.vm.host_name = "control.example.com"
		cfg.vm.network "private_network", ip: "192.168.110.10"
		cfg.vm.provision "shell", path: "ssh_conf.sh" 
		cfg.vm.synced_folder "../data", "/vagrant", disabled: true
        ############# 이부분 추가
		cfg.vm.provision "shell", inline: "yum -y install epel-release" 
        #########################
		cfg.vm.provision "shell", inline: "yum -y install centos-release-ansible-29.noarch"
		cfg.vm.provision "shell", inline: "yum install ansible -y"
		cfg.vm.provision "file", source: "ansible_env_ready.yml",
			destination: "ansible_env_ready.yml"
		cfg.vm.provision "shell", inline: "ansible-playbook ansible_env_ready.yml"	
			
	end
... 생략

ansible_env_ready.yaml 에서도 hosts파일에 있는 윈도우는 지우고(주석말고 지우기)

Generate sshkey 구문을 추가. (known_hosts를 직접 처리하기위해)

추가 안하면 ssh node1 이런식으로 접속하면서 fingerprint를 직접 등록해야 한다.

더보기
- name: setup for the ansible environment
  hosts: localhost
  gather_facts: no
  
  tasks:
    - name: add "/etc/hosts"
      blockinfile: 
        path: /etc/hosts
        block: |
          192.168.110.20 node1.example.com node1
          192.168.110.30 node2.example.com node2
          192.168.110.40 node3.example.com node3
          192.168.110.50 node4.example.com node4
          192.168.110.60 node5.example.com node5
          
    - name: add "/etc/ansible/hosts"
      blockinfile: 
        path: /etc/ansible/hosts
        block: |
          [centos]
          node1
          node2
          node3
          
          [ubuntu]
          node4
          node5

# 추가된 곳 #################
    - name: Generate sshkey  
      become: yes
      become_user: vagrant
      shell: "{{item}}"
      with_items: 
        - "ssh-keyscan 192.168.110.20 >> ~/.ssh/known_hosts"
        - "ssh-keyscan 192.168.110.30 >> ~/.ssh/known_hosts"
        - "ssh-keyscan 192.168.110.40 >> ~/.ssh/known_hosts"
        - "ssh-keyscan 192.168.110.50 >> ~/.ssh/known_hosts"
        - "ssh-keyscan 192.168.110.60 >> ~/.ssh/known_hosts"
##############################        
        
    - name: create vim env directory & files
      shell: "{{item}}"
      loop:
        - "touch /home/vagrant/.vimrc"
        - "touch /home/vagrant/.bashrc"

... 생략

하지만 이렇게 known_hosts에 직접 등록하는건 많으면 효율적이지 못하다.

그러므로 자동화 하기 위해 아래와 같이 작성한다.

 

공개키 생성 및 fingerprint 저장

먼저 auto_pass.yml 파일을 만든다

- name: Create authority between server and nodes
  hosts: all
  connection: local
  serial: 1
  gather_facts: no
  tasks: 
    - name: ssh-keyscan for known_hosts file
      command: /usr/bin/ssh-keyscan -t ecdsa {{ ansible_host }}
      register: keyscan
    
    - name: input key
      lineinfile: 
        path: ~/.ssh/known_hosts
        line: "{{ item }}"
        create: yes
      with_items: 
        - "{{ keyscan.stdout_lines }}"

 

그후 vagrantfile을 수정한다. (server만)

# --- Ansible Server ---

Vagrant.configure("2") do |config|
	config.vm.define "my-ansible-server" do |cfg|
		cfg.vm.box = "centos/7" 
		cfg.vm.provider "virtualbox" do |vb|
			vb.name = "my-ansible-server"
			vb.cpus = 2
			vb.memory = 4096
			vb.gui = false
		end
		cfg.vm.host_name = "control.example.com"
		cfg.vm.network "private_network", ip: "192.168.110.10"
		cfg.vm.provision "shell", path: "ssh_conf.sh" 
		cfg.vm.synced_folder "../data", "/vagrant", disabled: true
		cfg.vm.provision "shell", inline: "yum -y install epel-release"
		cfg.vm.provision "shell", inline: "yum -y install centos-release-ansible-29.noarch"
		cfg.vm.provision "shell", inline: "yum install ansible -y"
		cfg.vm.provision "file", source: "ansible_env_ready.yml",
			destination: "ansible_env_ready.yml"
		cfg.vm.provision "shell", inline: "ansible-playbook ansible_env_ready.yml"	
        ### 추가된 부분
		cfg.vm.provision "file", source: "auto_pass.yml", destination: "auto_pass.yml"   
		cfg.vm.provision "shell", inline: "ansible-playbook auto_pass.yml", 
        	privileged: false # vagrant 계정의 권한을 위해 (안쓰면 root 권한으로 만들어짐)
       	##############

 

ansible_env_ready.yml에 등록했던 Generate sshkey 부분을 삭제하고

vagrant provision으로 적용시킨다.

 

공개키 배포

fingerprint는 등록이 되어있지만,

아직까지는 비밀번호가 필요하다. (-k 사용해야함)

auto_pass.yml 파일을 수정

- name: Create authority between server and nodes
  hosts: nodes
  connection: local
  serial: 1
  gather_facts: no
### 추가된 부분 ###
  vars:
    ansible_password: vagrant
###################
  tasks: 
    - name: ssh-keyscan for known_hosts file
      command: /usr/bin/ssh-keyscan -t ecdsa {{ ansible_host }}
      register: keyscan
    
    - name: input key
      lineinfile: 
        path: ~/.ssh/known_hosts
        line: "{{ item }}"
        create: yes
      with_items: 
        - "{{ keyscan.stdout_lines }}"

### 추가된 부분 ###
    - name: ssh-keygen for authorized_keys file
      command: "ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ''"
      ignore_errors: yes
      run_once: true
      
    - name: input key for each node
      connection: ssh
      authorized_key: 
        user: vagrant
        state: present
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"   
###################

 

우분투는 안될 수 있는데 fingerprint 받은 다음 (ssh 접속을 통해)

파이썬을 설치하고 ans ubuntu -m shell -a "sudo apt python" -k ( 설치 안해도 되는듯?)

ans ubuntu -m shell -a "sudo ln -s /usr/bin/python3 /usr/bin/python" -k

위의 명령을 통해 python3를 그냥 python으로 link를 걸어주면 된다.

(서버가 CentOS7(2.75)이고 node가 ubuntu(3.xx)라서 python 버전이 안맞아서 인듯)

 

대신 이런 경고문구가 뜨는데 무시해도 된다.

더보기

[DEPRECATION WARNING]: Distribution ubuntu 22.04 on host node5 should use
/usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior
Ansible releases. A future Ansible release will default to using the discovered platform
python for this host. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for
more information. This feature will be removed in version 2.12. Deprecation warnings can
be disabled by setting deprecation_warnings=False in ansible.cfg.

하지만 경고를 없애고 싶다면, 다시 링크를 삭제 하면 됨

ans ubuntu -m shell -a "sudo unlink /usr/bin/python" -k

복잡하고 귀찮은 작업이지만 한번만 해두면 되는거니까...

물론 지금 하는 작업은 비밀번호가 다 같다는 전제하에 하는것임.

 

ansible vault

auto_pass.yml 같은 경우에는 비밀번호가 들어 있기 때문에 암호화 하는 것이 좋다.

ansible-vault encrypt auto_pass.yml # 암호화
ansible-vault decrypt auto_pass.yml # 암호화된 파일 복호화
ansible-vault create auto_pass.yml  # 파일 작성시부터 암호화
ansible-vault edit auto_pass.yml    # 암호화된 파일 수정
ansible-vault view auto_pass.yml    # 암호화된 파일 보기

암호화된 파일은 ansible-playbook으로 실행할 수 없다. (복호화 후에 진행해야 함)

 

Facts 변수

facts.yaml

- name: print ipv4.address for nodes
  hosts: nodes
  #gather_facts: no ## fact변수를 사용할때 gather_facts: no가 있으면 안됨.
  tasks:
    - name: debug by msg
      debug:
        msg:
          - "eth0s ip {{ ansible_eth0.ipv4.address }}"
          - "eth1s ip {{ ansible_eth1.ipv4.address }}"

    - name: debug by var
      debug:
        var: "{{ item }}"
      with_items:
        - hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']
        - hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']

결과를 확인해보면 알겠지만, msg와 var를 둘다 쓸 수는 있지만

msg로 쓰는것이 보기 좋고 작성할때도 구분자를 적게 입력하기 때문에 편하다.

msg는 "." 마침표로 구분, var는 "[ ]" 대괄호로 구분한다.

 

결과

더보기
[vagrant@control work]$ anp facts.yaml

PLAY [print ipv4.address for nodes] ***************************************************************

TASK [Gathering Facts] ****************************************************************************
ok: [node2]
ok: [node1]
ok: [node3]
ok: [node4]
ok: [node5]

TASK [debug by msg] *******************************************************************************
ok: [node1] => {
    "msg": [
        "eth0s ip 10.0.2.15",
        "eth1s ip 192.168.110.20"
    ]
}
ok: [node2] => {
    "msg": [
        "eth0s ip 10.0.2.15",
        "eth1s ip 192.168.110.30"
    ]
}
ok: [node3] => {
    "msg": [
        "eth0s ip 10.0.2.15",
        "eth1s ip 192.168.110.40"
    ]
}
ok: [node4] => {
    "msg": [
        "eth0s ip 10.0.2.15",
        "eth1s ip 192.168.110.50"
    ]
}
ok: [node5] => {
    "msg": [
        "eth0s ip 10.0.2.15",
        "eth1s ip 192.168.110.60"
    ]
}

TASK [debug by var] *******************************************************************************
ok: [node1] => (item=hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']": "10.0.2.15",
    "item": "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']"
}
ok: [node1] => (item=hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']": "192.168.110.20",
    "item": "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']"
}
ok: [node3] => (item=hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']": "10.0.2.15",
    "item": "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']"
}
ok: [node3] => (item=hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']": "192.168.110.40",
    "item": "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']"
}
ok: [node2] => (item=hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']": "10.0.2.15",
    "item": "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']"
}
ok: [node2] => (item=hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']": "192.168.110.30",
    "item": "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']"
}
ok: [node4] => (item=hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']": "10.0.2.15",
    "item": "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']"
}
ok: [node4] => (item=hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']": "192.168.110.50",
    "item": "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']"
}
ok: [node5] => (item=hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']": "10.0.2.15",
    "item": "hostvars[inventory_hostname]['ansible_eth0']['ipv4']['address']"
}
ok: [node5] => (item=hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']) => {
    "ansible_loop_var": "item",
    "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']": "192.168.110.60",
    "item": "hostvars[inventory_hostname]['ansible_eth1']['ipv4']['address']"
}

When 조건

조건에 맞을때 해당하는 play를 실행하라는것

 

nginx를 ubuntu와 centos에 설치하기

- name: install nginx
  hosts: nodes
  become: yes
  tasks:
    - name: install epel-release for CentOS
      action: "{{ ansible_pkg_mgr }} name=epel-release state=latest" # action은 facts변수를 쓰기위해
      when: ansible_distribution == 'CentOS'  # CentOS일때 action을 실행해라
    - name: install nginx web server for CentOS
      action: "{{ ansible_pkg_mgr }} name=nginx state=present"
      when: ansible_distribution == 'CentOS'
    - name: upload default index.html
      get_url:
        url: https://www.nginx.com
        dest: /usr/share/nginx/html/
        mode: 0644
      when: ansible_distribution == 'CentOS'
    - name: start nginx
      service:
        name: nginx
        state: started
      when: ansible_distribution == 'CentOS'
    - name: install nginx for ubuntu
      action: "{{ ansible_pkg_mgr }} name=nginx state=present update_cache=yes"
      when: ansible_distribution == 'Ubuntu'
    - name: upload default index.html
      get_url:
        url: https://www.nginx.com
        dest: /usr/share/nginx/html/
        mode: 0644
        validate_certs: no
      when: ansible_distribution == 'Ubuntu'

 

include_tasks

playbook안에서 다른 yaml 파일을 참조하는것

(참조되는 yaml파일은 hosts, tasks를 빼고 그냥 모듈부터 적어야함)

ex) include_tasks.yaml

[vagrant@control work]$ cat include_tasks.yaml
- name: install nginx
  hosts: nodes
  become: yes
  tasks:
    - name: nginx for CentOS
      include_tasks: CentOS.yaml
      when: ansible_distribution == 'CentOS'
    - name: nginx for Ubuntu
      include_tasks: Ubuntu.yaml
      when: ansible_distribution == 'Ubuntu'

 

CentOS.yaml

[vagrant@control work]$ cat CentOS.yaml
- name: install epel-release
  action: "{{ ansible_pkg_mgr }} name=epel-release state=latest"
- name: install nginx web server
  action: "{{ ansible_pkg_mgr }} name=nginx state=present"
- name: nginx start
  service:
    name: nginx
    state: started
  when: ansible_distribution == 'CentOS'

 

Ubuntu.yaml

[vagrant@control work]$ cat Ubuntu.yaml
- name: install nginx
  action: "{{ ansible_pkg_mgr }} name=nginx state=present update_cache=yes"
- name: upload default index.html
  get_url:
    url: https://www.nginx.com
    dest: /usr/share/nginx/html/
    mode: 0644
    validate_certs: no

 

if 조건

when말고도 if를 통해서 조건문을 작성 할 수 있다.

include_tasks.yaml 에서 when대신 vars를 이용한 if로 변경을 해본다면

 

if_nginx.yaml

[vagrant@control work]$ cat if_nginx.yaml
- name: install nginx on the nodes
  hosts: nodes
  become: yes
  vars:
    lnx_name: "{{ 'CentOS' if ansible_distribution == 'CentOS'
                  else 'Ubuntu' if ansible_distribution == 'Ubuntu'
                  else 'Just Linux' }}"
  tasks:
    - name: nginx for any linux
      include_tasks: "{{ lnx_name }}.yaml"

** lnx_name ?? 

if 조건문을 사용하기 위한 변수라고 생각하면 될듯. 

(lnx_name이 아닌 다른이름을 적고 playbook을 실행했더니 lnx_name이 없다고 나옴)

 

실행하면 skipped가 발생하지 않고 깔끔하게 된다.

대신 작성했을 때 가독성이 안좋음

더보기
[vagrant@control work]$ anp if_nginx.yaml

PLAY [install nginx on the nodes] *****************************************************************

TASK [Gathering Facts] ****************************************************************************
ok: [node3]
ok: [node2]
ok: [node1]
ok: [node5]
ok: [node4]

TASK [nginx for any linux] ************************************************************************
included: /home/vagrant/work/CentOS.yaml for node1, node2, node3
included: /home/vagrant/work/Ubuntu.yaml for node4, node5

TASK [install epel-release] ***********************************************************************
ok: [node1]
ok: [node2]
ok: [node3]

TASK [install nginx web server] *******************************************************************
ok: [node1]
ok: [node2]
ok: [node3]

TASK [nginx start] ********************************************************************************
ok: [node2]
ok: [node3]
ok: [node1]

TASK [install nginx] ******************************************************************************
ok: [node4]
ok: [node5]

TASK [upload default index.html] ******************************************************************
changed: [node4]
ok: [node5]

 

 

** 혹시나 ansible 모듈이 뭐가 있는지 궁금하다면 ansible-doc으로 찾아봐도 된다.

하지만 list로 보기 힘드므로 /usr/lib/python2.7/site-packages/ansible/modules 여기 안에 들어가서 찾는게 더 편함

물론 버전마다 python뒤에 있는 숫자가 바뀔수는 있음

 

'Ansible' 카테고리의 다른 글

ansible 미니 프로젝트 - wordpress 사이트 배포  (0) 2024.02.22
Handler, Template, Role  (0) 2024.02.20
centos, ubuntu, window node 생성 및 nfs  (0) 2024.02.15
실습  (0) 2024.02.14
Playbook  (0) 2024.02.13

+ Recent posts