리소스 제한과 보안
1. Authentication API 인증 요청
- User/Group, Service Account
2. Authentication
- user 또는 application이 API에 접근을 허가 받는 과정 (인증서/토큰)
3. Authorization
- RBAC 모델 기반
- 요청 ID에 적절한 role이 있는지 확인
4. Admission Control (승인 제어)
- 요청이 올바른 형식인지 판별
- 요청 내용에 대한 검증이나 요청 내용을 강제로 변경할 때 사용
- 쿠버네티스에서 특정 기능을 제한(validate) 또는 변경(mutate)
Admission Controller 플러그인
- Resourcequota: 네임스페이스에서 사용할 수 있는 전체 리소스 제한
- LimitRange: 네임스페이스 단위로 pod나 container의 리소스 사용량을 제한 설정
- History를 이용한 이전 버전으로 roll back 지원
LimitRange
- Pod나 컨테이너에 적용할 수 있는 request, limit의 하한값과 상한값을 미리 정의
- Pod 또는 Container에 적용할 수 있는 최대/최소 리소스(cpu, memory) limit과 request 설정
- Container에 default로 적용한 cpu, memory 값 설정
- 최대/최소 적용가능한 PersistentVolumeClaim 제한
- 하나의 네임스페이스에 하나의 LimitRange를 생성
기본적으로 컨테이너는 쿠버네티스 클러스터에서 무제한 컴퓨팅 리소스로 실행된다. 리소스 쿼터를 사용하면 클러스터 관리자는 네임스페이스별로 리소스 사용과 생성을 제한할 수 있다. 네임스페이스 내에서 파드나 컨테이너는 네임스페이스의 리소스 쿼터에 정의된 만큼의 CPU와 메모리를 사용할 수 있다. 하나의 파드 또는 컨테이너가 사용 가능한 모든 리소스를 독점할 수 있다는 우려가 있다. 리밋레인지는 네임스페이스에서 리소스 할당(파드 또는 컨테이너)을 제한하는 정책이다.
리밋레인지는 다음과 같은 제약 조건을 제공한다.
- 네임스페이스에서 파드 또는 컨테이너별 최소 및 최대 컴퓨팅 리소스 사용량을 지정한다.
- 네임스페이스에서 스토리지클래스별 최소 및 최대 스토리지 요청을 지정한다.
- 네임스페이스에서 리소스에 대한 요청과 제한 사이의 비율을 지정한다.
- 네임스페이스에서 컴퓨팅 리소스에 대한 기본 요청/제한을 설정하고 런타임에 있는 컨테이너에 자동으로 설정한다.
# vi /data/ckad/limit-range.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: example-lr
spec:
limits:
- type: Pod
min:
cpu: 50m
memory: 5Mi
max:
# 1000m = 1
cpu: 1
memory: 1Gi
- type: Container
defaultRequest:
cpu: 100m
memory: 10Mi
default:
cpu: 200m
memory: 100Mi
min:
cpu: 50m
memory: 5Mi
max:
cpu: 1
memory: 1Gi
maxLimitRequestRatio:
cpu: 4
memory: 10
- type: PersistentVolumeClaim
min:
storage: 1Gi
max:
storage: 10Gi
ResourceQuota
- Namespace에서 사용할 수 있는 전체 리소스 제한
- CPU/Memory에 대해 리소스 제한
- 생성 가능한 Object(Pod, Deployment, Service 등)수 제한
여러 사용자나 팀이 정해진 수의 노드로 클러스터를 공유할 때 한 팀이 공정하게 분배된 리소스보다 많은 리소스를 사용할 수 있다는 우려가 있다. 리소스 쿼터는 관리자가 이 문제를 해결하기 위한 도구이다. ResourceQuota 오브젝트로 정의된 리소스 쿼터는 네임스페이스별 총 리소스 사용을 제한하는 제약 조건을 제공한다. 유형별로 네임스페이스에서 만들 수 있는 오브젝트 수와 해당 네임스페이스의 리소스가 사용할 수 있는 총 컴퓨트 리소스의 양을 제한할 수 있다.
리소스 쿼터는 다음과 같이 작동한다.
- 다른 팀은 네임 스페이스에서 작업한다. 이것은 RBAC으로 설정할 수 있다.
- 관리자는 각 네임스페이스에 대해 하나의 리소스쿼터를 생성한다.
- 사용자는 네임스페이스에서 리소스(파드, 서비스 등)를 생성하고 쿼터 시스템은 사용량을 추적하여 리소스쿼터에 정의된 하드(hard) 리소스 제한을 초과하지 않도록 한다.
- 리소스를 생성하거나 업데이트할 때 쿼터 제약 조건을 위반하면 위반된 제약 조건을 설명하는 메시지와 함께 HTTP 상태 코드 403 FORBIDDEN으로 요청이 실패한다.
- cpu, memory와 같은 컴퓨트 리소스에 대해 네임스페이스에서 쿼터가 활성화된 경우 사용자는 해당값에 대한 요청 또는 제한을 지정해야 한다. 그렇지 않으면 쿼터 시스템이 파드 생성을 거부할 수 있다.
# vi /data/ckad/resourcequota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: example-rq
spec:
hard:
requests.cpu: 400m
requests.memory: 200Mi
limits.cpu: 600m
limits.memory: 500Mi
pods: 10
replicationcontrollers: 5
secrets: 10
configmaps: 10
persistentvolumeclaims: 4
services: 5
services.loadbalancers: 1
services.nodeports: 2
ssd.storageclass.storage.k8s.io/persistentvolumeclaim: 2
# vi limit.yaml
apiVersion: v1
kind: LimitRange
metadata:
name: devops-limit
namespace: devops
spec:
limits:
- type: Container
default:
cpu: 200m
memory: 50Mi
defaultRequest:
cpu: 200m
memory: 50Mi
kubectl apply -f limit.yaml
kubectl get -n devops limitranges
kubectl describe -n devops limitranges
kubectl create quota devops-quota --hard=pods=10,services=5 -n devops
kubectl get resourcequotas devops-quota -n devops
kubectl describe resourcequotas devops-quota -n devops
Pod Resource 요청 및 제한
-
Resource Requests: Pod를 실행하기 위해 필요한 리소스 양을 요청
-
Resource Limits: Pod가 사용할 수 있는 최대 리소스 양을 지정
-
Pod Resources 단위
- Kubernetes CPU는 AWS의 vCPU, GCP의 core, Azure의 vCore, Hyper-Threading을 지원하는 Bare Metal 프로세서의 하이퍼스레드와 동일
- 대부분의 Pod는 CPU 전체가 필요하지 않기 때문에 요청과 상한은 millicpus (또는 millicores)로 표시된다.
- 1000m (millicores) = 1 core = 1 CPU = 1 AWS vCPU = 1 GCP Core
- 100m (millicores) = 0.1 core = 0.1 CPU = 0.1 AWS vCPU = 0.1 GCP Core
- 메모리는 바이트나 mebibytes(MiB)로 측정된다.
하이퍼 스레딩이란 코어를 가상적으로 늘려주는 기술이며, 1코어를 가지고 있는 CPU를 2코어 CPU로 가상적으로 늘릴 때 이를 1코어 2스레드라고 말합니다.
- 특권명령(previleged instruction): 시스템 요소들과 소통할 수 있는 명령 - OS만 가능
- OS는 특권명령 떄문에 하나의 하드웨어 시스템당 하나밖에 돌아갈 수 없음
- 일반 프로그램은 특권 명령이 필요 없기 때문에 많은 프로그램을 동시에 수행 가능
컨테이너 는 모든 운영 체제에서 애플리케이션을 실행하는 데 필요한 모든 파일과 구성을 저장하는 소프트웨어 패키지입니다. 개발자들은 컨테이너를 사용하여 소프트웨어 개발의 복잡성을 줄이고 애플리케이션을 배포할 때 효율성을 높입니다. 컨테이너형 애플리케이션은 기본 운영 체제와는 독립되어 있기 때문에, 일관된 성능으로 퍼블릭, 하이브리드 또는 온프레미스 클라우드에서 실행할 수 있습니다.
하이퍼바이저와 컨테이너 모두 다른 소프트웨어 계층에서 가상화를 제공합니다. 하이퍼바이저는 소프트웨어 환경에서 하드웨어를 추상화합니다. 반대로 컨테이너는 컨테이너 엔진이 운영 체제를 추상화하는 환경에서 실행됩니다.
PV(Para Virtualization) vs HVM(Hardware Virtual Machine)
PV는 반가상화로 guest os가 hypervisor를 통해 hardware를 제어하고 가볍기 때문에 퍼포먼스가 좋지만 guest os에 수정이 필요하다. 반면에 HVM은 전가상화로 보면 되며 다른 guest와 완전히 독립되고 os 수정없이 그대로 사용가능하다는 이점이 있지만 hardware 자체가 전가상화 기능(CPU의 VT)을 지원해야 하기 때문에 이것이 부담이 되어 퍼포먼스가 PV에 비해 많이 떨어진다고 알려져 왔었다.
그러나, PV가 HVM보다 성능이 좋기는 했지만 그 차이가 크지 않았고 AWS가 발전하면서 퍼포먼스 차이가 더 줄어들었거나 어떤 경우는 HVM이 더 좋은 성능을 내는 경우도 있다고 한다. 또 이렇게 된 이유로 Xen 자체의 기술 향상, 새로운 세대의 CPU 도입, EC2 driver의 향상 등을 들고 있다.
현재는 PV를 지원하는 인스턴스 타입은 거의 없으며 최신 인스턴스 타입들은 모두 HVM으로만 제공하고 있다.
Resource Requests
- Container를 실행하기 위해 요청하는 리소스를 지정
- 예를 들어 CPU 100m (100 millicpus)와 메모리 250Mi (250MiB)로 리소스를 요청하면, 파드는 이보다 적은 리소스를 가진 노드에는 스케줄링 되지 않는다. 만약 사용할 수 있는 용량의 충분한 노드가 없다면, 파드는 여유 용량이 확보될 때까지 대기(pending) 상태로 기다린다.
Resource Limits
- Container가 사용할 수 있는 최대 리소스 양을 지정한다.
- Container가 limit을 초과하여 CPU를 사용하려고 하면, 해당 Pod는 제한되고 성능저하가 발생한다.
- Memory limit을 초과해서 사용하는 container는 종료(OOM Kill)되고 이후 다시 running 된다.
준비된 /data/ckad/nginx-pod.yaml을 이용해서 nginx 컨테이너가 동작될 때 최대 메모리 50Mi, CPU 200m를 넘지 못하도록 구성하시오. 또한 nginx 컨테이너는 동작을 위해 cpu 200m, memory 30Mi를 요청합니다.
# vi /data/ckad/nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
resources:
requests:
memory: "30Mi"
cpu: "200m"
limits:
memory: "50Mi"
cpu: "200m"
kubectl apply -f nginx-pod.yaml
kubectl get pod nginx-pod
kubectl describe pod nginx-pod
Kubernetes Security Context
-
Kubernetes 보안 컨텍스트
-
컨테이너에 대해 지정하는 보안 설정은 개별 컨테이너에만 적용되며 겹치는 경우 파드 수준에서 설정한 것보다 우선 적용됨.
- runAsUser, runAsGroup
- runAsNonRoot
- privileged mode
- capabilities
- securityContext.Caabilitiess.add
- securityContext.Caabilitiess.drop
- readOnlyRootFilesystem
- supplimentalGroups
apiVersion: v1
kind: Pod
metadata:
name: example-sc
spec:
securityContext:
runAsUser: 1000
containers:
- name: app-container
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
runAsNonRoot: true
runAsUser: 2000
allowPrivilegeEscalation: false
capabilities:
add: ["NET_ADMIN", "SYS_TIME"]
-
runAsUser, runAsGroup
: 컨테이너의 프로세스에서 실행할 수 있는 userID(UID)와 groupID(GID) 지정, 컨테이너 이미지에 내장된 특정 유저의 권한으로 컨테이너나 pod를 실행할 수 있다. 먼저 기본 파드를 실행 시 할당되는 유저 정보를 확인하고, 특정 유저를 권한으로 실행한 컨테이너와 비교해보자. -
runAsNonRoot
: 컨테이너가 root로 실행되는 것을 방지, 컨테이너 실행 시 root 사용자로 실행되는 것을 미리 방지할 수 있다. -
privileged mode
: 컨테이너에서 node의 root 권한을 획 득, privileged mode는 supervisor mode이다. 노드의 커널에 대한 모든 access를 얻으려면 시스템의 완전한 full access를 지원한다. -
Linux Capabilities
: 컨테이너를 privileges 모드로 실행하여 노드의 커널에 대한 모든 접근 권한을 부여 -
AllowPrivilegeEscalation
: 프로세스가 상위 프로세스보다 많은 권한을 얻을 수 있는지 여부를 제어한다. Privileged로 실행하거나 CAP_SYS_ADMIN이 있는 경우 allowPrivilegeEscalation은 항상 true이다. -
readOnlyRootFilesystem
: 프로세스가 컨테이너의 파일 시스템에 쓰는 것을 방지, 보안적인 이유로 대부분의 컨테이너에서는 실행중인 프로세스가 컨테이너의 파일 시스템을 쓰지 못하게 하고, 특별히 마운트된 볼륨에만 쓰기가 허용되도록 한다. -
supplementalGroups
: 추가그룹 권한 사용, supplementalGroups를 사용하면 컨테이너에서 실행중인 유저와 상관없이 추가 그룹의 권한으로 저장하여 파일을 공유할 수 있다.
- safe 컨테이너는 UID 0인 root로 실행을 금지한다.
- safe 컨테이너를 실행할 때는 405 UID로 실행되어야 한다.
- safe 컨테이너는 root의 권한으로 escalation 금지한다.
# vi /data/ckad/safe-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: safe-pod
spec:
containers:
- name: safe
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
runAsNonRoot: true
runAsUser: 405
allowPrivilegeEscalation: false
kubectl apply -f /data/ckad/safe-pod.yaml
kubectl get pod safe-pod
kubectl describe pod safe-pod
kubectl exec safe-pod -- id
- https://kubernetes.io/ko/docs/concepts/policy/limit-range/
- https://kubernetes.io/ko/docs/concepts/policy/resource-quotas/
- https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands
- https://isn-t.tistory.com/34
- https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/