개발

Docker 에서 /docker-entrypoint-initdb.d 를 통한 초기화 시 인코딩 오류 문제 해결법

모달조아 2023. 5. 1. 05:48
version: '3'
services:
  db:
    image: mysql:8.0.32
    ports:
      - "3306:3306"
    env_file:
      - ../env/docker-compose.env
    container_name: seat-view
    environment:
      TZ: Asia/Seoul
    volumes:
      - seat-view:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      - ./stadium.sql:/docker-entrypoint-initdb.d/stadium.sql
    restart: always

volumes:
  seat-view:

위와 같이 docker-compose 를 이용하여 MySQL 컨테이너를 생성하고 실행하면서 테이블과 초기 데이터들을 넣어주려고 하였다. init.sql 은 초기 테이블 DDL 에 관한 것이고, stadium.sql 은 초기 데이터 DML 에 관한 것이다.

 

그런데 위와 같이 한글로 입력된 컬럼들이 다 깨져서 입력되길래 데이터베이스를 관리하는 MySQL 서버의 인코딩 문제라고 생각하여서 아래와 같이 docker-compose 에 설정을 추가해주었다.

    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
  • --character-set-server=utf8mb4
    • MySQL 서버가 사용하는 캐릭터셋을 utf8mb4로 설정하는 것이다. utf8mb4 캐릭터셋은 이모지와 같은 다양한 언어의 문자를 처리할 수 있다. utf8mb4 이 utf-8 과 어떤 것이 다른지 등과 같은 정보는 아래의 글을 참고하자.
    • [링크]
  • --collation-server=utf8mb4_unicode_ci
    • MySQL 서버가 사용하는 콜레이션을 utf8mb4_unicode_ci 로 설정하는 것이다. 콜레이션은 문자열의 비교와 정렬에 사용되는 규칙을 정의한다. utf8mb4_unicode_ci 콜레이션은 대소문자를 구분하지 않으며, 다양한 언어의 문자를 각 언어의 특성에 따라 비교, 정렬하는 것을 지원한다.

서버의 캐릭터 셋을 설정해주었기에 이제 한글이 안깨질 것이라고 생각했지만, 여전히 깨져서 입력되었다.

 

stadium.sql 이 도커 볼륨으로 마운트될 때, 깨져서 전달이 되나 해서 확인해보았다.

docker exec -it seat-view bash
cd /docker-entrypoint-init.db.d
cat stadium.sql

깨지지 않고 잘 전달되는 것을 볼 수 있다.

 

MySQL 서버는 utf8mb4 로 설정되어 로컬 PC 에서 직접 INSERT 쿼리를 작성할 때에는 한글이 깨지지 않고 잘 조회되었다. 그리고, 전달된 stadium.sql 은 깨지지 않았으므로 stadium.sql 문제도 아니다. /docker-entrypoint-initdb.d 를 통해서 데이터를 입력할 때만 한글이 깨지는 상황이다. 그래서 도커 컨테이너 내부에서 sql 을 실행하는 MySQL 클라이언트의 인코딩 설정 문제라고 추측하였고 확인해보았다.

docker exec -it seat-view mysql -u root -p

 위 명령어 실행 후 docker-compose 에서 미리 설정해준 비밀번호를 입력 후,

SHOW VARIABLES LIKE 'character_set_%';

도커 컨테이너에서 실행 중인 MySQL 의 캐릭터 셋과 관련 정보를 조회했다.

실제로, 도커에서 실행 중인 MySQL 클라이언트의 캐릭터 셋은 utf8mb4 가 아니라 latin1 인 것을 확인할 수 있다.

 

이를 바꿔주기 위해 my.cnf 를 아래와 같이 작성해주고, 이를 /etc/mysql/my.cnf 로 마운트해주었다.

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4

[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
version: '3'
services:
  db:
    image: mysql:8.0.32
    ports:
      - "3306:3306"
    env_file:
      - ../env/docker-compose.env
    container_name: seat-view
    environment:
      TZ: Asia/Seoul
    volumes:
      - seat-view:/var/lib/mysql
      - ./my.cnf:/etc/mysql/my.cnf
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
      - ./stadium.sql:/docker-entrypoint-initdb.d/stadium.sql
    command: bash -c "chmod 644 /etc/mysql/my.cnf && docker-entrypoint.sh mysqld"
    restart: always

volumes:
  seat-view:

 

Warning : World-writable config file '/etc/mysql/my.cnf/' is ignored 이런 에러가 발생하면서 my.cnf 를 못읽는 에러가 발생할 수 있는데, World-writable (모든 사용자가 쓰기 권한을 가진 파일) 로 설정되어 있기 때문에 발생한다. MySQL은 보안상의 이유로 World-writable 파일을 무시한다.

소유자에게 읽기와 쓰기 권한을 주고, 그룹과 다른 사용자에게는 읽기 권한만을 부여하는 644 권한을 주자.

chmod 644 /etc/mysql/my.cnf/

컨테이너가 삭제되고 다시 새롭게 생성되거나, 다른 PC 에서 docker-compose 를 실행할 때도 644 권한을 자동으로 주는 것이 편하다. 644 권한을 준 후에 mysql 컨테이너를 생성하도록 하는 명령어를 docker-compose 내에 적어주었다.

command: bash -c "chmod 644 /etc/mysql/my.cnf && docker-entrypoint.sh mysqld"

 

클라이언트의 캐릭터 셋이 utf8mb4 로 설정되었고, 데이터도 깨지지 않고 잘 입력된 것을 확인할 수 있다.