문제
신년을 맞아 처음으로 올리는 일간 에러. 토요일 아침부터 참으로 통곡이 절로 나는 에러를 메시지를 맞이해 정말 기쁘다.
문제 상황은 간단한데, 기존에 토이프로젝트로 운영하던 서버의 데이터베이스를 복제하는 과정에서 위의 에러가 발생했다. 지금부터 에러가 발생하게 된 경위를 간단하게 브리핑 해 보겠다.
상황
기존에 토이프로젝트로 진행하던 프로젝트의 데이터베이스가, 운영서버의 DB를 개발환경에서도 바로 붙어서 테스트 및 반영까지 하던 상황. 지금까지는 개발서버나 운영서버나 어차피 개발환경이라는게 마찬가지였지만 모두 가족들이긴 해도 활발이 사용해주는 사용자들도 있고 거기에 사용자들이 사용하며 쌓은 데이터베이스를 나도 소중히 다루어야한다는 책임감이 생기며 개발환경과의 분리가 필요하다고 생각이 듬.
처음에 DB 서버를 실행 할 때 부터 볼륨을 걸었어야 되는데, 그 땐 이 데이터베이스 컨테이너가 테스트를 넘어 운영에서까지 쓰일꺼라고 생각을 못했었음.
docker inspect 컨테이너명
docker inspect
명령으로 Docker가 알아서 생성한 볼륨을 확인하니, 위와 같이 "/var/lib/docker/volumes/47a16e90aaaddc239c082d3ecfa090801fc5c451dd88da1d61f51ddfbfc5bf5b/_data
경로에 생성이 되어 있음.
해당 경로로 찾아가서 파일들을 확인 해 보니, 이 폴더를 쓰면 되겠다고 생각이 든다.
cd /var/lib/docker/volumes/47a16e90aaaddc239c082d3ecfa090801fc5c451dd88da1d61f51ddfbfc5bf5b/_data
해당 폴더 복사는 컨테이너를 멈추고 위의 경로를 통쨰로 카피 하거나, 아니면 아래의 명령어를 이용 하면 멈춰있는 컨테이너에서도 파일 복사가 가능 하다.
docker container cp dutypark-db:/var/lib/mysql ./data
우분투 서버에서 저렇게 챙겨온 폴더를, 내 로컬 환경(Mac) 에서 scp로 그대로 다시 떠갔다.
이제 위에 보이는 것 처럼 /Users/shane/Documents/dev/_dutypark_dev_db
경로에 볼륨설정할 폴더가 존재하는 상태.
이제 컨테이너를 실행 한다.
docker run -d --name dutypark_dev_db \
--restart unless-stopped \
-v /Users/shane/Documents/dev/_dutypark_dev_db:/var/lib/mysql \
-p 3306:3306 \
mysql
그런데 예상과 달리 컨테이너가 바로 죽어버린다.
로그를 확인 해 보자
docker logs -f dutypark_dev_db
바로 죽었는데 아래의 로그가 핵심으로 보인다
2023-01-07T02:31:25.105383Z 1 [ERROR] [MY-011087] [Server] Different lower_case_table_names settings for server ('2') and data dictionary ('0').
지금까지가 문제 상황이다.
원인
분명 볼륨을 통째로 가져오기 때문에 환경설정도 기존의 컨테이너와 다를게 없는데 설정에서 에러가 난다는게 참 이해가 되지 않는 상황이지만 어쨌든 우분투에서 Mac으로 환경이 변경된 건 사실이다.
그렇다면 제일 먼저 공식문서에서 lower_case_table_names
관한 내용을 체크 해 본다.
https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_lower_case_table_names
가장 먼저, 문서에 따르면 lower_case_table_names
값은
- 0으로 설정되어 있다면, 테이블 이름은 대/소문자를 구분(case-sensitive) 한다고 되어 있다.
- 1로 설정되어 있다면 테이블명은 디스크에 소문자로 저장되지만 비교는 대소문자 구분을 하지 않는다고 한다.
- 2로 설정되어 있다면 테이블명은 주어진대로 저장이 되지만, 소문자로 구분된다고 한다.
무엇보다 눈에 띄는 대목은 아래에 있다.
윈도우나 MacOS 처럼 대소문자 구분을 못하는 파일 시스템에서는 해당 설정이 0이 될 수 없다고 한다.
기본 값 또한 Unix 시스템은 0 이며 Windows 는 1, 그리고 MacOS 는 2로 설정 되어 있다. 이제 에러 메시지로 돌아와서
Different lower_case_table_names settings for server ('2') and data dictionary ('0').
이제 에러 메시지가 눈에 들어오기 시작하지 않는가? 데이터는 0으로(Linux) 설정되어 있는데 지금 설정이 2로(MacOS) 되어 있다고 한다.
해결
1차 시도: 실패
이제 문제도 파악했고, 원인도 찾아내었으니 해결을 해 보자.
일단 lower_case_table_names 를 강제로 0 으로 부여 해 보겠다.
docker run -d --name dutypark_dev_db \
-v /Users/shane/Documents/dev/_dutypark_dev_db:/var/lib/mysql \
-p 3306:3306 \
mysql \
mysqld --lower_case_table_names=0
해결이 되었는지 확인 해 보자.
lower_case_table_names를 0으로 설정 하셨는데요, 파일 시스템이 영 아니시네요? 안돼. 돌아가. 바꿔줄 생각 없어.
안된다.
2차 시도: 절반의 성공
이번에는 구글링을 통해 해결 방안을 찾아 보았다. 요즘엔 구글보다 ChatGPT에 먼저 물어보는 경우가 많은데, ChatGPT의 경우에는 이런 예외적인 상황에 대해서는 아직은 약한 모습을 보인다. 다행히 StackOverflow에서 비슷한 문제를 겪은 사람들이 많았는데 대다수의 경우에는 Docker Desktop 2.3 버전을 사용할 때는 괜찮다가 Docker Desktop 2.4 버전으로 업데이트 한 이후 부터 이런 문제를 겪었다고 한다.
그리고 Docker Desktop 2.4 버전은 gRPC FUSE가 file sharing으로 추가된 버전이기도 하다. 이를 위해 도커 데스크탑 버전을 낮춰 사용하기도 했다지만 이미 Docker Desktop은 4.15.0 버전 까지 나왔는데 그러기엔 많이 왔다.
Preferences -> General 에 가서 Choose file sharing implementation 을 osxfs(Legacy)로 변경 해 주자.
이후 Apply & restart 를 해서 재시작을 한 뒤 다시 컨테이너를 실행 하면
docker run -d --name dutypark_dev_db \
--restart unless-stopped \
-v /Users/shane/Documents/dev/_dutypark_dev_db:/var/lib/mysql \
-p 3306:3306 \
mysql
정상적으로 뜬다.
Tips
만약 변경 후 아래와 같은 오류가 나올 경우
2023-01-07 12:30:31 chown: changing ownership of '/var/lib/mysql/mysql.sock': No such file or directory
이는 불필요한 심볼릭 링크인 mysql.sock
까지 복사가 되어기 때문인데, 아래와 같이 확인을 해 보면
mysql.sock 파일은 필요가 없다. 볼륨을 걸 폴더에서 mysql.sock
파일을 제거하면 문제가 해결된다.
3차시도: 깔끔한 성공
이걸로 모든 해결이 끝났다고 생각 할 수 있지만, 어쨌든 기본 설정을 변경하는건 찝찝함이 남아있다. 그리고 심지어 VirtioFS 를 쓰면 마운트를 바인딩하는데 퍼포먼스 향상 효과가 있다고까지 한다. DB를 덤프해서 새로운 볼륨 폴더를 생성 해 보자.
mysqldump
이를 위해 mysqldump를 사용할 것이다.
Docker 컨테이너로 진입한다.
docker exec -it dutypark_dev_db bash
mysqldump 사용법은 아래와 같다.
mysqldump -u {사용자 계정} -p {원본 데이터베이스명} > {생성할 백업 데이터베이스명}.sql
# 예시
mysqldump -u root -p dutypark > dutypark.sql
처음에는 DB에 접속하는 계정으로 시도했는데, 권한이 없다하여 root 계정으로 덤프 했다.
dutypark.sql 파일이 생성된 것이 확인 됨.
dutypark.sql 파일을 로컬로 가져온다.
docker container cp dutypark_dev_db:/dutypark.sql .
정상적으로 덤프가 끝났으니 이제 방금 컨테이너는 미련 없이 제거 해 준다.
docker stop dutypark_dev_db
docker rm dutypark_dev_db
이번에는 설정을 VirtioFS 로 해보았다. gRPC FUSE로 설정해도 되지만 얼마전부터 도커 가상화 머신이 qemu 대신 Virtualization.Framework 로 변경 되면서 궁금하기도 했었기 때문에 VirtioFS 를 테스트 해 보기로 했다. 다시 Apply & Restart를 하고
볼륨을 걸 폴더에 있는 모든 파일을 삭제한다. 그러면 이제 mysql이 실행 될 때 초기화가 진행 될 것이다.
rm -rf _dutypark_dev_db/*
# 이후 다 삭제된 것 확인
ls -al _dutypark_dev_db
rm -rf 명령을 할 때는 항상 조심
이제 컨테이너를 다시 실행 해 준다. 이번에는 기존 데이터가 없으니 루트 비밀번호까지 지정해서 띄우도록 한다.
docker run -d --name dutypark_dev_db \
--restart unless-stopped \
-v /Users/shane/Documents/dev/_dutypark_dev_db:/var/lib/mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=pass \
mysql
이후 로그를 확인 해 보면 당연히 잘 떴다.
docker logs -f dutypark_dev_db
이제 덤프해둔 데이터베이스를 복구 할 차례. 로컬에서 아까 저장해둔 sql 파일을 역으로 복사한다.
docker container cp ./dutypark.sql dutypark_dev_db:/.
복사 후 컨테이너 내에 dutypark.sql 파일이 정상적으로 저장된 상태
root 계정으로 mysql 접속 후
docker exec -it dutypark_dev_db bash
mysql -u root -p
# 컨테이너를 띄울때 설정한 비밀번호를 입력해 접속
가장 먼저 사용자를 추가 해 준다.
create user 'dutypark'@'%' identified by '비밀번호';
이제 데이터 베이스를 생성 후 sql 파일을 밀어 넣는다.
create database dutypark;
# mysql 종로 휴
mysql -u root -p dutypark < dutypark.sql
순서에 주목
이제 미리 생성한 유저에게 방금 만든 데이터베이스에 대한 권한을 부여 한다.
mysql -u root -p
GRANT ALL PRIVILEGES ON dutypark.* TO 'dutypark'@'%';
드디어 모든 DB 이전도 끝났고, 성공적으로 설정도 원래상태로 돌릴 수 있었다.
결론
애초에 볼륨 폴더 긁어올 필요 없이 처음부터 dump 했으면 오히려 간단하게 해결 되었을 것이다.
어쨌든 덕분에 재밌는 오류도 발견했고 파일시스템의 대소문자 구분때문에 이런 일도 있구나 하는걸 겪어 재밌었다.
끝.
References
- https://stackoverflow.com/questions/64146845/mysql-not-starting-in-a-docker-container-on-macos-after-docker-update
- https://stackoverflow.com/questions/64146845/mysql-not-starting-in-a-docker-container-on-macos-after-docker-update/64150016#64150016
- https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html
- https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html
'Development > Daily Error' 카테고리의 다른 글
NVM 설치 후 터미널이 느리게 뜨는 문제 해결 (0) | 2023.03.18 |
---|---|
Safari 에서만 localhost에 쿠키가 저장 안되는 문제 해결 (0) | 2023.02.19 |
Cannot find a (Map) Key deserializer for type 해결 (0) | 2022.11.17 |
[iRods] 데이터 오브젝트가 포함된 리소스 삭제하기 CAT_RESOURCE_NOT_EMPTY (0) | 2022.10.17 |
[Java Mail] Could not convert socket to TLS; 문제 해결 (0) | 2022.10.12 |