MySQL DB를 보관하고 있는 컴퓨터의 하드디스크의 용량에 한계가 왔다. 더 큰 용량의 하드디스크로 DB를 마이그레이션 해야하는 상황이 되었다. 그리고 나는 DB 전문가가 아니다.
DB 이전 작업을 직접 하려고 봤더니 Ubuntu 버전이 문제였다. 너무 낮은 버전에서 기존 MySQL이 실행 중이었다. 되도록이면 MySQL 버전을 유지하면서 OS 버전에 dependency가 없으며, 확실한 방법을 찾아야 했다. 그리고 그 해결책은 docker 였다.
docker는 아이디어만 알았고 사용해본적은 없었다. 그래서 이번 기회에 써보면 익숙해지겠다 싶은 마음도 있었다.
이 글의 목표
- Ubuntu 12 버전에서 돌고 있는 MySQL(v5.6) DB를 docker 내부에서 실행 되도록 마이그레이션 해보기
- DB 하드디스크의 용량이 모자랄 때, docker를 이용해서 간편하게 마이그레이션 해보기
핵심 포인트
MySQL은 datadir 경로에 (설정을 제외한) 모든 내용이 다 들어있다.
- 이 사실이 이 작업에서 가장 중요한 개념이다. 이를 알고 있었기에, ‘docker 내부의 새로운 MySQL이 내가 새롭게 지정한 datadir에 있는 내용을 바라보게 해주면 되지 않을까?’라는 아이디어를 생각할 수 있었다.
- MySQL의 모든 내용이 다 들어있는 경로를 datadir 이라고 한다. 아무 설정이 없으면 datadir 경로는 다음과 같다.
// datadir 경로
/var/lib/mysql/
host의 특정 경로와 docker 내부의 datadir 경로를 공유해서, docker 내부 MySQL이 host datadir을 이용해서 실행되도록 한다.
- 이 아이디어가 이 작업에서 두번째로 중요한 내용이다.
- 최초 실행 이후에는 docker 내부 datadir이 수정되고, 이 경로를 host와 공유하도록 설정했으므로, docker 내부 MySQL의 수정사항이 host datadir에도 반영된다.
- docker run 옵션에 경로 공유 설정(-v 옵션)을 추가하면 된다.
- 추후 또 다시 마이그레이션 할 일이 생기는 경우(마운트 되어있는 디스크를 바꾸고 싶은 경우), 경로 공유 옵션만 바꾸는 식으로 손쉽게 할 수 있다.
MySQL datadir 을 통째로 복사하기 전에 MySQL 서버를 종료해야 한다.
- 이유 : MySQL 서버 실행중에 innodb 파일이 수정된다. 그런데 innodb 파일에 적힌 내용이 실제 data와 일치하지 않으면 MySQL 서버가 실행도 되지 않는 일이 발생하기 때문이다.
MySQL image는 container를 생성할 때, datadir 이 비어있는 경우에만 init 파일들을 생성하고, datadir 이 비어있지 않으면 datadir 에 있는 내용을 그대로 이용해서 서버를 띄운다.
- MySQL container를 생성시 실행되는 스크립트 파일(/entrypoint.sh)을 분석해보면, datadir이 비어있는 경우 MySQL은 init process를 생략한다(= datadir에 있는 내용을 이용해서 실행한다)는 사실을 알 수 있다.
// docker 내부 /entrypoint.sh 파일의 일부
…
if [ “$1” = ‘mysqld’ -a -z “$wantHelp” ]; then
…
if [ ! -d “$DATADIR/mysql” ]; then
// $DATADIR/mysql 경로가 비어있으면 init process를 생략한다.
…
…
echo 'MySQL init process done. Ready for start up.'
fi
fi
exec “$@”
테스트 환경
source 컴퓨터 (기존 MySQL이 실행중인 컴퓨터)
- OS version : Ubuntu 12
- MySQL version : 5.6
- 기존 MySQL 서버의 datadir 경로 : (/etc/mysql/mysql.conf.d/mysqld.cnf 파일에서 확인 가능)
destination 컴퓨터 (앞으로 Docker 기반 MySQL을 실행할 컴퓨터)
- OS version : Ubuntu 18.04
- Docker version : 18.09.2
- MySQL image Tag : 5.6 (= MySQL 버전)
- destination 컴퓨터 host에서 MySQL 서버가 참조할 datadir 경로 : (작업자가 결정하면 됨)
작업 내용
0. [source] MySQL 서버를 종료한다.
1. [source] MySQL (v5.6) datadir 경로를 찾아서 기록한다.
2. [destination] docker 내부 MySQL datadir 경로와 공유할 (destination 컴퓨터의) host 경로를 결정한다.
// 예시 datadir
/mnt/my_mount_hdd/mysql/
3. [source -> destination] source 컴퓨터의 datadir에 있는 모든 내용을 destination 컴퓨터의 datadir로 복사한다.
4. [destination] MySQL (v5.6) image를 docker hub에서 받는다.
docker pull mysql:5.6
5. [destination] 아래의 명령어를 이용해서 container를 생성하고 실행한다.
docker run -it -p {port번호}:3306 --name {컨테이너이름} \
-e MYSQL_ROOT_PASSWORD={root_pw} \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
-v {destination datadir}:{docker 내부 datadir} \
-d {image name}:{tag name}///// 명령어 예시
docker run -it -p 3306:3306 --name mysql_server \
-e MYSQL_ROOT_PASSWORD=Password123! \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
-v /mnt/my_db_hdd/mysql/:/var/lib/mysql/ \
-d mysql:5.6
///// 변수 설명
// port번호
// -> container 외부에서 MySQL 에 접근할 port 번호.
// 컨테이너이름
// -> 앞으로 사용할 MySQL 서버 컨테이너의 이름
// root_pw
// -> 무엇을 넣어도 상관없음. 왜냐하면 기존의 MySQL 계정 정보를 덮어쓸 예정이므로. (source MySQL 서버의 root 계정 비밀번호가 그대로 사용될 예정이므로.)
// destination datadir
// -> docker 내부 MySQL 서버와 공유할 host 쪽 datadir 경로.
// docker 내부 datadir
// -> docker 내부 MySQL 서버의 datadir
// -> 이 글에서는 아무 설정도 하지 않으므로 /var/lib/mysql/
// image name
// -> docker image의 이름
// tag name
// -> image의 tag 이름 (설정하지 않으면 latest 로 자동 결정)
///// 옵션 설명
// "-p 3306:3306"
// -> host와 docker 내부의 방화벽 연결 설정
// -> 이 옵션 없으면 host 에서 MySQL 서버로 접근 불가)
// "-v /etc/localtime:/etc/localtime:ro -v /etc/timezone:/etc/timezone:ro"
// -> docker 내부의 timezone 설정을 host의 설정에 맞추는 옵션
// -> 이 옵션 없으면 docker 내부 timezone은 기본값인 UTC로 설정됨
// -> ro는 read-only
6. 끝!!!
7. MySQL client를 등을 이용해서 작업이 잘 되었는지 확인한다.
- MySQL client : mysql yog, mysql workbench 등등을 이용해서 접속 테스트 및 data가 잘 복사되었는지 확인한다.
- MySQL error 로그 파일 : /var/log/mysql/error.log (docker 내부)
8. docker 내부 MySQL 설정 파일을 수정해서 기타 필요한 설정을 한다.
- MySQL 설정 파일 : /etc/mysql/mysql.conf.d/mysqld.cnf (docker 내부)
이 외에 docker run 스크립트 실행시 추가할 만한 옵션이 있다. 추후에 bash로 docker 내부 접근시 사용할 user를 host의 user와 일치시킨다든지, host와 docker 내부의 공유 디렉토리를 gateway 개념으로 만든다든지 등등.
다음에 기회가 되면 MSSQL도, ORACLE DATABASE도, Tibero도(?) 이처럼 docker에 마이그레이션 해보면 좋을 것 같다.