Ansible

Vagrant와 Ansible을 활용한 부하 분산이 가능한 웹 서버 구성

Joon0464 2021. 7. 28. 21:14

1. 구성도 및 계획

1. Host PC에서 Vagrantfile을 작성하여 Ansible-Server 및 총 4대의 서버 프로비저닝

2. Host PC에서 httpd template을 작성하고 Ansible-Server를 프로비저닝하면서 해당 탬플릿을 실행하여 httpd 웹 서버를 자동으로 구성

3. Host PC에서 haproxy.yml을 작성하고 Ansible-Server를 프로비저닝하면서 해당 플레이북을 동작시켜 자동으로 haproxy 로드벨런서 구성

4. 192.168.1.11로 접속시 정상적으로 웹서버에 접근이 가능하며 web-server01과 web-server02에 Round-Robin 메커니즘으로 번갈아가면서 한 번씩 접속된다.

 

2. Vagrantfile 작성

# Vagrantfile

Vagrant_API_Version ="2"

Vagrant.configure(Vagrant_API_Version) do |config|

  #haproxy-server
  config.vm.define:"haproxy-server" do |cfg|
     cfg.vm.box = "centos/7"
	 cfg.vm.provider:virtualbox do |vb|
	   vb.name="haproxy-server"
	   vb.customize ["modifyvm", :id, "--cpus",1]
	   vb.customize ["modifyvm", :id, "--memory",1024]
	 end
	 cfg.vm.host_name="haproxy-server"
	 cfg.vm.synced_folder ".", "/vagrant", disabled: true
	 cfg.vm.network "public_network", ip: "192.168.1.11"
	 cfg.vm.network "forwarded_port", guest: 22, host: 19211, auto_correct: false, id: "ssh"
	 cfg.vm.provision "shell", path: "bash_ssh_conf_CentOS.sh"
  end
  
  #web-server01	 
  config.vm.define:"web-server01" do |cfg|
     cfg.vm.box = "centos/7"
	 cfg.vm.provider:virtualbox do |vb|
	   vb.name="web-server01"
	   vb.customize ["modifyvm", :id, "--cpus",1]
	   vb.customize ["modifyvm", :id, "--memory",1024]
	 end
	 cfg.vm.host_name="web-server01"
	 cfg.vm.synced_folder ".", "/vagrant", disabled: true
	 cfg.vm.network "public_network", ip: "192.168.1.12"
	 cfg.vm.network "forwarded_port", guest: 22, host: 19212, auto_correct: false, id: "ssh"
	 cfg.vm.provision "shell", path: "bash_ssh_conf_CentOS.sh"
  end
  
  #web-server02	 
  config.vm.define:"web-server02" do |cfg|
     cfg.vm.box = "centos/7"
	 cfg.vm.provider:virtualbox do |vb|
	   vb.name="web-server02"
	   vb.customize ["modifyvm", :id, "--cpus",1]
	   vb.customize ["modifyvm", :id, "--memory",1024]
	 end
	 cfg.vm.host_name="web-server02"
	 cfg.vm.synced_folder ".", "/vagrant", disabled: true
	 cfg.vm.network "public_network", ip: "192.168.1.13"
	 cfg.vm.network "forwarded_port", guest: 22, host: 19213, auto_correct: false, id: "ssh"
	 cfg.vm.provision "shell", path: "bash_ssh_conf_CentOS.sh"
  end
  
  #Ansible-Server
  config.vm.define:"ansible-server" do |cfg|
     cfg.vm.box = "centos/7"
	 cfg.vm.provider:virtualbox do |vb|
	   vb.name="Ansible-Server"
	 end
	 cfg.vm.host_name="ansible-server"
	 cfg.vm.synced_folder ".", "/vagrant", disabled: true
	 cfg.vm.network "public_network", ip: "192.168.1.10"
	 cfg.vm.network "forwarded_port", guest: 22, host: 19210, auto_correct: false, id: "ssh"
	 cfg.vm.provision "shell", path: "bootstrap.sh"
	 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_known_host.yml", destination: "Auto_known_host.yml"
	 cfg.vm.provision "shell", inline: "ansible-playbook Auto_known_host.yml", privileged: false
	 cfg.vm.provision "file", source: "Auto_authorized_keys.yml", destination: "Auto_authorized_keys.yml"
	 cfg.vm.provision "shell", inline: "ansible-playbook Auto_authorized_keys.yml --extra-vars 'ansible_ssh_pass=vagrant'", privileged: false
	 cfg.vm.provision "file", source: "httpd_install.zip", destination: "httpd_install.zip"
	 cfg.vm.provision "shell", inline: "yum -y install unzip"
	 cfg.vm.provision "shell", inline: "unzip httpd_install.zip", privileged: false
	 cfg.vm.provision "shell", inline: "ansible-playbook httpd_install/Configure_httpd.yml", privileged: false
	 cfg.vm.provision "file", source: "haproxy.cfg", destination: "haproxy.cfg"
	 cfg.vm.provision "file", source: "haproxy_install.yml", destination: "haproxy_install.yml"
	 cfg.vm.provision "shell", inline: "ansible-playbook haproxy_install.yml", privileged: false
  end
  
end

Ansible Server와 Load-Balancer Server 그리고 두 대의 Web Server를 구성하기 위한 Vagrantfile이다.

Ansible-Server 구성에는 node들과 ssh키 교환 및 인증에 관한 yml 파일 및 sh 파일 실행이 포함되어있다.

또한, haproxy를 구성하기 위한 yml 파일과 httpd 패키지 구성을 위한 template를 사용하도록 설정되어 있다.

3. Ansible_env_ready.yml 작성

# Ansible_env_ready.yml

---
- name: Setup for the Ansible's Enviroment
  hosts: localhost
  gather_facts: no
  
  tasks:
    - name: Add "/etc/hosts"
      blockinfile: |
        dest=/etc/hosts
        content="
          192.168.1.10 server
          192.168.1.11 haproxy
          192.168.1.12 web01
          192.168.1.13 web02"
      
    - name: Add "/etc/ansible/hosts"
      blockinfile: |
        dest=/etc/ansible/hosts
        content="
          [nodes]
           haproxy
           web01
           web02
          [web]
           web01
           web02
          [haproxy]
           haproxy"
    
    - name: Install sshpass for Authentication
      yum:
        name: sshpass
        state: present
    
    - name: Install vim-enhanced
      yum:
        name: vim-enhanced
        state: present
        
    - name: Install git
      yum: 
        name: git
        state: present
        
    - name: Download pathogen.vim
      shell: "curl -fLo /home/vagrant/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim"
      
    - name: Git clone vim-ansible-yaml
      git:
        repo: 'https://github.com/chase/vim-ansible-yaml.git'
        dest: /home/vagrant/.vim/bundle/vim-ansible-yaml
        
    - name: Configure vimrc
      lineinfile: 
        dest: /home/vagrant/.vimrc
        line: "{{ item }}"
      with_items:
        - "set number"
        - "execute pathogen#infect()"
        - "syntax on"

    - name: Configure Bashrc
      lineinfile:   
        dest: /home/vagrant/.bashrc
        line: "{{ item }}"
      with_items:
        - "alias vi='vim'"
        - "alias ans='ansible'"
        - "alias anp='ansible-playbook'"

4. bootstrap.sh 작성

# bootstrap.sh

#! /usr/bin/env bash

#ansible 설치 
yum install epel-release -y
yum install ansible -y

#환경설정 초기 파일 구성 for vagrant Only 
mkdir -p /home/vagrant/.vim/autoload /home/vagrant/.vim/bundle
touch /home/vagrant/.vimrc
touch /home/vagrant/.bashrc

ansible-server에 ansible을 설치하고 vim을 활용하여 ansible playbook을 작성시에 글자에 색을 입히기 위한 설정이 포함되어 있다.

5. Auto_known_host.yml 작성

# Auto_known_host.yml
---
- hosts: nodes
  connection: local
  serial: 1
  gather_facts: no

  tasks:
  - command: /usr/bin/ssh-keyscan -t ecdsa {{ ansible_host }}
    register: keyscan

  - lineinfile:
      name=~/.ssh/known_hosts
      create=yes
      line={{ item }}
    with_items:
      - "{{ keyscan.stdout_lines }}"

known_hosts 파일에 접속 대상의 ssh키를 저장하기 위하여 필요한 playbook이다.

 

6. Auto_authorized_keys.yml 작성

---
- hosts: nodes
  gather_facts: no

  tasks:
  - name: ssh-keygen
    connection: local
    command: "ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ''"
    ignore_errors: yes
    run_once: true

  - name: read id_rsa.pub
    connection: local
    command: "cat ~/.ssh/id_rsa.pub"
    register: id_pub
    run_once: true

  - name: remote lineinfile for authorized_keys
    lineinfile:
      dest: /home/vagrant/.ssh/authorized_keys
      line: "{{ id_pub.stdout }}"

playbook을 동작시킬 때 -k 옵션을 사용하여 비밀번호를 입력하는 과정 없이 실행시킬 수 있도록 ssh 키를 ansible-server와 노드들간에 교환하도록 하는 playbook이다.

 

7. haproxy_install.yml 작성

# haproxy_install.yml

---
- name: install haproxy
  hosts: haproxy
  become: yes
  gather_facts: no

  tasks:
    - name: install haproxy
      yum:
        name: haproxy
        state: present

    - name: Configure haproxy
      copy:
        src: /home/vagrant/haproxy.cfg
        dest: /etc/haproxy/haproxy.cfg
        mode: 0644
        
    - name: start firewalld
      service:
        name: firewalld
        state: started

    - name: open 80 port
      firewalld:
        permanent: yes
        immediate: yes
        port: 80/tcp
        state: enabled

    - name: reload firewalld
      systemd:
        name: firewalld
        state: reloaded

    - name: start haproxy
      service:
        name: haproxy
        state: started

haproxy를 설치 및 구성하고 실행하는 플레이북이다. haproxy.cfg는 미리 작성하여 copy 모듈을 사용하여 붙여넣는 방식으로 설정했다.

 

8. haproxy.cfg

#---------------------------------------------------------------------
# Example configuration for a possible web application.  See the
# full configuration options online.
#
#   http://haproxy.1wt.eu/download/1.4/doc/configuration.txt
#
#---------------------------------------------------------------------

#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    # to have these messages end up in /var/log/haproxy.log you will
    # need to:
    #
    # 1) configure syslog to accept network log events.  This is done
    #    by adding the '-r' option to the SYSLOGD_OPTIONS in
    #    /etc/sysconfig/syslog
    #
    # 2) configure local2 events to go to the /var/log/haproxy.log
    #   file. A line like the following can be added to
    #   /etc/sysconfig/syslog
    #
    #    local2.*                       /var/log/haproxy.log
    #
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000

#---------------------------------------------------------------------
# main frontend which proxys to the backends
#---------------------------------------------------------------------
frontend  main *:80
    acl url_static       path_beg       -i /static /images /javascript /stylesheets
    acl url_static       path_end       -i .jpg .gif .png .css .js

    use_backend static          if url_static
    default_backend             app

#---------------------------------------------------------------------
# static backend for serving up images, stylesheets and such
#---------------------------------------------------------------------
backend static
    balance     roundrobin
    server      static 127.0.0.1:80 check

#---------------------------------------------------------------------
# round robin balancing between the various backends
#---------------------------------------------------------------------
backend app
    balance     roundrobin
    server  app1 192.168.1.12:80 check
    server  app2 192.168.1.13:80 check

frontend main을 80번 포트로 서비스하도록 설정한다.

backend app에서 로드 벨런스할 서버의 IP 주소를 지정한다.

8. httpd 설치 Template 생성

Template 구조

8.1 Configure_httpd.yml

---
- name: Install nginx on the nodes
  hosts: web
  become: yes

  roles:
    - { role: ./roles/httpd    }

Configure_httpd.yml에 의해 httpd 패키지 설치가 진행된다.

 

8.2 main.yml (handlers)

---
- name: Restart httpd web server
  service: name=httpd state=restarted

httpd 데몬을 재실행하는 모듈이 작성되어 있으며 차후에 httpd가 구성이 완료되면 호출된다.

8.3 main.yml (tasks)

- name: httpd for Any Linux
  include_tasks: "web.yml"
- name: Check httpd config
  include_tasks: chk_httpd_cfg.yml
- name: Check httpd service
  debug: msg="{{lookup('template','ins_chk.j2').split('\n')}}"

web.yml 과 chk_httpd_cfg.yml을 호출하여 설치를 진행한다.

ins_chk.j2 파일을 사용하여 디버그 메시지를 출력한다.

8.4 chk_httpd_cfg.yml

- command: "rpm -qa | grep httpd"
  register: httpd
- debug: msg="{{ httpd.stderr_lines }}"

httpd가 설치되었는지 확인하기 위한 명령어를 debug 메시지로 출력한다.

8.5 web.yml

- name: Install httpd web server
  action : "{{ ansible_pkg_mgr }} name=httpd state=present"

- name: start firewalld
  service:
    name: firewalld
    state: started

- name: open 80 port
  firewalld:
    permanent: yes
    immediate: yes
    port: 80/tcp
    state: enabled

- name: reload firewalld
  systemd:
    name: firewalld
    state: reloaded

- name: File Touch
  file:
    path: /var/www/html/index.html
    state: touch
    mode: 0644  

- name: Configure index.html
  lineinfile:
    dest: /var/www/html/index.html
    line: <html><body><h1>This is Web-Server</h1></body></html
  notify:
    - Restart httpd web server

httpd를 설치하고 구성을 진행하는 파일이다. 마지막에 httpd 구성을 완료하면 notify를 통해 handlers의 main.yml을 호출하여 httpd 데몬을 재실행한다.

8.6 ins_chk.j2

{% if ansible_distribution == 'CentOS' and ansible_distribution_major_version == '7' %}
   [ OS : CentOS ]
    >> yum list installed | grep httpd
    OR
    >> systemctl status httpd
{% elif ansible_distribution == 'CentOS' and ansible_distribution_major_version < '7' %}
   [ OS : CentOS ver6 ]
    >> yum list installed | grep httpd
    OR
    >> service httpd status
{% else %}
    >> service httpd status (* Gernally)
{% endif %}

CentOS 버전별로 httpd가 정상 설치되었는지 확인할 수 있도록 작성된 jinja2 형식의 파일이다.

 

9. 테스트

원하는 디렉터리 내부에 설치 파일들을 위치시킨다.

# vagrant up

Vagrantfile이 존재하는 디렉터리에서 vagrant up 명령어를 실행하면 설치가 진행된다.

설치가 모두 완료된 후 haproxy 서버의 IP 주소로 접근하면 부하 분산되어 각 웹서버로 번갈아가면서 접속된다.

 

10 Git Repository

 

아래 리포지토리에서 다운받아 위 실습을 진행해볼 수 있다.

thdguswns3/Ansible_Vagrant_Project (github.com)