개발환경 : Vue, spring, mysql, docker compose, redis
사건발생
프로젝트를 진행하다가 만난 녀석.
이 녀석이 발생하는 원인은 여러 가지라는데 실제로 이 문제를 해결해가는 와중에 여러 원인이 있었음을 발견했다. 시간도 참 오래 걸렸다.
위에서 작성했다시피 개발환경에서 spring 프로젝트를 dockerize해서 개발 서버에서 mysql, redis 이미지를 pull해와 docker-compose를 활용해 다중 컨테이너를 동시 관리하기로 했다.
그런데 계속 db 컨테이너와 spring 컨테이너에서 문제가 발생했다.
로그를 확인해보니 Caused by 문구에서 시작하는 각 로그가 결정적인 요인이었다.
대부분 'docker-compose.yml 내에서 오타가 있거나 활용을 잘못했거나'다!
그런데 마지막에 해결한 부분은 생각지도 못한 부분이라 꼭 글로 남겨야겠다.
해결
caused by: java.net.connectexception: connection refused
맨 마지막 caused by 로그는 connectexeception이다.
mysql에서 spring 컨테이너의 접속을 거절했다는 것인데, restart가 몇 번이나 계속 돼도 결과는 같았다.
그런데 왜 connectexeception이 발생하는지 이해할 수 없었다. 다음 docker-compose.yml 파일을 보자.
version: '3.8'
services:
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: 루트 계정 비밀번호
MYSQL_DATABASE: 데이터베이스명
volumes:
- ./db/mysql/data:/var/lib/mysql
ports:
- '3306:3306'
networks:
- app-network
restart: always
redis:
container_name: redis
image: redis:latest
ports:
- '6379:6379'
networks:
- app-network
frontapp:
container_name: front
image: ekwls20/viewgorithm
ports:
- '80:80'
- '443:443'
networks:
- app-network
restart: always
backapp:
container_name: back
image: backend
ports:
- '8088:8088'
environment:
DB_PASSWORD:
DB_USER:
DB_URL: jdbc:mysql://mysql/데이터베이스 테이블 명
REDIS_HOST: redis
networks:
- app-network
depends_on: <--이 속성을 주목
- mysql
- redis
restart: always
networks:
app-network:
driver: bridge
back 컨테이너의 depends_on 속성을 자세히 보자.
depends_on:
- mysql
- redis
depends_on은 아래 명시된 컨테이너가 시작되고 난 후 해당 컨테이너가 시작되는 것을 보장해준다. 다시 말해, "MySQL 컨테이너와 REDIS 컨테이너가 실행되고 나서야 back 컨테이너가 실행된다"는 말이다.
좋다. 그럼 아무 문제 없을 것이다. 그렇게 docker-compose up을 했지만, 결과는 connectexeception이었다.
확신에 차서 실행했는데 또 만나버렸다. 그런데 로그를 확인해보니 MySQL 컨테이너는 잘 돌아간다..? 분명 실행되는데 연결만 안되는 것이다. 에러 로그조차 없는 게 가장 난감하다. 그래서 구글링을 하면서 해당 문제와 관련된 글을 찾아나섰다.
https://stackoverflow.com/questions/54145220/spring-boot-docker-compose-mysql-connection-refused
원인은 간단하다. 컨테이너만 실행됐지 프로그램은 준비되지 않았다는 것이다.
즉, 위 depends_on 속성은 컨테이너가 실행되는 것만 보장하지 프로그램이 준비되는 것은 보장하지 않는다.
밥이 준비되지 않았는데 밥을 달라고 요구하는 것과 같은 상황이었던 것이다. MySQL 컨테이너는 실행만 됐지 MySQL 프로그램 자체는 준비되지 않았는데 이미 내장톰캣까지 실행된 Spring 어플리케이션이 MySQL 컨테이너에 데이터베이스 접속을 요구하니 당연히 거절당할 수밖에 없었던 것이다..
지금껏 컨테이너 시작=프로그램 시작으로 알고 있었다. docker에 대한 이해가 부족했기에 생긴 문제였다.
해결방안은 아래와 같이 docker-compose.yml 파일의 mysql 컨테이너 부분에 healthcheck 속성을 추가하는 것이다.
mysql:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: 루트 계정 비밀번호
MYSQL_DATABASE: 데이터베이스명
volumes:
- ./db/mysql/data:/var/lib/mysql
ports:
- '3306:3306'
networks:
- app-network
restart: always
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 30s
retries: 10
결과는 위와 같다.
'Projects > Resolve Problems' 카테고리의 다른 글
UnSupportedException (0) | 2024.09.06 |
---|---|
Uncaught Error: [🍍]: "getActivePinia()" was called but there was no active Pinia. (0) | 2024.07.05 |
FileNotFoundException (0) | 2024.05.17 |
NonUniqueResultException (1) | 2024.03.26 |
504 Gateway time out (0) | 2024.03.14 |