0. Introduction
목표
- 여러 서비스로 구성된 웹을 가상머신으로 로컬에서 환경을 구성하고 배포할 것이다.
- 수동으로 서비스들을 각각 provisioning한다.
- 수동으로 provisioning한 과정을 쉘 스크립트를 작성하여 자동으로 provisioning한다.
기대
- 수동으로 여러 서비스들을 설정하려면 시간이 많이 걸리고 복잡하다.
- 이를 해결하기 위해 vagrant와 bash script를 사용하여 VM 설정과 VM에서의 서비스 설정을 자동화 할 것이다.
- 코드로 인프라를 관리하면 반복 가능하고 전체를 설정하는 데 시간을 절약 할 수 있다.
기술 및 환경
- PC : M1 pro Macbook
- hypervisor : VMware Fusion Tech Preview
- vagrant
- git bash
- bash
웹 서비스 구성
- MySQL
- Memcached
- Rabbitmq
- Tomcat
- Nginx
아키텍처
- 사용자가 브라우저를 열고 URL(IP 주소)을 입력한다.
- 이 IP 주소는 로드 밸런서의 IP 주소다.
- NGINX를 사용하여 로드 밸런서를 만들 것이다. NGINX는 로드 밸런싱을 생성하는 데 자주 사용된다.
- NGINX에서 요청이 오자마자 Tomcat 서버로 라우팅하는 방식으로 구성할 것이다.
- Tomcat은 Java로 작성된 웹 애플리케이션을 호스팅한다.
- 웹 앱에서 외부 스토리지가 필요한 경우 NFS 서버를 사용할 수 있다. 서버 클러스터가 있고 중앙 집중식 스토리지가 필요한 경우 NFS에 저장할 수 있다.
- 사용자는 웹 페이지에 접속하여 로그인한다. 로그인 세부 정보는 SQL 데이터베이스 서비스에 저장된다.
- Tomcat에 연결된 Rabbit MQ라는 서비스가 있는데 Rabbit MQ는 더 복잡하게 만들기 위해 Dummy로 추가한 것이다.
- Dummy Rabbit MQ는 앱에 연결하기 위한 메시지 브로커 또는 queue 에이전트라고도 한다.
여기서 데이터를 스트리밍할 수 있다. - 메시지 브로커, 대기열, 에이전트가 웹 앱으로 돌아와서 tomcat에서 실행 중인 웹 앱을 사용자가 접근한다.
- 사용자가 로그인하면 앱은 SQL 쿼리를 실행하여 MySQL DB에 저장된 사용자 정보에 접근한다.
- 그러나 MySQL DB로 이동하기 전에 요청이 memcached로 이동한다. memcached는 데이터베이스 캐싱으로 SQL Server와 연결된다. My SQL Server는 DB에 첫 request가 올 때 사용자 정보를 저장한다. 그 다음 SQL Server에서 Tomcat으로 전송된 다음 memcached 서버로 캐시된다.
- 자동화를 구현하기 위해 vagrant를 사용하여 가상 머신을 자동으로 설정한다.
vagrant는 하이퍼바이저와 통신하여 자동으로 가상 머신을 생성한다. - 그 다음 bash script를 사용하여 Egnix, Tomcat, Memcached, Rabbit, MySQL 서비스를 설정할 것이다.
- 즉, 코드로 각각의 서비스를 위한 가상 머신을 자동으로 생성한다.
1. VM Setup
vagrant plugin install vagrant-hostmanager
VM을 설치하기 전에 vagrant-hostmanager 플러그인을 설치한다.
Vagrant.configure("2") do |config|
config.hostmanager.enabled = true
config.hostmanager.manage_host = true
### DB vm ####
config.vm.define "db01" do |db01|
db01.vm.box = "jacobw/fedora35-arm64"
db01.vm.hostname = "db01"
db01.vm.network "private_network", ip: "192.168.56.15"
db01.vm.provider "vmware_desktop" do |vmware|
vmware.gui = true
vmware.allowlist_verified = true
end
end
### Memcache vm ####
config.vm.define "mc01" do |mc01|
mc01.vm.box = "jacobw/fedora35-arm64"
mc01.vm.hostname = "mc01"
mc01.vm.network "private_network", ip: "192.168.56.14"
mc01.vm.provider "vmware_desktop" do |vmware|
vmware.gui = true
vmware.allowlist_verified = true
end
end
### RabbitMQ vm ####
config.vm.define "rmq01" do |rmq01|
rmq01.vm.box = "jacobw/fedora35-arm64"
rmq01.vm.hostname = "rmq01"
rmq01.vm.network "private_network", ip: "192.168.56.16"
rmq01.vm.provider "vmware_desktop" do |vmware|
vmware.gui = true
vmware.allowlist_verified = true
end
end
### tomcat vm ###
config.vm.define "app01" do |app01|
app01.vm.box = "jacobw/fedora35-arm64"
app01.vm.hostname = "app01"
app01.vm.network "private_network", ip: "192.168.56.12"
app01.vm.provider "vmware_desktop" do |vb|
vb.memory = "1024"
vb.gui = true
vb.allowlist_verified = true
end
end
### Nginx VM ###
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.11"
web01.vm.provider "vmware_desktop" do |vmware|
vmware.gui = true
vmware.allowlist_verified = true
end
end
end
DB, MemCache, Rabbit, Tomcat, Nginx VM을 생성하는 Vagrantfile을 작성한다.
vagrant up 명령어를 입력하면 VM이 생성된다.
vagrant status 명령어로 설치된 VM들을 확인한다.
Hosts entry가 제대로 되었는지 /etc/hosts 파일을 확인하다.
> vagrant ssh web01
...
$ cat /etc/hosts
app01에 ping을 보낼 수 있는지 확인한다.
app01에 로그인하여 다른 VM들에 ping을 보낼 수 있는지 확인한다.
> vagrant ssh app01
$ ping mc01
$ ping rmq01
$ ping db01
Nginx 가 서버들에 연결을 요청을 보낼 것이기 때문에 ping 명령어로 검증하는 것이 중요하다.
2. Provisioning
각 VM들은 다음과 같은 역할을 한다.
- Nginx : Web Service
- Tomcat : Application Server
- RabbitMQ : Broker/Queuing Agent
- Memcache : DB Caching
- ElasticSearch : Indexing/Search service
- MySQL : SQL Database
먼저 수동으로 각 VM에 서비스를 설치할 것이다. 순서는 아래를 따른다.
- MySQL (Database SVC)
- Memcache (DB Caching SVC)
- RabbitMQ (Broker/Queue SVC)
- Tomcat (Application SVC)
- Nginx (Web SVC)
MySQL
> vagrant ssh db01
- MySQL이 설치된 db01 VM에 로그인
$ sudo -i
$ yum update -y
- root 사용자로 변경 후 패키지 업그레이드
- VM에 첫 로그인 후 패키지 업그레이드를 권장한다.
$ yum install epel-release -y
- EPEL(Extra Packages for Enterprise Linux) repo를 설치한다.
- EPEL은 여러 소프트웨어를 설치하는 데 도움을 준다.
- 기본 패키지 매니저로 설치가 안되는 소프트웨어들이 있을 때 epel을 이용하는 경우가 많다.
$ yum install git mariadb-server -y
- git과 mariadb를 설치한다.
- mariadb와 mysql은 다른 패키지 이름으로 같은 서비스를 수행한다.
- rpm 패키지를 베이스로 하는 CentOS, Fedora는 mariadb를 사용한다.
- apm 패키지를 베이스로 하는 ubuntu는 mysql을 사용한다.
$ systemctl start mariadb
$ systemctl enable mariadb
$ systemctl status mariadb
- mariadb를 실행하고 부팅시 자동 시작되도록 한다.
$ mysql_secure_installation
- 설치한 mysql 서비스의 보안 설정을 위해 위 명령어를 입력한다.
[root@db01 ~]# mysql_secure_installation
NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!
In order to log into MariaDB to secure it, we'll need the current
password for the root user. If you've just installed MariaDB, and
haven't set the root password yet, you should just press enter here.
Enter current password for root (enter for none):
OK, successfully used password, moving on...
Setting the root password or using the unix_socket ensures that nobody
can log into the MariaDB root user without the proper authorisation.
You already have your root account protected, so you can safely answer 'n'.
Switch to unix_socket authentication [Y/n] y
Enabled successfully!
Reloading privilege tables..
... Success!
You already have your root account protected, so you can safely answer 'n'.
Change the root password? [Y/n] y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
... Success!
By default, a MariaDB installation has an anonymous user, allowing anyone
to log into MariaDB without having to have a user account created for
them. This is intended only for testing, and to make the installation
go a bit smoother. You should remove them before moving into a
production environment.
Remove anonymous users? [Y/n] y
... Success!
Normally, root should only be allowed to connect from 'localhost'. This
ensures that someone cannot guess at the root password from the network.
Disallow root login remotely? [Y/n] y
... Success!
By default, MariaDB comes with a database named 'test' that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.
Remove test database and access to it? [Y/n] y
- Dropping test database...
... Success!
- Removing privileges on test database...
... Success!
Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.
Reload privilege tables now? [Y/n] y
... Success!
Cleaning up...
All done! If you've completed all of the above steps, your MariaDB
installation should now be secure.
Thanks for using MariaDB!
$ mysql -u root -p[root's password]
- mysql로 로그인
mysql> create database accounts;
mysql> grant all privileges on accounts.* TO 'admin'@’%’ identified by 'admin123' ;
mysql> FLUSH PRIVILEGES;
mysql> exit;
- 데이터베이스 생성
- 권한 설정, 권한 삭제
mysql -u root -padmin123 accounts < db_backup.sql
- mysql에 생성한 accounts 데이터베이스에 테스트용 db sql 넣기
MemCache
$ vagrant ssh mc01
$ sudo -i
$ yum update -y
$ yum install memcached -y
$ systemctl start memcached
$ systemctl enable memcached
$ systemctl status memcached
- Memcache VM 로그인
- root 사용자로 변경
- yum 패키지 업데이트
- memcached 설치
- memcached 시작, VM 가동 시 자동 시작, 상태 확인
$ firewall-cmd --add-port=11211/tcp --permanent
$ firewall-cmd --reload
$ sed -i 's/OPTIONS="-l 127.0.0.1"/OPTIONS=""/' /etc/sysconfig/memcached
$ sudo systemctl restart memcached
$ memcached -p 11211 -U 11111 -u memcached -d
- tcp 포트 memcached와 연결
RabbitMQ
$ vagrant ssh rmq01
$ sudo -i
$ yum update -y
- rabbitmq VM 로그인
- 루트 사용자 변경
- yum 패키지 업데이트
$ sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
$ setenforce 0
- 서버 초기 세팅을 원활하고 효율적으로 하기 위해 selinux 끄기
selinux쪽의 에러 발생 시 원인을 알기 어렵다. - setenforce 0 : selinux 종료
$ yum install wget -y
$ cd /tmp/
$ wget http://packages.erlang-solutions.com/erlang-solutions-2.0-1.noarch.rpm
$ sudo rpm -Uvh erlang-solutions-2.0-1.noarch.rpm
$ sudo yum install erlang socat -y
- rabbitmq 설치를 위한 종속성 설치
$ curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.rpm.sh | sudo bash
$ sudo yum install rabbitmq-server -y
- rabbitmq 설치
$ sudo systemctl start rabbitmq-server
$ sudo systemctl enable rabbitmq-server
$ sudo systemctl status rabbitmq-server
- rabbitmq 실행, 부팅 시 자동 시작 설정
$ sh -c 'echo "[{rabbit, [{loopback_users, []}]}]." > /etc/rabbitmq/rabbitmq.config'
$ rabbitmqctl add_user test(ID) test(PASSWD)
$ rabbitmqctl set_user_tags test administrator
- guest 유저를 원격에서 연결 가능하도록 설정
- ID는 test, 비밀번호는 test로 사용자 생성
- test 사용자에게 administrator 태그 부여
Tomcat
$ vagrant ssh app01
$ sudo -i
$ yum update -y
$ yum install java-1.8.0-openjdk -y
$ yum install git maven wget -y
- tomcat VM 로그인
- 패키지 업데이트
- java, git, maven, wget 설치
$ cd /tmp/
$ wget https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.37/bin/apache-tomcat-8.5.37.tar.gz
$ tar xzvf apache-tomcat-8.5.37.tar.gz
- tomcat 다운로드
- 압축 해제
$ useradd --home-dir /usr/local/tomcat8 --shell /sbin/nologin tomcat
$ cp -r /tmp/apache-tomcat-8.5.37/* /usr/local/tomcat8/
$ chown -R tomcat.tomcat /usr/local/tomcat8
- tomcat 이름으로 사용자 추가, home dir 로 /usr/local/tomcat8 지정
- tomcat home dir(/usr/local/tomcat8) 로 데이터 복사
- tomcat 사용자, 그룹에 권한 부여
$ vi /etc/systemd/system/tomcat.service
[Unit]
Description=Tomcat
After=network.target
[Service]
User=tomcat
WorkingDirectory=/usr/local/tomcat8
Environment=JRE_HOME=/usr/lib/jvm/jre
Environment=JAVA_HOME=/usr/lib/jvm/jre
Environment=CATALINA_HOME=/usr/local/tomcat8
Environment=CATALINE_BASE=/usr/local/tomcat8
ExecStart=/usr/local/tomcat8/bin/catalina.sh run
ExecStop=/usr/local/tomcat8/bin/shutdown.sh
SyslogIdentifier=tomcat-%i
[Install]
WantedBy=multi-user.target
- systemctl start tomcat 명령어를 입력하면, /usr/local/tomcat8/bin/catalina.sh 파일을 실행한다.
- systemctl stop tomcat 명령어를 입력하면, /usr/local/tomcat8/bin/shutdown.sh 파일을 실행한다.
- tomcat 관련 명령어를 입력할 때 이와 같이 스크립트로 작성해놓으면 systemctl 명령어로 동작하게 할 수 있다.
$ systemctl daemon-reload
$ systemctl enable tomcat
$ systemctl start tomcat
$ systemctl status tomcat
- tomcat 시작
$ systemctl daemon-reload
$ firewall-cmd --add-port=8080/tcp --permanent
$ firewall-cmd --reload
$ systemctl start tomcat
$ systemctl enable tomcat
$ systemctl start firewalld
$ systemctl enable firewalld
$ firewall-cmd --get-active-zones
$ firewall-cmd --zone=public --add-port=8080/tcp --permanent
$ firewall-cmd --reload
- 방화벽 활성화
- tomcat은 기본적으로 8080 port를 사용하고 있으므로 8080 port 허용
$ echo 'JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk' > /etc/java/maven.conf
$ sudo yum install java-1.8.0-openjdk-devel -y
- fedora에서 maven은 위와 같이 설정해주어야 java를 정상적으로 동작시킬 수 있다.
Code Build & Deploy to Tomcat(app01) Server
maven으로 빌드하고 tomcat에 배포할 것이다.
artifact를 빌드하기 전에 backend 서비스인 db, memcached, rabbitmq 서비스와 연결할 설정 파일을 확인해야 한다.
src/main/resources/application.properties
#JDBC Configutation for Database Connection
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://db01:3306/accounts?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
jdbc.username=admin
jdbc.password=admin123
#Memcached Configuration For Active and StandBy Host
#For Active Host
memcached.active.host=mc01
memcached.active.port=11211
#For StandBy Host
memcached.standBy.host=127.0.0.2
memcached.standBy.port=11211
#RabbitMq Configuration
rabbitmq.address=rmq01
rabbitmq.port=5672
rabbitmq.username=test
rabbitmq.password=test
#Elasticesearch Configuration
elasticsearch.host =192.168.1.85
elasticsearch.port =9300
elasticsearch.cluster=vprofile
elasticsearch.node=vprofilenode
- 앞서 설정한 VM들의 정보들과 일치하는지 확인한다.
$ mvn install
- artifact를 빌드하기 위해 mvn install 명령어를 실행한다.
- maven으로 빌드한 artifact인 결과물이 war 파일이다.
- war 파일을 tomcat 서버에 배포할 것이다.
$ systemctl stop tomcat
$ systemctl status tomcat
$ rm -rf /usr/local/tomcat8/webapps/ROOT
- war 파일을 tomcat 서버에 배포하기 전에 tomcat server가 연결하고 있는 기본 파일을 삭제한다.
- 먼저 tomcat을 종료하고 기본 파일을 삭제한다.
$ cp target/vprofile-v2.war /usr/local/tomcat8/webapps/ROOT.war
$ ls /usr/local/tomcat8/webapps/
- war 파일을 tomcat으로 이동시킨다.
$ systemctl start tomcat
- tomcat을 실행하면 ROOT.war을 추출하여 ROOT 디렉토리에서 실행할 것이다.
Nginx Setup
$ vagrant ssh web01
$ sudo -i
$ apt update && apt upgrade -y
- nginx VM 로그인
- 루트 사용자 변경
- apt 패키지 업데이트
$ 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의 redirect 요청을 tomcat 서버 포트 8080으로 설정한다.
- nginx가 로드 밸런서처럼 작동할 것이다.
- nginx 서비스의 80번 포트에 접근하면 frontend는 80번 포트를 listen한다.
- tomcat의 포트인 8080으로 route한다.
- 만약 여러 개의 앱 서버가 있다면, 이와 같이 작성하면 될 것이다.
$ rm -rf /etc/nginx/sites-enabled/default
$ ln -s /etc/nginx/sites-available/vproapp /etc/nginx/sites-enabled/vproapp
$ systemctl restart nginx
- nginx의 기본 앱을 삭제한다.
- 기본 설정에 nginx 설정 파일을 연결한다.
- nginx 재시작
3. 브라우저에서 앱이 제대로 작동하는지 검증하기
- nginx의 private network로 설정한 IP 주소를 브라우저에 입력한다.
4. Iac - Automatically Provisioning
수동으로 VM을 세팅한 것과 달리 vagrant up 명령어 하나로 이 과정을 모두 자동화할 것이다.
bash script로 모든 서비스들을 provisioning할 것이다.
vagrantfile 변경
- 수동으로 진행한 모든 과정을 쉘 스크립트로 변환하여 작성한다.
- vagrantfile에 provision 섹터에 경로를 추가하여 작성해준다.
config.vm.define "db01" do |db01|
db01.vm.box = "jacobw/fedora35-arm64"
db01.vm.hostname = "db01"
db01.vm.network "private_network", ip: "192.168.56.15"
db01.vm.provision "shell", path: "mysql.sh"
db01.vm.provider "vmware_desktop" do |vmware|
vmware.gui = true
vmware.allowlist_verified = true
vmware.memory = "1024"
vmware.cpus = 2
end
end
- 위와 같이 다른 서비스들에도 위와 같이 provision 섹터를 작성한다.
- 그 다음 vagrant up 명령어를 입력하면 모든 서비스가 한 번에 세팅된다.
마치며
수동으로 VM과 서비스들을 세팅하는 데에 많은 시간이 걸리고 복잡하여 헷갈리기도 했다.
그런데 vagrantfile에 provision 섹션과 쉘 스크립트를 활용하여 간단하게 vm들을 provisioning할 수 있었다.
수동으로 하는 것이 복잡할지라도 이 과정을 통해 수동으로 여러 서비스를 테스트할 수 있고, 하나하나 자동화 할 수 있는 능력을 기를 수 있었다. 자동화하기 전에 수동으로 모든 과정을 직접 진행하는 것의 중요성을 느끼기도 했다.
또한, Java에서 applicaion.properties를 설정하고 nginx를 로드 밸런서로 활용함으로써 간단하게 엔진 서버와 앱 서버를 나누었다.
이 프로젝트를 진행하면서 가장 크게 얻은 것은 서비스를 직접 세팅하는 능력과 provisioning - IaC의 중요성을 체감했다.
다음에는 docker, aws, k8s를 활용하여 현업의 프로젝트와 유사한 환경을 구성하도록 하겠다.