문제 상황
- Github Action을 통해 CD(자동화 배포)과정을 구축하는 상황
- name: Deploy to Prod uses: appleboy/ssh-action@master id: deploy-prod with: host: ${{ secrets.NCP_PROD_SERVER_IP }} username: ${{ secrets.NCP_PROD_SERVER_USER }} password: ${{ secrets.NCP_PROD_SERVER_PASSWORD }} port: ${{ secrets.NCP_PROD_SERVER_SSH_PORT }} script: | sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} sudo docker rm -f $(docker ps -q -a) sudo docker pull ${{ secrets.DOCKER_REPO }}/goods-for-you docker-compose up -d docker image prune -f
- deploy.yml 파일
문제 상황 중 배포 서버에 배포를 진행하는 script 입니다. 이 중 docker-compose up -d 명령을 통해
배포 서버에 존재하는 docker-compose.yml 파일을 통해 배포를 진행하고 있었습니다.
docker-compose.yml 파일
version: '3.8'
services:
redis:
image: redis:7.0.4 #사용할 도커 이미지를 지정
command: redis-server --port 6379 #디폴트 커맨드를 덮어씁니다.
container_name: redis_standalone #컨테이너의 이름을 지정합니다.
hostname: redis_standalone # 컨테이너에 사용할 사용자 지정 호스트 이름을 지정합니다.
labels: #메타 데이터를 컨테이너에 추가합니다. 이 때 배열 또는 Map을 사용할 수 있습니다.
- "name=redis"
- "mode=standalone"
ports: # (HOST : CONTAINER)의 형식으로 두개의 포트를 모두 지정하거나, 컨테이너 포트만 지정할 수도 있습니다.
- 6379:6379
mysql:
image: mysql:8.0.27
cap_add:
- SYS_NICE
container_name: mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ...
MYSQL_DATABASE: ...
MYSQL_USER: ...
MYSQL_PASSWORD: ...
goods-for-you-app:
container_name: goods-for-you-app
build: .
depends_on:
- redis
- mysql
image: aorri2/goods-for-you
environment:
SPRING_DATASOURCE_URL: ...
SPRING_DATASOURCE_USERNAME: ...
SPRING_DATASOURCE_PASSWORD: ...
SPRING_DATA_REDIS_HOST: ...
SPRING_DATA_REDIS_PORT: ...
ports:
- "80:8080"
restart: always
# Docker - Compose
# 다수의 도커 컨테이너들을 통합해서 관리해 주는 툴입니다.
# 한 파일 안에 컨테이너 여러개의 설정 내용을 저장해서 연결시켜줍니다.
위와 같은 docker-compose 파일을 up 명령어를 통해 여러 서비스 컨테이너를 한 번에 생성하고 실행했습니다.
위 CD 과정에서 docker-compose up 명령어를 통해 컨테이너가 생성되고 실행될 때 마다, 테이블의 스키마와 테이블의 데이터가 초기화 되는 현상이 발생했습니다.
원인 분석
도커 컨테이너의 데이터 관리 방법
도커에서 컨테이너를 생성하고 실행하면, 해당 컨테이너 내부에 있는 데이터가 계속 유지되는 걸로 알고 있었습니다. 예를 들어 Mysql 컨테이너를 생성하고 해당 컨테이너 내부에 USER 라는 테이블이 존재하고, 그 안에 유저 관련 데이터가 존재한다고 가정하면 Mysql 컨테이너가 정지되고 삭제된 후에도 데이터가 남는다고 생각 했습니다.
하지만 실제로는 도커 컨테이너 안의 파일 변경 사항을 UnionFS라는 파일 시스템 서비스를 통해 관리합니다.
UnionFS는 이미지 layer와 write layer를 합쳐 container의 데이터를 관리하는 데, container 삭제 시 write layer도 삭제 됩니다. 또한 write layer에는 이미지 layer의 데이터에서 변경된 사항을 저장하므로 write layer 삭제 시 데이터가 사라집니다
위와 같은 정보를 토대로, 도커 컨테이너가 삭제되면 컨테이너 내부에 있던 데이터도 함께 삭제된다는 점을 알게 되었습니다.
docker-compose up 명령어를 통해 컨테이너를 생성하고, docker-compose down 명령어를 통해 컨테이너의 서비스를 중지하고 삭제하는 과정에서, 컨테이너 내부에 있는 데이터들도 함께 삭제된다는 점을 결론적으로 알게 되었습니다.
해결 과정
앞서 설명한 docker container의 데이터 휘발성 때문에 데이터를 container가 아닌 호스트에 저장할 때, volume을 사용할 수 있다는 것을 깨달았습니다.
볼륨은 Docker 컨테이너에서 생성하고 사용하는 데이터를 지속하기 위해 사용할 수 있는 메커니즘 입니다.
위 그림에서 Host는 현재 사용중인 PC를 의미합니다, Container에서 생성된 데이터를 Host PC의 파일 시스템의 Docker area라는 영역에 저장함으로써, Container가 삭제되더라도 데이터를 영속화 할 수 있게 되는 것 입니다.
services:
frontend:
image: node:lts
volumes:
- myapp:/home/node/app
volumes:
myapp:
따라서 위와 같이 volume을 설정해줌으로써, 컨테이너가 삭제되더라도 DB의 데이터가 유지될 수 있도록 할 수 있었습니다.
개인적인 생각으로 배포시에 배포 서버에서 재 배포되어야 하는 것은 애플리케이션 서버지, mysql이나 그 외 인프라적 요소들이 재배포되는 것은 맞지 않다고 생각합니다.
참고 자료
https://docs.docker.com/storage/volumes/#use-a-volume-with-docker-compose
'프로그래밍 > 프로젝트' 카테고리의 다른 글
Builder 패턴? (0) | 2023.04.05 |
---|---|
테스트 커버리지를 70% 이상 유지하면서 느낀점 (0) | 2023.03.21 |
캐싱은 언제 적용하는게 좋을까? (2) | 2023.03.07 |
CAP 이론을 바탕으로 NoSQL 을 적용 할 만한 포인트 고려 (0) | 2023.03.02 |
GoodsForYou 패키지 구조에 대한 고민(포트와 어댑터) (0) | 2023.02.25 |