2017년 6월 29일 목요일

[Vagrant] 15분만에 윈도우에서 Ansible 테스트 환경 구축하기 (서버 1대 + 노드 5대)



(자료화면은 vagrantfile을 sublime Text3에서 작성하던 화면~)

하하하 테스트 정말 오래 걸리네요
이게 테스트를 하려면 한번 쳐다보고 10분 지나고 이런 경우가 꽤 있다 보니...

설치를 하고 실행하는 것은 데모 동영상으로 진행할까 합니다.

요약

vagrantfile이 ini파일 처럼 구성할 파일들을 code로 정의해 놓으면, 해당 구성 정보에 맞게 vagrant가 virtualbox(또는 Hyper-V, Docker, VMware, AWS, KVM등) 아주 다양하게 올려줍니다. 

즉, 지속적으로 사용해야 하는 환경을 반복 구성하는데 최적화 되어 있습니다. 
또는 템플릿 형식처럼, 배포하는 것에도 쓰여질수가 있겠죠? UI만 잘 입히면요. :) 
위키 참조 : https://en.wikipedia.org/wiki/Vagrant_(software)



설치 및 실행

1. virtualbox 설치 

oracle Virtual Box (https://www.virtualbox.org/) 에서 적합한 윈도우 버전을 다운받습니다.


2. vagrant 설치 

https://www.vagrantup.com/ 에서 Download를 눌러서 파일을 다운 받고 실행한다. 그리고 설치를 다 마치고 나면, 정상적으로 설치되었는지 확인을 위해 'cmd'를 실행하고 버전을 확인해 보자.

C:\Users\pagai>vagrant --version
Vagrant 1.9.5


3. 설치할 OS 다운로드 

기본적으로 계속 사용할 OS들이 vagrant에 잘 패키지 되어 있는데, 이는 다운로드 해서 사용한다.

현재 다운로드 되어 쓸수 있는 것들은 'vagrant box list' 명령어로 확인가능하다.
(이미 꽤 반복 작업을 해서 여러개 나오는겁니다..보통은 없습니당)

C:\Users\pagai>vagrant box list
16.04           (virtualbox, 0)
16.04_docker    (virtualbox, 0)
centos/7        (virtualbox, 1705.02)
precise32       (virtualbox, 0)
ubuntu/trusty64 (virtualbox, 20170619.0.0)

우리는 우분투 이미지와 센트를 쓸겁니다.
센트는 Ansible-server용으로
우분투는 Ansible-Node용으로 사용할 것입니다.

이미지 다운로드는 신기하게 명령어가 'add'를 씁니다. 그래서 명령어가 다음과 같습니다.

vagrant box add <다운받을 이미지 이름 or 경로>




그리고 이미지 찾기는 여기서 가능합니다.
https://app.vagrantup.com/boxes/search

추가적으로 위의 그림과 같은 버전들을 선택한 이유는 간단한데..
남들이 많이 쓰는게 좋다는 지론때문입니다.

중간에 사람 얼굴이 있는데..제작자인가? 싶은데... 외국인은 대머리도 멋있네요.
vagrant 대신에 homestead를 쓰는 경우도 종종 있더라고요 나중에 시간이 되면 두개를 비교해 볼 필요도 있을 것 같습니다아~!




4. 설치후 vagrantfile 구성 

이미지가 준비되었다면,  vagrant init --force(만약에 기존에 만든게 있어서 오류가 난다면)
을 실행해 어떻게 설치할지 결정하는 초기 파일을 만들수 있지만, 어짜피 제시하는 코드(내가 만든 코드) 를 가지고 할 것이기때문에 초기화 할 필요는 없다.
그래도 궁금하다면, 파일이름도 있고 하니...vagrant init --force를 실행하고 현재의 경로에 vagrantfile이 생성된 것을 확인하자.

이제 부터 나올 것들은 코드인데.. vagrant는 기본적으로 'ruby'라는 언어로 되어 있다. 하지만~! 하지만~! 별로 쫄것이 없는게..복잡한 것은 안 쓴다.
그냥 python에 비하면, do가 쓰여지면, 아래에 들여쓰기가 생겨야 하고(하위 메뉴처럼) end로 마감되어야 한다. 오류를 보면 쉽게 알수 있는 내용이다.

5. 실행 

'vagrant up'을 실행하기 전에 vagrantfile을 아래의 내용에서 카피해서 생성해야 하고, 같은 디렉터리에 Bootstrap.sh, Ansible_env_ready.yml 도 생성해 두어야 한다.

그리고 중요한거 하나 'vagrant plugin install vagrant-vbguest를 실행해야만 Host에 있는 디렉토리와 sync할때 에러가 나지 않는다.


실행 데모는 다음과 같다.



6. 코드 설명 

하나하나 더 설명할수는 없지만, Ansible-Server를 한번 설명하면 나머지는 쉽게 알수 있으리라고 생각한다. 이 글을 보시는 분들은 분명히 대단하신 분들일꺼다 :)

[ 앤서블 서버 ]



  config.vm.define:"ansible-server" do |cfg|
    cfg.vm.box= "centos/7"                    # VM을 만들고자 하는 운영체제를 선택
    cfg.vm.provider:virtualbox do |vb|        # VM에 직접적인 그러니까 버추얼박스에 설정을 진행
      vb.name= "HoonJo-Ansible-Server"
      vb.customize ["modifyvm", :id, "--cpus", 2]
      vb.customize ["modifyvm", :id, "--memory", 2048]
    end
    cfg.vm.host_name="ansible-server"         # 실제 VM에 호스트이름 linux에서 hostname 치면 나오는 것
    # 호스트와 VM간에 파일 교환을 위한 건데, 기본 설정이 rsync로 되어 있어서 windows의 경우 변경을 안해주면 오류가 남
    cfg.vm.synced_folder ".", "/vagrant", type: "virtualbox" 
    # eth0은 내부 NAT로 고정되어 있고, eth1번을 인터넷과 연결하도록 public으로 설정
    cfg.vm.network "public_network", ip: "172.30.1.10"
    # 개별 네트워크들의 기본 fowarded_port는 guest 22, host: 2222인데 중복되면 에러가 발생함
    # 이것의 목적은 vagrant ssh <hostname>을 했을때 경로를 포워딩 해줌
    cfg.vm.network "forwarded_port", guest: 22, host: 30110, auto_correct: false, id: "ssh"
    # centos7/rhel7은 default route를 설정하는 명령어가 좀 다름...-_-;
    cfg.vm.provision "shell",
      inline: "ip route replace default via 172.30.1.254 dev eth1"
    #앤서블 자체를 설치하고 hosts 이름들을 설정함
    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"
  end

7. 실제 코드



[ vagrantfile ]

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|

# Ansible 테스트 서버
  config.vm.define:"ansible-server" do |cfg|
    cfg.vm.box= "centos/7"
    cfg.vm.provider:virtualbox do |vb|
      vb.name= "HoonJo-Ansible-Server"
      vb.customize ["modifyvm", :id, "--cpus", 2]
      vb.customize ["modifyvm", :id, "--memory", 2048]
    end
    cfg.vm.host_name="ansible-server"
    cfg.vm.synced_folder ".", "/vagrant", type: "virtualbox"
    cfg.vm.network "public_network", ip: "172.30.1.10"
    cfg.vm.network "forwarded_port", guest: 22, host: 30110, auto_correct: false, id: "ssh"
    cfg.vm.provision "shell",
      inline: "ip route replace default via 172.30.1.254 dev eth1"
    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"
  end



# Ansible 테스트 노드 1번
  config.vm.define:"ansible-node01" do |cfg|
    cfg.vm.box= "ubuntu/trusty64"
    cfg.vm.provider:virtualbox do |vb|
      vb.name= "HoonJo-Ansible-Node01"
      vb.customize ["modifyvm", :id, "--cpus", 1]
      vb.customize ["modifyvm", :id, "--memory", 1024]
    end
    cfg.vm.host_name="ansible-node01"
    cfg.vm.synced_folder ".", "/vagrant", disabled: true
    cfg.vm.network "public_network", ip: "172.30.1.11"
    cfg.vm.network "forwarded_port", guest: 22, host: 30111, auto_correct: false, id: "ssh"
    cfg.vm.provision "shell",
      run: "always",
      inline: "route add default gw 172.30.1.254"
    cfg.vm.provision "shell",
      run: "always",
      inline: "eval `route -n | awk '{ if ($8 ==\"eth0\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
  end



# Ansible 테스트 노드 2번
  config.vm.define:"ansible-node02" do |cfg|
    cfg.vm.box= "ubuntu/trusty64"
    cfg.vm.provider:virtualbox do |vb|
      vb.name= "HoonJo-Ansible-Node02"
      vb.customize ["modifyvm", :id, "--cpus", 1]
      vb.customize ["modifyvm", :id, "--memory", 1024]
    end
    cfg.vm.host_name="ansible-node02"
    cfg.vm.synced_folder ".", "/vagrant", disabled: true
    cfg.vm.network "public_network", ip: "172.30.1.12"
    cfg.vm.network "forwarded_port", guest: 22, host: 30112, auto_correct: false, id: "ssh"
    cfg.vm.provision "shell",
      run: "always",
      inline: "route add default gw 172.30.1.254"
    cfg.vm.provision "shell",
      run: "always",
      inline: "eval `route -n | awk '{ if ($8 ==\"eth0\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
  end



# Ansible 테스트 노드 3번
  config.vm.define:"ansible-node03" do |cfg|
    cfg.vm.box= "ubuntu/trusty64"
    cfg.vm.provider:virtualbox do |vb|
      vb.name= "HoonJo-Ansible-Node03"
      vb.customize ["modifyvm", :id, "--cpus", 1]
      vb.customize ["modifyvm", :id, "--memory", 1024]
    end
    cfg.vm.host_name="ansible-node03"
    cfg.vm.synced_folder ".", "/vagrant", disabled: true
    cfg.vm.network "public_network", ip: "172.30.1.13"
    cfg.vm.network "forwarded_port", guest: 22, host: 30113, auto_correct: false, id: "ssh"
    cfg.vm.provision "shell",
      run: "always",
      inline: "route add default gw 172.30.1.254"
    cfg.vm.provision "shell",
      run: "always",
      inline: "eval `route -n | awk '{ if ($8 ==\"eth0\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
  end



# Ansible 테스트 노드 4번
  config.vm.define:"ansible-node04" do |cfg|
    cfg.vm.box= "ubuntu/trusty64"
    cfg.vm.provider:virtualbox do |vb|
      vb.name= "HoonJo-Ansible-Node04"
      vb.customize ["modifyvm", :id, "--cpus", 1]
      vb.customize ["modifyvm", :id, "--memory", 1024]
    end
    cfg.vm.host_name="ansible-node04"
    cfg.vm.synced_folder ".", "/vagrant", disabled: true
    cfg.vm.network "public_network", ip: "172.30.1.14"
    cfg.vm.network "forwarded_port", guest: 22, host: 30114, auto_correct: false, id: "ssh"
    cfg.vm.provision "shell",
      run: "always",
      inline: "route add default gw 172.30.1.254"
    cfg.vm.provision "shell",
      run: "always",
      inline: "eval `route -n | awk '{ if ($8 ==\"eth0\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
  end



# Ansible 테스트 노드 5번
  config.vm.define:"ansible-node05" do |cfg|
    cfg.vm.box= "ubuntu/trusty64"
    cfg.vm.provider:virtualbox do |vb|
      vb.name= "HoonJo-Ansible-Node05"
      vb.customize ["modifyvm", :id, "--cpus", 1]
      vb.customize ["modifyvm", :id, "--memory", 1024]
    end
    cfg.vm.host_name="ansible-node05"
    cfg.vm.synced_folder ".", "/vagrant", disabled: true
    cfg.vm.network "public_network", ip: "172.30.1.15"
    cfg.vm.network "forwarded_port", guest: 22, host: 30115, auto_correct: false, id: "ssh"
    cfg.vm.provision "shell",
      run: "always",
      inline: "route add default gw 172.30.1.254"
    cfg.vm.provision "shell",
      run: "always",
      inline: "eval `route -n | awk '{ if ($8 ==\"eth0\" && $2 != \"0.0.0.0\") print \"route del default gw \" $2; }'`"
  end

end





[ Bootstrap.sh ]
#!/usr/bin/env bash

yum install epel-release -y
yum install ansible -y

#hosts file
echo "172.30.1.11 node01" >> /etc/hosts
echo "172.30.1.12 node02" >> /etc/hosts
echo "172.30.1.13 node03" >> /etc/hosts
echo "172.30.1.14 node04" >> /etc/hosts
echo "172.30.1.15 node05" >> /etc/hosts


#ansible hosts file
echo "[Nodes]" >> /etc/ansible/hosts
echo "node01" >> /etc/ansible/hosts
echo "node02" >> /etc/ansible/hosts
echo "node03" >> /etc/ansible/hosts
echo "node04" >> /etc/ansible/hosts
echo "node05" >> /etc/ansible/hosts

#환경설정 초기 파일 구성 for vagrant ONLY
mkdir /home/vagrant/.vim
touch /home/vagrant/.vimrc
touch /home/vagrant/.bashrc
[ Ansible_env_ready.yml ] --- - name: Ansible_vim   hosts: localhost   remote_user: vagrant   gather_facts: no   tasks:     - name: Install vim-enhanced        yum:          name: vim-enhanced         state: present     - name: Download ansible-vim       shell: "curl -fLo /home/vagrant/.vim/autoload/plug.vim --create-dirs \               https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim"     - name: configure vimrc        lineinfile:         dest: /home/vagrant/.vimrc         line: "{{ item }}"       with_items:         - "set autoindent"          - "set cindent"          - "set number"         - "set smartcase"         - "set smarttab"         - "call plug#begin('~/.vim/plugged')"          - "Plug 'pearofducks/ansible-vim'"         - "call plug#end()"     - name: configure bashrc       lineinfile:         dest: /home/vagrant/.bashrc         line: "{{ item }}"       with_items:         - "alias vi='vim'"          - "alias ans='ansible'"         - "alias anp='ansible-playbook'"     - name: install vim-Plugin       shell: "vim -E -c 'PlugInstall' -c 'source /home/vagrant/.vimrc' -c 'qa' || true"        ignore_errors: yes - name: Change .vim permission shell: chown -R vagrant:vagrant /home/vagrant/.vim     - name: bash reload       shell: "source /home/vagrant/.bashrc"


수정 (2017.07.03)
  - vagrant user에 맞게 경로를 좀 더 세밀하게 수정하였습니다. 


추가 정보 :

윈도우 환경에서는 ssh가 기본적으로 제공되지 않기 때문에 vagrant ssh가 실행되지 못함
따라서 저는 git에서 사용하고 있는 ssh를 환경변수/시스템 에 포함시켜주었음
putty를 선호한다면 다음을 참고하세여~(저는 해야 할게 많아서 안함)
https://github.com/nickryand/vagrant-multi-putty



힌트 & 팁 :

1. 개발할때?
그리고 이런 개발을 하다보면, 자주 반복적으로 vagrant halt, vagrant destroy -f, vagrant up 세단계를 계속 써야 하는데. 이를 배치파일을 만들어서 돌리면 편하다.


[ vag_reconf.bat ]
#!/usr/bin/env bash
vagrant halt
vagrant destroy -f
vagrant up


@echo off
setlocal 
set /p name=어느 vagrant를 재 구성 및 시작하시겠습니까? 모두는 그냥 enter를 입력 하세요: || set name=default
if %name%==default (
 vagrant halt && vagrant destroy -f && vagrant up
) else (
    vagrant halt %name% && vagrant destroy %name% -f && vagrant up %name%
)

변경(2017.07.03) :
모두 껐다 키는 것은 효율적이지 않고, 필요한 부분만 조율 가능하도록 변경했습니다.
수정 후에 앤서블 서버가 동작하는 영상은 다음과 같습니다.





2. 윈도우에서 앤서블을 쓰고 싶지 않나요?
cygwin을 이용해서 가능하도록 만들어 주는 방법입니다. 
하지만 꼭 이렇게까지 써야할지는 물음표~


참고 사이트:

vagrant의 바이블
https://www.vagrantup.com/docs/index.html

참고할만한 정보들이 있음 (bridge network 참조)
https://friendsofvagrant.github.io/v1/docs/bridged_networking.html

init --force 옵션 
https://stackoverflow.com/questions/23325109/vagrant-errorvagrantfile-already-exists-in-this-directory/23326656

default-gateway 제거 
https://superuser.com/questions/752954/need-to-do-bridged-adapter-only-in-vagrant-no-nat

rsync 이슈
https://stackoverflow.com/questions/34176041/vagrant-with-virtualbox-on-windows10-rsync-could-not-be-found-on-your-path

에러를 찾아볼 소스 파일 (vagrant.git)
https://searchcode.com/codesearch/view/71587663/

vagrant configuration file 참조
http://perfspy.blogspot.kr/2016/02/one-thing-to-rule-them-all-ansible-on.html
http://www.barrymorrison.com/2015/Apr/10/vagrant-ansible-or-how-to-make-your-life-better-as-a-developer/

리눅스처럼 윈도우에서 실행 시간 알고 싶을때 
https://blog.update.sh/archives/209

vagrant plugin uninstall
https://stackoverflow.com/questions/30336351/does-vagrant-destroy-also-remove-vagrant-plugins

만약에 플래이북을 직접 실행이 가능한 리눅스 환경이라면?
https://stackoverflow.com/questions/20952689/ansible-ssh-forwarding-doesnt-seem-to-work-with-vagrant

전반적으로 vagrant와 ansible을 잘 설명해 놓은 곳 
https://adamcod.es/2014/09/23/vagrant-ansible-quickstart-tutorial.html

ssh-keygen을 편하게 ansible로 
https://coderwall.com/p/_ryxma/generate-ssh-keys-with-ansible

댓글 11개:

  1. 글잘보고있습니다. 따라하는도중에 문제가좀생겼는데 조언을좀 구하고싶어서 글을 남깁니다.
    ansible all -m ping 시
    ECDSA key fingerprint is SHA256:WBe/1soVmsa2KNIJZyemAT2gS8zJIMq7+DuQgV5Mwik.
    ECDSA key fingerprint is MD5:e7:64:13:08:73:37:c3:17:f2:b4:4c:46:2f:d6:3a:f6.
    키가 이렇게 나오는점과
    YES 입력후
    [vagrant@ansible-server ~]$ ansible all -m ping -k
    SSH password:
    node02 | UNREACHABLE! => {
    "changed": false,
    "msg": "Authentication failure.",
    "unreachable": true
    }
    node01 | UNREACHABLE! => {
    "changed": false,
    "msg": "Authentication failure.",
    "unreachable": true
    }

    이렇게 출력되는 이유를좀 알고싶습니다

    답글삭제
  2. 다른 글을 보시면 있을텐데 known_host가 등록이 안되어서 키 교환하느라 그렇답니다~

    답글삭제
    답글
    1. 답변 정말감사드립니다

      삭제
    2. 알아보는도중 오랜시간이 걸렸지만 원인은 ssh가 일치하지않아 통신이 안됬던걸로 간주하여 수동으로 입력해주었습니다. 조언정말 감사드립니다

      삭제
    3. 별말씀을요. 완벽한 도움이 되지 않은거 같은데..ㅜㅜ 그래도 좋은 말씀에 감사드립니다.

      삭제
  3. 우선 글 너무 감사합니다. 현재 ansible-server에 접속해서 ansible all -m ping -k -K 명령어를 쳤을 때 ssh password가 나오는데 이때 무슨 값을 입력해야되는건가요? password를 설정한 기억이 없는데...

    답글삭제
    답글
    1. 디폴트 패스워드는 vagrant 라 이걸 입력하시면 될꺼에요~^^

      삭제
    2. 감사합니다. 문제해결했습니다.

      삭제
  4. 작성자가 댓글을 삭제했습니다.

    답글삭제
  5. 작성자가 댓글을 삭제했습니다.

    답글삭제