Spring Boot와 mySQL을 도커에 올리고, 다른 host machine에서도 사용할 수 있도록 도커 이미지를 Hub에 올리도록 하겠습니다.
마지막으로는 여러 도커 컨테이너를 한번의 명령으로 실행할 수 있도록 Docker Compose파일도 생성하겠습니다.
해당 내용은 여러 구글링을 통해 진행된 과정이기 때문에 정석적인 방법은 아닐 수 있습니다.
mySQL과 Spring Boot 서버를 도커 상에서 연결하기 위해서는 서버와 DB 컨테이너가 네트워크를 통해 통신을 해야합니다.
ex) docker network create 8am-net
도커 네트워크를 통해 여러 컨테이너를 그룹화하여 사용할 수 있습니다.
mySQL 이미지를 생성할 수 있는 방법은 크게 2가지가 있습니다.
첫번째 방법은 비교적 간단합니다. 도커 데스크탑에서 mySQL 이미지를 검색하여 pull해오면 됩니다.
터미널에 docker pull mysql:[원하는 버전] 를 입력해도 됩니다.
ex) docker pull mysql:8.0.32
두번째 방법은 기존의 mySQL 컨테이너가 존재했다면 해당 컨테이너의 이미지를 받아오는 방법입니다.
터미널에 docker commit [컨테이너명] [생성할 이미지명]를 입력하면 됩니다.
ex) docker commit 8amDB mysql
진행이 완료되면 docker desktop에 mySQL 이미지가 생성된 것을 확인하실 수 있습니다.
mySQL 버전확인은 아래의 방법으로 진행할 수 있습니다.
1. MySQL Command Line Client 터미널 실행
2. 비밀번호 입력
3.SELECT VERSION();
쿼리문 실행
4. 버전 확인(8.0.32)
DB를 이미지로 만들었으니, 이제는 서버를 이미지로 변환할 차례입니다. jar 파일을 Dockerfile을 통해 도커 이미지로 생성할 예정입니다.
Dockerfile이란 이름의 파일을 프로젝트폴더에 생성
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
- 컨테이너 시작 시 스크립트 실행
Gradle Clean 실행
build.gradle에 아래의 코드 작성 후 gradle 업데이트
jar {
enabled = false
}
Gradle bootJar 실행
docker build -t [이미지명] . 명령으로 이미지 생성
ex) docker build -t 8am_server .
마지막의 마침표를 꼭 찍어주어야 합니다. Argument를 지정하는 부분이기 때문입니다.
필요한 도커 이미지를 모두 생성하였습니다. 이제 이 이미지들을 공유하여 다른 호스트에서도 같은 이미지를 사용할 수 있도록 하겠습니다.
ex) 8am_backend_server
ex) docker tag 8am_server:1.0 tank3a/8am_backend_server:1.0
ex) docker login
ex) docker push tank3a/8am_backend_server:1.0
태그는 버전 번호로 생각하시면 됩니다. 아무것도 지정하지 않으면 latest로 자동으로 지정됩니다.
이미지들을 모두 완성했으면, 컨테이너를 생성하고 이를 네트워크에 연결해야 합니다. 하지만 이 과정을 모두 커맨드창에 쳐야하는데 여간 불편한 일이 아닙니다. 예시로 위에서 생성한 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.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 Volume
과 Bind 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 파일에서 볼륨을 생성하였습니다.
가장 많은 참고
- https://velog.io/@dhk22/Springboot-MySQL-Docker-%EB%8F%84%EC%BB%A4%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%84%B1
- https://da2uns2.tistory.com/entry/Docker-%EB%8F%84%EC%BB%A4%EC%97%90-Spring-Boot-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0
그 외- https://www.inflearn.com/questions/736029/only-one-usage-of-each-socket-address-protocol-network-address-port-is-normall
- https://immose93.tistory.com/20
- https://dasima.xyz/mysql-show-databases/
- https://lifere.tistory.com/entry/MySQL-%EB%B2%84%EC%A0%84-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0-MySQL-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95
- https://velog.io/@jkjan/Docker-MySQL-%EC%9B%90%EA%B2%A9-%EC%A0%91%EC%86%8D
- https://velog.io/@ssssujini99/Docker-docker-compose-%ED%8C%8C%EC%9D%BC-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%9D%84%EC%9A%B0%EA%B8%B0
- https://www.baeldung.com/dockerizing-spring-boot-application
- https://velog.io/@wo_ogie/Docker-Spring-Boot-MySQL-Spring%EA%B3%BC-MySQL%EC%9D%B4-%EC%97%B0%EA%B2%B0%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AC%B8%EC%A0%9C-Communications-link-failure
- https://hongdroid.tistory.com/36
- https://cocook.tistory.com/65
- https://reddb.tistory.com/183
- https://lifere.tistory.com/entry/%EB%8F%84%EC%BB%A4Docker-%EB%8F%84%EC%BB%A4-%ED%97%88%EB%B8%8C%EC%97%90-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%98%AC%EB%A6%AC%EA%B8%B0-Docker-Hub%EC%97%90-Push
- https://sightstudio.tistory.com/32
- https://beer1.tistory.com/15