게시: 2020년 12월 24일
수정: 2021년 3월 5일
대부분의 경우 AWS의 RDS나 워크스테이션의 DB 등을 사용하여 안정적인 DB 환경을 구축하지만, 테스트 용도나 간단한 프로젝트에서 DB를 구현할 때는 Docker를 이용할 수 있다. 이러면 MySQL의 데이터가 안정적이지 않다고 하는 사람들도 있지만, 그 문제는 volume을 연결하는 방식으로 해결할 수 있다. 그런데 막상 docker-compose로 node.js 서버와 mysql을 연결해보면 연결 오류가 나는 걸 알 수 있다. 그 문제는 바로 실행 순서 때문인데, mysql이 node.js 서버보다 느리게 구동되기 때문이다. 이를 위해 node.js가 mysql보다 느리게 구동되도록 동기화하는 작업이 필요하다.
이 포스팅에서는 node.js와 mysql을 예시로 들었지만 다른 언어의 서버, 다른 언어의 DB 모두에 적용할 수 있다.
위에서 언급한 것과 같이 /sync-docker/db/data 폴더는 mysql docker의 데이터 volume을 공유하는 폴더이다. 따라서, docker를 다시 실행하더라도 정보가 보존된다. 또한 /sync-docker/db/init 폴더의 init.sql을 생성하고 volume을 mysql docker와 공유하면 새로 시작할 때 초기의 DB와 Table들을 생성해둘 수 있다.
📦sync-docker
┣ 📂db
┃ ┣ 📂data
┃ ┣ 📂init
┃ ┃ ┗ 📜init.sql
┃ ┗ 📜.gitignore
┣ 📂server
┃ ┣ 📂node_modules
┃ ┣ 📜.dockerignore
┃ ┣ 📜.gitignore
┃ ┣ 📜docker-entrypoint.sh
┃ ┣ 📜Dockerfile
┃ ┣ 📜index.js
┃ ┣ 📜package-lock.json
┃ ┗ 📜package.json
┗ 📜docker-compose.yml
우선, 실행되는 컨테이너 간 네트워크를 연결하기 위해선 반드시 docker의 network를 생성해야 한다. 이 부분은 설명이 길어지니 안다고 가정하고 넘어가겠다. 또한, server 컨테이너에서 생성한 network에 접속함과 동시에 depends_on을 사용해 db 컨테이너의 이름을 지정하면 된다. (여기서는 db 컨테이너의 이름을 'db'로 지정했다.)
db 컨테이너에서는 volumes 부분에서 위에서 언급한 /init, /data 폴더를 db 컨테이너 내부의 폴더와 연결한다. (데이터 보존 및 초기화를 위해) 또한, command 부분에서는 DB의 문자 체계를 utf8로 지정하고 있다.
## docker-compose.yml
version: "3.5"
services:
server:
build:
context: ./server
restart: always
ports:
- 8080:8080
networks:
- db-net
depends_on:
- db
db:
image: mysql:8.0.20
command: mysqld --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
restart: always
volumes:
- ./db/init:/docker-entrypoint-initdb.d
- ./db/data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
networks:
- db-net
ports:
- 8086:3306
networks:
db-net:
기본적으로 /node_modules 폴더, package-lock.json, package.json, .gitignore 파일들은 node.js에서 항상 존재하는 것이므로 설명을 생략하겠다.
아래의 Dockerfile에서 dockerize라는 모듈을 설치하는 데, 이 모듈은 docker-compose에서 docker 컨테이너 간의 실행 순서를 동기화하는 기능을 수행해준다. 설치를 마친 후에 docker-entrypoint.sh 파일을 컨테이너에서 실행시켜 실행 순서를 동기화시켜준다.
## Dockerfile for node.js
FROM node:12.18.0
ENV DOCKERIZE_VERSION v0.2.0
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
RUN npm install -g nodemon
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
RUN chmod +x docker-entrypoint.sh
ENTRYPOINT ./docker-entrypoint.sh
EXPOSE 8080
위의 Dockerfile에서 설치한 dockerize를 사용하는 shell script이다. 설치된 dockerize 모듈을 통해 db 컨테이너의 3306 포트가 연결될 때까지 즉, db 컨테이너가 구동 준비를 완료할 때까지 기다린 후 동작을 서버를 실행시킨다.
## docker-entrypoint.sh for node.js
echo "wait db server"
dockerize -wait tcp://db:3306 -timeout 20s
echo "start node server"
nodemon index.js
docker 컨테이너에 복사되지 않아야 하는 폴더 또는 파일을 지정한다.
## .dockerignore for node.js
.gitignore
/node_modules
node.js docker 컨테이너에서 실행될 node.js 파일이다. mysql2 모듈을 설치해야 한다.
npm install --save mysql2
mysql의 연결이 제대로 진행됐는지 여부를 판단할 수 있게 코드를 작성했다. 아래의 host의 값은 docker-compose.yml에서 db 컨테이너의 이름을 변경하면 임의로 변경할 수 있고, password 값은 위의 docker-compose.yml에서 MYSQL_ROOT_PASSWORD의 값을 변경하면 임의로 변경할 수 있다. 또한, database도 아래의 MySQL 파트에서 init.sql을 임의로 수정하면 변경할 수 있다.
// index.js
const mysql = require("mysql2/promise");
const dbConnect = async () => {
try {
const connection = await mysql.createConnection({
host: "db",
user: "root",
password: "root",
database: "SampleDB",
});
console.log("mysql connection success");
} catch (error) {
console.log(error);
}
};
dbConnect();
mysql은 별도의 파일이 많이 존재하지 않으므로 초기화를 위한 init.sql만 설명하도록 하겠다. DB의 이름은 SampleDB로 지정했고, 문자 체계는 utf8로 지정했다.
CREATE DATABASE SampleDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE SampleDB;
CREATE TABLE User (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(32) NOT NULL,
PRIMARY KEY(id)
);