먼저, 스토리지와 볼륨의 개념을 살펴보자.
Kubernetes는 개체를 사용하여 관리하는 리소스를 나타낸다. 이 규칙은 포드뿐만 아니라 스토리지에도 적용되며, 이러한 모든 개체는 추상화로 작동힌다.
즉, 구현 세부 사항에 힘들게 주의를 기울이지 않고도 개체가 나타내는 리소스를 관리할 수 있다는 장점이 있는 것이다.
Kubernetes는 스토리지 추상화를 볼륨
및 PersistentVolume
으로 제공한다.
볼륨은 Pod의 모든 컨테이너에 액세스할 수 있는 디렉터리
이며, 일부 볼륨은 일시적이므로 볼륨이 연결된 Pod만큼만 지속된다.
여기서 ConfigMap
및 emptyDir
과 같은 이러한 유형의 예를 들 수 있고, 일부 볼륨은 영구적이므로 Pod보다 오래 지속될 수 있다.
볼륨은 유형에 관계없이 컨테이너가 아닌 포드에 연결되며, 포드가 더 이상 노드에 매핑되지 않으면 볼륨도 매핑되지 않는다.
또한 PersistentVolume
리소스는 클러스터에서 내구성 있는 스토리지를 관리하는 데 사용된다. GKE에서 PersistentVolume은 일반적으로 영구 디스크
로 지원되고, NFS와 같은 다른 스토리지 솔루션을 사용할 수도 있다.
Filestore
는 Google Cloud의 NFS 솔루션이며, 볼륨과 달리 PersistentVolume 수명 주기는 Kubernetes에서 관리한다.
PersistentVolume 리소스는 포드와 독립적으로 존재하는 클러스터 리소스이다. 즉, PersistentVolume이 나타내는 디스크와 데이터는 클러스터가 변경되고 포드가 삭제 및 재생성
될 때 계속 존재한다.
PersistentVolume 리소스는 PersistentVolumeClaim(pvc_
를 통해 동적으로 프로비저닝되거나 클러스터 관리자가 명시적으로 생성할 수 있어 백업 스토리지를 수동으로 만들고 삭제할 필요가 없다
는 말이다.
컨테이너 간에 파일을 공유해야 할 수 있으며 포드가 종료된 후에도 스토리지가 존재해야 할 수 있다.
볼륨은 포드 내의 컨테이너가 데이터를 공유할 수 있도록 하며 포드가 상태를 저장하는 데 필요하므로, 프로덕션 애플리케이션은 일반적으로 상태를 어딘가에 저장해야 한다는 중요한 점이 있다.
따라서, Kubernetes에서 볼륨 사용을 습득하는 것은 귀중한 기술이 된다
Kubernetes가 다양한 볼륨 유형을 제공한다고 이미 언급했고, 여기에는 수명이 짧은 임시 볼륨과 오래 지속되는 내구성 볼륨이 포함된다고 했는데, 우선 임시 볼륨 유형부터 살펴보자.
몇 가지 임시 볼륜 유형에 대해 자세히 살펴보겠다.
emptyDir Volume
은 Pod 내의 컨테이너가 읽고 쓸 수 있도록 하는 빈 디렉토리아다.
포드가 노드에 할당도리 때 생성되며 포드가 존재하는 한 존재하지만 어떤 이유로든 Pod가 노드에서 제거되면 디렉토리는 삭제된다.
따라서 지속적으로 보관하고 싶은 데이터가 있다면 이러한 볼륨 유형을 선택하는 것은 바람직하지 않다.
응용 프로그램은 일반적으로 단기 목적으로 emptyDi을 사용하며 k8s 노드의 로컬 디스크에서 또는 메모리 지원 파일 시스템을 사용하여 emptyDir 볼륨을 생성한다.
Configmap
리소스는 k8s에서 포드에 애플리케이션 config 데이터를 주입하는 방법을 제공한다.
Configmap 옵션에 저장된 데이터는 파일 및 디렉토리의 트리인 것처럼 볼륨에서 참조
할 수 있으며 애플리케이션에서 해당 데이터를 사용할 수 있다.
예를 들어 웹 서버를 사용하는 경우 Configmap을 사용하여 해당 웹 서버 매개변수를 설정할 수 있다.
Secret
은 Configmap과 유사하다.
시크릿을 사용하여 암호, 토큰 및 ssh 파일과 같은 중요한 정보를 저장해야 한다.
Configmap과 마찬가지로 민감한 정보를 Pod에 전달하기 위해 시크릿 볼륨이 생성되며 이러한 비밀 볼륨은 메모리 내 파일 시스템에서 지원하므로 시크릿은 비휘발성 스토리지에 기록되지 않는다
.
또한, 친숙한 base64 인코딩을 사용하여 시크릿에 들어가는 값을 난독화하는 것도 일반적으로 좋지만, 그 외에도 시크릿이 구성된 방식 때문에 시크릿이 시크릿이라고 가정해서는 안된다.
Configmap과 보안 비밀을 구분하면 중요하지 않은 포드 구성 데이터와 민감한 포드 구성 데이터를 다르게 관리할 수 있으며 각각에 다른 권한을 적용할 수 있다.
downwardAPI 볼륨
유형은 애플리케이션에서 하향 API 데이터
를 사용할 수 있도록 하는 데 사용되며 이것은 컨테이너가 Pod 환경에 대해 학습할 수 있는 방법이다.
예를 들어 컨테이너화된 애플리케이션이 클러스터에서 고유함을 보장하는 식별자를 구성해야 한다고 가정하자 Pod 이름도 클러스터에서 고유하므로 이 Pod의 이름을 기반으로 해당 식별자를 사용하는 것이 더 쉽지 않을까?
downwardAPI는 해당 데이터를 사용할 수 있도록 선택한 경우 애플리케이션이 실행 중인 팟(Pod)의 이름을 가져올 수 있는 방법
이고 이러한 모든 볼륨 유형은 기본적으로 유사하다.
emptyDir 또는 볼륨은 포드가 노드에 할당될 때 처음 생성되며 해당 노드에서 해당 포드가 실행되는 동안 존재하고 emptyDir 볼륨은 처음에 비어
있다.
Pod의 모든 컨테이너는 emptyDir에서 동일한 파일을 읽고 쓸 수 있으며 해당 볼륨은 각 컨테이너의 동일하거나 다른 부분에 마운트될 수 있디.
어떤 이유로든 포드가 노드에서 제거되면 emptyDir의 데이터가 영구적으로 삭제된다.
컨테이너 충돌은 노드에서 포드를 제거하지 않는다는 점에 유의해야 하며 emptyDir 볼륨의 데이터는 컨테이너 충돌 시 안전
하다.
위에서는 emptyDir 볼륨이 있는 포드를 생성한다.
emptyDir의 일부 용도는 디스크 기반 병합 정렬, 긴 계산 검사, 충돌로부터 복구하거나 웹 서버 컨테이너가 데이터를 제공하는 동안 콘텐츠 관리자 컨테이너가 가져오는 파일을 보관하기 위한 것이다.
시크릿 및 ConfigMap과 같은 일부 볼륨은 포드의 수명에 연결되며 포드가 더 이상 존재하지 않을 때 더 이상 존재하지 않는다.
비밀은 암호나 키와 같은 민감하고 암호화된 데이터를 저장하는 방법이며 중요한 정보를 포드 및 컨테이너에 굽지 않도록 하는 데 사용된다.
암호는 임시 파일 시스템에 저장되므로 비휘발성 저장소에 기록되지 않는다 즉, 휘발성이다.
ConfigMap은 미묘하게 다르다. 민감하지 않은 문자열 데이터에 사용하며 명령줄, 변수 설정, 구성 및 환경 변수 저장
은 ConfigMap의 자연스러운 사용 사례이다.
개별 Pod에 연결된 시크릿 및 ConfigMap 볼륨은 일시적이지만 개체는 그렇지 않다. 지금까지 살펴본 모든 예는 볼륨이며 데이터가 수명이 길든 아니든 볼륨은 정의된 Pod의 수명 주기에 연결된다.
하지만 PersistentVolume
은 포드와 독립적인 자체 수명 주기
를 가진다.
Kubernetes PersistentVolume 객체는 스토리지 Usage에서 스토리지 프로비저닝을 추상화
한다.
Kubernetes는 애플리케이션이 쉽게 확장 가능한 구성 요소로 분리되는 마이크로 서비스 아키텍처를 지원하기 때문에 영구 스토리지를 사용하면 오류를 처리하고 데이터 손실 없이 구성 요소를 동적으로 다시 예약할 수 있다는 강점이 있다.
그러나, 응용 프로그램 개발자가 응용 프로그램 구성 요소에 대해 별도의 볼륨을 만들고 유지 관리해야 할까?
또한 개발자는 애플리케이션의 포드 매니페스트를 수정하지 않고 프로덕션에 배포하기 전에 어떻게 애플리케이션을 테스트할 수 있을까?
테스트에서 프로덕션으로 이동하기 위해 항목을 재구성해야 할 때마다 오류의 위험이 있다.
Kubernetes의 PersistentVolume 추상화는 이 두 가지 문제를 모두 해결해준다.
PersistentVolume을 사용하여 클러스터 관리자는 다양한 볼륨 유형을 프로비저닝할 수 있으며, 클러스터 관리자는 단순히 스토리지를 프로비저닝하고 Usage에 대해 걱정할 필요가 없다.
그리고 애플리케이션 개발자는 스토리지 볼륨을 직접 생성하거나 유지하지 않고도 PersistentVolumeClaims
를 사용하여 프로비저닝된 스토리지를 쉽게 요청하고 사용할 수 있다.
그래서 관리자와 개발자의 역할 분리
는 영구 볼륨을 사용할 수 있게 만드는 것은 관리자의 작업이며 애플리케이션에서 이러한 볼륨을 사용하는 것은 개발자의 작업이다.
애플리케이션 개발자가 PersistentVolumeClaims를 사용할 때 애플리케이션이 실행되는 위치에 대해 걱정할 필요가 없으며 프로비저닝된 스토리지 용량을 요청할 수 있다.
PersistentVolume을 사용하기 위해 특정 클라우드 구현을 소유한 운영 팀은 스토리지 클래스를 정의
하고 PersistentVolume의 실제 구현을 관리
한다.
개발자와 애플리케이션 소유자는PersistentVolumeClaim
을 사용하여 스토리지 유형을 결정하는 스토리지 및 스토리지 클래스의 수량
을 요청한다.
이를 통해 운영 팀은 사용하려는 클라우드 서비스를 관리하고 허용할 수 있고, 애플리케이션 소유자는 특정 구현 세부 사항이 아니라 애플리케이션에 필요한 사항에 집중할 수 있다는 장점이 있다.
Google Cloud의 Compute Engine 서비스는 가상 머신 디스크에 영구 디스크를 사용하고 Kubernetes Engine은 PersistentVolume에 동일한 기술을 사용한다.
영구 디스크는 내구성 있는 스토리지를 제공할 수 있는 네트워크 기반 블록 스토리지임을 알고 먼저 gcloud 명령어를 사용하여 100GB Compute Engine Persistent Disk를 생성한다.
이 Persistent Disk를 Pod에서 사용하려면 누군가 또는 일부 프로세스에서 Persistent Disk를 만들어야 하며 해당 사용자 또는 프로세스에 Compute Engine 관리 권한이 있어야 한다.
이전에 NFS 볼륨 예시에서 본 것처럼 이 Compute Engine Persistent Disk는 포드 매니페스트 내에서 gcePersistentDisk 볼륨 유형을 지정하여 포드에 연결할 수 있다.
이 포드 매니페스트에서 Compute Engine 영구 디스크 볼륨은 gcePersistentDisk 필드에 'pdName: demo-disc'로, 파일 시스템 유형은 'ext4'로 설정했다.
pdName
은 이미 생성된 Compute Engine
, 영구 디스크
에 해당해야 한다.
이것은 영구 디스크에 연결하는 오래된 방법이며 좋은 방법은 아니다.
아래와 같은 방식이다.
Pod가 생성되면 Kubernetes는 Compute Engine API를 사용하여 Persistent Disk를 Pod가 실행 중인 노드에 연결한다.
볼륨이 자동으로 포맷되어 컨테이너에 마운트 되고, 이 포드가 다른 노드로 이동되면 Kubernetes는 기존 노드에서 Persistent Disk를 자동으로 분리하고 새 노드에 다시 연결한다는 장점!
kubectl describe Pod
명령을 사용하여 볼륨이 성공적으로 마운트되었는지 확인할 수 있고
Persistent Disk는 사용하기 전에 생성
해야 한다는 점을 잊지 말아야 한다.
Persistent Disk에는 Pod 내의 컨테이너에서 공유할 수 있는 미리 채워진 데이터가 있을 수 있을 수 있기 때문이다.
매우 편리하지만, 애플리케이션 소유자는 애플리케이션의 Pod 매니페스트 내부에 Persistent Disk의 특정 세부정보를 포함해야 한다는 점과Persistent Disk가 이미 존재하는지 확인해야 하는 과정이 필요하다.
이러한 방식으로 볼륨 구성 정보를 Pod 사양으로 하드 코딩
하면 한 클라우드에서 다른 클라우드로 데이터를 이식하는 데 어려움이 있다.
GKE에서 볼륨은 일반적으로 Compute Engine 영구 디스크
를 사용하도록 구성되며 예를 들어 온프레미스 Kubernetes 클러스터에서 VMware vSphere 볼륨 파일이거나 물리적 하드 디스크일 수도 있는데, 한 환경에서 다른 환경으로 이동하기 위해 애플리케이션을 재구성해야 할 때마다 오류의 위험이 있습니다.
이 문제를 해결하기 위해 Kubernetes는 영구 볼륨이라는 추상화를 제공하여 이 추상화를 통해 포드 사양 내
에서, 스토리지 유형 세부 정보를 정의하지 않고도 스토리지 풀
에서 Pod는 특정 크기
또는 특정 이름의 볼륨을 요구
할 수 있다.
PersistentVolume이 네트워크 스토리지 사용을 보다 관리하기 쉽게 만드는 방법을 자세히 살펴보자.
PersistentVolume 추상화에는 PersistentVolume과 PersistentVolumeClaim의 두 가지 구성 요소가 있다.
PersistentVolume은 클러스터 수준에서 관리되는 내구성 있고 지속적인 스토리지 리소스이고 이러한 클러스터 리소스는 포드의 수명 주기와 독립적이지만 포드는 수명 주기 동안 이러한 리소스를 사용할 수 있다.
그러나 Pod가 삭제되더라도 PersistentVolume과 해당 데이터는 계속 존재하고,이러한 볼륨은 Kubernetes에서 관리하며 수동 또는 동적으로 프로비저닝할 수 있다는 것은 앞서 말했다.
GKE는 Compute Engine 영구 디스크를 PersistentVolume으로 사용할 수 있으며 PersistentVolumeClaim 은 PersistentVolume을 사용하기 위해 포드에서 생성한 요청 및 클래스이다.
PersistentVolumeClaim 개체 내에서 볼륨 크기
, 액세스 모드
및 StorageClass
를 정의하는데 스토리지 클래스란 무엇인가?
➡️ 이름을 지정한 스토리지 특성 세트
포드는 이 PersistentVolumeClaim을 사용하여 PersistentVolume을 요청한다.
PersistentVolume이 PersistentVolumeClaim에 정의된 모든 요구 사항과 일치하면 PersistentVolumeClaim이 해당 PersistentVolume에 바인딩
된다고 하며 PersistentVolume의 스토리지를 사용할 수 있는 상태가 완료된것이다.
Pod 수준 볼륨과 클러스터 수준 PersistentVolume을 스토리지로 사용하는 것의 중요한 차이점은 무엇일까?
PersistentVolume은 애플리케이션 구성에서 스토리지 관리를 분리할 수 있는 추상화 수준을 제공하고 PersistentVolume의 저장소는 Pod에서 액세스하려면 PersistentVolumeClaim과 바인딩되어야 한다.
이전에 샘플 포드 매니페스트에서 Compute Engine, Persistent Disk Volume을 지정하는 방법에 대한 이 예시를 보았다.
하지만, 동일한 저장소에 대한 PersistentVolume 매니페스트를 만드는 방법은 다음과 같다.
먼저 볼륨 용량을 지정한 다음 storageClassName을 지정하고, StorageClass는 PersistentVolume을 구현하는 데 사용되는 리소스임을 기억하자.
PVC는 포드에서 PVC를 정의할 때 storageClassName을 사용하며 클레임이 성공하려면 PV storageClassName과 일치해야 한다.
GKE에는 오른쪽에 표시된 것처럼 Compute Engine Standard Persistent Disk 유형을 사용하기 위해 'standard'이라는 기본 StorageClass가 있다.
이 예시에서 왼쪽의 PV 정의는 GKE 기본 StorageClass와 일치하는 것을 볼 수 있다.
GKE 클러스터에서 정의된 StorageClass가 없는 PVC는 이 기본 StorageClass를 사용하고 표준 Persistent Disk를 사용하여 스토리지를 제공한다.
SSD Persistent Disk를 사용하려는 경우 'ssd'라는 이 예시와 같은 새 StorageClass를 만들 수 있고, 'ssd'라는 이 새로운 StorageClass를 사용하는 PVC는 'ssd'라는 이름의 StorageClass도 있는 PV만 사용한다.
이 경우 SSD Persistent Disk가 사용된다.
AccessModes는 볼륨을 읽거나 쓸 수 있는 방법을 결정하는 요소이다.
ReadWriteOnce
는 볼륨을 단일 노드에 읽기-쓰기로 마운트한다.
ReadOnlyMany
는 볼륨을 많은 노드에 읽기 전용으로 마운트한다.
ReadWriteMany
는 볼륨을 많은 노드에 읽기-쓰기로 마운트한다.
대부분의 애플리케이션
에서 영구 디스크
는 ReadWriteOnce로 마운트한다.
데이터가 정적
일 때 ReadOnlyMany로 마운트할 수도 있고, GCP 영구 디스크
는 ReadWriteMany를 지원하지 않는다.
NFS와 같은 일부 다른 네트워크 볼륨 유형
은 ReadWriteMany accessMode를 지원한다.
예시를 보면 'pd-volume'이라는 이 PV 예제에는 100GB의 스토리지가 할당되고 ReadWriteOnce 액세스 허용, storageClassName을 기반으로 하는 표준 Persistent Disk를 사용한다.
이 PV는 클러스터 관리자가 관리하며 예를 들어 관리자의 책임 중 하나는 이를 백업하는 것이다.
Compute Engine 또는 Kubernetes Engine에서 사용 중인지 여부에 관계없이 Persistent Disk의 스냅샷을 생성하여 Persistent Disk를 백업
할 수 있다.
Persistant Volume 은 Pod 사양에 직접 추가할 수 없다고 앞서 말했듯이 PersistentVolumeClaim을 사용해야한다.
이러한 PersistentVolumeClaim이 PersistentVolume을 요청하려면 해당 storageClassNames 및 accessModes가 일치해야 하고 또한, PersistentVolumeClaim에서 요청된 스토리지 양은 PersistentVolume의 스토리지 용량 내
에 있어야 한다.
그렇지 않으면 Claim이 실패
할 가능성이 높다.
이 클레임은 100GB를 원하고 PersistentVolume은 100GB를 제공하므로 PersistentVolume이 정상이라고 판단한다.
현대적이고 관리하기 쉬운 방법은 PersistentVolume 추상화를 사용하는 것이다.
예시에서 포드에 PersistentVolumeClaim을 추가해보자 이 예에서 'pd-volume-claim'이라는 이름의 PersistentVolumeClaim에는 '표준' storageClassName, 'ReadWriteOnce' accessMode 및 100GB의 요청 용량이 있다.
이 Pod가 시작되면 GKE는 storageClassName 및 accessModes가 동일하고 용량이 충분한 일치하는 PV를 찾는다.
개발자가 PersistentVolume에 이미 할당된 것보다 더 많은 스토리지를 요구한다면 어떻게 될까?
PersistentVolume은 클러스터 관리자가 관리하지만 애플리케이션 개발자가 PersistentVolumeClaim을 만들고 이로 인해 스토리지 할당 실패가 발생할 수 있다.
그렇다면 PersistentVolumeClaim을 충족할 기존 PersistentVolume이 없으면 어떻게 될까?
기존 PersistentVolume을 찾을 수 없는 경우 Kubernetes는 새 PersistentVolume을 동적으로 제공하려고 시도
한다.
➡️ Dynamic Provisioning
기본적으로 Kubernetes는 PersistentVolumeClaim의 storageClassName이 정의되고 적절한 PersistentVolume이 아직 존재하지 않는 경우 PersistentVolume을 동적으로 프로비저닝하려고 시도한다.
이전 예제에서 볼 수 있듯이 일치하는 PersistentVolume이 이미 존재하는 경우 Kubernetes는 이를 클레임에 바인딩한다.
storageClassName을 생략하면 PersistenVolumeClaim은 GKE에서 '표준'이라는 기본 표준 영구 디스크 StorageClass를 사용한다.
동적 프로비저닝은 클러스터에서 활성화된 경우에만 작동하며 이 모든 작업은 GKE에서 처리한다.
애플리케이션 소유자는 기본 스토리지가 프로비저닝되었는지 확인할 필요가 없고 분명, 사용할 수 있으며 기본 저장소의 세부정보를 Pod 매니페스트에 포함할 필요가 없다.
기본적으로 PersistentVolumeClaim을 삭제하면 프로비저닝된 PersistentVolume도 삭제되는데 PersistentVolume을 유지하려면 PersistentVolumeReclaimPolicy를 'Retain'
으로 설정해주면 된다.
일반적으로 기본 PersistentVolume이 더 이상 필요하지 않으면 PersistentVolumeClaim을 삭제해야 할 필요성도 있다.
PersistentVolume의 고가용성을 보장하려면 어떻게 해야 할까?
Compute Engine 영구 디스크를 리전 영구 디스크
로 배포하여 가용성을 높일 수 있다.
리전 영구 디스크는 동일한 리전의 두 영역 간에 데이터를 복제하므로 가용성이 향상되며, 리전 영구 디스크는 수동 또는 동적으로 시작하고 StorageClass에서 추가 필드를 구성하여 프로비저닝할 수 있다.
영역 중단이 발생하면 Kubernetes는 볼륨을 사용하는 워크로드를 다른 영역으로 장애 조치도 가능하다.
리전 영구 디스크를 사용하여 GKE에서 상태 저장 워크로드를 위한 고가용성 솔루션을 빌드할 수 있다.
Kubernetes에서 지역 영구 디스크를 사용하려면 해당 정의에서 '지역 pd' 복제 유형
을 지정하는 StorageClass를 만들고 PersistentVolume을 정의할 때 해당 StorageClass를 사용해주면 끝이다.
Deployment 및 StatefulSet와 같은 다른 컨트롤러에 PersistentVolume을 사용할 수도 있다.
디플로이먼트는 일반적으로 복제본으로 알려진 일련의 동일한 포드를 실행하고 유지 관리하는 포드 템플릿일 뿐인데 이러한 상태 비저장 애플리케이션에 이러한 디플로이먼트를 사용할 수 있다.
Deployment Replicatset은 ReadOnlyMany
또는 ReadWriteMany
액세스 모드를 사용하여 기존 PersistentVolume을 공유할 수 있다.
ReadWriteMany 액세스 모드는 NFS 시스템과 같이 이를 지원하는 스토리지 유형에만 사용할 수 있고, 다른 액세스 모드인 ReadWriteOnce
는 복제본이 PersistentVolume에 동적으로 연결하고
다시 연결
해야 하므로 디플로이먼트에 권장되지 않는다.
첫 번째 포드를 분리해야 하는 경우 두 번째 포드를 먼저 연결해야 하지만 첫 번째 포드가 이미 연결되어 있기 때문에 두 번째 포드는 연결할 수 없다.
이것은 교착 상태
를 만들고 따라서 어느 포드도 진전을 이룰 수 없다.
하지만, StatefulSet
는 이 교착 상태를 해결하고 애플리케이션이 PersistentVolume에서 상태를 유지해야 할 때마다 deployment가 아닌 StatefulSet로 관리하는 것이 좋다.
StatefulSet는 상태 저장 애플리케이션에 유용하다.
StatefulSets는 디플로이먼트와 마찬가지로 Pod 집합을 실행하고 유지 관리하며 StatefulSet 객체는 원하는 상태를 정의하고 해당 컨트롤러는 이를 달성한다.
그러나 배포와 달리 StatefulSet는 각 포드에 대한 영구 ID를 유지하는데 StatefulSet의 각 포드는 영구적인 ID를 유지하며 관련 항목이 있는 서수(Ordinal) 인덱스
를 갖는다.
팟(Pod) 이름, 안정적인 호스트 이름 및 서수 인덱스에 연결된 안정적으로 식별된 영구 스토리지이다.
Ordinal Index
이란 무엇일까?
StatefulSet의 각 포드에 할당되는 고유한 일련 번호일 뿐이고 이 숫자는 설정된 포드 시퀀스에서 포드 위치를 정의한다.
Deployment, Scaling 및 Update는 StatefulSet 내 경로의 Ordinal Index를 사용하여 정렬된다.
예를 들어, 이름이 demo인 StatefulSet가 3개의 복제본을 실행하는 경우 Pod 이름인 demo-0, demo-1 및 demo-2를 순차적으로 실행한다.
즉, 새 포드에서 작업을 수행하기 전에 모든 선행 작업이 실행되고 준비되어 있어야 한다.
예를 들어 demo-0이 실행되지 않고 준비되지 않은 경우 demo-1이 실행되지 않고 demo-1이 실행되고 준비된 후 demo-2가 생성되기 전에 demo-0이 실패하면 demo-0이 다시 실행되고 실행되고 준비될 때까지 실행되지 않는다.
스케일링 및 롤링 업데이트는 역순으로 발생하며 즉, demo-2가 먼저 변경된다.
이는 기본 OrderedReady 상태로 설정되는 포드 관리 정책에 따라 다르고 포드가 실행 중 및 준비 상태를 유지할 때까지 기다리지 않고 병렬로 포드를 시작하려면 포드 관리 정책을 병렬로 변경하면 된다.
상태 저장 애플리케이션에 유용
안정적인 Storage StatefulSets는 각 포드에 대해 고유한 영구 볼륨 클레임을 사용하므로 각 포드가 고유한 개별 상태를 유지할 수 있음
이러한 영구 볼륨 클레임은 애플리케이션에 대해 읽기-쓰기 1회 액세스 모드를 사용
StatefulSet의 예를 살펴보자
예시 대로 서비스 정의에서 클러스터 IP에 대해 None을 지정하여 헤드리스 서비스를 생성한다.
오른쪽에 정의된 StatefulSet는 서비스 이름 필드를 사용하여 이 서비스를 참조한다.
가장 중요한 것은 볼륨 클레임 템플릿이 템플릿 섹션 아래에 지정되어 있다는 것이고, 여기서 볼륨 클레임 템플릿의 이름이 지정되고 사양은 이 StatefulSet의 포드에 필요한 영구 볼륨 클레임과 동일하다.
Google Cloud 영구 디스크(동적으로 생성되거나 기존)에 대한 PersistentVolume(PV) 및 PersistentVolumeClaim(PVC)의 매니페스트 생성
Google Cloud 영구 디스크 PVC를 Pod의 볼륨으로 마운트
매니페스트를 사용하여 StatefulSet 생성
Google Cloud 영구 디스크 PVC를 StatefulSet의 볼륨으로 마운트
포드가 중지되고 다시 시작될 때 특정 PV에 대한 StatefulSet의 포드 연결을 확인
PVC 생성
$ kubectl get persistentvolumeclaim
No resources found in default namespace.
$ cat pvc-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: hello-web-disk
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 30Gi
# 상태는 다음 단계가 끝날 때까지 Pending 상태로 유지된다.
$ kubectl get persistentvolumeclaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
hello-web-disk Pending standard-rwo 28s
매니페스트 파일은 pod-volume-demo.yaml이며 nginx 컨테이너를 배포하고 pvc-demo-volume pod에 연결하며 해당 볼륨을 /var/www/html nginx 컨테이너 내부의 경로에 마운트
한다.
컨테이너 내부의 이 디렉터리에 저장된 파일은 영구 볼륨에 저장
되며 포드와 컨테이너가 종료되고 다시 생성되더라도 지속된다.
$ cat pod-volume-demo.yaml
kind: Pod
apiVersion: v1
metadata:
name: pvc-demo-pod
spec:
containers:
- name: frontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: pvc-demo-volume
volumes:
- name: pvc-demo-volume
persistentVolumeClaim:
claimName: hello-web-disk
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
pvc-demo-pod 1/1 Running 0 23s
$ kubectl exec -it pvc-demo-pod -- sh
# echo Test webpage in a persistent volume!>/var/www/html/index.html
chmod +x /var/www/html/index.html#
# cat /var/www/html/index.html
Test webpage in a persistent volume!
# exit
이제 클러스터에서 포드를 삭제하고 PV가 여전히 존재하는지 확인한 다음 포드를 다시 배포하고 PV의 콘텐츠가 그대로 유지되는지 확인해보자.
$ kubectl delete pod pvc-demo-pod
pod "pvc-demo-pod" deleted
$ kubectl get pods
No resources found in default namespace.
$ kubectl get persistentvolumeclaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
hello-web-disk Bound pvc-7a41319e-4ff2-4748-a6e7-121578e9a03d 30Gi RWO standard-rwo 5m26s
### 재배포
$ kubectl apply -f pod-volume-demo.yaml
pod/pvc-demo-pod created
student_03_013d944f1394@cloudshell:~/ak8s/Storage (qwiklabs-gcp-02-3441d95d86d9)$ kubectl get pods
NAME READY STATUS RESTARTS AGE
pvc-demo-pod 0/1 ContainerCreating 0 6s
$ kubectl exec -it pvc-demo-pod -- sh
# cat /var/www/html/index.html
Test webpage in a persistent volume!
# exit
statefulset와 함께 PVC를 사용하려면 현재 PVC를 사용 중인 포드를 삭제해야 한다.
kubectl delete pod pvc-demo-pod
스테이트풀셋 생성
매니페스트 파일은 statefulset-demo.yaml이고 LoadBalancer 서비스를 포함하는 StatefulSet와 이름이 30GB인 PVC에 대한 nginx 컨테이너 및 volumeClaimTemplate이 포함된 포드의 복제본 3개를 생성한다.
nginx 컨테이너는 이전 작업에서 호출된 PVC(hello-web-disk)를 마운트/var/www/html
한다.
$ cat statefulset-demo.yaml
kind: Service
apiVersion: v1
metadata:
name: statefulset-demo-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
type: LoadBalancer
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-demo
spec:
selector:
matchLabels:
app: MyApp
serviceName: statefulset-demo-service
replicas: 3
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: MyApp
spec:
containers:
- name: stateful-set-container
image: nginx
ports:
- containerPort: 80
name: http
volumeMounts:
- name: hello-web-disk
mountPath: "/var/www/html"
volumeClaimTemplates:
- metadata:
name: hello-web-disk
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 30Gi
$ kubectl apply -f statefulset-demo.yaml
service/statefulset-demo-service created
statefulset.apps/statefulset-demo created
$ kubectl describe statefulset statefulset-demo
Name: statefulset-demo
Namespace: default
CreationTimestamp: Mon, 24 Jul 2023 06:52:04 +0000
Selector: app=MyApp
Labels: <none>
Annotations: <none>
Replicas: 3 desired | 3 total
Update Strategy: RollingUpdate
Pods Status: 2 Running / 1 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=MyApp
Containers:
stateful-set-container:
Image: nginx
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts:
/var/www/html from hello-web-disk (rw)
Volumes: <none>
Volume Claims:
Name: hello-web-disk
StorageClass:
Labels: <none>
Annotations: <none>
Capacity: 30Gi
Access Modes: [ReadWriteOnce]
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 40s statefulset-controller create Claim hello-web-disk-statefulset-demo-0 Pod statefulset-demo-0 in StatefulSet statefulset-demo success
Normal SuccessfulCreate 40s statefulset-controller create Pod statefulset-demo-0 in StatefulSet statefulset-demo successful
Normal SuccessfulCreate 29s statefulset-controller create Claim hello-web-disk-statefulset-demo-1 Pod statefulset-demo-1 in StatefulSet statefulset-demo success
Normal SuccessfulCreate 29s statefulset-controller create Pod statefulset-demo-1 in StatefulSet statefulset-demo successful
Normal SuccessfulCreate 9s statefulset-controller create Claim hello-web-disk-statefulset-demo-2 Pod statefulset-demo-2 in StatefulSet statefulset-demo success
Normal SuccessfulCreate 9s statefulset-controller create Pod statefulset-demo-2 in StatefulSet statefulset-demo successful
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
statefulset-demo-0 1/1 Running 0 65s
statefulset-demo-1 1/1 Running 0 54s
statefulset-demo-2 1/1 Running 0 34s
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
hello-web-disk Bound pvc-7a41319e-4ff2-4748-a6e7-121578e9a03d 30Gi RWO standard-rwo 11m
hello-web-disk-statefulset-demo-0 Bound pvc-bae0d956-0a78-4db6-9565-298fac9e55c9 30Gi RWO standard-rwo 76s
hello-web-disk-statefulset-demo-1 Bound pvc-a4fa4c89-725e-4186-a1d2-e7a62d0050d4 30Gi RWO standard-rwo 65s
hello-web-disk-statefulset-demo-2 Bound pvc-eb5bee6b-9f16-4d63-8da7-3ce2f4c12626 30Gi RWO standard-rwo 45s
원래 hello-web-disk는 여전히 존재하며 이제 새 statefulset Pod에서 각 Pod에 대해 생성된 개별 PVC를 볼 수 있다.
$ kubectl describe pvc hello-web-disk-statefulset-demo-0
Name: hello-web-disk-statefulset-demo-0
Namespace: default
StorageClass: standard-rwo
Status: Bound
Volume: pvc-bae0d956-0a78-4db6-9565-298fac9e55c9
Labels: app=MyApp
Annotations: pv.kubernetes.io/bind-completed: yes
pv.kubernetes.io/bound-by-controller: yes
volume.beta.kubernetes.io/storage-provisioner: pd.csi.storage.gke.io
volume.kubernetes.io/selected-node: gke-standard-cluster-standard-cluster-626b367f-khcg
volume.kubernetes.io/storage-provisioner: pd.csi.storage.gke.io
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 30Gi
Access Modes: RWO
VolumeMode: Filesystem
Used By: statefulset-demo-0
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal WaitForFirstConsumer 116s persistentvolume-controller waiting for first consumer to be created before binding
Normal Provisioning 116s pd.csi.storage.gke.io_gke-b5c499e4d10648409b54-af30-1b8b-vm_01a58413-dd53-464a-b599-c016942b27a1 External provisioner is provisioning volume for claim "default/hello-web-disk-statefulset-demo-0"
Normal ExternalProvisioning 115s (x3 over 116s) persistentvolume-controller waiting for a volume to be created, either by external provisioner "pd.csi.storage.gke.io" or manually created by system administrator
Normal ProvisioningSucceeded 112s pd.csi.storage.gke.io_gke-b5c499e4d10648409b54-af30-1b8b-vm_01a58413-dd53-464a-b599-c016942b27a1 Successfully provisioned volume pvc-bae0d956-0a78-4db6-9565-298fac9e55c9
포드가 중지되고 다시 시작될 때 특정 PV에 대한 StatefulSet의 포드 연결을 확인
$ kubectl exec -it statefulset-demo-0 -- sh
# cat /var/www/html/index.html
cat: /var/www/html/index.html: No such file or directory
# echo Test webpage in a persistent volume!>/var/www/html/index.html
chmod +x /var/www/html/index.html#
# cat /var/www/html/index.html
Test webpage in a persistent volume!
# exit
## PVC에서 파일을 업데이트한 Pod를 삭제
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
statefulset-demo-0 1/1 Running 0 10s
statefulset-demo-1 1/1 Running 0 4m7s
statefulset-demo-2 1/1 Running 0 3m47s
StatefulSet이 statefulset-demo-0 포드를 자동으로 다시 시작
하는 것을 볼 수 있다.
$ kubectl exec -it statefulset-demo-0 -- sh
# cat /var/www/html/index.html
Test webpage in a persistent volume!
# exit
ConfigMap은 Pod에서 구성을 분리
하며 즉, 여러 Pod의 사양에 동일한 정보를 입력
할 필요가 없다.
이 구성을 한 곳에 저장하고 단일 정보 소스로 유지해서 구성 드리프트를 방지
한다는 장점이 있다.
configMap을 사용하면 구성 파일, 명령줄 인수, 환경 변수, 포트 번호 및 기타 구성 아티팩트를 저장하고 컨테이너 내에서 사용할 수 있다.
이를 통해 애플리케이션이 Kubernetes를 인식할 필요 없이 더 쉽게 이동하고 관리할 수 있다.
간단한 kubectl create 명령을 사용하여 리터럴 값, 파일 및 디렉토리에서 ConfigMap을 생성할 수 있고 이러한 데이터 소스에는 키-값 쌍
이 포함되어 있다.
각각이 어떻게 생성되는지 살펴보자.
여기에서 리터럴 값을 사용하여 'demo'라는 ConfigMap이 생성되는데
두 개의 키-값 쌍이 정의된다.
lab.difficulty=easy
및 lab.resolution=high
같은 여러 키-값 쌍을 추가할 수 있다.
kubectl 또는 Google Cloud Console을 사용하여 위의 사진과 가이configMaps의 세부정보를 볼 수 있다.
이번에는 from-file 구문
을 사용하여 만든 또 다른 ConfigMap을 살펴보자
이러한 파일에는 여러 키-값이 포함되어 있으며 ConfigMap에 여러 파일을 추가할 수 있다.
이러한 파일을 소스 코드 제어 시스템에 체크인하여 버전 및 기록을 유지할 수 도 있다.
키 이름을 지정하려는 경우 소스 파일 이름을 사용하는 대신 키 이름을 추가할 수 있다.
여기의 구문은 이전 슬라이드의 기본 from-file 구문과 매우 유사하지만 여기에는 color.properties라는 파일에 사용되는 키의 이름을 다음과 같이 변경하기 위해 추가 키 값(Color)이 삽입, ui.properties라는 파일에 사용된 키의 이름을 Graphics라는 키로 변경한다.
항상 그렇듯이 kubectl 또는 Google Cloud Console을 사용하여 ConfigMap의 콘텐츠를 확인할 수 있으며 동일한 디렉토리에서 각 파일을 지정하는 대신 디렉토리 이름을 직접 사용
할 수 있다.
디렉토리 내의 모든 파일이 ConfigMap에 추가되고 여기서 디렉토리가 생성되고 파일이 저장된다.
ConfigMap에 디렉토리를 추가하면 디렉토리에 포함된 파일도 추가된다.
ConfigMap은 매니페스트에서 만들 수도 있다.
여기에서 데이터는 이전 예제와 동일하며 kubectl apply를 사용하여 이 매니페스트를 적용하기만 하면 ConfigMap이 생성된다.
Pod는 컨테이너 환경 변수
, 포드 명령
에서 또는 볼륨 생성
의 세 가지 방식으로 ConfigMap을 참조한다.
여기서 단일 ConfigMap은 포드에서 컨테이너 환경 변수로 사용되며 env 필드 내에서 컨테이너 환경 변수의 이름은 VARIABLE_DEMO로 지정한다.
값은 configMapKeyRef
를 사용하여 검색되며 동일하거나 다른 ConfigMap에서 여러 변수를 추가할 수 있다.
컨테이너 환경 변수가 정의되면 여기에 표시된 구문을 사용하여 포드 매니페스트 명령 내에서 사용할 수 있다.
환경 변수 이름 앞에 달러 기호
와 여는 괄호
를 넣고 그 뒤에 닫는 괄호
를 넣는다.
Linux 셸에 익숙하다면 오류처럼 보일 수 있으나, Linux 셸은 다른 용도로 동일한 구문을 사용한다.
이렇게 하면 이미지 콘텐츠에서 구성 아티팩트를 분리하여 컨테이너화된 애플리케이션의 이식성을 유지할 수 있다는 장점이 있다.
결과적으로 kubelet
은 Pod에 접근하여 나중에 이러한 값을 수정할 수 없다
.
임시 볼륨
에 ConfigMap 데이터를 추가할 수도 있다.
예제에서는 config-volume이라는 볼륨이 볼륨 섹션에 demo라는 ConfigMap과 함께 생성되고 결과적으로 이 포드에 대한 ConfigMap 볼륨이 생성된다.
ConfigMap의 모든 데이터는 이 ConfigMap 볼륨에 파일로 저장되고 이 볼륨은 mountPath 디렉터리를 사용하여 컨테이너에 마운트된다.
ConfigMap 볼륨이 이미 마운트되어 있고 소스 ConfigMap이 변경되면 프로젝션된 키가 몇 초 또는 몇 분 정도 이내에 업데이트된다.
그보다 더 빠르게 변경되는 구성 데이터가 있는 경우에는 ConfigMap을 사용하는 대신 Pod에 값을 제공하는 마이크로서비스를 구현해야 한다.
ConfigMap과 유사하지만 Kubernetes 애플리케이션은 ConfigMap이 아닌 비밀을 사용하여 암호, 토큰 및 SSH 키와 같은 민감한 정보를 저장하는 것이 관례이다.
보안 비밀을 사용하면 자체 제어 영역에서 민감한 정보를 관리할 수 있으며 또한 Kubernetes가 실수로 이 데이터를 로그에 출력하지 않도록
하는 데 도움이 된다.
이러한 시크릿이란 이름에도 불구하고 Kubernetes Secrets는 진정한 비밀이 아니다.
애플리케이션이 고부가가치 자산을 관리하거나 엄격한 규제 요구 사항에 따라 Google Cloud KMS와 같은 키 관리 시스템을 고려해야 한다.
전체 비밀 관리에는 세 가지 유형의 비밀이 존재한다.
일반 유형
: 파일, 디렉터리 또는 리터럴 값에서 비밀을 만들 때 사용된다.
TLS 유형
: 기존 공개 개인 암호화 키 쌍을 사용
PEM 형식
으로 인코딩된 공개 키 인증서를 제공하고 해당 인증서의 개인 키도 제공해야 한다.Docker 레지스트리 비밀 유형
: 이미지 레지스트리에 대한 자격 증명을
kubelet 이 Pod를 대신하여 Docker 레지스트리에서 프라이빗 이미지를 가져올 수 있도록 한다.
ConfigMap과 마찬가지로 일반 유형 비밀은 키-값 쌍에 저장되나 Secrets에서는 base-64로 인코딩된 문자열로 값을 제공한다.
base-64 인코딩은 암호화가 아니므로 주의
그런 다음 이러한 인코딩된 문자열을 시크릿 매니페스트에서 사용할 수 있다.
다음은 제네릭 형식인데 파일에는 단일 항목이 있어야 하고, 모든 항목은 파일 이름을 키로, 항목을 값으로 함께 패키징, 키의 이름도 지정할 수 있다.
이것은 ConfigMap과 유사하지만 한 가지 차이점은 인코딩된 문자열이 GCP 콘솔에 표시되지 않으며 kubectl, get 또는 describe 명령어를 사용할 때 인코딩된 문자열이 표시되지 않는다는 점
이다.
시크릿은 해당 컨테이너에서 포드의 환경 변수
로 사용할 수 있다.
다음은 SECRET_USERNAME과 SECRET_PASSWORD라는 두 개의 환경 변수가 생성된 예시인데 이러한 변수는 해당 키와 함께 demo-secret이라는 비밀을 참조하며 이러한 변수는 자동으로 디코딩
된다.
팟(Pod)에서 비밀 볼륨
을 생성하여 비밀을 사용할 수도 있다.
여기서 storagesecrets라는 비밀 볼륨이 생성되고 demo-secret이라는 비밀을 참조한다.
이 볼륨은 읽기 전용 액세스
로 컨테이너에 마운트
된다.
이 볼륨은 팟(Pod) 내의 여러 컨테이너에서 사용할 수 있다는 장점이 있다.
비밀 키는 또한 특정 경로에 프로젝션
될 수 있는데 이 경우 비밀의 암호 키가 투영되지 않으며 암호 키가 필요한 경우 항목 필드 아래에도 나열되어야 한다.
ConfigMap과 마찬가지로 kubelet은 비밀 볼륨을 업데이트된 상태로 유지하기 위해 비밀과 주기적으로 동기화하는데 이미 볼륨으로 연결된 비밀이 변경
되면 결국 키와 값이 업데이트
된다.
kubectl명령 및 매니페스트 파일을 사용하여 시크릿 생성
kubectl명령 및 매니페스트 파일을 사용하여 ConfigMap 생성
환경 변수 또는 탑재된 볼륨을 사용하여 컨테이너의 암호 사용
환경 변수 또는 마운트된 볼륨을 사용하여 컨테이너에서 ConfigMap 사용
이 작업에서 Google Cloud 서비스에 액세스하기 위해 Google Cloud로 컨테이너를 인증한다.
Cloud Pub/Sub 주제 및 구독을 설정하고 GKE에서 실행 중인 컨테이너에서 Cloud Pub/Sub 주제에 액세스하려고 시도하고 액세스 요청이 실패하는지 확인할 것이다.
게시/구독 주제에 제대로 액세스하려면 자격 증명을 사용하여 서비스 계정을 만들고 해당 자격 증명을 Kubernetes Secrets를 통해 전달해야한다.
권한이 없는 서비스 계정 준비
Google Cloud Console의 탐색 메뉴 ( 탐색 메뉴 아이콘) 에서 IAM 및 관리자 > 서비스 계정을 클릭 > 서비스 계정 만들기를 클릭
서비스 계정 이름 텍스트 상자에 no-permissions를 입력 후 만들기 및 계속 을 클릭 계속을 클릭한 다음 완료를 클릭하자.
목록에서 권한이 없는 서비스 계정을 찾고 연결된 이메일 주소를 나중에 사용할 수 있도록 텍스트 파일에 복사해두자.
no-permissions@qwiklabs-gcp-04-7ba763485212.iam.gserviceaccount.com
Cluster 생성
이 클러스터를 만들 때 이전에 만든 서비스 계정을 지정한다.
적절한 권한이 있는 서비스 계정의 필요성을 설명하기 위해 해당 서비스 계정에는 다른 Google Cloud 서비스에 대한 권한이 없으므로 나중에 배포할 Cloud Pub/Sub 테스트 애플리케이션에 연결할 수 없지만 나중에 해결해보자.
$ export my_zone=us-central1-a
export my_cluster=standard-cluster-1
student_03_9e334c7bfa92@cloudshell:~ (qwiklabs-gcp-04-7ba763485212)$ source <(kubectl completion bash)
student_03_9e334c7bfa92@cloudshell:~ (qwiklabs-gcp-04-7ba763485212)$ export my_service_account=no-permissions@qwiklabs-gcp-04-7ba763485212.iam.gserviceaccount.com
student_03_9e334c7bfa92@cloudshell:~ (qwiklabs-gcp-04-7ba763485212)$
$ gcloud container clusters create $my_cluster \
--num-nodes 2 --zone $my_zone \
--service-account=$my_service_account
$ gcloud container clusters get-credentials $my_cluster --zone $my_zone
Cloud Pub/Sub를 설정하고 주제에서 읽을 애플리케이션을 배포
Cloud Shell에서 다음 명령어를 입력하여 Pub/Sub 구성요소의 환경 변수를 설정
export my_pubsub_topic=echo
export my_pubsub_subscription=echo-read
echo라는 Cloud Pub/Sub 주제와 해당 주제와 연결된 echo-read라는 구독을 만들려면 다음 gcloud명령어를 실행
$ gcloud pubsub topics create $my_pubsub_topic
gcloud pubsub subscriptions create $my_pubsub_subscription \
--topic=$my_pubsub_topic
Created topic [projects/qwiklabs-gcp-04-7ba763485212/topics/echo].
Created subscription [projects/qwiklabs-gcp-04-7ba763485212/subscriptions/echo-read].
Cloud Pub/Sub 주제에서 읽을 애플리케이션 배포
Cloud Pub/Sub 주제에서 읽을 수 있는 컨테이너로 배포를 만듭니다. Cloud Pub/Sub 주제를 구독하고 읽으려면 특정 권한이 필요하므로 Cloud Pub/Sub에 성공적으로 연결하려면 이 컨테이너에 자격 증명을 제공해야 한다.
$ git clone https://github.com/GoogleCloudPlatform/training-data-analyst
$ ln -s ~/training-data-analyst/courses/ak8s/v1.1 ~/ak8s
$ cd ~/ak8s/Secrets/
$ $ cat pubsub.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pubsub
spec:
selector:
matchLabels:
app: pubsub
template:
metadata:
labels:
app: pubsub
spec:
containers:
- name: subscriber
image: gcr.io/google-samples/pubsub-sample:v1
$ kubectl apply -f pubsub.yaml
deployment.apps/pubsub created
$ kubectl get pods -l app=pubsub
NAME READY STATUS RESTARTS AGE
pubsub-7bf44585c8-c86nw 0/1 Error 1 (17s ago) 24s
## 오류 발생
$ kubectl logs -l app=pubsub
return api_call(*args)
File "/usr/local/lib/python3.8/site-packages/google/gax/api_callable.py", line 376, in inner
return a_func(*args, **kwargs)
File "/usr/local/lib/python3.8/site-packages/google/gax/retry.py", line 125, in inner
raise errors.RetryError(
google.gax.errors.RetryError: RetryError(Exception occurred in retry method that was not classified as transient, caused by <_InactiveRpcError of RPC that terminated with:
status = StatusCode.PERMISSION_DENIED
details = "User not authorized to perform this action."
debug_error_string = "UNKNOWN:Error received from peer ipv4:142.250.1.95:443 {created_time:"2023-07-24T07:57:23.125703917+00:00", grpc_status:7, grpc_message:"User not authorized to perform this action."}"
>)
# 권한 오류
서비스 계정 사용자 인증 정보 만들기
이제 새 서비스 계정을 만들고 테스트 애플리케이션이 사용하려는 게시/구독 구독에 대한 액세스 권한을 부여해보자.
GKE 클러스터 노드의 서비스 계정을 변경하는 대신 서비스 계정의 JSON 키를 생성한 다음 Kubernetes 보안 비밀을 통해 JSON 키를 포드에 안전하게 전달한다.
Google Cloud Console의 탐색 메뉴 에서 IAM 및 관리자 > 서비스 계정을 클릭
서비스 계정 만들기
서비스 계정 이름 텍스트 상자 에 입력 pubsub-app
하고 만들기 및 계속을 클릭
역할 선택 드롭다운 목록 에서 Pub/Sub > Pub/Sub 구독자 선택
역할이 나열되는지 확인한 다음 계속을 클릭 하고 완료
서비스 계정 개요 화면에서 서비스 계정 오른쪽에 있는 세 개의 점을 클릭한 pubsub-app다음 키 관리를 선택
서비스 계정의 자격 증명이 포함된 JSON 키 파일이 컴퓨터에 다운로드 되고 이 키 파일을 사용하여 Cloud Pub/Sub API에 인증하도록 샘플 애플리케이션을 구성한다.
하드 드라이브에서 방금 다운로드한 JSON 키를 찾아 파일 이름을 credentials.json 으로 변경
자격 증명을 Secrets로 가져오기
Cloud Shell 도구 모음의 세 점( 더 많은 아이콘)을 클릭하여 추가 옵션을 표시하여 업로드 클릭 하고 credentials.json로컬 컴퓨터에서 Cloud Shell VM으로 파일을 업로드한 다음 업로드를 클릭하자
$ ls ~/
ak8s credentials.json README-cloudshell.txt training-data-analyst
이름이 credentials.json 인 Kubernetes Secret에 키 파일(pubsub-key)을 저장
$ kubectl create secret generic pubsub-key \
--from-file=key.json=$HOME/credentials.json
secret/pubsub-key created
$ rm -rf ~/credentials.json
시크릿으로 애플리케이션 구성
Pod 사양에 볼륨을 추가하고 이 볼륨에는 비밀이 포함되어 있다.
비밀 볼륨은 애플리케이션 컨테이너에 마운트된다.
환경 변수(GOOGLE_APPLICATION_CREDENTIALS)는 비밀 볼륨 마운트의 키 파일을 가리키도록 설정된다.
환경 변수(GOOGLE_APPLICATION_CREDENTIALS)는 Cloud 클라이언트 라이브러리(이 경우 Python용 Cloud Pub/Sub 클라이언트)에서 자동으로 인식된다.
$ cat pubsub-secret.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pubsub
spec:
selector:
matchLabels:
app: pubsub
template:
metadata:
labels:
app: pubsub
spec:
volumes:
- name: google-cloud-key
secret:
secretName: pubsub-key
containers:
- name: subscriber
image: gcr.io/google-samples/pubsub-sample:v1
volumeMounts:
- name: google-cloud-key
mountPath: /var/secrets/google
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
$ kubectl apply -f pubsub-secret.yaml
deployment.apps/pubsub configured
$ kubectl get pods -l app=pubsub
NAME READY STATUS RESTARTS AGE
pubsub-848c646f58-6mrd9 1/1 Running 0 32s
Cloud Pub/Sub 메시지 수신 테스트
이제 애플리케이션을 구성했으므로 이전 실습에서 생성한 Cloud Pub/Sub 주제에 메시지를 게시
$ gcloud pubsub topics publish $my_pubsub_topic --message="Hello, world!"
messageIds:
- '8693683151214560'
$ kubectl logs -l app=pubsub
Pulling messages from Pub/Sub subscription...
[2023-07-24 08:13:04.131038] Received message: ID=8693683151214560 Data=b'Hello, world!'
[2023-07-24 08:13:04.131158] Processing: 8693683151214560
[2023-07-24 08:13:07.133426] Processed: 8693683151214560
kubectl 패턴을 따르고 파일( ) 또는 리터럴( ) kubectl create configmap [NAME][DATA]에 대한 플래그(--from-file--from-literal)를 추가하여 ConfigMap을 생성하는 데 사용하자.
kubectl 명령을 사용하여 ConfigMap 생성
$ kubectl create configmap sample --from-literal=message=hello
configmap/sample created
$ kubectl describe configmaps sample
Name: sample
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
message:
----
hello
BinaryData
====
Events: <none>
# 다음으로 파일에서 ConfigMap을 생성
# 이 파일은 (sample2.properties) 나를 위해 제공되었고 몇 가지 샘플 데이터를 포함한다.
$ kubectl create configmap sample2 --from-file=sample2.properties
configmap/sample2 created
$ kubectl describe configmaps sample2
Name: sample2
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
sample2.properties:
----
message2=world
foo=bar
meaningOfLife=42
BinaryData
====
Events: <none>
매니페스트 파일을 사용하여 ConfigMap 만들기
config-map-3.yaml 에는 sample3 이라는 ConfigMap 정의가 포함되어 있다.
이 ConfigMap을 사용하여 컨테이너 내부의 데이터를 노출하는 두 가지 다른 방법을 알아보자.
$ cat config-map-3.yaml
apiVersion: v1
data:
airspeed: africanOrEuropean
meme: testAllTheThings
kind: ConfigMap
metadata:
name: sample3
namespace: default
selfLink: /api/v1/namespaces/default/configmaps/sample3
$ kubectl apply -f config-map-3.yaml
$ kubectl describe configmaps sample3
Name: sample3
Namespace: default
Labels:
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","data":{"airspeed":"africanOrEuropean","meme":"testAllTheThings"},"kind":"ConfigMap","metadata":{"annotations":{
},"name...
Data
====
airspeed:
----
africanOrEuropean
meme:
----
testAllTheThings
Events:
이제 애플리케이션에서 적절하게 분리되고 클러스터에서 사용할 수 있는 일부 비보호 비밀, 암호화되지 않은 구성 정보가 있다.
다양한 옵션을 보여주기 위해 세 가지 방법으로 ConfigMaps를 사용하여 이 작업을 수행했지만 실제로는 일반적으로 YAML 구성 파일
접근 방식인 한 가지 방법을 주로 선태간다.
구성 파일은 나중에 프로세스를 쉽게 반복할 수 있도록 저장한 값의 레코드를 제공한다는 장점이 있다.
환경 변수를 사용하여 컨테이너에서 ConfigMap 사용
환경 변수를 사용하여 컨테이너 내부에서 ConfigMap에 액세스하려면 Pod 정의 (configMapKeyRefs)를 하나 이상 포함하도록 업데이트해야 한다.
아래의 파일은 ConfigMap에서 컨테이너로 환경 변수를 가져오기 위해 pubsub-configmap.yaml 파일 끝에 .env: 과 같은 추가 설정을 포함하는 Cloud Pub/Sub 데모 배포의 업데이트된 버전이다.
-------------
- name: INSIGHTS
valueFrom:
configMapKeyRef:
name: sample3
key: meme
-------------
$ kubectl apply -f pubsub-configmap.yaml
## 이제 애플리케이션은 INSIGHTS이라는 값을 가진 환경 변수에 액세스할 수 있다.
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
pubsub-77df8f8c6-krfl2 1/1 Running 0 4m
$ kubectl exec -it [MY-POD-NAME] -- sh
# printenv
## INSIGHTS=testAllTheThings목록에 나타나야 한다.
마운트된 볼륨을 사용하여 컨테이너에서 ConfigMap 사용
환경 변수에 저장하는 대신 ConfigMap 데이터로 볼륨을 채울 수 있는데,
이 다플로이먼트에서는 이 작업의 앞부분에서 생성한 이름의 ConfigMap이 Pod 사양에서 sample-3으로 호출되는 config-3 볼륨으로도 추가된다.
그러면 볼륨 config-3이 path의 컨테이너 내부에 마운트
된다.(/etc/config)
.
환경 변수를 사용하여 ConfigMap을 가져오는 원래 방법도 구성되었다.
$ cat pubsub-configmap2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pubsub
spec:
selector:
matchLabels:
app: pubsub
template:
metadata:
labels:
app: pubsub
spec:
volumes:
- name: google-cloud-key
secret:
secretName: pubsub-key
- name: config-3
configMap:
name: sample3
containers:
- name: subscriber
image: gcr.io/google-samples/pubsub-sample:v1
volumeMounts:
- name: google-cloud-key
mountPath: /var/secrets/google
- name: config-3
mountPath: /etc/config
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/secrets/google/key.json
- name: INSIGHTS
valueFrom:
configMapKeyRef:
name: sample3
key: meme
$ kubectl apply -f pubsub-configmap2.yaml
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
pubsub-747cf8c545-ngsrf 1/1 Running 0 30s
pubsub-df6bc7b87-vb8cz 1/1 Terminating 0 4m
$ kubectl exec -it [MY-POD-NAME] -- sh
# cd /etc/config
# ls
airspeed meme
# cat airspeed
africanOrEuropean#
# exit
좋은 글이네요. 공유해주셔서 감사합니다.