MySQL과 Spring Boot 서버를 Docker에 올리기(+Docker Hub)

Jongwon·2023년 5월 9일
4

DMS

목록 보기
17/18

Spring Boot와 mySQL을 도커에 올리고, 다른 host machine에서도 사용할 수 있도록 도커 이미지를 Hub에 올리도록 하겠습니다.

마지막으로는 여러 도커 컨테이너를 한번의 명령으로 실행할 수 있도록 Docker Compose파일도 생성하겠습니다.

해당 내용은 여러 구글링을 통해 진행된 과정이기 때문에 정석적인 방법은 아닐 수 있습니다.


1. 도커 네트워크 생성

mySQL과 Spring Boot 서버를 도커 상에서 연결하기 위해서는 서버와 DB 컨테이너가 네트워크를 통해 통신을 해야합니다.

  1. 터미널에 docker network create [네트워크명] 입력
    ex) docker network create 8am-net
  2. docker network ls 로 네트워크 생성 확인

도커 네트워크를 통해 여러 컨테이너를 그룹화하여 사용할 수 있습니다.



2. MySQL 이미지 생성

mySQL 이미지를 생성할 수 있는 방법은 크게 2가지가 있습니다.

  • mySQL 공식 이미지를 받아서 사용
  • host machine에 저장되어 있는 mySQL을 이미지로 만들어 사용

첫번째 방법은 비교적 간단합니다. 도커 데스크탑에서 mySQL 이미지를 검색하여 pull해오면 됩니다.

터미널에 docker pull mysql:[원하는 버전] 를 입력해도 됩니다.
ex) docker pull mysql:8.0.32



두번째 방법은 기존의 mySQL 컨테이너가 존재했다면 해당 컨테이너의 이미지를 받아오는 방법입니다.

터미널에 docker commit [컨테이너명] [생성할 이미지명]를 입력하면 됩니다.

ex) docker commit 8amDB mysql



진행이 완료되면 docker desktop에 mySQL 이미지가 생성된 것을 확인하실 수 있습니다.



mySQL 버전확인

mySQL 버전확인은 아래의 방법으로 진행할 수 있습니다.
1. MySQL Command Line Client 터미널 실행
2. 비밀번호 입력
3. SELECT VERSION(); 쿼리문 실행
4. 버전 확인(8.0.32)



3. Spring boot 탑재

DB를 이미지로 만들었으니, 이제는 서버를 이미지로 변환할 차례입니다. jar 파일을 Dockerfile을 통해 도커 이미지로 생성할 예정입니다.

  1. Dockerfile이란 이름의 파일을 프로젝트폴더에 생성

  2. Dockerfile 내부를 다음과 같이 작성

FROM openjdk:17
ARG JAR_FILE=/build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

FROM <이미지명>:<태그>
- Docker 기본 이미지, openjdk:[프로젝트의 jdk버전]
ARG
- 변수선언. JAR_FILE을 *.jar파일로 지정
COPY
- JAR_FILE을 컨테이너의 app.jar로 복사
ENTRYPOINT
- 컨테이너 시작 시 스크립트 실행

  1. Gradle Clean 실행

  2. build.gradle에 아래의 코드 작성 후 gradle 업데이트

    jar {
    enabled = false
    }
  3. Gradle bootJar 실행

  4. docker build -t [이미지명] . 명령으로 이미지 생성
    ex) docker build -t 8am_server .

    마지막의 마침표를 꼭 찍어주어야 합니다. Argument를 지정하는 부분이기 때문입니다.



4. Docker Hub에 올려 공유하기

필요한 도커 이미지를 모두 생성하였습니다. 이제 이 이미지들을 공유하여 다른 호스트에서도 같은 이미지를 사용할 수 있도록 하겠습니다.

  1. https://hub.docker.com 으로 이동합니다.
  2. 로그인을 진행합니다.
  3. Create Repository 버튼을 클릭합니다.
  4. Repository를 생성합니다. Repository 이름이 추후 이미지 이름이 될 것입니다.
    ex) 8am_backend_server
  5. Github에 local repo를 커밋하고 푸쉬하는 느낌으로 진행하면 됩니다. 업로드할 이미지를 Repository에 맞게 태그를 지정합니다. 터미널에 docker tag [기존이미지 이름]:[태그]  [docker계정명]/[repo명]:[태그] 명령을 입력합니다.
    ex) docker tag 8am_server:1.0 tank3a/8am_backend_server:1.0
  6. 터미널에서 Docker 로그인을 진행합니다.
    ex) docker login
  7. 새로 생성된 이미지를 Docker Hub에 올립니다. docker push [docker계정명]/[repo명]:[태그]를 입력합니다.
    ex) docker push tank3a/8am_backend_server:1.0

    아래에 Tag에 업로드한 이미지와 태그가 붙은 것을 확인할 수 있습니다.

    태그는 버전 번호로 생각하시면 됩니다. 아무것도 지정하지 않으면 latest로 자동으로 지정됩니다.



5. Docker-Compose 작성

이미지들을 모두 완성했으면, 컨테이너를 생성하고 이를 네트워크에 연결해야 합니다. 하지만 이 과정을 모두 커맨드창에 쳐야하는데 여간 불편한 일이 아닙니다. 예시로 위에서 생성한 Spring boot 이미지와 mySQL 이미지를 컨테이너로 만드는 과정을 보이겠습니다.

수동으로 컨테이너를 생성한다면?

  • Spring Boot Image
    docker run -p 8080:8080 --name [컨테이너명] --network [네트워크명] -d [도커이미지] 작성
    ex) docker run -p 8080:8080 --name 8amServer --network 8am_net -d 8am_server

  • mySQL Image
    docker run --name [컨테이너명] -p 3306:3306 --network [도커 네트워크명] -e MYSQL_ROOT_PASSWORD=[MySQL Root사용자 비밀번호] -e MYSQL_DATABASE=[db명] -e MYSQL_USER=[DB사용자명] -e MYSQL_PASSWORD=[User비밀번호] -d mysql:[버전] 작성
    ex) docker run --name 8amDB -p 3306:3306 --network 8am_net -e MYSQL_ROOT_PASSWORD=12341234 MYSQL_DATABASE=dms -e MYSQL_USER=dms_admin -e MYSQL_PASSWORD=12341234 -d mysql:8..0.32


docker-compose 파일 작성

컨테이너 수정사항이 생길 때마다 항상 이런 명령을 쳐야한다는 것은 현실적으로 너무 어렵습니다. 따라서 이를 대신해줄 docker compose라는 파일을 사용합니다.

원하는 위치에 docker-compose.yml파일을 생성합니다. 저는 서버 프로젝트 폴더 아래에 넣었지만 터미널로 접근할 수 있는 어떤 위치든 상관없습니다.

해당 파일에 아래와 같이 작성하겠습니다.

version: "3"                         //docker-compose 지원버전
services:                            //컨테이너들을 지정
  8amDB:                             //컨테이너명 - mySQL컨테이너
    container_name: 8amDB            //컨테이너명 직접 명시
    image: mysql:8.0.32              //해당 컨테이너가 가질 이미지
    restart: always                  //재시작 옵션
    volumes:                         //볼륨, 아래에서 자세히 설명하겠습니다.
      - mysql_volume:/app/mysql
    environment:                     //환경변수 설정
      - MYSQL_ROOT_PASSWORD=****
      - MYSQL_DATABASE=dms
      - MYSQL_USER=dms_admin
      - MYSQL_PASSWORD=****
    ports:                          //Docker와 연결할 포트
      - "3306:3306"
    networks:                       //컨테이너를 그룹화하는 네트워크 
      - 8am-net
    healthcheck:                    //컨테이너가 healthy한지 판단할 기준
      test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
      timeout: 20s
      retries: 10
  8amServer:                        //컨테이너명 - Spring boot 컨테이너
    container_name: 8amServer
    ports:
      - "8080:8080"
    image: tank3a/8am_backend_server:1.0
    volumes:
      - 8am_images:/app/8am/images
    networks:
      - 8am-net
    depends_on:                     //다른 컨테이너에 의존적으로 실행
      8amDB:
        condition: service_healthy  //8amDB 컨테이너가 healthy 상태일때만 실행
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://8amDB:3306/dms
networks:                          //네트워크 생성
  8am-net:
    driver: bridge
volumes:                          //볼륨 생성
  mysql_volume:
    driver: local
  8am_images:
    driver: local

health check를 하는 이유는 DB가 서버보다 먼저 가동되어 있어야 서버에서 연동을 할 수 있기 때문입니다.

application.yml에서 multipart file이 저장되는 location을 spring boot 컨테이너의 volume이 마운트된 위치인 /app/8am/images로 수정해주시기 바랍니다.



추가사항: 볼륨

도커 컨테이너 자체에 데이터를 저장할 수 있지만, 컨테이너를 삭제시킨다면 영속적이어야 하는 데이터 역시 함께 삭제됩니다. 이를 테면 데이터베이스를 이용하다가 새로운 컨테이너를 만들어 연결해야하는데, 기존에 저장되어있던 데이터를 전달할 수가 없게 됩니다.

이를 해결하기 위해 도커는 2가지 개념을 도입합니다. 바로 Docker VolumeBind Mount입니다.

Chat GPT의 두 개념에 대한 설명은 아래와 같습니다.

A Docker volume is a managed filesystem that can be used to store and share data between containers. Volumes are created and managed by Docker, and can be used to store data on the host or on a remote storage service. Volumes are typically used for storing data that needs to persist even after the container is removed or restarted.

A bind mount, on the other hand, is a file or directory on the host machine that is mounted into a Docker container. Bind mounts are a simple way to provide access to the host filesystem from within the container. Any changes made to the bind mount within the container are reflected on the host, and vice versa.

The main difference between a volume and a bind mount is that volumes are managed by Docker and can be used across different containers, while bind mounts are managed by the host and are specific to a single container. Additionally, volumes can provide some additional features such as data encryption and backup, while bind mounts provide direct access to the host filesystem without any abstraction.

Docker Volume은 도커에서 관리하는 파일시스템을 이용해 도커 볼륨을 호스트의 파일시스템과 연결하는 방식입니다. Bind Mount는 호스트의 파일시스템을 직접적으로 컨테이너와 연결하는 것입니다. 두 방식은 크게 차이가 없어보이지만 도커 볼륨은 여러 컨테이너가 공유하며 데이터 접근 시 암호화나 백업 기능을 지원하지만, 바인드 마운트는 하나의 컨테이너에 대해 매핑된 방식으로 직접적인 접근이 가능합니다.


Docker에서 권장하는 방법은 Docker Volume 방식입니다. 따라서 docker-compose 파일에서 볼륨을 생성하였습니다.





참고자료

가장 많은 참고

profile
Backend Engineer

0개의 댓글