티스토리 뷰
개발 배경
리눅스 컨테이너는 런타임 환경에서 애플리케이션을 패키지화하고 분리하는 기술입니다. 실행에 필요한 모든 파일이 호스트 시스템과 분리되어 동작하기 때문에 컨테이너 환경에서 동작하는 프로레스는 이식과 관리가 쉽습니다. 하지만 컨테이너의 리소스가 호스트와 분리되었다고 해도 실제 스토리지에 저장되는 데이터들은 격리되지 않습니다. 즉, 하나의 스토리지를 여러 컨테이너가 공유해서 사용하고 여러 컨테이너가 동시에 한 스토리지에 접근하는 경우 I/O 간섭이 발생합니다. 만약 이러한 데이터들 사이에 격리(컨테이너 수준의 격리)가 이루어진다면 각 컨테이너들이 저장 장치에 접근할 때 I/O 간섭이 발생하지 않기에 빠르고 효율적인 처리가 가능해질 것입니다.
기존 문제점
현재 사용되는 Block Interface SSD는 리눅스 컨테이너 간에 저장 공간을 분리하지 않기 때문에 I/O 간섭이 발생합니다. Page size보다 작은 write가 요청되어도 무조건 하나의 page를 통째로 사용하는 write amplification, 즉 부가적 쓰기가 일어나게 됩니다. 또한, SSD에 데이터를 저장할 때는 접근하려는page가 free 상태일 때만 write가 가능합니다. 이미 쓰여진 데이터가 변경되면 기존 page의 내용은 내부 레지스터로 복사 후 변경되어 새로운 free 상태의 page에 기록되는 out-place update 과정을 거칩니다.
이러한 write amplification과 out-place update 제약으로 인해 유효한 데이터와 garbage 영역의 분리가 일어나지 않아 효율적인 저장 공간 사용이 어렵다는 단점을 갖습니다. 컨테이너별로 사용되는 데이터들 역시 무작위로 섞여서 관리되고, 하나의 서버를 공유해서 사용하는 리눅스 컨테이너들은 이러한 데이터의 혼재로 인해 발생하는 I/O 간섭 때문에 성능 저하를 겪게 됩니다.
ZNS SSD는 NAND flash 메모리를 zone 단위로 구별하여 사용하는 SSD로, 용도와 사용 주기가 동일한 데이터를 순차적으로 저장하기 때문에 성능 및 공간 효율이 높습니다. 전체 저장 공간을 작고 일정한 용량의 구역인 zone으로 나누어 별도의 영역에 순차적으로 write가 가능하게 합니다. 하나의 zone에는 유사한 데이터들이 모여 있으며 zone 단위로 삭제가 이루어지기 때문에 garbage가 발생하지 않습니다. 따라서 데이터들을 zone 단위로 묶어 관리하는 ZNS SSD 환경 위에서 리눅스 컨테이너를 실행하면 데이터의 사용 주기와 빈도에 따라 효율적인 접근이 가능합니다.
하지만 현재 ZNS SSD를 Storage Pool로 지정해 컨테이너를 생성하게 되면 device mapper를 거쳐 여러 개의 zone에 컨테이너의 I/O가 발생합니다. 컨테이너를 추가로 생성하는 경우 또다시 여러 개의 zone을 할당하게 됩니다.
이때 empty zone을 할당하지 않고 그림 1과 같이 기존에 생성된 컨테이너가 사용하고 있는 zone에 append하는 형식으로 I/O가 발생합니다. 이 경우 컨테이너마다 독립된 zone을 할당받지 못하고 이미 다른 컨테이너가 사용하고 있는 zone을 공유하므로 ZNS SSD의 장점을 제대로 활용할 수 없습니다. ZNS SSD는 기존의 regular device와 달리 애플리케이션당 독립된 zone을 사용함으로써 sequential write를 제공한다는 장점이 있는데, 현재 시스템의 구조에서는 각각의 zone에 여러 개의 컨테이너가 할당되어 있으므로 I/O 명령의 분리가 일어나지 않는다는 문제가 존재합니다.
개발 목표
ZNS SSD는 용도와 사용 주기가 동일한 데이터를 zone에 순차적으로 저장하고 지웁니다. 하지만 현재 리눅스 컨테이너는 ZNS SSD를 지원하지 않기 때문에 단순히 저장 장치의 종류를 변경하는 것만으로는 이점을 활용할 수 없습니다. 또한 여러 개의 컨테이너가 하나의 zone에 접근하여 컨테이너별 zone 분리가 일어나지 않습니다. 이를 개선하여 컨테이너별로 서로 다른 zone을 사용하도록 한다면 I/O stream 간의 간섭이 제거되어 성능을 향상시킬 수 있습니다.
현재의 device Mapper는 Logical ZNS SSD를 emulate할 수 있지만 단순히 데이터를 zone 단위로 관리하는 것 외에는 부가적인 기능이 없다는 한계가 있습니다. 여러 개의 컨테이너에서 발생하는 데이터들이 출처와 관계없이 단순히 사용 주기와 용도에 따라 분리되기 때문에 같은 zone에 다수의 I/O stream이 접근한다는 단점이 있습니다. 이는 곧 I/O 간섭을 야기시켜 성능 저하가 일어나기 때문에 기존의 zone 관리 방식에 컨테이너별 데이터 분리 기준을 추가하여 격리가 가능하게 해야 합니다.
따라서 그림 2과 그림 3처럼 각 컨테이너에서 I/O가 발생할 시 사용되는 데이터들을 zone 단위로 격리시키는 알고리즘을 개발하여 리눅스 컨테이너가 ZNS SSD의 특성을 이용할 수 있도록 하는 것을 목표로 했습니다.
개선 방안
- dm-zoned 내부에 cgroup metadata 추가
기존의 device mapper는 bio sector를 이용해 하나의 chunk를 가리키고, 각 chunk는 하나의 zone을 매핑하는 1:1 방식을 사용합니다. 그러나 하나의 chunk에는 sector는 같지만 다른 cgroup(서로 다른 컨테이너)에서 발생한 bio가 접근할 수 있기 때문에 여러 컨테이너가 하나의 zone을 공유해서 쓰는 문제가 발생합니다. 따라서 하나의 chunk에 여러 개의 zone(현재 시스템에서는 4개로 제한)이 매핑되는 1:N 방식을 이용해 chunk 내부에서 분리된 zone을 할당받도록 변경했습니다.
dmz_map 구조체는 chunk_id를 인덱스로 하는 dmap 배열의 엔트리입니다. 해당 chunk에 매핑된 dzone_id와 bzone_id 값을 가지고 있는데, 이를 크기 4의 배열 형식으로 수정하여 하나의 chunk당 4개(1:N)의 zone_id를 저장할 수 있도록 변경했습니다.
또한 컨테이너가 사용하는 zone의 id를 관리하는 128X4 크기의 2차원 배열 chunk_cg_zone을 추가했습니다. 이 배열은 chunk id를 인덱스로 하며 해당 chunk를 사용하는 cgroup id를 4개까지 저장합니다. chunk_cg_zone과 dmap에 같은 chunk id를 이용해서 접근한 후 범위 내 동일한 index 값을 사용해 cgroup_id 와 dzone_id/bzone_id를 저장하면 컨테이너가 사용하는 zone의 id를 계속해서 추적할 수 있습니다.
- Zone 매핑 알고리즘 개선
dmz_handle_bio를 통해 chunk에 bio 요청이 들어오면 dmz_get_chunk_mapping 함수는 dmz_chunk_cg를 호출해 cgroup에 할당된 zone id를 찾을 수 있는 index를 얻습니다. 만약 매핑된 zone이 없다면 dmz_map_zone 함수를 호출하고 dmz_set_chunk_mapping을 이용해 비어 있는 zone을 할당합니다. 매핑된 zone이 있는 경우 해당 zone을 가져와서 사용합니다. 사용이 끝난 zone은 dmz_unmap_zone 함수로 unmap 시켜줍니다. 만약 reclaim 요청이 들어올 경우 dmz_do_reclaim 함수가 해당 zone을 unmapping 하고 새로운 zone을 매핑합니다.
요약하면 다음과 같습니다.
1. I/O를 실행하기 위해 컨테이너가 chunk에 접근
2. Chunk에 접근하는 컨테이너 cgroup id로 해당 chunk와 zone를 컨테이너가 사용 중인지 확인
3. 컨테이너가 chunk에 해당하는 zone을 사용 중이면 사용 중인 zone을 반환, 사용하고 있지 않으면 unmapped zone을 할당
4. 컨테이너가 chunk에 해당하는 zone을 더 이상 사용하지 않는 경우 매핑 테이블에서 cgroup id를 삭제한 후 zone 할당 해제
개발 결과
시스템 환경
성능 개선
알고리즘을 적용하기 전 device mapper를 이용해 ZNS SSD를 장착한 환경과 알고리즘을 적용한 후 device mapper의 환경에서 컨테이너 내부 입출력을 시행했습니다. 양쪽 환경에는 컨테이너가 3개씩 생성되어 있으며, 각 컨테이너에서 I/O를 수행해 zone에 접근하는 cgroup 의 개수를 측정했습니다.
알고리즘 적용 전에는 여러 개의 cgroup이 하나의 zone에 접근하는 경우가 많았음을 알 수 있습니다. 적게는 2개부터 최대 4개의 cgroup이 하나의 zone에 접근하여 I/O 간섭이 발생합니다.
알고리즘을 적용한 후에는 cgroup 주소가 다를 경우 별개의 zone을 사용하도록 구현하였기 때문에 zone당 하나의 cgroup만 접근하는 것을 알 수 있습니다. 즉, 컨테이너간 zone을 분리해서 할당한다는 것을 알 수 있습니다. 일부 zone의 경우 2개의 cgroup이 접근하는 것처럼 관찰되는 경우가 있는데, 이는 기존에 사용되던 zone이 unmap 되었다가 다른 cgroup에 mapping 되어 두 개의 cgroup이 하나의 zone을 공유하는 것처럼 보이기 때문이라 추측할 수 있습니다.
다만 현재 chunk 당 4개의 zone을 할당받을 수 있도록 설계하였는데 4개보다 더 많은 컨테이너가 I/O 를 실행하는 상황에서는 컨테이너가 더 이상 zone 을 할당받지 못합니다. 또한 chunk 에 zone이 한 번 매핑된 경우 bio sector 정보를 기준으로 zone zone을 나누기 때문에 zone 용량을 최대의 효율로 사용하지 못합니다. 따라서 bio sector 정보를 기준으로 zone zone을 나누는 것이 아닌, segment 단위로 분리하여 cgroup 마다 zone zone을 할당하도록 개발한다면 보다 효율적으로 zone 용량을 사용할 수 있을 것입니다.
전체 개발 코드, 알고리즘에 대한 자세한 내용, 동작 방식이 궁금하신 분들은 아래 참고 자료와 사전에 읽으면 좋은 자료를 읽어보시면 도움이 될 것 같습니다. 추가적으로 궁금한 내용이 있으시면 메일로 연락 주시거나 댓글로 남겨주셔도 좋습니다.
참고 자료
사전에 읽으면 좋은 자료
'Linux' 카테고리의 다른 글
[Linux] ZNS SSD dm-zoned 동작 원리(2) - dm-zoned-metadata.c code분석 (0) | 2022.12.06 |
---|---|
[Linux] ZNS SSD dm-zoned 동작 원리(1) - dm-zoned-target.c code분석 (0) | 2022.12.04 |
[Linux] dm-zoned를 이용한 ZNS SSD 연결 (0) | 2022.11.10 |
[Linux] Device Mapper (dm-zoned) (0) | 2022.10.28 |
[Linux] 커널 디버깅 하는 방법 / printk, ftrace(trace_printk) (4) | 2022.10.13 |
- Total
- Today
- Yesterday
- 스프링 부트
- 알림기능개선기
- 피움
- dm-zoned
- 2차 데모데이
- 3차 데모데이
- 네트워크
- 피움 6주차 회고
- 스프링MVC
- 런칭 페스티벌
- 팀프로젝트
- 5주차 회고
- dm-zoned 코드분석
- 스프링 프레임워크
- 환경 별 로깅 전략 분리
- ZNS SSD
- 스프링 Logback
- java
- 프로젝트
- jpa
- CI/CD
- 8주차 회고
- 우테코
- 우테코 회고
- ZNS
- 알림개선기
- 회고
- Spring
- 백준
- 파이썬
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |