늘모자란, 개발 :: 싴갤러스 - 무중단(을 흉내내는) DB백업 시스템 구축

늘모자란, 개발

사건 발생
지난 10월 25일 0시경, 사이트가 이상하다는 말을 듣게 되었습니다.
확인해보니 MySQL 서버가 말썽, 근데 좀 이상합니다. 잘될땐 잘되는데 새로고침할때마다 DB가 에러를 throw하고 있는데 왜지..?
가끔은 no such file or directory도 뜨고, too many connections도 뜨고.. 그리고 gone a way도 뜨는 등 난리도 아닙니다. 도대체 무슨일이 일어난걸까요.

서버 다운
처음 의심했던것은 MySQL 을 5.7.20으로 업그레이드 했기 때문 아닌가였습니다. 그런데 업그레이드는 아침에 이뤄졌고, 오류가 난건 거의 15시간이 지난 이후.. 연관이 없어보입니다. 아직도 업그레이드 했기때문에 터진게 아닌가 하는 강력한 의심을 가지고 있지만, 어쨌든 사건에 집중해봅시다.

제일 먼저 사이트를 활성화 시켜야했습니다. 뭐 할때마다 에러가 나는데 되는것도 아니고 안되는것도 아닌 기괴한 상태가 지속되고 있었습니다. 쉘에서 MySQL에 붙으려고 해도 too many connections로 붙을 수가 없을정도로 이미 점거 당한 상태. 보나마나 process에 행이 걸려서 뒤의 요청을 처리 못하고 있는 것이겠지요. 도저히 확인을 할 수 없을정도로 밀려들어오니 일단 웹서버를 내렸습니다. (어차피 서비스가 정상작동하고 있지도 않았습니다)

이제 살펴보자
서버를 내린 이상 시간끌리지 않고 최대한 빠르게 문제를 확인해야 했는데 에러로그를 보니 메모리 관련해서 에러가 찍혀 있었습니다. key buffer size를 조절하라.. 뭐 memory trap 이라고 적힌거도 있고, because you hit a bug 이런식으로도 쓰여있는데 아니... 그래서 뭐가 문제라는건데... 정작 유추할 수 있는 에러로그는 없고 미칠 노릇입니다. 친구말로는 메타데이터 파일이나, 권한 문제가 일어났을 수 있다고 하던데 (바로 며칠전에 비슷한 에러를 겪음) 문제도 없었습니다. 

일단 뭐가 깨져도 깨진건 확실합니다. MySQL에서는 innodb 엔진을 쓰는 경우 1~6 레벨(?)까지 강제 복구 옵션을 지원하고 있습니다. recovery 레벨을 설정하면 read-only가 되는데 이때 살릴 수 있는거라도 살리려면 mysqldump해주는게 좋습니다. 저도 데이터를 일단 백업해야겠다는 생각에 mysqldump로 DB를 하나씩 덤프뜨긴했는데 특정 DB에 접근하니 바로 dump가 중지됩니다. select를 못한다는거죠. 어떤 DB가 깨졌구나하고 대강 범위를 좁힐 수 있었습니다.

2017-10-25T01:35:15.821493+09:00 4 [ERROR] InnoDB: Failed to find tablespace for table [ ] in the cache. Attempting to load the tablespace with space id 23958


왜 범위를 좁혔냐 하면, 위 로그는 전혀 상관없는 DB에서 났기때문입니다. 아마 하나가 깨지니 뒤에 따라오는 테이블을 모두 읽지 못하는 현상이 아닌가 싶은데.. 범위를 줄여나가는게 중요합니다.

그다음에는 .ibd 및 .frm을 모두 cp한후에, 문제가 일어났을것이라 생각하는 db의 내용을 모두 지우고, MySQL을 재시작해 싹 밀고, 다시 붙여넣어봤습니다. 해당 DB가 없으니 기가막히게 잘 굴러갑니다만 다시 데이터를 넣자마자 바로 db가 재시작되어버립니다. 여기서 ibdata가 박살이 났구나하고 감을 잡을 수 있었습니다.
보통은 이럴때 mysqlfrm을 사용해 테이블 구조를 새로 만들고, 데이터를 다시 밀어넣는식으로 복구합니다만, 저 같은 경우는 테이블 수도 많고, 일단 db에 select를 하면 db가 재시작되어버리기때문에 별 의미가 없다 생각했습니다. (사이사이 frm및 ibd 데이터를 하나씩 넣어서 산 테이블을 확보해보려했으나 거의 소용없는 짓이었습니다). 메모리 에러 일 수 있을것이라 생각해 서버 재부팅도 두번이나 했으나.. .안되네요.

결국 여기서 손을 들었습니다. ibd와 frm이 있지만 애초에 붙어지질 않으니 복구할 수가 없구나. 마지막 백업이 언제지? 아침 6시입니다. 그리고 새벽까지 삽질하고 있었으니 그 동안에 누적되지 못한 로그들도 많으니 문제가 된 DB는 최대 24시간정도의 데이터 유실이 발생합니다. 사이트는 다시 돌지만, 뼈아픈 경험입니다. 24시간마다 백업이 돌고 있으나, 유실될 양을 생각해보면 터무니 없이 부족합니다. 어떻게 해야할까요?

무중단 백업은 어떻게?
4년전 쯤에서는 MySQL의 replication을 활용하고 있었습니다. 이때는 DB쿼리가 최적화 되어있지 않아 쿼리한개가 DB를 잡아먹던 시절이기도 하고, VPS에서 사용했기때문에 서버 사양이 워낙 안좋아 시도때도 없이 DB가 죽을때가 있었습니다. 정확히 다시 말하면, 백업이 아니라 cpu 와 메모리 사용량을 분산시키기 위한 cluster개념으로 replication을 사용했습니다.

그러나 세월이 꽤 흘렀죠? 문제가 된 DB 쿼리도 다 수정되었고, 서버 스펙은 그때와 말도 안되게 뻥튀기되었습니다. 서버 두대를 돌릴 이유가 없으니 자연스럽게 replication 서버를 내렸었죠. 이 얘길 왜 하느냐? 지금부터 replication을 사용할 것이기 때문입니다.

왜? 기존에 사용하고 있는 mysqldump는 모든 db를 full select 합니다. 또한 쿼리 캐시 옵션을 비활성화하기때문에 DB에 심각한 부하가 발생하며, 사이트 전체의 성능저하도 일어납니다. 따라서 백업텀을 줄이면 안되겠느냐 같은 말은 사용량이 많은 시간대엔 절대 먹히지 않는 말입니다. 고로, 부하가 발생하지 않으면서도, 실시간 백업을 할 수 있는 방법 = 데이터가 insert되면 다른 데이터베이스에 저장하는 방법은 뭐가 있죠? replication입니다. 그래서 docker를 이용해 가상 서버를 하나 만드는걸로 진행합니다.

다음과 같이 변경했습니다
모든 과정이 매끄럽게 진행되진 않았지만, 주말동안 작업해 적용된 내용은,
Replication 서버를 돌려서 한쪽 DB가 맛이 가더라도 관리자 확인하에 즉시 DB를 변경할 수 있게 변경했습니다.
서비스 중일때 발생하는 dump 백업의 텀을 3시간으로 줄였습니다.(기존 24시간)
마지막으로, 서버 이미지를 하루마다 저장해 허브에 푸시합니다. DB 두개가 한번에 박살나도, 최대 10분내에 복구가 가능합니다.(순수하게 다운로드 시간)

얻은 결론
서버 용량이 풍족하진 않습니다. 이번 구성으로 인해 꽤 많은 용량을 차지하게 되었습니다만... 서버 용량이야 좀 더 자주 확인하면 되는 것이고, 밤을 안새게 해주진 않습니다. 또한 사용자 여러분들의 사이트 사용에도 차질이 없도록 만반의 준비를 했어야 했는데, 진작해야할 일을 이제야 한 것 같습니다. 좋은 교훈을 얻었다 생각하고, 서버 안정화에 더 힘쓰겠습니다
2017/10/26 10:02 2017/10/26 10:02