PKOS 7주차) IAM, ARMO, Polaris

메타데이터

EC2 메타데이터 탈취하기

# 워커 노드 1대 EC2 메타데이터를 제거한다
kops edit ig nodes-ap-northeast-2a
---
# 아래 3줄 제거
spec:
  *instanceMetadata:
    httpPutResponseHopLimit: 1
    httpTokens: required*
---
# 업데이트 적용 : 노드1대 롤링업데이트
kops **update** cluster --yes && echo && sleep 3 && kops **rolling-update** cluster --yes

# 테스트용 파드 생성(netshoot)
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: **Deployment**
metadata:
  name: netshoot-pod
spec:
  **replicas: 2**
  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})

# EC2 메타데이터 정보 확인
kubectl exec -it $PODNAME1 -- curl 169.254.169.254 ;echo
kubectl exec -it $PODNAME2 -- curl 169.254.169.254 ;echo

# 파드1에서 EC2 메타데이터 정보 확인
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest ;echo
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/ ;echo
kubectl exec -it $PODNAME1 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/**nodes**.$KOPS_CLUSTER_NAME | jq

# 파드2에서 EC2 메타데이터 정보 확인
kubectl exec -it $PODNAME2 -- curl 169.254.169.254/latest ;echo
kubectl exec -it $PODNAME2 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/ ;echo
kubectl exec -it $PODNAME2 -- curl 169.254.169.254/latest/meta-data/iam/security-credentials/**nodes**.$KOPS_CLUSTER_NAME | jq

파드에 접속해서 명령어를 날리면 액세스 키를 볼 수 있다!

파드 탈취 후 EC2 메타데이터의 IAM Role 토큰 정보로 AWS 서비스 강제 사용boto3

  • Boto란? Python용 AWS SDK
# boto3 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: **Deployment**
metadata:
  name: boto3-pod
spec:
  **replicas: 2**
  selector:
    matchLabels:
      app: boto3
  template:
    metadata:
      labels:
        app: boto3
    spec:
      containers:
      - name: boto3
        image: **jpbarto/boto3**
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

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

# 파드에 접속(1이나 2 중 선택)
kubectl exec -it $PODNAME1 -- sh

# AWS 리소스에 대한 접근 및 명령 py파일 생성
**cat <<EOF> ec2.py**
import boto3

ec2 = boto3.client('ec2', region_name = 'ap-northeast-2')
response = ec2.describe_instances()
print(response)
**EOF**

# 파이썬 파일 실행
**python ec2.py**  # aws ec2 describe-vpcs

# 실습 완료 후 삭제
kubectl delete deploy boto3-pod
  • boto3으로 EC2에 접근하여 정보를 탈취할 수 있다

과제1)Boto3으로 다른 실습 진행하기

cat <<EOF> ec22.py
import boto3

ec2 = boto3.client('ec2', region_name='ap-northeast-2')
response = ec2.describe_regions()
print('Regions:', response['Regions'])

EOF

Kubescape

kubescape란?

IDE, CI/CD 파이프라인 및 클러스터를 위한 오픈소스 Kubernetes 보안 플랫폼

  • NSA/CISA 에서 발표한 보안 규정 권고 사항 대비 얼마나 보안이 지켜지고 있는지 확인하는 오픈소스 도구

ARMO

kubescrape를 웹에서 간편하게 사용할 수 있는 웹 포탈

Kubescape

# ARMO를 통해 쿠버네티스 클러스터 검사하기
# kubescrape 설치
curl -s <https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh> | /bin/bash

# 웹 포털에서 주어지는 명령어를 입력하면 아래와 같은 내용이 뜬다
>>
helm upgrade --install kubescape kubescape/kubescape-cloud-operator -n kubescape --create-namespace --set account=022f6a1c-8239-4567-b08c-0b510b9d47de --set clusterName=`kubectl config current-context`
Release "kubescape" does not exist. Installing it now.
NAME: kubescape
LAST DEPLOYED: Mon Feb 27 22:43:47 2023
NAMESPACE: kubescape
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing kubescape-cloud-operator version 1.9.16.

In a few minutes your scan results will be available in the following link:
<https://cloud.armosec.io/compliance/play-board-net>

You can see and change the values of your's recurring configurations daily scan in the following link:
<https://cloud.armosec.io/settings/assets/clusters/scheduled-scans?cluster=play-board-net>
> kubectl -n kubescape get cj kubescape-scheduler -o=jsonpath='{.metadata.name}{"\\t"}{.spec.schedule}{"\\n"}'

You can see and change the values of your's recurring images daily scan in the following link:
<https://cloud.armosec.io/settings/assets/images>
> kubectl -n kubescape get cj kubevuln-scheduler -o=jsonpath='{.metadata.name}{"\\t"}{.spec.schedule}{"\\n"}'

See you!!!

과제2) ARMO 실습

Polaris

Polaris란?

Kubernetes 클러스터의 자동 감사를 수행하는 오픈 소스 도구

# 폴라리스 설치를 위한 네임스페이스 생성
kubectl create ns polaris

#
cat <<EOT > polaris-values.yaml
dashboard:
  replicas: 1
  service:
    type: LoadBalancer
EOT

# 배포
helm repo add fairwinds-stable <https://charts.fairwinds.com/stable>
helm install polaris fairwinds-stable/polaris --namespace polaris --version 5.7.2 -f polaris-values.yaml

# CLB에 ExternanDNS 로 도메인 연결
kubectl annotate service polaris-dashboard "external-dns.alpha.kubernetes.io/hostname=polaris.$KOPS_CLUSTER_NAME" -n polaris

# 아래 주소로 접속
<http://polaris.play-board.net>

과제3) 폴라리스 실습

앞서 만든 netshoot-pod의 테스트 결과를 참조하여 보안 문제를 수정한다

# 기존 netshoot-pod 삭제
kubectl delete deploy netshoot-pod

# 폴라리스에서 지적하는 보안 사항을 체크하여 다시 파드를 생성한다
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: netshoot-pod
  template:
    metadata:
      labels:
        app: netshoot-pod
    spec:
      containers:
      - name: netshoot-pod
        image: nicolaka/netshoot:v0.9 # 이미지 버전 확인
        command: ["tail"]
        args: ["-f", "/dev/null"]
        imagePullPolicy: Always # 이미지를 항상 리포지토리에서 가져오게 설정
        resources: # 리소스 자원 사용 보장 및 제한 
          limits:
            cpu: 150m
            memory: 512Mi
          requests:
            cpu: 100m
            memory: 128Mi
        securityContext: # 권한 제어
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          privileged: false
          readOnlyRootFilesystem: true
          #runAsNonRoot: true
      terminationGracePeriodSeconds: 0
EOF
#
  • netshoot-pod에 대한 주요 보안 이슈가 대부분 해결된 것을 확인할 수 있다.

Kubernetes의 인증 및 인가

https://happycloud-lee.tistory.com/259

https://happycloud-lee.tistory.com/259

인증(Authentication)

  • CA crt(발급 기관 인증서), Client crt(클라이언트 인증서), Client key(클라이언트 개인키) 를 통해 사용자가 맞는지 인증을 진행한다

인가(Authorization)

  • 인증이 성공하면, 해당 사용자가 특정 명령을 수행할 수 있는 권한이 있는지를 확인한다

실습

각각 다른 권한을 가진 SA를 생성한 뒤 권한을 테스트한다

# 네임스페이스 생성 및 서비스 어카운트 생성
kubectl create namespace dev-team
kubectl create ns infra-team

kubectl create sa dev-k8s -n dev-team
kubectl create sa infra-k8s -n infra-team
# 각각 네임스피이스에 kubectl 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: dev-kubectl
  namespace: dev-team
spec:
  serviceAccountName: dev-k8s
  containers:
  - name: kubectl-pod
    image: bitnami/kubectl:1.24.10
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: infra-kubectl
  namespace: infra-team
spec:
  serviceAccountName: infra-k8s
  containers:
  - name: kubectl-pod
    image: bitnami/kubectl:1.24.10
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF
# 각각 파드로 접근하여 명령어가 먹히는지 확인한다
kubectl exec -it dev-kubectl -n dev-team -- kubectl get pods
>>
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:dev-team:dev-k8s" cannot list resource "pods" in API group "" in the namespace "dev-team"
command terminated with exit code 1
# 권한이 없어서 pod 목록을 가져올 수 없다
# 모든 권한에 대한 롤 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role-dev-team
  namespace: dev-team
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]
EOF

cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role-infra-team
  namespace: infra-team
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]
EOF

# 롤바인딩 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: roleB-dev-team
  namespace: dev-team
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: role-dev-team
subjects:
- kind: ServiceAccount
  name: dev-k8s
  namespace: dev-team
EOF

cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: roleB-infra-team
  namespace: infra-team
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: role-infra-team
subjects:
- kind: ServiceAccount
  name: infra-k8s
  namespace: infra-team
EOF
# 파드에서 다시 작업 테스트
kubectl exec -it dev-kubectl -n dev-team -- kubectl get pods
>>
NAME          READY   STATUS    RESTARTS   AGE
dev-kubectl   1/1     Running   0          9m52s
#정상적으로 파드 리스트가 츨력되는 것을 확인한다

과제4) 읽기 전용 권한 생성

# 네임스페이스 생성
kubectl create ns read-only

# 서비스 어카운트 생성
kubectl create sa readonly -n read-only

# 파드 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: readonly-pod
  namespace: default
spec:
  serviceAccountName: readonly
  containers:
  - name: kubectl-pod
    image: bitnami/kubectl:1.24.10
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 롤 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: readonly-role
  namespace: default
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["get","watch","list"]
EOF

# 롤바인딩 생성
cat <<EOF | kubectl create -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: readonly-role-binding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: readonly-role
subjects:
- kind: ServiceAccount
  name: readonly
EOF

PKOS 6주차) Grafana ALERT MANAGER

실습 환경 구성

기존 명령어로 실습 환경을 구성한다

프로메테우스와 그라파나 접속 URL을 확인한다

# 프로메테우스 접속 주소
echo -e "https://prometheus.$KOPS_CLUSTER_NAME"
# 그라파나 접속 주소
echo -e "https://grafana.$KOPS_CLUSTER_NAME"
# 얼럿매니저 접속 주소
echo -e "https://alertmanager.$KOPS_CLUSTER_NAME"

AlertManager

AlertManager란?

https://www.oreilly.com/library/view/prometheus-up/9781492034131/ch18.html

Prometheus에서 수집한 메트릭이 일정 수준을 초과하는 등 이벤트가 발생했을 때 슬랙 등을 통해 알려줌

AlertManager → 슬랙 연동

프로메테우스의 Alert으로 접속하여 Firing 체크박스를 클릭한다. 각 체크박스의 의미는 아래와 같다.

  • Inactive : 정상 상태(경고가 비활성화되었다는 뜻)
  • Pending : 설정한 임계값을 초과하였지만 아직 경고는 발생하지 않은 상태. 특정 시간을 넘어가면 경고가 발생된다. 그 전에 다시 정상복구되면 경고는 발생되지 않는다.
  • Firing : 경보가 발생되어 AlertManager에게 경보가 발생된다

위 그림을 보면 3개의 경보가 발생된 것을 확인할 수 있는데, 프로메테우스는 구성할 때 자동으로 WatchDog(탐지견) 메시지가 경보 상태로 설정된다.

이는 해당 기능이 제대로 작동하는지 확인하기 위한 의도적인 경보

슬랙 웹훅 URL을 생성한뒤 이를 파드에 적용한다

cat <<EOT > ~/alertmanager-slack.yaml
alertmanager:
  config:
    **global:**
      resolve_timeout: 5m
      slack_api_url: '<웹훅 주소>'
    **route:**
      group_by: ['*job*']  # namespace
      **group_wait: 10s
      group_interval: 1m
      repeat_interval: 5m**
      receiver: 'slack-notifications'
      routes:
      - receiver: 'slack-notifications'
        matchers:
          - alertname =~ "InfoInhibitor|Watchdog"
    **receivers:**
    - name: 'slack-notifications'
      slack_configs:
      - channel: '#webhook'
        **send_resolved: true**
        title: '[{{.Status | toUpper}}] {{ .CommonLabels.alertname }}'
        text: |
          *Description:* {{ .CommonAnnotations.description }}
EOT

# 업데이트
helm **upgrade** kube-prometheus-stack prometheus-community/kube-prometheus-stack --version 45.0.0 **--reuse-values** -f alertmanager-slack.yaml --namespace monitoring

Firing 상태에 있는 Watchdog에 대해 알림이 슬랙에 발생하는 것을 확인할 수 있다.

얼럿매니저로 이동하여 해당 알림을 Silence시킨다.

그렇지 않으면 5분마다 계속 알람이 발생한다(어차피 Watchdog은 계속 Firing 상태이므로)

장애 재현 테스트

직접 장애를 발생시켜서 제대로 얼럿매니저가 작동하는지 확인해보자

# 노드 하나의 kubelet을 정지시킨다
ssh -i ~/.ssh/id_rsa ubuntu@3.38.105.145
sudo systemctl stop kubelet
  • Pending 상태로 전환되는 것을 확인할 수 있다

시간이 지나면 경고로 전환되고 슬랙에 해당 내용을 확인할 수 있다

PLG 스택

PLG 스택이란?

https://www.infracloud.io/blogs/logging-in-kubernetes-efk-vs-plg-stack/

Promtail + Loki + Grafana 여러 파드의 로그들을 중앙 서버에 저장하고 이를 조회 가능

  1. Promtail: 각 Kubernetes 노드에서 로그 데이터를 수집하는 에이전트 | 로그 데이터를 Loki에게 전달
  2. Loki: 시계열 로그(string) 데이터를 저장하는 DB
    • 전체 로그 기반이 아닌, 메타데이터를 기준으로 인덱스를 생성하여 자원 사용량이 현저히 적음
  3. Grafana : Loki의 Data를 시각화

PLG 스택 설치하기

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

# Repo 추가
helm repo add grafana <https://grafana.github.io/helm-charts>

# 로키 파라미터 설정 파일 생성
cat <<EOT > ~/loki-values.yaml
persistence:
  enabled: true
  size: 20Gi

serviceMonitor:
  enabled: true
EOT

# 로키 배포
helm install loki grafana/loki --version 2.16.0 -f loki-values.yaml --namespace loki

# 프롬테일 파라미터 설정 파일 생성
cat <<EOT > ~/promtail-values.yaml
serviceMonitor:
  enabled: true
config:
  serverPort: 3101
  clients:
    - url: <http://loki-headless:3100/loki/api/v1/push>
#defaultVolumes:
#  - name: pods
#    hostPath:
#      path: /var/log/pods
EOT

# 프롬테일 배포
helm install promtail grafana/promtail --version 6.0.0 -f promtail-values.yaml --namespace loki

Loki 데이터를 Grafana를 통해 시각화

  • 그라파나 접속하여 설정 페이지에서 Data Source에 Loki를 선택
  • while true; do curl -s http://nginx.$KOPS_CLUSTER_NAME -I | head -n 1; date; sleep 1; done 를 터미널에서 실행한 뒤 Explore 페이지에서 로키를 선택하고 default/nginx를 골라 쿼리를 실행한다
  • 정상적으로 로그가 수집되는 것을 확인할 수 있다.

PKOS) Prometheus 와 Grafana

Prometheus

Prometheus란?

Kubernetes에서 널리 사용되는 오픈 소스 모니터링 시스템

  • 다양한 소스에서 메트릭을 수집하고 저장, 데이터를 분석하고 시각화하기 위한 강력한 쿼리 언어 제공
  • 다차원 데이터 모델
  • 시계열 수집을 위한 HTTP 풀 모델
  • 다양한 그래프 및 대시보드 지원 모드(그라파나 연동 가능)

Prometheus의 작동 원리

  • 각 서비스들은 각 웹서버의 특정 디렉토리에 프로메테우스가 관리하는 각종 메트릭 값을 저장한다.
  • 해당 디렉토리 /metric의 정보들은 프로메테우스에 의해 Pull 되어 수집된다.

Prometheus 구성하기

  • 클러스터 구성 전 수정작업 # 클러스터 구성 시 kops edit cluster #spec 내 해당 내용을 추가한다. kubeProxy: metricsBindAddress: 0.0.0.0
# 모니터링을 위한 네임스페이스 생성
kubectl create ns monitoring

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

# Helm을 이용하여 프로메테우스 설치
helm repo add prometheus-community <https://prometheus-community.github.io/helm-charts>

# 파라미터 파일 생성
cat <<EOT > ~/monitor-values.yaml
alertmanager:
  ingress:
    enabled: true
    ingressClassName: alb

    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/group.name: "monitoring"

    hosts:
      - alertmanager.$KOPS_CLUSTER_NAME

    paths:
      - /*

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

  ingress:
    enabled: true
    ingressClassName: alb

    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/group.name: "monitoring"

    hosts:
      - grafana.$KOPS_CLUSTER_NAME

    paths:
      - /*

prometheus:
  ingress:
    enabled: true
    ingressClassName: alb

    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/group.name: "monitoring"

    hosts:
      - prometheus.$KOPS_CLUSTER_NAME

    paths:
      - /*

  prometheusSpec:
    serviceMonitorSelectorNilUsesHelmValues: false
    retention: 5d
    retentionSize: "10GiB"
EOT

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

# 프로메테우스 접속
<https://prometheus.play-board.net>

정상적으로 프로메테우스가 설치된 것을 확인할 수 있다

PromQL 명령어를 입력하여 그래프 형태로 자료를 확인할 수 있다

Grafana

Grafana란?

데이터 시각화 툴로서 보통 프로메테우스와 같이 사용하는 경우가 많다

Helm의 프로메테우스 스택을 설치하면 데이터 시각화 툴로서 Grafana가 자동으로 설치된다

https://grafana.play-board.net/login

  • 계정 및 비밀번호 : admin / prom-operator

프로메테우스 스택을 통해 설치되어 자동으로 프로메테우스가 데이터 소스로 등록되어 있다

기본 대시보드를 확인해보고 싸제 대시보드를 외부에서 다운받아서 사용해보자

  1. 기본 대시보드
샘플용으로 파드를 2개 생성한다
Dashboard→Browse로 이동한다
파드1,2가 정상적으로 모니터링되고 있는 것을 확인한다
  1. 추가 대시보드
대시보드 페이지에서 원하는 대시 보드를 검색한다
대시보드의 ID를 복사한다
Dashboard→Import로 이동하여 복사했던 ID값으로 대시보드를 불러온다
정상적으로 대시보드가 불러와진 것을 확인한다

Nginx 설치하여 프로메테우스-그라파나로 메트릭 수집 및 시각화 대시보드 구성

# helm 리포지토리 추가
helm repo add bitnami <https://charts.bitnami.com/bitnami>

# Helm 구성 파일을 생성한다
# ServiceMonitor 방식으로 nginx를 대상을 등록
# export 는 9113 포트 사용
# nginx 웹서버 노출은 AWS CLB 기본 사용

cat <<EOT > ~/nginx-values.yaml
metrics:
  enabled: true

  service:
    port: 9113

  serviceMonitor:
    enabled: true
    namespace: monitoring
    interval: 10s
EOT

# 배포
helm install nginx bitnami/nginx --version 13.2.23 -f nginx-values.yaml

# CLB에 ExternanDNS 로 도메인 연결
kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=nginx.$KOPS_CLUSTER_NAME"

정상적으로 Nginx가 작동되는 것을 확인한다

프로메테우스 대상에 정상적으로 nginx가 확인된다

12708번 대시보드(nginx)를 추가해본다

PKOS 4주차) ArgoCD 사용해보기

ArgoCD

ArgoCD란?

아르고CD는 쿠버네티스 환경에서 CD(지속적인 배포)를 구성하기 위한 GitOps 도구

Git에 있는 Manifest 파일에 변경 사항이 있을 때마다, 이를 감지해서 실제 환경에 적용하고 이를 유지하는 역할

ArgoCD 설치하기

argo-cd 5.20.3 · argoproj/argo

# helm으로 설치 진행
helm repo add argo <https://argoproj.github.io/argo-helm>
helm repo update
helm install argocd argo/argo-cd --set server.service.type=LoadBalancer --namespace argocd --version 5.19.14

# admin 계정의 암호 확인
ARGOPW=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d)
echo $ARGOPW
mf8bOtNEq7iHMqq1
Untitled
  • LB의 DNS 주소로 접속하여 admin 계정으로 접속한다

Installation – Argo CD – Declarative GitOps CD for Kubernetes

# ArgoCD 사용을 위한 CLI 설치
curl -sSL -o argocd-linux-amd64 <https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64>
install -m 555 argocd-linux-amd64 /usr/local/bin/argocd
chmod +x /usr/local/bin/argocd

# CLB 도메인 변수 지정
CLB=<각자 자신의 argocd 서비스의 CLB 도메인 주소>
CLB=ab2fca0b4c3954ed6b8e78eb73512b11-548568550.ap-northeast-2.elb.amazonaws.com 

# argocd 서버 로그인
argocd login $CLB --username admin --password $ARGOPW

Gitlab 프로젝트를 ArgoCD와 연동

# 위에서 설치한 깃랩의 프로젝트를 argocd와등록한다
argocd repo add https://gitlab.$KOPS_CLUSTER_NAME/daso/test-daso.git --username daso --password <암호>
>>
Repository 'https://gitlab.play-board.net/daso/test-daso.git' added

Gitlab과 ArgoCD를 이용한 RabbitMQ 배포

# 깃랩 리포와 연결된 디렉토리로 이동한다
cd ~/gitlab-test/test-daso

# helm을 통해 RabbitMQ를 설치한다
helm repo add bitnami <https://charts.bitnami.com/bitnami>
helm repo update
helm fetch bitnami/rabbitmq --untar

# 해당 차트 파일로 이동한 뒤 values값을 하나 더 만든다(my-values.yaml)
cd rabbitmq/
cp values.yaml my-values.yaml

# 깃랩 리포에 PUSH한다
git add
git commit -m "add rabbitmq helm"
git push

# 애플리케이션 yaml파일에서 URL을 수정한다음 배포한다
cd ~/
curl -s -O <https://raw.githubusercontent.com/wikibook/kubepractice/main/ch15/rabbitmq-helm-argo-application.yml>
vi rabbitmq-helm-argo-application.yml
>>
source:
    repoURL: <https://gitlab.play-board/daso/test-daso.git>
    path: rabbitmq

kubectl apply -f rabbitmq-helm-argo-application.yml
  • SYNC 버튼을 누르면 깃랩 페이지와 자동으로 연동된다.

PKOS 4주차) Gitlab 사용해보기

Gitlab이란?

GitLab은 소스 코드 관리, CI등을 제공하는 웹 기반 Git 리포지토리다.

Github에 대한 오픈 소스 대안으로 만들어졌으며 Github의 기능 외에도 여러가지 기능을 포함하고 있음

Installing GitLab by using Helm | GitLab

Gitlab 설치하기

# 설치하기 
helm repo add gitlab <https://charts.gitlab.io/>
helm repo update
helm fetch gitlab/gitlab --untar

# Helm 파일 수정하기
# aws를 사용하므로 대부분의 기능들을 사용할 필요가 없다. 모두 비활성화한다.
vi ~/gitlab/values.yaml
----------------------
global:
  hosts:
    domain: play-board.net          # 52줄
    https: true

  ingress:                  # 66줄
    configureCertmanager: false
    provider: aws
    class: alb
    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
    tls:
      enabled: false

certmanager:
  installCRDs: false
  install: false
  rbac:
    create: false

nginx-ingress:
  enabled: false

prometheus:
  install: false

gitlab-runner:
  install: false
----------------------

#Helm 설치하기
helm install gitlab gitlab/gitlab -f ~/gitlab/values.yaml --namespace gitlab --version 6.8.1

# 계정 암호 확인
kubectl get secrets -n gitlab gitlab-gitlab-initial-root-password --template={{.data.password}} | base64 -d ;echo

Gitlab 접속 및 사용자 생성

✅ 웹 접속 : https://gitlab.play-board.net/(root / 웹 root 계정 암호)

  • 정상적으로 실행되는 것을 확인할 수 있다.
  • 계정 생성 방법
    • Admins → Users로 이동한 뒤 Administrator 권한의 사용자를 생성한다
      • 사용자 화면에서 Impersonation Tokens으로 이동한 뒤 모든 Scope를 다 선택한 뒤 토큰 생성 glpat-odKxhb9M2urqus2RECB_
    • 생성했던 아이디로 다시 접속한다
  • 로그인 후 신규 프로젝트를 생성한다(test-daso, Internal)

PKOS 4주차) Harbor란?

Harbor

Harbor란?

Harbor는 쿠버네티스 컨테이너 이미지를 안전하게 저장하고 배포할 수 있는 온프레미스 기반 솔루션

Harbor 설치하기

# 사용 리전의 인증서 ARN를 변수로 지정한다
CERT_ARN=`aws acm list-certificates --query 'CertificateSummaryList[].CertificateArn[]' --output text`

# Helm을 이용하여 설치를 진행한다
kubectl create ns harbor
helm repo add harbor <https://helm.goharbor.io>
helm fetch harbor/harbor --untar
vi ~/harbor/values.yaml

# Helm Value 파일을 열고 아래 내용을 수정한다
# JSON 형태 파일의 위치를 잘 따라서 수정
expose.tls.certSource=none  # 19줄
expose.ingress.hosts.core=harbor.play-board.net   # 36줄
expose.ingress.hosts.notary=notary.play-board.net  # 37줄
expose.ingress.controller=alb                      # 44줄
expose.ingress.className=alb                       # 46줄
expose.ingress.annotations=alb.ingress.kubernetes.io/scheme: internet-facing
expose.ingress.annotations=alb.ingress.kubernetes.io/target-type: ip
expose.ingress.annotations=alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}, {"HTTP":80}]'
expose.ingress.annotations=alb.ingress.kubernetes.io/certificate-arn: ${CERT_ARN} #변수 이름 직접 입력할 것
externalURL=https://harbor.play-board.net # 131줄

# 설치
helm install harbor harbor/harbor -f ~/harbor/values.yaml --namespace harbor --version 1.11.0

# 확인
kubectl krew install df-pv && kubectl df-pv

AWS 내에서 인스턴스들이 정상적으로 생성된 것을 확인해본다

로드밸런서의 인증서

Route53 DNS 구성

externalIP로 입력한 도메인으로 접속한다

  • 정상적으로 접근되는 것을 확인한다
  • Harbor의 기본 로그인 정보는 admin/Harbor12345 이다
  • 정상적으로 로그인이 되는 것을 확인한다

Harbor 사용해보기

NEW PROJECT를 클릭 후 pkos-daso 라는 이름으로 프로젝트를 생성한다

Harbor는 온프레미스 컨테이너 이미지 레지스트리로서 사용해야하므로 여기 저장할 이미지가 필요하다

# 이미지를 다운받는다(Pull)
docker pull nginx
docker pull busybox

# 이미지에 태그를 한다
docker tag busybox harbor.$KOPS_CLUSTER_NAME/pkos-daso/busybox:0.1
>> harbor.play-board.net/pkos-daso/busybox
# Docker 설정을 위해 아래 구문을 추가한다.
cat <<EOT> /etc/docker/daemon.json
{
    "insecure-registries" : ["harbor.$KOPS_CLUSTER_NAME"]
}
EOT

# 데몬 재시작
systemctl daemon-reload && systemctl restart docker
# Harbor에 로그인한다
docker login harbor.$KOPS_CLUSTER_NAME -u admin -p Harbor12345
>>
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
&https://docs.docker.com/engine/reference/commandline/login/#credentials-store>

Login Succeeded

# 이미지를 업로드한다
docker push harbor.$KOPS_CLUSTER_NAME/pkos-daso/busybox:0.1
  • 정상적으로 생성한 이미지가 Harbor에 업로드된 것을 확인할 수 있다.

PKOS 3주차) Storage

쿠버네티스의 스토리지 종류

기본적으로 쿠버네티스는 Stateless 방식으로 각 파드의 생명주기에 따라 데이터도 모조리 삭제된다.
하지만 실사용간 데이터 보존이 필요한 경우가 있어 여러가지 형태로 스토리지를 사용할 수 있다
주요 스토리지는 다음과 같다

emptydir
파드가 노드에 생성되는 경우 파드 안에 생기는 볼륨으로 해당 노드에서 파드가 실행되는 동안에만 존재한다

 🔒 컨테이너가 크래시되어도 emptyDir 볼륨의 데이터는 안전하다고 함
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume  
  volumes:
  - name: cache-volume
    emptyDir: {}

hostpath

공식 문서에 hostpath를 검색하자마자 살벌한 경고문이 뜬다. 나는 공식 문서의 신봉자이니만큼 hostpath를 쓰지 않겠다고 선언했다.

하지만 어떤 녀석인지는 알아봐야한다

hostpath는 호스트 노드의 파일시스템에 있는 파일이나 디렉터리를 파드에 연결한다.

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: registry.k8s.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # 호스트의 디렉터리 위치
      path: /data
      # 이 필드는 선택 사항이다
      type: Directory

PersistentVolume/PersistentVolumeClaim

PV는 클러스터 스토리지다.이름처럼 영구적인 저장 공간을 제공하는 리소스라고 생각하면 편하다.
굳이 예를 들자면 물류 창고 단지라고 할 수 있다.

PVC는 사용자가 스토리지가 필요할 때 저장 공간을 요청하기 위해 필요한 리소스다.
사용자가 어떤 스토리지가 얼마나 필요한지 PVC를 통해 요청할 수 있다.
물류 창고 상황을 잘 알고 있는 창고 관리자라고 이해할 수 있다

PV를 사용하는 방법을 간단하게 요약하면 아래와 같다.

  1. PV를 생성한다. 이때 PV를 파드와 바로 연결하지 않는다. (창고 단지를 만듦)
  2. 적합한 PV에 자동으로 바인딩되는 PVC를 생성한다 (창고 관리자 김씨 아저씨 불러옴)
  3. 스토리지가 필요한 파드에 PVC를 바인딩한다. (물건을 창고가 아니라 김씨 아저씨한테 보냄)

쿠버네티스는 파드와 스토리지를 직접 연결하지 않고 PVC라는 중간 단계를 거쳐 바인딩한다.
PVC를 통해 필요한 스토리지 스펙을 정해놓으면,
PVC가 알아서 적절한 스토리지로 바인딩하므로 파드는 어떤 스토리지를 쓸지 고민할 필요가 없다.

Hostpath PV/PVC 실습(Local Path Provisioner)

Local Path Provisioner는 각 노드의 로컬 스토리지를 활용할 수 있게 해주는 프로그램이다

https://github.com/rancher/local-path-provisioner

# 프로그램 다운로드
curl -s -O https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.23/deploy/local-path-storage.yaml>

# Deployment에 내용 추가
spec:
      nodeSelector:
        kubernetes.io/hostname: "<마스터 노드 이름>"
      tolerations:
        - effect: NoSchedule
          key: node-role.kubernetes.io/control-plane
          operator: Exists

# Configmap 수정
data:
  config.json: |-
    {
            "nodePathMap":[
            {
                    "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
                    "paths":["/data/local-path"]
            }
            ]
    }
# 배포
k create -f local-path-storage.yaml

PVC 생성하기

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: localpath-claim
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: "local-path"

# 위 내용대로 PVC 생성

파드 생성

apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  terminationGracePeriodSeconds: 3
  containers:
  - name: app
    image: centos
    command: ["/bin/sh"]  # out.txt 파일을 생성하여 data 디렉토리 아래에 넣는다
    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: localpath-claim # 앞서 생성한 PVC 이름

# 위 내용으로 Pod 생성

파드가 생성된 노드를 확인

NAME   READY   STATUS         IP             NODE               
app    1/1     Running        172.30.65.57   i-0d4e418aef046782e

해당 노드에 SSH 접속한 뒤 파일을 찾아보면 있다


ubuntu@i-0d4e418aef046782e:/data/local-path/pvc-7aea0be5-072c-4d42-a6b4-cce75003bd62_default_localpath-claim$ cat out.txt
Wed Feb 1 11:44:06 UTC 2023
Wed Feb 1 11:44:11 UTC 2023
Wed Feb 1 11:44:16 UTC 2023
Wed Feb 1 11:44:21 UTC 2023
Wed Feb 1 11:44:26 UTC 2023
  • 다른 노드에서 찾아보면 해당 파일이 없다. 파드가 생성된 노드에만 있음

파드를 삭제하고 데이터를 확인한다

# 파드 삭제
kubectl delete pod app

# 데이터가 있던 위치를 살펴본다
ssh -i ~/.ssh/id_rsa ubuntu@3.35.8.69 cat /data/local-path/pvc-7aea0be5-072c-4d42-a6b4-cce75003bd62_default_localpath-claim/out.txt
Wed Feb 1 11:44:06 UTC 2023
Wed Feb 1 11:44:11 UTC 2023
Wed Feb 1 11:44:16 UTC 2023 # 있음!

Kubestr & sar 모니터링 및 성능 측정 확인 (NVMe SSD)

Kubestr

스토리지 간의 IOPS 차이를 확인하는 프로그램이다

테스트용 스토리지 값을 입력하면 자동으로 PV/PVC/Pod을 원클릭으로 만들어서 IOPS를 테스트한다.

  • IOPS란? HDD, SDD, NVMe 등 저장장치의 속도를 나타내는 측정 단위
    → 초당 처리되는 I/O의 개수
    → IOPS가 100인 경우 읽고 쓰는 작업을 할 때 1초에 100 * 블럭크기 만큼 보장
# kubestr 설치 
wget https://github.com/kastenhq/kubestr/releases/download/v0.4.36/kubestr_0.4.36_Linux_amd64.tar.gz
tar xvfz kubestr_0.4.36_Linux_amd64.tar.gz && mv kubestr /usr/local/bin/ && chmod +x /usr/local/bin/kubestr

PKOS 3주차) Ingress

인그레스(Ingress란?)

외부에서 들어오는 요청을 클러스터 내부로 전달해주는 역할을 한다.(HTTP/HTTPS)
서비스에 외부에서 연결할 수 있는 URL을 제공하고 들어오는 트래픽을 로드밸런싱까지 해줌

AWS의 VPC CNI의 경우
노드와 파드가 같은 IP 대역을 사용하므로, 인그레스(LB)가 노드를 거치지 않고 파드와 바로 통신할 수 있다.

인그레스의 구조는 다음과 같다.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80

인그레스 구성을 위한 LB 및 External DNS 설치

인그레스를 구성하기 위해 로드밸런서와 External DNS를 설치하고 배포한다

우선 배포 전에 IAM 역할을 생성해야한다

# IAM 정책을 생성한다. 이전 실습에서 만든 경우 Already Exist 에러가 발생된다. 무시하자
# AWSLoadBalancerControllerIAMPolicy 와 AllowExternalDNSUpdates를 생성한다

curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.5/docs/install/iam_policy.json
curl -s -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/AKOS/externaldns/externaldns-aws-r53-policy.json

aws iam create-policy --policy-name **AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json
aws iam create-policy --policy-name **AllowExternalDNSUpdates --policy-document file://externaldns-aws-r53-policy.json

생성한 IAM 정책을 인스턴스에 연결한다

# AWSLoadBalancerControllerIAMPolicy
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy --role-name nodes.$KOPS_CLUSTER_NAME

# AllowExternalDNSUpdates
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name masters.$KOPS_CLUSTER_NAME
aws iam attach-role-policy --policy-arn arn:aws:iam::$ACCOUNT_ID:policy/AllowExternalDNSUpdates --role-name nodes.$KOPS_CLUSTER_NAME

kops로 클러스터를 수정하여 로드밸런서와 External DNS를 허용한다

kops edit cluster
-----
spec:
  certManager:
    enabled: true
  awsLoadBalancerController:
    enabled: true
  externalDns:
    provider: external-dns
-----

모든 수정이 끝난 뒤 클러스터를 업데이트한다

kops update cluster --yes && echo && sleep 3 &&kops rolling-update cluster

로드밸런서를 포함하여 세팅이 완료되었으니 예제용 게임을 배포해보자

apiVersion: v1
kind: Namespace
metadata:
  name: game-2048
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: game-2048
  name: deployment-2048
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: app-2048
  replicas: 2
  template:
    metadata:
      labels:
        app.kubernetes.io/name: app-2048
    spec:
      containers:
      - image: public.ecr.aws/l6m2t8p7/docker-2048:latest
        imagePullPolicy: Always
        name: app-2048
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  namespace: game-2048
  name: service-2048
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  type: NodePort
  selector:
    app.kubernetes.io/name: app-2048
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: game-2048
  name: ingress-2048
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  ingressClassName: alb
  rules:
    - http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: service-2048
              port:
                number: 80
kubectl apply -f ~/pkos/3/ingress1.yaml

kubectl get all -n game-2048
>>
# Deployment로 생성된 Pod
NAME                                   READY   STATUS    RESTARTS**  
pod/deployment-2048-6bc9fd6bf5-2mppd   1/1     Running   0         
pod/deployment-2048-6bc9fd6bf5-mhsvz   1/1     Running   0         

# Service(NodePort)
NAME                   TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)       
service/service-2048   NodePort   100.64.157.50   <none>        80:31958/TCP  

# Deployment
NAME                              READY   UP-TO-DATE   AVAILABLE
deployment.apps/deployment-2048   2/2     2            2         

# replicaset
NAME                                         DESIRED   CURRENT   READY
replicaset.apps/deployment-2048-6bc9fd6bf5   2         2         2     

# kubectl get-all -n game-2048
NAME                                                               NAMESPACE
configmap/kube-root-ca.crt                                         game-2048 
endpoints/service-2048                                             game-2048 
pod/deployment-2048-6bc9fd6bf5-2mppd                               game-2048 
pod/deployment-2048-6bc9fd6bf5-mhsvz                               game-2048
serviceaccount/default                                             game-2048
service/service-2048                                               game-2048
deployment.apps/deployment-2048                                    game-2048
replicaset.apps/deployment-2048-6bc9fd6bf5                         game-2048
endpointslice.discovery.k8s.io/service-2048-2jlrf                  game-2048
targetgroupbinding.elbv2.k8s.aws/k8s-game2048-service2-bd3585d62f  game-2048
ingress.networking.k8s.io/ingress-2048                             game-2048 

AWS 로드밸런서에 인그레스 형태로 게임이 배포되는 것을 확인할 수 있다.

게임을 실행하는 주소는 로드밸런서의 DNS 주소이다. 콘솔에서 찾을 수도 있고 명령어로 구할 수도 있다

kubectl get ingress -n game-2048 ingress-2048 -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "http://"$1 }'

실행 중인 파드의 주소와 콘솔 상에서 로드밸런서의 대상 그룹의 아이피를 비교해보자

k get pod -owide -n game-2048
>>
NAME                               READY   IP              NODE                 
deployment-2048-6bc9fd6bf5-trzhg   1/1     172.30.89.8     i-07e4645ded83d8038   
deployment-2048-6bc9fd6bf5-x82rr   1/1     172.30.40.104   i-0af2bba53c6a21f55   

같다!

External DNS로도 배포해보자

# 도메인을 변수로 지정한다
WEBDOMAIN=2048.play-board.net

# 정확히 어떤 의미의 명령어인지 찾아봐야겠다
WEBDOMAIN=$WEBDOMAIN envsubst < ~/pkos/3/ingress2.yaml | kubectl apply -f -

# DNS 서버에 등록이 되었는지 확인한다. 등록이 완료될 때까지 조금 걸린다
dig +short $WEBDOMAIN
dig +short $WEBDOMAIN @8.8.8.8

# 삭제
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

정상적으로 처리된 것을 확인한다.

PKOS-3주차) Instance Store 

Amazon EC2 인스턴스 스토어란?

AWS의 EC2에 사용할 수 있는 스토리지는 크게 4가지다.

  1. S3 객체 스토리지
  2. EFS(Elastic File System)
  3. EBS(Elastic Block Storage)
  4. EC2 Instance Store

이 중 인스턴스 스토어에 대해 간단히 알아보자

ephmeral은 우리말로 ‘일시적인’ 이라는 뜻이다.

이 중 EC2 인스턴스 스토어는 EC2에 직접 연결되는 블록 디바이스 스토리지인데,
EC2 인스턴스에 바인딩된 임시 스토리지로 인스턴스가 죽으면 같이 죽는다.
별도로 분리해서 다른 인스턴스에 바인딩할 수 있는 영구 스토리지인 EBS와 다르다.
인스턴스 스토어는 나중에 따로 추가할 수 없으니 인스턴스 생성할 때 같이 생성해야 함
인스턴스 스토어는 버퍼, 캐시나 기타 임시 데이터를 보관하는데 적절하다.

💡 인스턴스 스토어는 인스턴스가 죽을 때 같이 죽는 임시 스토리지

인스턴스 스토어와 EBS는 어떤 차이가 있습니까?

인스턴스 스토어의 세부 내용 확인

클러스터를 구성하는 노드 인스턴스 타입별 Total size를 확인해보자

# 인스턴스 스토어 볼륨이 있는 c5 인스턴스 타입의 크기(GB)를 확인한다
aws ec2 describe-instance-types \\
 --filters "Name=instance-type,Values=**c5***" "Name=instance-storage-supported,Values=true" \\
 --query "InstanceTypes[].[InstanceType, InstanceStorageInfo.TotalSizeInGB]" \\
 --output table

>>
--------------------------
|  DescribeInstanceTypes |
+---------------+--------+
|  c5d.large    |  50    |
|  c5d.12xlarge |  1800  |
|  c5d.4xlarge  |  400   |
|  c5d.24xlarge |  3600  |
|  c5d.2xlarge  |  200   |
|  c5d.18xlarge |  1800  |
|  c5d.metal    |  3600  |
|  c5d.xlarge   |  100   |
|  c5d.9xlarge  |  900   |
+---------------+--------+

현재 클러스터 인스턴스 타입인 c5d.large의 경우 50기가인 것을 확인할 수 있다.

현재 인스턴스 스토어의 볼륨을 확인해보자

# 현재 사용중인 인스턴스의 IP주소를 테이블 형태로 반환하는 명령어
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value}" --filters Name=instance-state-name,Values=running --output table

# 워커 노드의 IP를 변수로 선언하고 etc/profile 디렉토리에 워커 노드 IP 주소를 선언
# etc/profile : 시스템 전역(모든 사용자)에 대한 환경설정 파일. 로그인시 설정 내용을 읽어들임
W1PIP=52.79.159.145
W2PIP=54.180.153.219
echo "export W1PIP=52.79.159.145" >> /etc/profile
echo "export W2PIP=54.180.153.219" >> /etc/profile

워커 노드의 블록 스토리지 정보를 확인해보자

# lsblk(list block) 블록 스토리지 정보를 확인하는 명령어
# -e 7 : loop 디바이스를 제외한 내용을 출력하라는 옵션
# -d : 딸려있는 디바이스를 제외하고 출력하라는 옵션

ssh -i ~/.ssh/id_rsa ubuntu@**$W1PIP lsblk -e 7 -d
>>
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
nvme1n1 259:0    0 46.6G  0 disk
nvme0n1 259:1    0  128G  0 disk

ssh -i ~/.ssh/id_rsa ubuntu@**$W2PIP lsblk -e 7 -d
>>
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
nvme0n1 259:0    0  128G  0 disk
nvme1n1 259:1    0 46.6G  0 disk

워커 노드의 디스크 상태를 확인해보자

# df(dist free) 디스크의 남아있는 공간을 확인하는 명령어
# -h : 사람이 읽을 수 있게 단위 바꿔주는 옵션
# -T : 파일 시스템 타입을 출력하는 옵션
# -t <타입> : 입력한 타입의 파일 시스템만 출력하는 옵션 
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP df -hT -t ext4
>>
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/root      ext4  124G  3.6G  121G   3% /

ssh -i ~/.ssh/id_rsa ubuntu@$W2PIP df -hT -t ext4
>>
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/root      ext4  124G  3.5G  121G   3% /

파일 시스템 생성 및 마운트 해보기

워커 노드에 xfs 파일 시스템을 만들고 data 디렉토리를 생성한 뒤 그 둘을 마운트해보자

# xfs 타입의 파일시스템을 생성한다
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP sudo mkfs -t xfs /dev/nvme1n1

# 워커 노드의 루트 디렉토리 아래 /data 디렉토리를 생성한다
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP sudo mkdir /data

# 파일시스템과 data 디렉토리를 마운트한다
ssh -i ~/.ssh/id_rsa ubuntu@**$W1PIP sudo mount /dev/nvme1n1 /data

# 생성된 파일 시스템을 확인한다
ssh -i ~/.ssh/id_rsa ubuntu@**$W1PIP df -hT -t ext4 -t xfs
>>
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/root      ext4  124G  3.6G  121G   3% /
/dev/nvme1n1   xfs    47G  365M   47G   1% /data

#워커 노드 2도 똑같이 진행한다

재부팅 후에도 연결된 볼륨을 자동으로 탑재해보자

우선 디바이스 UUID를 확인한다

ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP sudo blkid
>>
/dev/nvme0n1p1: LABEL="cloudimg-rootfs" UUID="f3b90bbe-f515-4471-9805-3eab4c179492" TYPE="ext4" PARTUUID="b6f09fc9-a0ef-4282-a149-e9c131355c9f"
/dev/nvme0n1p15: LABEL_FATBOOT="UEFI" LABEL="UEFI" UUID="1AE8-7A32" TYPE="vfat" PARTUUID="5161b7d2-d6ef-425d-94ec-0a4263d253d3"
...
/dev/nvme1n1: UUID="842b79eb-0474-4670-8c66-58b0d0b179c4" TYPE="xfs"
/dev/nvme0n1p14: PARTUUID="69929353-ebd8-453a-94b1-9f947686278e"

fstab 디렉토리를 수정한다

# 0을 지정하여 파일 시스템이 덤프되지 않도록 하고 2를 지정하여 루트 디바이스가 아님을 나타낸다
ssh -i ~/.ssh/id_rsa ubuntu$W1PIP sudo vi /etc/fstab
수정>>
UUID=842b79eb-0474-4670-8c66-58b0d0b179c4  /data  xfs  defaults,nofail  0  2

마운트 해제 후 다시 마운트하여 오류가 발생하지 않는 경우 정상적으로 fstab 등록이 완료된 것이다

ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP sudo umount /data
ssh -i ~/.ssh/id_rsa ubuntu@$W1PIP sudo mount -a

PKOS-2주차) External DNS

ExternalDNS

ExternalDNS란?

Public한 도메인서버(AWS Route53, GCP DNS 등)를 사용하여 쿠버네티스의 리소스를 쿼리할 수 있게 해주는 오픈소스 솔루션

ExternalDNS 설치하기

기본 DNS 서버 수량을 조정한다

kubectl delete deploy -n kube-system coredns-autoscaler
kubectl scale deploy -n kube-system coredns --replicas 1

관련 IAM 정책을 생성한 뒤 마스터/워커 노드에 바인딩한다

curl -s -O <https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/AKOS/externaldns/externaldns-aws-r53-policy.json>
aws iam create-policy --policy-name AllowExternalDNSUpdates --policy-document file://externaldns-aws-r53-policy.json

export POLICY_ARN=$(aws iam list-policies --query 'Policies[?PolicyName==`AllowExternalDNSUpdates`].Arn' --output text)

EC2에 정책을 바인딩한다

aws iam attach-role-policy --policy-arn $POLICY_ARN --role-name masters.$**KOPS_CLUSTER_NAME**
aws iam attach-role-policy --policy-arn $POLICY_ARN --role-name nodes.$**KOPS_CLUSTER_NAME**

프로그램 설치(kops edit cluster) : 아래 내용을 추가한다

spec:
  externalDns:
    provider: external-dns

서버 재설정 및 업데이트 시작

kops update cluster --yes && echo && sleep 5 && kops rolling-update cluster

ExternalDNS 설정하기

# 테스트용 파드를 생성한다
kubectl apply -f ~/pkos/2/echo-service-nlb.yaml

도메인 정보 입력

# 변수로 도메인 이름을 입력한다
MyDOMAIN1=nginx.play-board.net
# 서비스 Annotation에 도메인이 추가된 것을 확인한다
kubectl annotate service svc-nlb-ip-type "external-dns.alpha.kubernetes.io/hostname=$MyDOMAIN1."

분산 접속을 확인한다

for i in {1..100}; do curl -s $MyDOMAIN1 | grep Hostname ; done | sort | uniq -c | sort -nr

>> for i in {1..100}; do curl -s $MyDOMAIN1 | grep Hostname ; done | sort | uniq -c | sort -nr
     50 Hostname: deploy-echo-5c4856dfd6-zvcnf
     50 Hostname: deploy-echo-5c4856dfd6-cqh74