회사에서 하나의 사내서버로 모든 아키텍쳐(디비, 깃랩 러너, 기존 애플리케이션)을 옮겨달라는 요청을 받고 docker compose로 한번에 끝내려다가 문제가 발생했다.
먼저 docker-compose.yml을 확인해보자
version: '3.8'
services:
discovery:
image: gram14.mooo.com:5000/discovery:latest
container_name: discovery
ports:
- "8761:8761"
networks:
- ticketing-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8761/actuator/health"]
interval: 10s
timeout: 5s
retries: 5
mysql:
image: mysql:8.0
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: <password>
MYSQL_DATABASE: <db>
MYSQL_USER: <user>
MYSQL_PASSWORD: <password>
ports:
- "3306:3306"
networks:
- ticketing-network
healthcheck:
test: [ "CMD-SHELL", "mysqladmin ping -h localhost -u root --password=spacecl^^#" ]
interval: 10s
timeout: 5s
user:
image: gram14.mooo.com:5000/user:latest
container_name: user
ports:
- "8000:8000"
depends_on:
mysql:
condition: service_healthy
discovery:
condition: service_healthy
networks:
- ticketing-network
gateway:
image: gram14.mooo.com:5000/gateway:latest
container_name: gateway
ports:
- "8080:8080"
depends_on:
discovery:
condition: service_healthy
networks:
- ticketing-network
networks:
ticketing-network:
driver: bridge
이렇게 mysql을 docker-compose.yml로 내 컨테이너와 같이 올리고 내리면서 life cycle을 동기화 하려는 용도였다.
보기에는 문제가 없어 보였고 Chatgpt도 잘짜여진 코드라 했다.
그리고 user의 application.yml을 확인해 보자.
# JPA Settings
spring:
jpa:
hibernate:
ddl-auto: create
generate-ddl: true
show-sql: false
data:
redis:
lettuce:
pool.max-active: 10
pool.max-idle: 10
pool.min-idle: 2
host: redis
port: 6379
password: <password>
sessiontime: 3600000
sessionUseYn: Y
keyprefix: LOCAL
application:
name: user-service
# MySQL DB Connection Info
datasource:
hikari:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://mysql:3306/<db>?autoReconnect=true&useSSL=false&&serverTimezone=Asia/Seoul
username: <user>
password: <password>
maximum-pool-size: 10
# API Context Path
server:
port: 8000
servlet:
context-path: /api/v1
jwt:
header: Authorization
secret: UDVaY0VRREoyaEl2d2JlWXFFN2haOWF3ekRxbFBzTnpscEFFaE5mdDBSZw==sadjh1jhkljdakllksjvdsiohwejknsakijdjqw
# token-validity-in-seconds: 2592000
token-validity-in-seconds: 60000
# refresh-token-validity-in-seconds: 7776000
refresh-token-validity-in-seconds: 120000
app:
oauth2:
authorizedRedirectUris: http://ticketing.site/oauth/redirect
authorizedRedirectUrisLocal: http://localhost:3000/oauth/redirect
## Eureka Client
eureka:
instance:
instanceId: ${spring.application.name}:${spring.application.instance_id:${server.port}}
client:
eureka-server-port: 8761
register-with-eureka: true
fetch-registry: true
disable-delta: true
serviceUrl:
defaultZone: http://discovery:8761/eureka
management:
endpoints:
web:
exposure:
include: health,info,prometheus
metrics:
tags:
application: ${spring.application.name}
endpoint:
health:
show-details: always
prometheus:
enabled: true
springdoc:
swagger-ui:
config-url: /user-service/api/v1/v3/api-docs/swagger-config
url: /user-service/api/v1/v3/api-docs
원래 docker compose에서는 기본적으로 yml파일 안의 컨테이너들은 같은 네트워크로 묶여있어서 서로의 컨테이너를 참조할때는 http://discovery:8761/eureka 이런식으로 컨테이너 이름(별칭)으로 참조가 가능하도록 기본설정이 돼있다.
그래서 의심의 여지 없이 디비도
jdbc:mysql://mysql:3306/<db>?autoReconnect=true&useSSL=false&&serverTimezone=Asia/Seoul
이렇게 연결했건만 Database에 연결이 되지 않았다.
이유는 바로 application.yml 에서 다른 도커 네트워크 컨테이너와는 달리 데이터베이스 설정를 할때는 로직이 다르다는것을 알게 되었다.(생각지도 못한것 때문에 오류를 잡는데 오랜 시간이 걸렸다.)
만약 별칭으로 디비를 연결하고 싶다면 도커 컴포즈에서 빌드하는 단계도 추가해줘야 한다.
아래는 예시이고 차이를 주목하도록 하자.
version: '3.8'
services:
mysql-db:
image: mysql:8.0
container_name: mysql-container
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: inventory
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- "3306:3306"
networks:
- springmysql
spring-app:
build:
context: .
dockerfile: Dockerfile
container_name: spring-container
ports:
- "8082:8080"
depends_on:
- mysql-db
environment:
MYSQL_URL: jdbc:mysql://mysql-container:3306/inventory?autoReconnect=true&useSSL=false
MYSQL_USERNAME: root
MYSQL_PASSWORD: root
deploy:
restart_policy:
condition: on-failure
max_attempts: 10
networks:
- springmysql
networks:
springmysql:
name: springmysql
만약 별칭으로 디비 주소를 연결하고 싶다면 docker-compose에서 빌드 작업까지 해줘야했다.
나는 CI-CD 자동화를 위해 도커 컴포즈를 미리 빌드한 이미지를 조립하는 방식을 써야 했기에 디비를 아예 도커 컴포즈에서 제외하고 터미널에서 도커로 따로 실행해 주었고 내부 IP 주소로 application.yml에 설정을 해줘서 디비 연결에 성공했다.
이때 주의할것은 내부에 디비일지라도 application.yml에서 localhost:3306 으로 하면 안되고 <내부-ip-주소>:3306 으로해야한다는것이다. 도커가 참 편리하긴 한데 네트워크 같은 부분으로 들어갈수록 디테일이나 헷갈리는 부분이 많기 때문에 주의해야한다.
꼭 기억하자 도커는 같은 컴퓨터 내에 있더라도 격리된 또다른 컴퓨터에 있는것처럼 동작한다는 것을