RPC 는 기술 문서를 읽다보면, 현업에서 흔히 접하게 되는 용어지만 은근히 개념을 이해하기는 쉽지 않다.

 

RPC 의 개념을 인터넷에서 (혹은 최근에 핫한 ChatGPT 에서) 찾아보자면 다음과 같이 나온다.

 

Remote Procedure Call 은 원격 컴퓨터나 프로세스에 존재하는 함수를 호출하는데 사용하는 프로토콜로, 원격지의 프로세스를 동일 프로세스의 함수를 호출하는 것 처럼 사용하는 것을 말한다.

 

하지만 위의 개념을 읽고 한번에 와닿는 사람들은 많지 않을 것이라고 생각한다. 실제로 현업의 엔지니어들도 애매모호하게 해석하게 되는 개념 중 하나이다.

 

그도 그런게 RPC 는 특정 프로토콜 이라기 보다는 전통적인 개념의 API 호출 규약이기 때문이다.

예시를 보면 이해가 보다 쉬울 것이다.

 

POST /sayHello HTTP/1.1
HOST: api.example.com
Content-Type: application/json

{"name": "Tom"}

 

위의 예시는 HTTP 위에서 구현된 RPC 방식의 예시이다. 위의 HTTP 스키마는 HTTP Client (웹 브라우저) 를 통해 HTTP Server (웹 서버) 에 전달될 것이고, WAS 는 위의 프로토콜을 처리하기 위한 로직을 구현하게 된다.

 

여기서 보통은 "그냥 HTTP 프로토콜 아닌가?" 라는 생각이 들 것이고, 그 생각은 맞다.

하지만 일반적인 REST API 처럼 보인다면, 엄밀히 따지자면 그렇지 않다. RPC 와 REST 의 차이점은 다음과 같다.

 

  REST RPC
Protocol HTTP  프로토콜에 무관 (TCP, UDP, HTTP, WebSocket, ...)
Scheme Resource 기반의 API 인터페이스
(예) 강아지 목록을 반환하는 API 를 GET /dogs 로 설계
Action 기반의 API 인터페이스
(예) 강아지 목록을 반환하는 API 를 POST /listDogs 로 설계
Design RESTful API 는 Stateless 를 전제 제약이나 규약이 없음

 

즉, RPC 는 서버와 클라이언트가 통신하기 위한 API 의 통신 패러타임 중 하나이고, REST API 와 비교될 수 있는 차원의 개념이라고 할 수 있다.

RPC 는 기본적으로 정해진 구조를 갖고있지는 않지만, 기본적인 Terminology 를 몇가지 갖고있다.

 

  • IDL (Interface Definition Language) : 서로 다른 언어로 작성된 서비스들 사이에서 공통된 인터페이스를 정의하기 위한 중간 언어
  • Stub : 서버와 클라이언트는 서로 다른 주소 공간을 사용하므로 함수 호출에 사용된 매개변수를 변화해줘야하며, 그 역할을 담당한다
    • client stub - 함수 호출에 사용된 파라미터의 변환(Marshalling) 및 함수 실행 후 서버에서 전달된 결과의 반환
    • server stub - 클라이언트가 전달한 매개변수의 역변환(Unmarshalling) 및 함수 실행 결과 변환을 담당

그리고 위의 개념에 따라서 다음과 같은 순서로 통신이 이뤄지게 된다.

 

  • IDL 을 사용하여 호출 규약을 정의한다. IDL 파일을 rpcgen 으로 컴파일하면 stub code 가 자동으로 생성
  • Stub Code 에 명시된 함수는 원시 코드의 형태로 상세 기능은 server 에서 구현된다. Stub 코드는 서버/클라이언트에서 각각 빌드된다
  • 클라이언트에서 stub 에 정의된 함수를 사용 시 client stub 은 RPC runtime 을 통해 함수를 호출하고 서버는 수신된 Procedure 호출에 대해 처리 후 결과를 반환
  • 클라이언트는 서버로부터 응답을 받고 함수를 로컬에 있는 것처럼 사용할 수 있다

 

이해가 잘 되지 않는다면 쉽게 말해서, RPC 는 이를 해석하고 처리하기 위한 Client / Server 가 존재하며, 그 사이에 규약(IDL) 을 정의하고 IDL 을 클라이언트측 / 서버측 Stub 을 통해 해석하고 비즈니스 로직을 구현한다고 보면 된다. 

 

RPC 는 RESTful API 에 비해 복잡하며, 프로젝트에 따라 Learning Curve 가 있기 때문에,

근래에 RPC 의 개념은 전통적인 방식으로는 잘 사용되지 않으며 gRPC (Google RPC) 와 같은 발전된 개념으로 사용되어진다.

 

RESTful API 가 갖는 개념적 제한에서 벗어나서 보다 자유로운 설계를 위해 사용되는 개념이고, gRPC 와 같은 Modern RPC 는 Micro Service Architecture 등에서 Internal Bidirectional Communication 을 위해 사용되어진다.

 

 

위의 예시는 gRPC 를 이용해서 Micro Service Architecture 를 구현하는 방식을 보여준다. gRPC 의 경우 Protobuf 를 스키마로 사용하기 때문에, 통신을 위한 서버-클라이언트 간에 .proto 파일에 대한 별도 관리가 필요하다.

 

Protobuf 는 구글이 만든 언어 및 플랫폼 중립적인 Data Format 으로, 일반적인 JSON Serialize / Deserialize 대비 효율적인 통신이 큰 장점이다. 관련해서는 블로그의 다음 글을 참고해볼 수 있다.

 

현업에서 RPC 는 주로, 웹 서비스 보다는 게임이나 IoT, Device 등 Non HTTP 기반 산업에서 보다 많이 보이는 형태이지만 Micro Service Architecture 가 시장에 자리잡기 시작하면서 Modern RPC 형태로 많이 보이고 있다.

 

기술이 어떻게 진화할지 모르겠지만 여러 기술적인 문제를 해결하기 위한 시도 중 하나로 알아둘 법한 패러다임이라고 생각되어진다.

 

 

 

22 년 기준 Amazon Linux2 는 CentOS 기반으로 되어있으며, 기본적으로 Yum Repository 에는 MySQL 서버의 패키지 경로가 존재하지 않는다.

따라서 먼저 Amazon Linux2 서버 위에 Yum Repository 를 추가해준다.

 

sudo yum install https://dev.mysql.com/get/mysql80-community-release-el7-5.noarch.rpm

 

Yum Repository 가 등록되었다면 다음 명령어를 통해 MySQL Community Server 를 구축해준다.

 

sudo amazon-linux-extras install epel -y
sudo yum -y install mysql-community-server

 

다음으로는 EC2 위에서 MySQL 서비스를 실행시켜준다.

 

sudo systemctl enable --now mysqld

 

서비스의 실행 상태는 다음 명령어로 확인할 수 있다.

 

systemctl status mysqld

 

MySQL 서버는 초기에 Root 계정만 존재하며, 임시 비밀번호가 발급된 상황이다. 

초기에 해야할 일은 임시 비밀번호를 대체하고, 실제 데이터베이스에 접근할 사용자 계정을 만드는 일이다.

 

sudo grep 'temporary password' /var/log/mysqld.log

명령어를 치게 되면 다음과 같이 임시 비밀번호가 튀어나온다.

임시 비밀 번호를 이용해서 MySQL 서버에 접속한다.

mysql -uroot -p

위와 같이 명령어를 입력하면 localhost 에 루트 계정으로 접속을 시도하게 된다. 비밀번호 창이 나오면 임시 비밀번호를 입력해준다.

 

초기에 MySQL 에 들어가면 가장 먼저 해야할 일은 루트 비밀번호를 변경하는 것이다. 이를 하지 않고는 사실상 아무 작업도 하지 못한다.

ALTER user 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '변경할 비밀번호';

다음 명령어를 입력해서 권한을 반영해준다.

FLUSH PRIVILEGES;

 

- 슈퍼 유저 만들기

 

Root 계정으로 MySQL 에 접근하는 건 안전하지 못하며, 필요한 권한만 가진 데이터베이스 사용자 계정을 만드는 것이 좋다.

다음 명령어를 통해 사용자 계정을 만든다.

create user '<계정 이름>'@'%' identified by '<비밀번호>';

원래는 가져야하는 권한만 정의해서 사용자 계정을 만들어야하지만, 이번 포스팅에서는 슈퍼 유저를 만들어보도록 한다.

다음 명령어를 사용하면 모든 데이터베이스에 대한 모든 권한을 사용자가 어디에서 접근하던지 부여할 수 있다.

GRANT ALL PRIVILEGES ON *.* to '<계정 이름>'@'%';

마찬가지로 설정 이후에는 다음 명령어로 권한을 반영해주도록 한다.

FLUSH PRIVILEGES;

 

 

참고 : 

https://techviewleo.com/how-to-install-mysql-8-on-amazon-linux-2/

 

CSR (Client Side Rendering) 이니 SPA (Single Page Application) 과 같은 내용들은 10년도 지난 개념들이지만, 시장이 변하는건 그렇게 빠르지 않다.

 

많은 웹 서비스들이 아직 SSR (Server Side Rendering) 기반으로 백엔드와 프론트엔드를 관리하고 있으며 아키텍처를 어떻게 디자인하는 것이 좋은지는 장단점이 있는 중요한 설계 포인트 중 하나가 된다.

특히 팀의 개발 환경 구성 및 전체적인 설계도를 구상해야하는 시니어 개발자 및 Tech Lead 라면 사용 사례를 잘 분석하고 알맞은 접근 방법을 택하는 것이 중요해진다.

 

이번 포스팅에서는 CSR 기반의 Single Page Application 을 위한 프론트엔드 개발 파이프라인을 알아본다.

 

여전히 Java 와 Spring 환경은 주류이지만, 이 환경이 지배적이던 수 년 전에는 프론트엔드 코드가 백엔드 어플리케이션과 같이 묶여서 개발되어지고 배포되어지는 SSR (Server Side Rendering) 방식이 대부분이었다.

이렇게 되면 개발 환경이 분리되지 않기 때문에 역할 분리나 협업에 있어서 애로 사항들이 발생하고, 무엇보다도 하나의 WAS 가 프론트엔드 & 백엔드를 모두 서비스하게 되다보니 배포도 같이 일어나며 인프라 비용의 비효율성도 생기게 된다.

백엔드의 버그가 생겼는데 프론트엔드 코드까지 재빌드가 되어야하거나, 프론트엔드는 리소스를 거의 잡아먹지 않는데 백엔드가 리소스를 많이 잡아먹어서 Scaling 을 해야하는 경우를 생각해보면 이해가 빠를 것이다.

 

무튼.. 이러저러한 애로사항이 있던 와중에 밀결합(Decoupling)의 개념이 세계적으로 대세가 되며 프론트엔드와 백엔드가 분리되어 각자의 라이프사이클을 가지며, API 를 통해 통신하는 CSR 방식이 점차 확산되어졌다.

CSR 방식에서는 프론트엔드와 백엔드가 물리적으로 분리되기 때문에 인프라를 각자 관리할 수 있으며 개발 환경이 나뉘어지기 때문에 역할 분리 및 작업의 비효율성이 개선되어질 수 있다.

 

그렇다면 Client Side Rendering 을 위한 아키텍처 설계는 어떻게 이뤄질까?

먼저 동적 코드에서 정적 코드가 분리된다는 점에 주목해야한다.

기존에 프론트엔드는 Javascript 가 동적 로직을 처리하기는 하지만 기본적으로 클라이언트 브라우저에서 동작한다는 측면 때문에 정적 리소스 (Static Resource) 로 분류되어진다. 

 

이와 같은 정적 리소스들은 인터넷에서 접근될 때 클라이언트 브라우저가 "다운로드" 하여 "실행" 하는 방식이기 때문에 웹 기반의 스토리지에서 제공되어질 수 있다.

이 말은 웹 서버만으로도 정적 리소스는 제공될 수 있으며, 당연히 CDN 을 통한 가속화도 가능하다는 것이다.

 

예전에는 웹 서버 스택을 nginx, apache 위에서 로드밸런싱을 구축해서 제공을 했었지만 클라우드 환경이 대세가 되면서 이 역시 Amazon S3 와 같은 웹 기반의 스토리지로 옮겨가게 되었다. 그리고 이런 정적 웹사이트는 Amazon CloudFront 와 같은 CDN 을 통해 사용자 더 가까운 위치에서 제공될 수 있게끔 디자인이 되게 된다.

 

위와 같이 구성 시 사용자는 CDN 을 통해 전세계 어디에서든 빠르게 정적 리소스를 접근할 수 있으며, 해당 리소스의 렌더링을 받을 수 있다. 또한 S3 와 같은 스토리지는 내구성이나 가용성을 AWS 에서 관리해주기 때문에 개발자 입장에서는 nginx 환경 구성이나 Auto Scaling 을 직접 구축할 필요없이 최소한의 노력으로 서비스를 구축할 수 있게 된다.

 

특히 위와 같은 구조는 동적인 처리가 적게 요구되는 마케팅 페이지 등에서 많이 활용하는 아키텍처이기도 하다.

 

그러면 위와 같은 변경된 구조에서 코드의 배포는 어떻게 수행해야할까?

전통적인 환경에서는 Jenkins 등을 통해 백엔드와 프론트엔드가 전체 빌드된 이후 서버 머신들 위에 직접 배포되었지만, 위와 같은 경우에는 머지된 코드가 단순히 Amazon S3 라는 오브젝트 스토리지 위에 배포되기만 하면 된다. 다만 React 와 같이 프론트엔드 코드를 빌드해주는 경우, 이에 대한 작업 구성은 별도로 필요해진다.

 

AWS 의 CodePipeline 은 위의 배포를 지원하며, 코드 저장소에서 작업한 코드가 Merge 되면 자동으로 S3 에 빌드가 배포되게 구성할 수 있다.

다음 샘플은 AWS 의 Infra As A Code 툴인 AWS CDK 를 이용해서 위의 파이프라인을 만들어놓은 샘플이다.

CDK 를 구동하기만 함으로써 쉽게 AWS 를 활용해서 정적 페이지의 구성과 개발을 위한 파이프라인을 만들어 볼 수 있다.

 

코드 참고 : 

https://github.com/jinspark-lab/cdk-samples/tree/main/frontend-cicd 

 

React 기반으로 어플리케이션을 배포하는 프론트엔드 파이프라인이 만들어져있기 때문에 React 개발 및 배포 환경 구성이 필요할 때 참고해볼 수 있을 것 같다.

 

CSR 이 무조건적인 장점만 있는 것은 아니며, SEO (Search Engine Optimization, 검색 엔진 최적화) 에 있어서 불리한 측면이나 작업 완료 시간이 SSR 에 비해 느리다는 점 등이 단점으로 뽑히며, 현재 두 개념은 적절히 혼합되어 사용 사례에 알맞게 사용되고 있다고 볼 수 있다.

 

 

 

 

오늘날 많은 웹서비스는 전통적인 Server Side Rendering 방식대신 정적 리소스와 동적 WAS 서버를 분리하는 Client Side Rendering 방식을 사용하고 있으며, 이와 동시에 Single Page Application 형태로 구축하고, 정적 리소스들을 CDN 형태로 서비스하는 경우가 많다.

 

이렇게 구성할 경우 정적 리소스를 엔드유저 근처에서 제공함으로써 Latency 를 낮추고 사용자 경험을 향상시키는 동시에 웹 서버의 부담을 확연히 줄일 수 있다.

또한 Client 와 Server 가 분리되게 된다면 보다 프론트엔드와 백엔드의 역할 구분이 분명해지고 인프라 구축 및 개발 환경 관리가 용이해진다.

 

단, 정적 리소스를 CDN 을 통해 제공하게 될 경우 Caching 옵션에 대해 주의할 필요가 있으며, 그 중에서도 가장 중요한 Cache-Control 헤더에 대해 간략히 기술해보고자 한다.

 

HTTP 헤더에 Cache-Control 헤더를 포함시키면 각 정적 리소스에 대해 헤더를 지정할 수 있고 각자의 캐싱 옵션을 제공할 수 있다. 이 옵션은 다음과 같이 구성되어진다.

위의 예시에서 public 은 모든 캐시가 응답의 사본을 저장한다는 것을 의미한다. 이 말은 CDN, Proxy 서버들이 모두 해당 리소스를 캐싱해도 된다는 것을 의미한다. 이 값을 private 으로 세팅하면 응답의 최종 수신자 (클라이언트 브라우저) 만 파일 사본을 저장할 수 있게 된다.

 

max-age 는 응답이 Fresh 한 것으로 간주되는 시간 단위(초) 를 정의한다. 서버의 컨텐츠가 새로 갱신된다면 200 응답과 함께 새 파일을 다운로드해서 이전 캐시를 Refresh 하고 캐싱 헤더를 유지한다.

서버 컨텐츠가 이미 Fresh 하다면 304 응답을 주며, 새로 파일을 다운로드하지 않는다.

 

그 외에 해당 헤더에 올 수 있는 중요한 다음과 같은 옵션들이 있다.

  • no-store : 이 옵션이 지정될 경우 응답은 절대 캐싱되지 않는다. 모든 요청은 서버를 히트하게 된다.
  • no-cache : 이 설정은 '캐싱하지 않음' 을 의미하지 않으며, 단순히 서버에서 유효성을 재검사하기 이전까지 캐시가 복사본을 제공하지 않음을 의미한다. 이 옵션은 Fresh 한 컨텐츠를 확인하는 가장 합리적인 방법이고, 동적인 HTML 페이지가 사용하기 적합하다.
  • must-revalidate : max-age 와 같이 사용해야하며, 유예기간이 있는 no-cache 옵션처럼 동작한다.
  • immutable : 클라이언트에게 파일은 절대 바뀌지 않음을 알린다

Cache-Control 을 적용한 실제 서비스별 모범 사례를 확인해보자.

 

(1) 온라인 뱅킹 서비스 - 금융 서비스는 트랜잭션과 계좌를 항시 최신으로 보여줘야하고, 어떤 스토리지에도 캐싱해서는 안된다.

(2) 실시간 기차 시간표 서비스 - 실시간성 업데이트가 필요하고, 데이터의 Freshness 가 가장 중요하다.

(3) FAQ 페이지 - 컨텐츠는 자주 업로드되지만 반영이 빠를 필요는 없으므로 페이지는 캐싱되어도 된다.

(4) 정적 CSS 또는 Javascript 번들 - 대부분 정적 파일들은 app.[fingerprint].js 와 같이 관리되며 자주 업데이트되어진다.

 

 

내용은 다음 페이지의 글을 참고하여 작성되었다. 중요한 부분 위주로 번역해서 정리해놓았으나, 웹서비스 바이탈을 설계 시에 고려할만한 명문이라고 생각한다.

 

https://csswizardry.com/2019/03/cache-control-for-civilians/

 

가십 프로토콜 (Gossip Protocol) 이란 분산 환경에서 메시지를 전달하는 커뮤니케이션 방식의 하나이다.

외국에서는 바이러스가 퍼지는 방식으로 동작한다하여 Epidemic Protocol 과 동의어로 사용되기도 한다고 한다.

 

가십 커뮤니케이션 방식의 특징은 소문이 전파되어나가듯 Broadcast 해주는 마스터가 없이 각 노드가 주기적으로 TCP/UDP 기반으로 메타데이터를 주고받으면서 데이터를 전송하는 점에 있다.

 

각 노드들은 주기적으로 다른 노드의 Health Check 를 수행하고 통신하므로, 주기적으로 Peer to Peer (P2P) 의 통신으로 전달이 일어나며, 일반적으로 신뢰성은 보장하지 않는다.

다음 그림을 통해 일반적인 분산 환경에서의 싱크업 방식인 Mesh Broadcast 방식과의 차이점을 확인할 수 있다.

 

가십 프로토콜은 아주 가볍기 때문에 특히 클라우드와 같은 분산 환경에서 성능 및 안정성을 위해 많이 사용되는데,

Peer 간 데이터의 비동기 Sync Up 이 가장 큰 예시가 된다.

 

Docker 같은 경우에도 내장된 Control Plane 에서 노드 발견을 위해 Gossip 메커니즘을 사용한다.

이를 통해 기존 분산 환경에서의 동기화 알고리즘인 Paxos 나 Raft 의 어려움과 복잡함 대신 다소 가볍게 멀티 클러스터를 관리할 수 있는 근간 알고리즘으로 동작할 수 있게 된다.

 

 

 

알고리즘 문제를 풀 때에나 혹은 실무에서도 종종 데이터 내에서 순차적으로 대소 비교를 해야하는 경우는 있다.

이런 문제를 풀 때에는 디자인 적으로 단일 값에 대해 스트림 내에서 가장 가까운 최솟값 / 최댓값을 찾는 것 뿐 아니라 전체 데이터에서 그 다음 나오는 가장 가까운 작은값 / 큰값을 찾아야할 때가 있게 된다.

 

가령 다음과 같은 배열이 있다고 해보자.

 

이 때 스트림 내에서 각 배열의 요소들에 대해 다음에 나오는(가장 가까운) 작은 값은 다음과 같이 매핑된다

위의 배열에서는 가장 가까운 작은값이 없다면 -1 이라고 표시했다. 

배열 내에서 단일 요소에 대해서라면 가장 가까운 작은 값을 구하는건 어려운 일이 아니다.

하지만 전체 배열, 실무에서라면 전체 데이터 셋에서 가장 가까운 작은값 / 큰값을 찾는 데에는 시간 복잡도를 생각해야되게 된다.

 

가장 간단히 백트래킹으로 한다면 이는 O(n^2) 의 시간 복잡도로 해결할 수가 있다.

즉, 위의 그림에 색깔별 화살표 모양으로 나타난대로 각 요소별로 배열을 전체 순회하면서 나오는 첫번째 상대값을 찾아 배열에 저장하면 되는 것이다.

 

하지만 늘 그렇듯 O(n^2) 의 알고리즘은 데이터 사이즈가 커질수록 부담스러운 알고리즘이 된다.

 

스택을 이용한다면 이에 대해서 O(n) 의 시간복잡도를 갖는 최적 알고리즘을 설계할 수가 있다.

알고리즘은 다음과 같이 동작한다.

왼쪽에서 오른쪽으로 훑으면서 가장 가까운 작은/큰 값을 찾아야하기 때문에 반대로 오른쪽에서 왼쪽으로 스위핑하는 알고리즘을 설계하고, 각 요소를 스택에 담는다.

각 요소들을 검사하면서 스택에서 조건에 만족하지 않는 요소들은 Pop 해주고, 자기 자신을 Push 해준다.

다음 코드를 참고해본다.

public class Solution {
    private int[] nearestSmallerToRight(int[] nums) {
        // [4,5,2,10,8]
        // [2,2,-1,8,-1]
        int[] solve = new int[nums.length];
        Stack<Integer> stack = new Stack<>();
        for (int i = nums.length - 1; i >= 0; i--) {
            while (!stack.isEmpty() && stack.peek() >= nums[i]) {
                stack.pop();
            }
            if (stack.isEmpty()) {
                solve[i] = -1;
            } else {
                solve[i] = stack.peek();
            }
            stack.push(nums[i]);
        }
        return solve;
    }
}

이와 같은 알고리즘은 비단 데이터셋 내에서 가장 근사한 작은/큰 값을 찾는 것 뿐 아니라 조건에 맞는 근사값을 찾는 데에도 상당히 유용하게 쓸 수 있는 알고리즘이다.

 

실무에서도 생각 안하고 코딩을 할 뿐 데이터 셋에 따라 성능에 영향을 줄 수 있을만한 포인트이기 때문에 생각해보는 것이 좋다. :) 

특히 코딩 인터뷰를 준비하고 있다면, 국내에는 주로 BFS 나 DFS 와 같은 백트래킹 중심의 알고리즘이 면접에 나오지만, 외국계 기업들에서는 시간 복잡도와 밀접한 코드 설계를 물어보는 경우가 많다.

이 개념 역시 NSR / NSL 알고리즘 형태로 외국에서 알려진 테크닉이기 때문에 머릿 속에 담아두고 꺼내어 쓰면 좋을 것 같드.

 

 

MySQL 의 빈로그 혹은 바이너리 로그는 MySQL 서버 인스턴스의 데이터 변경사항들에 대한 정보를 포함하는 로그 파일의 세트이다.

여기에는 에러코드, 바이너리 로그 자체에 대한 메타데이터 등 다양한 데이터가 같이 포함되게 된다.

기본적으로 Transaction Commit 시에 기록되어지며, 데이터 변경 순서를 보장한다는 특징이 있다.

 

주로 복제(Replication) 및 복구(Recovery)를 목적으로 binary log 가 사용되어지며, 복제 시에는 Secondary Node 가 Primary Node 로부터 binlog 데이터를 전달받아서 로깅하게 된다. (그리고 전달받아 로깅하는 이 로그를 릴레이 로그 라고 한다)

 

MySQL 에서 제공하는 바이너리 로그에는 3가지 종류가 있다.

 

(1) Statement-based logging

Insert, Update, Delete 에 대한 SQL 문들이 포함된다. Statement base 로 복제를 수행 시 Statement-Based Replication (SBR) 이라고 한다.

이 방식에서는 로그 파일에 적은 데이터가 기록되며, 로그 파일에 필요한 저장 공간이 줄어드는 장점이 있다.

백업에 대한 복구는 Replay 처럼 수행되며 빠르게 복원이 수행될 수 있다.

다만 로그 기반으로 복원 시 제약이 조금 있는데, 예를들어 RAND(), LOAD_FILE, UUID() 등과 같이 Deterministic 하지 않은 동작에 대해서는 정확히 복제가 안된다고 보면 된다. (당연히 RAND() 를 복원해서 나온 두 번의 결과가 같을리는 만무하다!)

 

(2) Row-based logging

이 방식은 각 행에 대한 변화를 기록한다. Row-based logging 을 이용해서 Primary → Secondary 로 복제를 수행할 수 있고, 이를 Row-Based Replication(RBR)이라 한다

RBL 의 경우 각 행의 변경 사항을 이진 로그에 기록하므로 로그 파일의 크기가 매우 빠르게 증가할 수 있으며 지연이 발생할 수도 있다.

또한, 임시 테이블의 경우 RBL 기반으로 복제되지 않으므로 임시 테이블 관련 구문은 Statement base 로 기록되어야 한다.

 

(3) 혼합형

MySQL 5.1 이상부터 Row-based logging 과 함께 혼합형태가 지원되어진다. 기본적으로는 Statement based logging 을 사용하지만, 스토리지 엔진 및 특정 명령문에 따라 로그가 자동으로 Row based logging 으로 기록되어진다.

물론 사용하는 개발자 입장에서는 큰 차이가 느껴지지는 않는다.

 

mysqlbinlog 유틸리티를 사용하면 바이너리 로그에 대한 내용을 쉽게 조회해볼 수 있다.

 

또 한가지.. 바이너리 로그와 헷갈리는 개념이 MySQL 의 Transaction log (트랜잭션 로그) 라는 개념이다.

binlog 는 데이터베이스에 기록 및 업데이트한 이력(History)을 로깅하는 개념이고, 이를 기반으로 상태 복원에 대해 핵심적으로 사용될 수 있는 Incremental Backup 을 지원한다.

반면 transaction log(redo log) 는 트랜잭션에 대한 처리, 롤백, Crash Recovery, Point In Time Recovery 등을 위한 용도로 사용되어지게 된다. 

MySQL 에서 트랜잭션은 InnoDB 스토리지 엔진만 사용하므로, MyISAM과 같은 스토리지 엔진에서는 binlog 만 사용하게 된다.

Point In Time Recovery 가 트랜잭션 로그를 통해서 지원되므로, 어떤 데이터베이스 솔루션 등을 쓴다라고 할 때에는 Transaction Log 에 대한 지원을 살펴볼 필요가 있겠다.

 

 

 

컨테이너로 어플리케이션 워크로드를 운영하는 것은 일반 가상 환경에서 서버를 운영하는 것과 다르다.

특히 실제 서비스 운영 단계에 봉착하면 컨테이너 자체의 관리 뿐 아니라 Docker 설정 환경 상 이슈, 컨테이너 들 간에 Dependency 관리 및 스케일링과 같은 다양한 문제를 만나게 되며, 이러한 문제를 마주치면 컨테이너가 무조건 좋은 기술만은 아니라는 생각이 들게 된다.

 

이 문제를 해결하기 위해 실제 프로덕션 환경에서는 Container 들 간에 논리적인 Orchestration 을 수행하게 된다. 이를 통해 컨테이너를 적절히 배치하고 스케줄링하며 죽은 컨테이너를 재구성해준다.

 

이를 통해 실제로 컨테이너 기반의 서비스를 한다는 것은 기존 전통적인 방식대로 몇개 컨테이너 위에서 서비스를 구동하고 직접 유지보수하는 것이 아니라 수많은 컨테이너를 띄워놓고 Container Orchestration Tool 을 통해 수많은 컨테이너의 관리 및 제어를 하는 것을 의미하게 된다.

이를 통해 각종 리소스의 관리, 컨테이너 라이프사이클 관리 및 서비스 관리 전반을 포괄하는 Container Orchestrator 에 대해서는 반드시 알아야하는 필수 지식이 되게 된다. 컨테이너 오케스트레이터는 YAML, JSON 과 같은 설정 파일을 기반으로 컨테이너 클러스터를 서술하며 이를 통해 어플리케이션 환경, 네트워크 구성 방법, 로그 저장 방식 등 어플리케이션 전체 환경을 포괄하는 코드 파이프라인을 구성한다.

 

가장 유명한 서비스는 역시 구글의 Kubernetes(k8s) 이다. 사실상 컨테이너 오케스트레이션 툴의 스탠다드가 되어가고 있으며, 이를 클라우드 위에서 활용한다면 아마존의 EKS(Elastic Kubernetes Service) 등 위에서 매니지드 인프라를 통해 제공받게 된다.

 

쿠버네티스는 구글에서 시작되어 CNCF 재단에서 기증받아 운영되는 오픈소스 프로젝트로, 운영 환경의 기본 사상 자체가 모든 클러스터 환경의 관리를 kubectl 과 같은 CLI 를 통해 YAML 파일 형태로 관리하는 것을 추구한다. (실제로 수많은 컨테이너를 중앙 관리하려면 코드 없이 관리가 사실 상 불가능하다..)

 

쿠버네티스에서 컨테이너들은 일종의 클러스터(Cluster)를 구성하며 이는 노드들의 집합으로 이해하면 된다.

쿠버네티스는 위와 같이 제어부(Control Plane)와 데이터부(Data Plane)을 갖고 있으며, Control Plane 에서는 Worker Node 들과 Cluster 내의 Pod 들을 관리하고 제어하는 역할을 수행하고, 사용자가 제어할 수 있는 인터페이스를 API 서버 형태로 제공한다.

Data Plane 은 워커 노드(Worker Node)들로 구성되어 있으며 컨테이너화된 어플리케이션의 구성 요소인 파드(Pod)를 호스팅하게 된다.

 

데이터 플레인에는 Kube-proxy 라는 일종의 프록시 서버가 존재하는데, Data Plane 내에서 네트워크의 조작을 담당하며, 특히 각 Pod 가 Failover 될 수 있기 때문에 해당 부분에 대한 제어 등 Access 를 위한 엔드포인트로써 사용되어진다.

Data Plane 의 kubelet 은 컨테이너 제어를 위한 런타임 인터페이스로 초창기에는 Docker 에 종속적인 내부 모듈이었지만, 이제는 표준화된 런타임인 Container Runtime Interface(CRI) 를 기반으로 구성되어 컨테이너 관리를 위한 추상화된 계층 형태를 제공한다.

 

개발자 및 관리자는 Kubectl 이라는 쿠버네티스 API 명령어를 인터페이스로 k8s 클러스터의 컨트롤 플레인과 통신하며, 내부를 CLI 로 제어한다. 

 

 

기초적인 개념에 대한 1차 정리는 여기까지 마치고, 내부 구성 요소들에 대한 개념은 다음 포스팅에 정리합니다. :) 

 

 

빅데이터 기반 분석은 많은 엔터프라이즈에서 기본적으로 고려하는 부분이 되었으며, 이에 대해 기본적으로 이해하는데 도움이 되는 몇가지 기본 지식들을 정리해보았다.

 

최근의 데이터 분석의 추세는 Data Gravity 라는 용어로 시작할 수 있을 것 같다. 기술적인 용어라기 보다는 추세를 비유하는 용어에 가까운데, 이 개념은 다음과 같다

 

  • 개념 : 데이터 규모가 커지고 무거워질 수록 비즈니스를 끌어당기는 중력(Gravity) 이 강해지는 현상을 나타내는 개념
  • 성질 : 데이터를 저장할 스토리지, 데이터를 운용 및 관리할 인력, 데이터 활용을 위한 어플리케이션 등 비즈니스는 데이터에 기반해서 함께 이동한다

즉, 데이터 기반의 의사 결정을 통해 서비스의 방향이 결정되고 향후 비즈니스 가치를 판단하는 기준이 바뀔 수 있다는 측면이다.

이러한 데이터 기반의 의사 결정을 위해 데이터를 다루는 큰 두가지 틀이 있을 수 있다.

 

(1) Data Warehouse (데이터 웨어하우스)

  • 데이터를 저장해두는 창고. 데이터 분석이 필요할 때 창고의 데이터를 가져다 이용하자는 개념
  • 분석을 위해 OLAP 툴 또는 SQL 을 이용하여 최종 정보 이용자들이 활용한다
  • 데이터들에 대해 정형화된 형식으로 통합해 단일 형식으로 구성
  • 복잡한 데이터 모델에 대해 갖고 있는 데이터 모델에 제약을 맞춰야하는 이유 등으로 인해 데이터 모델 통합이 어렵고, 인프라 증설에 따른 운영 비용이 많이 발생

 

데이터 웨어하우스는 잘 구성된 데이터를 규격화된 데이터베이스에 적재해놓고 쿼리를 조합해서 필요한 정보를 얻어내는 방식이다.

가장 친숙하게는 관계형 데이터베이스(RDB) 에서 쿼리를 통해 데이터를 조회하는 것도 웨어하우싱 방식의 접근 방식이라 할 수 있겠다.

 

장점으로는 적재(Storing)된 데이터를 정해진 Case 에 대해서는 다루기가 아주 쉽다. 정형화된 형식이기 때문에 마케팅, 사업 등 엔지니어가 아니라도 쉽게 조회 및 집계 등이 가능하다.

단점으로는 데이터를 잘 구성해서 적재해주는 측면이 중요하다. 특히 최근에 생성되는 비정형 데이터를 사용할 수 있는 형태의 정형 데이터로 바꿔줘야 한다. 또한 데이터가 적재되는 방식은 데이터가 사용되는 방식과 다른 경우가 많아 확장성을 갖추기가 쉽지 않다.

 

 

(2) Data Lake (데이터 레이크)

  • 정형 데이터로 구성된 전통적 소스 외에도 수많은 비정형 데이터들을 실시간으로 수집, 정제, 통합하여 활용하기 위해 확장된 개념
  • Raw 데이터 형식으로 저장했다가 나중에 쉽게 분석할 수 있도록 구성
    • 이를 위해 분산 시스템 환경에서 데이터를 독립적으로 쪼개고 다시 취합하는 기법을 사용
    • 하둡과 같은 맵리듀스 방식이 대표적
  • 데이터를 구축하고 활용하는 기술이 어려워서 습득과 활용이 쉽지 않다

데이터 레이크는 데이터 웨어하우스에서 접근 방식을 바꾼 방법이다. 비정형 데이터를 정형화시키지 않고 Raw 데이터 형태로 적재하고, 사용할 때 필요에 맞게끔 구성하는 방법이다.

 

장점으로는 데이터의 수집(Ingestion) 및 적재(Storing) 에 있어서 부담을 덜 수 있다. 데이터의 정형화에 대한 부담이 적기 때문에 새로이 비즈니스 확장이나 새로운 데이터가 들어오더라도 비정형으로 그냥 적재하면 된다. 따라서 데이터의 수집 자체가 더 용이하다.

단점으로는 결국 데이터는 사용 시에 Use Case 에 맞게 형변환되어야 한다. 따라서 적재된 데이터의 사용을 위해 별도의 인프라 및 시스템이 필요한 경우가 많다. 이런 측면으로 인해 비개발자가 다루기가 쉽지 않다.

 

 

최근의 추세는 Data Lake 형태로 온갖 데이터를 몰아넣고, 이를 적절한 사례에 맞게 가져가 쓰는 것으로 보인다.

비즈니스 확장성이 중요해진 추세다보니, 언제 어떤 데이터가 어떻게 쓰일지 모르고, 따라서 일단 적재해놓고 적합한 사용 사례에 맞게 데이터 분석 시스템을 모두 갖춰놓고, 이용하는 방식으로 많이 사용되어지는 것 같다.

이제 빅데이터도 어느새 알아야하는 기본 지식이 되어가기 때문에, 기반 지식들을 알아두면 많이 도움이 될 것으로 보인다.

 

 

하드 링크 (Hard Link) 와 심볼릭 링크 (Symbolic Link) 는 운영체제 파일시스템을 이해하는데 기초적인 개념이다.

아마 윈도우를 많이 사용한다면, 원본 - 바로 가기 개념이 떠오를 수 있지만 다소 차이가 있다.

 

  • Hard Link
    • 원본 파일과 동일한 inode 를 가지며 원본 파일이 삭제되더라도 링크 파일을 여전히 사용 가능하다
    • ln [Source] [Target] 명령어로 생성 가능
    • 위치 정보를 가지고 있는 이름을 여러 개 생성하는 개념이다. 그렇기 때문에 한 파일을 지워도 하드에서 해당 위치를 찾아갈 수 있다
  • Symbolic Link
    • 원본 파일의 이름을 가리키는 링크로 원본 파일이 삭제되면 사용 불가능하다
    • 전혀 다른 파일이라도 가리키는 원본 파일 이름이 같으면 계속 사용 가능하다
    • ln -s [Source] [Target] 명령어로 생성 가능. Source 를 가리키는 심볼릭 링크 Target 을 만든다
    • Source 파일을 수정하면 심볼릭 링크인 Target 파일도 수정된다
      • Target 파일을 수정해도 Source 파일이 같이 수정된다
    • 위치 정보를 갖고 있는 파일명을 또 다른 이름으로 가리키는 포인터의 개념이다
      • 하드링크는 한 위치 정보를 또 다른 이름으로 가리키는 개념

 

특히 리눅스 환경에서는 개발 환경 구성 시, 심볼릭 링크를 사용해서 파일 경로를 간편하게 관리하기도 한다

(루트 디렉토리 내에 심볼릭 링크를 구성해서 마운트한 파일 시스템을 연결시킨다던지)

 

잘 알아두면 유용하게 사용할 수 있다. :)

 

+ Recent posts