전 글에서 언급했듯이 프로젝트를 Docker container 화 시키는 과정과 그 과정에서 겪었던 여러 이슈들을 기록해둔다.
docker는 간단히 말하면 컨테이너 방식을 통해서 프로젝트(애플리케이션)을 구축하고, 배포할 수 있게 해주는 플랫폼이라고 할 수 있다. Docker의 경우 여러 공식 문제나 잘 설명되어 있는 글들이 많다.
https://www.docker.com/resources/what-container/
https://aws.amazon.com/ko/docker/
그 중에서도 하나 가장 강력한 무기라고 생각되는 점은 docker container가 라이브러리, 코드 등 애플리케이션에 포함된 모든 것들을 포함하여 환경에 구애받지 않고 항상 동일한 애플리케이션을 돌릴 수 있다는 것이다.
이 작업을 하기전에 먼저 컴퓨터(노트북)에 Docker를 깔아주자.
https://www.docker.com/products/docker-desktop/
desktop용으로 무료로 받을 수 있기 때문에 이를 먼저 설치해주자.
우선 기존의 application.properties를 application.yml 파일로 수정해주면서 여러 설정 값들도 추가해준다.
spring:
datasource:
url: jdbc:mysql://db-mysql:3306/mydb?useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.cj.jdbc.Driver
username: spring
password: spring
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
profiles:
include: oauth
server:
servlet:
encoding:
charset: UTF-8
force-response: 'true'
간단히 설명을 넣는다.
spring:datasource: -> DB에 대한 설정이 나온다. url에서 "db-mysql"은 db conatiner 이름이며, mydb가 database 이름이다. 한글 사용을 위해 unicode, encoding 관련 값을 넣어주었다.
jpa:database-platform: -> 후에 작업을 하다 hibernate.dialect 설정이 되어있지 않다는 error를 보고 추가해주었다. 나는 mysql8.0을 쓰기에 해당 버전에 맞는 dialect를 설정해주었다.
jpa:hibernate:ddl-auto: -> 나는 우선 프로젝트 개발 진행중이기 때문에 update로 둔다(이 옵션은 DB schema 변환이 있을 때 어떻게 처리하는가이다.)
jpa:show-sql -> sql log가 보이도록 해준다
jpa:properties:hiberante:format_sql -> sql이 이쁘게 보이게 해준다.
profiles:include: oauth -> 이는 oauth용으로 만들어둔 properties 파일을 읽는 용도임으로 그렇게 중요하지는 않다.
사실 docker file 자체는 아직 별게 없다.
FROM openjdk:17-alpine
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
이렇게까지막 적어도 별 어려움 없이 dockerfile 작성이 끝이난 것이다.
우선 본 프로젝트에서는 mysql과 spring 코드 두 개를 container로 올려야 한다. 먼저 mysql을 해보자
무엇인가가 안된다면 Docker가 켜져있는지 우선 확인해보자 ㅎㅎ
docker 에서 container들 끼리 서로 통신을 할 때는 Docker network를 통해 수행한다. 아직 자세히는 알지 못하지만, 나는 DB와 프로젝트가 통신을 해야 하므로 이를 추가해주자
docker network create how-was-your-day-net
우선 mysql 이미지를 받아오자.
docker pull mysql:8.0
그 후 셋팅을 하며 container를 생성하고 실행해보자
docker run -p 3306:3306 --name db-mysql --network how-was-your-day-net -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=mydb -e MYSQL_USER=spring -e MYSQL_PASSWORD=spring -d mysql:8.0
관련 설명은 아래와 같다.
실행 파일을 먼저 만들어줘야 한다. 그 이전에 build 했던 결과물들이 있을 수 있기에 우선 이를 지우고 새로 만들어주자
## 여기서 그냥 gradle을 사용해도 되긴 한다.
./gradlew clean
## assemble은 실행 없이 build 파일 작성
./gradlew asseble
그리고서 docker image를 만들어주자
docker build -t how-was-your-day:1.0 .
이미지를 만들었으면 이를 이용해서 이제 container로 올려주면 된다.
docker run -p 8080:8080 --name how-was-your-day --network how-was-your-day-net -d how-was-your-day:1.0
우선 terminal에서 별다른 문제 없이 돌아갔다면 docker desktop 앱을 들어가보자. 아래와 같이 images와 containers 탭에서 정상적으로 돌아가고 있다면 성공이다.
하지만 나도 이 과정을 처음부터 성공해내지는 못했다. 여러 힘든 점을 겪었기에 이를 처리했던 방식들을 짧게나마 남겨둔다.
처음에 mysql container를 올리려고 할 때 마주할 수 있는 에러이다. 나는 서버를 킨 적이 없는데 3306 포트를 누가 점유하고 있다~ 라고 나온다. 이럴 땐 한번 포트 점유 체크를 해주자.
cmd를 켜서 netstat -ano
를 쳐주면 현재 사용중인 포트들이 나온다. 그 중에서 3306 포트가 사용중인지를 확인하고 그 pid를 체크하자. 그리고는 taskkill /f /pid [확인한pid]
를 해주면 된다. 이 명령이 오류가 난다면 작업관리자를 들어가서 이름에서 우클릭을 해서 pid를 체크해 pid가 보이게 하고, 해당 pid를 직접 찾아서 종료해주면 된다.
container 두 개가 다 켜졌고, localhost:8080에도 잘 접속이 되었다고 치자. 근데 왠걸 db와 통신하는 작업을 하자마자 에러 로그가 파파박 찍힌다. 근데 로그를 확인하니 아래와 같이
java.sql.SQLException: Incorrect string value: '\xEA\xB3\xA0\xED\x83\x9C...' for column 'name' at row 1
한글이 깨진 것과 같은 표현이 뜰 때가 있다. 이때는 mysql에 설정을 바꿔주어야 한다.
이럼에도 안되는 경우가 있을 것이다. 나도 그러했다.
그렇다면 다음으로 넘어간다.
우선 mysql을 접속해서 show variables like 'c%'
명령을 친다. 그러면 아래로 변수들이 촤라라 펼쳐질텐데, character_set 관련 값들을 보면 아마 utf8, utf8mb3 등이 아닌 latin1과 같은 값들로 설정되어 있을 것이다.
실행창에서 서비스를 검색하고 들어가 mysql 을 찾아 중지해준다.
utf8 설정을 위해서 /ProgramData/MySQL/MySQL Server 8.0에 있는 my.ini 파일을 수정해야 한다.
만약 수정권한이 없다면 파일의 속성/보안/편집을 가서 user에게 권한을 부여하기를 바란다.
my.ini를 키고 아래와 같은 값들을 넣는다. (여기서 []로 되어 있는 것들은 아마 미리 입력되어 있을 것이기에 그 아래 적힌 코드를 해당 위치에 추가해주면된다.)
[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
character-set-client-handshake = FALSE
init_connect="SET collation_connection = utf8_general_ci"
init_connect="SET NAMES utf8"
character-set-server = utf8
my.ini 설정을 마쳤다면 다시 서비스로 돌아가 mysql을 실행해주자. 이때 뭔가 오류가 뜬다면 아마 my.ini 파일에 오타가 있을 가능성이 크니 다시 확인해보자.
mysql이 성공적으로 켜졌다면 다시 mysql을 접속해서 다시 show variables like 'c%'
명령을 친다. 아래와 같이 utf8이 잘 설정되었다면 성공이다.
이는 가장 귀찮게 했던 문제인데 해결법은 단순무식했다. ini 파일을 건들다 뭔가를 잘못건드렸는지 정확한 이유는 모르겠지만 mysql root 계정으로 로그인이 안되기 시작했다. 에러 로그는 아래와 같다.
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
이는 여러 시도를 많이 해봤는데... 결국 선택한 선택지는 mysql server만 지웠다가 다시 까는거였다. 내가 버린 한시간이 아까우니 안된다 싶으면 지웠다 까는걸 추천한다.
trouble shooting 때문에 글이 길어진 감이 없지 않지만 이렇게 dockerize를 성공적으로 끝마쳤다. 이제는 docker container를 켜고 끄는 식으로 쉽게 서버를 킬 수 있다. 다음으로는 docker를 좀 더 쉽게 사용하는 docker-compose를 적용해보겠다.