docker를 사용하여 배포하기 전에 제가 배포했던 방식에 대해 간략하게 설명해보겠습니다.
- AWS에서 우분투 이미지의 서버를 하나 생성하고 포트포워딩을 해준다.
- 우분투 서버에 jdk 19을 설치한다.
- 내 컴퓨터로 작업한 스프링 부트 프로젝트의 jar 파일을 생성한다.
- 이 jar 파일을 filezilla를 통해 우분투로 보낸다.
- 우분투 서버로 전송된 jar 파일을 실행한다.
1, 2 단계는 한번만 실행해서 이해할 수 있지만 매번 새로운 버전이 업데이트 될 때 마다 3, 4, 5 과정을 진행하면서 이건 매우 비효율적이라고 생각했습니다.
여러 기업에서 에자일을 도입하고 있는 와중에 배포 하나로 이렇게 시간을 허비할 수 없다고 판단했고 과정을 효율적으로 관리하기 위해 도커를 도입하기로 결정했습니다.
42에서 inception 과제를 했으면 대략적으로 docker가 무엇인지 어떻게 사용하는지 알고 있을 것 입니다. 해당 과제에서는 nginx, wp + php, mariaDB를 각 독립된 컨테이너에 올리고 docker-compose로 관리하는 방식이었습니다.
당시 과제를 진행하기전 virtual box를 사용하여 가상화를 통해 우분트를 배포한 뒤, 과제를 실행했는데, 돌이켜보면 도커와 기존 가상화방식의 차이점을 알려주려고 구성을 그렇게 짠 것 같다고 생각합니다.
가상머신과 같은 가상화는 OS와 하드웨어 드라이버를 host의 운영체제 즉 커널 단이 아닌 유저 단에 띄워서 가상화를 구현했습니다. 하지만 우리가 가상화를 사용하는 주 목적은 Application을 사용하기 위함인데 매번 운영체제를 따로 설치해주는 것은 host 컴퓨터 입장에서는 부담스러운 일이었습니다.
이를 개선하기 위해서 우리는 도커엔진을 이용하여 컨테이너라는 것을 띄웠습니다. 컨테이너는 Application을 사용할 수 있는 가상 환경이고 별도의 운영체제가 필요하지 않기 때문에 우리의 목적인 Application 사용도 가능하고 host환경에도 부담이 덜 가는 작업을 할 수 있었습니다.
이 때 저 컨테이너를 이미지를 통해 저장한 뒤, 다양한 곳에서 생성할 수 있습니다. 도커엔진이 설치된 환경에서 이미지만 있다면 불필요하게 우분투와 같은 운영체제를 설치하지 않고 Application을 실행할 수 있게 되는 것입니다. 이것이 제가 스프링부트 배포를 하게 된 배경이자 이유입니다.
특정 이미지로 컨테이너를 생성하고 일련의 작업을 수행하는 과정을 기록하는 파일입니다. 한줄 한줄 코드들은 레이어로써 동작합니다.우리가 자바배포 과정을 도커 컨테이너 환경에서 구축한다고 가정해보겠습니다.
- jdk 이미지로 컨테이너를 생성한다.
- host에 저장되어 있는 jar 파일을 컨테이너 내부로 복사한다.
- jar 파일을 빌드한다.
우리는 도커파일에 이 명령들을 레이어로서 기록하여 서버가 1대이든 100대 이든 자동으로 빌드가 될 수 있도록 구성할 수 있게 됩니다.
인텔리제이 우측 탭에 Gradle로 들어가 build/bootJar를 실행하여 Jar 파일을 생성해주겠습니다. Jar 파일은 build/libs 디렉토리에 생성될 것입니다.
build/bootJar는 Spring Boot 프로젝트에서 사용되는 Gradle 또는 Maven 빌드 도구의 명령어입니다.
bootJar 명령을 실행하여 생성된 JAR 파일에는 스프링 부트 애플리케이션과 해당 애플리케이션의 모든 의존성이 포함됩니다. 이 JAR 파일은 실행 가능한 형태로 빌드되어 있으며, 스프링 부트 애플리케이션을 실행할 수 있습니다.
이제 원하는 위치에 Dockerfile이라는 이름의 file을 생성해주겠습니다. 저는 프로젝트 디렉토리에 바로 생성했습니다.
FROM openjdk:19
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
이 프로젝트는 jdk 19을 기준으로 제작되었으므로 openjdk:19을 베이스 이미지를 기반으로 dockerfile을 작성합니다.
build/libs에 존재하는 jar 파일을 복사하여 app.jar파일을 컨테이너에 생성해주겠습니다.
그 후 jar 파일을 실행해줍니다. 이 때 COPY를 하는 이유는 도커파일을 수정할 때 마다 COPY 부분의 이미지만 수정되면 커밋 후의 용량이 크게 변하지 않기 때문입니다.
이제 터미널에서 dockerFile이 있는 디렉토리로 이동해준 후 dockerfile을 실행해주겠습니다.
$ docker build --tag <도커계정명>/soccerfriend:1.0.1 .
저는는 M1 맥북을 보유하고 있으므로
$ docker build --platform linux/amd64 --build-arg DEPENDENCY=build/dependency --tag <도커계정명>/jungmyeonghan/hometownboard:1.0.1 .
다음과 같이 별도로 platform 설정을 해주었습니다.
도커계정명 뒤에 오는 것이 이미지의 [이름:버전]을 의미합니다. 도커 계정명을 작성하는 이유는 dockerhub에 push할 때 계정이름이 이미지에 작성되어야 하기 때문입니다. 그 뒤에는 dockerfile의 경로를 작성하면되고 현재 디렉토리이므로 점 하나만 작성해주면됩니다.
$ docker images
위 명령을 통해서도 확인 가능합니다.
docker image 목록을 확인해보면 성공적으로 jungmyeonghan/hometownboard:1.0.1라는 이름으로 생성된 것을 확인할 수 있고 docker desktop이 설치되었다면 한눈에 확인할 수 있습니다.
위에서 만든 이미지로 컨테이너를 실행만 해주면 자동으로 스프링부트 프로젝트가 실행됩니다. 하지만 다른 서버 환경에서도 이 이미지에 접근하기 위해서는 docker hub에 push한 후 이미지가 필요할 때 pull 하는 것이 편합니다.
$ docker login
먼저 도커에 로그인을 해주고
$ docker push <이미지 이름>
해당 명령어로 도커허브로 push 해주면 됩니다. 이 때 이미지 이름은 작성자명, 이름, 버전등이 모두 포함된 풀네임으로 작성해야합니다.
이제 클라우드 환경에 방금 만들었던 도커 이미지가 공유되었습니다.(깃 레포지토리같은 느낌) 어디서든 도커가 설치된 환경이라면 도커허브에서 다운로드 받아 해당 이미지의 컨테이너를 생성할 수 있습니다.
AWS 서버를 하나 생성한 후 SSH로 접속하고, 새로운 서버에 접속했으므로 다시 도커에 로그인 해줍니다.
$ docker login
로그인을 해준 후 이미지를 도커 컨테이너에서 가져옵니다.
$ docker pull <이미지 이름>
도커 이미지 목록을 확인하면 방금 push 했던 도커이미지가 정상적으로 다운로드 되었을 것입니다.
$ docker images
해당 image를 실행할텐데 이 프로젝트는 스프링 MVC 기반으로 이루어져 있기 때문에 도커와 로컬의 포트포워딩을 잘 설정해주어야합니다. 해당 이미지는 8080포트를 할당해주었지만 이건 어디까지나 도커 컨테이너 내부의 포트입니다. 도커 컨테이너의 8080포트를 로컬의 8080포트와 포트 포워딩을 해주기 위해서
$ docker run -i -t -p 8080:8080 <도커이미지> &
해당 명령어로 컨테이너를 백그라운드로 생성해줍니다. 성공적으로 해당 서버의 공인 ip의 8080포트로 접속되는 것을 확인할 수 있습니다.
개발자로서 성장하는 데 큰 도움이 된 글이었습니다. 감사합니다.