Skip to main content

Command Palette

Search for a command to run...

[ 살펴보기 ] Docker - Volume

Updated
4 min readView as Markdown
[ 살펴보기 ] Docker - Volume
C

A developer living in Busan, Korea

컨테이너의 파일 시스템은 실행중인 컨테이너와 생애주기가 같다. 즉, 구동되는 컨테이너가 삭제되면 해당 컨테이너에 새로 생성했던 모든 데이터도 함께 사라진다.

컨테이너 기반 개발을 한다면 컨테이너의 삭제는 아주 빈번하게 발생한다. 하지만 그때 마다 컨테이너 안의 데이터가 함께 사라지면 굉장히 곤란할 것이다. 예를 들어 백엔드 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와 연결하여 사용할 수 있다.

More from this blog

[ 살펴보기 ] TypeORM - Transactions, Migration

Transation Database 종류에 따라 detail한 부분은 차이점이 조금씩 있겠지만 각 sql statement는 개별적인 transaction block을 통해 실행되며 Database 설정에 따라 sql statement의 실행 결과가 자동으로 commit되어 영구히 적용되거나 commit을 직접 실행하기 전까지는 영구히 적용되지 않을 수 있다. 대부분의 경우 default로 sql statement 실행 결과가 자동으로 comm...

Feb 9, 20256 min read
[ 살펴보기 ] TypeORM - Transactions, Migration

Dev Diary

184 posts