도커로 로컬 환경을 서버 환경으로 옮기기
2022년 1월 11일잔뜩 개발한 서비스의 마지막 단계는 서버에 올려 확인하는 작업입니다. 도커를 몰랐던 시절, 저는 로컬 환경을 구성 했던 것처럼 똑같이 서버 환경을 설정 했습니다. 환경 설정에 대한 기록을 일일이 하지 않아 기억을 더듬고 오류를 만나면서 겨우 환경을 구축했습니다. 그러다 보니 원치 않은 프로그램들이 깔리기도 하고 의존성 패키지들이 꼬인 찝찝한 서버를 볼 수 있었습니다. 로컬에서 개발한 환경을 그대로 서버에 옮길 수만 있다면 편하지 않을까 생각하던 와중, 인생 프로그램인 **docker(이하 도커)**를 만납니다. 이번 글은 제가 애용하는 도커를 가지고 로컬/서버 개발 환경을 구축하는 방법을 소개하겠습니다. 기본적인 도커의 개념, 사용법은 생략하고 진행됩니다. 도커 공부를 시작하고 싶다면 subicura님의 ‘초보를 위한 도커 안내서’를 추천 드립니다.
로컬에 구축했던 환경을 서버로 옮기면서 불편했던 점은 다음과 같습니다.
- 관련 프로그램 및 패키지 설치
- 서비스를 돌리기 위한 최소한의 환경 구축
- 내 컴퓨터/서버에 1회성 프로그램을 설치하고 싶지 않다! or 개발 관련 툴이 내 컴퓨터 자원을 갉아 먹는 것이 싫다!
2번은 서버를 돌리는데 필요하지 않는 프로그램을 설치하기 싫은 필자의 생각입니다. 마음만은 패키지 하나하나 프로그램 실행과 관련이 있는지 확인하고 싶지만 현실은 서버 실행만 잘 된다면 감지덕지합니다. 3번 같은 경우는 db로 예를 들 수 있겠습니다. 프로젝트에 DB 사용이 잦은데 새로운 프로젝트마다 새로운 DB를 사용하고 싶은 마음도 있고 DB를 사용하고 있지 않는데 줄줄 새는 자원을 낭비하고 싶지 않아 매번 끄기도 귀찮기도 하고 무엇보다도 DB 데이터가 알게 모르게 계속 쌓여가는게 싫었습니다. (저만 그런건가요..?) 이런 고민을 덜어주는 것이 바로 도커입니다.
도커의 dockerfile과 docker-compose를 사용해서 위의 문제를 해결할 수 있습니다. 프로젝트 시작 전, 이제는 반사적으로 도커화(dockerize) 쪽으로 생각합니다. 도커를 통해서 환경 구성이 가능한지? 이를 위해 필요한 패키지가 무엇인지? 장황하게 설명 하는 것 보다 최근에 도커화를 진행했던 Spring Boot 환경 구축을 예시로 살펴보겠습니다.
Spring Boot 백엔드 코드를 가지고 도커 컨테이너에 올려 자동으로 빌드하고 실행하는 것을 목표로 삼았습니다. 도커화를 위해 가장 먼저 한 일은 docker hub 사이트에 접속하여 관련 도커 이미지가 있는지 확인합니다. docker hub에서 운영체제, 아키텍처, 버전 별로 도커 이미지를 제공하기 때문에 편리합니다. Spring Boot를 빌드하기 위해선 java와 gradle이 필요하므로 이를 검색해보니 openjdk와 gradle 이미지가 있습니다. openjdk와 gradle이 함께 깔려져 있는 gradle 이미지를 써도 되지만 Spring Boot를 빌드하면 자동으로 gradle를 설치해주므로 저는 openjdk 이미지를 선택했습니다. 컴팩트한 환경을 구축하고 싶다면 alpine 이미지를 위에서 필요한 패키지를 한땀한땀 설치해도 좋습니다.
다음은 dockerfile과 docker-compose의 작성입니다. dockerfile를 통해 구체적으로 설치하고 싶은 프로그램을 명시하는 과정입니다. 이번 예제에서는 Spring Boot에 필요한 java(openjdk) 말고는 필요한 프로그램이 없어 따로 dockerfile을 작성하지 않았습니다. 만약 도커 컨테이너 위에서 작업을 해야 한다면 vim, procps, vimrc/bashrc 설정 등을 추가하여 자신한테 맞는 환경을 만들 수 있습니다. 아래는 Nestjs 환경을 위해 작성했던 dockerfile 입니다.
DOCKERFROM node:16.13.0-stretch WORKDIR /home/nest/ RUN apt-get update && apt-get install -y \ vim \ procps \ && rm -rf /var/lib/apt/lists/* COPY ./entrypoint.sh /entrypoint.sh COPY bashrc /root/.bashrc RUN [ "chmod", "+x", "/entrypoint.sh" ]
작성한 dockerfile을 docker build를 하여 이미지를 만들고 docker run으로 컨테이너를 실행해도 좋지만 실행해야 하는 컨테이너가 여러 개라면 docker-compose를 사용하는 것이 편합니다. 길고 긴 docker run 명령어를 안 쳐도 되지만 docker-compose로 작성한 컨테이너들끼리 통신할 수 있는 네트워크가 만들어집니다. 필자는 보통 서버 컨테이너 1개, DB 컨테이너 1개로 총 두 개의 컨테이너를 구성합니다.
- (참고) docker run vs docker-compose
BASHdocker run --name pg -e POSTGRES_USER=pg -e POSTGRES_PASSWORD=pg -e PGDATA=/var/lib/postgres/data -v ../server/data:/var/lib/postgres/data -p 5432:5432 -it postgres:12.4-alpine
YAMLversion: '3.5'
services:
db:
container_name: pg
image: postgres:12.4-alpine
environment:
- POSTGRES_USER=pg
- POSTGRES_PASSWORD=pg
- PGDATA=/var/lib/postgres/data
volumes:
- ../server/data:/var/lib/postgres/data
ports:
- 5432:5432
YAMLversion: "3.5"
services:
app:
container_name: app
image: openjdk:17
volumes:
- ../:/home/test
ports:
- 8080:8080
tty: true
db:
container_name: db
image: mysql:8.0
environment:
- MYSQL_DATABASE=test
- MYSQL_ROOT_PASSWORD=test
- MYSQL_USER=test
- MYSQL_PASSWORD=test
ports:
- 3306:3306
이제 컨테이너를 만들고 컨테이너가 띄워졌을 때 실행할 동작을 설정합시다. 저희의 목표는 자동으로 컨테이너가 띄워지면 Spring Boot의 빌드와 실행을 해야하니 gradlew를 통해 빌드하고 java 명령어로 실행을 하면 됩니다. docker의 ENTRYPOINT도 좋은 방법이지만 쉘 스크립트로 작성해보겠습니다. Spring Boot와 Mysql의 연결을 위한 설정 파일인 application.properties
파일을 복사하는 과정을 넣고 쉘 스크립트를 작성하면 이런 모습이 됩니다.
BASH#!/bin/sh docker-compose -p test up -d FILE="/home/test/src/main/resources/application.properties" if [ ! -e $FILE ]; then docker cp application.properties app:/home/test/src/main/resources fi docker exec -w /home/test -it app sh -c "chmod +x gradlew && ./gradlew bootJar && cp build/libs/*.jar /app.jar && java -jar /app.jar" docker stop app db
드디어 한 번의 run.sh
스크립트 실행으로 Spring Boot를 실행할 수 있게 되었습니다. 애초에 도커 이미지를 만들 때 Spring Boot 백엔드 코드를 COPY하거나 빌드 파일만 가지고 와서 컨테이너를 생성하면 되지 않느냐라고 할 수 있겠습니다. 필자는 Spring Boot 백엔드 코드를 볼륨으로 연결하여 백엔드 코드의 업데이트가 있을 때마다 이를 반영해 다시 빌드하고 실행하는 식으로 구성하고 싶어 위와 같이 구축하였습니다.
음? 그래서 로컬 환경 구성은 됐고 서버 환경은 어떻게 구성하면 되는건가요? 이때까지 한 작업이 동일하게 서버 환경에서도 적용이 됩니다. 도커 프로그램과 도커 파일만 있다면 최소한의 프로그램, 패키지 설치로 환경 구축을 할 수 있습니다. 매번 프로젝트만의 컨테이너를 생성하고 삭제하면서 독립적인 환경을 만들어 다소 깨끗한 로컬 환경을 유지할 수 있습니다. 개발이 끝났다면 도커 데몬을 중지시켜 자원도 아낄 수 있답니다? 저는 이제 도커 없이는 살 수 없는 몸이 되어버려서 앞으로도 도커 사랑이 이어질 예정입니다. 여러분도 도커로 컴퓨터 환경에 구애받지 않고 서비스 환경을 구축해보세요.