강의 정보
- 러닝스푼스에서 2023년 4월 ~ 6월 간 강의한 쿠버네티스 딥다이브 2기 를 듣고 정리한 내용입니다.
강의 필기록 원본이라 글이 좀 어수선할 수 있습니다.
Kubernetes API
K8s API 는 ReST 에 아주 진심이다
- 쿠버네티스 API 에서
Content-Type
으로 형식을 지정해줄 수 있다- 즉,
application/json
으로 보내면 JSON 응답이 application/yaml
로 보내면 YAML 응답이protobuf
로 보내면 Binary (와 비슷한..) 로 온다
- 즉,
- ReST API 도 어떤 괴짜 박사과정이 졸업논문으로 만든거다.
- 참고) Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST)
- “State” 가 핵심이다: Action 이 아니라 Resource 의 State 를 변경하는 것이 중요한거다
- 그래서 Action 이 아닌 Resource 를 URL path 에 기술하게 되고, Action 은 HTTP Method 를 활용해라
- 안좋은 예) Docker 의
/container/{id}/start
- 좋은 예) Kubernetes 는 모든 것이 state 로 관리되기에 그에 따라 API 도 ReST 에 입각하여 정의해 놓았다
- 안좋은 예) Docker 의
Kube-apiserver 의 ReST API
- Kube-apiserver 는 HATEOAS 을 제공한다
- HATEOAS 개념: 특정 API 를 찔렀을 때 제공하는 API 목록을 전부 보여주자
/openapi/v3
로 Swagger spec 을 제공해주기도 한다- Swagger UI 를 연동하면 편하게 볼 수 있을듯
API 설계
- 모든 리소스는 TypeMeta, ObjectMeta, Spec, Status 로 구성된다
- TypeMeta: GVK (Group, Version, Kind)
- 모든 K8s 에의 state 는 GVK 로 분류가 된다 (스키마가 정의가 된다)
- apiVersion: 모든 apiVersion 은 (core를 제외하면)
${GROUP}/${VERSION}
로 구성된다.- Core 의 경우에는 Group 없이 v1 만 드가고 이것은 legacy 지만 의존성이 너무 강해 고치지는 않았다고 한다.
- Kind: 뭐 리소스 종류 (Pod, Deployment, …)
- ObejctMeta:
Resource.metadata
의 내용- GVK 와 ns, name 으로 클러스터 내에서 특정 리소스를 global 하게 특정이 가능하다
- Spec (바람직한 상태): Defaulting 되는 것 이외에는 Controlplane component 가 건들지 않는다
- DeletionTimestamp: Delete API 를 호출했을 때 삭제되는데 시간이 오래 걸리는 경우가 있고 이럴때 실제로 삭제되지 않았는데 etcd 에서 바로 지워버리면 안되기 때문에 약간 GracePeriod 같은 이 값을 걸어버리고 이 시간이 지나면 지워지길 “희망” 한다는 의미
- 실제로는 이런식으로 진행
- Delete API 호출
- DeletionTimespamp 설정
- Controller 가 DeleteTimestamp 가 걸린놈을 보고 자신이 담당하고 있는 Finalizer 를 수행 후 해당 line 삭제
- GarbageCollector 는 DeletionTimestamp 가 설정되어 있고 Finalizer 가 비워져있는 놈을 찾아서 지워야 한다.
- 따라서 Finalizer 를 임의로 지워서 stuck 걸린 리소스를 지우는 것은 state 에서만 지우는 거고 실제 자원은 남아있기 때문에 Controller 의 이슈를 먼저 해결해야 한다
- Scale subresource: CRD 를 생성했을 때 Scale subresource 를 정의할 수 있고, 이것을 이용해 HPA 같은 것을 사용할 수 있음
- 이것을 자동화해주는게 Keda 라는 솔루션이다
- Status (실제 상태): Controlplane component 는 이것만 건든다
- TypeMeta: GVK (Group, Version, Kind)
- 팁) API reference 에도 defaulting 에 관한 내용은 제대로 적혀있지 않기에 직접 코드를 까보는 것도 좋다더라
- 참고) kubernetes/types.go at master · kubernetes/kubernetes
- 주석중에
// +optional
가 있는데 이건 Controller 의 코드 자동생성을 위한 것이고, 필수가 아니고 defaulting 이 수행될 값임을 나타냄
Authorization & Authentication
- 김해람이 맞나? Authentication (인증)
- 인증서의 CN(user) OU(group) 로 User 와 Group 을 확인하고 그 결과를 반환
- CN 은 GetUser 로 변환,
- OU 는 GetGroup 로 변환
- Token 으로도 가능한데, Kubeconfig 의 인증서를 사용하는 경우에는 대부분 Token 을 사용
- 인증서의 CN(user) OU(group) 로 User 와 Group 을 확인하고 그 결과를 반환
- 김해람이 권한이 있나? Authorization (인가)
- 인증과정에서 나온 유저 정보가
- GVK, ns, name 에 권한이 있는가 확인
func Authorizer.Authorize()
가 담당한다고 한다.
- 결과로 DecisionAllow, DecisionDeny, DecisionNoOpinion 을 반환
- RBAC
- 세가지: 나, 역할, 나와 역할의 관계
- ABAC: Action Based
- 얘는 누가 뭘 할수 있는지를 명시적으로 정의한 JSON 파일을 node 에 저장하고 Apiserver 가 readfs 하게 하는 방식이었지만 지금은 완전히 deprecated 되어 활성화되어있지 않음
- NodeAuthorizer
- 파드가 노드 A 에 떠있을 때 NodeAuthorizer 는 BFS 로 확인하며 노드 A 에만 파드가 접근할 수 있는 정보 (eg, Secret) 접근 권한을 준다.
- 마찬가지로 해당 파드가 노드 C 로 이동하면 노드 A 의 권한은 드랍되고 C 로 권한이 옮겨간다
- 따라서 직접적인 관계가 없는 노드가 털려도 정보를 갈취해 갈 수 없다.
- 이러한 관계를 그래프로 관리하며 BFS 로 순회하며 권한을 지속적으로 갱신한다.
- API server flag 로 RBAC 과 함께 기본적으로 활성화되어 있다
- Kube-apiserver 옵션 확인:
kubectl -n kube-system get po kube-apiserver-week2 -oyaml | yq '.spec.containers.[0].command'
- kube-apiserver
- --advertise-address=192.168.100.18
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-issuer=https://kubernetes.default.svc.cluster.local
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- SA:
hehe
ns 의boi
sa 는system:serviceaccount:hehe:boi
User 와 동치이다. 즉,
- kind: ServiceAccount
namespace: hehe
name: boi
- 와
- kind: User
name: system:serviceaccount:hehe:boi
- 는 동치인데 실험해보지는 않았다고 한다
Admission Webhook
- MutationWebHook: Pod 등이 생성되기 전에 외부에 구현되어 있는 mutation webhook 을 호출하도록 하여 추가적으로 변경할 수 있는 것들을 변경
- ValidationWebHook: 이름에서부터 보이다시피 manifest 를 보고 허용되지 않은 작업을 드랍
- ingress nginx 에서 사용해보기만 하고 뭔지는 잘 몰랐는데 이 수업을 듣는 중에서도 설명을 자세히는 안해줘서 디테일한건 모르겠다
kubectl api-resources | grep validating
# validatingwebhookconfigurations admissionregistration.k8s.io/v1 false ValidatingWebhookConfiguration
kubectl api-resources | grep mutating
# mutatingwebhookconfigurations admissionregistration.k8s.io/v1 false MutatingWebhookConfiguration
Extension API
CRD vs Aggregation layer
- CRD 로 정의된 리소스는 etcd 에 저장되기 때문에 조회의 속도가 느림
- 그래서 metrics server 나 calico 같은 경우에는 Aggregation layer 를 사용하여 kube-apiserver 를 proxy bypass 하여 속도를 취하는 방법이 있댄다
Helm controller: Helm release 를 CRD 로 작성하여 자동으로 배포
- 참고) https://github.com/fluxcd/helm-controller
- fluxcd 에서 gitOps 를 위해 사용하는 듯
- ArgoCD 가 더 나아보이긴함