쿠버네티스 패턴 중 3부(구조패턴), 4부(설정패턴), 5부(고급패턴)을 다룬다.
쿠버네티스를 어떻게 확장하여 사용할 수 있을까?
쿠버네티스의 작동 방식의 핵심 철학은 자원의 상태를 '선언(declare)'하는 것.
A deploy의 현재 Pod가 2개인 상태 (The current state)에서 Pod를 3개로 유지(The desired state)하고 싶은 경우, 사용자는 Pod를 하나 더 생성하라는 명령을 보내지 않고, Replicas의 값을 3으로 변경함.
그렇다면 누가 그 다음 일들을 대신해주나?
쿠버네티스의 내장 컨트롤러들이 현재 상태에서 요청된 상태가 되도록 'reconcile'함
이미 쿠버네티스의 control plane은 위와 같이 컨트롤러를 통해 쿠버네티스 자원을 관리하도록 설계되어있음.
위처럼 reconciliation을 해주는 사용자 컨트롤러도 만들 수 있겠다.
참고) 컨트롤러는 보통 deploy로 배포되며, 동일한 자원에 여러 개의 컨트롤러가 동시에 작동하는 것을 피하기 위해 싱글톤 패턴으로 동작함. 즉 하나의 replica.
컨트롤러가 무엇을 보고 어떤 리소스를 관리할 지 정할 수 있을까?
app: web
meta.helm.sh/release-name: myservice
apiVersion: v1
kind: ConfigMap
metadata:
name: webapp-config
annotations:
k8spatterns.io/podDeleteSelector: "app=webapp"
data:
message: "Welcome to Kubernetes Patterns !"
---
apiVersion: apps/v1
kind: Deployment
# ...
spec:
# ...
template:
spec:
containers:
- name: app
image: k8spatterns/mini-http-server
ports:
- containerPort: 8080
env:
- name: MESSAGE
valueFrom:
configMapKeyRef:
name: webapp-config
key: message
namespace=${WATCH_NAMESPACE:-default}
base=http://localhost:8001
ns=namespaces/$namespace
curl -N -s $base/api/v1/${ns}/configmaps?watch=true | \
while read -r event
do
type=$(echo "$event" | jq -r '.type')
config_map=$(echo "$event" | jq -r '.object.metadata.name')
annotations=$(echo "$event" | jq -r '.object.metadata.annotations')
if [ "$annotations" != "null" ]; then
selector=$(echo $annotations | \
jq -r "\
to_entries |\
.[] |\
select(.key == \"k8spatterns.io/podDeleteSelector\") |\
.value |\
@uri \
")
fi
if [ $type = "MODIFIED" ] && [ -n "$selector" ]; then
pods=$(curl -s $base/api/v1/${ns}/pods?labelSelector=$selector |\
jq -r .items[].metadata.name)
for pod in $pods; do
curl -s -X DELETE $base/api/v1/${ns}/pods/$pod
done
fi
done
apiVersion: apps/v1
kind: Deployment
# ....
spec:
template:
# ...
spec:
serviceAccountName: config-watcher-controller
containers:
- name: kubeapi-proxy
image: k8spatterns/kubeapi-proxy
- name: config-watcher
image: k8spatterns/curl-jq
# ...
command:
- "sh"
- "/watcher/config-watcher-controller.sh"
volumeMounts:
- mountPath: "/watcher"
name: config-watcher-controller
volumes:
- name: config-watcher-controller
configMap:
name: config-watcher-controller
컨트롤러로 코드의 변경 없이 애플리케이션 관리할 수 있음.
예를 들어, 프로메테우스라는 어플리케이션을 모니터링 솔루션으로 사용하고 싶다. 그렇다면 create deploy
처럼 create prometheus
가 있었으면 좋겠다.
오퍼레이터 = (쿠버네티스 + 쿠버네티스 이외의 것들)을 이해하는 컨트롤러
사용자가 쿠버네티스 리소스를 정의하고 그 것들을 관리하기 위한 API도 추가할 수 있음
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: prometheuses.monitoring.coreos.com
spec:
group: monitoring.coreos.com
names:
kind: Prometheus
plural: prometheuses
scope: Namespaced
version: v1
validation:
openAPIV3Schema: ....
subresources:
status: {}
scale:
specReplicasPath: .spec.replicas
statusReplicasPath: .status.replicas
labelSelectorPath: .status.labelSelector
---
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
name: prometheus
spec:
serviceMonitorSelector:
matchLabels:
team: frontend
resources:
requests:
memory: 400Mi
파드가 필요한 리소스, 레플리카 개수, 클러스터 노드의 갯수를 요구량에 맞게 동적으로 설정하기 위한 방법.
각 컨테이너에 적절한 Resource requests/limits를 설정하는 것.
각 파드의 적절한 Replica 개수를 설정하는 것
HPV에 정의된 스케일링될 파드들에 대한 메트릭을 가져온 뒤, 필요한 레플리카 수를 계산함
desiredReplicas = currentReplicas * currentMetricValue / desiredMetricValue
중단 가능성이 낮고 자동화가 쉬워서 가장 흔하게 사용됨
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: random-generator
spec:
minReplicas: 1
maxReplicas: 5
scaleTargetRef:
apiVersion: extensions/v1beta1
kind: Deployment
name: random-generator
metrics:
- resource:
name: cpu
target:
averageUtilization: 50
type: Utilization
type: Resource
컨테이너에 대한 resources.requests가 설정되어 있어야 함.
클러스터 자체의 용량을 조절하는 방법. 동적으로 노드를 할당 받아 클러스터에 조인하는 것으로 클라우드(EKS, GKS 등)에서 실행될 때 사용 가능.
Pod가 수동/HPA/VPA를 통해 스케일 될 때 적당한 노드에 배치되어야 하지만 노드가 없다면 unschedulable 상태로 대기함
CA는 이런 Pod들을 모니터링해 미리 지정한 Node Group 중 어느 노드가 요구 사항을 만족하는지 판단한 뒤 노드를 새로 할당
노드가 더 이상 필요하지 않다고 판단될 때 해당 노드를 제거함
클러스터에서 사용하는 이미지도 클러스터 안에서 빌드하자.