동기화

장애 경험

처음 서비스를 라이트세일로 옮기고 나서 한동안은 이걸 전통적인 호스팅 서비스와 비슷한 것으로 생각했습니다. 하지만 호스팅과는 달리 문제가 생길 때 내가 직접 해결해야 했습니다. 그러다가 지난번에 루트볼륨이 읽기전용으로 바뀌는 장애를 겪었습니다. 이 즈음부터 인스턴스가 오동작하는건 클라우드 컴퓨팅 세계에서 꽤 흔하게 일어나는 일이고 서비스는 인스턴스에 장애가 일어날 경우를 대비해서 구성해야 한다는 점을 배웠습니다. 이 전까지는 그저 인스턴스에 자동 스냅샷 생성이 전부였습니다. 하지만 이때부터는 장애가 생긴 인스턴스와 격리할 수 있도록 디스크를 하나 붙여 루트볼륨의 사라지면 안되는 데이터를 일정 시간마다 복사하기 시작했습니다.

그리고 두번째 장애가 생겼습니다. 이 때는 애초에 인스턴스에 접근할 수가 없었습니다. 터미널에 접속할 수 없었고 다른 웬만한 방법으로도 반응이 없었습니다. 문서를 찾아보니 지원 요청을 하면 사람이 직접 상태를 알아봐준다고 나와 있었고 바로 포기했습니다. 이상하게도 그동안 생성했던 스냅샷으로 인스턴스를 생성했는데 이들 역시 접근이 불가능했습니다. 이 때 스냡샷이 백업으로써 내 예상대로 동작하지 않을 가능성이 있다는 의심을 하기 시작했습니다. 한참 낑낑댄 끝에 인스턴스를 포기하고 디스크를 분리했습니다. 새 인스턴스를 생성해 디스크를 붙여보니 다행히 디스크는 문제가 없었습니다.서버에서 돌고 있던 스크립트는 깃헙에 올려놓고 관리하고 있어 같은 경로에 클론하고 디스크는 같은 자리에 마운트했습니다. 몇몇은 설정을 다시 편집해야 했지만 처음 각오한 것 보다는 훨씬 짧은 시간 안에 다시 서비스할 수 있었습니다. 이 경험을 통해 백업은 인스턴스 바깥에 해야 하고 문제가 생길 때 서비스 중단을 막거나 줄이려면 서버를 추가해 로드밸런싱을 해야겠다는 결론에 이르렀습니다.

로드밸런싱

로드밸런싱 서비스를 선택하는건 어렵지 않았습니다. 간단히 라이트세일은 한달에 $18를 내면 트래픽에 관계 없이 로드밸런싱을 사용할 수 있었고 클라우드플레어는 $5를 내면 기본적인 로드밸런싱 서비스를 시작할 수 있었습니다. 단 트래픽과 서버 수에 따라 추가 과금을 해야 합니다. 라이트세일은 최근에 저 개인의 경험 상 여러 번 문제를 일으켜 리전을 옮기도록 만들어 신뢰도가 약간 떨어진 상태였습니다. 계속 이런 식이라면 아예 다른 회사 제품으로 옮겨야 할지도 모른다고 생각하는 중이었고 또 당장 서버를 한 대만 더 붙여서 로드밸런싱을 시작하기에는 클라우드플레어 제품이 기초 기능을 훨씬 싸게 제공했기 때문에 클라우드플레어의 로드밸런싱을 선택했습니다. 두 번째 인스턴스를 만들고 설정한 다음 로드밸런서에 연결하자 바로 동작하기 시작했습니다.

첫 번째 서버는 지금까지 사용하던 바로 그 서버입니다. 이 서버에는 디스크를 추가로 붙이고 서비스의 핵심 데이터를 모두 이 디스크를 통해 서비스하고 있습니다. 디스크 자체의 백업은 인스턴스를 매일 자동 스냅샷을 만드는 정도로 만족하기로 했습니다. 지난번에 스냅샷을 통해 생성한 인스턴스 역시 똑같은 장애가 일어났지만 디스크를 분리하면 대응할 수 있을 거라고 예상합니다. 두 번째 서버는 첫 번째 서버보다 사양이 낮고 추가로 디스크를 사용하지 않은 채 핵심 데이터를 첫 번째 서버의 디스크와 같은 경로에 놓고 서빙합니다. 두 서버는 어느 한 쪽 데이터가 변경되면 느슨하게 이를 다른 쪽에 동기화합니다. 첫 번째 서버는 백업 대상으로 디스크를 추가해 내결함성을 약간 더 가지고 있고 두 번째 서버는 첫 번째 서버가 중단될 때 서비스를 계속하는 역할을 합니다. 두 번째 서버는 처음 설정을 마치고 나서 만들어 둔 스냅샷 외에는 별도로 백업을 하지 않게 했습니다.

단방향 동기화

그렇게 꽤 행복한 결말에 다다랐지만 간단히 이야기한 서버 두 대 사이에 파일 뭉치를 동기화하는 일은 난처한 문제였습니다. 서버가 두 대가 되면 로드밸런서가 사용자들을 어느 서버에라도 데려다 놓을 수 있었습니다. 두 서버는 겉으로는 한 대 처럼 동작하기 때문에 사용자의 눈에 띄지 않게 변경사항을 계속해서 동기화해야 했습니다. 다행히도 위키는 읽기에 비해 쓰기가 훨씬 더 적게 일어나고 쓰기 자체가 같은 파일에 집중될 가능성이 거의 없는 시나리오여서 느슨하게 동기화할 수 있었습니다. 하지만 파일 동기화 문제는 현대에도 정답이 없는 복잡한 문제였고 여러 솔루션이 난립하며 이들을 시나리오에 맞게 적당히 선택해 사용해야 하는 상황이었습니다.

처음에는 서버 두 대를 하나는 오리진 서버, 다른 하나를 카피 서버라고 하고 항상 오리진에서 카피 방향으로 동기화가 일어나게 해봤습니다. 그러면 읽기는 아무 서버에나 접근하면 되고 명시적으로 쓰기 작업을 할 때 별도 오리진 서버 주소로 접근하는 겁니다. 그래서 이 때는 rsync를 써서 간단하게 시작했습니다.

rsync -e "ssh -i /path_to_private_key/private_key.pem" -arvz --delete /path_to_origin_data/ bitnami@remote_ip_address:/path_to_copy_data/

이걸 크론탭에 넣고 1분마다 돌렸습니다. 오리진 서버 주소를 별도로 입력해 나타난 사이트에 글을 쓰고 잠깐 기다리면 양쪽 서버에 동기화됐습니다. 뭐 이 정도면 괜찮지 않을까 하고 한 시간 쯤 만족하다가 사용하기 굉장히 불편하고 실수하기 쉽다는 사실을 깨달았습니다. 일단 글을 읽을 때와 쓸 때 서로 다른 주소를 사람이 선택해야 한다는 점은 어떻게 봐도 이상했습니다. 또 글을 쓸 때 잘못된 서버에 접속했다는 점을 눈치채지 못하면 최대 1분 안에 글이 사라질 것이었습니다. 잘못된 서버에 접속해 있다는 사실은 주소표시줄을 보고 알 수 있었는데 항상 여기 신경을 쓰고 있어야 한다는 건 납득할 수 없었습니다.

양방향 동기화

찾아보니 서로 다른 두 기계 사이에 파일을 양방향으로 동기화하는데는 lsyncd라는 솔루션과 unison이라는 솔루션이 널리 사용되는 분위기였습니다. 전자는 백그라운드에 계속 떠있다가 파일시스템에 변경이 일어나면 바로바로 이를 다른 쪽 서버에 동기화해주는 식으로 동작했습니다. 후자는 크론탭에 넣어 일정 시간마다 한번씩 돌리도록 되어있는데 일단 실행되면 양쪽을 비교해 변경된 파일을 다른 쪽에 복사해줍니다. 제 시나리오는 읽기가 많이 일어나고 쓰기가 적게 일어나며 같은 파일에 동시 쓰기는 거의 일어나지 않았고 파일시스템 기반의 위키라 동기화할 파일 개수가 아주 많았습니다. lsyncd는 아주 많은 파일을 동기화 대상으로 할 때 종종 일어난다는 문제를 직접 확인하지 않기로 하고 unison으로 동기화를 설정했습니다.

위에서 rsync는 상대 서버의 로그인 인증서를 포함한 모든 옵션을 커맨드 한 줄에 쭉 나타낼 수 있는 점이 취향에 맞았습니다. 어딘가 한 눈에 안 보이는 장소에 적힌 작은 설정 하나 때문에 전체 동작이 좌우되는 경험은 썩 마음에 들지 않았습니다. 하지만 unison은 최소한 상대 서버에 로그인할 때 사용할 키 파일 경로만은 별도 설정 파일에 적어야 했습니다.

~/.unison$ cat default.prf # Unison preferences file sshargs = -i /path_to_private_key/private_key.pem

위 처럼 키 파일 경로를 설정하고 나면 나머지 옵션은 모두 커맨드 한 줄에 표시할 수 있습니다. 로그인은 설정 파일의 프라이빗 키로 알아서 하고 커맨드 상에는 동기화할 로컬 경로와 리모트 경로, 동기화 옵션들만 나열합니다. -auto는 어느 한쪽이 새 파일이라고 판단하면 바로 덮어쓰는 결정을 합니다. 단 양쪽 모두 파일이 변경됐을 때는 아무것도 하지 않고 지나갑니다. -batch는 실행 중 나에게 아무것도 묻지 않습니다. 이전 옵션과 함께 사용하면 충돌 상황을 해결하지 않고 방치하게 됩니다. 그래서 -prefer newer옵션이 필요한데 양쪽에서 수정이 일어나 파일이 충돌하면 그냥 더 나중에 수정한 쪽으로 덮어씁니다. 제 시나리오는 양쪽의 변경사항에 민감하게 반응해 머지하는 대신 '어쨌든' 어느 한쪽으로 동기화 되는 것입니다.

unison /path_to_origin_data ssh://bitnami@remote_ip_address//path_to_copy_data -auto -prefer newer -batch

커맨드를 만들어 크론탭에 넣고 1분마다 돌리기 시작했습니다. sudo크론탭에 넣으면 동작을 안하는데 이유는 모르겠습니다. 유저 권한의 크론탭에 넣어야 동작했습니다. 그리고 오리진 서버에 접근하는 별도 주소를 사용하지 않고 파일을 써야 할 때도 똑같이 로드밸런서가 지정해주는 서버에 로그인해서 똑같이 글을 썼습니다. 그리고 잠깐 기다리면 다른 쪽에도 변경사항이 동기화됐습니다. 일부러 다른 쪽 서버에 로그인해서 글을 써도 똑같이 동작했고 규모가 작은 쓰기 시나리오에는 문제를 일으키지 않을 겁니다.

결론

  • 라이트세일 인스턴스는 예상보다 훨씬 더 자주 장애가 일어나며 이를 통해 구동하는 서비스는 인스턴스 장애상황에 대응할 수 있도록 구성해야 합니다.

  • 인스턴스의 핵심 데이터는 별도 디스크를 사용해 백업하거나 디스크 자체를 통해 서비스해서 인스턴스 장애상황에 인스턴스와 격리할 수 있어야 합니다.

  • 서비스에 사용하는 스크립트는 깃헙에 올려 관리하고 있었습니다. 이번 장애상황에는 재설치 없이 클론만 했습니다. 몇 가지 설정을 더 올려 관리하기 시작했습니다.

  • 백업과는 별도로 장애상황에 서비스 중단을 줄이려면 서버를 추가해 로드밸런싱을 해야 합니다. 클라우드플레어 로드밸런싱이 월 $5로 가장 싸서 선택했습니다.

  • 로드밸런싱을 하려면 서버 간에 파일을 양방향으로 동기화해야 합니다. 쓰기 서버를 두고 단방향으로 동기화해보니 너무 불편했습니다.

참고

문제를 해결하는데 다음 글을 참고했습니다.