프로젝트로 진행했던 서비스를 AWS로 제대로 배포하기에 앞서서, 서비스가 실제로 구동되는 구조에 대해서 먼저 공부해보려한다.
물론 우리가 진행했던 프로젝트는 모바일 앱이지만, 서버의 경우는 모바일/웹서비스 구분없이(실제로 웹도 일종의 앱과 같은 개념으로도 보고있기 때문에) 모바일 앱이든, PC웹이든 큰 차이가 없을것이라 생각한다.
전체적인 web architecture에 대한 공부와 정리는 해당 블로그를 참고했고 추가적인 개념은 생활코딩 강의를 공부했다.
내가 참고한 블로그의 글은 Jonathan Fulton의 Web Architecture 101을 번역한 글이다.
INTRO
위의 다이어그램이 웹 애플리케이션 아키텍쳐를 나타내고 있다.
사실 나도 아직은 주니어라 각각의 의미가 무엇인지 얼추 알지만 확실하게 알고는 있지 않아서, 블로그에서 정리한 말을 인용으로 가지고와서 정리하려 한다.
사용자가 구글에서 “짙고 아름다운 안개와 숲 속의 햇살”을 검색한다.
첫 번째 결과는 Storyblocks, 우리의 메인 사진 저장 및 벡터 사이트에서 나온다. 사용자는 결과를 클릭하고 브라우저는 사진 상세 페이지로 이동한다. 보이지 않지만, 브라우저 내부에서는 DNS 서버에 Storyblocks에 어떻게 접속할 수 있는지 물어본 후 Storyblocks에 접근을 시도한다.
브라우저의 요청은 우리의 로드 밸런서에 도착하고, 서비스를 운영하기 위해 동작 중인 10여 개의 서버 중 하나를 랜덤하게 선택해서 요청을 처리한다. 웹 서버는 캐싱 서비스에서 필요한 이미지 정보를 가져온 후 더 필요한 정보는 데이터베이스에 요청한다. 사용자에게 전달한 이미지의 컬러 프로필이 아직 만들어지지 않았음을 인지한 후 “컬러 프로필” 잡(job)을 잡 큐에 보낸다. 잡 서버는 큐에 추가된 것들을 비동기적으로 처리한 후 데이터베이스에 결과를 적절히 업데이트한다.
다음, 우리는 전체 텍스트(full-text) 검색 서비스에 사진의 제목을 전달해서 비슷한 사진들을 찾으려고 한다. 사용자가 Storyblocks의 멤버로 로그인했다면 그의 계정 정보를 계정 서비스에서 가져온다. 일련의 작업들이 끝난 후, 데이터 firehose에 페이지 뷰 이벤트를 발생시켜서 우리의 클라우드 스토리지 시스템에 기록하고, 그 정보는 분석가들이 비즈니스와 관련된 질의에 답하는 데 사용할 수 있도록 데이터 저장소(warehouse)에서 사용된다.
서버는 이제 HTML로 화면을 렌더링한 후 로드 밸런서를 통해서 사용자의 브라우저로 보낸다. 페이지는 CDN에 연결된 우리의 클라우드 스토리지 시스템에서 가져오는 자바스크립트와 CSS 파일을 포함하고 있다. 그래서 브라우저는 그 콘텐츠를 가져오기 위해 CDN에 접속한다. 마지막으로, 브라우저는 사용자가 볼 수 있는 콘텐츠를 렌더링한다.
각 단계에 대한 설명
1. DNS(추가 설명은 블로그 링크 참고)
DNS는 “Domain Name Server”의 약자며 월드 와이드 웹(WWW)이 가능하도록 만드는 기반 기술이다. 이는 당신의 컴퓨터가 알맞은 서버에 요청을 보낼 수 있는 경로를 찾는 데 필요하다. 전화번호에 비유하자면 도메인 이름과 IP 주소의 차이는 “Jone Does에게 전화하기”와 “201-867–5309에 전화하기”의 차이다. 도메인에서 IP 주소를 찾기 위해서는 DNS가 필요하다. DNS를 인터넷의 전화번호부라고 생각하면 된다.
host
인터넷에 연결된 컴퓨터 한 대를 host라고 한다. 이 host들이 연결되기 위해서는 각각의 주소인 IP가 필요하다. 이를 통해 컴퓨터끼리 통신하게 되며 다양한 기능이 가능해졌다.
그러나 점차 IP주소를 일일이 기억하기 힘들었고, 이를 해결하기 위해 등장한 것이 DNS가 등장한다.
DNS의 태동
DNS server에 모든 DNS와 IP 주소를 키와 값의 형태로 저장되어 있다. 흔히들 우리가 자주 사용하는 www.naver.com 같은 것이 DNS이며, 유저가 해당 주소를 입력하면 DNS server로 접속하여, 주소에 해당하는 IP주소를 반환하여 유저의 브라우저화면을 이동시켜 준다.
2. 로드 밸런서
로드 밸런싱에 대해 자세히 알아보기 전에, 수평적 vs 수직적 애플리케이션 확장(scaling)에 관해서 얘기해보자. 수평적 확장은 더 많은 장치를 새로 추가하는 것이고, 수직적 확장은 이미 사용하고 있던 장치의 성능(예. CPU, RAM)을 높이는 것이다.
웹 개발을 한다면 당신은 아마 언제나 수평적 확장을 원하게 될 텐데, 그 이유는 간단히 말해 서비스 중단을 막기 위해서다. 서버는 언제든지 고장이 날 수 있다. 네트워크는 속도가 느려질 수 있다. 데이터 센터에 정전이 발생할 수도 있다. 1대 이상의 서버를 가짐으로써 이러한 상황에 대비할 수 있으며 서비스는 멈추지 않고 계속 제공된다. 달리 말하면 ‘오류 내성’이 생긴다. 두 번째로, 수평적 확장은 애플리케이션 백엔드(웹 서버, 데이터베이스, 서비스 X, 기타 등등)들을 각각 다른 서버에서 돌림으로서 서로가 최소한으로 결합할 수 있도록 한다. 마지막으로, 수직적 확장은 언젠가 한계에 부딪힌다. 세상에는 당신의 앱이 하는 일을 모두 처리할 수 있을 만큼 강력한 성능을 가진 컴퓨터는 없다.
여기서 로드 밸런서는 수평적 확장이 가능하도록 만드는 마술이다. 그들은 들어오는 요청을 복제/미러링 된 수많은 애플리케이션 서버 중의 하나로 연결하고 서버의 응답을 다시 클라이언트로 보낸다. 모든 서버는 특정 요청을 같은 방식으로 처리해야 하며, 로드 밸런서는 이들 서버에 과부하가 걸리지 않도록 들어오는 요청을 적절히 분배해주는 일을 한다.
현실로 돌아와서, 실제로 서버 배포를 하게 된다면, 흔히들 AWS를 사용하게 될 것이고, ELB(Elastic Load Balancer)가 그 역할을 하게 된다. (실제 사용에 대한 설명은 블로그 링크 참고)
3. 웹 애플리케이션 서버
사용자의 요청이 들어오면 핵심 비즈니스 로직을 실행하고 그 결과를 HTML에 담아 브라우저로 보낸다. 이 일을 하기 위해서는 보통 데이터베이스, 캐싱 계층, 잡 큐, 검색 서비스, 기타 마이크로 서비스, 데이터/로그 큐(queue) 등 무척 다양한 백엔드 인프라와 데이터를 주고받아야 한다. 앞서 언급했듯 사용자의 요청을 처리하기 위해 최소 2번, 보통 그보다 많은 횟수로 로드 밸런서에 연결될 것이다.
서버 구현은 우리가 흔히 아는 python이나 java 등의 언어로 코드를 구현하는 것을 의미한다.
웹 애플리케이션 서버 즉 WAS에 대한 설명은 해당 링크 참고
4. 데이터베이스 서버
데이터베이스는 데이터 구조를 정의하고, 새로운 데이터를 삽입하고, 데이터를 찾고, 데이터를 수정하거나 삭제하고, 데이터로 연산을 수행하는 등의 일을 한다. 대부분은 웹 앱 서버는 잡 서버의 역할을 하는 데이터베이스 서버와 직접 통신한다. 거기에 더해서 각각의 백엔드 서비스는 애플리케이션의 다른 영역과 분리된 자신만의 데이터베이스를 가지고 있을 수 있다.
데이터 베이스 : SQL과 NoSQL
SQL은 “Structured Query Language”의 약자이며, 1970년대에 더 많은 사람들이 사용할 수 있도록 관계형 데이터 세트 질의(query)의 표준으로 만들어졌다. SQL 데이터베이스는 공통 ID(보통 숫자를 사용)로 연결된 테이블에 데이터를 저장한다. 간단한 예제로 사용자의 과거 주소 정보를 저장하는 과정을 살펴보자. 아마 사용자의 아이디로 연결된 user와 useraddress라는 2개의 테이블을 사용할 것이다. 아래의 이미지를 보자. 두 테이블은 useradress 의 user_id 컬럼이 user의 id 컬럼을 참조하고 있는 외래 키(foreign key)이기에 연결된 것이다.
NoSQL, “Non-SQL”의 약자인 이것은 대규모 웹 애플리케이션에 의해 생성되는 많은 양의 데이터를 처리하기 위해 등장한 최신 데이터베이스 기술의 집합이다(SQL에서 파생된 기술 대부분은 수평적 확장이 어려우며 특정 지점에 도달하면 수직적 확장만이 가능하다).
5. 캐싱 서비스
캐싱 서비스는 정보를 거의 O(1) 시간 안에 찾을 수 있는 단순한 키/값 데이터 저장소를 제공한다. 애플리케이션은 캐싱 서비스를 통해 자원이 많이 소모되는 연산의 결과를 다시 계산하지 않고 캐시에서 가져옴으로써 효율을 높인다. 애플리케이션은 데이터베이스의 쿼리 결과, 외부 서비스 호출 결과, 주어진 URL의 HTML 등을 캐시에 저장한다. 다음은 실무에서 사용되는 몇몇 예제다.
- 구글은 일반적인 검색어인 ‘dog’나 ‘Taylor Swift’를 사용한 검색을 매번 실행하기보다는 결과를 캐시 한다.
- 페이스북은 당신이 로그인할 때 포스트 데이터, 친구 목록 등 많은 데이터를 캐시 한다.
가장 널리 사용되는 캐싱 서버 기술 2개는 Redis와 Memcache다.
6. 잡 큐(job queue) & 서버
거의 모든 웹 애플리케이션은 사용자의 요청에 대한 응답과는 직접적인 관련이 없는 작업을 백그라운드에서 비동기적으로 실행할 필요가 있다. 예를 들어 구글은 검색 결과를 얻기 위해 인터넷에서 데이터를 크롤링하고 인덱싱할 필요가 있다. 이는 당신이 검색을 요청할 때마다 실행되지 않으며 구글의 검색 엔진은 비동기적으로 웹을 크롤링하고 있다.
비동기적인 작업을 가능하게 하는 여러 가지 아키텍쳐가 있지만 가장 널리 사용되는 것은 “잡 큐” 아키텍처다. 이는 2개의 컴포넌트로 구성된다. “잡”으로 이루어진 큐, 그리고 큐에 들어있는 잡을 실행하는 1개 이상의 잡 서버가 그것이다.
잡 큐는 비동기적으로 실행될 잡 목록을 저장하고 있다. 대부분 애플리케이션이 결국에는 우선순위가 적용된 큐가 필요하게 되지만 가장 단순한 것은 first-in-first-out(FIFO) 큐다. 앱이 정기적인 일정이나 사용자에 의해 발생한 잡을 실행할 필요가 생기면, 그에 알맞은 잡을 큐에 추가하기만 하면 된다.
Storyblocks를 예로 들자면, 우리의 마켓플레이스를 지원하는 데 필요한 내부 작업에 잡 큐를 사용해서 힘을 실어주고 있다. 잡 큐를 통해 영상과 사진을 인코딩하고, 메타데이터 태그를 붙이기 위해 CSV를 처리하고, 사용자 통계를 취합하고, 비밀번호 재설정을 위한 이메일을 전송하는 등의 일을 한다. 우리는 간단한 FIFO 큐로부터 시작했지만 빠른 처리 속도가 중요한 민감한 비밀번호 재설정과 같은 작업을 위해 우선순위 큐로 업그레이드하게 되었다. 잡 서버는 잡을 처리 한다. 그들은 잡 큐를 가져와서 할 일이 있는지 확인하고, 있다면 큐에서 잡을 뽑아내서 실행한다.
7. 전체 텍스트 검색 서비스
대부분은 아니지만 많은 웹 앱이 사용자가 텍스트 입력(보통 ‘쿼리’라고 불리는)을 입력을 하면 검색을 하고 가장 ‘관련 있는’ 결과를 보여주는 기능을 제공한다. 이 기능을 가능하게 하는 것은 보통 “전체 텍스트 검색”이라고 불린다. 전체 텍스트 검색은 쿼리 키워드를 포함하는 문서를 빠르게 찾기 위해 역 인덱스(inverted index)를 활용한다.
3개의 문서의 제목이 어떻게 역 인덱스로 변환되어서 특정 키워드를 포함한 제목을 가진 문서를 찾을 수 있도록 활용하는지를 보여주는 예제다. 보통 ‘in’, ‘the’, ‘with’ 같은 일반적인 단어(stop 단어라고 불린다)는 역 인덱스에 포함되지 않음을 주의 깊게 보라.
어떤 데이터베이스에서는 전체 텍스트 검색을 바로 사용할 수 있지만(MySQL은 전체 텍스트 검색을 지원한다), 보통 역 인덱스를 연산하고 쿼리 인터페이스를 제공하는 “검색 서비스”를 분리해서 운영하는 사례가 많다. 오늘날 가장 인기 있는 전체 텍스트 검색 플랫폼은 Elasticsearch지만 Sphinx 또는 Apache Solr 같은 다른 선택지도 있다.
8. 서비스
앱이 특정 규모에 도달하면 별도의 애플리케이션으로 분리해서 운영하기 위한 ‘서비스’가 생기게 된다. 외부에는 노출되지 않지만, 앱과 다른 서비스와는 연동된다. Storyblocks을 예로 들자면 몇 개의 운영, 계획 서비스를 하고 있다.
- 계정 서비스는 우리의 모든 사이트의 사용자 정보를 저장해서 교차 판매 기회를 더 쉽게 제공하고, 더 일관적인 사용자 경험을 가능하게 한다.
- 컨텐츠 서비스는 우리의 모든 비디오, 오디오, 이미지의 메타데이터를 저장한다. 또 콘텐츠 다운로드 인터페이스와 다운로드 이력을 보여주는 기능을 제공한다.
- 결제 서비스는 고객이 카드로 결제할 수 있는 인터페이스를 제공한다.
- HTML → PDF 서비스는 HTML을 PDF로 변환하는 간단한 인터페이스를 제공한다.
9. 데이터
최근 거의 모든 앱은 특정 규모에 도달하면 데이터를 제어, 저장, 분석하기 위해 데이터 파이프라인을 사용한다. 전형적인 파이프라인은 3개의 주요 단계를 가진다.
- 앱은 보통 사용자 상호작용으로 발생한 데이터를 데이터 ‘firehose’라 불리는 곳으로 전달한다. 그것은 데이터를 받아들이고 처리할 수 있는 스트리밍 인터페이스를 제공한다. 가공되지 않은 원시 데이터는 변형되거나(transformed) 추가 정보와 함께(augmented) 다른 firehose로 전달된다. AWS Kinesis와 Kafka는 이러한 작업을 위한 대표적인 기술이다.
- 원시 데이터와 최종 데이터는 모두 클라우드 스토리지에 저장된다. AWS Kinesis는 원시 데이터를 AWS의 클라우드 스토리지(S3)에 저장할 수 있도록 매우 쉽게 사용할 수 있는 ‘firehose’로 불리는 설정을 제공한다.
- 변형/추가된 데이터는 종종 분석을 위해 데이터 웨어하우스(DW)에서 로드된다. 우리는 AWS Redshift를 사용한다. 큰 기업에서는 Oracle이나 기타 독점적인 웨어하우스 기술을 사용하고 있지만, 스타트업 업계에서는 RedShift를 많이 사용하고 있으며 점유율도 계속 오르고 있다. 만약 데이터가 충분히 축적되었다면 Hadoop같은 NoSQL MapReduce 기술이 분석을 위해 필요하게 될 것이다.
처음에 제시한 아키텍쳐 다이어그램에서는 빠진 단계가 있다. 데이터 웨어하우스에서 앱과 서비스에서 사용중인 데이터베이스에서 데이터를 불러오는 과정이다. 예를 들어 Storyblocks에서 우리는 VideoBlocks, AudioBlocks, Storyblocks, 계정 서비스, 그리고 컨트리뷰터 포털 데이터베이스를 매일 밤 Redshift로 불러온다. 이는 사용자 상호작용 이벤트 데이터와 핵심 비즈니스 데이터를 함께 둠으로써 우리의 분석가에게 총체적인 데이터 집합을 제공한다.
10. 클라우드 스토리지
“클라우드 스토리지는 인터넷을 통해 데이터를 저장, 접근, 공유할 수 있는 단순하고 확장성 있는 방법”이다. 당신은 로컬 파일 시스템에 저장할 수 있는 거의 모든 것을 RESTful API를 사용해서 HTTP를 통해 클라우드에 저장하고 접근할 수 있다. 아마존의 S3는 현재로써는 가장 인기 있는 클라우드 스토리지이다.
11. CDN
CDN은 ‘Content Delivery Network’의 약자며 HTML, CSS, 자바스크립트, 이미지 같은 정적인 데이터를 웹을 통해 1개의 원본(origin) 서버를 사용하는 것보다 더 빠르게 제공하기 위한 기술이다. 이는 콘텐츠를 전 세계의 많은 ‘엣지(edge)’ 서버에 분산시킴으로써 동작한다. 그리고 사용자는 데이터를 원본 서버 대신 가장 가까운 엣지 서버에서 다운로드한다. 예를 들어 아래의 이미지처럼 스페인에 있는 사용자가 뉴욕에 있는 원본 서버의 웹페이지에 접근하면 정적인 데이터는 대서양을 가로지르는 매우 느린 HTTP 요청을 하는 대신 영국에 있는 CDN ‘엣지’ 서버에서 빠르게 가져오는 것이다.
'SW Engineering > Dev' 카테고리의 다른 글
객체지향언어(OOP)_Class 기반/프로토타입 기반 (0) | 2021.02.06 |
---|---|
"객체 지향 언어(OOP)"에 대해서 다시 정리해보기 (0) | 2021.01.26 |
Web Server vs WAS 이해하기 (0) | 2021.01.25 |
What is HTTPS? (0) | 2021.01.17 |
HTTP의 구조에 대해 이해하기(특히 Header 구조 파악하기) (0) | 2021.01.17 |