MSA 구현하기(4) - DB 구성 (feat. Docker)
Skala 과정에서 마이크로 서비스 아키텍처 구조에 대해 새롭게 알게 되었습니다.
배운 것을 실제로 구현해보기 위해 이 여정을 시작하기로 했습니다.
처음 글부터 보러가기
Docker를 이용하여 MariaDB 서버를 3개 만들어보겠습니다!
MariaDB 서버 띄우기
docker-compose.yml 구성
여러 개의 MariaDB 서버를 띄워야 하기 때문에 docker-compose.yml 파일을 이용해서 한꺼번에 띄워보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
version: "3.8"
services:
order-db:
image: mariadb:latest
container_name: order-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${ROOT_PASSWORD}
MYSQL_DATABASE: order
MYSQL_USER: ${USER}
MYSQL_PASSWORD: ${PASSWORD}
ports:
- "3307:3306"
volumes:
- order_data:/var/lib/mysql
- ./order-init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- mariadb_network
healthcheck:
test: ["CMD", "mariadb", "-uroot", "-p${ROOT_PASSWORD}", "-e", "SELECT 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
item-db:
image: mariadb:latest
container_name: item-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${ROOT_PASSWORD}
MYSQL_DATABASE: item
MYSQL_USER: ${USER}
MYSQL_PASSWORD: ${PASSWORD}
ports:
- "3309:3306"
volumes:
- item_data:/var/lib/mysql
- ./item-init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- mariadb_network
healthcheck:
test: ["CMD", "mariadb", "-uroot", "-p${ROOT_PASSWORD}", "-e", "SELECT 1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
user-db:
image: mariadb:latest
container_name: user-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${ROOT_PASSWORD}
MYSQL_DATABASE: user
MYSQL_USER: ${USER}
MYSQL_PASSWORD: ${PASSWORD}
ports:
- "3308:3306"
volumes:
- user_data:/var/lib/mysql
- ./user-init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- mariadb_network
depends_on:
order-db:
condition: service_healthy
item-db:
condition: service_healthy
volumes:
user_data:
order_data:
item_data:
networks:
mariadb_network:
driver: bridge
docker-compose.yml 파일을 Git에 올릴 예정이어서 따로 .env 파일을 만들어서 변수들의 보안을 관리해주었습니다.
.env 파일은 docker-compose.yml과 같은 경로에 저장하면 됩니다.
각 서버의 포트번호도 겹치지 않게 설정합니다.
도커 컨테이너로 올리지 않은 로컬 mysql과 겹치지 않게 3306 포트는 피해서 설정해주었습니다
사실 로컬에 MySQL 과 MariaDB를 같이 설치하니 충돌이 일어나서 Docker를 사용하게 되었습니다..
user-db 는 order-db 와 item-db 가 생성된 이후에 federated 엔진을 통해 테이블을 생성해야 하기 때문에 healthcheck 를 수행하여 두 컨테이너가 정상적으로 생성된 이후에 생성되도록 설정하였습니다
.env 파일
1
2
3
4
5
6
7
8
9
ROOT_PASSWORD={비밀번호}
USER={유저 이름}
PASSWORD={비밀번호}
USER_DATABASE=user
ITEM_DATABASE=item
ORDER_DATABASE=orders
ORDER_TABLE=orders
ITEM_DATABASE=item
ITEM_TABLE=item
데이터베이스 생성 및 유저 생성/권한 부여 자동화
매번 docker-compose up -d 을 실행할 때마다 데이터베이스를 만들고, 유저를 생성하는 일은 굉장히 귀찮습니다.
원래 모놀리식 구조로 동작할 때 유저와 데이터베이스를 자동으로 만들어주는 파일을 만들 때에는 init.sql 파일 하나면 되지만 저는 MSA 구조로 동작하기 때문에 만들어야 할 파일이 많습니다.
그래서 서버 별로 데이터베이스가 자동으로 만들어질 수 있게 .sh 파일을 먼저 만들어보겠습니다.
entrypoint.sh
1
2
3
4
5
#!/bin/sh
export $(grep -v '^#' .env | xargs)
for file in user-init-template.sql item-init-template.sql order-init-template.sql; do
envsubst < "$file" > "${file/-template/}"
done
이렇게하면 user-init-templete.sql, item-init-template.sql, order-init-template.sql을 한번에 각각의 init.sql 파일로 만들 수 있습니다.
export ~ 구문을 실행하는 이유는 .env 파일에 저장되어 있는 환경 변수로 안전하게 데이터베이스와 유저를 생성하기 위함입니다.
또한, 한꺼번에 변수들을 관리할 수 있어 유지보수도 편리합니다.
init.sql 파일 (Ex: user-init-template.sql)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
CREATE DATABASE IF NOT EXISTS `${USER_DATABASE}`;
USE `${USER_DATABASE}`;
CREATE TABLE IF NOT EXISTS `${USER_TABLE}` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`email` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`phone` varchar(255) DEFAULT NULL,
`username` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci;
CREATE TABLE IF NOT EXISTS federated_item (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
quantity INT(11) NOT NULL,
seller_id BIGINT(20) DEFAULT NULL,
description VARCHAR(255) DEFAULT NULL,
name VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=FEDERATED
CONNECTION='mysql://${USER}:${PASSWORD}@host.docker.internal:3309/item/item';
CREATE TABLE IF NOT EXISTS federated_orders (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
address VARCHAR(255) DEFAULT NULL,
customer_id BIGINT(20) DEFAULT NULL,
description VARCHAR(255) DEFAULT NULL,
item_id BIGINT(20) DEFAULT NULL,
order_date DATETIME(6) DEFAULT NULL,
quantity INT(11) NOT NULL,
PRIMARY KEY (id)
) ENGINE=FEDERATED
CONNECTION='mysql://${USER}:${PASSWORD}@host.docker.internal:3307/orders/orders';
CREATE USER IF NOT EXISTS '${USER}'@'%' IDENTIFIED BY '${PASSWORD}';
GRANT ALL PRIVILEGES ON `${USER_DATABASE}`.* TO '${USER}'@'%';
FLUSH PRIVILEGES;
이런 식으로 모든 파일을 작성해주면 docker-compose up -d 만 실행해주면 MariaDB 서버 3개가 동시에 생기게 됩니다.
위 sql문의 단점이 한 가지 있는데,
스프링에서 엔티티의 구조가 바뀔 때마다 DDL을 변경해주어야 한다는 점입니다,,,
###### 더 좋은 방법이 있다면 알려주세욥,,
다른 좋은 방법이 생기면 수정해보겠습니다!
2025.03.14
찾았습니다! 요기로 오세욧
Federated 관련 설정을 아예 없애도 좋습니다 ^^
(즉, init.sql의 테이블 선언 코드 부분이 없어도 된다는 의미입니다 하하)
그래도 데이터베이스를 생성할 때마다 계속해서 데이터베이스를 생성하고, 유저를 만들어서 권한을 부여하는 일련의 과정들이 생략되었습니다!
FEDERATED 엔진
MariaDB 에 있는 FEDERATED 엔진을 사용하면 Oracle의
SYNONYM을 이용한 DB Link 같이 실시간으로 외부 데이터베이스에 있는 테이블을 읽어올 수 있습니다.차이점은
Oracle은 접속하고나면 권한이 허용된 범위내에서 단순 SELECT 뿐만 아니라,다양한 작업을 자유롭게할 수 있지만(원격 테이블 수정 가능!),
MariaDB는 각자 다른 데이터베이스 간 테이블 동기화에 더 가깝습니다. (원격 테이블 수정 불가!)이를 통해 CUD만 가능했던 Kafka의 단점을 보완할 수 있었습니다!
무한 감사,,
MariaDB에도 Synonym과 비슷한게 있을거라고 힌트 주신 Skala 정윤석 교수님 감사합니다,,
결과
아주 잘 생성된 모습을 볼 수 있습니다!
마치며
아직 많이 부족하지만..열심히 달려보겠습니다…!!
다음은 Federated 기술을 사용하지 않고, GraphQL 을 통해 API Server와 통신하고, API Server가 각 서버로 요청을 전달하는 형태로 동작하는 API 기능 구현에 대해 작성해보겠습니다 🤯
참고 자료


