Nginx 액세스 로그를 CloudWatch Logs Agent 로 보내기

이 글은 다음의 글을 번역한 것입니다. 저작권은 원작자에게 있습니다.

최근에 나는 Ubuntu 18.04 에서 CloudWatch Logs 를 어떻게 설정하는지에 대한 글을 썼다. 그 글에서 나는 Agent 가 */var/log/syslog* 를 CloudWatch Logs로 syslog log 파일을 푸쉬하도록 설정했다. 이전 글을 보고 여기로 오길 바라고 혹시나 여러분이 Ubuntu 를 사용하지 않는다면 여러분이 사용하는 OS 에 CloudWatch Logs Agent 를 설치하기 위해서 AWS 문서를 체크해야 한다.

여기서 나는 Nginx 의 액세스 로그(access log) 와 에러 로그(Error log) 를 AWS CloudWatch Logs Agent 를 통해서 CloudWatch Logs 로 어떻게 전송하는지를 보여줄 것이다.

우리는 에이전트가 소비하기 원하는 두개의 Nginx 로그 파일을 가진다고 가정하자: */var/log/nginx/access.log**/var/log/nginx/error.log* 로 원하는 만큼 많은 Nginx 로그를 추가할 수 있다.

AWS CloudWatch Logs Agent 는 *amazon-cloudwatch-agent.json* 파일로부터 설정들을 가지고 오고 이것은 Ubuntu 에서 */opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json* 위치해 있다.

이미 파일이 그곳에 있을거라 생각하고 collect_list 배열 아래쪽에 다음을 추가하면 된다.

여기서 핵심은 Nginx 로그 파일에서 timestamp_format 과 일치하는 포맷을 찾는다는 것이고, 만약 Ubuntu 에서 Nginx 에 기본 로깅 설정을 사용중이라면 위 설정은 잘 맞는다.

또 log_group_name 이 CloudWatch Logs Agent 의 자격증명에 로그 스트림 생성을 위한 logs:CreateLogSteam, 로그 스트림 기술을 위한 logs:DescribeLogStream 그리고 로그 이벤트를 푸쉬하기 위한 logs:PutLogEvents 의 IAM 허가권을 가지는 로그 그룹과 일치하는지 확인해봐야 한다.

amazon-cloudwatch-agent.json 파일을 업로드 한 후에 agent 서비스를 재시작해줄 필요가 있다:

곧바로 CloudWatch Logs 에서 Nginx 로그들이 표시된다.

Ubuntu 18.04 LTS 에 CloudWatch Logs Agent 설정하기

이 글은 다음의 글을 번역한 것입니다. 저작권은 원작자에게 있습니다.

AWS CloudWatch Logs Agent 는 서버로부터 AWS CloudWatch Logs 서버로 로그를 전송하기 위해 설정될 수 있다. 이 문서에서, 나는 어떻게 Ubuntu 18.04 LTS 에 어떻게 설정하는지 보여줄 것이지만 여러분은 Ubuntu16.04 혹은 다른 운영체제 에서도 유사한 절차를 따를 수 있다. CloudWatch Logs Agent 는 Windows Servers 에서 EventViewer 로그를 수집하기 위해 설정할 수도 있다.

아주 좋은것은 여러분이 AWS EC2 를 실행하지 않는다고 하더라도, Azure, Google Cloud, Linode, Digistal Ocean 등과 같이 어떤 서버든지 이 기술을 사용할 수 있다는 점이다. 이 문서는 여러분이 따라하기에 쉬운 간단한 샘플이다.

여러분은 이러한 절차를 설정관리 스크립트로 손쉽게 만들거나 자신만의 Docker 이미지 사용을 위해 Dockerfile에 추가할 수 있어야 한다.

Download / Install the Debian Package

AWS 는 그들의 문서에 CloudWatch Logs Agent 데미안 패키지 위치를 기술하고 있고 우리는 curl 을 이용해 다운로드하고 실행하면 된다.

이 문서의 모든 명령어는 sudo 를 사용허거나 root 사용자로 실행하는 것으로 한다.

Add cwagent User to adm group

다음으로 우리는 설치 프로그램이 생성한 cwagent 리눅스 사용자 계정을 Ubuntu 시스템 로그들에 읽기 허가권을 주기 위해서 adm 그룹에 추가하는 수정작업을 할 것이다.

Setup an IAM User Account and Permissions

이제 우리는 여러분의 AWS 계정에 CloudWatch Logs 에 로그 데이터를 전송하기 위해 Ubuntu 서버에 허가권을 부여할 필요가 있다.

만약 AWS EC2 운영하고 있다면, 이 과정을 건너뛰고 Assumed Role 를 사용해라. 하지만 IAM 허가권을 가진 Assumed Role 이 있는지 확인할 필요가 있다.

AWS Console 에서 IAM 사용자를 생성하고 AWS accessID 와 AWS secretKey 를 기록하고 여러분의 Ubuntu 서버에서 다음과 같이 /home/cwagent/.aws/credentials 파일을 생성하십시오.

또, 다음과 같이 /home/cwagent/.aws/config 파일을 생성하십시오

AWS console 에서 만든 새로운 IAM user 의 aws_access_key_id 와 aws_secret_access_key 를 사용해야 해야하며 region 또한 정확하게 지정해야 한다. 다음과 같이 /opt/aws/amazon-cloudwatch-agent/etc/common-config.toml 에 추가함으로써 인증을 위해 CloudWatch Logs Agent 가 여기를 바라보도록 한다.

Create a Log Group

어떻게 로그들을 구성하길 원하는지에 대해서 잠깐 동안 생각해봐야 한다. CloudWatch Logs 는 Log Groups 과 Logs Streams 개념을 제공한다. 로그 그룹을 로그별로 다이나믹하게 생성하거나 하나의 로그 그룹에 모든 서버 로그들을 사용할지와 같은 것이다. 이 문서에서 우리는 hostname.example.com/syslog 와 같은 로그 스트림을 생성하는 여러 서버에 대한 로그를 가질 수있는 하나의 로그 그룹을(web-server-log-group) 생성할 것이다.

나는 단일 로그 그룹에 스트림을 생성하고 로그를 출판하기 위한 접근만 제공하기 위해 IAM 허가권을 제한할 수 있기 때문에 이러한 접근방법을 선호한다.

Grant the IAM User / Role Permission to Publish Logs

web-server-log-group 으로 명명된 로그 그룹을 가지고 있다고 가정하고 이전 절차에서 생성한 IAM user에 IAM 정책(Policy) 를 붙일 것이다. 만약 EC2 를 운영중이라면 Assumed Role 에 이 정책을 붙일 수 있다.

주의해야 할 것은 숫자 1234567890 는 여러분의 AWS account id 로 바꿔야 한다. 만약 CPU, Memory, Network, Disk 와 같은 서버 메트릭 데이터를 CloudWatch Logs Agents 가 출판하길 원한다면 다음과 같이 cloudwatch:PutMetricData 허용을 위해 추가적인 정책을 붙일 필요가 있다.

나도 리소스(Resource)에 대해 와이들카드(*) 를 사용하는 대신에 좀더 제한적일 수 있는지 좀 더 찾아봐야 한다.

또, AWS 는 여러분이 사용할 수 있도록 CloudWatchAgentServerPolicy 으로 명명된 이미 만들어진 정책을 제공한다. 하지만 개인적으로 이건 조금 과도한 허가권이라 생각한다. 이것은 Agent 가 새로은 로그 그룹과 모든 로그 그룹에 로그를 출판하도록 서버에 허가한다.

Telling CloudWatch Logs Agent Which Log Files to Collect

AWS CloudWatch Logs Agent 는 위자드(Wizard) 를 통해서 json 설정 파일 생성을 위해 실행할 수 있는 툴을 가지고 있다:

아니면 이 위치에: /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json 수동으로 JSON 파일을 생성할 수 있는데 다음과 같다.

이것은 Ubuntu 서버의 /var/log/syslog 의 내용을 AWS CloudWatch Logs 서비스로 출판 한다. 여기에 nginx 로그를 CloudWatch Logs로 전송하는 예제가 있다.

Enable and Start the CloudWatch Logs Agent service

마지만 단계로 AWS CloudWatch Logs Ubuntu 서비스를 활성화 해주고 서비스를 시작해준다. 이것은 다음의 두개 명령어로 실행할 수 있다.

AWS CDK 개발 환경 구축하기

이 문서는 AWS CDK 개발 환경 구축에 대한 글이다. AWS CDK 는 코드로 AWS 자원을 관리하게 해주는 것으로 Infrastructure As Code 에 부합한다. 이를 사용하기 위해서는 환경 구축이 필수이기에 이를 기록한다.

구축 환경

AWS CDK 는 몇가지 언어를 지원 한다. 나는 Python 을 사용할 것임으로 Python 관련된 설정도 필수다. 그런데, Python 을 사용해보면 알겠지만 Windows 플랫폼 보다는 Unix 스타일의 플랫폼이 적합하다는 것을 알게 된다. 따라서 구축 환경은 다음과 같이 정했다.

  • OS: Mint Linux 19.3 Tricia XFCE
  • Python3

한가지 더 있다. Python3 을 사용할 경우에 AWS SDK 등을 모두 설치해야 한다. 하지만 Python 은 캡슐화를 지원한다. Virtualenv 가 바로 그것인데, 모든 것을 Virtualenv 환경으로 구축을 할 예정이다.

PreRequirement

이제 본격적으로 구축을 해보자. 이를 위해서 필수 프로그램이 필요한데, 이는 다음과 같다.

  • npm (Node)

이는 다음의 URL 을 명시되어 있다.

Node, npm 설치

npm 을 기반으로 한다고 문서에 나와 있다. 따라서 npm 을 설치해 줘야 한다. npm 은 Node.js 기반이며 Node.js 를 설치하는 방법은 다양하지만 이전에 이에 대해서 기술한 문서가 이미 있기에 다음을 참고 했다.

주의할 것은 AWS 의 문서에 나와 있다시피 LTS 버전을 설치해줘야 한다. AWS 문서에 나와 있듯이 CDK 를 설치해 준다.

Python3 환경 구축

Python3 에도 필요한 것이 있는데, distutils 패키지 이다. 이것은 우분투 패키지로 설치를 해줘야 한다. 다음과 같이 설치를 해준다.

이것을 설치해야지만 python3 을 로컬 계정에서 환경을 구축할 수 있다. 다음과 같이 pip 를 설치해 준다.

virtualenv 를 설치하고 난 후에 이제 캡슐환경을 만들어준다. 그냥 .python3 으로 만들어준다.

여기서 이제 AWS CLI 와 기타 몇가지를 함께 설치해준다. 이는 Python3 에 패키지로 존재함으로 pip 명령어를 이용해서 설치하면 된다.

이로써 필수 프로그램 설치는 다 됐다.

VS Code 설정

이제 Editor 설정을 해야 한다. VS Code 라면 AWS CDK 를 사용하는데 부족함이 없다. 데스크탑 리눅스를 사용함으로 VS Code 에서 우분투 패키지를 다운로드해 설치하면 된다.

Color Themes

좋은 Color Themes 가독성을 높여줘 프로그래밍을 하는데 많은 도움이 된다. 내가 주로 사용하는 Color Themes 는 다음과 같다.

  • Ayu
  • Primal

물론, 이건 지극히 개인적인 것임으로 검색을 통해서 자신에게 맞는 Color Themes 를 찾으면 된다.

Python Extension 설정

VS Code 가 Python 을 지원하도록 해야 한다. 이를 위해서 Python Extension 을 설치하고 다음과 같이 설정을 해준다. 그리고 다음과 같이 pip 를 이용해서 프로그램을 설치해준다.

AWS Toolkit for Visual Studio Code

AWS Tookit Extension 을 설치해 준다. 별도의 설정은 해주지 않아도 된다.

Indenticator, indent-rainbow

Python, Yaml 문법의 특성상 Indent 를 기반으로 문법 체크를 하는데 이 Extension이 도움이 된다.

Sort lines

라인을 알파벳 순, 숫자순으로 정렬을 해준다.

vscode-cfn-lint

cfn 는 CloudFormation 을 말한다. CloudFormation 문법을 체크해 준다.

YAML Support by Red Hat

CloudFormation 은 YAML 문법을 따른다. 이 문법을 체크해 준다.

Cloudformation YAML snippets for VS Code

코딩을 할때에 필요한 코드 조작을 넣어준다.

Better Comments

주석에 색깔을 입혀서 가독성을 높여준다.

이렇게 설치한 Extension 에 대한 설정값은 다음과 같다.

AWS Credential 세팅

Python3 에 가상환경에서 aws configure 명령어를 이용해서 접속을 위한 credential 을 만들어준다.

이로써 AWS CDK 를 위한 VS Code 설정을 기술했다. 정답은 없다. 개인적으로 사용하는 설정을 기반으로 보다 좋은 방법, 환경을 위한 설정이 있을 것이다.

Node 설치하기

Node.js 줄여서 Node 는 브라우저에서 탑재되었던 Javascript 엔진을 독립된 애플리케이션으로 만들어 Javascript 를 이용해 애플리케이션을 작성할 수 있도록 해준다.

준비

모르긴 몰라도 Javascript 만큼이나 다이나믹하고 빠르게 변화를 수용하는 언어를 찾기는 쉽지 않다. 그러다보니 이를 지원하는 Node.js 도 다양한 버전이 존재하게 되는데 이러한 다양한 버전을 관리하기 위한 별도의 툴이 필요하게 되었던 모양이다.

Node Version Manager (NVM). Node.js 의 버전을 관리하기 위한 툴로서 이를 이용하면 다양한 버전의 Node.js 를 설치하고 관리할 수 있게 된다.

Node.js 설치는 NVM 을 사용해 할 것이다.

NVM 설치

NVM 의 장점은 대략 다음과 같다.

  • 간단한 명령어로 로컬 계정에 설치가 가능하다.
  • 다양한 버전의 Node.js 를 쉽게 교체 가능하다.
  • Default 버전을 alias 를 통해서 간단하게 지정이 가능하다.

다음과 같이 설치 쉘 스크립트를 다운로드해 실행함으로써 설치는 끝난다.

설치가 끝나면 로그인을 새로하거나 다른 로그인 세션을 열면 된다.

설치가 정상적으로 되었다면 다음과 같이 확인해 볼 수 있다.

Node.js 설치

NVM 을 설치했다면 Node.js 설치는 매우 쉽다.

확인은 역시 버전을 통해서 가능하다.

간단 NVM 사용 방법

NVM 이 설치한 Node.js 를 비롯한 컴포넌트들을 확인하기 위해서는 다음과 같하면 된다.

Node.js 역시 Long Term Support 버전을 지원한다. 이 버전을 설치하기 위해서는 다음과 같이 하면 된다.

이렇게 하면 default 값이 v12.14.1 로 변경된다. 확은은 nvm ls 명령어를 이용한다.

사용하고자하는 버전으로 변경하고 싶다면 use 를 사용하면 된다.

Reactive Stream 간단 사용

Reactive Stream 을 사용하기 위해서는 다음과 같은 것을 구현해줘야 한다.

  • Publisher
  • Subscriber
  • Subscription

한가지 문제가 있다. 이것을 구현하기가 쉽지가 않다. 특히 Publisher 의 경우에는 Reactive Stream 의 기본 아이디어만 가지고 간단하게 구현할 수가 없다. 어떤 것을 Publisher 할지도 영향을 주는것도 문제지만 Reactive 의 Spec 을 마춰짜야 하는데 이게 쉽지가 안다.

Publisher 를 간단하게 구현 방법이 없을까? SubmissionPublisher 를 이용하면 간단하게 사용해 볼 수 있다.

이 문서는 SubmissionPublisher 를 이용해 어떻게 Reactive Stream 을 구현하는지를 설명한다.

SubmissionPublisher Class

이 클래스를 간단히 살펴보자.

이 클래스는 Publisher 인터페이스를 구현한 구현체 클래스 이다. 하지만 아주 간단하지만은 않다.

SubmissionPublisher 클래스가 가지는 메소드가 꽤 있다. 자세히 보면 get 혹은 is 로 시작하는 메소드가 많음을 알 수 있다. 이 클래스들은 Publisher 의 상태 정보를 체크하기 위한 것들이다. 반대로 set 으로 시작하는, 그러니까 뭔가를 지정하는 메소드는 많지가 않다.

이 클래스에 설명은 다음과 같이 시작한다.

Flow.Publisher that asynchronously issues submitted (non-null) items to current subscribers until it is closed. Each current subscriber receives newly submitted items in the same order unless drops or exceptions are encountered. Using a SubmissionPublisher allows item generators to act as compliant reactive-streams Publishers relying on drop handling and/or blocking for flow control.

Flow.Publisher 는 Null 이 아닌 제출된 아이템들을(item) 현재 구독자에게 닫힐때까지 비동기적으로 발행한다. 각각의 현재 구독자들은 삭제 또는 예외가 발생하지 않는한 새롭게 제출된 아이템들을 같은 순서로 받는다. SubmissionPublisher 를 사용하는 것은 아이템 생성기를 흐름제어를 위해 삭제 핸들링과 블럭킹을 필요로하는 reactive-streams Publisher 와 호환되는 것처럼 행동하게 해준다.

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/SubmissionPublisher.html

어찌되었든 간에 이것을 이용하면 Reactive Streams Publisher 처럼 만들어 준다 것에 주목해야 한다.

SubmissionPublisher 사용하기

사용방법은 아주 간단하다.

SubmissionPublisher 의 객체를 하나 생성한다. 그리고 Reactive Stream 이 Publisher 에 요구하는 subscribe 메소드를 호출해 준다.

하지만 subscribe() 메소드는 인자값으로 Subscriber 클래스를 요구한다. Subscriber 클래스는 Interface 클래스여서 구현체 클래서를 넣어야 한다. 다양한 방법이 있지만 여기서는 다음과 같이 작성해 본다.

Reactive Stream 이 요구하는 Subscriber 에 요구 메소드들이(onNext, onError, onComplete) 모두 나온다. 다음과 같이 모두 구현해 준다.

위와같이 구현을 해주면 된다. 여기서 중요한 것이 존재하는데, Subscription 객체 변수를 반드시 써야 한다는 것이다. onSubscribe, onNext 메소드에서 공통으로 보이는 subscription.request(1) 이 바로 그것인데 onNext 메소드에 이를 사용하기 위해서 Subscriber 구현체에서 멤버변수를 만들어두고 onSubscribe 시에 주입된 객체변수를 할당해 준다.

이제 다음과 같이 publisher 의 submit 메소드를 사용해 아이템을 넣어준다.

submit 메소드는 아이템을 제출해 비동기적으로 Subscriber 에게 생성해준다. SubmissionPublisher 의 비동기는 Thread 를 이용한다. 그래서 모든 Thread 가 완료되기를 Main Thread 를 몇초간 중지줘야해서 Thread.sleep(100) 코드가 필요하다.

모든 비동기 제출이 완료되면 제출자를 닫아준다. publisher.close() 다.

이렇게 완료된 전체 코드는 다음과 같다.

Reactive Stream 을 어떻게하면 간단하게 사용해볼까. Reactive Stream 이 요구하는 최소사항을 어떻게하면 직접 사용해볼 수 있을까? 그렇다면 SubmissionPublisher 클래스를 활용하라.

자바 Reactive Stream 구현체

자바(Java) 에서 Reactive Stream 구현체를 살펴본다.

이전 글에서 Reative Stream 의 기본 아이디어를 설명했었다.

  • Flow control
  • Publisher – Subscriber pattern

또한 이 아이디어를 위한 포맷은 Publisher – Subscriber 이다. Reactive Stream 에서 이 모델은 1) Subscriber 가 Publisher 에게 가입을 요청하면 2) Publisher 는 Subscriber 와 통신을 위한 채널인 Subscription 을 생성하고 이를 통보한다. 이는 다음 그림과 같이 묘사할 수 있다.

출처:https://dzone.com/articles/reactive-streams-in-java-9

여기서 주목해야할 것이 Subscriber 는 Subcription 을 통해서 데이터를 요청하고 받게 된다는데 있다.

java.util.concurrent.Flow

자바에서 구현은 위 클래스에 구현 되어 있다. 그리고 이 클래스 첫줄에 다음과 같이 설명이 시작된다.

Interrelated interfaces and static methods for establishing flow-controlled components in which Publishers produce items consumed by one or more Subscribers, each managed by a Subscription

흐름 제어 컴포넌트를 설정하기 위한 (밀접하게) 상호연관된 인터페이스(Interface) 와 스태틱 메소드(Static method)

java.util.concurrent.Flow

주석에 내용에 등장하는 컴포넌트 Publisher, Subscriber, Subscription 이다. Flow.class 는 다음과 같이 구성되어 있다.

Publisher

FunctionalInterface 인게 눈에 뛴다. 이것은 오직 한개의 메소드만을 가진다는 것과 Lambda 식을 지원하게 된다. subscribe 메소드는 인자로 Subscriber 를 받는다.

중간에 주석에 주목할만한 내용이 나온다.

Subscribers may enable receiving items by invoking the request method of this Subscription, and may unsubscribe by invoking its cancel method.

Subscriber 는 이 Subcription 의 request 메소드를 호출함으로써 아이템들을 수신할 수 있으며 cancel 메소드를 호출함으로써 가입이 해제된다.

Subcriber 는 Subscription 의 request 메소드를 호출함으로써 데이터(아이템)을 수신한다는 것이다.

Subscriber

4개의 메소드를 가지고 있다. 이 메소드들이 다루는 것이 무엇인지를 아는것이 제일 중요하다. 각 메소드의 주석에는 Subscription 이 항상 나온다.

Method invoked with a Subscription’s next item. If this method throws an exception, resulting behavior is not guaranteed, but may cause the Subscription to be cancelled

Subscription’s 의 다음 아이템을 호출하는 메소드. 만약 이 메소드가 예외를 발생시키면, 최종 결과를 보장하지 않지만, Subcription 은 취소될 수 있다.

onNext

Subscriber 가 다음 아이템을 호출하는데 Publisher 를 대상으로 하지 않고 Subscription 을 대상으로 한다는 것을 꼭 기억해야 한다.

Subscription

Publisher 와 Subscriber 를 연결하는 메시지 제어를 하는게 목적이다 .request 메소드는 Subcription 에 대해 현재 처리되지 않은 요청을 위한 아이템의 개수를 추가하게 된다. 몇개의 아이템을 요청할 것인지를 요청하는 것이다.

SubmissionPublisher

SubmissionPublisher 는 non-null 아이템을 생성해주는 Publisher 이다. 생성을 해준다고 해서 자동으로 생성해주는 것이 아니라 아이템을 제출해야 한다. 이 제출된 아이템은 순차적으로 Publish 해준다.

submit

이 메소드는 비동기적으로 Subscriber의 onNext 메소드를 호출함으로써 인수로 받은 아이템을 Subscriber 에 아이템을 발행한다.

Reactive Stream Family

Reactive Stream 에는 다양한 프레임워크, 라이브러리들이 존재한다. 이것들은 각자의 고유한 이름과 특징을 가지고 있다. 어떤 것들이 있는지 간단하게 정리해 본다.

Reactive Streams

저수준의 규약으로 주로 자바 인터페이스(Interface) 로 구현 되어 있다. 명시적인 back-pressure 으로 Publisher 와 Subscriber 의 기본 빌딩 블록을 표현한다. Java 9 에서 java.util.concurrent.Flow 로 구현되어 있다.

RxJava

이것은 Reactive Extension 이다. Neflix 에서 개발해 오픈소스로 전화하면서 세상에 알려졌다. ReactiveX 라고도 불린다. 브릿지 Reactive Stream 이라고 말하기도 하는데, Reactive Stream 을 위한 타입 전환을 지원한다.

Reactor

자바 프레임워크 이다. Spring 배포로 유명한 피보탈(Pivotal) 에서 제작해 오픈소스로 배포하고 있다.

Spring Framework

HTTP 서버/클라이언트을 툴을 비롯해 리액트 기능을 가지고 있다. Spring 의 특징은 어노테이션을 이용한 기능 주입등을 사용할 수 있다. 비동기 HTTP 서버에서 구동가능하다. (Netty, Undertow, Tomcat 8 이상)

Akka Stream

Actor Model 을 기반으로 애플리케이션 개발을 위한 Java, Scala 를 위한 툴킷이다.

이것을 정리하는 이유가 있다. Reactive Stream 은 스펙이기 때문이다. 이 스펙을 만족하면 Reactive Stream 이라고 할 수 있다. 물론 자바의 저수준 인터페이스 이름도 Reactive Stream 이라 헷깔릴 수 있지만 Reactive Stream 스펙을 만족하는 다양하고 라이브러리, 프레임워크, 툴킷들이 있다.

Reactive Stream 기본 아이디어

자바 9에서 소개된 Reactive Stream 은 비동기(Asynchronously), 논 블럭킹(Non-Blocking)이 특징이다.

그렇다면 왜 이것이 특징이되어야만 했나. 이 특징에 반대되는 개념은 블럭킹(Blocking) 이며 블럭킹을 발생시키는 요인은 동시식 요청인 것이다. 결과적으로 이 둘을 해결해야만 하는 과제를 안고 있었는데 Reactive Stream 이 이 문제를 해결했다고 볼수 있다.

이 글에서 Reactive Stream 에서 핵심 특징의 아이디어를 아주 가볍게 고찰해 본다.

일반적인 데이터 처리 흐름.

데이터를 처리를 어떻게 할까? 좀 더 정확하게 표현을 한다면 데이터 처리를 어떠한 형태를 가지고 할까?

대부분 위와 같은 형태를 가진다. 첫째로 데이터 처리를 요청하는 무언가가 있다. 이를 ‘데이터 처리 요청자’라고 하자. 둘째로 데이터를 처리하는 무언가가 있다. 이를 ‘데이터 처리자’라고 하자. 데이터 처리 요청자는 데이터 처리를 담당하는 데이터 처리자에게 데이터를 던진다. 그러면 데이터 처리자는 요청이 들어온 순서대로 데이터를 처리하게 된다.

그런데, 데이터 처리 요청자가 갑자기 한꺼번에 많은 데이터를 던지면 어떻게 될까? 아니면 데이터 처리 요청자 여러개가 하나의 데이터 처리자에게 데이터를 던지면 어떻게 될까?

데이터 처리자는 일을 처리하는데 필요한 자원이 한정되어 있다. 순차적으로 일을 처리하는 데이터 처리자는 한꺼번에 밀려드는 데이터를 쌓아두게 된다. 또, 데이터 처리 요청자는 요청한 데이터가 모두 처리되어 돌아올때까지 기달려야 한다.

이렇게 되면 전체적으로 시스템의 성능이 느려지게 된다. 모든 데이터처리가 동기화되어서 작동되는 방식이며 데이터 처리자가 모든 것을 처리할때까지 요청자는 다른 요청을 모두 차단(Block) 하게 된다.

Reactive Stream 은 이러한 문제를 해결하고자 하는게 핵심 포인트라고 보면 된다.

기본 아이디어 – 데이터 흐름을 뒤집다.

데이터 처리자의 자원은 한정되어 있다. 자원이 할당되어져야 데이터 처리자는 데이터를 처리하게 된다.

그렇다면 데이터 처리자가 자원이 할당 되었을때에 데이터 처리자가 데이터 요청자에 데이터를 가지고 가면 될 것이 아닌가? 이렇게 하기 위해서는 데이터 처리자가 데이터 요청자를 알고 있어야 한다.

이러한 아이디어는 Reactive Stream 에서는 다음과 같은 것이 핵심적인 키 포인트로 정리가 된다.

  • Flow Control
  • Publish-Subscribe pattern

기존의 데이터 흐름 제어를 완전히 뒤집은 새로운 아이디어다.

Reactive Stream 작동 방법

Reactive Stream 에서 구현은 기본 아이디어를 바탕으로 하지만 약간 다르다.

Reactive Stream 에서는 Subcriber 가 Publisher 에게 직접적으로 데이터를 요청하는 것이 아니라 어느정도 데이터 처리가 가능한지에 대한 정보를 알려준다. 그렇게되면 Publisher 는 처리가 가능한 정도만 데이터를 보내게 된다.

Publisher 와 Subscriber 사이에 정보를 교환하기 위한 일종의 채널이 필요하게 된다. 이러한 채널을 Subscription 이라고 한다. 이러한 Subscription 은 Subscriber 가 Publisher 에 가입하는 순간 생성된다.

Subscription 은 Publisher 와 Subcriber 간의 통신 채널이다. Subcriber 는 Subscription 을 통해 몇개나 받을 수 있는지에 대한 정보를 통지한다. 그러면 Publisher 는 다음과 같은 정보를 Subcriber 에게 전송한다.

  • Subscribed with subscription
  • error
  • complete
  • cancel

Publisher 가 complete 나 cancel 을 통지하면 채널은 해제되고 Publisher 와 Subscriber 관계는 끝이 난다.

Back-Pressure

역압이라고 번역하던데, 의미를 파악하기 어려운 용어들이다. 이 용어에 대한 정의는 다음에서 찾을 수 있다 .

This back-pressure is an important feedback mechanism that allows systems to gracefully respond to load rather than collapse under it

Back-Pressure 는 중요한 피드백 매커니즘인데, 대량으로 데이터를 수신한 Subscriber 가 과부하에 응답을 못하거나 시스템이 다운되지 않고 정상적으로 응답하게 해준다.

https://www.reactivemanifesto.org/glossary#Back-Pressure

Back-Pressure 는 매커니즘인데, 앞에서 설명한 어쩔땐 pull-based 혹은 push-based 로 작동되게하는 것이 바로 Back-Pressure 다.

여기서 한가지 중요한 포인트가 있다. Back-Pressure 는 성능을 보장하지 않는다는 것이다. 시스템을 보호해 어떻게든 응답을 보장하는게 목적인 것이지 Subscriber 가 응답을 빠르게 해야한다는 것은 아니다.

Gradle 설치

이 문서는 Gradle 설치에 관한 것이다. 설치할 시스템은 Linux 이다.

설치(Installation)

Gradle 공식 페이지에서 다운로드가 가능하다.

다음과 같이 압축을 해제하면 기본적으로 설치는 끝난다.

환경변수 설정

쉘 환경변수는 두가지로 다음과 같이 설정을 해줘야 한다. 쉘 환경변수는 계정에만 적용할 수도 있고 전체 시스템에 적용할 수도 있다.

GRADLE_HOME

GRADLE_HOME 은 Gradle 설치한 홈 디렉토리를 지정하면 된다.

GRADLE_USER_HOME

이 디렉토리는 다운로드한 리소스, 컴파일된 빌드 스크립트들을 캐쉬하는데 사용된다. 의존성 라이브러리를 다운로드 하는 디렉토리이기도 하다. 다음과 같이 디렉토리를 생성해 준다.

이제 이것들을 모두 쉘 환경변수로 등록해 준다.

배민의 배신??

배달의 민족이 딜리버리 히어로에 매각(?) 됐다. 국내 배달앱 1위를 달리는 배달의 민족을 인수한 딜리버리 히어로는 이미 요기요 배달앱을 인수해 한국 시장에 진출한 상황이다. 이런 상황에 배달의 민족을 인수한 딜리버리 히어로는 국내시장에서 사실상 독점적 지위에 올랐다라고 밖에 말할 수 밖에 없다.

그런데, 논란은 배달의 민족이 독일계 딜리버리 히어로에 매각한 것을 두고 ‘배신’ 이라는 성토가 이어지고 있다는데 있다. 국내에 자영업자들의 고열을 빨아 성정한 배달의 민족… ‘민족’ 이라는 어찌보면 ‘국내용’ 간판을 단것도 사람들의 마음을 아프게 한것도 있을지도 모른다.

이런 논란을 뒤로하고 이것을 바라보는 내 시각을 정리해 본다.

전세계 배달앱 시장에서의 한국.

한국 만큼 24시간 잠을 안자는 민족도 없을 것이다. 유튜브에 유행하는 한국 거주 외국인들의 말을 들어봐도 그렇다. 영미, 유럽등 다른 나라에서는 안전상의 이유도 있지만 대부분 밤9시나 10시쯤이면 가게가 문을 닫는다. 하지만 한국은 24시 편의점이 존재만으로도 그것을 입증한다.

그것뿐만이 아니라 야식이라는 문화도 한몫한다. 새벽까지 잠을 안자는 습성,, 그 중간에 뭔가를 먹어야겠다는 욕망.. 한국의 종특이라고 할수도 있다. 그런 문화를 기반으로 하는 배달 문화는 당연하다.

실제로 딜리버리 히어로에서의 한국 시장에서 직위는 어마어마하다. 딜리버리 히어로는 전세계를 기반으로 배달시장에 진출했지만 한국 시장에서 벌어들이는 수익은 다른 시장에서의 수익을 훨씬 높다.

한마디로 다른 세계시장에서 한국 시장에서의 수익은 단연 원탑이다.

자영업자들의 고열을 빨아 외국 자본에 배만 불린….

내가 봤을때 자영업자의 고열을 빤것은 맞는 말이기도 하다. 내가 자주가는 중화요리집이 있어 그 분들과 자주 이야기를 하는데 배달앱을 별로 좋아하지 않았다. 문제는 수수료. 시장의 독점적 지위를 이용해 수수료를 강제하는 행위는 명백한 독과점 행위다.

문제는 외국자본이라는 건데, 외국자본뿐만 아니라 국내자본등이 이번 거래로 인해서 어마어마한 돈을 벌었다는 것인데 이것에 대한 비판도 있다.

이 부분에 대해서 배민을 욕할 수 있나… 내가 사장이며 사업을 넓히긴 바란다면 당연히 돈이 필요할 것이며 투자자금이 절실하게 된다. 배민 대표도 당연히 사업을 확장하면서 이런 고민을 했을게 분명하다. 사업을 하는 대표라면 이런 고민을 다하게 된다.

이런때에 외국자본이 배달의 민족을 먼저 알아보고 투자금을 내준 것일 뿐이다. 먼저 알아보기도 했거니와 배달의 민족 대표가 투자 요청을 공개적으로 했을 수도 있지만 어쨌든 결정은 투자자가 하게된다. 외국자본은 시장의 흐름을 분석해 배달의 민족에 선 투자를 했을 뿐이다.

“외국 자본의 배만 불린..” 기사는 그래서 내게는 좀 다른 생각을 갖게 한다. 그렇다면 한국 자본은 뭘했다는 건가…. 아무리 생각을 해봐도 한국자본은 배달의 민족에 투자를 결정할 능력이 없었다는 거다.

배달의 민족 대표…

내가 배민의 대표였다면 이런 결정을 할 것인가…. 이런 질문에 대부분의 사람들은 도덕주의적인 관점에서만 바라 보게 된다. 남읠 비판하기에 가장 좋은 도구는 도덕성일테니까.

민족을 배신한 대표…. 글쎄다…. 일단 돈을 받은것도 아닌 딜리버리 히어로에 한 자리를 받았을 뿐이다. 물론 지분이 있기 때문에 지분을 처분하면 현찰이 되겠지만, 어쨌든 지분을 가지고 있는 상태… 그리고 딜리버리 히어로에서 경영자로서 한 자리..

내가봤을때 내가 그 대표였다면 이렇게 하지 않았을 것이다. 그것 확실하다. 그 이유는 아마도 국가, 지역사회, 커뮤니티와 지역적이 고유한 미덕, 문화등을 무시한 자본적인 사고를 별로 좋아라하지 않기 때문이다.

현실을 살아가는데 자본은 중요하다. 부정할 수 없는 현실성이 아니겠나. 그렇다고 그것이 내가 살아가는데 중요한 가치까지 넘어설수는 없다고 생각한다.

배민의 대표는 자신의 경영자로서 딜리버리 히어로로 한 자리를 얻었겠지만 그런 판단에는 그동안에 그런 회사를 키워준 사회에 대한 고려가 과연 있었겠나 하는 생각…. 그런 생각을 했을까….

독과점…

신자유주의 시장이라는 미국. 다른걸 제외하고 자본을 운영하는 미국을 보고 있노라면 무섭기도 하다. ‘반독점법안’ 이 강력함은 말을 하지 않아도 될 정도고 문제는 자본을 어지럽힌 자는 같은 자본으로서 벌을 내리는 제도를 운영하는 미국…

미국에서 이런 일이 벌어졌다면 어떻게 될까… 미국 정보는 이번 거래를 승인할까? 미국 뿐만 아니라 유럽에서 일이 벌어졌다면 과연 유럽은 이 거래를 승인할까?

굳이 전문가가 아니라고 하더라도 내가 본 몇년간 배달앱 시장은 독점적 시장으로 발전해가고 있었다. 1위와 2위가 이미 시장의 70% 이상을 넘어서고 있는 것이 증명한다.

이런 상황에서 국가인 한국정부는 뭐했냐고 하고 싶지만 지금의 자유시장, 그것도 한국의 자유시장에서 국가의 힘은 그렇게 크지 않고 어찌할 수도 없었을 것이다.

방법은 그져 한국자본이 배달의 민족과 같은 회사에 조기 투자를 하는 수밖에.. 아니면 지분을 가지던가….

이래저래 생각을 해봐도 어렵구만..