Docker 에서 /docker-entrypoint-initdb.d 를 통한 초기화 시 인코딩 오류 문제 해결법
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 로 설정되었고, 데이터도 깨지지 않고 잘 입력된 것을 확인할 수 있다.