2024 OSSCA [참여형] 활동기간: 약 13주
2024. 7. 13 (토) - 2024. 11. 2 (토)
[Challenges: 4주]
2024. 7. 13 (토) - 2024. 8. 9 (금)
[Masters: 9주]
2024. 8. 10 (토) - 2024. 11. 2 (토)
1) Raft 알고리즘 배경 지식과 핵심 개념
Raft 알고리즘 배경 지식
1. 등장 배경
- 성능 향상과 장애 복구를 위해 분산 시스템이 등장했고, 이를 위한 합의 알고리즘이 필요해졌다. 대표적으로 Raft, Paxos, Zookeeper가 있다.
2. Raft 합의 알고리즘
- 리더 선출: 한 노드가 리더가 되어 로그 복제를 담당하고, 리더가 실패하면 새로운 리더를 선출.
- 로그 복제: 리더가 클라이언트로부터 받은 로그를 팔로워에 복제, 과반수의 동의로 커밋.
- 안전성 보장: 커밋된 로그는 변경되지 않으며, 리더는 최신 로그 기반으로 선출됨.
3. Raft의 신뢰성 확보 방안
- 정족수 기반의 합의, 로그 일관성, 장애 복구를 통해 시스템의 안정성과 신뢰성을 보장.
4. 노드 상태
- 리더, 팔로워, 후보로 나뉘며, 리더가 주도적으로 클라이언트 요청을 처리하고 로그를 복제.
5. Term의 역할
- 리더의 ID로, 중복 투표 방지, 서버 간 통신에서 Term을 이용해 상태를 최신으로 유지.
6. 리더 선출 과정
- 하트비트를 받지 못하면 Candidate로 전환, 과반수의 투표를 받아 리더로 선출됨.
7. 로그 복제 과정
- 리더가 로그를 받아 모든 팔로워에 복제, 과반수 응답 후 커밋.
2) Raft 합의 알고리즘 이해하기
In Search of an Understandable Consensus Algorithm
1. Abstract
- Raft는 이해하기 쉬운 합의 알고리즘으로, Paxos와 동일한 효율성을 유지하면서도 구조적으로 차별화된 설계를 통해 더 나은 이해도를 제공합니다.
2. Introduction
- Raft는 강력한 리더 중심 구조로, 리더가 로그 항목을 다른 서버로 복제하여 시스템의 일관성을 유지합니다. 리더 선출은 랜덤화된 타이머를 통해 효율적으로 이루어집니다.
3. Replicated State Machines
- 여러 서버가 동일한 상태 기계를 유지하여 시스템을 복제하며, 이 과정에서 로그 복제와 합의 알고리즘이 중요한 역할을 합니다.
4. What’s Wrong with Paxos?
- Paxos는 이해하기 어렵고 실용적인 시스템 구현에 적합하지 않다는 두 가지 주요 문제점을 가지고 있습니다.
5. Designing for Understandability
- Raft는 시스템 구축과 이해 용이성을 고려한 설계로, 분산 시스템에서 안전성과 가용성을 보장하면서 효율성을 유지합니다.
6. The Raft Consensus Algorithm
- 합의 문제를 리더 선출, 로그 복제, 안전성 보장의 세 가지 하위 문제로 나누어 처리합니다.
7. Raft Basics
- Raft 클러스터는 리더, 팔로워, 후보자 상태로 나뉘며, 임기(Term)와 RPC를 통해 통신합니다.
8. Leader Election
- 팔로워는 하트비트를 받지 못하면 선거를 시작하며, 랜덤화된 타이머를 통해 무승부를 방지합니다.
9. Log Replication
- 리더는 클라이언트의 요청을 로그에 저장하고 팔로워에게 복제하며, 로그 항목이 안전하게 복제되면 이를 커밋합니다.
10. Safety
- 리더는 모든 커밋된 로그 항목을 포함해야 하며, 로그 일관성을 유지하도록 설계되었습니다.
11. Follower and Candidate Crashes
- Raft는 팔로워 및 후보자의 장애를 멱등성(idempotency)을 통해 처리하며, 서버 충돌에도 안전성을 보장합니다.
12. Timing and Availability
- 리더 선출과 타이밍은 Raft의 가용성에 영향을 미치며, 안정적인 시스템 운영을 위한 적절한 타이밍 설정이 필요합니다.
13. Cluster Membership Changes
- Raft는 클러스터 멤버십 변경을 위한 안전한 두 단계 접근법을 사용하여, 시스템 안정성을 유지합니다.
14. Log Compaction
- 스냅샷을 통해 로그 압축을 수행하며, 이를 통해 로그 크기를 관리하고 시스템 성능을 유지합니다.
15. Client Interaction
- 클라이언트는 리더를 찾기 위해 무작위로 서버에 연결하며, 리더가 실패하면 다른 서버에 재접속하여 요청을 처리합니다.
16. Implementation and Evaluation
- Raft는 이해 용이성, 정확성, 성능 면에서 평가되었으며, Paxos보다 더 쉽게 이해된다는 연구 결과를 보여주었습니다.
17. Related Work
- Raft는 Paxos와의 비교 연구, 성능 최적화 연구 등을 통해 더 효율적이고 간단한 합의 알고리즘으로 입증되었습니다.
18. Conclusion
- Raft는 Paxos보다 이해하기 쉬운 설계를 목표로 하였으며, 실제 시스템 구축에 있어 더 나은 기반을 제공합니다.
3) Raft 코드 분석하기
Raft 코드 뜯어보기 1)
1. lib.rs 파일 분석
- Raft 알고리즘의 주요 모듈 정의 및 외부 크레이트와의 상호작용
lib.rs
파일에서 Raft 알고리즘의 핵심 모듈들을 정의하고, 외부 크레이트를 가져와 노출하는 방식을 설명하며, 이를 통해 각 모듈이 어떻게 연결되어 있는지 분석
2. raft_node 폴더 분석
- Raft 노드의 동작 원리와 구조 설명
raft_node
디렉토리 내의 bootstrap.rs
, mod.rs
, response_sender.rs
, role.rs
, utils.rs
파일을 분석하고 Raft 노드의 동작, 리더 선출 및 로그 복제에 대한 로직 설명
3. raft_client.rs 파일 분석
- Raft 서비스에 대한 gRPC 클라이언트 생성
- Raft 클라이언트가 gRPC 채널을 통해 서버와 어떻게 통신하는지 설명
4. raft_server.rs 파일 분석
- Raft 서버와 gRPC 통신 구조
RaftServer
구조체 및 gRPC 서비스 메서드 구현을 통해 클라이언트 요청 처리 방법과 서버 간 통신을 설명
5. state_machine 폴더 분석
- 상태 머신 추상화와 비동기 메서드 사용
AbstractStateMachine
트레이트를 분석하며 Raft 상태 머신의 적용, 스냅샷 생성, 복구 등의 동작 방식 설명
6. log_entry 폴더 분석
- 로그 엔트리의 직렬화 및 역직렬화
AbstractLogEntry
트레이트를 통해 로그 엔트리의 직렬화와 복구 과정 설명
7. codec.rs 파일 분석
- Heed 라이브러리를 이용한 데이터 인코딩 및 디코딩
- 로그 엔트리와 키를 Heed에 저장하기 위한 인코딩 및 디코딩 과정 설명
8. constant.rs 파일 분석
- Raft 저장소에서 사용되는 주요 상수 설명
- 스냅샷, 로그 인덱스, 하드 스테이트 등의 저장에 사용되는 상수 정의
9. heed_storage 폴더 분석
- Raft의 스토리지 관리 및 로그 압축 처리
- Heed 데이터베이스를 이용해 Raft 로그를 저장하고 압축하는 방법 설명
10. storage 폴더 분석
- StableStorage 트레이트 구현
- 로그 엔트리 관리, 스냅샷 생성 및 상태 저장 기능을 담당하는
StableStorage
트레이트의 동작 설명
11. utils.rs 파일 분석
- 로그 파일 처리 및 경로 관리
- 파일 시스템과 관련된 유틸리티 함수 및 로그 데이터를 JSON으로 저장하는 방법 설명
각 글에서 다루는 주요 주제
- Raft 알고리즘의 구조적 분석 및 코드 구현에 대한 세부적인 설명
- 모듈별 기능 설명 및 클러스터 구성, 로그 복제, 스냅샷 관리 등의 메커니즘 소개
4) RocksDB와 Rust 결합
1. Rocks DB에 대해 간단히 알아보자
1. RocksDB란 무엇인가?
- Facebook에서 개발된 오픈 소스 key-value 저장 방식의 고성능 데이터베이스로, 다양한 저장 장치 환경에서 높은 성능을 제공하도록 최적화됨.
- LevelDB를 기반으로 하며, 특히 대용량 데이터 처리에 적합함.
2. Key-value 저장 방식
- RocksDB는 순서대로 정렬된 key-value 저장 방식을 사용하며, 데이터 접근을 효율적으로 처리할 수 있는 다양한 기능 제공 (예:
Get
, Put
, Delete
, Scan
).
3. ReadOnly Mode
- RocksDB의 ReadOnly Mode는 읽기 전용으로 데이터베이스를 열어 데이터 수정 없이 성능을 극대화할 수 있음. 이는 데이터 일관성을 유지하면서도 높은 성능을 요구하는 환경에서 유용.
4. Database Debug Logs
- 시스템 로그 파일인 LOG*에 RocksDB의 내부 작업, 오류 및 성능 통계 등이 기록되어 성능 분석 및 문제 해결에 도움을 줌.
5. Rust와의 결합 가능성
- Rust와 RocksDB는 성능과 안전성 면에서 공통된 목표를 가지고 있어 결합이 자주 이루어짐.
- Rust에서 RocksDB를 효율적으로 사용하기 위한 라이브러리와 바인딩이 제공되며, 빠르고 안정적인 데이터베이스 운영이 가능.
6. 예시 코드
- Rust 환경에서 RocksDB를 사용하는 기본적인 코드 예시 제공.
use rocksdb::{DB, Options};
fn main() {
let mut opts = Options::default();
opts.create_if_missing(true);
let db = DB::open(&opts, "path/to/db").unwrap();
db.put(b"my_key", b"my_value").unwrap();
match db.get(b"my_key") {
Ok(Some(value)) => println!("Got value: {}", String::from_utf8(value).unwrap()),
Ok(None) => println!("Value not found"),
Err(e) => println!("Error reading value: {}", e),
}
drop(db);
}
참고자료
5) Rocks DB 스토리지 구현
Rocks DB 스토리지 구현 1)
Rocks DB 스토리지 구현 2)
Rocks DB 스토리지 구현 3)
Rocks DB 스토리지 구현 4)
Rocks DB 스토리지 구현 5)
1. 목표
- RocksDB 스토리지를 구현하여 로그를 저장하는 엔진으로 사용 가능하게 한다.
2. 작업
2.1 RocksDB 스토리지 구현
- RocksDB를 사용하여 로그 데이터를 관리하고 저장하는 구조체 정의.
StableStorage
트레이트를 구현하여 기존 코드베이스와 원활하게 통합.
2.2 RocksDB 메서드 활용
- RocksDB의
put
, get
, delete
메서드를 사용하여 로그 저장, 검색, 삭제 기능을 구현.
- 대용량 데이터를 효율적으로 처리하면서 성능과 안정성을 유지.
2.3 기존 코드베이스와 통합
- RocksDB 스토리지가 기존 스토리지 인터페이스와 호환되도록 구현.
- 구성 또는 런타임 중 다른 스토리지 백엔드를 선택할 수 있도록 코드베이스 수정.
2.4 성능 벤치마킹
- RocksDB 스토리지의 성능을 측정하고, 읽기/쓰기 속도 및 메모리 사용량 평가.
- 다른 스토리지 유형과 비교하여 최적의 스토리지 옵션을 도출.
3. 학습할 것들
3.1 RocksDB 기초
- RocksDB의 내부 구조 및 동작 방식 이해.
- Column Families, Write-Ahead Logging (WAL), Compaction, Snapshots 등의 핵심 개념 학습.
3.2 Rust와 RocksDB 통합
- Rust에서 RocksDB 라이브러리를 사용하여 데이터베이스를 생성하고, 데이터를 읽고, 쓰고, 삭제하는 방법 숙달.
4. RocksDBStorage 구조체 정의 및 트레이트 구현
4.1 RocksDBStorage 구조체 정의
pub struct RocksDBStorage {
db: DB,
logger: Arc<dyn Logger>,
}
- db: RocksDB 데이터베이스 인스턴스.
- logger: 여러 스레드에서 공유될 수 있는 로깅 객체.
4.2 생성 함수
impl RocksDBStorage {
pub fn create(path: &str, logger: Arc<dyn Logger>) -> Result<Self> {
let mut opts = Options::default();
opts.create_if_missing(true);
let db = DB::open(&opts, path).unwrap();
Ok(RocksDBStorage { db, logger })
}
}
4.3 StableStorage 트레이트 구현
append 함수
impl StableStorage for RocksDBStorage {
fn append(&mut self, entries: &[Entry]) -> Result<()> {
for entry in entries {
let key = format!("{:020}", entry.index);
let value = entry.encode_to_vec();
self.db.put(key, value).unwrap();
}
Ok(())
}
}
HardState 처리
- HardState 저장:
set_hard_state()
로 HardState 저장.
- commit 값 설정:
set_hard_state_commit()
로 commit 값 업데이트.
4.4 스냅샷 생성 및 처리
스냅샷 생성
fn create_snapshot(&mut self, data: Vec<u8>, index: u64, term: u64) -> Result<()> {
let mut snapshot = Snapshot::default();
snapshot.set_data(data);
snapshot.mut_metadata().index = index;
snapshot.mut_metadata().term = term;
let value = snapshot.write_to_bytes()?;
self.db.put("snapshot", value)?;
Ok(())
}
스냅샷 적용
fn apply_snapshot(&mut self, snapshot: Snapshot) -> Result<()> {
let metadata = snapshot.get_metadata();
self.set_conf_state(metadata.get_conf_state())?;
self.set_hard_state_commit(metadata.index)?;
self.db.put("snapshot", snapshot.write_to_bytes()?)?;
Ok(())
}
5. 기타 메서드
5.1 로그 관리 및 압축
- 로그 관리:
compact()
로 오래된 로그를 자동으로 제거하고 RocksDB의 압축 기능 사용.
5.2 모든 엔트리 조회
- 모든 엔트리 조회:
all_entries()
로 RocksDB에 저장된 모든 로그 엔트리 반환.
6) 최종 결과물
🔗 Pull Request #124 - Raftify 프로젝트에 RocksDB 스토리지 구현
Implement Rocksdb
storage · Issue #93 · lablup/raftify
🚀 주요 기능:
- RocksDB 스토리지 통합
- Raft 로그, 상태 및 스냅샷을 관리하기 위해 RocksDB를 사용한 새로운 스토리지 백엔드 도입.
- 로그 저장 및 조회
- RocksDB의
put
, get
, delete
메서드를 사용하여 로그를 효율적으로 저장, 조회 및 삭제하는 핵심 기능 구현.
- 스냅샷 관리
- 데이터의 내구성 및 복구를 위해 Raft 스냅샷을 생성하고 적용할 수 있는 기능 추가.
- 스토리지 압축
- 오래된 항목을 제거하여 스토리지 사용을 최적화하는 로그 압축 기능 활성화.
- 포괄적인 테스트
- 새로운 스토리지 백엔드의 신뢰성을 보장하기 위해 모든 주요 함수에 대한 단위 테스트 제공.
🔗 Pull Request #124 - Raftify 프로젝트에 RocksDB 스토리지 구현 및 GET HTTP 메서드 수정
fix: Correct wrong GET
http method usage in example code by TaskerJang · Pull Request #147 · lablup/raftify
Raft 기반 시스템에서 올바른 HTTP 메서드 선택
🛠️ 수정 사항:
- GET HTTP 메서드 수정
- 예제 코드에서 잘못된 GET HTTP 메서드 사용을 바로잡고, 올바른 메서드 사용으로 수정 (#147).
☑️ 아카데미 참가 후기 & 앞으로의 계획
아카데미 프로그램을 통해 정말 많은 성장을 할 수 있었습니다. 13주 동안 Raftify 프로젝트에 기여하면서 분산 시스템과 RocksDB, Raft 알고리즘에 대해 깊이 있는 이해를 쌓을 수 있었고, 실제로 구현하고 문제를 해결해 나가는 과정에서 많은 도전과 배움을 얻었습니다. 함께 했던 멘토와 팀원들 덕분에 더욱 성장할 수 있었고, 컨트리뷰션의 의미를 몸소 느낄 수 있었습니다.
향후 계획으로는, 내년에는 자바 컨트리뷰터로 활동하여 새로운 영역에 도전하거나, 이번에 진행했던 Raftify 프로젝트에 다시 한번 참여해 더 고도화된 기여를 해보고 싶습니다. 지속적으로 오픈소스 프로젝트에 기여하며 제 개발 역량을 확장하고, 협업의 가치를 실천하는 컨트리뷰터가 되고자 합니다.