[CKA] 컨테이너 저장소 및 보안
Docker Volume (가장 기본적인 컨테이너 저장소)
pod는 기본적으로 두가지 Layer기반으로 운영된다. 컨테이너 레이어(읽기 + 쓰기 가능)과 이미지 레이어(읽기 가능) 읽기만 가능한 이미지 레이어의 경우 이미 docker를 통해 이미지화가 되어 있기에 수정이 불가능하다. (이미지 파일의 소스코드는 수정할 수 없음)
기본적으로 컨테이너 레이어는 휘발성이다. 그렇기에 volume이라는 Docker에서 제공하는 별도의 저장소를 활용할 수 있다.
이런 Volume을 기반으로 컨테이너 환경에서 저장소는 더욱 발전되어진다.
Docker Volume → emptyDir, hostPath, persistentVolumeClaim…
PersistentVolume
기존의 Volume은 pod가 재시작되지 않고 아예 삭제되면 저장소가 날라간다. 이러한 기능을 극복하기 위해 PersistentVolume라는 개념이 나왔다.
더이상 삭제될 수 있는 emptyDit를 사용하지 않고 PersistentVolume를 사용하자!
PersistentVolume yaml
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 100Mi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /pv/log
PersistentVolumeClaim
PV를 만들었으면 어떤식으로 매핑 해야할까? 이전에 만든 yaml을 기반으로 pod가 해당 PV를 사용하기 위해 요청서가 필요하다.
Pod: 🙋 저는 스토리지 1G와 읽고쓰기 정책이 필요한 PV가 필요해요!
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
이렇게 하면 쿠버네티스는 조건에 맞는 PV를 자동으로 바인딩 시킨다.
이를 통해 개발자는 직접 PV를 만들지 않고 PVC를 통해 PV를 자동으로 할당 받으며 동적으로 저장소를 관리할 수 있게 된다.
PV를 사용하는 Pod yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
name: webapp
spec:
containers:
- name: event-simulator
image: kodekloud/event-simulator
env:
- name: LOG_HANDLERS
value: file
volumeMounts:
- mountPath: /log
name: log-volume
volumes:
- name: log-volume
persistentVolumeClaim:
claimName: claim-log-1
webapp은 PVC를 참조하고 해당 PVC가 어떤 PV와 연결이 되어있는지는 관심이 없다.
즉 간접적으로 PV는 모른채 PV를 이용할 수 있는 패턴이 완성된다.
외부 저장소 활용 (Container Storage Driver)
기존 Azure Key Vault에서 활용한 CSI처럼 쿠버네티스는 외부 저장소를 사용이 가능한데, 각 외부 저장소와 컨테이너의 표준을 맞추기 위해 사용되는 인터페이스가 CSI이다.
즉 컨테이너 → CSI 드라이버 → 외부 Storage의 흐름으로 이어진다.
CSI를 사용하는 저장소
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: csi-hostpath-sc
resources:
requests:
storage: 1Gi
TLS in Kubernetes
통신 데이터를 암호화해 인증을 보장해주는 보안 프로토콜 TLS는 k8s에서의 내부 통신에서 주로 사용된다. kube-apisever, kubelet, ETCD 등등… control-plane의 구성요소들과 k8s의 작업을 위해 통신시 사용되어지고 있다.
일반적으로 인증서는 /etc/kubernetes/pki 경로에 저장되며 추후 trouble shooting이나 인증서 관련 Node 이슈가 발생하면 해당 경로를 조사해보면 된다.
인증서의 유효기간 확인하기
1
kubeadm certs check-expiration
유효기간 만료 이외에도 누락, 설정(kubectl config) 등 다양한 인증서 관련 이슈로 TLS 관련 시나리오가 출제된다.
Authentication
k8s에서 인증은 사용자가 누구인지 확인하는 단계로 클러스터에 요청한 사람의 신원을 확인하는 방식이다. 이러한 사람의 신원에 대한 정보는 kube-config에 저장되어 있으며, 권한을 부여 받으며 클러스터에 접근하여 작업(get pods, delete secrets등)을 할 수 있도록 역할을 지정한다.
이러한 권한을 주는데는 주로 RBAC 방식을 사용한다.
권한을 확인하기
1
2
3
4
5
kubectl auth can-i <verb> <resource> [flags]
kubectl auth can-i get pods # get pods를 할 수 있는 사용자 목록을 원합니다.
kubectl auth can-i delete secrets --namespace=dev # dev ns에서 secret을 지울 수 있는 사용자들을 원합니다.
kubectl auth can-i list nodes --as=dev-user dev-user # dev-user는 node list를 나열 할 수 있는지요?
get pods를 할 수 있는 사용자는 누구인가? --as 특정 사용자로 어떤 node에 접근할 수 있는지 시뮬레이션 하며 --namespace에 대한 권한을 확인한다.
RBAC이란?
k8s에서 리소스에 대한 접근을 역할(Role)기반으로 정의하고 사용자나 서비스 계정에 연결하는 시스템이다.
단순히 k8s에서만 사용되지 않고 Azure 같은 곳에서도 사용되는 역할 개념이다.
RBAC 구성요소
k8s에서 RBAC의 구성요소는 크게 4가지 리소스로 구분된다. Role 같은 경우 특정 네임스페이스의 범위에 대한 권한을 정의하고, 이보다 더 큰 클러스터에 대한 권한은 ClusterRole에서 정의를 한다.
또한 RoleBinding은 만든 Role을 사용자/서비스 계정에 연결하며, ClusterRoleBinding은 클러스터 단위로 Role을 연결한다.
Role 예시 yaml
1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
dev 네임스페이스에 pod 조회 권한을 부여하는 Role 이런 Role을 만든다고 바로 사용자에게 연결이 되지는 않는다. RoleBinding으로 role을 연결 시켜줘야한다.
RoleBinding 예시 yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: dev
subjects:
- kind: ServiceAccount
name: my-sa
namespace: dev
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
이렇게 RBAC을 설정하고 auth명령으로 확인 할 수 있다.
1
kubectl auth can-i get pods --as jane -n dev
ClusterRole 예시 yaml
1
2
3
4
5
6
7
8
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list"]
ClusterRoleBinding 예시 yaml
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-nodes-binding
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-reader
apiGroup: rbac.authorization.k8s.io
ServiceAccount
저번 Azure Key Vault를 pod에 연동시킬 때 사용했던 SA(ServiceAccount)가 다시 등장했다.
그때와 동일하게 SA는 시스템(user가 아닌)이 role을 할당받을 수 있게 사용되는 개념이다.
이런 SA는 일반적으로 네임스페이스 안에 디폴트적으로 생성되지만, 사용자가 원한다면 아래와 같이 SA를 생성 할 수 있다.
my-sa라는 sa를 네임스페이스 dev에 생성
1
kubectl create serviceaccount my-sa -n dev
이렇게 생성된 SA는 Pod에 적용할 수 있는데 pod의 yaml에 아래와 같이 적용하면된다.
my-sa의 SA를 가진 pod
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: mypod
namespace: dev
spec:
serviceAccountName: my-sa
containers:
- name: app
image: nginx
기본적으로 SA마다 하나의 JWT 토큰이 발급되고, 이러한 작업을 통해 pod에 자동으로 마운트되어진다.
이 pod가 API와 통신할때 누구인지 식별하는 수단이 되어진다.
RBAC을 통해 접근을 제어할 경우 기존의 User의 RoleBinding과 동일하게 설정하여 pod에 권한을 넣어줄 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: dev
subjects:
- kind: ServiceAccount
name: my-sa
namespace: dev # group과는 달리 SA를 사용할때 ns는 무조건 지정해줘야함.
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
kubectl get sa를 통해 현재 존재하는 SA를 확인할 수 있으며, describe 명령어를 통해 SA에게 할당받은 토큰과 secret정보를 얻을 수 있다.



