를 이용한 발 환 Ansible과 Vagrant 개 경구 김 (http://knight76.tistory.com) 용환 축 #1 이 글은 양이 많은 관계로 다음 주소의 글에 게재된 내용으로 축소되었다. 오타가 많고 정리가 안되었으나, 배경지식을 많이 할애한 관계로 공개한다. ( http://knight76.tistory.com/entry/%ec%86%8c%ed%94%84%ed%8a%b8%ec%9b%a8%ec %96%B4 %EA%B3%B5%ED%95%99%EC%84%BC%ED%84%B0 %EA%B8%B0%EA%B3% A0 Vagrant%EC%99%80 Ansible%EC%9D%84 %EC%9D%B4%EC%9A%A9%ED%95%9C %EA%B0%9C%EB%B0%9C %ED%99%98%EA%B2%BD ) 순서 상 머신 이용 배경 상화 존 임베디드 / 스마트폰 개발 환경 데스크탑 또는 서버 환경 Part 1 1. 가 1.1. 가 1.2. 기 1.3. 의 소개와 설치 소개 설치 실행 2. Vagrant 2.1. Vagrant 2.2. Vagrant 2.3. Vagrant 1. 가 상 머신 이용 배경 1.1. 가 상화 가상화는 컴퓨터에서 컴퓨터 리소스의 추상화를 일컫는 광범위한 용어이다. "물리적인 컴퓨터 리소스의 특징을 다른 시스템, 응용 프로그램, 최종 사용자들이 리소스와 상호 작용하는 방식으로부터 감추는 기술"로 정의할 수 있다. 기업은 서버 가상화를 통해 하나의 컴퓨터에서 동시에 1개 이상의 운영체제를 가동 시킬 수 있다. 대부분의 서버는 단지 용량의 10~15%만 사용하는데, 가상화는 이런 서버의 효용률(utilization rate)을 70% 그 이상으로 올릴 수 있다. 높은 수준의 효용률은 같은 분량의 업무 처리에서 요구하는 컴퓨터 수를 줄여준다. (출처 : http://ko.wikipedia.org/) 저자가 집중하는 가상화는 어플리케이션에 대해서 개발자나 테스터 중심으로 개발/테스트/재현 가능한 시나리오를 찾아줄 수 있는 환경이다. 즉 테스트 환경을 개발자/테스터와 함께 공유하고 개발 또는 테스팅을 로컬 환경에서 빨리 실행할 수 있도록 하는 것이 필요하다. 개발자 및 테스터는 테스팅 환경에 집중하기 때문에 성능 좋은 하드웨어를 갖춘 물리 장비를 여러 개의 가상화 장비로 분리하는 서버 가상화에 관한 얘기는 본 글에서는 하지 않도록 하겠다.
개발자의 입장에서는 개발자끼리 서로 환경을 공유할 수 있도록 하여 테스트를 진행할 수 있도록 하고, 다양한 컴포넌트들이 엮여 있는 환경에서도 하나의 가상머신으로 모을 수 있으니 개발 환경을 빠른 시간 내에 테스트할 수 있다. 게다가 CI (Continuous Integration)를 로컬 PC에서 진행할 수 있다. 가상화 환경에서 어플리케이션 또는 플랫폼과 DB를 설치 및 배포 한 후 CI 서버를 연동할 수 있는 환경이 될 수 있다. 특히, 원래 CI 에서는 DB를 삭제하고 새롭게 설치해서 완벽한 Integration 테스트를 진행하는 것을 권고한다. 그러나 실제 개발 환경에서는 DB를 로컬이 아닌 IDC 내의 장비로 활용했기 때문에 실질적인 CI를 진행할 수 없었다. 그러나 가상환경을 구축하게 되면 진정한 CI로 활용할 수 있다. 테스터의 입장에서는 미리 서버에 배포되어 있는 버전을 테스트하지 않고, 개발 중인 테스트 버전을 로컬환경에서 사전에 테스트함으로서 보다 높은 테스팅 품질 지표를 달성할 수 있도록 노력해볼 수 있다. 즉, 블랙 박스 테스트를 로컬환경에서 쉽게 진행할 수 있다. Jmeter나 SoapUI, WebDriver 를 이용해 테스트를 진행하고, 빠른 피드백을 개발팀에 전달할 수 있도록 해준다. 1.2 기 존 임베디드 / 스마트폰 개발 환경 임베디드 소프트웨어 개발자는 임베디드 개발 회사에 고용되어 해당 제품을 출시할 수 있도록 노력한다. 제품을 출시한 후에는 고객으로부터 오는 다양한 문제들과 VOC 이슈들을 해결해야 한다. 만약 테스트 환경이 구축되지 않는다면 발생되는 이슈, 문제들의 해결을 충분히할 수 없을 것이다. 테스트 환경이 구축된다 하더라도 로컬 PC 가 아닌 임베디드 기기 안에서 진행하면 많은 시간이 소요될 수 밖에 없다. 실제 안드로이드 단말을 사용하기 전에는 보드, 모니터, 셋탑박스, 핸드폰 단말에 이슈, 문제들을 재현할 수 있도록 테스트 환경 구축을 한다. 이러한 환경 구축을 위해 크로스 컴파일과 버닝(Burn, 이미지를 단말에 저장하는 말을 의미, CD/DVD를 굽는다 라는 표현과 같다고 보면 된다.) 시리얼 또는 패러릴 케이블을 이용하면, 최소 1시간에서 최대 3시간까지 소요되었다. <그림 1>과 같이 패러럴 포트를 이용해서 테스트를 진행하는 과정을 매번 했다. 로컬 PC에서 타겟 임베디드 보드에서 동작할 수 있는 바이너리 이미지를 타겟 보드로 전송하고, 그 디버그 내용을 받아서 테스트하는 과정이 일반적이었다. 따라서 많은 시간이 소요되곤 했다.
< 그림 1> 패러럴포트를이용한개발환경 ( 출처 : http://www.ebay.com/ ) 개발능력이좋은회사의경우에는이런시간을최소화하고, 생산효율성을높이기위한시도를하였다. 또는개발플랫폼을좋은회사의제품을이용해개발및테스트 / 디버깅시간을최소화시킬수있었다. < 그림 2> 과같이 OS 업체의제품의개발도구를이용한방법이있었다. MS 제품의경우에는 Pocket Pc 나 Windows Embedded 제품을사용할수있었다. 그리고 < 그림 3> 과같이특정하드웨어업체의에뮬레이터툴을이용하는방법도있다.
< 그림 2> MS 의 Visual studio 를이용한 pocket pc 테스트환경 ( 출처 : http://msdn.microsoft.com/)
< 그림 3> 하드웨어업체, 삼성의에뮬레이터툴 ( 출처 : http://mobiforge.com/) 방송셋탑박스의경우는방송스트리밍 (MPEG 2, MPEG 4) 인코딩데이터를복사해임베디드시스템에서재현을할수있는에뮬레이터를개발한경우가있다. 그래서 core dump ( 메모리영역분석 ) 나, 재현가능시나리오및바이너리내용을가지고재현하면서재현가능한문제점을해결하기도했다. 마치게임에뮬레이터와똑같은원리라할수있다. < 그림 4> 는한업체의가상머신에뮬레이터환경을보여준다.
< 그림 4> DTV Emulator ( 출처 : http://developer.lgappstv.com/) 하드웨어업체나전문단말소프트웨어업체의에뮬레이터환경은가상화를근간으로하고있다. 에뮬레이터가완벽할수록제품의품질은완벽해져갔다. 저자가근무했던임베디드미들웨어업체에서는이런에뮬레이터를보다완벽하게만들어테스트 / 디버깅을똑같이재현할수있었다. 마찬가지로 < 그림 5> 의구글의안드로이드에뮬레이터나 < 그림 6> 의애플의아이폰에뮬레이터등과같이 App 개발자에게개발환경을제공하고있다. 일부기능을제외하고는많은부분을에뮬레이터에서테스트 / 디버깅을지원하고있다. 특히시스템및앱로그출력을편하게하여높은품질을이룰수있도록도와주고있다.
< 그림 5> 구글의안드로이드에뮬레이터 ( 출처 : http://www.akeric.com/) 참고로구글안드로이드에뮬레이터대신성능좋고기능좋은 Genymotion(http://www.genymotion.com/) 이라는에뮬레이터를많이사용하고있다. non commercial ( 기능한정 ) 에게만무료이니 http://widzard.tistory.com/23 를참조해사용하면된다.
<그 림 6> 애플의 iphone 개발환경 (출처 : http://www.hakbox.com/) 특히 바이너리를 서로 공유하여 문제나 이슈에 대한 재현을 쉽게 할 수 있도록 방법을 제공하고 있다. 그러나 안드로이드의 경우는 파편화나 UI 크기, 하드웨어 업체의 포팅 이슈가 있어서 단말기마다 테스트를 진행해야 하는 불편함이 존재하고 있다. 1.3 데스크탑 또는 서버 환경 오래된 역사를 가지고 있다는 MS 기반의 C++, Basic 또는 Delphi 데스크탑 어플리케이션의 경우는 작업 환경이 곧 개발 환경이기 때문에 쉽게 개발이 가능했다. 다만 운영체제에 의존성을 가지고 있기 때문에 다양한 운영체제에서 바이너리 실행을 할 수 있는 여건이 중요하다. 특히 MS 윈도우 운영체제 기반의 어플리케이션의 경우에는 윈도우 버전마다 조금씩 다른 변화가 있기 때문에 여러 MS 윈도우를 실행시킬 수 있는 환경을 두어야 했다. PC가 없는 환경이라면 VMware의 fusion, Oracle의 Virtual Box, parallels, funsion, boot camp와 같은 가상화 솔루션을 이용해서 한 대의 PC 에서 다양한 운영체제를 설치하여 어플리케이션을 테스트 했다.
이런데스크탑어플리케이션개발의불편함으로한때는 Java 가각광을받기도했다. MS 제품뿐아니라리눅스와같은이질적인운영체제에서도실행가능한 Java 는 < 그림 6> 처럼한번만작성 ( 설치 ) 하면어디서든실행시킬수있다는 Write once, run anywhere 캐치프레이즈로이목을끌기도했다. < 그림 6> 윈도우플랫폼의 Java 의전략 Write once, run anywhere ( 출처 : http://dirkriehle.com/2011/06/30/the java ip story/) 실제많은개발업체들의진행했던예제를들고실제어떻게방향을바꿀지얘기하도록한다. Java 라는가상머신 (Virtual Machine) 상에서동작하는어플리케이션은성능이슈로늘문제가있었다. 그러나점점 PC 성능이좋아지고, Java 와 Servlet Container(resion, tomcat, jetty) 위에동작하는웹서버 ( 여기서말하는웹서버는 Web/WAS 를총칭하는의미 ) 는윈도우, 리눅스의대부분의운영체제에서도충분히개발 / 테스트 / 재현이가능하게되었다. 따라서 < 그림 7> 처럼 DB 는 IDC 내의서버를활용하고, 웹서버는로컬환경을이용하는단순한웹서버개발환경을사용하고있었다. 따라서개발자가테스트하는동안테스터는개발버전을테스트할수없고개발서버로배포될때까지기다려야했다. 이방법은 2 가지이슈가있다. 첫번째, DB 를초기화해다시셋팅하고 DAO 유닛테스트를통해서 CI (Continuous Integration) 를진행하는과정을할수없다. 그이유는 DB 는모든이들이함께쓰는장비이기때문이다. 그래서이부분은진행을할수없다. DB 데이터를모든개발자가공유할수는있지만, 모든테스트를다진행할수없는형태이다.
두번째, 테스터는개발되고있는버전이완료되었다는테스트시작을알려주기전까지는기다려야한다. 애자일개발방법으로진행할때, 2 주단위로구현하고테스트하는일정이존재하면개발자가개발서버에따로배포해줄때까지기다리는것말고는마땅히할수있는방법이없다. < 그림 7> 일반적인웹서버의개발방식 이런불편함을제거하기위해서 < 그림 8> 과같이하나의가상머신으로통일화를할수있다. 웹서버와 Database 를함께설치하면하나의개발서버를구축할수있고, 이구축된가상머신은개발자와테스터간에공유될수있을것이다. 또한설정을통해서웹서버에접속할포트역시하나의포트로통일을시킬수있고, 필요하다면로컬 PC 에서충돌하지않을포트를지정하여서비스할수있다.
< 그림 8> 가상머신내에웹서버와 Database 를설치한환경 이런장점은 3 가지를제공한다. 위에서이미언급한 2 문제를해결할뿐아니라더좋은장점들이추가된다. 첫째, CI 철학에맞게가상머신에서 DB 초기화를진행할수있다. 따라서 DAO 테스트를완벽하게진행할수있다. ( 물론, staging 환경에맞게 production 레벨에서는테스트코드가동작되지못하게진행해야한다.) 둘째, 테스터는가상머신만받을수있으면언제든지테스트를진행할수있다. 테스터가개발자로부터어느버전을테스트할지를미리알고있는상태라면, 해당소스의버전을다운로드해테스트를병행으로진행하면서스크럼팀의빠른스피드의개발을진행시킬수있도록도와준다. 셋째, 개발자 테스터뿐아니라, 연동이필요한다른개발자에게도움을줄수있다. 즉, 일종의개발환경킷을제공하는효과를가질수있다. < 그림 9> 처럼가상머신위에 API 테스트 Layer (API 테스트베드 ) 를두어웹서버의환경을두어 Acceptance Test 환경을만들수있다. 이런사례는 Facebook 나 Twitter 의개발자센터와같은비슷한모델로구현이가능하다. 넷째, 사무실 IP 에서 IDC 로의연결이방화벽으로막혀있는환경에서유용하다. 전자금융거래법 ( 일명전금법 ) 이적용되거나, 개인정보를가지고테스트해야하는상황에서는 < 그림 7> 과같은방식은더이상개발이불가능해진다. 즉, 로컬개발환경에서 Production 이아닌개발단계라할지라도 IDC 의서버나 DB 에연결하는방식이어려워진다. 보안이더욱대두되는상황이기때문이다. 다섯째, 로컬환경에서소프트웨어테스트를위해새로설치하고삭제하는것이번거로울수있다. 예를들어새로출시된 JDK 8 위에서동작하는 WAS 를테스트하기위해서 JDK8 을설치했다가테스트하고다시삭제해야하는경우가있는데, 가상머신을이용해서이런번거로움을해결할수있다.
< 그림 9> 가상머신위에 API Test Layer 를위치한개발환경 Practical 한개발자환경은 < 그림 9> 에서조금더진화한 < 그림 10> 의모습이다. 개발자환경기반위에다양한유틸리티를포함한그림이될것이다. 일반유틸리티부터개발자나테스터를위한소스배포툴, 재시작스크립트, DB 쪽스크립트를포함한그림이될것이다.
< 그림 10> < 그림 9> 에서형상관리툴과필요한 Utility 가포함된개발환경 가상머신이존재하는것은간단한작업을하는웹서버를테스트하기위함보다는여러개의웹서버와 DB 또는 Nosql 이존재하는다양한스토리지가존재하는테스트환경을테스트하는경우가더많다. 이를위해서는가상머신내에서는다양한시스템을설치하고테스트할수있는환경을 < 그림 11> 과같이구성할수있을것이다.
< 그림 11> 연동가능한개발환경과 API Test Layer 가포함된가상머신 이렇게만들가상머신은 Vagrant 로구현이가능하다. 그리고배포는 puppet, chef, ansible 과같은자동화툴로가상머신을만들수있는배포기능을추가할수있다. 유지보수가가능한가상머신을계속만들수있다. 2. Vagrant 의소개와설치 2.1 Vagrant 소개 Vagrant 는가상머신관리도구이다. 즉, 가상머신플랫폼 (Hypervisor) 이먼저설치되어야하고그가상머신플랫폼을관리하는 CLI 로관리하는도구가 Vagrant 이다.
Vagrant 를쓰는이유는자동화가편하다는것이다. 즉, virtual box 를실행시키려면설정과설치, 시작 / 중지 / 종료등이런작업을일일이해야하는작업을 Vagrant 를이용하면 CLI(command line interface) 기반에서할수있다. 즉, < 그림 12> 의예처럼 Mac OS 에서 Windows 7 이나 Ubuntu 리눅스를실행시키려면 Oracle VM Virtual Box 에서이미지를다운로드해서이미지의설정을정하고시작 / 중지 / 종료버튼을눌러야한다. 이과정은사람이수동으로작업해야한다. < 그림 12> Oracle VM Virtual Box 화면 그러나, Vagrant 는이를 CLI 상에서쉽게해결할수있다. 예를들어시작은 vagrant up, 종료는 vagrant halt 이다. 자세한설명은뒤에서하도록한다. Vagrant 는가상서버를구현하는좋은툴로서, 지금까지얘기한모든것이가능하도록지원한다. 우선간단하게소개하자면다음과같다. 라이선스 : MIT License
개발언어 : Ruby 메인개발자 : Mitchell Hashimoto (http://mitchellh.com/) 홈페이지 : http://www.vagrantup.com/ 사용할수있는이미지목록 : http://www.vagrantbox.es/ 버전 1.6.3 (2014.7.7 현재 ) 1.0.0 (2012.3.7) 문서 : http://docs.vagrantup.com/v2/ 2.2 Vagrant 설치 < 그림 13> 처럼 https://www.virtualbox.org/wiki/downloads 에접속해서가장먼저가상머신플랫폼 (Hypervisor) 인 Oracle 의 Virtual Box 를설치한다. Oracle Virtual Box 는 GPL License 이기때문에사용하는데는별다른이슈가없다. 저자는 Mac OS X 사용자이므로 OS X hosts 용을다운로드했다. < 그림 13> Oracle 의 Virtual Box 다운로드화면 ( 출처 : https://www.virtualbox.org) 다음은 Vagrant 설치를진행한다. < 그림 14> 와같이 http://www.vagrantup.com/ 에접속해서하단좌측 Download 버튼을클릭한다.
< 그림 14> vagrant 홈페이지화면 ( 출처 : http://www.vagrantup.com/) Download 화면 (http://www.vagrantup.com/downloads.html) 은 < 그림 15> 와같다. 여기에서 Vagrant 를운영체제에맞게설치한다. 본필자는 MAC OS X 를사용하므로 MAC OS X 기준으로설명하도록하겠다.
< 그림 15> vagrant 다운로드화면 ( 출처 : http://www.vagrantup.com/downloads.html) 설치한이후에는 Vagrant 의버전을확인한다. $ vagrant v Vagrant 1.6.3 2.3 Vagrant 실행 2.3.1 가상머신이미지다운로드및 vagrant init ( 초기화 ) 먼저프로젝트디렉토리를생성하고생성한디렉토리로접근한다. $ mkdir idp testbox $ cd idp testbox Vagrant 이미지를다운로드해서 local 에저장한다. 아래예는 precise (ubuntu 12.04) 64 비트버전을다운로드한예시이다. precise64 는 Vagrant 안에서사용되는이름또는별칭이다. OS 가설치된가상머신이미지를 box 로생각하면된다. $ vagrant box add precise64 http://files.vagrantup.com/precise64.box
참고로저장된 vagrant 가상머신이미지는 Mac 의경우 ~/.vagrant.d/boxes 디렉토리에저장된다. ~/.vagrant 디렉토리에는가상머신에대한메타정보가포함되어있다. vagrant init 명령어를사용하여 box 를초기화한다. 그래서 vagrant 메타파일인 Vagrantfile 을생성하도록한다. $ vagrant init A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant. $ ls Vagrantfile Vagrantfile 파일은다음과같다. ruby 파일템플릿이고, 주석과프로퍼티설명이되어있다. # * mode: ruby * # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do config # All Vagrant configuration is done here. The most common configuration # options are documented and commented below. For a complete reference, # please see the online documentation at vagrantup.com. # Every Vagrant virtual environment requires a box to build off of. config.vm.box = "base" # Disable automatic box update checking. If you disable this, then # boxes will only be checked for updates when the user runs # `vagrant box outdated`. This is not recommended. # config.vm.box_check_update = false # Create a forwarded port mapping which allows access to a specific port # within the machine from a port on the host machine. In the example below, # accessing "localhost:8080" will access port 80 on the guest machine. # config.vm.network "forwarded_port", guest: 80, host: 8080 # Create a private network, which allows host only access to the machine # using a specific IP. # config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network. # Bridged networks make the machine appear as another physical device on # your network. # config.vm.network "public_network" # If true, then any SSH connections made will enable agent forwarding. # Default value: false # config.ssh.forward_agent = true # Share an additional folder to the guest VM. The first argument is # the path on the host to the actual folder. The second argument is # the path on the guest to mount the folder. And the optional third # argument is a set of non required options. # config.vm.synced_folder "../data", "/vagrant_data" # Provider specific configuration so you can fine tune various # backing providers for Vagrant. These expose provider specific options. # Example for VirtualBox: # # config.vm.provider "virtualbox" do vb # # Don't boot with headless mode # vb.gui = true # # # Use VBoxManage to customize the VM. For example to change memory: # vb.customize ["modifyvm", :id, " memory", "1024"] # end # # View the documentation for the provider you're using for more # information on available options. # Enable provisioning with CFEngine. CFEngine Community packages are # automatically installed. For example, configure the host as a # policy server and optionally a policy file to run: # # config.vm.provision "cfengine" do cf # cf.am_policy_hub = true # # cf.run_file = "motd.cf" # end # # You can also configure and bootstrap a client to an existing # policy server: # # config.vm.provision "cfengine" do cf # cf.policy_server_address = "10.0.2.15" # end
# Enable provisioning with Puppet stand alone. Puppet manifests # are contained in a directory path relative to this Vagrantfile. # You will need to create the manifests directory and a manifest in # the file default.pp in the manifests_path directory. # # config.vm.provision "puppet" do puppet # puppet.manifests_path = "manifests" # puppet.manifest_file = "site.pp" # end # Enable provisioning with chef solo, specifying a cookbooks path, roles # path, and data_bags path (all relative to this Vagrantfile), and adding # some recipes and/or roles. # # config.vm.provision "chef_solo" do chef # chef.cookbooks_path = "../my recipes/cookbooks" # chef.roles_path = "../my recipes/roles" # chef.data_bags_path = "../my recipes/data_bags" # chef.add_recipe "mysql" # chef.add_role "web" # # # You may also specify custom JSON attributes: # chef.json = { :mysql_password => "foo" } # end # Enable provisioning with chef server, specifying the chef server URL, # and the path to the validation key (relative to this Vagrantfile). # # The Opscode Platform uses HTTPS. Substitute your organization for # ORGNAME in the URL and validation key. # # If you have your own Chef Server, use the appropriate URL, which may be # HTTP instead of HTTPS depending on your configuration. Also change the # validation key to validation.pem. # # config.vm.provision "chef_client" do chef # chef.chef_server_url = "https://api.opscode.com/organizations/orgname" # chef.validation_key_path = "ORGNAME validator.pem" # end # # If you're using the Opscode platform, your validator client is # ORGNAME validator, replacing ORGNAME with your organization name. # # If you have your own Chef Server, the default validation client name is # chef validator, unless you changed the configuration. # # chef.validation_client_name = "ORGNAME validator"
end 주요프로퍼티에대한설명을진행한다. 자세한내용은 http://docs.vagrantup.com/v2/vagrantfile/index.html 을참조한다. VAGRANTFILE_API_VERSION 은 Vagrantfile API/syntax 버전으로상수 2 값을지정해야동작한다. config.vm.box 의값은 vagrant box add 명령어로저장한이름을입력하면된다. 만약 vagrant box add precise64 http://~ 라하면 precise64 가 config.vm.box 의값으로지정하면된다. 눈치챘겠지만, 하나의이름만쓸수있는것이아니라, 여러개의이름으로저장이가능하다. config.vm.box 의이름을지정하지않으면 base 로디폴트로등록된다. $ vagrant box add precise64 http://files.vagrantup.com/precise64.box config.vm.network 은 network 에관련된설정을수정할수있다. network 타인은 forwarded_port, private_network, public_network 와같은식별자를따로두어 network 레벨의설정을진행할수있다. 특정포트를포워딩하거나, DHCP, 고정 IP 등을지정할수있다. 예를들어다음설정의내용은가상서버 (guest) 에서 8080 으로띄운서버가있다면가상머신을실행한 OS(host, 앞으로 Host 라는단어사용 ) 에서는 80 으로바운드 (bound) 하도록한다는내용이다. config.vm.network "forwarded_port", guest: 80, host: 8080 그리고 vagrant 가상서버에 provisioning 툴을 CFEngine, VBoxManage, Puppet, Chef 중하나로쓸수있는템플릿을제공하고있다. 저자는 ansible 을이용할예정이니때문에관련없는내용과주석을제외해서 Vagrantfile 파일을아래와같이수정한다. # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do config config.vm.box = "precise64" config.vm.box_url = "http://files.vagrantup.com/precise64.box" config.vm.hostname = "web" config.vm.network "private_network", ip: "192.168.1.50" config.vm.network :forwarded_port, guest: 9966, host: 8888
config.vm.provision "ansible" do ansible ansible.playbook = "playbook.yml" end end config.vm.box 에 precise64 로선택하고 config.vm.box_url 을이미지를다운로드할수있는 url 로지정했다. 가상머신의 host 이름을 web 이라지칭했다. config.vm.network 프로퍼티를이용해 private_network ip 를사용하도록했다. 그리고가상서버에서 9966 포트로띄운서버를 Host 서버에서 8888 포트로포트포워딩방식을사용한다. 예를들어가상서버에서는 http://192.168.1.50:9966/view 라는주소를가진웹주소를호스트서버에서는 http://127.0.0.1:8888/view 라는주소로접근할수있음을의미한다. config.vm.provision 은가상서버로어떤툴로 provision( 배포 ) 할것인지를지정하는것이다. config.vm.provision ansible 의의미는 ansible 을이용해서하겠다는의미를가진다. ansible.playbook = playbook.yml 은 ansible provisioning 메타파일로 playbook.yml 파일을사용하겠다는의미를가진다. ansible 에서사용하는스크립트를 playbook 이라고하며 yml 파일은 yaml 이라는표준스펙을지킨다. 자세한얘기는다음장에서설명할예정이다. 예제로 playbook.yml 을다음과같이저장한다. 간단하게설명하면 vagrant 계정으로 ls al /home 결과를화면에출력하라는샘플정보이다. hosts: all user: vagrant tasks: name : test action: command ls al /home register: vagrant debug: var=vagrant.stdout_lines 2.3.2 vagrant up
< 그림 16> 평상시 Virtual Box 상태 < 그림 16> 과같이평소가상머신의상태는 전원꺼짐 " 으로되어있다. 사용자가 vagrant up 명령를이용하면 < 그림 17> 과같이가상머신의상태는 실행중 " 으로바뀐다.
< 그림 17> vagrant up 명령이후가상머신의 status 가 실행중 " 으로변경된화면 vagrant up 명령시가상머신이실행되면서관련데몬들이실행하고로그가출력된다. $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Clearing any previously set forwarded ports... ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat default: Adapter 2: hostonly ==> default: Forwarding ports... default: 9966 => 8888 (adapter 1) ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222
default: SSH username: vagrant default: SSH auth method: private key ==> default: Machine booted and ready! ==> default: Checking for guest additions in VM... default: The guest additions on this VM do not match the installed version of default: VirtualBox! In most cases this is fine, but in rare cases it can default: prevent things such as shared folders from working properly. If you see default: shared folder errors, please make sure the guest additions within the default: virtual machine match the version of VirtualBox you have installed on default: your host and reload your VM. default: default: Guest Additions Version: 4.2.0 default: VirtualBox Version: 4.3 ==> default: Setting hostname... ==> default: Configuring and enabling network interfaces... ==> default: Mounting shared folders... default: /vagrant => /development/work/idp testbox ==> default: Machine already provisioned. Run `vagrant provision` or use the ` provision` ==> default: to force provisioning. Provisioners marked to run always will still run. 로그상으로확인했듯이 provision 된것을확인할수있다. 상황에따라서는 provision 하지않은가상머신시작을하고싶을때는 no provision 을명령어뒤에붙여사용한다. provision 되지않은상태임을확인할수있다. $ vagrant up no provision Bringing machine 'default' up with 'virtualbox' provider... ==> default: Clearing any previously set forwarded ports... ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat default: Adapter 2: hostonly ==> default: Forwarding ports... default: 9966 => 8888 (adapter 1) default: 22 => 2222 (adapter 1) ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key ==> default: Machine booted and ready! ==> default: Checking for guest additions in VM... default: The guest additions on this VM do not match the installed version of default: VirtualBox! In most cases this is fine, but in rare cases it can default: prevent things such as shared folders from working properly. If you see
default: shared folder errors, please make sure the guest additions within the default: virtual machine match the version of VirtualBox you have installed on default: your host and reload your VM. default: default: Guest Additions Version: 4.2.0 default: VirtualBox Version: 4.3 ==> default: Setting hostname... ==> default: Configuring and enabling network interfaces... ==> default: Mounting shared folders... default: /vagrant => /development/work/idp testbox ==> default: Machine not provisioning because ` no provision` is specified. 2.3.3 vagrant suspend 가상머신을잠깐중지하려면 vagrant suspend 명령을실행한다. < 그림 18> 과같이가상머신의상태가 저장중 " 으로바뀐다. $ vagrant suspend ==> default: Saving VM state and suspending execution
< 그림 18> vagrant suspend 명령이후가상머신의 status 가 저장됨 " 으로변경 2.3.3 vagrant resume 중지된가상머신을다시재개하려면 vagrant resume 을실행하면된다. < 그림 17> 과같은화면으로출력한다. $ vagrant resume ==> default: Resuming suspended VM... ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key default: Warning: Connection refused. Retrying... ==> default: Machine booted and ready!
2.3.4 vagrant provision 위에서언급했던 playbook.yml 을기반으로 provisioning 을한다. vagrant provioning 명령을하면다음과같이가상머신서버로들어가 ls al /home 결과를화면에출력한다. $ vagrant provision ==> default: Running provisioner: ansible... PLAY [all] ******************************************************************** GATHERING FACTS *************************************************************** ok: [default] TASK: [test] ****************************************************************** changed: [default] TASK: [debug var=vagrant.stdout_lines] **************************************** ok: [default] => { "vagrant.stdout_lines": [ "total 12", "drwxr xr x 3 root root 4096 Sep 14 2012.", "drwxr xr x 25 root root 4096 Jun 27 06:00..", "drwxr xr x 7 vagrant vagrant 4096 Jun 30 05:50 vagrant" ] } PLAY RECAP ******************************************************************** default : ok=3 changed=1 unreachable=0 failed=0 만약 provision 을 ansible 이아닌 shell 기반으로수정한다. Vagrantfile 을아래예제의볼드체로표시한부분 ( 가상머신에서 test.sh 을실행 ) 하는부분으로수정한다. $ cat Vagrantfile # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do config config.vm.box = "precise64" config.vm.box_url = "http://files.vagrantup.com/precise64.box" #config.vm.network :public_network config.vm.hostname = "web" config.vm.network "private_network", ip: "192.168.1.50" config.vm.network :forwarded_port, guest: 9966, host: 8888
end config.vm.provision :shell, :path => "test.sh" test.sh 파일은간단히 /home 디렉토리출력하는것이다. $ cat test.sh #!/bin/sh ls al /home 그결과는 ansible 로이용했던것과결과가같다. shell 결과 ansible 결과를비교해볼때고급스러운개념이있다는것을확인할수있다. $ vagrant provision ==> default: Running provisioner: shell... default: Running: /var/folders/nx/lkzmd37d6fj3sg9kg5flt3yr0000gp/t/vagrant shell20140710 58428 mkmu26.sh ==> default: stdin: is not a tty ==> default: total 12 ==> default: drwxr xr x 3 root root 4096 Sep 14 2012. ==> default: drwxr xr x 25 root root 4096 Jun 27 06:00.. ==> default: drwxr xr x 7 vagrant vagrant 4096 Jun 30 05:50 vagrant 2.3.5 vagrant status 가상머신 status 정보를보려면 vagrant status 명령을사용한다. 아래와같이영어로실행중을의미하는 running 이출력되는것을볼수있다. $ vagrant status Current machine states: default running (virtualbox) The VM is running. To stop this VM, you can run `vagrant halt` to shut it down forcefully, or you can run `vagrant suspend` to simply suspend the virtual machine. In either case, to restart it again, simply run `vagrant up`.
2.3.6 vagrant ssh vagrant ssh 를이용하여가상머신서버로접근이가능한지테스트한다. ssh 로가상서버로접근한다. 위에서 config.vm.hostname 으로지정된호스트명 web 을확인할수있으며, 기본계정은 vagrant 이다. $ vagrant ssh Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0 23 generic x86_64) * Documentation: https://help.ubuntu.com/ Welcome to your Vagrant built virtual machine. Last login: Wed Jul 9 12:48:04 2014 from 10.0.2.2 vagrant@web:~$ uname a Linux web 3.2.0 23 generic #36 Ubuntu SMP Tue Apr 10 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux ssh 포트가열려있으니 ssh 연결하는방법은당연히존재한다. RSA 알고리즘의 ssh key 인 id_rsa( 개인키 ), id_rsa.pub 키를 $HOME/.ssh 에생성하도록한다. $ ssh keygen t rsa host 의 ssh 키를가상서버에복사한다. vagrant 설정에서지정한 config.vm.network "private_network" ip 로지정한 ip 를입력한다. $ ssh copy id vagrant@192.168.1.50 이후 ssh 연결시정상적으로접근한것으로나온다. $ ssh vagrant@192.168.1.50 Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0 23 generic x86_64) * Documentation: https://help.ubuntu.com/ Welcome to your Vagrant built virtual machine. Last login: Wed Jul 9 13:25:58 2014 from 10.0.2.2 vagrant@web:~$ 2.3.6 vagrant halt vagrant halt 명령을내려가상머신을종료한다. < 그림 16> 의상태로복귀하게된다. $ vagrant halt
==> default: Attempting graceful shutdown of VM 2.3.7 vagrant package vagrant package 를통해서패키징하여다른개발자에게전달할수있다. $ vagrant package ==> default: Clearing any previously set forwarded ports... ==> default: Exporting VM... ==> default: Compressing package to: /development/work/idp testbox/package.box $ ls al package.box total 2154256 rw r r 1 knight wheel 1102929920 7 9 22:38 package.box 가상머신을 virtual box 가상머신리스트에서삭제를할수있다. < 그림 19> 와같이 vagrant up 을해서 vagrant test_default_1401100903183_2186 이란이름을가진가상머신을실행했다.
< 그림 19> vagrant up 으로가상머신이 실행중 인화면. 2.3.7 vagrant destroy 이때 vagrant destroy 명령을내리면다시한번 destroy 를확인한다. y 를입력하고엔터를입력한다. $ vagrant destroy default: Are you sure you want to destroy the 'default' VM? [y/n] y ==> default: Forcing shutdown of VM... ==> default: Destroying VM and associated drives... ==> default: Running cleanup tasks for 'ansible' provisioner... virtual box 가상머신리스트화면을보면 vagrant test_default_1401100903183_2186 이름을가진가상머신은삭제되었다.
< 그림 20> 실행중이었던가상머신이삭제된화면 2.3.8 vagrant reload Vagrantfile 수정이후반영을하려면 vagrant reload 명령을실행하면설정파일을읽고반영한다. 가상머신도종료했다가다시실행한다. $ vagrant reload ==> web: VM not created. Moving on... ==> testing: Attempting graceful shutdown of VM... ==> testing: Clearing any previously set forwarded ports...
2.3.9 가상서버이름지정하기 Vagrantfile 에지금까지 name 을주지않았다. 아래와같이 config.vm.box 와 config.vm.box_url 만지정하면 < 그림 21> 과같이 디렉토리명 _testing_1405275482326_94624 의명을가지게된다. Vagrant.configure(VAGRANTFILE_API_VERSION) do config config.vm.box = "precise64" config.vm.box_url = "http://files.vagrantup.com/precise64.box"... < 그림 21> 이름을따로주지않았을때나오는가상서버디폴트이름 virtual box 의화면에서이름을직접수정하지않고 Vagrantfile 의내용을수정하여이름을변경할수있다. 아래예제는 < 그림 21> 의디폴트가상서버이름을 web 으로바꾸는예제이다.
Vagrant.configure(VAGRANTFILE_API_VERSION) do config config.vm.box = "precise64" config.vm.box_url = "http://files.vagrantup.com/precise64.box" config.vm.provider :virtualbox do vb vb.name = "web" end.. end < 그림 22> 예제실행후가상서버의디폴트이름이 web 으로변경됨 2.3.10 여러가상서버 (Multiple VM) 을통제하기
다른디렉토리를하나생성한후, Vagrantfile 파일을아래와같이수정한다. 가상머신 2 대를동시에실행 / 종료또는하나만실행이가능하다. $ cat Vagrantfile # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do config config.vm.box = "precise64" config.vm.box_url = "http://files.vagrantup.com/precise64.box" config.vm.provision :shell, :path => "test.sh" end config.vm.define :web do web_config web_config.vm.box = "web" web_config.vm.define "foohost" do foohost end web_config.vm.hostname = "web" web_config.vm.network "private_network", ip: "192.168.1.50" web_config.vm.provider :virtualbox do vb vb.name = "web" end end config.vm.define :mysql do mysql_config mysql_config.vm.box = "mysql" mysql_config.vm.define "foohost" do foohost end mysql_config.vm.hostname = "mysql" mysql_config.vm.network "private_network", ip: "192.168.1.51" mysql_config.vm.provider :virtualbox do vb vb.name = "mysql" end end config.vm.box_url 으로정의한 "http://files.vagrantup.com/precise64.box" vagrant 이미지를다운로드해 web 과 mysql 이라는이름으로각각 virtual box 이미지의이름을변경한다. 가상머신이름 (config.vm.define) 과프로바이더 (config.vm.provider) 의이름을정의할수있다. 프로바이더 (config.vm.provider) 를잘활용하면 cpu 나 memory 설정을구체적으로지정할수있다. config.vm.provider "virtualbox" do v v.memory = 1024 v.cpus = 2
end vagrant up 명령을실행하면 web 과 mysql 이라는이름의가상서버를아래와같이실행한다. < 그림 23> 과같은화면을볼수있다. 두개의가상머신이실행중인것을확인할수있다. $ vagrant up Bringing machine 'web' up with 'virtualbox' provider... Bringing machine 'mysql' up with 'virtualbox' provider... ==> web: Clearing any previously set forwarded ports... ==> web: Clearing any previously set network interfaces... ==> web: Preparing network interfaces based on configuration... web: Adapter 1: nat web: Adapter 2: hostonly ==> web: Forwarding ports... web: 22 => 2222 (adapter 1) ==> web: Booting VM... ==> web: Waiting for machine to boot. This may take a few minutes... web: SSH address: 127.0.0.1:2222 web: SSH username: vagrant web: SSH auth method: private key ==> web: Machine booted and ready! ==> web: Checking for guest additions in VM... web: The guest additions on this VM do not match the installed version of web: VirtualBox! In most cases this is fine, but in rare cases it can web: prevent things such as shared folders from working properly. If you see web: shared folder errors, please make sure the guest additions within the web: virtual machine match the version of VirtualBox you have installed on web: your host and reload your VM. web: web: Guest Additions Version: 4.2.0 web: VirtualBox Version: 4.3 ==> web: Setting hostname... ==> web: Configuring and enabling network interfaces... ==> web: Mounting shared folders... web: /vagrant => /development/work/idp testbox2 ==> web: Machine already provisioned. Run `vagrant provision` or use the ` provision` ==> web: to force provisioning. Provisioners marked to run always will still run. ==> mysql: Clearing any previously set forwarded ports... ==> mysql: Fixed port collision for 22 => 2222. Now on port 2200. ==> mysql: Clearing any previously set network interfaces... ==> mysql: Preparing network interfaces based on configuration... mysql: Adapter 1: nat mysql: Adapter 2: hostonly ==> mysql: Forwarding ports... mysql: 22 => 2200 (adapter 1)
==> mysql: Booting VM... ==> mysql: Waiting for machine to boot. This may take a few minutes... mysql: SSH address: 127.0.0.1:2200 mysql: SSH username: vagrant mysql: SSH auth method: private key ==> mysql: Machine booted and ready! ==> mysql: Checking for guest additions in VM... mysql: The guest additions on this VM do not match the installed version of mysql: VirtualBox! In most cases this is fine, but in rare cases it can mysql: prevent things such as shared folders from working properly. If you see mysql: shared folder errors, please make sure the guest additions within the mysql: virtual machine match the version of VirtualBox you have installed on mysql: your host and reload your VM. mysql: mysql: Guest Additions Version: 4.2.0 mysql: VirtualBox Version: 4.3 ==> mysql: Setting hostname... ==> mysql: Configuring and enabling network interfaces... ==> mysql: Mounting shared folders... mysql: /vagrant => /development/work/idp testbox2 ==> mysql: Machine already provisioned. Run `vagrant provision` or use the ` provision` ==> mysql: to force provisioning. Provisioners marked to run always will still run.
< 그림 23> 가상머신 2 개를동시에실행시킨이후화면 만약각가상머신별로실행을따로하려면 vagrant up web 또는 vagrant up mysql 을사용한다. 참고로 json 파일로여러개의가상머신을생성할수있다. 아래블로그글을참고하면힌트를얻을수있을것이다. http://programmaticponderings.wordpress.com/2014/02/27/multi vm creation using vagrant andjson/ 3. 정리지금까지가상머신을활용한배경을이야기했고, vagrant 에대한설치, 소개를모두마쳤다. 다음회에서는 Ansible 을활용한방식을설명할예정이다. 참고자료
1. https://docs.vagrantup.com 2. https://www.virtualbox.org 3. http://stackoverflow.com/questions/17845637/vagrant default name