교육, 커뮤니티 후기/DOIK (데이터베이스 오퍼레이터 인 쿠버네티스) 스터디

DOIK(Database Operator In Kubernetes) 1주차

Somaz 2023. 10. 18. 23:18
728x90
반응형

Overview

이번주는 CloudNet@ 스터디에서 진행하시는 DOIK(Database Operator In Kubernetes) 스터디 1주차이다.

 

출처 : https://www.techrepublic.com/article/behind-the-scenes-a-day-in-the-life-of-a-database-administrator/

 

 

 

 


 

EKS 원클릭 배포

# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/EKS/eks-oneclick.yaml

# CloudFormation 스택 배포
예시) aws cloudformation deploy --template-file eks-oneclick.yaml --stack-name myeks --parameter-overrides KeyName=somazkey SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32  MyIamUserAccessKeyID=AKIA5... MyIamUserSecretAccessKey='CVNa2...' ClusterBaseName=myeks --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text
43.201.5.150

 


 

작업용 EC2 에 SSH 접속 (SSH 키 파일 사용) : 쿠버네티스 정상 설치 확인

← 스택 생성 시작 후 20분 후 접속 할 것

# 작업용 EC2 SSH 접속
ssh -i ~/.ssh/somazkey.pem ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output text)

(somaz@myeks:N/A) [root@myeks-bastion-EC2 ~]#

# cloud-init 실행 과정 로그 확인
tail -f /var/log/cloud-init-output.log

# cloud-init 정상 완료 후 eksctl 실행 과정 로그 확인
tail -f /root/create-eks.log

# 설치 확인
kubectl cluster-info
eksctl get cluster
eksctl get nodegroup --cluster $CLUSTER_NAME

# 환경변수 정보 확인
export | egrep 'ACCOUNT|AWS_|CLUSTER|KUBERNETES|VPC|Subnet'
export | egrep 'ACCOUNT|AWS_|CLUSTER|KUBERNETES|VPC|Subnet' | egrep -v 'SECRET|KEY'

# 인증 정보 확인
cat /root/.kube/config | yh
kubectl config view | yh
kubectl ctx

# 노드 정보 확인
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
eksctl get iamidentitymapping --cluster myeks

# krew 플러그인 확인
kubectl krew list
kubectl resource-capacity

# default 네임스페이스 적용
kubectl ns default

# 모든 네임스페이스에서 모든 리소스 확인
kubectl get-all

 

워커 노드 정보 확인 및 SSH 접속

# 노드 IP 확인 및 PrivateIP 변수 지정
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

N1=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2a -o jsonpath={.items[0].status.addresses[0].address})
N2=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2b -o jsonpath={.items[0].status.addresses[0].address})
N3=$(kubectl get node --label-columns=topology.kubernetes.io/zone --selector=topology.kubernetes.io/zone=ap-northeast-2c -o jsonpath={.items[0].status.addresses[0].address})
echo "export N1=$N1" >> /etc/profile
echo "export N2=$N2" >> /etc/profile
echo "export N3=$N3" >> /etc/profile
echo $N1, $N2, $N3
192.168.1.46, 192.168.2.25, 192.168.3.203

# 보안그룹 ID와 보안그룹 이름(Name아님을 주의!) 확인
aws ec2 describe-security-groups --query 'SecurityGroups[*].[GroupId, GroupName]' --output text

# 노드 보안그룹 ID 확인
aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text
NGSGID=$(aws ec2 describe-security-groups --filters Name=group-name,Values=*ng1* --query "SecurityGroups[*].[GroupId]" --output text)
echo $NGSGID

# 노드 보안그룹에 eksctl-host 에서 노드(파드)에 접속 가능하게 룰(Rule) 추가 설정
aws ec2 authorize-security-group-ingress --group-id $NGSGID --protocol '-1' --cidr 192.168.1.100/32

# eksctl-host 에서 노드의IP나 coredns 파드IP로 ping 테스트
ping -c 2 $N1
ping -c 2 $N2
ping -c 2 $N3

# 워커 노드 SSH 접속 : '-i ~/.ssh/id_rsa' 생략 가능
for node in $N1 $N2 $N3; do ssh ec2-user@$node hostname; done

ssh ec2-user@$N1
exit

ssh ec2-user@$N2
exit

ssh ec2-user@$N3
exit

 


 

설치 후 Add-on 정보 확인

# eksctl 설치/업데이트 addon 확인
eksctl get addon --cluster $CLUSTER_NAME
NAME                    VERSION                 STATUS          ISSUES  IAMROLE                                            UPDATE AVAILABLE CONFIGURATION VALUES
aws-ebs-csi-driver      v1.23.1-eksbuild.1      CREATING        0       arn:aws:iam::61184xxxxxxx:role/eksctl-myeks-addon-aws-ebs-csi-driver-Role1-1Q3GLPAVLF3KD
coredns                 v1.10.1-eksbuild.4      ACTIVE          0
kube-proxy              v1.27.6-eksbuild.2      ACTIVE          0
vpc-cni                 v1.15.1-eksbuild.1      ACTIVE          0       arn:aws:iam::61184xxxxxxx:role/eksctl-myeks-addon-vpc-cni-Role1-LLWD34XVXOU9

 

기본 설정 작업

  • `AWS LB Ctrl` 설치, `ExternalDNS` 설치, `kube-ops-view` 설치, `스토리지 클래스` 생성, `AWS Cert Manager` 인증서 확인

 

AWS Load Balancer Controller 설치 - 링크

# 설치
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME \
  --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller

# 설치 확인
kubectl get crd
kubectl get deployment -n kube-system aws-load-balancer-controller
kubectl describe deploy -n kube-system aws-load-balancer-controller

 

ExternalDNS 설치 - 링크

MyDomain=<자신의 도메인>
echo "export MyDomain=<자신의 도메인>" >> /etc/profile
MyDomain=somaz.link
echo "export MyDomain=somaz.link" >> /etc/profile

MyDnsHostedZoneId=$(aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text)
echo $MyDomain, $MyDnsHostedZoneId
somaz.link, /hostedzone/Z03204211VEUZG9O0RLE5

curl -s -O https://raw.githubusercontent.com/cloudneta/cnaeblab/master/_data/externaldns.yaml
MyDomain=$MyDomain MyDnsHostedZoneId=$MyDnsHostedZoneId envsubst < externaldns.yaml | kubectl apply -f -

 

kube-ops-view 설치

helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set env.TZ="Asia/Seoul" --namespace kube-system

kubectl patch svc -n kube-system kube-ops-view -p '{"spec":{"type":"LoadBalancer"}}'
kubectl annotate service kube-ops-view -n kube-system "external-dns.alpha.kubernetes.io/hostname=kubeopsview.$MyDomain"
echo -e "Kube Ops View URL = http://kubeopsview.$MyDomain:8080/#scale=1.5"
Kube Ops View URL = http://kubeopsview.somaz.link:8080/#scale=1.5

# 확인 및 로그 모니터링
kubectl get pod -l app.kubernetes.io/name=external-dns -n kube-system
NAME                            READY   STATUS    RESTARTS   AGE
external-dns-595f6dc454-467jd   1/1     Running   0          84s

k get svc -n kube-system
NAME                                TYPE           CLUSTER-IP       EXTERNAL-IP
                      PORT(S)          AGE
aws-load-balancer-webhook-service   ClusterIP      10.100.92.90     <none>
                      443/TCP          41m
kube-dns                            ClusterIP      10.100.0.10      <none>
                      53/UDP,53/TCP    55m
kube-ops-view                       LoadBalancer   10.100.163.152   aaa652ecd61c74a00ab377e0d8aa4e88-101488255.ap-northeast-2.elb.amazonaws.com   8080:30475/TCP   46s

kubectl logs deploy/external-dns -n kube-system -f

이렇게 확인 가능하다. 이쁘다?

 

 

스토리지 클래스 생성

# gp3 스토리지 클래스 생성
kubectl apply -f https://raw.githubusercontent.com/gasida/DOIK/main/1/gp3-sc.yaml

# 스토리지 클래스 확인
kubectl get sc
NAME            PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
gp2 (default)   kubernetes.io/aws-ebs   Delete          WaitForFirstConsumer   false                  17m
gp3             ebs.csi.aws.com         Delete          WaitForFirstConsumer   true                   5s

 

AWS Cert Manager 인증서 확인

# ACM 인증서 확인
aws acm list-certificates

# ACM 인증서 변수 선언
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`; echo $CERT_ARN
arn:aws:acm:ap-northeast-2:6118xxxxxxxx:certificate/75e6fb4f-5999-4701-80f2-ab94e015bc9b

 

 


 

프로메테우스-스택 설치

모니터링에 필요한 여러 요소를 단일 차트(스택)으로 제공 ← 시각화(그라파나) 등 - Helm

 

설치

# 모니터링
kubectl create ns monitoring
watch kubectl get pod,pvc,svc,ingress -n monitoring

# 사용 리전의 인증서 ARN 확인
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`
echo $CERT_ARN

# repo 추가
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# 파라미터 파일 생성
cat <<EOT > monitor-values.yaml
prometheus:
  prometheusSpec:
    podMonitorSelectorNilUsesHelmValues: false
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"
    scrapeInterval: '15s'
    evaluationInterval: '15s'

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - prometheus.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

grafana:
  defaultDashboardsTimezone: Asia/Seoul
  adminPassword: prom-operator

  ingress:
    enabled: true
    ingressClassName: alb
    hosts: 
      - grafana.$MyDomain
    paths: 
      - /*
    annotations:
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
      alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
      alb.ingress.kubernetes.io/success-codes: 200-399
      alb.ingress.kubernetes.io/load-balancer-name: myeks-ingress-alb
      alb.ingress.kubernetes.io/group.name: study
      alb.ingress.kubernetes.io/ssl-redirect: '443'

defaultRules:
  create: false
kubeEtcd:
  enabled: false
alertmanager:
  enabled: false
EOT


# 배포
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 51.7.0 \
-f monitor-values.yaml --namespace monitoring

# 확인
## operator : 시스템 경고 메시지 정책(prometheus rule), 애플리케이션 모니터링 대상 추가 등의 작업을 편리하게 할수 있게 CRD 지원
## prometheus-0 : 모니터링 대상이 되는 파드는 ‘exporter’라는 별도의 사이드카 형식의 파드에서 모니터링 메트릭을 노출, pull 방식으로 가져와 내부의 시계열 데이터베이스에 저장
## node-exporter : 노드익스포터는 물리 노드에 대한 자원 사용량(네트워크, 스토리지 등 전체) 정보를 메트릭 형태로 변경하여 노출
## kube-state-metrics : 쿠버네티스의 클러스터의 상태(kube-state)를 메트릭으로 변환하는 파드
## grafana : 프로메테우스는 메트릭 정보를 저장하는 용도로 사용하며, 그라파나로 시각화 처리
helm list -n monitoring
kubectl get pod,svc,ingress -n monitoring
kubectl get-all -n monitoring
kubectl get prometheus,servicemonitors -n monitoring
kubectl get crd | grep monitoring

 

확인

# 프로메테우스 ingress 확인
kubectl get ingress -n monitoring kube-prometheus-stack-prometheus
kubectl describe ingress -n monitoring kube-prometheus-stack-prometheus

# 프로메테우스 ingress 도메인으로 웹 접속
echo -e "Prometheus Web URL = https://prometheus.$MyDomain"

# 그라파나 ingress 확인
kubectl get ingress -n monitoring kube-prometheus-stack-grafana
kubectl describe ingress -n monitoring kube-prometheus-stack-grafana

# 그라파나 ingress 도메인으로 웹 접속 : 기본 계정 - admin / prom-operator
echo -e "Grafana Web URL = https://grafana.$MyDomain"

 


 

삭제 방법

eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME

 


 

쿠버네티스 특징

 


 

쿠버네티스 아키텍처

`Kubernetes Components` : K8S 클러스터는 Controle Plane(마스터)와 Node(노드)로 구성 - 링크

출처 : CloudNet@ 스터디

 

출처 : https://blog.naver.com/love_tolty/222167051615

 


 

선언형(멱등성) 실습

쿠버네티스의 선언형 접근법과 멱등성은 쿠버네티스의 핵심 원칙 중 하나이다.

 

​선언형 접근법 (Declarative Approach)

  • 선언형 접근법에서는 원하는 시스템의 최종 상태를 선언하고, 시스템이 그 상태를 달성하도록 자동으로 변경한다.
  • 예를 들어, 쿠버네티스에서 파드의 수를 5개로 유지하고 싶다면, 원하는 상태를 선언하기만 하면 쿠버네티스가 이를 관리한다. 이 접근법의 장점은 시스템의 현재 상태를 직접 관리할 필요가 없다는 것 이다.

 

멱등성 (Idempotence)

  • 멱등성은 동일한 연산을 여러 번 적용하더라도 동일한 결과를 가져오는 성질을 의미한다.
  • 쿠버네티스의 선언형 접근법은 멱등성을 가져야 한다.
  • 즉, 동일한 구성을 여러 번 적용하더라도 시스템의 최종 상태는 동일해야 한다.
  • 예를 들어, 동일한 파드 구성을 여러 번 적용하더라도, 최종적으로 원하는 파드의 수와 상태가 유지되어야 한다.

 

# 터미널1 (모니터링)
watch -d 'kubectl get pod'

# 터미널2
# Deployment 배포(Pod 3개)
kubectl create deployment my-webs --image=nginx --replicas=3
kubectl get pod -w

# 파드 증가 및 감소
kubectl scale deployment my-webs --replicas=6 && kubectl get pod -w
kubectl scale deployment my-webs --replicas=3
kubectl get pod

# 강제로 파드 삭제 : 바라는상태 + 선언형에 대한 대략적인 확인! ⇒ 어떤 일이 벌어지는가? 
kubectl delete pod --all && kubectl get pod -w
kubectl get pod

# 실습 완료 후 Deployment 삭제
kubectl delete deploy my-webs

 

 


 

Amazon EKS 소개

Amazon Elastic Kubernetes Service는 자체 Kubernetes 컨트롤 플레인 또는 노드를 설치, 운영 및 유지 관리할 필요 없이 Kubernetes 실행에 사용할 수 있는 관리형 서비스


EKS 아키텍처

  • EKS 컨트롤 플레인 : AWS Managed VPC - 3개 AZ, API NLB, ETCD ELB - 링크

 

출처 : https://awskoreamarketingasset.s3.amazonaws.com/2022

ETCD란? 

Kubernetes는 etcd모든 클러스터 데이터의 기본 스토리지로 사용된다. Kubernetes 클러스터 상태를 저장하고 복제하는 데 도움이 된다.

  1. 서비스 검색 : Kubernetes의 포드, 서비스 및 기타 워크로드는 시간이 지남에 따라 변경될 수 있는 IP 주소를 얻는다. `etcd` 서비스 검색에 중요한 서비스 및 해당 위치에 대한 일관된 보기를 제공한다.  
  2. 구성 : ConfigMap 및 Secrets와 같은 구성 세부정보는 `etcd` 에 저장된다.
  3. API 서버 : 모든 Kubernetes는 api-server시스템의 전체 상태를 알고 있으며 이 데이터를 `etcd`에 저장한다.


 

쿠버네티스 스토리지

  • 파드 내부의 데이터는 파드가 정지되면 모두 삭제됨 → 즉, 파드가 모두 상태가 없는(Stateless) 애플리케이션이였다. Temporary filesystem, Volume - 링크

출처 : https://aws.amazon.com/ko/blogs/tech/persistent-storage-for-kubernetes/

 

데이터베이스(파드)처럼 데이터 보존이 필요 == 상태가 있는(Stateful) 애플리케이션 : PV & PVC → 로컬 볼륨(hostPath) ⇒ 퍼시스턴트 볼륨(Persistent Volume, PV) - 어느 노드에서도 연결하여 사용 가능, 예시) NFS, AWS EBS, Ceph 등

파드가 생성될 때 자동으로 볼륨을 마운트하여 파드에 연결하는 기능을 동적 프로비저닝(Dynamic Provisioning)이라고 한다.

 

퍼시스턴트 볼륨의 사용이 끝났을 때 해당 볼륨은 어떻게 초기화할 것인지 별도로 설정할 수 있는데, 쿠버네티스는 이를 Reclaim Policy 라고 부른다.

 

Reclaim Policy 에는 크게 Retain(보존), Delete(삭제, 즉 EBS 볼륨도 삭제됨) 방식이 있습니다.


 

스토리지 소개 

  • 출처 : (🧝🏻‍♂️) 김태민 기술 블로그 - 링크

 

`볼륨`

  • emptyDir, hostPath, PV/PVC

출처 : 김태민 기술 블로그

 

`다양한 볼륨사용`

  • K8S 자체 제공(hostPath, local), 온프렘 솔루션(ceph 등), NFS, 클라우드 스토리지(AWS EBS 등)

출처 : 김태민 기술 블로그

 

`동적 프로비저닝` & 볼륨 상태 , `ReclaimPolicy`

출처 : 김태민 기술 블로그

 


 

AWS EBS Controller

 

  • Volume (ebs-csi-controller) 소개 : EBS CSI driver 동작 : 볼륨 생성 및 파드에 볼륨 연결 - 링크
    • persistentvolume, persistentvolumeclaim의 accessModes는 ReadWriteOnce로 설정해야 한다 - Why?
      • accessModes는 PV와 PVC에서 스토리지에 어떻게 접근할 것인지를 정의한다.
      • 주요 accessModes에는 ReadWriteOnce (RWO), ReadOnlyMany (ROX), ReadWriteMany (RWX)가 있다.
      • ReadWriteOnce (RWO)는 스토리지 볼륨에 단일 노드에서 읽기/쓰기로 접근할 수 있음을 의미한다.
    • EBS스토리지 기본 설정이 동일 AZ에 있는 EC2 인스턴스(에 배포된 파드)에 연결해야 한다 - Why? 파드 스케줄링 방안은?
      • AWS EBS 볼륨은 특정 AZ에서 프로비저닝되며 해당 AZ 내의 EC2 인스턴스만 해당 볼륨에 연결할 수 있다.
      • EBS 볼륨을 다른 AZ의 EC2 인스턴스에 연결하려면 볼륨 스냅샷을 생성하고 해당 스냅샷을 사용하여 새 AZ에서 볼륨을 생성해야 한다

출처 : https://malwareanalysis.tistory.com/598

 

 


 

PVC/PV 파드 테스트

# 워커노드의 EBS 볼륨 확인 : tag(키/값) 필터링 - 링크
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --output table
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[*].Attachments" | jq
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[*].{ID:VolumeId,Tag:Tags}" | jq
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[].[VolumeId, VolumeType, Attachments[].[InstanceId, State][]][]" | jq
aws ec2 describe-volumes --filters Name=tag:Name,Values=$CLUSTER_NAME-ng1-Node --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq

# 워커노드에서 파드에 추가한 EBS 볼륨 확인
aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --output table
aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[*].{ID:VolumeId,Tag:Tags}" | jq
aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" | jq

# 워커노드에서 파드에 추가한 EBS 볼륨 모니터링
while true; do aws ec2 describe-volumes --filters Name=tag:ebs.csi.aws.com/cluster,Values=true --query "Volumes[].{VolumeId: VolumeId, VolumeType: VolumeType, InstanceId: Attachments[0].InstanceId, State: Attachments[0].State}" --output text; date; sleep 1; done

# PVC 생성
cat <<EOT > awsebs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ebs-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 4Gi
  storageClassName: gp3
EOT
kubectl apply -f awsebs-pvc.yaml
kubectl get pvc,pv

# 파드 생성
cat <<EOT > awsebs-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]
    args: ["-c", "while true; do echo \$(date -u) >> /data/out.txt; sleep 5; done"]
    volumeMounts:
    - name: persistent-storage
      mountPath: /data
  volumes:
  - name: persistent-storage
    persistentVolumeClaim:
      claimName: ebs-claim
EOT
kubectl apply -f awsebs-pod.yaml

# PVC, 파드 확인
kubectl get pvc,pv,pod
kubectl get VolumeAttachment

# 추가된 EBS 볼륨 상세 정보 확인 
aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq

# PV 상세 확인 : nodeAffinity 내용의 의미는?
kubectl get pv -o yaml | yh
...
    nodeAffinity:
      required:
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.ebs.csi.aws.com/zone
            operator: In
            values:
            - ap-northeast-2b
...

kubectl get node --label-columns=topology.ebs.csi.aws.com/zone,topology.kubernetes.io/zone
kubectl describe node | more

# 파일 내용 추가 저장 확인
kubectl exec app -- tail -f /data/out.txt

# 아래 명령어는 확인까지 다소 시간이 소요됨
kubectl df-pv

 PV NAME                                   PVC NAME   NAMESPACE  NODE NAME                                         POD NAME  VOLUME MOUNT NAME   SIZE  USED  AVAILABLE  %USED  IUSED  IFREE   %IUSED
 pvc-4cbf6134-73c7-478b-a2e3-c535b293304e  ebs-claim  default    ip-192-168-3-203.ap-northeast-2.compute.internal  app       persistent-storage  3Gi   24Ki  3Gi        0.00   11     262133  0.00

## 파드 내에서 볼륨 정보 확인
kubectl exec -it app -- sh -c 'df -hT --type=overlay'
kubectl exec -it app -- sh -c 'df -hT --type=ext4'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   ext4  3.9G   16M  3.8G   1% /data

 

볼륨 증가 - 링크 ⇒ 늘릴수는 있어도 줄일수는 없다! - 링크

# 현재 pv 의 이름을 기준하여 4G > 10G 로 증가 : .spec.resources.requests.storage의 4Gi 를 10Gi로 변경
kubectl get pvc ebs-claim -o jsonpath={.spec.resources.requests.storage} ; echo
kubectl get pvc ebs-claim -o jsonpath={.status.capacity.storage} ; echo
kubectl patch pvc ebs-claim -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}'
kubectl patch pvc ebs-claim -p '{"status":{"capacity":{"storage":"10Gi"}}}' # status 는 바로 위 커멘드 적용 후 EBS 10Gi 확장 후 알아서 10Gi 반영됨

Console 확인 ing중!

 

# 확인 : 볼륨 용량 수정 반영이 되어야 되니, 수치 반영이 조금 느릴수 있다
kubectl exec -it app -- sh -c 'df -hT --type=ext4'
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/nvme1n1   ext4  9.8G   28K  9.8G   1% /data

kubectl df-pv

 PV NAME                                   PVC NAME   NAMESPACE  NODE NAME                                         POD NAME  VOLUME MOUNT NAME   SIZE  USED  AVAILABLE  %USED  IUSED  IFREE   %IUSED
 pvc-4cbf6134-73c7-478b-a2e3-c535b293304e  ebs-claim  default    ip-192-168-3-203.ap-northeast-2.compute.internal  app       persistent-storage  9Gi   28Ki  9Gi        0.00   12     655348  0.00

aws ec2 describe-volumes --volume-ids $(kubectl get pv -o jsonpath="{.items[0].spec.csi.volumeHandle}") | jq

 

Pod 삭제 후 재생성해도 데이터가 남아있는지 확인

kubectl delete pod app
pod "app" deleted

kubectl apply -f awsebs-pod.yaml
pod/app created

kubectl exec app -- tail -f /data/out.txt
Sun Oct 15 12:39:34 UTC 2023
Sun Oct 15 12:39:39 UTC 2023
Sun Oct 15 12:39:44 UTC 2023
Sun Oct 15 12:39:49 UTC 2023
Sun Oct 15 12:39:54 UTC 2023
Sun Oct 15 12:39:59 UTC 2023
Sun Oct 15 12:40:04 UTC 2023
Sun Oct 15 12:40:09 UTC 2023
Sun Oct 15 12:40:51 UTC 2023
Sun Oct 15 12:40:56 UTC 2023

 

 

삭제

kubectl delete pod app
kubectl get pvc,pv
kubectl delete pvc ebs-claim

 

 


 

쿠버네티스 네트워크

  • `K8S CNI` : Container Network Interface 는 k8s 네트워크 환경을 구성해준다 - 링크, 다양한 플러그인이 존재 - 링크

 


 

AWS VPC CNI

  • 파드의 IP를 할당해준다, 파드의 IP 네트워크 대역과 노드(워커)의 IP 대역이 같아서 직접 통신이 가능하다 - Github Proposal

출처 : CloudNet@ 스터디

 

 


 

[실습] 테스트용 파드 생성

# 테스트용 파드 netshoot-pod 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: netshoot-pod
  template:
    metadata:
      labels:
        app: netshoot-pod
    spec:
      containers:
      - name: netshoot-pod
        image: nicolaka/netshoot
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

# 파드 이름 변수 지정
PODNAME1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].metadata.name})
PODNAME2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].metadata.name})
PODNAME3=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[2].metadata.name})

# 파드 확인
kubectl get pod -o wide
NAME                           READY   STATUS    RESTARTS   AGE   IP              NODE
         NOMINATED NODE   READINESS GATES
netshoot-pod-84d58d854-5xfdb   1/1     Running   0          22s   192.168.1.59    ip-192-168-1-46.ap-northeast-2.compute.internal    <none>           <none>
netshoot-pod-84d58d854-krb58   1/1     Running   0          22s   192.168.3.206   ip-192-168-3-203.ap-northeast-2.compute.internal   <none>           <none>
netshoot-pod-84d58d854-vg6t6   1/1     Running   0          22s   192.168.2.239   ip-192-168-2-25.ap-northeast-2.compute.internal    <none>           <none>

kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

 

  • 파드가 생성되면, 워커 노드에 eniY@ifN 추가되고 라우팅 테이블에도 정보가 추가된다

 

테스트용 파드 접속(exec) 후 확인

# 테스트용 파드 접속(exec) 후 Shell 실행
kubectl exec -it $PODNAME1 -- zsh

# 아래부터는 pod-1 Shell 에서 실행 : 네트워크 정보 확인
----------------------------
ip -c addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
3: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
    link/ether e2:21:7e:5c:f2:52 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.1.59/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::e021:7eff:fe5c:f252/64 scope link
       valid_lft forever preferred_lft forever

ip -c route
route -n
ping -c 2 <pod-2 IP> # 192.168.2.25
ex) ping -c 2 192.168.2.25
PING 192.168.2.25 (192.168.2.25) 56(84) bytes of data.
64 bytes from 192.168.2.25: icmp_seq=1 ttl=254 time=2.51 ms
64 bytes from 192.168.2.25: icmp_seq=2 ttl=254 time=1.02 ms

--- 192.168.2.25 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.024/1.766/2.509/0.742 ms

ps
cat /etc/resolv.conf
exit
----------------------------

# 파드2 Shell 실행
kubectl exec -it $PODNAME2 -- ip -c addr

# 파드3 Shell 실행
kubectl exec -it $PODNAME3 -- ip -br -c addr

 


 

[실습] 파드에서 외부 통신 테스트 및 확인

# 작업용 EC2 : pod-1 Shell 에서 외부로 ping
kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com
kubectl exec -it $PODNAME1 -- ping -i 0.1 www.google.com

# 워커 노드 EC2 : 퍼블릭IP 확인
ssh ec2-user@$N1 curl -s ipinfo.io/ip ; echo
3.35.51.76

ssh ec2-user@$N2 curl -s ipinfo.io/ip ; echo
ssh ec2-user@$N3 curl -s ipinfo.io/ip ; echo

# 작업용 EC2 : pod-1 Shell 에서 외부 접속 확인 - 공인IP는 어떤 주소인가?
## The right way to check the weather - 링크
kubectl exec -it $PODNAME1 -- curl -s ipinfo.io/ip ; echo
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul?format=3
kubectl exec -it $PODNAME1 -- curl -s wttr.in/Moon
kubectl exec -it $PODNAME1 -- curl -s wttr.in/:help

 

다음 실습을 위해서 파드 삭제: `kubectl delete deploy netshoot-pod`

 


 

Service란?

Service는 쿠버네티스 클러스터 내부를 외부에 노출해준다. 

  • ClusterIP
  • NodePort
  • LoadBalancer
  • Service(LoadBalancer Controller) : AWS Load Balancer Controller + NLB IP 모드 동작 with AWS VPC CNI

`ClusterIP`

출처 : CloudNet@ 스터디

 

`NodePort`

출처 : CloudNet@ 스터디

 

`LoadBalancer` 

출처 : CloudNet@ 스터디

 

`Service(LoadBalancer Controller)` : AWS Load Balancer Controller + NLB IP 모드 동작 with AWS VPC CNI

출처 : CloudNet@ 스터디

 


 

 

[실습] 서비스/파드 배포 테스트 with NLB

# 작업용 EC2 - 디플로이먼트 & 서비스 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/echo-service-nlb.yaml
cat echo-service-nlb.yaml | yh
kubectl apply -f echo-service-nlb.yaml

# 확인
kubectl get deploy,pod
kubectl get svc,ep,ingressclassparams,targetgroupbindings
kubectl get targetgroupbindings -o json | jq

# AWS ELB(NLB) 정보 확인
aws elbv2 describe-load-balancers | jq
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text

# 웹 접속 주소 확인
kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Pod Web URL = http://"$1 }'

# 파드 로깅 모니터링
kubectl logs -l app=deploy-websrv -f

# 분산 접속 확인
NLB=$(kubectl get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
  52 Hostname: deploy-echo-55456fc798-2w65p
  48 Hostname: deploy-echo-55456fc798-cxl7z

# 지속적인 접속 시도 : 아래 상세 동작 확인 시 유용(패킷 덤프 등)
while true; do curl -s --connect-timeout 1 $NLB | egrep 'Hostname|client_address'; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

 

 

AWS NLB의 대상 그룹 확인 : IP를 확인해보자

  • 파드 2개 → 1개 → 3개 설정 시 동작 : auto discovery ← 어떻게 가능할까?
# 작업용 EC2 - 파드 1개 설정 
kubectl scale deployment deploy-echo --replicas=1

# 확인
kubectl get deploy,pod,svc,ep
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr

# 작업용 EC2 - 파드 3개 설정 
kubectl scale deployment deploy-echo --replicas=3

# 확인
kubectl get deploy,pod,svc,ep
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
  • - 실습 리소스 삭제:  `kubectl delete deploy deploy-echo; kubectl delete svc svc-nlb-ip-type`

 


 

Ingress란?

클러스터 내부의 서비스(ClusterIP, NodePort, Loadbalancer)를 외부로 노출(HTTP/HTTPS) - Web Proxy 역할

  • ​ AWS Load Balancer Controller + Ingress (ALB) IP 모드 동작 with AWS VPC CNI 를 사용

 


 

[실습] 서비스/파드 배포 테스트 with Ingress(ALB) - ALB

# 게임 파드와 Service, Ingress 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ingress1.yaml
cat ingress1.yaml | yh
kubectl apply -f ingress1.yaml

# 모니터링
watch -d kubectl get pod,ingress,svc,ep -n game-2048

# 생성 확인
kubectl get-all -n game-2048
kubectl get ingress,svc,ep,pod -n game-2048
kubectl get targetgroupbindings -n game-2048
NAME                               SERVICE-NAME   SERVICE-PORT   TARGET-TYPE   AGE
k8s-game2048-service2-f0a415c192   service-2048   80             ip            2m32s

# Ingress 확인
kubectl describe ingress -n game-2048 ingress-2048

# 게임 접속 : ALB 주소로 웹 접속
kubectl get ingress -n game-2048 ingress-2048 -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Game URL = http://"$1 }'
Game URL = http://k8s-game2048-ingress2-70d50ce3fd-1856533181.ap-northeast-2.elb.amazonaws.com

# 파드 IP 확인
kubectl get pod -n game-2048 -owide

 

  • ALB 대상 그룹에 등록된 대상 확인 : ALB에서 파드 IP로 직접 전달

도메인 접속 확인
대상그룹 확인

 

 

 

파드 3개로 증가

# 터미널1
watch kubectl get pod -n game-2048

# 터미널2 : 파드 3개로 증가
kubectl scale deployment -n game-2048 deployment-2048 --replicas 3

 

파드 1개로 감소

# 터미널2 : 파드 1개로 감소
kubectl scale deployment -n game-2048 deployment-2048 --replicas 1

 

 

실습 리소스 삭제

kubectl delete ingress ingress-2048 -n game-2048
kubectl delete svc service-2048 -n game-2048 && kubectl delete deploy deployment-2048 -n game-2048 && kubectl delete ns game-2048

 


 

ExternalDns란?

K8S 서비스/인그레스 생성 시 도메인을 설정하면, AWS(Route 53), Azure(DNS), GCP(Cloud DNS) 에 레코드 자동 생성/삭제한다.

ExternalDNS CTRL 권한 주는 방법 3가지 : Node IAM Role, Static credentials, IRSA

출처 : https://edgehog.blog/a-self-hosted-external-dns-resolver-for-kubernetes-111a27d6fc2c

 

 


 

CoreDns란?

CoreDNS는 유연하고 확장 가능한 DNS 서버로서, 특히 쿠버네티스 환경에서 서비스 디스커버리에 사용되는 도구이다. 쿠버네티스에서는 기존의 kube-dns를 대체하기 위해 CoreDNS를 기본 DNS 시스템으로써 도입되었다.

출처 : Cloudnet@ 스터디

 

 

 


 

스테이트풀셋 & 헤드리스서비스

 


 

Stateless vs Stateful 비교 및 스테이트풀셋 소개

`Stateless`와 `Stateful`은 애플리케이션 또는 시스템의 동작 방식과 데이터의 지속성에 관한 용어이다.

 

Stateless 

  • 애플리케이션 또는 시스템이 이전의 요청 또는 트랜잭션에서의 상태 정보를 기억하지 않고 각 요청을 별개의 단위로 처리하는 것을 의미한다.
  • 각 요청은 독립적으로 처리되며, 이전 또는 이후의 요청에 영향을 주지 않는다.

Stateful 

  • 애플리케이션 또는 시스템이 이전의 요청 또는 트랜잭션에서의 상태 정보를 유지하고, 이 정보를 이용해 후속 요청을 처리하는 것을 의미한다.
  • 상태 정보는 메모리, 디스크, 또는 외부 데이터베이스 등에 저장될 수 있다.

 

 

출처 : CloudNet@ 스터디

 

이와 반대로 쿠버네티스에서 상태가 존재하는 포드를 지칭할 때는 '애완동물'에 비유한다.

애완동물에는 특별한 이름을 붙여주기 때문에 다른 애완동물과 명확히 구분된다.

따라서 사람의 입장에서 애완동물은 대체 불가능한 개체로서, 항상 고유한 식별자를 갖는 것으로 여겨진다.

이와 같은 이유로 쿠버네티스에서는 상태를 갖는 애플리케이션, 즉 스테이트풀셋을 통해 생성되는 포드를 보통 애완동물에 비유한다.

상태를 갖는 각 포드는 모두 고유하며, 쉽게 대체될 수 없기 때문다.

 


 

스테이트풀셋 & 헤드리스서비스 실습

# 모니터링
watch -d 'kubectl get sts,pods,svc,pvc,pv'

# 스테이트풀셋 & 헤드리스서비스 배포
curl -s -O https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/application/web/web.yaml
cat web.yaml | yh
kubectl apply -f web.yaml && kubectl get pods -w -l app=nginx

# 파드 hostname 확인
for i in 0 1; do kubectl exec "web-$i" -- sh -c 'hostname'; done
kubectl df-pv

# netshoot 이미지로 netdebug 파드에 zsh 실행
kubectl run -it --rm netdebug --image=nicolaka/netshoot --restart=Never -- zsh
--------------------
nslookup nginx
nslookup -type=srv nginx
nslookup web-0.nginx
nslookup web-1.nginx
exit
--------------------

# 파드 삭제 실행 후 재실행 시 생성 순서 확인
kubectl delete pod -l app=nginx && kubectl get pods -w -l app=nginx

# 파드 hostname 확인
for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; done

# netshoot 이미지로 netdebug 파드에 zsh 실행
kubectl run -it --rm netdebug --image=nicolaka/netshoot --restart=Never -- zsh
--------------------
nslookup nginx
Server:         10.100.0.10
Address:        10.100.0.10#53

Name:   nginx.default.svc.cluster.local
Address: 192.168.1.19
Name:   nginx.default.svc.cluster.local
Address: 192.168.3.84

nslookup -type=srv nginx
Server:         10.100.0.10
Address:        10.100.0.10#53

nginx.default.svc.cluster.local service = 0 50 80 web-0.nginx.default.svc.cluster.local.
nginx.default.svc.cluster.local service = 0 50 80 web-1.nginx.default.svc.cluster.local.

nslookup web-0.nginx
Server:         10.100.0.10
Address:        10.100.0.10#53

Name:   web-0.nginx.default.svc.cluster.local
Address: 192.168.3.84

nslookup web-1.nginx


exit
--------------------

# PVC 확인
kubectl get pvc -l app=nginx
kubectl get pvc -l app=nginx
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
www-web-0   Bound    pvc-0c499e39-d55d-4e38-9c7a-ef8ec58c6730   1Gi        RWO            gp2            116s
www-web-1   Bound    pvc-c2b76cc8-f9e4-4c47-8e63-a15491dd5287   1Gi        RWO            gp2            104s

# 웹 서버 index.html 에 hostname 추가 후 웹 접속 해서 확인
# 웹 서버 파드는 볼륨 마운트 정보 : Mounts: /usr/share/nginx/html from www (rw) - PersistentVolumeClaim
for i in 0 1; do kubectl exec "web-$i" -- sh -c 'echo "$(hostname)-pv-test" > /usr/share/nginx/html/index.html'; done
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done

# 파드 삭제 실행 후 재실행 시 생성 순서 확인
kubectl delete pod -l app=nginx && kubectl get pods -w -l app=nginx

# 웹 접속 해서 확인 : PV 저장소 확인
for i in 0 1; do kubectl exec -i -t "web-$i" -- curl http://localhost/; done

# 파드 증가
kubectl scale sts web --replicas=5 && kubectl get pods -w -l app=nginx

# 파드 감소
kubectl patch sts web -p '{"spec":{"replicas":3}}' && kubectl get pods -w -l app=nginx

# 삭제
kubectl delete -f web.yaml && kubectl delete pvc --all

 

 


 

(실습 완료 후) 자원 삭제

  • 삭제 방안 : 장점(1줄 명령어로 완전 삭제), 단점(삭제 실행과 완료까지 SSH 세션 유지 필요)
eksctl delete cluster --name $CLUSTER_NAME && aws cloudformation delete-stack --stack-name $CLUSTER_NAME

 

 


 

Reference

 

728x90
반응형