일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- Python
- 스프링부트
- springboot
- 백엔드
- 개발자
- 도커
- docker
- Elasticsearch
- 캐시
- Java
- 파이썬
- 운영체제
- 프로그래머스 #카카오 #IT #코딩테스트
- C
- 쿠버네티스
- 자바
- 코딩테스트
- 스프링
- Linux
- programmers
- 알고리즘
- Spring
- IT
- 네트워크
- 엘라스틱서치
- Kakao
- DPDK
- 프로그래머스
- 리눅스
- 카카오
- Today
- Total
저고데
[Docker] Spring Boot와 MySQL을 도커로 배포해보자(feat. 시행착오) 본문
들어가며
caaai의 오픈베타 서비스 런칭이 얼마 남지 않은 요즘... CTO님께서는 생각보다 나를 엄하게 키우기를 원하신다.
"진철아. 서버도 너가 한 번 맡아볼래?"
이걸 나한테 시키시다니 ... DB 설계도 나한테 시키셨으면서 .. (CTO님께서 시애틀에 거주하셔서, 피드백을 주신다고 새벽 4시까지 일했던 기억이 ...)
무튼무튼 아무튼, 2주 후에는 다른 모바일 앱도 배포를 해야하는 터라, 어차피 해야하는 거 한꺼번에 할 수 있음에 참 다행이라고 생각한다.
따라서 최근에는 계속 도커만 공부하는 중인데, 오늘은 도커를 사용하여 Spring Boot와 MySQL을 배포하는 방법에 대해서 알아보도록 하자.
그리고 필자가 했던 치명적인 실수에 대해서도 간단히 말하도록 하겠다.
docker-compose 파일을 사용하느냐 안하느냐에 따라서 크게 두 가지 방법이 있는데, 우선 docker-compose 파일을 사용하지 않고 배포하는 방법에 대해서 알아보도록 하자. (docker-compose 파일까지 작성하기에는 분량이 너무 많아서 다음 시간에 계속하겠다.)
docker network 생성하기
우선 docker network에 대해서 알 필요가 있다.
이름에서도 느낌을 알 수 있듯이 도커 컨테이너 간의 통신을 관리하고 격리하기 위한 기능을 제공하는 녀석이다.
하나의 어플리케이션을 서비스한다고 하면, 여러 개의 컨테이너(Web, WAS, DB)로 구성될 수가 있다.
따라서, 컨테이너가 서로 통신하고 데이터를 주고 받아야 하는데 도커 네트워크는 컨테이너 간의 통신을 도와준다.
도커 네트워크는 아래의 명령어를 통해 생성할 수 있다.
docker network create [만들 네트워크 이름]
MySQL 컨테이너 만들기
다음으로 DB 컨테이너를 만들어보자.
DB 서버가 배포되어야, backend 서버도 작동할 수 있기 때문에 반드시 DB 서버를 먼저 열어주어야한다.
컨테이너를 생성하기 위해서는 가장 먼저 Dockerfile을 생성해야한다.
하지만, MySQL의 경우에는 따로 Dockerfile을 만들어줄 필요 없이, DockerHub에서 이미 만들어진 MySQL 이미지 파일을 다운받을 수 있기 때문에 필자는 이를 다운하였다. (그리고 이게 편함 ㅋㅋ)
docker pull mysql:8.0
이미지 파일을 다운받으면 docker 어플리케이션에서 이미지 파일이 잘 다운된 것을 확인할 수 있다.
MySQL을 사용해보신 분들이라면 아시겠지만, 워크 벤치에 접속하기 위해서는 username과 password가 필요하다. (이는 Spring Boot의 로컬 환경에서 DB를 연결할 때도 마찬가지이다.)
따라서 이미지 파일을 컨테이너화하기 위해서는 몇 가지 설정이 필요하다.
아래의 명령어를 통해 앞서 언급한 설정 내용을 지정하고 이미지 파일을 컨테이너화할 수 있다.
docker run --name <컨테이너명> -p <호스트 포트>:<컨테이너 내부 포트> --network <도커 네트워크> -e MYSQL_ROOT_PASSWORD=<패스워드> -e MYSQL_DATABASE=<DB명> -d mysql:8.0
컨테이너가 잘 실행된 것을 확인할 수 있다.
여기까지 잘 따라왔으면, DB 부분은 배포가 완료된 것이다.
로컬 환경에서 배포한 것이기 때문에 MySQL workbench에서도 접속이 가능한 것을 확인할 수가 있다. (개인적으로 접속이 성공했을 때, 돌고래가 웃는 모습이 너무 귀엽다고 생각한다.)
Spring Boot 컨테이너 만들기
1. application.properties 작성하기
Spring Boot에서 MySQL을 연결할 때의 방법을 기억하는가? (Hibernate를 직접 사용하는 방법 말고 ㅎㅎ 좀 더 편한거)
바로 application.properties 파일이다. (yml 파일도 상관 없다.)
Spring Boot에서는 application.xxx 파일을 통해 외부 프레임워크나 어플리케이션과 연결할 수 있다.
배포를 하지 않은 localhost에서는 아래와 같이 application.properties 파일을 작성했을 것이다.
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/spring?characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=사용자명
spring.datasource.password=비밀번호
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
하지만, 컨테이너를 통해서 배포를 할 경우에는 아래와 같이 환경 변수를 통해 값을 지정하는 것을 추천한다.
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=${SPRING_DATASOURCE_URL}
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
왜냐하면 설정을 더 유연하게 관리하고 컨테이너 재사용성을 높이기 위해서이다. (실제로 컨테이너 종류도 정말 많아서 헷갈릴 우려가 있다.)
2. Spring Boot Dockerfile 만들기
Spring Boot의 경우, MySQL처럼 따로 제공하는 이미지 파일이 없기 때문에 Dockerfile을 직접 생성해야한다.
Dockerfile은 일종의 커맨드(격투기 커맨드할 때 그거다)라고 생각하면 된다.
java 환경에서 jar 파일을 실행하려고 하면 "java -jar [jar 파일명]"이라고 실행하듯이 이를 한 번에 지정하는 것이다.
따라서 jar 파일이 필요하기에 gradle의 bootJar을 더블클릭하여 jar파일을 생성한다.
그리고 아래와 같이 Dockerfile을 생성한다.
FROM bellsoft/liberica-openjdk-alpine:17
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} demo.jar
ENTRYPOINT ["java", "-jar", "demo.jar"]
각 행을 하나하나 살펴보자.
FROM bellsoft/liberica-openjdk-alpine:17
FROM 지시어는 도커 이미지 빌드의 시작을 알리며, 이 이미지를 기반으로 새로운 이미지를 빌드하겠다는 것을 의미한다.
따라서, 이 행은 도커 이미지의 베이스 이미지를 지정하는 부분으로 필자의 경우에는 java 17을 사용하고 있기 때문에 BellSoft의 Liberica JDK 17을 사용한 Alpine Linux 이미지를 베이스 이미지로 사용하였다.
ARG JAR_FILE=build/libs/*.jar
ARG 지시어는 도커 빌드 시에 사용할 수 있는 변수를 정의한다.
앞서 생성한 .jar 파일이 build/libs 디렉토리에 생성되었기 때문에 해당 디렉토리 아래에 있는 모든 .jar 파일을 가리킨다.
따라서, 빌드 시 사용할 변수 JAR_FILE을 정의한다.
참고로 해당JAR_FILE 변수는 도커 빌드 시점에만 유효하며, 실행 시점에는 사용되지 않는다.
COPY ${JAR_FILE} demo.jar
COPY 지시어는 호스트 시스템의 파일을 도커 이미지의 파일 시스템으로 복사한다.
build/libs/ 디렉토리에서 ARG로 정의된 JAR_FILE 변수를 사용하여 해당 .jar 파일을 demo.jar로 컨테이너 내부에 복사한다.
ENTRYPOINT ["java", "-jar", "demo.jar"]
ENTRYPOINT 지시어는 컨테이너가 시작될 때 실행할 명령을 설정한다.
"java -jar demo.jar" 명령을 실행하여 복사된 demo.jar 파일을 실행하도록 설정하였다.
3. Dockerfile을 기반으로 이미지 파일 생성 후, 컨테이너화하기
간단하다. 아래의 명령어를 통해 Dockerfile을 이미지 파일로 build할 수 있다.
단, 명령어 마지막에 "." 점을 사용했기 때문에 해당 dockerfile이 상위 디렉토리에 위치해야하므로 주의하도록 하자.
docker build -t <원하는 이름> .
생성된 이미지 파일을 컨테이너화하기 위해서는 아래의 명령어를 사용하면 된다.
docker run -p <호스트 포트>:<컨테이너 내부 포트> --name <컨테이너명> --net <도커 네트워크명> -e SPRING_DATASOURCE_URL=<DB 서버 url>
-e SPRING_DATASOURCE_USERNAME=<user명> -e SPRING_DATASOURCE_PASSWORD={비밀번호} -d demo-test:0.0.1
중요한 점은 application.properties에서 설정한 환경 변수대로 '-e' 명령어를 통해 알맞게 배치해야만, DB 서버와 성공적으로 연결된다.
그리고 같은 네트워크에 배치할 것이기 때문에 네트워크는 동일하게 'springboot-mysql'로 지정한다.
시행착오
하하 물론 한 번만에 성공하지는 못했다.
여러 과정을 통해서 성공할 수 있었는데, 특히나 Spring Boot를 DB 서버와 연결하는 과정이 가장 힘들었던 것 같다.
그 이유는 바로 포트 연결을 이상하게 했기 때문이다.
도커 컨테이너를 띄울 때, "<호스트 포트 번호>:<컨테이너 내부 포트 번호>"로 포트 값을 설정하는데 이 과정에서 큰 착각을 했다.
바로, 컨테이너 간 통신을 할 때 호스트 포트를 통해 통신을 한다는 착각이였다.
더군다나, 필자의 경우 MySQL을 테스트용으로 이미 3306번으로 사용하고 있었기에 "33061:3306"으로 앞 뒤가 다르게 사용하였다.
따라서, 주구장창 33061 번호로 연결을 하니 ... 이게 될리가 ..
연결을 localhost로 하게 되면 이 역시 외부에서의 접근이기에 33061 -> 3306이라 원칙적으로 33061번 포트로 접근하는 것이 맞다.
하지만, 컨테이너 이름으로 접속 시도 시의 내부 포트로 바로 접근하게 되기에 3306번 포트로 접근을 해야한다.
명심하자. 컨테이너끼리의 연결은 컨테이너 내부 포트 번호로 한다는 것을!
(그리고 도움을 준 지표띠와 김인후 엔지니어님에게 무한한 감사를 ...)
'Docker' 카테고리의 다른 글
[Docker] Docker 네트워크의 작동 원리와 구조(feat. 신입 개발자가 회사 서버를 죽여버린 실화) (0) | 2024.10.16 |
---|---|
[Docker] 도커란? (0) | 2024.05.14 |