[CKAD] Kubernetes Certificate Application Developer(CKAD) with Tests 학습노트(2/)

개요 : CKAD

쿠버네티스 학습, 자격증 취득 등. Kubernetes Certificate Application Developer with Tests 학습 두 번째 글.

  • Docker

Docker

이미지는 결국 도커로 말아야 한다. 말때 정의는 docker에 되어 있다.

5초를 슬립하는 우분투 이미지를 빌드하려면 dockerifle을 아래와 같이 정의하고 빌드 명령을 수행하면 된다.

from ubuntu
cmd sleep 5
docker build -t ubuntu-sleeper .
docker run ubuntu-sleeper

하지만 매번 초가 5, 6, 7, 8로 바뀐다면 어떨까. 파라미터를 던질 수 있는데 이때는 cmd 가 아니라 entrypoint를 선언하면 된다.

from ubuntu
entrypoint ["sleep"]
docker build -t ubuntu-sleeper .
docker run ubuntu-sleeper 5

기본 파라메터는 어떻게 줄수 있을까. entrypoint바로 다음 cmd를 넣어서 처리할 수 있다. 이 때 파라메터를 넣으면 뒤쪽 cmd 는 무시된다

from ubuntu
entrypoint ["sleep"]
cmd 5

entrypoint자체를 바꾸고 싶으면 어떻게 해야 할까? --entrypoint옵션을 주면 entrypoint를 덮어쓴다.

docker run --entrypoint sleep2.0 ubuntu-sleeper 5

command and arguments

도커 이미지를 실행할 때 쓰던 entrypoint와 cmd는 쿠버네티스에서 어떻게 대체할 수 있을까? commandargs를 사용하면 된다.

apiVersion: v1
kind: Pod
metadata:
name: ubuntu-sleeper-pod
spec:
containers:
- name: ubuntu-sleeper
image: ubuntu-sleeper
command: ["sleep2.0"] <-- entrypoint ["sleep2.0"] 동일
args: ["10"] <-- cmd ["10"] 동일

kubectl edit 실패 관련

kubectl edit으로 리소스를 수정하려고 하면 일부 주요 옵션들에 대한 수정이 불가능하다는 메세지를 출력한다. 이때 적용이 실패한 yaml파일이 저장되므로 수정에 실패한 리소스 삭제 후 해당 yaml 파일을 이용하여 재생성하자.

$ kubectl edit pod redis
A copy of your changes has been stored to "/var/folders/1l/zy49b0n13rq2kym45rvv1ts40000gn/T/kubectl-edit-809005869.yaml"
error: At least one of apiVersion, kind and name was changed
$ kubectl delete pod redis
$ kueectl create -f "/var/folders/1l/zy49b0n13rq2kym45rvv1ts40000gn/T/kubectl-edit-809005869.yaml"
or
$ kubectl replace --force -f "/var/folders/1l/zy49b0n13rq2kym45rvv1ts40000gn/T/kubectl-edit-809005869.yaml"

이미지 내부에서 수행되는 프로그램에게 던지는 인수(예를 들어 dockerfile의 cmd)는 --를 사용하면 된다. 예를 들어 실행되는 컨테이너 이미지에 --color green이라는 파라메터를 전달하려면 아래와 같이 -- 다음에 적어주면 된다.

kuberctl run webap-green --image=kudekloud/webapp-color -- --color green

환경변수

프로그램 실행마다 파라미터를 던질 수도 있지만, 환경 변수로 설정할 수 있다.

plain key value

가장 기본적인 환경변수다

spec:
containers:
- env:
- name: APP_COLOR
value: pink

ConfigMap

여러 pod에서 동시에 사용하기 위하여 클러스터에서 관리하는 환경변수를 ConfigMap이라고 부른다.

예를 들어, app-config라는 configmap에 app_color=blue라는 환경변수와, app_mod=prod라는 환경변수를 추가하고 싶다면 아래와 같이 실행하면 된다. 하나의 컨피그맵에 여러개의 환경 변수를 담을 수 있다.

kubectl create configmap app-config --from-literal=APP_COLOR=blue --from-literal=APP_MODE=prod

하나하나 치면 복잡하니까 파일로 만들어 둘 수도 있다.

kubectl create configmap app-config --from-file=app_config.properties

yaml로 정의하면 다음과 같이 정의할 수 있다.

apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
APP-COLOR: blue
APP_MODE: prod

Configmap 참조

이제 pod에서 생성한 ConfigMap을 참조해보자.

spec:
containers:
- envFrom:
- configMapRef:
name: app-config

특정 값만 참조하고 싶다면(예를 들어 APP_COLOR를 configmap의 APP_COLOR를 사용하고 싶다면..)

spec:
containers:
- env:
- name: APP_COLOR
valueFrom:
configMapKeyRef:
name: app-config
key: APP_COLOR

Secret

그렇다면 DB비밀번호와 같은 평문이 아닌 값은 어떻게 저장할까? 이럴때 secret을 사용한다.

kubectl create secret generic <secret-name> --from-literal=<key>=<value>
--from-file=옵션 가능

가져다 쓸때는, 1) 환경변수 전체 땡기기 2) 값 하나 땡기기 3) volume 땡기기 중 하나를 사용할 수 있다.

환경변수 전체 땡기기

spec:
containers:
- name: redis
envFrom:
secretRef:
name: <secret-name>

환경변수 값 하나 땡기기

spec:
containers:
- name: redis
env:
- name: DB_Password
valueFrom:
secretKeyRef:
name: <secret-name>
key: <ENV_KEY_NAME>

파일에서 통체로 땡기기

볼륨을 secret으로 땡기면 볼륨 내의 파일명을 key로 파일값을 value로 사용한다.

volumes:
- name: app-secret-volume
secret:
secretName: app-secret

secret의 암호화

기본적으로 base64로 인코딩된 값이며 암호화되지 않는다. 별도의 설정을 해야한다. 아래 문서에서 저장시(secret이 etcd에 저장시) 어떻게 암호화할 수 있는지!?!?? 알아보자 Encrypting Secret Data at Rest https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/

도커 보안

컨테이너 보안 : 유저와 권한

컨테이너는 커널을 공유한다. 기본적으로 컨테이너에서 수행하는(entrypoint) 프로세스는 루트로 수행된다. 그러면 프로세스의 루트로? 아니다. 호스트 입장에서보면 컨테이너는 도커를 부모로 가지는 자식 프로세스로 생성되며, 별도의 네임스페이스를 가진다. 즉, 컨테이너 내부에서는 root(pid=1)로 프로그램이 실행되지만, 호스트에서는 별도의 pid를 가지며 docker 사용자로 수행된다.

컨테이너 내에서 root가 아닌 다른 유저로 실행을 하려면

  1. 실행하려는 유저를 dockerfile에 정의
  2. --user UID 옵션을 추가하여(혹은 dockerfile 안에 USER UID를 정의하여) 실행할 유저 선택 하면 된다.

일반 유저에게는 시스템 관련 권한이 아무것도 없다. 예를 들어 CAP_NET_BIND_SERVICE가 없는 유저가 http를 서비스하려고 80포트를 expose하면 안된다. < 1024는 웰논 포트(privileged port)로 권한이 있는 유저만 사용할 수 있기 때문이다. 이를 위해 --cap-add 옵션을 이용하여 권한을 부여한다. 유저 권한은 아래 문서에 정의되어 있다. https://linux.die.net/man/7/capabilities

쿠버네티스 보안

쿠버네티스의 관점에서 컨테이너는 파드로 캡슐레이션 된다. 파드와 컨테이너 양쪽에 모두 권한을 걸 수 있지만, 둘다 동일 권한이 걸려있다면, 컨테이너의 권한이 파드의 권한을 덮어쓴다. 예를 들어 1000이라는 유저로 프로세스를 실행하는 파드를 정의하자. 파드에 보안 설정(실행유저)을 추가하거나

apiVersion: v1
kind:
metadata:
spec:
securityContext:
runAsUser: 1000
containers:
- name:
image:

컨테이너에 보안 설정(실행유저)을 추가할 수 있다.

apiVersion: v1
kind:
metadata:
spec:
containers:
- name:
image:
securityContext:
runAsUser: 1000
capabilities: <-- capabilities는 container아래에만 들어갈 수 있어요
add: ["MAC_ADMIN"]

컨테이너에서 프로세스를 실행하는 유저를 확인하려면 whoami를 실행해서 확인하자.

$ kubectl exec <podname> -- whoami

ServiceAccount

쿠버네티스는 두가지 유형의 계정이 있다.

  • user account : 사람이 쓰는 계정. 예를 들어 admin, developer...
  • service account : 서비스가 쓰는 계정. 예를 들어 prometheus, buildtools...

서비스어카운트는 생성고 동시에 토큰이 생성되고, 해당 토큰은 secrets로 저장된다. 해당 토큰은 인증 등에 사용된다. 예를 들어 api서버에 아이디와 토큰을 던져 인증할 수 있다.

$ curl https://localhost:6443/api -insecure --header "Authorization: Bearer <token>"

pod를 생성하면 serviceaccount의 정보를 volume으로 마운트한다. 마운트된 폴더는 아래 세가지 파일을 가진다.

  • ca.crt
  • namespace
  • token

기본적으로 token의 값을 긁어서 디코딩해보면(https://jwt.io/#decoded-jwt) 토큰에 어떤 정보가 담겨있는지 알 수 있다.

Resource

CPU 자원의 단위. 기본적으로 1이란 1코어를 의미하며, 물리자원에서는 하이퍼쓰레딩된 1쓰레드를 의미한다. 소수점 할당 가능하다.

  • 1 Azure core, 1GCP Core, 1 AWS EC2 vCPU
  • 1 Hyperthread

메모리도 비슷하게 할당할 수 있다. pod에서 사용할 자원을 정의해보면 다음과 같이 정의할 수 있다.

spec:
containers:
- name:
images:
resources:
requests: <- 요청자원
memory: "1Gi"
cpu: 1
limits: <- 자원 한계
memory: "2Gi"
cpu: 2

기본 request 자원은 0.5cpu, 256M memory로 설정된다.

limit을 넘어서면 어떻게 될까? 도커로 말면 그냥 쭉 올라가지만, kubernetes죽여버린다!(terminate) 허거덩...

Taints and Tolerations

어느 파드를 어느 노드에 놓을 것인가? 파드를 어느 노드로 스케줄링할까의 문제에서 사용되는 정의이다. 보안 설정 등을 포함하고 있지는 않다. taint는 오염시키다는 동사로 노드를 다양하게 오염 시킨다(스케줄할수 없다거나 실행할 수 없다거나). Toleration은 용인한다는 의미로 노드가 taint로 오염되어도 그 오염을 용인하고 실행한다는 의미이다.

taint

어떻게 노드를 오염시킬까? 오염은 아래와 같이 시킬 수 있다.

$ kubectl taint nodes <nodename> <key>=<value>:<taint-effect>
ex>
$ kubectl taint nodes node1 app=blue:NoSchedule

taint-effect

테인트 이펙트는 세가지를 가진다.

  • NoSchedule : pod를 node에 스케줄링하지 않음. 실행 중인 pod는 유지.
  • PreferNoShcedule : 가급적 스케줄링하지 않음. 다른 클러스터 자원 부족시 스케줄링 할 수도 있음.
  • NoExecute : pod를 node에 스케줄링하지 않음. 실행 중인 pod도 종료.

tolerations

app=blue:noschedule로 오염된 노드에 파드를 배치하려면 어떻게 해야할까. 아래와 같이 파드 spec에 tolerations를 정의하여 오염에 대한 관용성을 표현할 수 있다.

apiVersion: v1
kind: Pod
metadata:
spec:
containers:
...
tolerations:
- key: "app"
operator: "Equal"
value: "blue"
effect: "NoSchedule"
or
- key: "app"
operator: "Exists"
effect: "NoSchedule"

tolerations은 관용이라는 의미 그대로 오염된 노드에 배치될 수 있다는 의미이지, 오염된 노드에 배치한다는 의미는 아니다. 파드를 특정 노드에 배치하고 싶으면 nodeaffer?!?를 사용하자.

  • 마스터 노드에 파드를 배치하지 않는 것도 테인트를 이용한다.

rediness and liveness

pod는 status와 condition을 가진다.

status는 단일 값이다.

  • pending : .
  • running : condition
  • podscheduled
  • initialized
  • containersready
  • ready : 실제로 트래픽을 받을 수 있는 상태 컨테이너가 실행된 상태와 실제로 트래픽을 받는 상태는 다른 상태일 수 있다 (tomcat 초기화 등이 돌면 서버는 올라왔지만 tomcat은 응답을 못준다) 이를 테스트하기 위하여 rediness probe고 컨테이너 상태를 파악한다

readinessProbe

kind: Pod
spec:
containes:
- redinessProbe
httpGet:
path: /api/ready
port: 8080
#or
tcpSocket:
port: 3306
initialDelaySeconds: 10 # 처음 서버가 뜰때까지 기다림
periodSeconnd: 5 # 반복 주기
failureThreshold : 8 # 실패 threshold
# or
exec:
command:
- cat
- /app/is_healthy

livenessProbe

정상적인 상태를 체크하고 정상이 아닌 경우 재시작함 readinessProbe는 실행 단계에서 준비가 된지 여부를 판단하고, livenessProbe는 실행 중 정상 동작 여부를 판단 쓰는 방법은 똑같다.

logs

쿠버네티스 로그 추적하기

  • 기본 형태
kubectl logs -f <podname>
  • 멀티플 컨테이너를 가지고 있는 경우
kubectl logs -f <podname> <containername>

monitor and debug

노드와 파드의 자원 모니터링을 어떻게 하면 될까?

metrics server

  • cAdvisor가 자원 정보를 모은다.
kubectl top node
kubectl top pod

job

배치, 분석, 리포팅 등 다른 자원을 사용하기 위한 자원. 원타임으로 실행되어야 하기 때문에 재시작 정책을 Never로 설정한다.

apiVersion: batch/v1
kind: Job
metadata:
name: jobname
spec:
# 3회 반복. 한 job 정상 종료 후 다음 작업 수행
# 실패하면 3회 성공할 때까지 계속 반복함. 성공 실패 실패 실패 성공 실패 성공(종료)
completions: 3
# 병렬 수행
parallelism: 3
template:
containers:
- name : continaername
image: ubuntu
command: ['echo', 'hello world!', 'this is job']
restartPolicy : Never

CronJob

크론잡이다. 잡과 다른 부분은 스펙에일정이 들어가서 주기적으로 수행된다는 것이다.

apiVersion: batch/v1beta1
kind: cronJob
metadata:
name: jobname
spec:
# 일정이 추가됨
schedule: "*/1 * * * *"
completions: 3
parallelism: 3
template:
containers:
- name : continaername
image: ubuntu
command: ['echo', 'hello world!', 'this is job']
restartPolicy : Never

리턴 코드

  • 0 : 정상 종료

Service

외부로 파드를 어떻게 서비스하면 좋을까?

Nodeport

노드의 특정 포트를 매핑. 서비스를 생성하며 모든 노드의 특정 포트가 하나의 서비스에 매핑됨.

  • Target Port : 컨테이너의 포트. target은 서비스 관점이므로...
  • Service Port : 서비스 포트. 클러스터IP에서 서비스되는 포트. 클러스터 IP는 랜덤으로
  • Node Port : 클러스터 노드에서 서비스되는 포트. 30,000~32767까지 정의됨
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80 # 컨테이너 포트
port: 80 # 서비스 포트. 작성하지 않으면 타겟포트와 등일하게 구성됨
nodePort: 30008 # 작성하지 않으면 범위네 랜덤
selector:
app: myapp
type: front-end
apiVersion: v1
kind: Pod
metadata:
name: myapp
type: front-end
spec:
containers:
- name: myapp
image: nginx

ClusterIP

클러스터내에서 동일 포트 군을 하나의 가상 IP로 묶기 위한 리소스로, 클러스터내 IP레인지에서 할당한다. NodePort처럼 selector로 구성한다.

apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP
ports:
- targetPort: 80
port: 80
selector:
app: myapp
type: back-end

LoadBalancer - Ingress

노드포트로 외부 접속을 지원하면 3만번대 이상의 포트로 서비스를 하게 된다. 고객이 매번 포트를 쳐야할까? 아니될 말씀이다. 그러면 어떤 방법이 있을까? 로드밸런서에서 가상ip로 80, 443포트를 받고, 뒷단의 노드포트 서비스로 트래픽을 던질 수 있겠다. 그런데 이렇게하면 서비스를 추가할 때마다 로드밸런서에서 포트를 추가해야 한다. 이걸 위해서 Ingress가 있다. Ingress는 L7 로드밸런서다.

Ingress Controller

nginx, haproxy와 같은 실제 로드밸런서 도구이다. nginx와 nginx controller가 동일 제품..은 아니며 쿠버네티스 전용 빌드로 보면 되며, 구글에서 관리한다. ingress controller를 배포로 구성

쿠버네티스 클러스터의 워크로드로 들어가는 네트워크 구성을 그려보면

service(ex. nodeport) -> deployment(ingress contoller. ex) nginx ingress contoller) -> [ingress resource 확인] -> service -> pod으로 라우팅

nginx ingress contoller의 구성은 다음과 같을 수 있다.

apiVersion: extensions:v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
spec:
replicas: 1
selector:
matchLabels:
name: nginx-ingress
template:
metadata:
labels:
name: nginx-ingress
spec:
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-contoller/nginx-ingress-contoller
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
env:
- name: POD_NAME
valueFrom:
fieldFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMEPSACE
valueFrom:
fieldRef:
fieldPath: metadata.namepsace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443

Ingress Resources

Ingress Controller에 정의된 서비스들의 설정한다. Ingress Controller로 들어온 서비스를 wear-service로 보내는 Ingress Resources는 다음과 같다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wear
spec:
backend:
serviceName: wear-service #진짜 서비스 네임
servicePort: 80

여기에 복수의 워크로드가 있다면 어떨까? /wear, /watch와 같이 복수의 백엔드가 구성되어 있다면?

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-wear-watch
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
#rewrite-target이 없으면 뒤에 붙은 파라미터가 붙는다
#예를 들어 ingressservice:ingressport/servicepath로 접근할 경우
# servicename:port로 접근하길 바란다.
# 하지만 servicename:port/servicepath로 접근하게 된다.
# 이를 방지하기 위하여 rewrite시 target을 /로 만드는 것이 rewrite-target이다.
spec:
rules:
- host: wear.my-online-store.com # hostname에 대한 설정 가능
http: # ingress contoller port name
paths:
- path: /wear # 패스가 없으면 호스트의 전체 트래픽을 서비스로 던진다
pathType: Prefix
backend:
service:
name: wear-service
port:
number: 80
- path: /watch
backend:
serviceName: watch-service
servicePort: 80

1.20버전부터 다음과 같은 패턴으로 인그레스를 생성할 수 있게 되었다.

kubectl create ingress <ingress-name> --rule="host/path=service:port"
kubectl create ingress ingress-test --rule="wear.my-online-store.com/wear*=wear-service:80"

https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-ingress-em-

network policy

일반적응로 방화벽 룰이라고 부르는 바로 그것

Ingress & Egress

아래와 같은 형태로 정의할 수 있다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
spec:
#누구에게
podSelector:
matchLabels:
role: db
#어떤룰을 적용할 것인가?
policyTypes:
- Ingress
ingerss:
- from:
- podSelector:
matchLabels:
name: api-pod
ports:
- protocol: TCP
port: 3306

from에는 복수의 소스를 정의할 수 있는데 and 조건과 or조건으로 정의할 수 있다.

prod 네임스페이스의 api-pod에서 오는 트래픽, 즉 두 조건을 같이 걸고 싶은 경우는 하나의 아이템으로 선언한다.

- ingress:
- from:
- namespaceSelector:
matchLabels:
name: prod
podSelector:
matchLabaels:
name: api-pod

prod 네임스페이스에서 오거나 api-pod에서 오는 트래픽, 즉 두 조건 중 하나만 만족해도 되는 or 조건을 걸고 싶은 경우는 두개의 아이템으로 선언한다.

- ingress:
- from:
- namespaceSelector:
matchLabels:
name: prod
- podSelector:
matchLabaels:
name: api-pod

Volumes

Percistant Volume

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-voll
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
awsElasticBlockStore:
volumeID:
fsType: ext4

PV 모드

  • ReadWriteOnce : 싱글 노드에 리드라이트로 마운트된다. pod가 같은 노드에서 돌고 있다면, 여러 pod에서 사용할 수 있지만...
  • ReadOnlyMany : 많은 노드에서 읽기만 가능한 볼륨
  • ReadWriteMany : 많은 노드에서 읽고 쓸 수 있는 볼륨

Percistanc Volume Claim

정의한 PV에서 땡겨옴. 필요한 용량만큼을 요청하고, PV에 충분한 용량이 있으면 할당한다. 필요한 용량이 없는 경우(다른 쪽에서 claim을 걸어서 사용 중인 경우) 용량이 생길 때 까지 claim 상태에 남는다.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myClaim:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi

StorageClass

Static provisioning

필요할 때마다 스토리지 프로비져닝을 손으로 실시함 NFS를 붙인다? 직접 붙여서 각 노드에 연결 cloud를 사용한다? cloud의 스토리지 서비스를 연결

Dynamic Provisioning

동적으로 PV를 생성한다. 예를 들어 구글 클라우드의 스토리지를 사용한다고 하면 아래와 같이 연결할 수 있다.

  1. 구글 스토리지에 대한 storageclass를 정의하고
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: google-storage # 여기에서 스토리지 클래스 이름 선언
provisioneer: kubernetes.io/gce-pd
  1. pvc에서 해당 스토리지 클래스의 이름을 연결한 후 클레임을 걸면 된다 (pod은 pvc에 대한 연결이므로 이전과 등일)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myClaim
spec:
accessMode:
- ReadWriteOnce
storageClassName: google-storage # 여기에 선언한 스토리지 클래스 이름 사용
resources:
requests:
storage: 500Mi

즉, pvc만큼의 용량이 동적으로 프로비저닝되어 별도의 pv에 대한 정의를 하지 않아도 된다. PV가 자동으로 생성된다는 것이지, PV가 생성되지 않는다는 것은 아니다.

스토리지 클래스를 여러타입으로 정의해 두면 필요할 때마다 pvc만 정의하여 사용하면 된다. 예를 들어, hdd/ssd타입의 디스크로 서비스되는 스토리지 서비스를 사용할 때 각각을 스토리지 클래스로 정의해두고 pvc에서 필요한 storage class name을 가져다 쓰면 해당 타입의 디스크를 사용할 수 있다.

StatefulSet

상태를 기억하고 있어야 하는 파드의 세트를 정의한다. 이때 접근을 위하여 headless service를 정의해준다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
labels:
app: mysql
spec:
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql
replicas: 3
selector:
matchLabels:
app: mysql
serviceName: mysql-h
podManagementPolicy: Parallel

Headless Services

mysql cluster service가 있다고 하자. 기본적으로 서비스는 로드밸런싱을 하기때문에 읽기 작업에 대해서는 동일하게 접근하여도 되지만 쓰기 작업이 필요할 때는 마스터에 접근이 필요하다. 개별 pod의 경우 ip.namespace.pod.cluster.local과 같은 엔드포인트로 접근이 가능하지만, ip는 생성될 때마다 변경된다. 이때 사용되는 것이 headless sevrice이다. clusterIP가 None으로 지정하고 서비스의 name을 pod의 subdomain으로 지정해주면 된다.

apiVersion: v1
kind: Service
metadata:
name: mysql-h
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None
kind: Pod
spec:
...
subdomain: mysql-h # 여기를 잡아줘야된다.
hostname: mysql-pod

hostname.subdomain.svc.cluster.local의 형식으로 구성되며, mysql-pod.mysql-h.default.svc.cluster.local이라는 도메인을 통해 해당 pod에 접근할 수 있다.

요거를 deployment로 묶으면

kind: Deployment
spec:
serivceName: mysql-h # opt1. mysql-0, 1, 2.mysql-h.default.svc.cluster.local로 headless service가 생성되어 접근
replicas: 3
matchLabels:
app: mysql
template:
metadata:
name: myapp-pod
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql
subdomain: mysql-h # opt2. 3파드 모두 mysql-pod.mysql-h.default.svc.cluster.local로 접근
hostname: mysql-pod

참조

[1] 강의링크 :
https://www.udemy.com/course/certified-kubernetes-application-developer

[2] secrets : https://kubernetes.io/docs/concepts/configuration/secret/

[3] https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/memory-default-namespace/

[4] https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource