본문 바로가기

카테고리 없음

docker compose와 database를 같이 쓸때 주의할점

회사에서 하나의 사내서버로 모든 아키텍쳐(디비, 깃랩 러너, 기존 애플리케이션)을 옮겨달라는 요청을 받고 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&amp&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&amp&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 으로해야한다는것이다. 도커가 참 편리하긴 한데 네트워크 같은 부분으로 들어갈수록 디테일이나 헷갈리는 부분이 많기 때문에 주의해야한다.

꼭 기억하자 도커는 같은 컴퓨터 내에 있더라도 격리된 또다른 컴퓨터에 있는것처럼 동작한다는 것을