[웹 개발자를 위한 대규모 서비스를 지탱하는 기술]Chapter 13

zzarbttoo·2021년 8월 16일
0

이 글은 절판도서 "웹 개발자를 위한 대규모 서비스를 지탱하는 기술" 을 개인적인 용도로 정리한 글입니다. 모든 내용을 정리한 것이 아니라 필요한 부분만 정리했다는 점 양해 부탁드립니다
문제/오류가 있을 시 댓글로 알려주면 감사하겠습니다


| 다중성 확보

100%에 근접한 가동률을 실현하는 원리

  • 시스템을 얼마나 멈추지 않도록 할 것인가
  • SPOF(Single Point Of Failure), 단일 장애점 제거
  • 한 곳에 장애가 나면 시스템이 멈춰버리는 부분을 가능한 없앰으로써 가동률을 높인다

다중성 확보

| AP 서버

  • AP 서버에서는 확장성을 생각하는 방식과 마찬가지로 서버 여러대를 늘어놓는게 기본이 된다
  • 1~2대 정도 정지해놓더라도 충분히 처리할 수 있도록 처리 능력을 확보해두는 것이다
  • 서버는 엔지니어가 서버 프로세스를 중지한 채로 잊어버리는 것과 같이 인위적인 실수에서부터 서버가 물리적으로 고장났다거나 메모리에 이상이 생겨 멈춘다거나 하는 등의 다양한 요인으로 멈춘다
  • 이에 대한 대응으로 로드밸런서 failover(장애 극복), fallback(정상복귀)해서 고장 난 서버를 자동으로 분리하고 서버가 복구되면 원상태로 복귀시키는 작업을 수행하는 중이다
  • 로드밸런서는 서버에 대해 주기적으로 헬스체크를 하며 AP 서버, DB 서버가 살아있는지 여부를 판정하게 된다(다중화의 가장 기본적인 부분)

| DB 서버

  • DB 서버도 서버를 여러 대 나열해서 1, 2대 정지해도 충분한 처리능력이 있도록 하는 것이 중요
  • 마스터의 다중화도 수행(어려움)
  • 멀티 마스터를 이용 : 쌍방으로 레플리케이션, 즉 서로가 서로의 슬레이브가 되는 상태로 해두고 한쪽에 쓰기 작업을 하면 다른 한쪽으로 전달하고 반대쪽에 쓰더라도 다른 쪽으로 전달하는 양방향 레플리케이션이다
  • 밀리초단위로 보면 지연이 생겨 동기가 맞지 않는 리스크가 항상 존재하게 된다
  • 서비스는 그냥 지연을 받아들이지만, 엔터프라이즈에서는 이를 동기적으로 처리함으로써 대체한다
  • 슬레이브까지 쓰여졌다는 것을 확인한 뒤에 클라이언트에 결과를 반환하도록 하며 이럴 때 성능에선 큰 손실이 날 수 있다

| 멀티 마스터

  • 페일오버는 VRRP(Virtual Router Redundancy Protocol)라는 프로토콜로 서로 감시하게 된다
  • VRRP는 원래 라우터용으로 개발된 프로토콜이다
  • VRRP에 의해 한쪽이 분리된 것을 알게 되면 자신이 Active 마스터로 승격한다
  • 멀티 마스터 구성에서 서버는 기본적으로 2대가 있고 Active/Standby 구성을 하고 있다(한쪽은 Active, 한쪽은 Standby로 기본적으로 Active만 쓰기 작업을 하는 구성)
  • 서버가 다운되면 Standby였던 쪽이 Active로 승격해 새로운 마스터가 되고 다운된 서버는 수작업으로 복구시켜 다시 Standby로 돌림 or Active/Standby 구성으로 돌림
  • 외부에서는 어떤 서버가 Active인지 판단하기 위해 Virtual IP(가상 IP 주소)를 가지고 있다
  • Active 쪽에 가상 ip 주소 0.3을 부여하며 0.3으로 접근하도록 한다

| 스토리지 서버

  • 하테나에서는 이미지 파일과 같은 미디어 파일을 저장하기 위한 분산 스토리지 서버로 MogileFS를 사용하고 있다
  • 분산 파일 시스템을 사용함으로써 대량의 파일을 보존할 수 있는 확장성과 일부 서버가 다운되더라도 전체 장애가 되지 않도록 다중성을 부여할 수 있다
  • 트래커, 스토리지 노드, 메타 정보 저장하는 트래커 DB로 구성돼있다
  • 실제 파일은 스토리지 노드에 위치하며 특정 URL에 대한 실제 파일을 위치를 나타내는 메타 정보를 DB로 관리하는 형태의 심플한 설계로 되어있다
  • 거대한 파일을 분할해서 저장하는 스토리지도 존재한다(MogileFS는 아님)
  • 스토리지 서버 용량 소비량은 점점 늘어가고 있으며, 벤더로부터 스토리지 서버를 사면 비싸다(자체 제작 스토리지 서버 댓수 여러개+ HDD 탑재)
  • 2TB HDD를 사용해 스토리지 노드 1대당 용량을 높이고 있는 전략을 사용하고 있지만, 1대당 용량을 높이게 되면 파일수가 많아져 I/O 병목이 되어 100% 용량을 사용할 수 없을 수도 있다
  • 분산 시스템에 이미지 등의 미디어 파일을 분배해 저장중이다(이전에는 NFS마운트를 사용했지만 사고를 많이 겪어 현재의 구성으로 바꿈)
  • 분산 파일시스템은 특정 노드가 다운되었을 때 해당 노드가 가지고 있는 데이터를 어떻게 해서 이동시킬 것인지, 특정 노드 상의 파일로 액세스가 편중될 때 이를 평준화하기 위해 데이터를 어떻게 재배치할 것인지 등 이와 관련된 여러 기술들이 있다
    (MogileFS는 사용률 평준화 기능은 있다)
  • 스토리지 노드를 추가함으로써 용량을 무한으로 확대 가능하다 (현재는 메타 데이터 처리가 병목이므로 스토리지 노드 뿐만 아니라 메타 데이터도 확장해야한다)

시스템 안정화

| 시스템 안정화를 위한 상반관계

  1. 안정성 <-> 자원효율
  2. 안정성 <-> 속도
  • 메모리 튜닝을 통해 8GB 중 7.5GB정도까지 메모리를 사용하고 있다 친다(메모리를 빠듯하게 튜닝)
    -> 처리량/데이터량/어플리케이션 버그/메모리 누수가 발생해 수백MB가 늘어나는 경우 발생
    -> 메모리가 늘어났을 때 곧바로 스왑 사용하게 됨
    -> 성능 저하
    -> 서비스 장애로 이어짐

  • CPU를 한계에 다다르게 사용한다
    -> 서버 대수를 줄일 수는 있다
    -> 하지만 한계의 상태에서 1대에 장애가 발생하면 전체적인 처리 능력이 부족해서 요청을 다 처리하지 못하게 됨
    -> 장애 발생

  • 메모리, CPU를 7할까지만 사용하는 등 어느 정도 여유있게 설계하는 것이 중요

  • 한계에 다다를 정도로 사용하지 않고 어느 정도 버퍼를 유지하고 버퍼가 부족해지면 새로운 서버를 추가하거나 구성을 변경해 전체적인 사용량을 줄이는 대책으로 안정성 확보

| 시스템의 불안정 요인

  • 시스템 구성이 늘어나면 불안정 요인이 늘어가게 된다
- 어플리케이션/서비스 레벨 -> 부하 증가 
1. 기능 추가
2. 메모리 누수 
3. 지뢰 
4. 사용자 엑세스 패턴 
5. 데이터량 증가 
6. 외부 연계 추가 

- 하드웨어 -> 처리능력 저하 
7. 메모리, HDD(Hard Disk Drive) 장애
8. NIC(Network Interface Card) 장애

1. 기능 추가, 2. 메모리 누수

  • 새로운 기능을 추가하면 그 기능이 예상보다 무거워서 전체적인 부하가 늘어나 서비스가 다운되는 일 많음
  • 메모리 누수도 불안정한 요인이며 Lightweight Language를 사용하면 메모리 누수를 완전히 배제하기 어려움
  • 버퍼가 너무 작으면 시간이 지남에 따라 버퍼를 다 사용하고, 스왑을 사용하기 시작해서 부하가 증가하는 것을 알 수 있다

3. 지뢰

  • 특정 URL이 읽히면(지뢰를 밟으면) 시간이 지나도 응답이 오지 않아서 지뢰처럼 장애의 원인이 되는 현상을 말한다
  • 그 원인으로 메모리 누수, 무한 루프 등 다양한 요인이 있다
ex) XML 데이터를 외부로 가지러갈 때 이전 규격 사용, 
라이브러리/어플리케이션 코드에 버그 존재 등의 경우
이 상황에서 XML 분석을 할 때 무한루프에 빠지거나 메모리를 비정상적으로 소비할 경우가 있다 

ex2) 코멘트 1만건 정도의 데이터를 처리할 때 코멘트 한건당 DateTime모듈을 사용한 오브젝트를 생성하고 있었고,
그 오브젝트가 메모리를 잡아먹고 있어서 1만건 이상 실행하니 
스왑을 사용하기 시작해 서버가 무거워져 다운되게 되엇다 
  • 지뢰를 밟고 시스템이 다운되기 전에 찾아내지 못하면 파악하기 어려워진다
  • gdb와 같은 디버거를 사용하면서 해결한다
  • 구현을 가볍개 하거나 출력 상한을 정하도록 한다

4. 사용자의 액세스 패턴

  • 인기 많은 사이트에 링크를 걸어두면 그 사이트 사용자가 집중적으로 접속해서 다운되는 경우가 있음(Slashdot 효과, Digg효과, yahoo 어택이라고 부른다)
  • 액세스 변동도 흡수할 수 있도록 구성하는 것이 중요하다
  • 전형적으로 Squid 와 같은 캐시 서버를 사이에 추가해서 게스트 사용자의 경우는 캐시를 반환할 수 있도록 해두는 방법이 있다
  • 캐시를 사용하면 리소스를 거의 사용하지 않고 요청에 대한 응답을 반환할 수 있으므로 제대로 캐싱해서 게스트의 집중적인 엑세스를 잘 처리할 수 있도록 해두는 것이 효과적이다

5. 데이터량 증가

  • 서비스가 안정적으로 유지되고 있었다고 하더라도 당초 예상했던 데이터량보다도 늘어나서 이것이 전체적인 부하의 증가로 이어져서 시스템이 불안정해지는 경우가 있다
    (ex 인터페이스의 변화로 인해 특정 기능이 더 사용되는 경우가 발생)
  • 당초 설계했을 때는 예상하지 못했던 사용법으로 해서 비정상적으로 데이터량이 늘어나면 시스템이 불안정해지는 일이 자주 발생한다
  • 이럴 땐 DB 설계를 변경해 데이터량의 규모를 적정한 수준으로 줄이는 대책 사용 가능

6. 외부 연계 추가

  • 웹 API, Amazon 웹 API 등을 새롭게 추가하는 등 외부의 새로운 웹 API를 연결하는 것을 말한다
  • Amazon이 다운됨으로써 자사 서비스가 같이 다운되는 사태가 발생할 수 있다
  • 외부 연계를 늘리게 되면 외부 시스템이 다운되어 있을 때 덩달아서 다운되는 형태가 되기 쉽다
  • 외부 시스템이 다운되거나 다운되지는 않더라도 부하가 높으면 연계하고 있는 서비스가 영향을 받지 않고 충분한 속도로 동작하도록 시스템을 구현한다
  • 또는 외부로부터 데이터를 가져올 수는 없지만 그 부분만 작동을 안하고 다른 부분은 출력할 수 있도록 하는 등 외부 노이즈에 견디는 시스템을 구현한다

7. 메모리, HDD 장애, 8. NIC 장애

  • 하드웨어 장애는 일상적으로 발생한다
  • 하드웨어 능력이 저하되더라도 문제가 되지 않도록 하는 것이 중요하다
ex) 로드밸런서에서 적절한 항목에 대해 헬스체크를 해서 하드웨어 장애로 이상이 생겼을 때 바로 문제가 발생한 서버로 요청이 전송되지 않도록 할 수 있다 
헬스체크가 적절하지 않은 경우 이상이 발생했음을 검출하지 못하고 계속해서 요청이 전송되어 에러 페이지가 밖으로 표출되는 상황이 생길 수 있다 
  • 시스템의 불안정 요인을 고려하고 설계하고 개선해나가는 것이 인프라 관련 업무에서 중요하다

시스템 안정화 대책

| 실제 안정화 대책

  • 적절한 버퍼 유지와 불안정 요인 제거로 안정화를 한다
  1. 적절한 버퍼 유지를 위해 한계의 7할 운용을 수행한다(70% 상한선을 넘을 경우 서버를 추가하거나 메모리를 늘리는 등 임계치를 설정)
  2. 불안정 요인을 제거하는 것과 관련해서는 SQL 부하대책, 메모리 누수 줄이기, 비정상 동작 시 자율제어를 들 수 있다

1) SQL 부하 : DB에 이상한 SQL을 날리면 바로 멈추면서 시스템이 다운되게 된다

  • 부하가 높아질 듯한 SQL을 발생하게 하지 않는 것은 시스템을 안정화 시키기 위해 매우 중요하다
  • 어플리케이션 엔지니어는 자신의 서비스가 어떤 SQL을 발생시켜두는지 가능한 파악해두고 부하가 높아질만한 SQL을 발행할 경우 해당 용도를 위해 격리시킨 DB를 준비해 거기로 SQL을 날리도록 한다
  • 배치로만 처리할 수 있는 작업은 배치용 DB를 준비해두고 거기에서 처리하도록 해서 사용자가 일반적으로 사용하는 DB와는 부하를 분리시켜 두는 것이 효과적이다

2) 메모리 누수 줄이기

  • 어플리케이션 엔지니어가 매일 수행해야함

| 이상 동작시의 자율제어

  • 이상 동작 시의 자율제어 대책으로는 자동 DOS 판정, 자동 재시작, 자동 쿼리 제거라는 세가지 대책이 있다
  1. 자동 DOS 판정
  • F5 어택이라고 하는, 리로드를 반복하는 행위가 있다
  • 이에 대한 대처방안으로 자동 DoS 판정을 수행하도록 해서 1시간에 특정 IP주소로부터 다수의 요청이 오면 당분간 403을 반환해서 엑세스를 자율적으로 차단함으로써 이와 같은 비정상 액세스에 대처한다
  • script를 실행하면 바로 차단되는 경우가 있는데 차단에 대한 대책으로 sleep을 삽입해서 별표를 조금씩 달 수 있도록 구현을 변경하였다
  1. 자동 재시작
  • 메모리 누수로 인해 스왑을 사용하기 시작하면서 부하가 증가하여 성능이 떨어질 때, 리소스를 지나치게 사용했다 판단하면 웹 서버를 재시작하도록 한다
  • 가상화되어 있는 호스트에서는 가상화되어 있는 OS 별로 재시작한다
  1. 자동 쿼리 제거(소요가 긴 SQL Kill)
  • 자동 쿼리제거란 DB 서버에 어떤 쿼리가 실행되고 있는지를 10초에 한 번씩 파악해서 어느 정도 이상으로 시간이 경과한 쿼리를 강제적으로 Kill 하는 것
  • 소요시간이 긴 SQL을 실행하면 DB가 멈추는 경우가 있는데 어떤 액세스를 할 때 그런 SQL을 실행하는 것인지 판명해서 문제를 해결해야한다
  • 코드 개선을 바로 하는 것은 어렵기 때문에 잠정적인 조치의 의미를 포함해서 자동 쿼리 제거를 일부 서비스에서 수행한다

이러한 자율 제어를 사용하는데 자율제어에 너무 의존할 경우 문제가 발생하는 코드를 계속 짜게되는 경우가 발생하게 된다
본질적인 해법을 찾는 것이 중요하다

profile
나는야 누워있는 개발머신

0개의 댓글