레이블이 architecture인 게시물을 표시합니다. 모든 게시물 표시
레이블이 architecture인 게시물을 표시합니다. 모든 게시물 표시

[컴] kafka outbox pattern, Transactional outbox pattern

debzium /

kafka outbox pattern, Transactional outbox pattern

분산시스템에서 2번 write 되는 상황을 해결해준다.

예를 들어, 하나의 작업(operation) 이 ‘db write opertion’ 과 ‘message notification’ 을 갖고 있는 작업이라면, db 에 한번, message 쪽에 이렇게 두번의 write 를 해야 한다.

이 경우 failure(실패)가 날 때 data 의 일관성이 깨진다.(inconsistent), 그래서 이 작업은 atomically 하게 이뤄져야 한다.

예를 들어보자, 만약 가입을 완료하면, 가입완료 email을 보내는 작업이 있다고 하자.

  1. user table에 row를 만들고,
  2. email worker에게 email 을 보내라고 한다.(email event)

우리는 user 가 생성됐다면, email 이 나갔다고 확신하고 싶지만, 위 2개중 1개가 실패하면 data 가 inconsistent 하게 된다.

1. DB table 을 이용

outbox table 을 하나 만들고, email event 를 저장한다.(timestamp, sequence number), 그리고 이 table 의 변동사항을 보고, kafka event 를 보내는 것이다.(당연히, 이 작업을 하는 worker가 필요하다.)

2. Change Data Capture(CDC)를 이용하는 방법

여긴 outbox 에 표시를 하고, 그러면 그것이 transaction 이 log 에 기록되고, 이 log 의 Change Data Capture(CDC)를 보고, kafak event 를 보내는 방법을 취한다.

다만 위 2개의 글에서 이야기하는 방식은 ref.1 에서 이야기하는 Using CDC 와 조금 차이를 보인다.

ref.1 에서는 user table 의 row 가 만들어지면, 그것에 대한 binary log 등을 CDC로 사용해서 kafka 에 event 를 보내도록 한다. 하지만 위의 2개의 글에서는 user table 에 insert 가 끝나고, outbox table 에 다시 insert 를 하게 하고, 그 outbox table 의 CDC 를 이용해서 kafka를 보낸다.

둘다 틀린 접근은 아니다. db 에 기록을 남기느냐 마느냐는 선택의 문제인듯 하다.

kafka 에 send 를 확인하고 나서, user table 을 insert 하는 방식

’kafka 에 send 를 확인하고 나서, user table 을 insert 하는 방식’은 어떨까 ?

이것의 문제는 kafka send 가 성공하고나서, user table의 insert 가 실패하는 경우다. 이 경우 rollback 이 불가능하다.

See Also

  1. 강남언니, 분산 시스템에서 메시지 안전하게 다루기

Reference

  1. Transactional outbox pattern - AWS Prescriptive Guidance

[컴] netflix 의 Marken

넷플릭스 / 주석 시스템 / 레이블링 / 어떻게 화면을 찾나 / 대용량 대규모 처리 / 하둡 / 카산드라 / 일레스틱서치

netflix 의 Marken

ref. 1, ref.2 를 보면, 넷플릭스는 Marken 이란 이름의 시스템으로 화면에 대한 metadata 를 저장하고 있다.

metadata를 annotation 이란 이름으로 만들었는데, 이 annotation 으로 넷플릭스의 영상 프레임에서 원하는 화면에 원하는 정보를 저장해 놓을 수 있다. 간단히 이야기하면, 화면에 대한 tagging, labeling 같은 것으로 보면 될 듯 하다.

예를 들면, 어느 영화의 어느 시간은 폭력적이다. 어떤 장면은 선정적이다. 어느 장면에는 장갑을 낀 사람이 있다. 등등.

architecture

  • DSL(definition schema language)은 json 을 사용했다. 이 schema 도 버전별로 저장해 놓는다.
  • annotation 을 표현하는 것도 json 으로 했다.
  • db는 casandra 를 사용했다.
  • 다양한 검색을 위해서 elasticseach 도 이용한다.
  • search latency 를 낮은 상태로 유지하려고 노력했다.
from 1.

확장성의 고려

  • 계속해서 annotation data 가 만들어지고, annotation 을 뽑아내는 알고리즘이 변경될 때마다 다시 새롭게 annotatino 들이 만들어진다. 그래서 계속 확장되는 것을 고려한다.
  • 이런 작업은 search 에 영향을 주기에 다른 stack 을 가지고 작업한다.
  • 알고리즘이 향상되면, 좀 더 많은 annotation 을 만들어내는데, 그렇게 되면, 기존의 annotation 을 update 해야 한다. 그것과 관련된 이야기를 ref. 2 에서 해준다.
    • annotation 의 양이 많기에, 그리고 어떤 것을 update할지에 대한 추적도 비용이 크기에
    • casandra/elasticsearch 위에서, data pipeline 작업을 사용자가 뒷단에서의 일을 신경쓰지 않고, 잘 수행할 수 있게 해준다.
    • 그래서 사용자가 annotation 검색을 할 때, 알고리즘에 의해 annotation 이 생성이 완료됐다면, 결과를 넘겨준다. 완료되지 않았다면 아무것도 return 하지 않는다. 그리고 새로운 알고리즘2가 실행된다면, 그동안은 기존의 annotation 의 결과를 넘겨준다. 그리고 알고리즘2의 수행이 끝나면 그때부터 update된 알고리즘을 return 해준다.
    • 이것과 관련된 flag 들은 Cassandra, Elasticsearch 에 들어가 있다. 그리고 api를 통해서 사용자도 알 수 있다.

Reference

  1. Scalable Annotation Service — Marken | Netflix TechBlog
  2. Data ingestion pipeline with Operation Management (Marken) | Netflix TechBlog

[컴][네트워크] reverse proxy

리버스 프록시 / 프록시 간단/ 프록시란

reverse proxy

reverse proxy 의 의미를 보면 forward proxy 와 ’역’방향이라는 이야기다.

forward proxy <--> reverse proxy

아래 그림을 보면서 이해해 보자.

여기서 중요한 것은 방향이다. client 에서 출발해서 server 방향으로 간다. 이 방향을 기본으로 해서, forward proxy 가 정방향이 되는 것이고, reverse proxy 가 역방향이 되는 것이다.

reverse proxy 는 load balancer를 생각해보면 쉽다. reverse proxy 가 client 들로 부터 오는 packet 을 받아서 어느 서버로 전달할 지를 정해서 보내준다.

proxy 는 ’대리자’이다. 가장 우리가 많이 사용하는 경우가 internet proxy 이다. 특정 상황에서 internet 이 막혀있다면, 이 proxy 를 통해서 인터넷에 접근할 수 있다. 그런데 이런방식은 결국 모든 나가는 packet을 proxy 를 통해서 나가게 만든다. 즉, proxy 가 일종의 gate 같은 역할을 하게 된다.

이것에 대한 기술적 구현은 다양할 수 있다. 구체적으로 지원하는 기능도 다를 수 있다. 그저 개념적으로 어디에 위치해서 어떤 역할을 하느냐가 forward / reverse proxy 를 결정한다고 보면 된다.

[컴] JunoDB 의 overview

주노 / 페이팔 디비 / 대규모 / 처리 / 대용량 처리 디비 

JunoDB 의 overview

paypal 에서 자신들이 사용하던 NoSQL DB 를 open source 로 공개했다.

여기선 ref.1 의 내용을 정리한다.

  • 사실상 paypal 의 모든 core back-end service 가 JunoDB에 의존하다.
  • JunoDB 를 이용해서, application 들은 data 를 효과적으로 저장하고, cache 를 해서 RDS 와 다른 서비스들에 대한 빠른 access 와 부하를 줄일 수 있다.
  • 단일 스레드 C++ 프로그램으로 시작
  • 이후 동시성이 높고 멀티코어 친화적인 Golang으로 재작성
  • 인메모리의 짧은 TTL(Time To Live) 데이터 저장소
    --> 에서 긴 TTL을 지원하는 영구 데이터 저장소로 발전
  • 온디스크 암호화와 전송 중 TLS를 통해 향상된 데이터 보안을 제공
  • 데이터 재분배(data redistribution)를 통한 빠른 확장(quick scaling)
  • 현재, 시스템 가용성이 99.999%에 달하는 JunoDB
  • 매일 3,500억 건의 요청을 처리.(평균 초당 40,510건)

Juno DB 사용하는 경우

  • 캐싱
  • Idempotency 로 사용 : 여러 요청이 와도 오직 하나의 동작만 하도록
  • counter : 특정 자원의 한도를 정하고 사용할 수 있게 해준다.
  • SoR(System of Record, 기록시스템) : 영구적인 기록은 아니지만, 몇년정도의 장기적인 기록을 위해 사용한다.
  • latency bridging
    • JunoDB의 빠른 클러스터간 복제(replication)는, Oracle 처리중의 복제 지연들 해결해준다.
    • 그래서 거의즉시(near-instant)적으로 모든 곳에서 일관된 읽기를 가능하게 해준다.
    • latency bridging 기술은 “계정/사용자 생성 과 결제 처리를 위한 멀티 데이터 센터 active-active processing” 에서 사용돼서 높은 가용성, 신뢰성, 읽기확정성(read sacling)을 보장해 준다.

JunoDB Architecture: A High-Level Overview

JunoDB 는 매끄럽게 함께 작동하는 3개의 중요 component들로 구성돼 있다.

  1. JunoDB client library : 이것은 DB를 사용하는 Client 쪽에서 사용하는 library
  2. JunoDB proxy :
    • JunoDB proxy instance들은 load balancer 에 의해 제어된다.
    • client 의 request들과 다른 site들로 부터의 replication traffic 을 받는다.
    • 각각의 proxy 는 모든 JunoDB storage server instance들에게 연결되고,
    • 각 request 를 storage server instance들의 group 으로 전달한다.
    • 이 group은 shard mapping 을 기반으로 만들어져 있다.
    • ETCD에서 유지된다. 이 ETCD는 JunoDB cluster 설정(configurations)들을 저장하는 data store 다.
  3. JunoDB storage server
    • JunoDB storage server instance들은 proxy 로 부터 오는 작업 요청들을 수락하고 RocksDB 를 이용해서 memory 또는 영구 저장소(persistent storage)에 data 를 저장한다. 각 storage server instance 는 shard들의 집합을 담당해서 원활하고, 효과적인 data 저장과 관리를 보장한다.

Scalability 달성

PayPal 은 십수년전에 지속적인 빠른 고객 및 결제 건수의 성장을 지원하기 위해, application layer 에서 horizontally scale 되는 마이크로서비스들의 구조로 변경했다. 그래서 키-값 저장소로 지속적으로 인바운드 연결 수가 증가했다.

필요한 규모를 처리하기 위한 상용 또는 오픈 소스 솔루션이 제공되지 않았기 때문에, 직접만들었다.

분산 key-value store들에서 2가지 주요한 scaling 요구를 처리한다.

  1. 성장하는 클라이언트 연결 수를 수용하여, 원활한 액세스와 시스템 응답성을 보장합니다.
  2. 데이터 양과 액세스 속도가 증가함에 따라 데이터 증가을 처리하고 효율적인 읽기 및 쓰기 처리량(throughput)을 보장한다.

Scaling for Client Connections

  • 가로 연결 확장을 용이하게 하기 위해 proxy 기반 아키텍처가 선택되었다.
  • 이 구성에서 클라이언트는 가벼워서 모든 저장 노드와 연결을 설정할 필요가 없다.
  • 클라이언트 연결이 한도에 도달하는 경우, 추가적인 프록시를 간단히 추가할 수 있다.
  • 이 방법은 약간의 지연을 초래할 수는 있지만, 확장성에 매우 효과적인 해결책을 제공한다.

데이터 크기에 대한 Scaling 과 Access 처리량

  • 데이터 크기가 커짐에 따라, 효율적인 저장 및 검색을 보장하기 위해 ‘데이터 파티셔닝’ 방식을 통해 데이터를 여러 storage nodes 또는 servers에 분산하는 것이 필수적이다.
  • JunoDB는 일관된 해싱을 활용하여 고정된 파티션(샤드)을 효과적으로 분할하며, 이러한 샤드는 샤드 맵을 사용하여 물리적인 저장 노드에 할당된다.
  • 클러스터 내 노드 수가 ‘추가’ 또는 ’제거’로 인해 변경될 때는 오직 적은 수의 샤드만 다른 저장 노드로 재할당되어야 한다. 또한, 각 샤드 내에, 마이크로 샤드(micro-shard)를 도입했다. 이 micro-shard는 데이터 재분배의 기본 구성 요소로 작용한다.
  • 전체 샤드 수는 충분히 크고 클러스터의 수명 동안 일정하게 유지되어야만 한다.
  • 실무에서는 일반적으로 1,024개의 샤드를 사용한다. 샤드 맵은 사전에 생성되어 ETCD에 저장된다.
  • 샤드 맵의 변경은 데이터 재분배 처리(data redistribution process)를 일으킨다.
  • 우리의 효율적인 데이터 재분배 과정은 JunoDB 클러스터를 빠르게 점진적으로 확장하여 트래픽 성장에 대응할 수 있게 한다.
  • 현재, 대규모 JunoDB 클러스터는 200개 이상의 저장 노드로 구성될 수 있다. 이것은 하루에 1000억 건 이상의 요청을 처리한다.

고가용성 (Ensuring Availability)

  • JunoDB storage node들은 논리적인 grid 로 구성된다.
  • 각 열(column)은 zone이고, 각행(row)은 storage group 이다.
  • 데이터는 shard로 partitioned되고 storage group에 할당된다.
  • 저장 그룹 내에서 각 샤드는 quorum protocol 에 기반해서 다양한 zone으로 동기적으로(synchronously) replicated 된다.

quorum protocol 은 저장 그룹 내에서 값에 대한 합의(consensus)를 도출하기 위해 사용한다. 데이터 일관성을 보장하기 위해 다음 2가지 rule을 준수해야 한다.

quorum protocol :

  1. read quorum(R)과 write quorum(W)의 합이 존의 수(N)보다 커야 합니다: W+R > N.
    • 이는 read quorum이 데이터의 가장 최신 버전을 포함하는 노드를 적어도 하나 이상 포함하도록 보장합니다.
  2. write quorum은 존(zone) 수의 절반보다 많아야 합니다: W > N/2.
    • 이는 동시에 두 개의 쓰기 작업이 동일한 키에 대해 수행되는 것을 방지합니다. 일반적으로 PayPal은 5개 zone, read quorum 3, write quorum 3의 구성을 사용합니다.

노드 오작동이 발생하는 경우, JunoDB의 장애 조치 프로세스는 자동적이고 즉각적으로 이루어지므로 리더를 다시 선출하거나 데이터를 재분배할 필요가 없습니다.

프록시는 연결이 끊기거나 읽기 시간 초과를 통해 노드 장애를 감지할 수 있으며, 일반적으로 읽기 시간 초과가 100밀리초 이하로 구성됩니다.

JunoDB는 동일한 행/그룹 내에서 두 번 이상 장애가 발생하지 않는 한 클러스터에서 여러 노드 장애를 견딜 수 있습니다.

또한, 이러한 설계를 통해 다운타임 없이 소프트웨어 또는 OS 업그레이드와 같은 유지보수 목적으로 전체 영역을 오프라인으로 전환할 수 있습니다.

Cross-data center replication between clusters

데이터센터 간 복제도 구현, 각 클러스터의 proxy들 사이의 데이터를 비동기적으로 복제 하도록 해서 구현했다.

Performance at Scale

  • JunoDB는 가장 부하가 높은 시점에서(the most demanding workloads)도 한 자릿수 밀리초의 응답 시간과 원활한 사용자 경험을 유지하도록 해준다.
  • 애플리케이션이 성능 저하 없이 선형 확장성(linear scalability)을 달성할 수 있도록 지원하여, 높은 처리량과 짧은 지연 시간을 제공한다.
  • junodb/juno_performance_bench.md at main · paypal/junodb · GitHub

High security

  • JunoDB 시스템은 ‘전송 중인 데이터’(in transit)와 ‘미사용 데이터’(at rest)를 모두 보호하도록 설계
  • 전송 중 데이터 보안을 위해 TLS 가 활성화 되어 있다.
    • 클라이언트 <–> proxy 사이
    • 다른 데이터 센터에 위치한 proxy 사이(데이터 복제를 할 때)
  • 페이로드 암호화는 클라이언트 또는 proxy 수준(스토리지 서버 외부)에서 수행되어 동일한 데이터가 여러번 암호화되는 것을 방지한다. 이상적으로는, 암호화는 클라이언트 측에서 수행돼야만 하지만, 클라이언트가 실행하지 않는 경우 proxy는 메타데이터 플래그를 통해 이를 감지하고 암호화를 수행한다.
  • 스토리지 서버가 받아서 스토리지 엔진에 저장되는 모든 데이터는 암호화되어 유휴 상태에서의 보안을 유지(maintain security).
  • Key management module
    • TLS, 세션, 암호화 키 배포를 위한 인증서를 관리하고
    • 키 교체(key rotation)를 용이하게 하기 위해 사용된다.

junodb 빌드방법

See Also

  1. https://github.com/paypal/junodb/tree/main

Reference

  1. Unlocking the Power of JunoDB: PayPal’s Key-Value Store Goes Open-Source | by Yaping Shi | The PayPal Technology Blog | May, 2023 | Medium

[컴] 인프라 참고자표 from gruntwork

grunt / grunt work / best practice infra / infrastructure

인프라 참고자표 from gruntwork

gruntwork 라는 곳에서 대규모 아키텍쳐에 대한 best practice 를 공유한다. 항상 이것이 정답은 아닐 수 있으나 참고하면 좋은 구조일 듯 싶다.

[컴] Terraform 사용법

helloworld / hell world / 테라폼 예시 / 예제 / 간단 예시

Terraform 사용법

prerequisite

  1. AWS CLI 가 설치돼야 한다.
  2. AWS secret key 도 설정돼야 한다.
  3. terraform

기본적으로 .tffile 을 수정하고, terraform apply 를 하면 infra 에 반영된다. terraform apply을 하면, 일단 변경사항이 어떤 것들인지 보여주고, 적용할 것인지를 묻는다. 그 때 yes 를 하면 실제 적용이 이뤄진다.

  1. 다음처럼 main.tf를 하나 만들고,
  2. terraform init 을 한다.
    • 다음 내용들이 만들어진다.
      • .terraform.lock.hcl : lock file, provider version 들이 적혀있다.
      • .terraform/ : download 해서 install 한 provider 가 들어가 있다.(.exe file)
  3. terraform fmt : formatting 을 해준다. format 에 변경이 있는 file 이름만 stdout 으로 보인다.
  4. terraform validate : 문법확인
  5. terraform apply : code를 보고, 실제 infra 를 생성해준다.
    • terraform.tfstatae file
      • 그러면 이 때 생기는 instance 정보를 다시 가져와서 terraform.tfstataefile에 넣어준다.
      • terraform 은 현재 instance 의 정보를 이 state file(terraform.tfstatae) 로 판단하기 때문에 aws web console 로 변경하면 이 state file 이 out-of-sync 가 된다.
      • 그리고 보안도 중요하다. 그래서 이 파일(terraform.tfstatae)에 대한 접근은 제한적인 사람만 access 할 수 있게 해야 한다.
      • terraform show 를 하면 이 파일의 내용을 보여준다.
  6. terraform state list : 이것을 하면 terraform state file 에 있는 resource 의 list 를 보여준다.
  7. terraform destroy : 만들어 놓은 infra 를 삭제한다. dependency 가 있으면, 그에 맞는 순서대로 terraform 이 삭제해 준다.
  8. terraform output : .tf에서 output 으로 정의한 것들을 보여주게 된다.
# main.tf
# ref: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region  = "ap-northeast-2"
}

resource "aws_instance" "myserverid1" {
  ami           = "ami-055437ee4dcf92427"
  instance_type = "t2.micro"

  tags = {
    Name = "my-instance-name-namh"
  }
}

# 이 부분을 따로 variable.tf 등으로 만들어도 상관없다. 동작은 같다.
variable "instance_name" {
  description = "Value of the Name tag for the EC2 instance"
  type        = string
  default     = "my-instance-name-namh"
}

# 이 부분을 따로 output.tf 등으로 만들어도 상관없다. 동작은 같다.
output "instance_id" {
  description = "ID of the EC2 instance"
  value       = aws_instance.namh-test.id
}
d:\a\terrform\test>terraform fmt
hello.tf

d:\a\terrform\test>terraform validate
Success! The configuration is valid.

terraform 에서 infra 변경

만약 위 main.tf 에서 ami 부분을 변경하면, terraform 은 현재 떠 있는 instance 에서 ami 를 변경할 수 없기 때문에 기존의 것을 삭제 하고 새로운 ami 로 instance 를 만들게 된다.

resource "aws_instance" "myserverid1" {
  ami           = "ami-0676d41f079015f32"
  instance_type = "t2.micro"
d:\a\terrform\test>terraform apply
aws_instance.namh-test: Refreshing state... [id=i-05b38172e99a351bd]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # aws_instance.namh-test must be replaced
-/+ resource "aws_instance" "namh-test" {
      ~ ami                                  = "ami-055437ee4dcf92427" -> "ami-0676d41f079015f32" # forces replacement
      ~ arn                                  = "arn:aws:ec2:ap-northeast-2:786531771744:instance/i-05b38172e99a351bd" -> (known after apply)
      ~ associate_public_ip_address          = true -> (known after apply)
      ~ availability_zone                    = "ap-northeast-2a" -> (known after apply)
      ~ cpu_core_count                       = 1 -> (known after apply)
      ~ cpu_threads_per_core                 = 1 -> (known after apply)
      ~ disable_api_stop                     = false -> (known after apply)
      ~ disable_api_termination              = false -> (known after apply)
      ~ ebs_optimized                        = false -> (known after apply)
      - hibernation                          = false -> null
      + host_id                              = (known after apply)
      + host_resource_group_arn              = (known after apply)
      + iam_instance_profile                 = (known after apply)
      ~ id                                   = "i-05b38172e99a351bd" -> (known after apply)
      ~ instance_initiated_shutdown_behavior = "stop" -> (known after apply)
      ~ instance_state                       = "running" -> (known after apply)
      ~ ipv6_address_count                   = 0 -> (known after apply)
      ~ ipv6_addresses                       = [] -> (known after apply)
      + key_name                             = (known after apply)
      ~ monitoring                           = false -> (known after apply)
      + outpost_arn                          = (known after apply)
      + password_data                        = (known after apply)
      + placement_group                      = (known after apply)
      ~ placement_partition_number           = 0 -> (known after apply)
      ~ primary_network_interface_id         = "eni-0024d58ca1afb689e" -> (known after apply)
      ~ private_dns                          = "ip-172-31-6-77.ap-northeast-2.compute.internal" -> (known after apply)
      ~ private_ip                           = "172.31.6.77" -> (known after apply)
      ~ public_dns                           = "ec2-52-78-198-216.ap-northeast-2.compute.amazonaws.com" -> (known after apply)
      ~ public_ip                            = "52.78.198.216" -> (known after apply)
      ~ secondary_private_ips                = [] -> (known after apply)
      ~ security_groups                      = [
          - "default",
        ] -> (known after apply)
      ~ subnet_id                            = "subnet-d1f1b0b8" -> (known after apply)
      ~ tags                                 = {
          ~ "Name" = "ExampleAppServerInstance" -> "namh-test"
        }
      ~ tags_all                             = {
          ~ "Name" = "ExampleAppServerInstance" -> "namh-test"
        }
      ~ tenancy                              = "default" -> (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      ~ vpc_security_group_ids               = [
          - "sg-14baa97d",
        ] -> (known after apply)
        # (4 unchanged attributes hidden)

      ~ capacity_reservation_specification {
          ~ capacity_reservation_preference = "open" -> (known after apply)

          + capacity_reservation_target {
              + capacity_reservation_id                 = (known after apply)
              + capacity_reservation_resource_group_arn = (known after apply)
            }
        }

      - credit_specification {
          - cpu_credits = "unlimited" -> null
        }

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + tags                  = (known after apply)
          + throughput            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      ~ enclave_options {
          ~ enabled = false -> (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      ~ maintenance_options {
          ~ auto_recovery = "default" -> (known after apply)
        }

      ~ metadata_options {
          ~ http_endpoint               = "enabled" -> (known after apply)
          ~ http_put_response_hop_limit = 1 -> (known after apply)
          ~ http_tokens                 = "optional" -> (known after apply)
          ~ instance_metadata_tags      = "disabled" -> (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_card_index    = (known after apply)
          + network_interface_id  = (known after apply)
        }

      ~ private_dns_name_options {
          ~ enable_resource_name_dns_a_record    = false -> (known after apply)
          ~ enable_resource_name_dns_aaaa_record = false -> (known after apply)
          ~ hostname_type                        = "ip-name" -> (known after apply)
        }

      ~ root_block_device {
          ~ delete_on_termination = true -> (known after apply)
          ~ device_name           = "/dev/xvda" -> (known after apply)
          ~ encrypted             = false -> (known after apply)
          ~ iops                  = 100 -> (known after apply)
          + kms_key_id            = (known after apply)
          ~ tags                  = {} -> (known after apply)
          ~ throughput            = 0 -> (known after apply)
          ~ volume_id             = "vol-0e01fc659049ed2d4" -> (known after apply)
          ~ volume_size           = 8 -> (known after apply)
          ~ volume_type           = "gp2" -> (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 1 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_instance.namh-test: Destroying... [id=i-05b38172e99a351bd]
aws_instance.namh-test: Still destroying... [id=i-05b38172e99a351bd, 10s elapsed]
aws_instance.namh-test: Still destroying... [id=i-05b38172e99a351bd, 20s elapsed]
aws_instance.namh-test: Destruction complete after 30s
aws_instance.namh-test: Creating...
aws_instance.namh-test: Still creating... [10s elapsed]
aws_instance.namh-test: Still creating... [20s elapsed]
aws_instance.namh-test: Still creating... [30s elapsed]
aws_instance.namh-test: Creation complete after 31s [id=i-0ff6c6e6655dec3bc]

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

terraform destroy

d:\a\terrform\test>terraform destroy
aws_instance.namh-test: Refreshing state... [id=i-0ff6c6e6655dec3bc]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  - destroy

Terraform will perform the following actions:

  # aws_instance.namh-test will be destroyed
  - resource "aws_instance" "namh-test" {
      - ami                                  = "ami-0676d41f079015f32" -> null
      - arn                                  = "arn:aws:ec2:ap-northeast-2:786531771744:instance/i-0ff6c6e6655dec3bc" -> null
      - associate_public_ip_address          = true -> null
      - availability_zone                    = "ap-northeast-2a" -> null
      - cpu_core_count                       = 1 -> null
      - cpu_threads_per_core                 = 1 -> null
      - disable_api_stop                     = false -> null
      - disable_api_termination              = false -> null
      - ebs_optimized                        = false -> null
      - get_password_data                    = false -> null
      - hibernation                          = false -> null
      - id                                   = "i-0ff6c6e6655dec3bc" -> null
      - instance_initiated_shutdown_behavior = "stop" -> null
      - instance_state                       = "running" -> null
      - instance_type                        = "t2.micro" -> null
      - ipv6_address_count                   = 0 -> null
      - ipv6_addresses                       = [] -> null
      - monitoring                           = false -> null
      - placement_partition_number           = 0 -> null
      - primary_network_interface_id         = "eni-0ebdd0e3153dba51d" -> null
      - private_dns                          = "ip-172-31-5-167.ap-northeast-2.compute.internal" -> null
      - private_ip                           = "172.31.5.167" -> null
      - public_dns                           = "ec2-3-35-50-89.ap-northeast-2.compute.amazonaws.com" -> null
      - public_ip                            = "3.35.50.89" -> null
      - secondary_private_ips                = [] -> null
      - security_groups                      = [
          - "default",
        ] -> null
      - source_dest_check                    = true -> null
      - subnet_id                            = "subnet-d1f1b0b8" -> null
      - tags                                 = {
          - "Name" = "namh-test"
        } -> null
      - tags_all                             = {
          - "Name" = "namh-test"
        } -> null
      - tenancy                              = "default" -> null
      - user_data_replace_on_change          = false -> null
      - vpc_security_group_ids               = [
          - "sg-14baa97d",
        ] -> null

      - capacity_reservation_specification {
          - capacity_reservation_preference = "open" -> null
        }

      - credit_specification {
          - cpu_credits = "unlimited" -> null
        }

      - enclave_options {
          - enabled = false -> null
        }

      - maintenance_options {
          - auto_recovery = "default" -> null
        }

      - metadata_options {
          - http_endpoint               = "enabled" -> null
          - http_put_response_hop_limit = 2 -> null
          - http_tokens                 = "required" -> null
          - instance_metadata_tags      = "disabled" -> null
        }

      - private_dns_name_options {
          - enable_resource_name_dns_a_record    = false -> null
          - enable_resource_name_dns_aaaa_record = false -> null
          - hostname_type                        = "ip-name" -> null
        }

      - root_block_device {
          - delete_on_termination = true -> null
          - device_name           = "/dev/xvda" -> null
          - encrypted             = false -> null
          - iops                  = 3000 -> null
          - tags                  = {} -> null
          - throughput            = 125 -> null
          - volume_id             = "vol-0198723c2720811de" -> null
          - volume_size           = 8 -> null
          - volume_type           = "gp3" -> null
        }
    }

Plan: 0 to add, 0 to change, 1 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

aws_instance.namh-test: Destroying... [id=i-0ff6c6e6655dec3bc]
aws_instance.namh-test: Still destroying... [id=i-0ff6c6e6655dec3bc, 10s elapsed]
aws_instance.namh-test: Still destroying... [id=i-0ff6c6e6655dec3bc, 20s elapsed]
aws_instance.namh-test: Destruction complete after 30s

Destroy complete! Resources: 1 destroyed.

terraform output

d:\a\prog\terrform\test>terraform output
╷
│ Warning: No outputs found
│
│ The state file either has no outputs defined, or all the defined outputs are empty. Please define an output in your
│ configuration with the `output` keyword and run `terraform refresh` for it to become available. If you are using
│ interpolation, please verify the interpolated value is not empty. You can use the `terraform console` command to
│ assist.
╵

d:\a\prog\terrform\test>terraform apply
aws_instance.namh-test: Refreshing state... [id=i-0f9248e0f31df356f]

Changes to Outputs:
  + instance_id = "i-0f9248e0f31df356f"

You can apply this plan to save these new output values to the Terraform state, without changing any real
infrastructure.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes


Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

instance_id = "i-0f9248e0f31df356f"

d:\a\prog\terrform\test>terraform output
instance_id = "i-0f9248e0f31df356f"

기본 rds 생성하는 예제

rds 생성하는 terraform이다. 이것은 기존에 존재하는 security group 등을 사용했다.

# ref: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
}

provider "aws" {
  region = var.region
}


data "aws_availability_zones" "available" {}

resource "aws_db_parameter_group" "myproj" {
  name   = "myproj"
  family = "mariadb10.6"

  # parameter {
  #   name  = "log_connections"
  #   value = "1"
  # }
}

resource "aws_db_instance" "myproj-1" {
  identifier        = "myproj-1"
  instance_class    = "db.t3.micro"
  allocated_storage = 5
  engine            = "MariaDB"
  engine_version    = "10.6.12"
  username          = "myusername"
  password          = var.db_password
  # my-group-prod
  # https://ap-northeast-2.console.aws.amazon.com/rds/home?region=ap-northeast-2#db-subnet-group:id=my-group-prod
  db_subnet_group_name = "my-group-prod"
  vpc_security_group_ids = [
    "sg-0a0a0a0a0a0a0a0aa", # my security group 1
    "sg-0b0b0b0b0b0b0b0b"   # my security group 2
  ]
  # https://ap-northeast-2.console.aws.amazon.com/rds/home?region=ap-northeast-2#parameter-groups-detail:ids=mariadb-10-6-v220825;type=DbParameterGroup;editing=false
  parameter_group_name = "mariadb-10-6-v220825"
  publicly_accessible  = true
  skip_final_snapshot  = true
}


# ----------------------------------------
# variable
# ----------------------------------------

variable "region" {
  default     = "ap-northeast-2"
  description = "AWS region"
}

variable "db_password" {
  description = "RDS root user password"
  sensitive   = true
}

# ----------------------------------------
# output
# ----------------------------------------

output "rds_hostname" {
  description = "RDS instance hostname"
  value       = aws_db_instance.myproj-1.address
  sensitive   = true
}

output "rds_port" {
  description = "RDS instance port"
  value       = aws_db_instance.myproj-1.port
  sensitive   = true
}

output "rds_username" {
  description = "RDS instance root username"
  value       = aws_db_instance.myproj-1.username
  sensitive   = true
}

state 가 다른 방법으로 변경되었을 때 다시 state 를 맞추는 법

terraform plan -refresh-only

terraform 으로 만든후, web console로 변경하면, state 의 sync가 달라지게 된다. 그 때 다시 state 를 sync 하는 방법이다.

terraform plan -refresh-only 를 해서 변경되는 state 가 마음에 들면, terraform apply -refresh-only 를 하면 state file 내용이 변경된다. 이 때 infrastructure 를 적은 .tf 파일 내용은 변경되지 않는다.

terraform plan -refresh-only
terraform apply -refresh-only

terraform state mv

예시:

resource stage_b 를 stage_c 로 이름 변경하는 경우

resource "aws_instance" "stage_b" {
  ...
}
resource "aws_instance" "stage_c" {
  ...
}
terraform state mv aws_instance.stage_b aws_instance.stage_c

moved

stage_bstage_c로 변경한 경우 아래 처럼 써주고, terraform apply 를 해주면 된다. apply 를 하고나선 moved는 삭제되어도 된다.

resource "aws_instance" "stage_c" {
  ...
}

moved {
  from = aws_instance.stage_b
  to   = aws_instance.stage_c
}

만약 .tfstate 파일이 없을때.

만약 infra 가 존재하고 있고, .tfstate 가 없다면, 어떻게 해야 하는가?

이 때는 terraformer 를 사용하도록 하자. 개인적인 경험으로는, terraform import 를 이용하는 것이 낫다.

terraform import

먼저 terraform 에 import 할 내용을 일부 적는다. 그리고 import 를 하면 된다.

아래는 ec2 예시다. ec2 에서는 id 를 사용하고, 다른 곳에서는 id 외의 것을 사용할 수도 있다.

팁: 일단 그냥 시도해봐도 된다. 존재하지 않으면, 존재하지 않는다는 error 가 뜬다. AWS console 의 description 부분을 보면 된다. 그리고 terraformer 을 이용해도 id를 얻을 수 있다.

팁2: terraform console 을 이용하면, import 한 값들을 확인할 수 있다.

resource "aws_instance" "example" {
  # ...instance configuration...
}
terraform import aws_instance.example i-abcd1234

terraform 과 infra 의 연결을 끊으려면(detach)

cloud 에 있는 service 는 그대로 두고, terraform 의 resource 를 지우는 방법.

terraform state 에서 지우면 된다.

terraform state list
terraform state rm <name>

terraform.tfstate 를 s3 에 저장하는 법

# ref: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/aws-build
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.16"
    }
  }

  required_version = ">= 1.2.0"
  backend "s3" {
    bucket = "mys3bucket-name"
    key    = "global/ex/myapp/terraform.tfstate"
    region = "ap-northeast-2"
  }
}

instance 를 생성후 bash script 실행하는 법

resource "aws_instance" "linux_instance" {
  ...
  provisioner "file" {
    source      = "./mysetup.sh"      # local 에서의 bash script 위치
    destination = "/tmp/mysetup.sh"   # aws 에서의 위치
  }

  provisioner "remote-exec" {
    inline = [
      "chmod +x /tmp/mysetup.sh",  # 권한 변경
      "sudo /tmp/mysetup.sh",      # 실행
    ]
  }
  # Login to the ec2-user with the aws key.
  connection {
    type        = "ssh"
    user        = "ec2-user"
    password    = ""
    private_key = file(var.keyPath)
    host        = self.public_ip
  }
}

global variable 을 사용하는 법

AWS관련 다른 예시들

See Also

  1. 쿠…sal: [컴] terraform
  2. 쿠...sal: [컴] terraform 에서 ebs block 을 추가하고, user_data 사용하는 예시

[컴] reddit 의 ranking system 에 대한 이야기

recommendation system/ 추천 시스템 / 순위 / 랭킹 시스템 / 구조

reddit 의 ranking system 에 대한 이야기

출처: From Service to Platform: A Ranking System in Go : RedditEng

  • Reddit 의 ranking system 에 대한 이야기, unix pipeline 의 컨셉을 빌려왔다는 점이 흥미롭다.

[컴][머신러닝] GPT-3

이 글은 ref. 1 에 대한 일부 번역이다.

GPT-3

  • GPT-3는 일반적인 언어 기반 작업(tasks)을 해결할 수 있다. 전례없이 쉽게 글을 만들고, 분류하고 다른 글 스타일과 글 목적들 사이를 자유롭게 움직인다.
  • GPT-3 은 generic(포괄적인, 일반적으로 사용가능한) 언어 모델이다.

NLP(Natural Language Processing)

NLP 는 글(text)과 말하는 단어들을 사람과 같은 방식으로 이해하는 컴퓨터를 만드는 것과 관련한 AI 분야중 하나.

현재 NLP는 다음 것들을 가지고 있다.

  • 통계학적으로 계산을 요구하는 언어학 모델(사람 language modling 의 rule-based modeling)
  • 머신러닝 모델
  • 딥러닝 모델

NLP 는 ‘스팸처리’, 기계번역, 가상 도우미와 챗봇들, 소셜미디어 감정경향분석, 글자 요약 등에 사용

language model 은 자연어 처리(NLP) application 들에서 가장 중요한 구성요소이다. 통계학적인 예측 기계들로 생각하면 된다. 이 기계는 text 를 input 으로 주면, output 으로 예측되는 것을 준다. 폰에서 ‘자동완성’ 기능을 생각하면 된다.

GPT-3 이전에는 잘동작하는 generic language model 이 없었다. 언어 모델은 이미 존재하는 알고리즘 및 아키텍처를 사용해서 텍스트 생성, 요약 또는 분류와 같은 하나의 특정 NLP 작업를 수행하도록 설계되었다.

GPT-3

Generative model 들, 자체생산이 가능한 모델
이건 통계적 모델링의 한가지 이다.
수학적으로 세상을 어림짐작하는 방법
2개의 통계모델이 있다.
generative 와 discriminative

generative 모델들은 새로운 data 를 만들 수 있다.
Discriminative 모델들은 다른 종류의 data 를 식별할 수 있다.

generative model 을 훈련하려면, dataset 을 모으고,
이 dataset 은 모델이 특정 task 를 수행하는 법을 배우게 하는데 도움을 주는 예제들의 모음이다.
보통 특정 분야에 대한 많은 양의 data 이다. 예를 들면, ’차’가 무엇인지 알게 하기위해 차에 대한 몇백만개의 차 이미지들

Pre-trained model 들

잘 동작하는 model 을 만들려면, 그것을 ‘특정변수’들의 집합을 이용해서 훈련해야 한다. 이 특정변수가 parameter 이다.
model 내부에 있는 설정변수이다. 이 값들은 training data 로부터 측정된다.
모델의 이상적인 parameter 들을 결정하는 과정을 ’trainning’ 이라고 한다.
모델은 성공적인 trainning iteration 을 통해서 parameter 값들을 배운다.

pre-trained model 은 특정 문제를 풀기위해 만들어진 model 이다.
아무것도 없이 처음부터 model 을 만드는 대신에, 다른 문제에서 훈련된 model 을 이용할 수 있다.
pre-trained model 을 가져와서 좀더 구체적인 trainning 을 시켜줄 수 있다.

ML(machine learning) 에서 model 은 dataset 위에서 훈련된다. 해결하려는 task 에 따라 data sample 의 크기나 종류가 다양하다. GPT-3 는 5개의 dataset 들의 text corpus 를 이용해서 훈련됐다.

  • Common Crawl: petabyte 의 data, OpenAPI 에서 curated, filtered version 을 사용
  • WebText2 : WebText 의 확장버전, 내부의 OpenAI corpus 이다. 품질좋은 웹페이지 크롤링 해서 모은 자료다. 좋은 품질을 위해서 적어도 3개이상 karma 가 걸린 Reddit 에 걸린 outbound link 들을 scrap 했다. 450만 링크에서 800만이 넘는 문서에서 가져온 40GB 의 text 가 있다.
  • Book1, Book2 : 다양한 주제에 대한 많은 책에서 가져온 text 들
  • Wkikipedia : Wikipedia 에 있는 모든 영어 기사들, 대략 600만개 이상의 기사들

GPT-3 는 확장적인 다양한 text corpus에서 미리훈련됐다(pre-trained). GPT-3 는 놀랍도록 많은 NLP task 들을 성공적으로 수행한다. user가 추가적인 example data 를 제공하지 않아도 말이다.

Transformer model 들

2가지 종류의 신경 망

  • sequence-to-sequence model
  • transformer model

transformer model 의 아이디어는 [1706.03762] Attention Is All You Need 에서 나왔다.

sequence-to-sequence(Seq2Seq) 구조는 transformer model 의 근간(backbone) 이다.
Seq2Seq 는 문장의 단어(word) 같은 요소들의 순서를 바꿔서 다른 sequence 로 바꿔준다.
이 seq2seq 는 ‘text 요약’과 ’image captioning’ 에서 큰 성공을 거둔상황이다.

Seq2Seq 는 특히 번역에서 특히 좋다.
번역에서는 특정 언어에서의 단어의 순서를 다른 언어의 다른 단어들 순서로 변환해준다.
Google translate 은 2016년 후반부터 비슷한 종류의 model 을 이용하기 시작했다.

Seq2Seq 는 2개의 model 로 이뤄져 있다. Encoder와 Decoder
Ecoder 와 Decoder 는 각각 2개의 언어를 번역할 수 있다. 2개가 갖는 공통적인 언어가 있어서 Encoder 가 B라는 언어를 읽어서 A라는 언어로 번역하면, 그것을 Decoder 가 읽고 그것을 C라는 언어로 번역한다.

transformers 와 attention mechanisms

Transformer architecture 또한 하나의 sequence를 다른 sequence 로 변환한다.
마찬가지로, Encoder와 Decoder의 도움을 받지만 기존의 seq2seq와 차이점이 있다.
Transformer architecture 는 2017년에 발명됐는데, 기계 번역 task 들에 대한 Ai들의 성능을 향상시키기 위해 발명됐다.

attention mechanism은 ‘인지적 주의집중’(cognitive attention) 을 흉내낸 기술이다.
예를 들면, 사람이 글을 읽을때, 현재 단어를 읽겠지만, 문맥을 제공하기 위해서 이전의 읽은 문장의 증요한 키워드를 기억하고 있다.

attention mechanism 도 비슷하다.
Encoder가 문장의 의미에 중요한 키워드를 적어서, 번역과 함께 Decoder에 제공한다
이런 키워드들이 Decoder가 번역을 더 쉽게 하게 해준다.
문장의 어떤 부분이 중요한지, 어떤 용어가 문맥을 제공하는지를 알 수 있게 됐기 때문이다.

attention mechanism 은 transformer 가 잡음을 걸러내고, 관련성에 더 집중하게 도와준다.
즉, 서로 어떤 관계가 있는지 표시되어 있지 않은 2개의 연관있는 단어들을 연결한다.

Transformer model 들은 더 거대한 구조와 더 거대한 양의 data 로 부터 이익을 얻는다.
큰 dataset 들을 이용한 훈련과 특정 task 들에 대한 fine-tuning(미세조정)은 결과를 향상시킨다.

GPT-3 는 Open API 형태로 제공된다.

GPT-2 는 15억개 parameter 들과 40GB 의 text 에서 훈련됐다.
GPT-3 는 1750억개의 parameter 들과 570 GB 의 text 에서 훈련됐다.
GPT-2 가 몇몇 후속 task 들에서 사용할 수 없었지만, GPT-3 는 심지어 예제 context 만 있는 좀더 이전에 보지못한 새로운 task(novel task) 들에서도 수행가능하다.

See Also

  1. GPT-3 🤖 (@gpt_three) / Twitter
  2. https://github.com/abhagsain/ai-cli : gpt 3를 이용해서 cli명령어에 대한 help를 가져오는 예제

Referenence

  1. GPT-3 Building Innovative NLP Products Using Large Language Models

[컴][os] /proc file system

리눅스 / linux proc / proc file

/proc file system

/proc file system 은 kernel 또는 kernel module 이 다른 process 에게 정보를 알려주기 위해 만들어졌다.[ref. 1, 2]

여기서는 ref. 2 의 source code 를 가지고 설명하려 한다.

우리는 create_proc_entry 함수를 호출해서, 내가 원하는 파일을 /proc 에 만들 수 있다.

만약 자신이 kernel module 을 만들었으면, module 의 시작점에 create_proc_entry 를 호출해서 /proc 에 자신의 process 이름을 만들어 놓으면 된다. 여기서는 그 이름을 helloworld 라고 하자.

그러면 다른 process 가 이 /proc/helloworld 를 read 할 수 있다. 간단하게 cat /proc/helloworld 를 할 수 있다. 마치 file 처럼.

단순한 file 이라면, 그냥 그 file 의 내용을 보여줄 것이다. 그런데 이것은 file 은 아니다. 그래서 어떤 내용을 보여줘야 할 지 정해줘야 한다. 즉, read 라는 command 가 올 때 어떤 동작을 하라고 지정해 줘야 한다. 그것을 ->read_proc 라는 변수에 지정해 주게 된다.(아래 code 참고)

// from ref. 2


int
procfile_read(char *buffer,
          char **buffer_location,
          off_t offset, int buffer_length, int *eof, void *data)
{
    ...
}

int init_module()
{
  ...
  Our_Proc_File = create_proc_entry("helloworld", 0644, NULL);
  ...
  Our_Proc_File->read_proc = procfile_read;

Reference

  1. Using proc For Input - Linux Kernel Programming - Halo Linux Services
  2. The /proc File System

[컴] DirectStorage

 

Direct Storage / 다이렉트 스토리지 기술 / 다이렉트 엑스 / 게이밍 / gaming / directx

DirectStorage

최근의 SSD 와 PCIe 기술으리 발전, 특히 NVMe 기술, 은 옛날 하드 드라이브 기술에서 가능한 것 보다 더 높은 대역폭을 전송하는 storage solution 들을 게이밍 pc가 갖게 해준다. “초당 몇십MB” 가 아니라, NVMe 는 “초당 수십GB” 를 가능하게 해준다.

많은 data 양을 shared memory/GPU accessible memory 에 맞추기 위해서는 data loading 최적화가 필요하다.

게임들은 이제, 큰 덩어리로 load 한번에 불러와서 매우 적은 IO request 를 사용하는 것 대신에, 텍스쳐(textures) 같은 asset 들을 작은 조각으로 쪼개서 현재 화면에서 rendering 되야 할 필요한 조각들만 불러온다. 이것은 더 많은 IO request 를 만들지만 좀 더 효율적으로 memroy 를 사용할 수 있고, 더 나은 화면(scene)을 보여준다.

하지만 기존 storage APIs 들은 이런 많은 IO request 에 적합하지 않다. 아주빠른 PC hw 를 사용하고, NVMe drive 를 사용해도, 게임들이 현재의 APIs 를 사용하면 완전히 IO 파이프라인을 사용하지 못하게 되고 귀중한 bandwidth(대역폭) 을 남겨놓게 된다.

그래서 DirectStorage for PC 가 나오게 된 것이다. 이 API 는 PC 게이밍에서 발전하는 storage 와 IO 전망에 대한 응답이다. DirectStorage 는 NVMe drives 이 있는 시스템들을 지원할 것이다.

새로운 api 에서 향상되는 분야는 2가지이다.

  1. 긴 load time 이 줄어줄고
  2. 게임이 이전보다 좀 더 detailed 하고 광활하게(expansive) 하게 된다.

이 2개는 결국 DirectStorage 가 가져오는 IO 시스템발전 에 의해 이뤄진다.

예전에는 초당 몇백개의 IO request 들이 가능했다. 이제는 초당 수만개의 IO request 가 가능하다. 만약 게임에서 50MB/s 로 asset streaming 을 해야한다라고 한다면, 64k block size 로 요청을 하는 경우에, 몇백번의 IO request 가 발생할 것이다. 그런데 NVMe 를 사용해서 더 빠른 asset streaming 를 사용한다고 하면 초당 수십GB 이기 때문에 수만개의 IO request 가 발생한다.

기존의 API 들은 application 이 각각의 request 를 다루게 했다. request 를 보내고, 끝나기를 기다리고, 그리고 그것의 완료를 처리하고 했다. 그런데 이것이 느린 HDD 에서는 큰문제가 안됐다. 하지만 NVMe 처럼 수만개의 IO 가 가능한 환경에서는 이렇게 하는 것은 부하가 된다.

게다가 보통 이 asset 들은 압축되어 있다. CPU 나 GPU 에서 사용하려면 압축을 풀어야 한다. DirectStorage 를 사용하는 것으로 최고의 최신 압축풀기(decompress) 기술과 향후 압축풀기 기술을 사용할 수 있다. 다음 frame 을 위해서 수천개의 block 들을 load 하고 decompress 하는 것이 필요하다. one-at-a-time model(한번에 하나씩 처리하는 model) 에서는 data block 이동의 다양한 지점에서 효율성의 저하가 나온다.

DirectStorage API 는 이 모든것을 고려해서 설계되었고, NVMe 드라이브에서 GPU까지 전체 파이프라인에서 성능을 극대화 한다. 이것은 다음의 방식을 통해 이뤄진다.

  1. request 당 NVMe 오버헤드를 줄이고,
  2. GPU 에게 효과적으로 공급할 수 할 수 있는 batched many-at-a-time 병렬 IO reuqests 를 가능하게 하고,
  3. 작은 IO 완료에 일일이 반응하는 대신에, IO request 완료 의 알림을 받을때 게임들에게 세부적인 제어를 줄 수 있다.

개발자들에게 많은 여러 명령들을 훨씬 효율적으로 더많은 IO request 들을 submit/handle 할 수 있는 효과적인 방법을 제공한다. 그리고 이것들이 궁극적으로 게임을 기다리는 시간을 줄이고, 더 큰, 더 자세한 가상세계를 가져오는 시간을 줄인다.(게임 캐릭터가 그 세계를 이동할 수 있을 만큼 빠르게)

NVME 를 사용하는 이유

NVMe queue라는 하드웨어 데이터 액세스 파이프를 가지고 있다. 드라이브에서 데이터를 가져오기 위해 OS가 드라이브에 요청을 제출하면 데이터가 이러한 queue을 통해 앱으로 전송된다. NVMe 디바이스에는 여러 개의 queue가 있을 수 있고, 각 queue에는 한 번에 많은 request 가 들어갈 수 있다. 최신 게임 작업량의 “병렬 및 일괄 처리 특성” 과 잘 맞는다. DirectStorage 프로그래밍 모델은 기본적으로 개발자에게 고도로 최적화된 하드웨어를 직접 제어할 수 있는 기능을 제공한다.

기존 스토리지 API는 IO 요청을 하는 애플리케이션과 storage 디바이스에서 요청을 이행하는 단계 사이에 많은 ’추가 단계’가 발생했다.(정상적인 IO 작동하는 동안 특정 부분에서 필요한 “데이터 변환”과 같은 것)

“DirectStorage는 지원되는 NVMe 드라이브”와 적절히 구성된 게이밍 기계에서는 이러한 추가 단계가 필요하지 않음을 미리 감지하고 불필요한 검사/작업을 건너뛰게 할 수 있다. 그래서 모든 IO 요청을 더 저렴하게 만들 수 있다.

Reference

  1. DirectStorage is coming to PC | DirectX Developer Blog, 2020-09-01

[컴] kafka 특징

카프카 / kafka

kafka 특징

ref.1 에 대한 번역 및 정리

  • Kafka 는 data stream 들에 적응하는 real-time streaming data pipeline 과 real-time streaming application 들을 만들때 사용한다.
  • Kafka 는 다음의 것들을 묶는다. 그래서 historical data 와 real-time data 에 대한 저장(storage)과 분석(analysis)이 가능해 진다.
    • messaging
    • storage
    • stream processing
  • data pipeline : 신뢰할 수 있게 data 를 처리하고 한 시스템에서 다른 시스템으로 이동시킨다.
  • streaming application : data 의 stream 을 사용하는 application 이다.

웹사이트의 사용자의 행동 데이터를 실시간(real-time) 으로 처리하는 data pipeline 이 있다면, kafka(카프카)는 다음 2개를 동시에 제공할 수 있다.

  • streaming data 를 소화하고(ingest) 저장 하면서
  • data pipeline 에 동력을 공급하는 application 에게 read 를 제공

Kafka 는 또한 message broker solution 으로도 이용되기도 한다.

message broker solution: 2개의 application 간에 통신을 처리하고, 중재하는 플랫폼이다.

from: see also. 1

zookeeper:

  • kafka는 cluster state 와 cluster cofiguration을 관리하기 위해 zookeeper 를 사용한다. zookeeper 없이는 kafka 를 clustering 으로 구성할 수 없다. 심지어, 1개의 kafka 를 사용할 때도 zookeeper가 같이 설치돼야 동작한다. 요즘 새버전은 이 zookeeper없이 clustering 을 가능하도록 변하고 있다.
  • What is ZooKeeper & How Does it Support Kafka? - Dattell

kafka 동작

Kafka 는 각 model 의 주요 이점을 사용자에게 제공하기 위해서 다음 2개의 messaging model 을 결합했다.

  • queueing
  • publish-subscribe

기존 queue, publish-subscribe 의 문제

  • 기존의 queue 는 multi-subscriber(하나의 queue 에서 하나의 message 를 여러명이 가져가는 구조) 가 되지 않았다.
  • 기존의 publish-subscribe 은 모두 subscriber 에게 같은 message 를 뿌리는데 그래서 work 를 여러 worker process 로 분산시키는 작업은 안됐다.

partitioned log model

그래서 kafka 가 partitioned log model 을 사용해서 이 solution 들을 묶었다.

kafka는 “record들을 다른 topic 들에게 publishing 하는 방법”(by publishing records to different topics) 으로 2개의 다른 model의 문제점을 개선한다. 각 consumer 는 topic 안에 있는 partition 에 할당된다. 이것이 data 의 순서를 유지하는 동시에 multi-subscribers 를 가능하게 해준다.

  • log 는 record 들의 ordered sequence (정렬된 순서)이다.
  • 이 log 들은 다른 subscriber 들에 대응되는 segment 또는 partition 으로 쪼개서 넣어질 수 있다.
  • 같은 topic 에 대해서 여럿의 subscriber 들이 있을 수 있고,
  • 각 topic 은 더 높은 수준의 scalability 를 가능하게 하기위해 partition 에 할당된다.
  • 이 partition 들은 여러 서버에 분산되어지고, 복제되어진다.
    • 이것이 높은 확장성, fault-tolerance, 병령성(parallellism)을 가능하게 해준다.
  • kafka 의 model 은 replayability 를 제공한다.
    • application 마다 독립적으로 read 가능: data stream 에서 read 하는 여러 독립적인 application 들이 독립적으로 그들의 속도로 일할 수 있게 해준다.

partitioned log

  • 각 topic 은 partitioned log 를 갖는다.
  • 이 partitioned log 는 구조화된 commit log 이다.
  • 이 commit log 는 모든 record 들을 순서대로 추적하고 실시간으로 새로운 record 를 append 한다.
kafka broker 는 kafka server 를 이야기 한다. (참고)

 

kafka 사용시 이점

  • scalable : partitioned log model 이라서 여러 서버에 data 를 분산시켜 저장할 수 있다. 그래서 한개의 서버를 넘어서는 확장이 가능하다.
  • fast : data stream 과 분리되어 있다. 그래서 latency 가 매우 낮기에 매우 빠르다.
  • durable : partition 들은 여러서버에 분산되어지고, 복제되어 있다.(replicated) 그리고 모든 data 들은 disk 에 써져있다. 이것이 server failure 를 막아준다. data를 매우 fault tolerant 하고 durable 하게 해준다.

storage system 처럼 동작

  • Kafka는 또한 모든 데이터를 디스크에 쓰고 복제하기에 확장성과 fault-tolerance가 뛰어난 storage system 처럼 동작힌다.
  • Kafka 는 disk space를 다 쓸때(run out of)까지는 data를 disk 에 보관한다.
  • 하지만 user가 retention limit (정체 한도)을 정할 수 있다.

kafka가 제공하는 4개의 api

  • Producer API: Kafka topic 으로 “records 의 stream” 을 publish 하기 위해 사용된다.
  • Consumer API: topic들에 subscribe 하고 그들의 “records 의 stream” 을 처리하기 위해 사용한다.
  • Streams API: applications 이 stream processors 처럼 행동하는 것을 가능하게 한다.
    • stream processor : topic(s)으로 부터 input stream을 받아 다른 topic(s)로 가는 output stream 로 변환해서 보낸다.(me: 한 topic 에서 data 를 받아서 그것을 다른 topic 으로 넘겨주는 역할을 이야기하는 듯 하다.)
  • Connector API: user 가 이 api 를 사용해서 현재 kafka topic들에 다른 application 또는 다른 data system 을 추가하는 것을, 중간에 끊김이 없이(seamlessly) 자동으로, 할 수 있다.

See Also

  1. Best Practices for Running Apache Kafka on AWS | AWS Big Data Blog
  2. Kafka 와 Confluent Schema Registry 를 사용한 스키마 관리 #1 | by Junghoon Song | Medium : message 를 전달할때 일정한 format (schema)을 맞춰서 보내고 받아야 하는데, 이 schema 를 dynamic 하게 변경할 수 있게 해주는 Schema Registry 를 소개한다. GraphQL 과 비슷한 concept 이라고 보면 될 듯 하다.
  3. LINE에서 Kafka를 사용하는 방법 - 1편 - LINE ENGINEERING: line 에서 "데이터 허브"로 kafka 를 사용한다. 가혹한 작업 부하(work load)에서 보호하는 방법으로 request quota 를 조절하는 것이 중요하다고 한다.
  4. Apache Kafka(카프카)의 특징 및 모델
  5. 쿠...sal: [컴] kafka 설치하기

Reference

  1. What is Apache Kafka? | AWS

[컴] RDD, Resilient Distributed Datasets

rdd / spark rdd / apache spark / 아파치 스파크 rdd 란? / rdd 의 의미

RDD, Resilient Distributed Datasets

ref. 1 의 내용을 정리했다.

RDD

Spark의 핵심 아이디어는 RDD (Resilient Distributed Datasets) 이다. Spark는 RDD 개념을 사용하여 더 빠르고 효율적인 MapReduce 작업을 수행한다.

  • RDD (Resilient Distributed Dataset)는 Spark의 기본 ‘자료구조’(data structure)다.
  • 변하지 않는 분산 된 객체 모음 이다.(immutable distributed collection of objects)
  • RDD의 각 데이터 세트는 ’클러스터의 다른 노드에서 계산 될 수있는 논리 파티션’으로 나뉜다.
  • RDD는 사용자 정의 클래스를 포함하여 모든 유형의 Python, Java 또는 Scala 객체를 담을 수 있다.


RDD 생성방법

RDD를 생성하는 방법은 다음 2가지가 있다.

  1. 드라이버 프로그램의 기존 컬렉션을 병렬화
  2. 외부 스토리지 시스템의 dataset 를 참조(referencing)
    • 외부 스토리지 시스템
      • 공유 파일 시스템
      • HDFS
      • HBase
      • Hadoop Input Format 을 제공하는 모든 데이터 소스

기존의 framework 에서 MapReduce

MapReduce는 클러스터에서 병렬 분산 알고리즘을 사용하여 대규모 데이터 세트를 처리하고 생성하는 데 널리 채택된다. 이를 통해 사용자는 작업 분산(work distribution) 과 fault tolerance에 대해 걱정할 필요없이 high-level 연산자 set (set of high-level operators)을 사용하여 병렬 계산(write parallel computations)을 write 할 수 있다.

  • 대부분의 현재 프레임 워크에서 계산들 사이에서 (Ex-두 MapReduce 작업 사이) 데이터를 재사용하는 유일한 방법은 외부의 stable storage system (ex: HDFS)에 데이터를 write 하는 것이다.
  • 반복(iterative) 및 대화형(interactive) 애플리케이션 모두 병렬 작업을 할 때 더 빠른 데이터 공유가 필요하다.
  • 복제(replication), 직렬화(serialization), 디스크 IO(disk IO)로 인해 MapReduce에서 데이터 공유는 속도가 느리다.
  • 스토리지 시스템과 관련하여 대부분의 Hadoop 애플리케이션은 HDFS read-write operation에서 시간의 90 % 이상이 소요된다.

MapReduce 에서 반복적인 작업(iterative operations)

다음 그림은 현재 framework 들이 동작하는 방식을 보여준다. 다음 3개의 동작으로 인해 상당한 overhead 가 발생하고, 그로인해 시스템 속도가 느려진다.

  • data replication
  • disk I/O
  • serialization
from: https://www.tutorialspoint.com/apache_spark/apache_spark_rdd.htm

MapReduce 에서 대화형 작업(interactive operations)

사용자는 ’같은 data 의 하위집합(subset)’에서 여러 query 들을 실행하게 된다. 그런데 이 query 를 실행할 때마다 data 를 HDFS 에서 읽어온다. 즉 disk I/O 가 발생한다. 그래서 이것이 시간을 잡아먹는다.

from: https://www.tutorialspoint.com/apache_spark/apache_spark_rdd.htm

Apache Spark RDD

Apache Spark 는 그래서 in-memory processing computation(메모리내 처리 계산) 을 지원한다. Spark 는 작업전반에 걸쳐 ‘현재 메모리 상태’(state of memory) 를 object 로 저장한다 그리고 이 object 는를 작업들(jobs) 사이에서 공유가 가능하다(sharable). 메모리에 있는 data 를 공유하는 것은 ‘네트워크' 와 'disk’ 보다 10~100배는 빠르다.

Spark RDD에서 반복적인 작업(iterative operations)

  • 중간결과를 stable storage 에 계산하는 대신 ‘분산 메모리(distributed memory)’ 에 저장한다.
  • 메모리가 부족하면 disk 에 적는다.
from: https://www.tutorialspoint.com/apache_spark/apache_spark_rdd.htm

Spark RDD에서 대화형 작업(interactive operations)

  • 만약에 여러개의 다른 query 들이 '같은 data set' 에서 실행되면, 이 특정 data 는 메모리에 저장된다. 그래서 실행속도가 빨라지게 된다.
  • 기본적으로 RDD는 실행할 때마다 새롭게 만들어지지만, 유저가 RDD 를 memory 에 유지시킬 것이다.(persist) 이 때에는 다음 query 시점에서 더 빠른 access 를 가능하게 하기위해, Spark 가 cluster 주위의 요소들을 유지할 수 있다.
  • 유지하려는 RDD(persisting RDDs) 를 disk 에 저장하거나, 여러 노드들에 복사하는 방법도 가능하다.
from: https://www.tutorialspoint.com/apache_spark/apache_spark_rdd.htm

See Also

  1. 쿠…sal: [컴] Apache Spark
  2. Using Apache Spark and MySQL for Data Analysis, 2015-10-07
    • 압축된 big data 를 spark 를 이용해서 바로 query 를 적용하는 내용
  3. RDD Programming Guide - Spark 3.1.2 Documentation
  4. 쿠...sal: [컴] RDD programming

Reference

  1. Apache Spark - RDD - Tutorialspoint

[컴] ReactiveX

비동기 프로그래밍 / async / asynchronous programming / reactor pattern /

ReactiveX

다양한 언어에서 Observe Pattern 이 가능하도록 해준다.

wiki 에서의 설명은 ReactiveX는 observable streams 를 이용한 asynchronous programming 을 가능하게 해주는 API 라고 이야기 한다.

중요한 부분은 결국 observable streams 를 이용한다는 사실인 듯 하다. 단순히 asynchronous programming 을 하게 해주는 것이라면 그것은 nodejs 같은 다른 언어에서 async/await 을 사용하는 것과 유사하다. 하지만 observable stream 을 구현해서 그것을 사용할 수 있도록 api 를 구성했기에 관련한 여러 api 들이 존재하는 것 같다.

Documentation

구체적인 이유는 알 수 없지만, ReactiveX 가 언어별로 따로 개발되는 듯 하다. 그래서 몇몇 언어들은 문서화가 잘 안되어 있기도 하다. 그래서 일단 어느정도 사용법이 나와있는 문서들의 링크를 모아본다.

See Also

  1. ReactiveX - Wikipedia
  2. 쿠...sal: [컴] Project Reactor
  3. 쿠…sal: [컴] Rx.js 에서 Observable.subscribe 의 동작

Reference

  1. The Reactive Extensions for Python (RxPY) — RxPY Documentation

[컴][네트워크] 2020월 12월 14일 구글의 서비스의 접속 에러 원인

 장애 / 중단 / 구글 중단 / outage

2020월 12월 14일 구글의 서비스의 접속 에러 원인

2020월 12월 14일 47분 가량 구글 서비스가 접속이 안됐다. 그 원인에 대한 설명이 Google Cloud Status Dashboard 에 올라와서 번역을 해 봤다.


Google User ID Service 가 하는 일

  • 모든 계정에 대해서 unique 한 id 를 가지고 있다. 
  • OAuth token 들과 cookie 들을 위한 접속자격 증명서(authentication credentials) 을 다룬다.
  • 계정관련 data(account data) 를 분산된 db 에 저장한다. 이 분산db 는 update 의 동기화등을 위해서 Paxos protocol 을 사용한다.
  • 보안을 위해서 이 서비스는 outdated data 를 발견하면 requests 를 거부한다.

구글은 서비스들에 할당(allocate) 된 다양한 resource 들의 몫(quota)을 관리하는데 자동화툴을 사용한다.

User ID Service 은 새로운 quota system 으로 migration 이 진행되고 있다.

"quota 제한"을 집행하는 것에 대한 "기존의 유예기간(grace period)"은 이 이슈가 발생하는 것을 지연시켰다. 그리고 그것은 결국 유예기간이 종료됐고, 자동화된 quota system 들이 User ID service에게 허락된 quota 를 감소시키는 것을 시작하게 했고 이 사건을 일으켰다.

기존의 safety checks 은 많은 의도되지 않은 quota 변경들을 막기 위해 존재하지만 "하나의 service 에 대한 0으로 보고된 부하(zero reported load for a single service)"의 시나리오에 대해서는 커버하지 않았다.

  • Quota changes to large number of users: since only a single group was the target of the change
  • Lowering quota below usage, since the reported usage was inaccurately being reported as zero
  • Excessive quota reduction to storage systems, since no alert fired during the grace period
  • Low quota, since the difference between usage and quota exceeded the protection limit.

결과적으로, account database 의 quota 가 줄어들었고 그것이 Paxos leader 가 writing 하는 것을 막았다. 바로 그 후에 read operations 의 많은 수가 outdated 가 되었고 이것이 authentication lookups에 대한 error를 만들었다.

Reference

  1. Google Cloud Infrastructure Components Incident #20013 | Google Cloud Status Dashboard

[컴] Akka Streams

 

아래 내용을 일부 번역했다. 

Akka Streams 의 핵심 개념

  • Akka Streams 는 bounded buffer space 를 사용해서 sequence of elements 를 처리하고, 전송하기 위한 library 이다. 이 bounded buffer space 를 사용하는 속성은 우리가 boundedness 라고 부르는 것이다.
  • 그리고 이것은 Akka Streams 의 특징을 정의하고 있다.
  • entity 들을 처리하는 것의 chain 을 표현하는 것이 가능하다.(graphs)
  • 이 각각의 entity 들은 각각 독립적으로 수행된다. 그리고 동시에 수행 가능하다.
  • 반면에 특정시간에 단순히 제한된 elements의 수를 buffering 한다.
    • bounded buffer 들의 이 속성은 actor model 과의 다른점 중 하나이다.
  • 일반적으로 각각의 actor 는 unbounded mailbox 나 bouned 지만 dropping mailbox 를 가지고 있다.
  • Akka Streams processing entity들은 drop 을 하지 않는 bounded mailbox 들을 가지고 있다.

Stream

  • data를 움직이고, 전송하는 active process

Element

  • stream 의 processing unit
  • 모든 operation들은 elements의 모습을 변경하거나 upstream 에서 downstream 으로 전송한다.
  • Buffer 사이즈들은 언제나 elements의 수로서 표현된다. elements의 실제 사이즈와 상관없이

Back-pressure

  • flow-control 의 방법
    • flow-control :consumer 가 그들의 현재 availability 를 producer 에게 알리는 방법
  • 효과적으로 the upstream producer 를 slowing down 한다. 그래서 consumption speed 들을 맞춘다.
  • Akka Streams 의 context 에서 back-pressure는 언제나 non-blocking 과 비동기로 이해되어진다.

Graph

  • 그래프는 pathway 들을 정의해서 stream processing topology 를 설명한다.
  • stream 이 실행되고 있을 때, 이 그래프를 통해서 elemnt 들은 flow 해야 한다.

Operator

streams 을 정의하고, 실행하는 것

linear processing pipeline 은 Akka Streams 에서 다음과 같은 core concept 을 사용해서 표현되어질 수 있다.

Source

  • 하나의 output 을 갖는 operator 이다.
  • downstream operator 들이 data elements을 receive 할 준비가 되면 항상 data elements 를 방출한다.

Sink

  • 하나의 input 을 갖는 operator 이다.
  • data elements를 request 하고, accept 하는
  • element 들의 upstream producer 를 slowing down 하는 것이 가능하다.

Flow

  • 하나의 input 과 output 을 갖는 operator이다.
  • Flow 는 그것으로 흘러 가는 data elements 를 transforming 하는 것에 의해 upstream 과 downstream 을 연결한다.

RunnableGraph

양쪽 끝이 각각 Source 와 Sink 에 attached 된 Flow 은 run() 할 수 있는 상태이다.

Flow 를 Source 에 붙여서 composite source 를 만드는 것이 가능하고, Sink 에 Flow 를 prepend 해서 새로운 sink 를 만드는 것도 가능하다.

stream 이 적절하게 양쪽에 source 와 sink 를 갖고 만들어지면, 이것은 RunnableGraph type 에 의해 표현된다. 그리고 이것은 실행될 수 있는 상태라는 것을 가리키는 것이다.

RunnableGraph 를 만들고 나서 모든 source, sink, 다른 operator 들을 연결하는 것에 의해서 RunnableGraph를 만들고 난 이후에도 이것이 materialized 되기 전까지는 data 가 들어오지 않는다. Materialization 은 Graph 에 의해 설명된 계산을 실행하기 위해 필요한 모든 리소스들을 할당하는 절차이다.


사용 예시

아래처럼 source -- flow ---> sink 를 연결한다.

// Explicitly creating and wiring up a Source, Sink and Flow
Source(1 to 6).via(Flow[Int].map(_ * 2)).to(Sink.foreach(println(_)))

아래처럼 Flow 가 생략될 수도 있다.

// Starting from a Source
val source = Source(1 to 6).map(_ * 2)
source.to(Sink.foreach(println(_)))

[컴] RabbitMQ temporary queue

queue / 임시큐 / 송수신 다른 큐/ queue /

RabbitMQ temporary queue

아래처럼 임시 queue 를 만들 수 있다.
result = channel.queue_declare(queue='', exclusive=True)

한명 접속 후 삭제

exclusive=True 주면, cosumer 의 connection 이 close 되면, queue 가 삭제된다.(참고:Queues — RabbitMQ)

example

"""
                                  to_b_rk     +-----------------+
                                              |                 |
+--------+                          +------>  |     to_b_q      |          +---------+
|        |                          |         |                 +--------> |         |
|        |                          |         +-----------------+          |         |
|        |       +----------+       |                                      |         |
|    A   +-----> | to_b     |       |                                      |   B     |
|        |       |          +-------+                                      |         |
|        |       +----------+                                              |         |
|        |       +----------+                 +------------------+         |         |
+----+---+       | to_a     |                 |                  |         |         |
     ^      +--> |          +---------------->+ <random_name>    +-----+   +---+-----+
     |      |    +----------+                 | temporary queue  |     |       |
     |      |                  <random_name>  +------------------+     |       |
     |      |                                                          |       |
     |      |                                                          |       |
     |      |                                                          |       |
     |      +------------------------------------------------------------------+
     |                                                                 |
     |                                                                 |
     +-----------------------------------------------------------------+


"""

parameters = pika.URLParameters('amqp://guest:guest@127.0.0.1:5672/%2F')
connection = pika.BlockingConnection(parameters)
channel = connection.channel()

to_a = 'to_a'
to_b = 'to_b'
to_b_q = 'to_b_q'
to_b_rk = 'to_b_rk'

# to_b queue
channel.exchange_declare(exchange=to_b,
                         exchange_type='direct')
channel.queue_declare(queue=to_b_q)  # idempotent
channel.queue_bind(exchange=to_b,
                   queue=to_b_q,
                   routing_key=to_b_rk)

# to_a queue, random temporary queue
channel.exchange_declare(exchange='to_a',
                         exchange_type='direct')
result = channel.queue_declare(queue='', exclusive=True)
recv_queue = result.method.queue
channel.queue_bind(exchange='to_a',
                   queue=recv_queue,
                   routing_key=recv_queue)

# send message to to_b queue
data = {}
data['to_a_queue'] = recv_queue
message = json.dumps(data)
channel.basic_publish(exchange=to_b,
                      routing_key=to_b_rk,
                      body=message)


python 에서 간단한 메시지 전송을 하려면 ref. 1 을 참고하자.

See Also

  1. 쿠...sal: [컴] RabbitMQ 자료들

Reference

  1. RabbitMQ tutorial - "Hello world!" — RabbitMQ

[컴] 백업 방식들

백업 / 백업 방식 / 운영 노하우/

백업 종류

  • full backup: 모든 것을 백업
    • 백업 주기: 매주(또는 그 보다 긴 주기로) 실행하는 것이 보통.
  • incremental backup(증분 백업) : 변경된 것만 백업하는 것
    • 백업 주기: 증분 백업은 매일 밤
  • synthetic full backup(통합 풀 백업) : 복원은 풀 백업처럼 하지만 백업은 그렇지 않은 백업이다.

주기적으로 전체 백업을 실행하는 이유

증분 백업으로 순차적으로 백업하면, 특정부분이 지워지고, 업데이트되는 등 비효울 적이다. 그래서 적정수준까지 full backup 으로 살리고, 이 이후 부분만 incremetal backup 을 복원(retore)하면 된다.
하지만 전체 백업을 수행하면 그 서버에 상당한 부하가 간다.
백업과 복원 효율성 사이의 trade off 가 있다.

백업방식

결과적으로 file 의 저장이 어떤 방식으로 이뤄져 있느냐에 따른 분류인듯 하다.

1. Copy

  • 가장 흔한 방법
  • 각 파일을 한 매체에서 다른 매체로 간단히 복사.(최신 파일 리스트와 파일 버전을 보고)
  • 백업 중인 시스템에 아무런 영향을 미치지 않는다.
  • 이 방법은 테이프나 디스크에서 활용
  • 시간이 오래 걸린다.

2. Block level incremental backup Snapshot

  • 디스크에서만 가능
  • 백업 시스템이 변경된 각 파일이나 블록을 스토리지 시스템에 별도의 개체로 저장하는 경우에만 가능한 방법
  • 백업 시스템이 전통적으로 백업본을 저장하던 방식, 즉, 여러 파일을 컨테이너 내부에 넣는(예: tar형식 또는 상용 백업 형식)과는 대비된다.
  • 전체 백업본을 각 object 에 대한 최신버전들의 모음(gathering) 으로 만들수 있다.(snapshot)
  • 데이터의 이동이 없기 때문에 통합 풀 백업본을 만드는 시간이 거의 걸리지 않는다. 그래서 훨씬 더 자주 만들 수 있다.
  • 실제로 이 방식을 지원하는 시스템은 대부분은 특정 파일이나 object 가 백업이 되면, 자동으로 '전체 백업본'을 만든다.
    • 블록 수준 증분 영구 백업 시스템(block-level incremental permanent backup system, block-level backup)
    • 전체 백업본을 만들어야 할 일이 없다.

3. virtual full backup

Reference

  1. 복원과 백업을 모두 효율적으로··· ‘통합 풀 백업’이란? - CIO Korea, 2020-05-15

[컴] 오브젝트 스토리지(Object Storage)

백업 방법 /  여러리즌으로 / back up / 운용 / 주기적인 백업 / object storage

오브젝트 스토리지(Object Storage)

  • 아마존의 심플 스토리지 서비스(Simple Storage Service, S3)
  • 애저의 블롭 스토어
  • 구글의 클라우드 스토리지

Object storage System

계층적 디렉토리 및 서브디렉토리 구조가 없는 일종의 파일 시스템
  • 오브젝트 스토리지 시스템에 저장된 모든 오브젝트는 콘텐츠에 따라 고유 식별자(Unique IDentifier, UID)가 주어진다.
  • 보통 uid 는 SHA-1 같은 hash algorithm 응 이용해서 만든다.

오브젝트 스토리지와 블록 스토리지 간의 큰 차이

  • 오브젝트 스토리지에 저장된 모든 오브젝트는 최소한 3곳의 가용 구역으로 자동으로 복사된다
  • 오브젝트 단위로 복제를 한다.
    • 클라우드 블록 스토리지 및 보편적 RAID 시스템은 블록 수준에서 복제가 이루어진다.
  • 오브젝트는 절대로 변경되지 않는다. (위키의 편집같은 것을 생각해보자.)
    • 오브젝트가 변경되어야 한다면 그냥 새 오브젝트로 저장
    • 버전 분류가 지원된다면 이전 오브젝트 버전은 역사적 목적으로 저장
  • 오브젝트 스토리지는 위해를 끼칠 수 있는 여러 원인을 방어하는 여러 보호 수준을 자동으로 포함
  • 선택적인 WORM(Write-Once-Read-Many) 보호
  • 교차-지역 복제 등 이용 가능한 모든 베스트 프랙티스를 준수
  • 오브젝트 스토리지가 여전히 오류를 범할 수 있는 인간에 의해 작성 –> 오브젝트 스토리지 내 데이터가 미션 크리티컬이라면 백업하는 것이 적절

다른 백업 서비스 이용

  • AWS 글레이저 딥 아카이브
  • 애저 아카이브 스토리지
  • 구글 콜드라인
  • 상이한 계정 및 지역 을 이용해야 한다.

블록 볼륨 백업

  • 블록 볼륨은 백업해야 한다.
  • 스스로 이들을 백업.
  • 블록 스토리지 스냅샷
    • 다른 지역 및 계정으로 복제

오브젝트 스토리지 백업

  • 오브젝트 스토리지는 여러 가용 구역으로 복제가 자동으로 이루어진다.
  • 하지만 완벽이란 없다 –> 필요하다고 판단되면 백업을 해라.

See Also

  1. 백업 방식들

Reference

[컴][NodeJs] CPU Intensive job 을 Node.js 에서 처리할 때

노드js nodejs 에서 싱글 쓰레드 / single thread job / cpu 사용량이 큰 작업 처리 방법 / node multi / nodejs / multi / multi thread  / multithread

CPU Intensive job 을 Node.js 에서 처리할 때

node js - single thread

기본적으로 single thread 로 알려져 있는데, 이것은 약간 이해를 잘 못 할 수 있어서 간단한 설명을 하자면, ref. 2 에서 여기 를 보면 node js 의 구성을 살짝 엿볼 수 있는데, libeio 를 통해 thread pool 을 만들어서 사용한다. (ref. 3)
그렇기 때문에 이것이 single threaded 인 것은 user 의 source code 를 run 하는 부분을 이야기 한다. 아래 코드를 예로 들면 user 의 code 부분은 하나의 thread 에서 동작한다. 하지만 fs.readFile 같은 부분은 Thread Pool 로 던져질 것이다. 그리고 response 가 오기까지 또 이 single thread 는 console.log('right after') 부분으로 처리할 수 있게 되는 것이다.
var fs = require('fs');  
fs.readFile(‘/files/help.txt’, function(err, buf) {  
    console.log('done');
});
console.log('right after');
from : ref. 1

cpu intensive job

그러므로 nodejs 로 만든 server 들은 기본적으로 single thread 로 user request 를 처리하게 된다. 하지만 만약 request 하나에서 cpu intensive 한 작업을 처리하는 경우가 있는 request 에 대해서는 nodejs 에서 처리가능한 request 양이 급격히 줄어들 수 밖에 없다. 이런 문제점에 대한 해결책에 대한 이야기가 아래 글들에 담겨있다.
대략적인 내용은 cluster 를 사용해서 request 마다 process 를 하나 더 만들어서 cpu intensive job 을 그쪽으로 넘기는 것인데, 문제는 이것이 너무 많은 memory 를 할당해야 한다는 것이다. 그래서 cluster 와 queue(kue library) 를 이용해서 일종의 process pool 을 만들어서 사용하는 해결책을 제시한다.

여담으로, 이 방법들을 살펴보고 있으려니까 아주 오래전에 linux 에서 c 로 server programming 을 하던 때의 생각이 난다.

node js http server timeout

nodejs v6.x 에서 일단 기본 timeout 이 2 분으로 설정되어 있다고 한다. 즉, request 를 보낸지 2분후 까지 response 가 없으면 error 가 된다. 이 timeout 내에 더 많은 request 를 처리하기 위해서라도 "cluster 의 사용"은 필요할 듯 하다.
 

See Also

References

  1. Introduction to NodeJS, A SSJS: Part II - EventLoop Explained
  2. https://youtu.be/L0pjVcIsU6A?t=366
    • 동영상에서는 Node.js 의 철학? 을 알 수 있다.
  3. javascript - Nodejs Event Loop - Stack Overflow
  4. Cluster | Node.js v12.3.1 Documentation