2026-04-29

Vagrant — 멀티 서비스 환경 구축하기

프로젝트 개요

로컬에서 멀티 서비스 환경을 구축하고, 이를 통해 반복 가능한(repeatable) IaaC 환경을 만드는 것이 목표다.


아키텍처

사용자 → Nginx(로드밸런서) → Tomcat(Java 앱) → MySQL(DB)
                                    ↕               ↕
                               RabbitMQ         Memcache(캐시)
서비스역할VM
Nginx로드밸런서web01 (Ubuntu)
TomcatJava 애플리케이션 서버app01 (CentOS)
RabbitMQ메시지 브로커 (더미)rmq01 (CentOS)
MemcacheDB 캐싱 서버mc01 (CentOS)
MySQL(MariaDB)데이터베이스db01 (CentOS)

서비스 시작 순서는 의존받는 쪽부터: MySQL → Memcache → RabbitMQ → Tomcat → Nginx


VM Setup

vagrant-hostmanager 플러그인

vagrant plugin install vagrant-hostmanager

이 플러그인은 모든 VM의 /etc/hosts에 hostname과 IP 매핑을 자동으로 등록한다. 덕분에 VM 간에 IP 대신 hostname으로 통신할 수 있다.

Vagrantfile

Vagrant.configure("2") do |config| config.hostmanager.enabled = true config.hostmanager.manage_host = true config.vm.define "db01" do |db01| db01.vm.box = "bandit145/centos_stream9_arm" db01.vm.hostname = "db01" db01.vm.network "private_network", ip: "192.168.56.25" end config.vm.define "mc01" do |mc01| mc01.vm.box = "bandit145/centos_stream9_arm" mc01.vm.hostname = "mc01" mc01.vm.network "private_network", ip: "192.168.56.24" end config.vm.define "rmq01" do |rmq01| rmq01.vm.box = "bandit145/centos_stream9_arm" rmq01.vm.hostname = "rmq01" rmq01.vm.network "private_network", ip: "192.168.56.23" end config.vm.define "app01" do |app01| app01.vm.box = "bandit145/centos_stream9_arm" app01.vm.hostname = "app01" app01.vm.network "private_network", ip: "192.168.56.22" app01.vm.provider "vmware_desktop" do |vb| vb.memory = "1024" end end config.vm.define "web01" do |web01| web01.vm.box = "spox/ubuntu-arm" web01.vm.hostname = "web01" web01.vm.network "private_network", ip: "192.168.56.21" end end

MySQL(MariaDB) Setup — db01

vagrant ssh db01 sudo -i dnf update -y dnf install git mariadb-server -y dnf install epel-release -y systemctl start mariadb systemctl enable mariadb

패키지 이름은 mariadb-server이지만 systemd 서비스 이름은 mariadb다.

보안 설정 및 DB 생성

mysql_secure_installation # root 비밀번호 설정 (자신의 비밀번호를 설정해야 한다) mysql -u root -pyourpassword create database accounts; grant all privileges on accounts.* TO 'admin'@'localhost' identified by 'yourpassword'; grant all privileges on accounts.* TO 'admin'@'%' identified by 'yourpassword'; FLUSH PRIVILEGES; exit

yourpassword 부분을 자신의 비밀번호로 변경해야 한다. 이후 애플리케이션 설정(application.properties)에서도 동일한 비밀번호를 사용해야 한다.

초기 데이터 투입

cd /tmp/ git clone -b local https://github.com/hkhcoder/vprofile-project.git cd vprofile-project mysql -u root -pyourpassword accounts < src/main/resources/db_backup.sql

방화벽 설정

systemctl start firewalld systemctl enable firewalld firewall-cmd --get-active-zones firewall-cmd --zone=public --add-port=3306/tcp --permanent firewall-cmd --reload systemctl restart mariadb

Memcache Setup — mc01

vagrant ssh mc01 sudo -i dnf update -y dnf install memcached -y systemctl start memcached systemctl enable memcached

외부 접속 허용

sed -i 's/127.0.0.1/0.0.0.0/g' /etc/sysconfig/memcached systemctl restart memcached

Memcache는 기본적으로 127.0.0.1(자기 자신)에서만 접속을 허용한다. 0.0.0.0으로 변경하면 다른 VM에서도 접속할 수 있다. app01(Tomcat)에서 mc01으로 접속해야 하기 때문에 필요한 설정이다.

Memcache 실행

sudo memcached -p 11211 -U 11111 -u memcached -d
옵션의미
-p 11211TCP 포트 11211에서 대기
-U 11111UDP 포트 11111에서 대기
-u memcachedmemcached 사용자 권한으로 실행
-d백그라운드(데몬)로 실행

RabbitMQ Setup — rmq01

vagrant ssh rmq01 sudo -i dnf update -y dnf install epel-release -y dnf install wget -y dnf install centos-release-rabbitmq-38 -y dnf --enablerepo=centos-rabbitmq-38 -y install rabbitmq-server systemctl start rabbitmq-server systemctl enable rabbitmq-server

centos-release-rabbitmq-38은 RabbitMQ 패키지가 있는 저장소(repository) 정보를 추가하는 것이고, 그 다음 --enablerepo로 해당 저장소를 활성화해서 실제 패키지를 설치한다. CentOS 기본 저장소에는 RabbitMQ가 없기 때문에 두 단계가 필요하다.

외부 접속 허용 및 사용자 설정

sudo sh -c 'echo "[{rabbit, [{loopback_users, []}]}]." > /etc/rabbitmq/rabbitmq.config' systemctl restart rabbitmq-server sudo rabbitmqctl add_user test test sudo rabbitmqctl set_permissions -p / test ".*" ".*" ".*" sudo systemctl restart rabbitmq-server
  • loopback_users 해제: RabbitMQ는 기본적으로 localhost에서만 접속을 허용한다. 이 설정으로 다른 VM에서도 접속할 수 있게 한다.
  • 사용자 생성: test 사용자를 비밀번호 test로 생성한다.
  • 권한 부여: 세 개의 ".*"는 각각 configure(설정), write(쓰기), read(읽기) 권한이고, ".*"는 모든 리소스를 의미한다.

Tomcat Setup — app01

패키지 설치

vagrant ssh app01 sudo -i dnf install epel-release -y dnf install java-17-openjdk java-17-openjdk-devel -y dnf install git wget -y

Tomcat 설치

cd /tmp/ wget https://archive.apache.org/dist/tomcat/tomcat-10/v10.1.28/bin/apache-tomcat-10.1.28.tar.gz tar xzvf apache-tomcat-10.1.28.tar.gz useradd --home-dir /usr/local/tomcat --shell /sbin/nologin tomcat cp -r /tmp/apache-tomcat-10.1.28/* /usr/local/tomcat/ chown -R tomcat.tomcat /usr/local/tomcat

서비스 파일 생성

vi /etc/systemd/system/tomcat.service
[Unit] Description=Tomcat After=network.target [Service] Type=forking User=tomcat Group=tomcat WorkingDirectory=/usr/local/tomcat Environment=JAVA_HOME=/usr/lib/jvm/jre Environment=CATALINA_PID=/var/tomcat/%i/run/tomcat.pid Environment=CATALINA_HOME=/usr/local/tomcat Environment=CATALINA_BASE=/usr/local/tomcat ExecStart=/usr/local/tomcat/bin/catalina.sh run ExecStop=/usr/local/tomcat/bin/shutdown.sh RestartSec=10 Restart=always [Install] WantedBy=multi-user.target
systemctl daemon-reload systemctl start tomcat systemctl enable tomcat

Code Build & Deploy — app01

Maven 설치

cd /tmp/ wget https://archive.apache.org/dist/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.zip unzip apache-maven-3.9.9-bin.zip cp -r apache-maven-3.9.9 /usr/local/maven3.9 export MAVEN_OPTS="-Xmx512m"

Maven은 Java 프로젝트의 빌드 도구다. 소스코드 컴파일, 의존성 다운로드, 배포 파일(WAR) 생성을 자동화한다.

소스코드 다운로드 및 빌드

git clone -b local https://github.com/hkhcoder/vprofile-project.git cd vprofile-project # application.properties에 백엔드 서버 정보 업데이트 vim src/main/resources/application.properties # 빌드 /usr/local/maven3.9/bin/mvn install

Tomcat에 배포

rm -rf /usr/local/tomcat/webapps/ROOT cp target/vprofile-v2.war /usr/local/tomcat/webapps/ROOT.war chown -R tomcat.tomcat /usr/local/tomcat/webapps/ROOT.war systemctl restart tomcat
  • webapps/ROOT는 Tomcat의 기본 웹 애플리케이션 경로다. http://서버IP:8080/으로 접속하면 ROOT에 있는 앱이 응답한다.
  • 기존 ROOT(Tomcat 환영 페이지)를 지우고, 프로젝트 WAR 파일을 ROOT.war로 배포하면 Tomcat이 자동으로 압축을 풀어 ROOT 폴더를 생성한다.

방화벽 설정

systemctl start firewalld systemctl enable firewalld firewall-cmd --zone=public --add-port=8080/tcp --permanent firewall-cmd --reload

Nginx Setup — web01

vagrant ssh web01 sudo -i apt update apt upgrade -y apt install nginx -y

Nginx 설정

vi /etc/nginx/sites-available/vproapp
upstream vproapp { server app01:8080; } server { listen 80; location / { proxy_pass http://vproapp; } }

Nginx가 80번 포트로 들어오는 요청을 Tomcat(app01:8080)으로 전달하는 리버스 프록시 설정이다. upstream은 요청을 전달할 백엔드 서버를 정의하는 블록이다.

rm -rf /etc/nginx/sites-enabled/default ln -s /etc/nginx/sites-available/vproapp /etc/nginx/sites-enabled/vproapp systemctl restart nginx
  • sites-available/: 설정 파일을 보관하는 곳
  • sites-enabled/: 여기에 있는 설정만 실제로 활성화됨
  • ln -s: 심볼릭 링크(바로가기)를 생성. 비활성화하려면 링크만 삭제하면 되고 원본 파일은 그대로 남는다.
  • 기본 config(default)를 지우는 이유는 프로젝트 설정과 충돌 방지.

Provisioning으로 자동화

위 수동 과정을 각 서비스별 shell script 파일로 생성하고, Vagrantfile에서 provisioning으로 연결하면 vagrant up 한 번으로 전체 환경을 자동 구축할 수 있다.

config.vm.define "web01" do |web01| web01.vm.provision "shell", path: "nginx.sh" end config.vm.define "app01" do |app01| app01.vm.provision "shell", path: "tomcat.sh" end # db01, mc01, rmq01도 동일하게 각각의 sh 파일 연결

shell script 안에서 설정 파일을 생성할 때는 vi 대신 Heredoc(cat > 파일 <<EOT ... EOT)을 사용한다. 자동화 스크립트에서는 대화형 편집기를 쓸 수 없기 때문이다.

이 자동화를 통해 기존에 수동으로 처리했던 내용을 개선했다:

자동화된(Automated) Local setup 환경을 코드 기반의(IaaC) 반복적으로(Repeatable) 구축할 수 있는 환경을 마련


Q&A


Q. Nginx가 로드밸런서에 적합한 이유는?

이벤트 기반(event-driven) 아키텍처로, 적은 리소스로 수만 개의 동시 연결을 처리할 수 있다. Apache는 요청당 프로세스/스레드를 생성하는 방식이라 동시 연결이 많아지면 메모리 사용량이 급증한다. 그래서 Nginx는 앞단에서 트래픽을 분산하는 역할에 적합하고, Apache는 뒤에서 애플리케이션을 실행하는 역할에 적합하다.



Q. NFS 서버란?

Network File System. 네트워크를 통해 여러 서버가 하나의 스토리지를 공유할 수 있게 해주는 파일 시스템이다. 예를 들어 웹 서버가 3대인데 업로드된 이미지를 모든 서버에서 접근해야 할 때, 각 서버에 파일을 복사하는 대신 NFS로 중앙 스토리지를 공유한다.



Q. 서버 클러스터란?

같은 역할을 하는 서버들의 그룹이다. 웹 서버 3대를 묶어서 운영하면 "웹 서버 클러스터"라고 부른다.



Q. 서비스 시작 순서가 중요한 이유는?

의존성 때문이다. Tomcat(앱)이 시작할 때 MySQL(DB)과 Memcache(캐시)에 연결을 시도한다. DB가 아직 안 떠있으면 연결 실패로 앱이 에러가 난다. 그래서 의존받는 쪽(DB)부터 의존하는 쪽(Web)으로 순서대로 올린다.



Q. dnf update는 매번 실행해야 하는가?

새 VM을 처음 세팅할 때는 실행하는 것이 좋다. box 이미지가 만들어진 시점의 패키지 상태이므로 보안 패치가 누락되어 있을 수 있다. CentOS/RHEL 계열은 메이저 버전을 올리지 않고 보안 패치와 버그 수정만 적용하므로 호환성 문제가 거의 없다.



Q. epel-release 패키지는 무엇인가?

Extra Packages for Enterprise Linux. CentOS/RHEL 기본 저장소에 없는 추가 패키지를 설치할 수 있게 해주는 확장 저장소다.



Q. centos-release-rabbitmq-38을 설치한 뒤 다시 rabbitmq-server를 설치하는 이유는?

centos-release-rabbitmq-38은 RabbitMQ 패키지가 있는 저장소(repository) 정보를 추가하는 것이다. 앱스토어에 새로운 마켓을 등록한 것과 같다. 이후 --enablerepo로 해당 저장소를 활성화해서 실제 패키지를 다운로드한다.



Q. Memcache IP를 0.0.0.0으로 설정하면 모든 곳에서 접속 허용인가?

이 서버의 모든 네트워크 인터페이스에서 접속을 허용한다는 의미다. 실습 환경에서는 편의상 열지만, 실무에서는 특정 IP만 허용하거나 방화벽으로 접근을 제한한다.



Q. Maven은 어떤 역할인가?

Java 프로젝트의 빌드 도구다. 소스코드 컴파일, 의존성(라이브러리) 다운로드, 최종 배포 파일(WAR/JAR) 생성을 자동화한다.



Q. webapps/ROOT를 왜 지우는가?

webapps/ROOT는 Tomcat의 기본 웹 애플리케이션 경로다. 기존 Tomcat 기본페이지를 지우고 프로젝트 WAR를 ROOT.war로 배포하면, Tomcat이 자동으로 압축을 풀어 ROOT 폴더를 생성한다.



Q. WAR 파일은 수정할 일이 거의 없는가?

맞다. WAR 파일은 빌드의 결과물이다. 수정이 필요하면 소스코드를 수정하고 다시 mvn install로 빌드해서 새 WAR를 만든다.



Q. web01은 왜 Ubuntu인가?

강의에서 의도적으로 CentOS와 Ubuntu를 섞어서 실습하는 것이다. 실무에서도 팀마다 다른 OS를 쓸 수 있으므로 양쪽 다 다뤄보는 것이 목적이다.



Q. apt update와 upgrade의 차이는?

update는 패키지 목록을 최신으로 갱신하는 것(어떤 패키지가 있는지 정보만), upgrade는 설치된 패키지를 실제로 최신 버전으로 업그레이드하는 것이다. CentOS의 dnf update는 이 두 과정을 한 번에 처리한다.



Q. sites-available은 Nginx 공식 폴더인가?

Nginx 공식 구조가 아니라 Ubuntu/Debian 계열의 패키지가 사용하는 관례적 구조다. vproapp이라는 이름도 자유롭게 지은 것이다.



Q. upstream이란?

"위쪽 흐름", 즉 요청을 전달할 백엔드 서버를 정의하는 블록이다. 서버를 여러 개 나열하면 로드밸런싱이 된다.



Q. ln -s로 만드는 링크는 무엇인가?

심볼릭 링크(바로가기)다. Nginx는 sites-enabled/ 폴더에 있는 설정만 활성화한다. sites-available/에 원본을 두고, 활성화할 것만 링크를 걸어서 관리한다. 비활성화하려면 링크만 삭제하면 된다.



Q. shell script에서 EOT란?

End Of Text의 약자로, Heredoc 문법의 종료 표시다. 자동화 스크립트에서 vi 대신 cat > 파일 <<EOT ... EOT로 파일 내용을 직접 작성할 때 사용한다.