베이그런트(Vagrant) 튜토리얼
개발 환경 공유와 가상 머신 관리를 위한 커맨드라인 도구

들어가며

베이그런트는Vagrant는 미셸 하시모토Mitchell Hashimoto가 프로그래밍 언어로 루비Ruby로 개발하고 2010년 3월 처음 릴리스한 커맨드라인 인터페이스로 가상 머신 기반 개발 환경을 관리하는 도구입니다. 현재는 미셸 하시모토, 아몬 데드거Armon Dadgar 2012년 설립한 하시코프HashiCorp에서 오픈소스로 개발하고 있습니다. 하시코프는 클라우드 프로비저닝 테라폼Terraform*과 비밀 정보 관리 볼트Vault, 컨테이너 오케스트레이션 노마드Nomad 등 인프라 관리 도구들을 만드는 것으로 잘 알려져있습니다. 하시코프의 도구들은 대부분 프로그래밍 언어 Go로 개발되고 있습니다만, 루비로 만들어진 베이그런트는 이런 흐름의 선봉에 있는 도구라고 이야기할 수 있습니다. 베이그런트의 최신 버전은 2020년 11월 21일에 출시된 2.2.14 버전이며, 깃허브GitHub 루비 프로젝트 중 13번째로 스타를 많이 받은 프로젝트입니다.

* 44BITS에서도 테라폼의 기본 사용방법에 대해서 소개했었습니다: 테라폼(Terraform) 기초 튜토리얼: AWS로 시작하는 Infrastructure as Code

2010년대 초반에는 개발환경을 공유하는 표준화된 방법이 따로 없었기 때문에 가상 머신과 프로비저닝 도구를 조합해서 사용하는 베이그런트는 팀원들 간에 개발환경을 공유할 수 있는 아주 좋은 수단이었습니다. 2014년 도커Docker 1.0이 출시되는 등 지난 10년 간 인프라 분야에도 지각 변동이 있었습니다. 도커를 사용하면 가상 머신보다 훨씬 가볍게 동일한 개발환경을 구축할 수 있기 때문에 베이그런트의 역할도 많이 줄어들었습니다. 하지만 개발 머신의 스펙 향상으로 가상 머신 사용에 부담이 줄었고, 좀 더 엄밀하게 같은 환경을 구성할 필요가 있거나, 커맨드라인으로 가상 머신을 컨트롤하는 도구로는 여전히 활용도가 높습니다.*

* 개발자들의 오랜 숙원이었던 맥북 프로 메모리 32GB CTO 모델은 2018년에서야 출시되었습니다 😅.

이 글에서는 베이그런트 설치를 시작으로, 기본적인 사용법에 대해서 소개합니다. 가상 머신 이미지에 해당하는 박스 개념과 Vagrantfile을 사용해 프로젝트 별 가상 머신 설정을 작성하는 법을 알아보고, 베이그런트로 관리하는 가상 머신에 개발 환경 구축을 위해 프로비저닝을 수행해봅니다. 그리고 프로비지닝된 가상머신을 다시 박스로 만드는 법까지 소개합니다.

44BITS 소식과 클라우드 뉴스를 전해드립니다. 지금 5,000명 이상의 구독자와 함께 하고 있습니다 📮

베이그런트(Vagrant) 설치

베이그런트는 독립적으로 사용되는 도구가 아니며, 가상 머신을 생성하거나 조작하는 기능을 직접 제공하지는 않습니다. 베이그런트에는 프로바이더라는 개념이 있어서 버추얼박스VirtualBox, VMWare, 도커Docker, Hyper-V와 같은 도구들을 가상 머신을 관리하는 도구로 조합해서 사용할 수 있습니다.*

* 이러한 특징은 이후에 개발된 하시코프 도구들에서도 찾아볼 수 있는 매우 중요한 컨셉입니다. 예를 들어 인프라스트럭처를 코드로 관리할 수 있게 도와주는 테라폼에는 프로바이더 개념이 있습니다. AWS 프로바이더, GCP 프로바이더, Azure 프로바이더 등 메이저 클라우드 서비스를 지원할 뿐만 아니라 테라폼에서 사용 가능한 다양한 서비스의 프로바이더가 개발되고 있습니다.

베이그런트는 공식적으로 맥OS*, 윈도우, 리눅스를 지원하며, 다운로드 페이지에서 운영체재 별 최신 버전을 다운로드 할 수 있습니다. 또한 하시코프 릴리스 페이지에서 과거 버전들을 다운로드할 수 있습니다.

* 2021년 1월 현재 애플 실리콘 M1 기반의 맥북에서는 가상 머신 도구들이 지원하지 않아 정상적으로 사용이 불가능한 것으로 보입니다.

맥OS(macOS)에서 베이그런트와 버추얼박스 설치

맥OS에서는 홈브류Homebrew를 사용해* 베이그런트Vagrant와 버추얼 박스VirtualBox를 손쉽게 설치할 수 있습니다. 베이그런트 매니저는 필수는 아닙니다만, GUI로 현재 베이그런트에서 관리중인 가상 머신 상태를 쉽게 모니터링할 수 있도록 도와줍니다.**

* 홈브류의 설치와 기본적인 사용법은 다음 페이지를 참고해주세요: 홈브류(Homebrew)란?

8* 베이그런트는 프로바이더로 버추얼박스, VMWare, 도커, Hyper-V 등을 지원합니다만, 이 글에서는 버추얼박스를 기준으로 사용 방법을 소개합니다.

$ brew install --cask virtualbox vagrant vagrant-manager
virtualbox requires a kernel extension to work.
If the installation fails, retry after you enable it in:
  System Preferences → Security & Privacy → General
For more information, refer to vendor documentation or this Apple Technical Note:
  https://developer.apple.com/library/content/technotes/tn2459/_index.html

잠시 기다리면 설치가 완료됩니다. 버추얼 박스의 경우 설치에 문제가 있다면, 설치 과정에서 출력된 메시지를 참고해 직접 문제를 확인해야합니다.*

* 홈브류로 설치가 어렵다면 공식 사이트에서 직접 다운로드 후 설치하는 방법을 추천합니다: 버추얼박스 공식 다운로드 페이지

$ vagrant --version
Vagrant 2.2.14

버추얼박스와 베이그런트 매니저Vagrant Manager는 GUI 앱으로 애플리케이션 목록에서 확인할 수 있습니다. 처음 실행할 때는 시스템의 보안 설정에서 실행을 허용해줘야할 수도 있습니다. 베이그런트 매니저는 맥OS 상단 메뉴바의 V 모양의 아이콘으로 실행되니 참고해주세요.

튜토리얼: 박스(Box), 프로바이더(Provider), 그리고 첫 vagrant up

자, 그럼 베이그런트 설치가 완료되었으니, 바로 실전으로 들어가보겠습니다. 베이그런트 하면, 이거 하나만 기억해주세요: vagrant up.

$ vagrant up
A Vagrant environment or target machine is required to run this
command. Run `vagrant init` to create a new Vagrant environment. Or,
get an ID of a target machine from `vagrant global-status` to run
this command on. A final option is to change to a directory with a
Vagrantfile and to try again.

호기롭게 시작해보았습니다만, 아무런 일도 일어나지 않네요. 베이그런트를 사용하기에 앞서 알아두어야할 것이 있습니다. 베이그런트는 기본적으로 어떤 프로젝트의 개발 환경을 공유하기 위한 도구입니다. 따라서 사용하기에 앞서 개발 환경이 필요한 프로젝트 디렉터리로 이동하거나, 베이그런트 테스트를 위한 빈 디렉터리 하나를 생성해주어야합니다. 여기서는 vagrant_ubuntu 디렉터리를 하나 만들고 vagarnt init을 해보겠습니다.

$ mkdir vagrant_ubuntu
$ cd vagrant_ubuntu
$ 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.

vagrant init이 해주는 일은 간단합니다. 바로 Vagrantfile 예제 파일을 작성해줍니다. 도커 이미지 빌드를 위해 Dockerfile이 있는 것처럼, 베이그런트 개발 환경 관리를 위해서 사용되는 파일입니다. 처음 이 파일들을 열면 매우 자세한 내용들이 주석들로 기록되어있습니다만, 주석들을 제외하면 단 세 줄에 불과하니 겁 먹을 필요는 없습니다.

Vagrant.configure("2") do |config|
  config.vm.box = "base"
end

베이그런트가 프로그래밍 언어 루비Ruby로 만들어졌다는 이 글의 첫 문장을 기억하실지 모르겠지만, Vagrantfile의 내용은 루비 언어 문법으로 작성되어있습니다. 루비 언어에 대해서 일단 신경쓰진 않아도 됩니다. 여기서 중요한 2번째 줄입니다.

config.vm.box = "base"

베이그런트에서 박스(Box)란 가상 머신의 이미지를 의미합니다. 기본값으로 base가 지정되어있습니다. 이 상태에서 Vagrantfile이 있는 디렉터리에서 다시 vagrant up을 해봅니다.

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'base' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: >= 0
==> default: Box file was not detected as metadata. Adding it directly...
==> default: Adding box 'base' (v0) for provider: virtualbox
    default: Downloading: base
An error occurred while downloading the remote file. The error
message, if any, is reproduced below. Please fix this error and try
again.

Couldn't open file ./vagrant_ubuntu/base

실행 결과가 아까와는 약간 다릅니다. 메시지를 자세히 읽어보면 base 박스를 다운로드 하려고 했지만, 원격에도 없고 로컬에도 없어서 실행에 실패했습니다.

베이그런트 클라우드Vagrant Cloud에서는 베이그런트에서 바로 사용 가능한 박스들을 제공하고 있습니다.

베이그런트 클라우드에서 바로 사용가능한 박스를 찾아볼 수 있습니다

virtualbox를 선택하면 버추얼박스 프로바이더에서 사용가능한 이미지들을 확인할 수 있습니다. 가장 많이 다운로드된 이미지들이 조금 오래된 느낌이 있네요 😅 우분투 12.04나 14.04는 이미 일반 지원이 종료된 상태이므로 사용을 권장하지 않습니다.16.04도 2021년 4월 지원 종료 예정입니다.

아무래도 베이그런트가 오래된 툴이다 보니 박스를 선택할 때 주의가 필요해보입니다. 2021년 현재 시점에서 우분투 18.04 LTS(bionic)나 20.04 LTS(focal) 사용을 추천합니다.

박스 페이지에 접속해서 박스를 파일로 다운로드 받는 것도 가능합니다. 최신 버전을 자동으로 다운로드해서 사용하는 경우에는 config.vm.box의 값만 변경해주면 됩니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
end

혹은 vagrant init을 할 때 vagrant init ubuntu/bionic64와 같이 박스의 이름을 지정해줍니다.

이제 준비는 마쳤습니다. 다시 vagrant up을 실행합니다.

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/bionic64'...
==> default: Matching MAC address for NAT networking...
...
    default: your host and reload your VM.
    default:
    default: Guest Additions Version: 5.2.34
    default: VirtualBox Version: 6.1
==> default: Mounting shared folders...
    default: /vagrant => ./vagrant_ubuntu

설명을 위해 조금 돌아왔습니다만, 베이그런트의 기본적인 사용법을 이해하기 위해서 3가지를 기억해주세요. 가상 머신을 실행하는 프로바이더(기본값으로 버추얼박스가 사용됩니다), 가상 머신의 이미지를 제공하는 박스(box), 그리고 vagrant up입니다.

정말로 버추얼박스 가상 머신이 실행된 건지 확인해볼까요? 애플리케이션 목록에서 VirtualBox를 실행해보면 vagrant_ubuntu_default로 시작하는 가상 머신이 하나 실행되어있는 것을 볼 수 있습니다. OS는 우분투, 메모리는 1GB, 프로세서는 2개로 설정되어있습니다.

버추얼박스에서 vagrant_ubuntu_defalut 머신을 확인할 수 있습니다

가상 머신에 대한 스펙을 별도로 지정하지는 않았습니다만, 앞서 보았던 Vagrantfile에서 더 상세한 설정이 가능합니다. 베이그런트는 단순히 가상 머신을 실행해줄 뿐만아니라, 네트워크 셋업이나 공유 폴더 셋업도 한번에 같이 해줍니다. 특히 개발에 필요한 프로젝트 파일을 전달하기 위해 기본적으로 현재 프로젝트의 디렉터리를 가상 머신의 /vagrant에 마운트해줍니다.

튜토리얼: SSH 접속, 가상 머신 상태 확인, 중지 및 삭제

베이그런트를 사용할 때 특히 편리한 기능은 vagrant ssh 명령어로 실행중인 가상 머신에 SSH로 바로 접속할 수 있다는 점입니다.

$ vagrant ssh
Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.15.0-91-generic x86_64)
...
New release '20.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
vagrant@ubuntu-bionic:~$

lsb_release 명령어로 우분투 환경임을 확인해봅니다.

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:        18.04
Codename:       bionic

잘 동작하고 있는 것을 확인할 수 있습니다. exit로 SSH를 종료해봅니다.

vagrant status 명령어로 현재 상태를 확인해봅니다.

$ 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`.

default 가상 머신이 실행중입니다. 반드시 기억해야하는 중요한 게 하나 있습니다. 처음에 vagrant init을 했던 지점까지 기억을 거슬러 올라가봅니다. 베이그런트 셋업 전에, 특정 프로젝트의 디렉터리나 빈 디렉터리에서 vagarnt init을 실행했습니다.

예를 들어 다른 디렉터리로 이동해서 vagrant statusvagrant ssh를 실행하면 어떻게 될까요?

$ cd ..
$ vagrant status
A Vagrant environment or target machine is required to run this
command. ...

$ vagrant ssh
A Vagrant environment or target machine is required to run this
command. ...

실행이 되지 않습니다.

즉, vagarntVagrantfile이 있는 디렉터리에 의존적입니다. 또한 vagrant up이 된 이후에는 가상 머신과 관련된 정보를 .vagrant 디렉터리에 저장해둡니다. 이 디렉터리에는 SSH 접속 등을 위한 정보도 포함되어있기 때문에 강제로 삭제하면 베이그런트가 정상적으로 동작하지 않을 수 있습니다.*

* 예제에서는 상위 디렉터리로 이동했습니다. vagrant 명령어는 상위 디렉터리로 Vagrantfile을 찾기 때문에 프로젝트 디렉터리의 하위에서는 vagrant를 실행했을 때 프로젝트 루트(Vagrantfile이 있는 위치)를 기준으로 명령을 실행합니다. 더 자세한 내용은 공식 매뉴얼을 참고해주세요.

이제 머신을 중지시키고 삭제해보겠습니다. 다시 vagarnt_ubuntu 디렉터리로 이동한 이후, vagrant halt를 실행합니다.

$ cd vagrant_ubuntu
$ vagrant halt

이 상태에서 status를 확인해보면, 가상 머신이 종료된 것을 확인할 수 있습니다.

$ vagrant status
Current machine states:

default                   poweroff (virtualbox)

버추얼박스에서도 같은 상태입니다.

버추얼박스에서도 머신이 꺼진 것을 확인할 수 있습니다

마지막으로 vagrant destroy를 실행해주면, 가상 머신이 삭제됩니다. destroy할 때는 가상 머신이 실제로 삭제되니 데이터 백업 등은 미리 해주시기 바랍니다.

$ vagrant destroy
    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Destroying VM and associated drives...

$ vagrant status
Current machine states:

default                   not created (virtualbox)

가상 머신이 삭제되고, not created 상태가 되었습니다. 버추얼박스에서도 가상 머신이 사라졌습니다.

버추얼박스에서도 가상 머신이 삭제된 것을 확인이 가능합니다

여기까지 베이그런트로 가상 머신을 생성하고 삭제해보았습니다. 다시 가상 머신을 사용하고 싶다면, vagrant up을 실행해주기만 하면 됩니다.

베이그런트를 사용하면 이런 식으로 프로젝트 별로 손쉽게 가상 머신을 준비하고 사용하는 것이 가능합니다.

Vagrantfile 기초와 베이그런트 박스

Vagrantfile에 대해서는 config.vm.box 설정만 살펴보았습니다만, 이외에도 다양한 설정이 가능하며 루비 언어를 사용해 동적으로 확장하는 것도 가능합니다. 최소한의 내용은 앞에서 살펴본 대로 다음과 같습니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
end

여기서 Vagrant.configure("2")는 베이그런트 설정 형식 버전 2를 사용한다는 의미입니다. 이 설정은 하위 호환성을 위해 사용됩니다만, 메이저 버전 2 이상에서는 2를 지정합니다. 그 뒤로는 루비의 do...end 블록을 만들고 그 안에 추가적인 설정을 작성합니다. 즉, 모든 설정은 do...end 사이에 작성합니다.

최신 버전의 박스를 사용할 경우에는 버전을 지정하지 않아도 되지만, 특정 버전의 박스를 사용하고 싶은 경우가 있습니다. 예를 들어 ubuntu/bionic64 박스의 버전들은 박스 페이지에서 확인할 수 있습니다. 버전을 확인한 후 box_version 설정을 지정해줍니다. 페이지에는 버전이 v20210108.0.0와 같이 표시되어있습니다만 맨 앞의 v는 지워줍니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.box_version = "20210108.0.0"
end

이제 가상 머신을 새로 만들면 ubuntu/bionic6420210108.0.0 버전 박스가 사용됩니다.

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Box 'ubuntu/bionic64' could not be found. Attempting to find and install...
    default: Box Provider: virtualbox
    default: Box Version: 20210108.0.0
.....
    default: Downloading: https://vagrantcloud.com/ubuntu/boxes/bionic64/versions/20210108.0.0/providers/virtualbox.box
Download redirected to host: cloud-images.ubuntu.com
==> default: Successfully added box 'ubuntu/bionic64' (v20210108.0.0) for 'virtualbox'!
==> default: Importing base box 'ubuntu/bionic64'...

박스를 새로 다운로드 받는 것을 확인할 수 있습니다.

vagrant box 명령어를 사용하면 박스와 관련된 상태를 확인하거나 추가적인 작업을 수행할 수 있습니다.

$ vagrant box
Usage: vagrant box <subcommand> [<args>]

Available subcommands:
     add
     list
     outdated
     prune
     remove
     repackage
     update
...

vagarnt box 서브 커맨드를 실행해보면, 이 커맨드의 서브 커맨드들을 확인해볼 수 있습니다. 먼저 list로 목록을 출력해보겠습니다.

$ vagrant box list
ubuntu/bionic64 (virtualbox, 20200320.0.0)
ubuntu/bionic64 (virtualbox, 20210108.0.0)

앞에서 사용한(즉, 로컬에 설치된) 2개의 박스가 목록에 나타납니다. config.vm.box 설정에 대해서 조금 더 정확히 설명해보자면, 로컬에 설치된 박스 중에서만 선택이 가능합니다. 이 때 로컬에 해당 이름과 버전을 가진 박스가 없다면 베이그런트 클라우드에서 검색해봅니다. 박스가 검색 결과에 있으면, 그 박스를 다운로드 받아서 로컬에 설치하고 사용합니다.

박스는 이렇게 자동으로 설치할 수도 있습니다만, 수동으로 설치하는 것도 가능합니다. 아직 프로비저닝 개념을 다루지 않았습니다만, 공식으로 제공되는 박스를 기반으로 프로젝트 개발 환경을 구축한 다음(이 과정을 프로비저닝이라고 부릅니다) 그 이미지를 다시 박스로 저장할 수 있습니다. 실제로는 팀원들 간에 이렇게 만들어진 커스텀 박스를 공유하게 됩니다.

커스텀 이미지는 아닙니다만 이번에는 베이그런트 클라우드에서 직접 박스를 다운로드 받아 설치하는 방법을 알아보겠습니다. 먼저 ubuntu/focal64 박스의 최신 버전을 다운로드 받아보겠습니다.

베이그런트 클라우드 ubuntu/focal64 이미지

버전 우측 하단의 보이는 아랫쪽 화살표를 클릭하면 박스를 다운로드 받을 수 있습니다. 여기서는 해당 링크 패스를 복사해서 박스를 설치해보겠습니다.

$ vagrant box add ubuntu/focal64  \
  https://app.vagrantup.com/ubuntu/boxes/focal64/versions/20210119.1.0/providers/virtualbox.box

URL로 설치하는 경우 이미지의 버전을 지정하는 것이 불가능한 것으로 보입니다. 😢

조금 이상하긴 합니다만, 일단 새로운 박스가 설치된 것을 확인할 수 있습니다.

$ vagrant box list
ubuntu/bionic64 (virtualbox, 20200320.0.0)
ubuntu/bionic64 (virtualbox, 20210108.0.0)
ubuntu/focal64  (virtualbox, 0)

Vagrantfile: 프로젝트 용 가상 머신의 설정을 정의하는 파일

다시 Vagrantfile로 돌아가보겠습니다. 자주 보니 이제 이 파일의 기본 형태에 대해서는 비교적 익숙하실 것 같습니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.box_version = "20210108.0.0"
end

다시 한 번 강조하자면, do...end 블럭 사이에 설정을 작성합니다.

가상 머신과 관련된 프로바이더 설정을 하려면 블럭을 하나 더 만들어야합니다. 예를 들어 CPU 프로세스와 메모리 설정은 다음과 같이 지정합니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.box_version = "20210108.0.0"

  config.vm.provider "virtualbox" do |machine|
    machine.memory = 4096
    machine.cpus = 4
  end
end

루비 코드가 어색할 수도 있습니다만, 이러한 형식으로 사용된다는 점을 기억해주시기 바랍니다. 새로운 설정을 적용하려면 vagrant destroy로 가상 머신을 완전히 삭제 후, 다시 vagrant up을 해야합니다. 이 방법 대신 vagrant reload 명령을 사용해도 됩니다.

가상 머신의 스펙이 메모리 4GB, 프로세스 4개로 변경되었습니다

새로 만든 머신의 프로세스와 메모리가 증가하였습니다.

이외에 자주 사용하는 중요한 설정들을 몇 가지 소개합니다.* 네트워크 관련된 설정으로 호스트와 가상 머신 사이에 포트 포워딩을 할 수 있습니다. 가상 머신의 80 포트를 호스트의 8080 포트에 연결하려면 다음 설정을 추가해줍니다.

* 주로 앞에서 주석 처리되어있던 내용들이니, 자동 생성되는 Vagrantfile의 주석 내용을 주의 깊게 읽어보는 것을 추천드립니다.

config.vm.network "forwarded_port", guest: 80, host: 8080

개발환경에서 자주 사용하는 포트를 미리 설정해두면 편리합니다. 호스트 IP를 강제할 수도 있습니다. 127.0.0.1로 IP를 강제하면, 호스트에 포트 포워딩이 되더라도 호스트 머신 밖에서는 가상 머신에 접근하는 것이 불가능합니다.

config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"

private_network를 사용해 호스트에서만 접근 가능한 아이피 값을 추가적으로 지정하는 것도 가능합니다.

config.vm.network "private_network", ip: "192.168.33.10"

public_network를 지정하면 브릿지를 통해 마치 내부 망의 물리머신에 있는 머신처럼 사용할 수도 있습니다.

config.vm.network "public_network", :bridge => 'en0'

ifconfig를 사용하면 내부망의 DHCP 서버를 통해 IP를 받아온 것을 확인할 수 있습니다.

$ ifconfig
...
enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.55  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 fe80::a00:27:f2:9932  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:72:99:32  txqueuelen 1000  (Ethernet)
        RX packets 118  bytes 13065 (13.0 KB)
        RX errors 0  dropped 69  overruns 0  frame 0
        TX packets 16  bytes 1734 (1.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

실제로 라우터 장비의 어드민에서 가상 머신이 DHCP로 IP를 할당받은 내역도 확인이 됩니다.

DHCP에서 IP가 가상 머신에 할당된 것이 확인됩니다

퍼블릭 네트워크는 프로바이더마다 설정이 다를 수 있으므로 자세한 내용은 베이그런트의 네트워킹 문서나 프로바이더 별 페이지를 참고해주시기 바랍니다.

베이그런트는 기본적으로 프로젝트 디렉터리를 가상 머신의 /vagrant에 마운트시켜줍니다. 이를 기반으로 게스트에서 개발 서버를 실행하고, 호스트 상에서 IDE나 텍스트 에디터로 코드를 편집하면서 개발을 하는 것이 가능합니다.

synced_folder 옵션을 통해서 추가적인 디렉터리를 마운트하는 것이 가능합니다. 첫 번째 인자는 호스트의 디렉터리를 의미하고, 두 번째 인자는 가상 머신 상의 디렉터리를 의미합니다.

config.vm.synced_folder "../data", "/vagrant_data"

그 외에 config.vm에서 설정할 수 있는 값들은 아래 페이지에서 확인하실 수 있습니다.

버추얼박스에 특화된 설정들은 다음 페이지에서 확인하실 수 있습니다.

개발 환경 구축을 위한 프로비저너

베이그런트에서 가장 중요한 두 가지 개념은 프로바이더Provider와 프로비저너Provisioner입니다. 이름이 묘하게 비슷합니다만, 앞에서 살펴봤듯이 프로바이더는 가상 머신 기능을 제공하는 소프트웨어를 의미합니다. 이 글에서는 버추얼박스를 기준으로 설명하고 있습니다만, 필요에 따라서 VMWare나 도커를 프로바이더로 사용하는 것도 가능합니다.

프로비저너는 가상 머신의 개발 환경을 셋업해주는 역할을 합니다. 프로비저너가 없이 가상 머신을 컨트롤하는 용도로 베이그런트를 사용할 수도 있습니다만, 프로비저너를 통해서 개발 환경을 구축하고 가상 머신 기반의 개발 환경을 쉽게 공유하는 것이 가능해집니다. 프로바이더와 마찬가지로 베이그런트에서는 다양한 프로비저닝 툴을 지원하고 있습니다. 간단한 셸 스크립트를 사용할 수도 있고, 앤서블Ansible, 셰프Chef와 같은 전문적인 설정 관리Configuration Management 툴을 사용하는 것도 가능합니다. 이 도구들을 다루는 것은 이 글의 범위를 넘어가는 일이라, 여기서는 간단히 셸 스크립트 기반의 프로비저닝 방법을 소개해보겠습니다.

여기서는 도커가 설치된 우분투 이미지를 준비한다고 가정해보겠습니다. 도커에서 공식적으로 제공해주는 스크립트를 사용하면 명령어 하나로 도커 최신 버전을 설치할 수 있습니다.

$ wget -qO- https://get.docker.com/ | sh

이 내용을 설정에 추가해보겠습니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.provision "shell", inline: "wget -qO- https://get.docker.com/ | sh"
end

shell 프로비저너를 사용해서 inline으로 명령어를 한 줄 지정해주었습니다.

주의사항이 있습니다. 이 프로비저닝 내용은 맨 처음 vagrant up을 실행할 때만 동작합니다. 따라서 이미 베이그런트로 가상 머신이 실행되어있는 경우 vagrant provision 명령어를 명시적으로 실행하거나, vagarnt reload --provision과 같이 reload--provision 옵션을 명시적으로 붙여주어야합니다. 이번에는 destroy 후 다시 up을 해보겠습니다.

$ vagrant destroy
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu/bionic64'...
...
==> default: Mounting shared folders...
    default: /vagrant => ./vagrant_ubuntu
==> default: Running provisioner: shell...
    default: Running: inline script
    default: # Executing docker install script, commit: 3d8fe77c2c46c5b7571f94b42793905e5b3e42e4

이전과 마찬가지로 가상 머신 셋업이 진행되는데, 마지막에 프로비저닝이 실행되는 것을 확인할 수 있습니다. 이전에 vagrant up을 할 때보다는 시간이 더 걸립니다. 프로비저닝이 끝나면 ssh로 접속해서 docker가 설치 되었는지 확인해봅니다.

$ docker --version
Docker version 20.10.2, build 2291f61
$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/json: dial unix /var/run/docker.sock: connect: permission denied

도커는 잘 설치된 것 같은데, docker ps를 실행하니 권한 문제로 실행이 되지 않습니다. 이를 해결하려면 실행하는 사용자 계정(vagrant)을 도커 그룹에 포함시켜야합니다.

Vagrantfile에 프로비저닝을 위한 명령어 한 줄을 더 추가해줍니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  config.vm.provision "shell", inline: "wget -qO- https://get.docker.com/ | sh"
  config.vm.provision "shell", inline: "usermod -aG docker vagrant"
end

이 내용을 적용하려면 (호스트 머신에서) 명시적으로 vagrant provision을 실행해주어야합니다.

$ vagrant provision
==> default: Running provisioner: shell...
    default: Running: inline script
    default: # Executing docker install script, commit: 3d8fe77c2c46c5b7571f94b42793905e5b3e42e4
    default: Warning: the "docker" command appears to already exist on this system.
...

눈치 채셨을 수도 있지만, 베이그런트는 셸스크립트를 그냥 명령어대로 실행해줍니다. 그 이상도 이하도 아닙니다. 무슨 말이냐면, 앞에서 이미 도커를 설치했지만 vagrant provision은 그냥 적혀진대로 셸 명령어를 실행해준다는 의미입니다. 메시지를 자세히 보면 도커가 이미 설치되어있다는 내용을 볼 수 있습니다. 셸 스크립트는 멱등성을 보장해주지 않습니다. 조금 더 복잡한 이야기가 됩니다만, 셸 스크립트를 여러번 실행하면 시스템의 상태가 실행할 때마다 변경될 수 있습니다. 이러한 이유 때문에 본격적으로 프로비저닝을 할 때는 셸 스크립트보다는 멱등성을 보장해주는 설정 관리 도구를 사용하는 게 일반적입니다.

어쨌건, 다시 vagrant ssh로 접속해서 docker ps를 실행해봅니다.

$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

정상 동작하는 것을 확인할 수 있습니다!

여기까지 베이그런트의 프로비저닝 기능에 대해 간단히 살펴보았습니다. 설정 관리 도구와 연동을 비롯한 더 자세한 내용은 공식 문서를 참고해주세요.

가상 머신을 다시 박스로 만들어주는 package

앞에서는 ubuntu/bionic64 박스에 도커를 설치해보았습니다. 이 가상 머신을 박스 파일로 내보내기하려면 package 서브 커맨드를 사용합니다.

$ vagrant package
==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Exporting VM...
==> default: Compressing package to: ./vagrant_ubuntu/package.box

package.box라는 이름으로 새로운 베이그런트 박스 파일이 생성되었습니다.* 이 박스가 자동으로 로컬 환경에 설치되지는 않습니다. 이 박스를 사용하려면 vagrant add 명령어로 박스를 등록해주어야합니다.

* --output 옵션을 사용해 내보내는 파일의 이름을 지정할 수 있습니다.

$ vagrant box add ubuntu/bionic64/docker ./package.box
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'ubuntu/bionic64/docker' (v0) for provider:
    box: Unpacking necessary files from: ./package.box
==> box: Successfully added box 'ubuntu/bionic64/docker' (v0) for 'virtualbox'!

vagrant box list로 박스 목록을 확인해봅니다.

$ vagrant box list
ubuntu/bionic64        (virtualbox, 20200320.0.0)
ubuntu/bionic64        (virtualbox, 20210108.0.0)
ubuntu/bionic64/docker (virtualbox, 0)
ubuntu/focal64         (virtualbox, 0)

ubuntu/bionic64/docker 박스가 등록된 것을 확인할 수 있습니다.

이번에는 새로운 프로젝트 디렉터리를 만들고 이 박스를 사용해, 도커가 설치되어있는지 확인해보겠습니다.

$ cd ..
$ mkdir vagrant_ubuntu_docker
$ vagrant init ubuntu/bionic64/docker
$ vagrant up
$ vagrant ssh
vagrant$ docker --version
Docker version 20.10.2, build 2291f61
vagrant$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

도커가 잘 설치되어있는 것을 확인할 수 있습니다. 참고로 이 프로젝트의 Vagrantfile에는 프로비저너 설정이 포함되어있지 않습니다.

새로운 박스로 실행한 가상 머신

버추얼 박스에서도 새로 실행된 가상 머신을 확인해볼 수 있습니다.

멀티 VM 개발 환경 맛보기

지금까지는 1대의 가상 머신을 가정하고 Vagrantfile을 작성해왔습니다만, 여러대의 가상 머신으로 개발 환경을 구축하는 것도 가능합니다. 조금 설정이 복잡해지는 느낌이 있습니다만, 아래의 예제에서는 우분투 18.04를 사용하는 web과 CentOS 8을 사용하는 db 머신을 셋업합니다.

Vagrant.configure("2") do |config|
  config.vm.define "web" do |web|
    web.vm.box = "ubuntu/bionic64"
  end

  config.vm.define "db" do |db|
    db.vm.box = "generic/centos8"
  end
end

1대를 셋업하는 경우와 비교해주시기 보시기 바랍니다.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
end

2대를 셋업할 때는 config.vm.define으로 머신을 명시적으로 정의하고, 첫 번째 인자로 머신의 이름을 넘겨줍니다. 그리고 그 안에 다시 블럭으로 머신의 상세 설정을 하는 것을 확인할 수 있습니다. 블럭 안에서 설정에 사용하는 이름이 config가 아니라는 점에 주의가 필요합니다. 코드 상에서 이 이름은 || 사이의 값이 사용됩니다. 예를 들어 web 머신의 블럭에는 |web|이라고 작성되어있는데, 블럭 안에서 web 머신의 설정은 web.vm.과 같이 시작하는 것을 확인할 수 있습니다.

vagrant up을 하면 2대의 가상 머신이 실행됩니다.

$ vagrant up
$ vagrant status
Current machine states:

web                       running (virtualbox)
db                        running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.

눈치가 빠르신 분들은 알아채셨을 수도 있지만, 1대를 기준으로 Vagrantfile을 작성한 경우 가상 머신 이름은 default로 생성되었습니다. 현재는 멀티 VM을 정의하고 있기 때문에 이름을 명시적으로 지정해줬고 따라서 webdb라는 이름을 가진 가상 머신이 실행중인 것을 확인할 수 있습니다. 버추얼박스 앱에서도 확인할 수 있습니다.

멀티 VM 베이그런트 프로젝트에서 실행된 가상머신들

이제 머신이 2대인데 vagrant ssh를 실행하면 어떻게 될까요?

$ vagrant ssh
This command requires a specific VM name to target in a multi-VM environment.

멀티 VM 환경에서는 머신 이름을 명시적으로 지정해야한다고 친절하게 알려줍니다. vagrant ssh db로 접속해보면 centos 프롬프트를 확인해볼 수 있습니다.

$ vagrant ssh db
[vagrant@centos8 ~]$ 

베이그런트를 사용하면 단일 머신 기반의 개발 환경 구축 뿐만 아니라, 비교적 쉽게 멀티 VM으로 확장 가능한 것을 확인해볼 수 있습니다.

vagrant의 서브 커맨드 총정리

아직 다루지 않은 베이그런트의 기능들이 많이 있습니다만, 프로비저너까지 이해했다면 기본적인 활용에 큰 문제는 없습니다. 현재 프로젝트의 개발 환경을 프로비저닝하고 박스로 공유하는 게 가장 기본적인 용도입니다만, 반드시 프로그래밍 프로젝트에서 사용해야하는 것은 아닙니다. 개인적으로는 개발 환경 공유보다 리눅스 환경 테스트 용도로 더 많이 사용하고 있습니다.

지금까지 사용했던 명령어들을 한 번 정리해보겠습니다.

이 글에서 다루지는 않았지만 이외에 자주 사용하는 명령어들입니다.

마치며

여기까지 베이그런트에 대해서 함께 알아보았습니다. 2021년에 베이그런트를 소개하자니 조금 뒤늦은 감이 없지 않아 있습니다만, 지금도 자주 활용하는 툴이다 보니 꼭 한 번 별도로 소개해보고 싶었습니다. 5년은 벼르다가 쓴 것 같네요 😅 이번에도 다른 글을 쓰려고 하다가 마침 베이그런트로 작업 환경을 구축하고 있어서, 이 참에 베이그런트 글부터 쓰게되었습니다.

베이그런트를 처음 알게 됐던 건 (지금은 사라진 KTH의) 개발 컨퍼런스 H3 2012 강소리 님 발표 영상 덕분인데, 지금봐도 베이그런트의 근본적인 아이디어와 장점을 이해하는 데는 좋은 자료입니다.

최근에는 베이그런트보다 도커나 도커 컴포즈를 사용해 로컬 개발환경을 구축하는 게 좀 더 일반적입니다. 베이그런트 컨셉은 매우 좋지만 아무래도 로컬 컴퓨터에서 프로젝트 몇 개만 돌려도 맥북으로는 버거운 게 사실입니다. 44BITS에서도 도커 컴포즈로 개발 환경 구축하는 방법을 소개한 적이 있으니 참고해주세요.

또한 베이그런트에 대해 더 깊게 알고 싶으신 분들은 공식 문서와 저장소를 찾아가보시기 바랍니다. 👍

서버스펙(Serverspec)을 사용한 도커 이미지 테스트 자동화: 인프라스트럭처 테스트 프레임워크

🗒 기사, 2015-07-07 - 구성 관리 도구는 서버의 상태를 코드로 기술하고 관리할 수 있게 도와줍니다. 이러한 패러다임을 기반으로 서버를 관리할 때도 소프트웨어에 사용할 수 있는 기법들을 활용할 수 있게 되었습니다. 서버스펙(Serverspec)은 루비의 테스트 프레임워크인 R스펙(RSpec)을 확장한 인프라스트럭처 테스트 도구입니다. 서버스펙을 사용하면 소프트웨어를 테스트하듯이 인프라스트럭처도 테스트하는 게 가능해집니다. 이 글에서는 서버스펙을 사용해 도커(Docker) 이미지 테스트 방법을 소개합니다.

AWS, 컨테이너 전용 리눅스 OS 보틀로켓(BottleRocket) 1.0 릴리스

🗞 새소식, 2020-09-03 - AWS에서는 오픈소스로 개발중인 컨테이너 전용 리눅스 배포판 보틀로켓(BottleRocket) 1.0을 정식 릴리스하였습니다. 보틀로켓은 컨테이너에 최적화된 OS로 컨테이너 실행을 위한 최소한의 소프트웨어만을 포함하며 보안과 확장성에 집중하고 있습니다.

44BITS 2020년 결산 및 2021년 새해 인사

🗒 기사, 2021-01-11 - 44BITS에서는 블로그, 팟캐스트,유튜브를 통해서 IT, 프로그래밍, 클라우드를 주제로 컨텐츠를 제작하고 있습니다. 2021년 새해를 맞아 2020년 44BITS 활동을 정리하고 인기 있었던 컨텐츠들을 소개합니다. 새해 복 많이 받으세요!