[ 살펴보기 ] Docker - Volume
![[ 살펴보기 ] Docker - Volume](https://cdn.hashnode.com/res/hashnode/image/upload/v1719051197422/c06ce0ba-465f-4f12-9846-f73f6b3c6d42.jpeg)
컨테이너의 파일 시스템은 실행중인 컨테이너와 생애주기가 같다. 즉, 구동되는 컨테이너가 삭제되면 해당 컨테이너에 새로 생성했던 모든 데이터도 함께 사라진다.
컨테이너 기반 개발을 한다면 컨테이너의 삭제는 아주 빈번하게 발생한다. 하지만 그때 마다 컨테이너 안의 데이터가 함께 사라지면 굉장히 곤란할 것이다. 예를 들어 백엔드 application을 구동하고 있는 컨테이너에 데이터베이스 데이터가 컨테이너 삭제마다 사라진다면 우리는 새로운 컨테이너를 구동할 때 마다 회원가입부터 테스트에 필요한 데이터를 새로 생성해서 넣어야 한다.
위와 같은 상황을 방지하기 위해 데이터를 저장하는 Volume을 따로 생성하여 특정 Container에 붙여 사용할 수 있다. 데이터 저장을 위한 Volume은 컨테이너와는 별개의 생애주기를 가지기에 컨테이너가 삭제되어도 Volume이 저장하고 있는 데이터에는 지장을 주지 않는다
Database의 데이터를 유지하고 있는 Volume을 이용해 삭제 후 새로운 컨테이너에서도 이전에 생성된 데이터를 가지고 있는 Volume을 연결해 사용해보자. 간단한 테스트를 위해 API server는 NodeJS framwork인 NestJS와 Database는 mysql를 사용할 것이다.
Mysql with Volume
우선 다음 명령어를 통해 Volume을 생성한다
docker volume ( 볼륨이름 )
// ex) docker volume volume-mysql
그리고 Database container와 API container를 연결해줄 network를 생성한다
docker network create ( network 이름 )
// ex) docker network create test-volume-network
Mysql Docker Image는 데이터를 /var/lib/mysql 경로에 저장하므로 우리가 만든 Volume을 해당 경로에 mapping 시켜야 한다.
다음 명령어를 통해 mysql container를 실행할 때 앞서 만든 volume과 network를 연결한다
docker run --name mysql-container -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=비밀번호 -v volume-mysql:/var/lib/mysql --network test-volume-network mysql:8
위의 명령어에서 사용된 option을 하나씩 살펴보자
--name mysql-container
// 생성할 contianer 이름
-d
// container를 background로 실행
-p 3306:3306
// mysql의 default port 번호인 3306으로 port mapping
-e MYSQL_ROOT_PASSWORD=비밀번호
// mysql root 계정 접속을 위한 비밀번호 환경변수, 비밀번호 부분에
// 원하는 비밀번호를 입력
-v volume-mysql:/var/lib/mysql
// volume-mysql라는 이름의 volume을 컨테이너의 /var/lib/mysql 경로에
// 마운트한다, 즉 mysql에서 생성되는 데이터는 해당 볼륨에도 저장된다
// ( Mysql Image는 데이터를 /var/lib/mysql 경로에 저장하므로 )
--network test-volume-network
// container를 실행할 때 test-volume-network라는 network를 사용
mysql:8
// mysql 8버전 image를 사용하여 container 생성
API Server
API 요청을 통해 database에 데이터를 저장 및 읽기를 하기 위한 API server로 NodeJS의 NestJS framework를 통해 간단한 구현하였다. ( NestJS에 대해서는 해당 포스트에서 다루지 않으므로 기본적은 내용은 Documentation 참고를 부탁드린다 )
예제에서 사용된 Dockerfile은 다음과 같다
FROM node:18-alpine As development
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY --chown=node:node package*.json ./
RUN npm ci
COPY --chown=node:node . .
USER node
FROM node:18-alpine As build
WORKDIR /app
COPY --chown=node:node package*.json ./
COPY --chown=node:node --from=development /app/node_modules ./node_modules
COPY --chown=node:node . .
RUN npm run build
ENV NODE_ENV production
FROM node:18-alpine As production
COPY --chown=node:node --from=build /app/node_modules ./node_modules
COPY --chown=node:node --from=build /app/dist ./dist
EXPOSE 4000
CMD [ "node", "dist/main.js" ]
NestJS container를 생성할 때 주의할 점은 database 정보 설정시 host를 mysql container 이름으로 지정해야 한다
// app.module.ts
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'mysql-container', // mysql container 이름
port: 3306,
...
}),
...
],
controllers: [AppController],
providers: [AppService],
})
...
Dockerfile과 image로 만들 NestJS application code가 준비되었으면 다음 명령어를 통해 image를 생성한다
docker build -t test-volume-backend .
이제 생성된 backend image를 통해 container를 실행 시켜보자. backend container 역시 mysql과 동일한 network로 실행해주어야 한다
docker container run --name backend-container -d -p 4000:4000 --net test-volume-network test-volume-backend
위의 명령어 옵션을 하나씩 살펴보자
--name backend-container
// 생성할 contianer 이름
-d
// container를 background로 실행
-p 4000:4000
// 테스트용 backend가 4000 port에서 실행되고 있으므로
// 4000을 기준으로 port mapping
--network test-volume-network
// container를 실행할 때 test-volume-network라는 network를 사용
test-volume-backend
// 위에서 생성한 test-volume-backend 이미지로 container 실행
위의 backend container까지 정상적으로 운영이 된다면 mysql container와 backend container는 같은 network 상에 운영되고 있으며 mysql container는 volume에 새로운 데이터를 저장하고 있기에 mysql container를 지우고 새로 만들더라도 기존 데이터를 가지고 있다.
이제 실제로 테스트를 해보자.
현재 필자가 테스트 용도로 사용중인 api server는 user table에서 user list를 출력하는 api와 새로운 user를 추가하는 api만 갖추고 있는 매우 간단한 api server다
Docker Desktop을 사용중이라면 Mysql container의 Exec tab을 통해 해당 container에 접속해 mysql을 실행해볼 수 있다

다음 명령어를 통해 mysql에 접속한다. 비밀번호는 위에서 mysql container를 생성 및 실행할 때 환경 변수로 함께 넘겨준 비밀번호를 입력하면 된다
mysql -u root -p
mysql이 익숙하지 않다면 테스트 점검에 필요한 간단한 명령어만 살펴보자
show databases;
// database list 출력
use 데이터베이스 이름;
// 특정 데이터베이스를 사용하기
show tables;
// table list 출력
select * from users
// users라는 table의 모든 데이터 조회하기
필자는 docker_volume이라는 database에 users라는 테이블을 생성하여 테스트를 진행하고 있으므로 mysql에 로그인 후 명령어를 다음과 같은 명령어를 통해 데이터를 조회할 수 있다
use docker_volume;
select * from users;
필자는 현재 users table에 3개의 데이터를 추가해 놓은 상태이며 mysql container를 삭제하고 다시 생성해 data를 volume으로 부터 다시 불러오는지 확인해보자

container 중지, 삭제를 위한 명령어는 다음과 같다
docker container stop mysql-container // mysql-container container 중지
docker container rm mysql-container // mysql-container container 삭제
그리고 위에서 mysql container를 생성 및 실행할 때 사용했던 명령어를 통해 다시 mysql container를 생성해본다
docker run --name mysql-container -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=비밀번호 -v volume-mysql:/var/lib/mysql --network test-volume-network mysql:8
mysql container를 지우고 새로 실행한 후에 위에서 했던 방식과 동일하게 Exec tab을 통해 해당 container에 접속하여 데이터를 확인해보자

위와 같이 데이터가 그대로 보존되는 것을 확인할 수 있다.
이처럼 특정 데이터를 별개로 보관해야 할 때 우리는 Volume을 사용하여 데이터를 보존할 수 있으며 해당 Volume을 특정 container와 연결하여 사용할 수 있다.
![[ 살펴보기 ] RDB - Relationships](https://cdn.hashnode.com/res/hashnode/image/upload/v1739711556668/48dc9e84-a621-42aa-9c9f-5fc5c436f0ec.jpeg)
![[ 살펴보기 ] MySQL - Data types](https://cdn.hashnode.com/res/hashnode/image/upload/v1739593589113/530f8704-4d27-42c9-a451-bb5c63150b99.jpeg)
![[ 살펴보기 ] TypeORM - Transactions, Migration](https://cdn.hashnode.com/res/hashnode/image/upload/v1739106042581/980b8133-61d4-406a-a026-65be9c28eace.jpeg)
![[ 살펴보기 ] TypeORM - Relations](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666874402/b688bd0b-b6bb-4f43-87d8-c1b46b59f1b7.jpeg)
![[ 살펴보기 ] TypeORM - Basics](https://cdn.hashnode.com/res/hashnode/image/upload/v1738666803591/bef5df17-7dc7-4123-ae55-004d5042df39.jpeg)