웹 애플리케이션 확장: 기본 단계
게시 됨: 2022-09-13비즈니스를 위한 응용 프로그램을 만드는 것만으로는 충분하지 않으며 최적화해야 합니다. 효과적인 방법은 확장하는 것입니다. 이 기사에서는 코드 최적화, 아키텍처 최적화 및 일반적으로 확장 가능한 웹 애플리케이션을 구축하는 방법에 대해 배웁니다.
최적화
Gearheart는 다음과 같은 질문을 스스로에게 할 것을 제안합니다.
- 데이터베이스 쿼리가 최적입니까(EXPLAIN 분석, 인덱스 사용)?
- 데이터가 올바르게 저장되어 있습니까(SQL 대 NoSQL)?
- 캐싱이 사용됩니까?
- FS 또는 데이터베이스에 대한 불필요한 요청이 없습니까?
- 데이터 처리 알고리즘이 최적입니까?
- Apache/Nginx, MySQL/PostgreSQL, PHP/Python과 같은 환경 설정이 최적입니까?
이러한 각 질문은 별도의 기사에서 다룰 수 있으므로 이 기사의 프레임워크 내에서 이에 대한 자세한 고려는 분명히 과도합니다. 응용 프로그램 확장을 시작하기 전에 가능한 한 작업을 최적화하는 것이 매우 바람직하다는 것을 이해하는 것이 중요합니다. 실제로 확장이 전혀 필요하지 않을 수도 있습니다.
스케일링
애플리케이션을 이미 최적화했지만 여전히 로드를 처리할 수 없다고 가정합니다. 이 경우 명백한 솔루션은 사용 가능한 리소스를 늘려 애플리케이션의 전체 성능을 높이기 위해 여러 호스트에 애플리케이션을 배포하는 것입니다. 이 접근 방식을 공식적으로 응용 프로그램 "크기 조정"이라고 합니다. 보다 정확하게 말하면 확장성은 사용 가능한 리소스의 양을 늘려 성능을 향상시키는 시스템의 능력입니다.
확장성에는 수직 및 수평의 두 가지 유형이 있습니다. 수직적 확장성은 하나의 노드(호스트) 내에 리소스(CPU, 메모리, 디스크)를 추가하여 애플리케이션 성능을 높이는 것을 의미합니다. 수평적 확장은 분산 애플리케이션에 일반적이며 다른 노드를 추가하여 애플리케이션 성능을 향상시키는 것을 의미합니다.
가장 쉬운 방법은 간단한 하드웨어 업그레이드(프로세서, 메모리, 디스크), 즉 수직 확장이라는 것이 분명합니다. 또한 이 접근 방식은 애플리케이션을 수정할 필요가 없습니다. 그러나 수직적 확장은 금세 한계에 도달하고, 그 이후에는 개발자와 관리자가 애플리케이션의 수평적 확장으로 전환할 수 밖에 없습니다.
애플리케이션 아키텍처
대부분의 웹 애플리케이션은 아키텍처가 웹 서버, 비즈니스 로직(애플리케이션), 데이터(데이터베이스, 정적)의 세 가지 이상의 계층으로 나눌 수 있기 때문에 선험적으로 분산되어 있습니다.
이러한 각 레이어는 크기를 조정할 수 있습니다. 따라서 시스템에 동일한 호스트에 있는 응용 프로그램과 데이터베이스가 있는 경우 첫 번째 단계는 확실히 서로 다른 호스트에서 이들을 분리하는 것입니다.
병목 현상
시스템 확장을 진행하면서 가장 먼저 해야 할 일은 계층 중 어떤 계층이 "병목 현상", 즉 시스템의 나머지 부분보다 느린지 결정하는 것입니다. 먼저 top(htop)과 같은 간단한 유틸리티를 사용하여 CPU/메모리 소비를 평가하고 df, iostat를 사용하여 디스크 소비를 평가할 수 있습니다. 그러나 xdebug, oprofile 등과 같은 유틸리티를 사용하여 응용 프로그램을 프로파일링할 수 있는 전투 로드 에뮬레이션(AB 또는 JMeter 사용)이 있는 별도의 호스트를 제공하는 것이 바람직합니다. pgFouine과 같은 유틸리티를 사용하여 좁은 데이터베이스 쿼리를 식별할 수 있습니다(물론 배틀 서버의 로그를 기반으로 하는 것이 좋습니다).
일반적으로 응용 프로그램의 아키텍처에 따라 다르지만 일반적으로 병목 현상이 발생할 가능성이 가장 높은 후보는 데이터베이스와 코드입니다. 애플리케이션이 많은 사용자 데이터를 처리하는 경우 병목 현상은 정적 저장소일 가능성이 높습니다.
데이터베이스 확장
위에서 언급했듯이 최신 응용 프로그램의 병목 현상은 종종 데이터베이스입니다. 문제는 일반적으로 성능과 많은 양의 데이터를 저장할 필요성의 두 가지 클래스로 나뉩니다.
데이터베이스를 여러 호스트로 나누어 데이터베이스의 부하를 줄일 수 있습니다. 동기 또는 비동기 복제로 마스터/슬레이브 체계를 구현하여 해결할 수 있는 동기화의 심각한 어려움이 있습니다. PostgreSQL의 경우 동기 복제에는 Slony-I를, 비동기 복제에는 PgPool-II 또는 WAL(9.0)을 사용할 수 있습니다. 읽기 및 쓰기 요청 분할 문제를 해결하고 슬레이브 간의 로드 균형을 조정하기 위해 특수 데이터베이스 액세스 계층(PgPool-II)을 구성할 수 있습니다.
관계형 데이터베이스의 경우 많은 양의 데이터를 저장하는 문제는 파티셔닝(PostgreSQL의 "파티셔닝") 또는 Hadoop DFS와 같은 분산 데이터베이스에 데이터베이스를 배포하여 해결할 수 있습니다.
PostgreSQL 구성에 대한 훌륭한 책에서 두 솔루션에 대해 읽을 수 있습니다.
1. 그러나 많은 양의 데이터를 저장하는 경우 가장 좋은 솔루션은 대부분의 NoSQL 데이터베이스(예: MongoDB)의 고유한 장점인 샤딩입니다.
2. 게다가, 일반적으로 NoSQL 데이터베이스는 쿼리의 구문 분석/최적화, 데이터 구조 무결성 검사 등을 위한 오버헤드가 없기 때문에 SQL 형제보다 더 빠르게 작동합니다. 관계형 데이터베이스와 NoSQL 데이터베이스를 비교하는 주제도 상당히 광범위하고 가치가 있습니다. 별도의 기사.
3. JOIN 선택 없이 MySQL을 사용하는 Facebook의 경험은 별도로 주목할 가치가 있습니다. 이 전략을 사용하면 데이터베이스를 훨씬 더 쉽게 확장할 수 있으며 데이터베이스에서 코드로 로드를 전송할 수 있습니다. 이 코드는 아래에서 설명하겠지만 데이터베이스보다 쉽게 확장할 수 있습니다.
코드 스케일링
- 확장 코드의 복잡성은 호스트가 애플리케이션을 실행하는 데 필요한 공유 리소스의 수에 따라 다릅니다. 세션만 가능합니까, 아니면 캐시와 파일을 공유해야 합니까? 어느 쪽이든, 가장 먼저 해야 할 일은 동일한 환경의 여러 호스트에서 애플리케이션 사본을 실행하는 것입니다.
- 다음으로 이러한 호스트 간에 로드/요청 밸런싱을 설정해야 합니다. TCP(HAProxy), HTTP(nginx) 또는 DNS 모두에서 수행할 수 있습니다.
- Gearheart가 언급한 다음 단계는 정적 파일, 캐시 및 웹 애플리케이션 세션을 각 호스트에서 사용할 수 있도록 하는 것입니다. 세션의 경우 네트워크를 통해 작동하는 서버(예: Memcached)를 사용할 수 있습니다. 캐시 서버로서 동일한 Memcached를 사용하는 것이 합리적이지만 물론 다른 호스트에서 사용합니다.
- 정적 파일은 NFS/CIFS를 통해 또는 분산 FS(HDFS, GlusterFS, Ceph)를 사용하여 일부 공유 파일 스토리지에서 마운트할 수 있습니다.
또한 파일을 데이터베이스(예: Mongo GridFS)에 저장하여 가용성 및 확장성 문제를 해결할 수 있습니다(NoSQL 데이터베이스 확장성 문제가 샤딩으로 해결된다는 점을 고려).
별도로 주목할 가치가 있는 것은 여러 호스트에 배포하는 문제입니다. "업데이트"를 클릭하여 사용자에게 다른 버전의 응용 프로그램이 표시되지 않도록 하는 방법은 무엇입니까? 내 생각에 가장 간단한 해결책은 업데이트되지 않은 호스트를 구성 로드 밸런서(웹 서버)에서 제외하고 업데이트가 만들어지면 순차적으로 켜는 것입니다. 쿠키 또는 IP를 사용하여 사용자를 특정 호스트에 바인딩할 수도 있습니다. 업데이트를 위해 데이터베이스를 크게 변경해야 하는 경우 가장 쉬운 방법은 프로젝트를 일시적으로 닫는 것입니다.