새소식

Recent Study/Kubernetes 학습과정

21. Storage - NFS(FileStorage), Longhorn(BlockStorage) & Logging - PLG Stack

  • -

1. PV Volume Plugin

 

PV Volume Plugin

 

 PV는 기본 속성 중 capacity라는 용량을 설정하는 부분accessMode를 지정하는 부분이 있고, Volume Plugin이라고 해서 이 PV의 실질을 결정하는 옵션이 있습니다.

 

1) hostPath : 이 PV를 hostPath로 만들면 워커 노드에 지정된 /path를 이 PV에 대한 볼륨으로 사용하겠다는 것 입니다.

2) NFS : 웹 스토리지에 NFS 서버가 고정되어 있다면 PV의 Volume Plugin을 NFS로 지정하게 되면 이 PV는 외부에 있는 NFS와 연결할 수 있는 NFS 클라이언트의 역할을 하게 됩니다. OS에 NFS 클라이언트가 설치되어 있어야 합니다.

3) Cloud Service Volume : Azuer, GCP, AWS 등 클라우드 서비스 볼륨에 연결하고 싶다면 해당 서비스에 맞는 Volume Plugin이 있습니다. 

 

 각 서비스 플랫폼이나 유명 솔루션 업체들에서 자신들의 볼륨과 연결할 수 있는 플러그인들을 k8s 오픈소스에 기여를 했기 때문에 바로 연결될 수 있는 것 입니다. 볼륨 플러그인 뿐만 아니라 Provisioner도 같이 추가한 것들은 스토리지 클래스를 통해서 다이나믹 프로비저닝을 할 수 있습니다. 해당 k8s docs를 참고하면 플러그인별 내부 프로비전어 가능 여부 확인도 가능합니다.

 

CSI (Container Storage Interface) - 만약 미리 자신의 볼륨 솔루션에 대한 플러그인들을 k8s에 반영해 놓지 않았다던가, 반영해놨어도 볼륨의 최신 버전이 나왔을 때 그 최신 버전을 지원할 수 있는 볼륨 플러그인을 바로 적용해보려면 이것을 사용하면 됩니다. 

 

 

 

2. Access Mode

 

Access Mode

 

 각각 볼륨마다 Storage Type이 있습니다. NFS는 전형적인 FileStorage, Cloud Service는 여러 볼륨 솔루션들이 있기 때문에 대부분 BolockStorage, FlieStorage, ObjectStorage 모두 지원합니다. 그리고 현재 k8s에 플러그인 되어 있는 3rd Party Venders는 대부분 FileStorage를 지원하고 있습니다. 마지막으로 CSI는 새롭게 컨테이너 베이스로 볼륨 솔루션을 설치할 경우 모든 Storage 타입이 제공 가능합니다. 다른 솔루션들은 각자의 특성에 맞게 스토리지 타입을 제공합니다.

 

결국 각각의 볼륨 솔루션마다 이 솔루션이 어떤 타입의 Storage인지 기본적으로 인지하고 있어야 하고, 그 Storage에서 지원하는 Access Mode는 해당 가이드를 확인해서 실제 제공하고 있는 기능을 사용해야 하는데 크게 FileStorage, ObjectStorage는 기본적으로 BlockStorage와 달라지는 부분이 있습니다. 

 

 먼저 FileStorage, ObjectStorageRWO를 지원하기 때문에 한 노드 위에 여러 Pod에서 연결이 가능하고, RWM, RWO도 지원할 수 있기 때문에 위처럼 다른 노드에 있는 Pod들에서도 연결이 됩니다. 공유 데이터용으로 사용합니다. ObjectStorage는 백업용, 이미지 저장용으로 주로 사용이 됩니다. 그래서 이런 특징으로 Deployment에 PVC를 붙일 때 주로 사용이 됩니다.

 

 하지만 BlockStorage는 기술적으로 특정 한 노드에 Storage를 마운팅하는 개념이기 때문에 위처럼 한 노드 위에 Pod가 몇개 있건 연결이 되는 건 문제없지만, 다른 노드 위에 있는 Pod와 함께 연결할 수 없습니다. RWO 옵션만 지원. DB 데이터용으로 주로 사용되며, 직접 노드와 BlockStorage가 마운팅 되었기 때문에 빠른 Read,Write가 가능하고 StatefulSet과 특성이 잘 맞아 이를 이용해 배포를 하는 경우가 자주 있습니다.

 

 

 

3. Storage

 

1. FileStorage (NFS)

: PV를 만들어서 연결을 하고, selector와 Label을 이용해 PVC와 연결합니다. 이후 Node1, Node2에 각각 Pod를 만들어서 PVC와 붙인 후 FileStorage를 통한 파일 공유가 잘 되는지 실습을 해볼 수 있습니다.

 

2. BlockStorage (Longhorn)

: 볼륨 솔루션과 CSI 플러그인을 설치하면 노드 위에 Pod가 생성됩니다. 크게 Controller Plugin, Node Plugin으로 나뉩니다. Controller Plugin에 대한 Pod들은 Deployment로 주로 만들어지고, CSI를 담당하는 주요 기능에 대한 역할을 합니다.Node Plugin은 해당 Pod들이 데몬셋으로 만들어지고 각 노드별 해당 솔루션으로서의 기능인 볼륨 생성 역할을 합니다.

 

 먼저 Longhorn에 대한 StorageClass가 만들어져 있을 것이고, 이것을 이용해서 PVC를 만들면 Provisioner는 PVC를 바라보고 있다가 PVC가 생성된 걸 확인하고 이에 맞는 PV를 만들어서 PVC와 연결이 됩니다. csi-resizer도 PVC를 모니터링하고 있는데 PVC에 대한 볼륨 사이즈가 변하는지 체크하다가 사용자가 볼륨의 크기를 변경하면 이것을 처리해주는 역할을 합니다. csi-snapshotter는 볼륨 스냅샷이라는 오브젝트를 통해서 볼륨에 대한 스냅샷을 찍도록 해줍니다.

 

PV가 만들어져 있으면 특정 노드 위에 있는 CSI 플러그인들은 해당 노드에 있는 볼륨 솔루션인 매니저를 통해서 볼륨을 생성하라고 하고 해당 요청은 최종적으로 엔진까지 가서 엔진이 결국 볼륨을 만듭니다. 이제 Pod를 PVC에 붙이게 되면 VolumeAttachment라는 오브젝트가 생기고 이 VolumeAttachment 감시하던 csi-attacher가 이걸 보고 Volume과 Pod를 연결해줍니다.

그리고 솔루션 자체적으로 ui가 있는데, 이 ui들은 매니저와 직접 통신하고 있기 때문에 이 ui를 통한 볼륨에 대한 모니터링과 직접 제어가 가능합니다. 

 

 

 

4. Logging

: k8s 아키텍처에서 말하는 Logging은 Log Data를 말하는 것이고, Monitoring은 Cpu, Memory같은 자원을 얘기합니다. 이런 기능들에 대해 k8s 자체적으로 제공되는 Core Pipeline이 있고 추가적으로 설치해서 기능을 강화할 수 있는 Service Pipeline이 있습니다. 

 

1. Core Pipeline

 

 

: Worker Node에는 kubelet과 Container Runtime 영역이 있고, 노드의 자원인 Cpu / Memory와 데이터를 저장하는 Disk가 있습니다. k8s로 Pod 생성 요청을 받으면 kubelet은 Pod를 만들고 컨테이너 생성은 컨테이너 런타임에게 맡기는데 여러 종류의 컨테이너 런타임을 만들어주는 종류가 있지만 현재 Docker를 사용하고 있습니다. 이 Docker가 Worker Node의 Disk를 사용하면서 데이터를 쓰고 로그를 생성합니다. 뿐만 아니라 서비스가 기동되면서 CPU와 메모리 자원도 사용을 할 것입니다.

 

 이제 이 상태에서 모니터링에 대한 Core Pipeline은 이전에 HPA를 설명할 때 말했던 내용인데, 각 노드에 있는 kubelet은 별도로 설치되어 있는 Resource-Estimator인 C-advisor를 통해 Docker로 부터 Cpu / Memory 정보를 가져올 수 있습니다. 각 노드의 모니터링 정보는 metrics-server로 모아집니다. 그러면 사용자는 kubectl top 명령으로 api 서버를 통해 이 정보를 조회할 수 있는 것 입니다. 그리고 kubectl log 명령으로 컨테이너의 로그 정보를 조회할 수도 있는데 그럼 kubelet을 통해 해당 컨테이너의 로그 파일을 볼 수 있습니다.

 

 

2. Service Pipeline

 

 

: kubelet 처럼 모든 노드에 설치가 돼서 데이터를 가져오는 역할을 위해 DaemonSet을 이용한 Agent 영역이 있습니다. 이 Agent가 C-advisor, Container Runtime, Worker Node를 통해 로그와 리소스 자원을 수집하는 역할을 합니다. 별도의 Worker Node에 metrics-server와 같이 각각의 노드의 매트릭들을 수집하고 분석하는 서버 영역이 있습니다. 이 서버는 많은 데이터가 저장되기 때문에 별도의 저장소를 구성하길 권장합니다. 그리고 마지막으로 웹 UI가 수집 서버로 쿼리를 하면서 필요한 정보를 사용자게 필요한 정보를 보여주는 것이 Service Pipeline에 기본적인 구조입니다. 대표적인 제품으로 Elastic 제품, Loki, Prometheus가 있는데 이 내용들은 마지막 장에 다시 설명을 드리겠습니다.

 

 

3. Logging Archtectures

 

1) Node level Logging

 

 

: 단일 노드 안에서 일어나는 로깅 구조입니다. 단일 노드에서 로그 파일이 생기는 구조를 보면 이렇게 kubelet, Docker, Worker Node 영역이 있습니다.

 

 Pod내 컨테이너에서 앱 자체 로그를 위처럼 파일 형태로 생성한다고 가정하겠습니다. 컨테이너는 Docker가 만들기 때문에 Docker의 로깅 드라이버를 통해 로그가 생성되는데 그 설정이 /etc/docker라는 패스에 daemon.json 파일에 들어있습니다. 내용을 보면 컨테이너 당 최대 3개의 로그 파일이 생기고 각 파일은 100mb 씩 저장되도록 하는 설정입니다. 그 이상 만들어지면 이전 파일들이 지워집니다. 이를 위해 로그를 stdout, stderr로 써야합니다. 이렇게 출력된 로그는 /var/lib/docker에 containers라는 폴더가 있고 여기에 컨테이너별로 id 폴더가 만들어지면서 <container_id>-json.log라는 로그 파일이 만들어집니다. 여기까지가 도커 자체적인 로깅 드라이브를 통해 컨테이너 안의 로그가 워커 노드에 만들어지는 과정입니다.

 

 k8s는 /var/log/ 폴더에 pods라는 폴더를 만들고 여기에 위처럼 특정 네이밍 규칙에 따라 도커에서 만든 로그 파일을 링크로 걸어줍니다. 그리고 이 링크된 파일을 가지고 또 containers라는 폴더에 위와 같은 네이밍 규칙을 가지고 파일을 링크합니다. 이 상태에서 Pod가 삭제되면 컨테이너도 삭제가 되면서 해당 폴더가 날라가는데, 그럼 해당된 로그와 링크된 부분도 모두 삭제되고 더 이상 로그를 확인할 수 없습니다. 즉 Node level Logging은 Pod가 살아있는 동안 유지가 되는 거고, 이때 kubectl 로그 명령으로 Pod의 현재 실시간 로그를 조회해 볼 수 있는데, Pod가 죽게되면 더 이상 그 Pod의 로그를 볼 수 없습니다. 이것을 보고 싶다면 Cluster level Logging을 사용하면 됩니다.

 

 추가적으로 Termination Message Path라는 것이 있는데 디폴트로 컨테이너 안에 /dev라는 폴더에 터미네이션 로그 파일이 세팅되어 있습니다. 이것은 앱에 오류가 생기거나 LivenessProbe에 문제 시 Pod가 Restart 될 때 해당 원인이 되는 로그 파일을 찾아야 할 때 이럴 필요 없이 죽기 전 에러 로그를 이 파일에 쏘게 되면 k8s가 이 파일을 읽어서 Pod의 상세 status 부분에 표시를 해주기 때문에 굳이 로그 파일을 찾이 않아도 원인을 찾는 로그를 볼 수 있게 됩니다. Pod가 Restart, Scale Out이든지 이런 Pod의 상태를 볼 수 있습니다.

 

 

 

2) Cluster level Logging

 

 

: k8s 클러스터 내 모든 노드들에 있는 로그들을 포괄하는 로깅입니다.

 

(1) Node Logging Agent : 기본적으로 Node level Logging을 기반으로 각각의 노드에 Agent를 두는 방식인데 각 노드에 DaemonSet으로 Agent를 둬서 워커 노드에 만들어지는 이 경로의 로그들을 조회하는 형태입니다. 또한 Docker Log Drive를 수정해서 Agent로 Log를 쓰게 할 수 있습니다. 이렇게 읽은 로그를 수집 서버로 전송하는 패턴이 Node Logging Agent 구조 입니다.

 

(2) Sidecar Container Streaming : 한 컨테이너에서 Access_log와 app.log라는 로그 파일이 쌓인다고 가정했을 때 이 두 로그 파일을 스탠다드 아웃으로 쏘게 되면 워커 노드의 파일이 쌓일 때 로그가 한 파일에 적재됩니다. 이럴 때 이 메인 Container에서 Log를 바로 스탠다드아웃으로 바로 쏘지말고 그냥 파일로만 저장을 하고, 별도의 컨테이너를 하나 더 만들어서 이 컨테이너의 역할을 Access_log 파일을 읽어서 스탠다드 아웃으로 출력을 하게 만듭니다. 그럼 로그 파일 이름이 컨테이너 단위로 저장되기 때문에 Pod명에 access 컨테이너 이름으로 로그 파일이 생깁니다. 앱 로그도 마찬가지로 별도의 컨테이너를 만들어서 스탠다드 아웃으로 출력을 하게 되면 위처럼 로그 파일이 분리되는 구성입니다. 

 

(3) Sidecar Container with a logging agent : Logging Agent를 SideCar 형태로 Pod안에 집어넣는건데 이렇게 하면 앱 컨테이너는 스탠다드 아웃으로 출력하지 않아도 이 Agent Container에 의해 로그가 수집되고, 그 로그는 수집 서버로 바로 보내주는 구조도 있습니다.

 

 

 

3) Logging/Monitoring Stack

 

 

: 대부분 로깅와 모니터링 플러그인들은 위와 같은 스택을 가지고 있습니다. 대표적인 오픈소스로 Elastic사 에서 제공하는 솔루션입니다.

1. UI를 담당하는 Kibana, 조회와 분석으로 유명한 Elasticsearch, Agent Log로 Logstash라는 것을 조합 이를 ELK Stack

 

2. 수집 에이전트로 가장 유명한 FluentD가 있고 이것을 k8s용으로 가볍게 나온 Fluent-bit이 있는데 Logstash 보다 이를 Elasticsearch와 연결을 해서 주로 사용하고 이를 EFK Stack이라고 부릅니다. 

 

3. 대표적으로 모니터링 시스템으로 유명한 Prometheus가 있습니다. 이것도 자체 Dashboard보다는 Grafana라는 시각화 UI가 유명하기 때문에 위처럼 연결을 해서 자주 사용합니다. 또한 Loki라고 해서 PLG Stack이라고 불리는 모니터링이 있습니다.

 

 

 

4) PLG Stack

 

 

: 이는 마스터 노드와 워커 노드에는 앞에서 배운바와 같이 Pod별로 로그 파일이 쌓이고 있을 것이고, PLG 스택을 설치하면 DaemonSet으로 Promtail이라는 Agent가 모든 노드에 설치되고, ConfigMap으로 Promtail이 해당 Pod의 log Path를 읽도록 설정이 됩니다.

 

 그리고 statefulSet으로 Loki가 설정 됩니다. Loki로 들어오는 서비스를 통해 모든 Promtail에서 이 서비스로 로그 파일을 푸쉬하게 됩니다. Deployment로 Grafana가 설치되고, 원래 NodePort가 아닌 ClusterIp로 서비스가 생기는데 외부에서 접속하기 위해 위처럼 NodePort로 변경해주면 됩니다. 그렇게 생기는 30000 포트로 접근해서 ui로 로그를 조회하게 되면 해당 Grafana에서 Loki로 API Query를 알리면서 원하는 로그를 볼 수 있게 됩니다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.