[우아한테크코스] 10월 7일 TIL

8 minute read

[infra] 쿠버네티스 설치하기

  • kops를 사용한 aws에 쿠버네티스 설치하기

kubeadm에 비해 서버 인스턴스와 네트워크 리소스를 클라우드에서 자동으로 생성해 설치해준다.

Kops 실행 바이너리 다운로드

curl -Lo kops https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
chmod +x kops
sudo mv kops /usr/local/bin/kops

kubectl 실행 바이너리 다운로드

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/

iam 역할 부여 (kube-role)

sudo apt update
sudo apt install awscli
aws configure
...
Default region name [None]: ap-northeast-2
...

aws ec2 describe-instances

쿠버네티스를 설치한 ec2 인스턴스에 배포할 ssh 키 생성

ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa

kops 재인증

kops export kubecfg --admin 

클러스터 생성

클러스터의 설정 정보 저장

export BUCKET_NAME=zzimkkong-kube
export NAME=2021-zzimkkong.k8s.local
export KOPS_STATE_STORE=s3://$BUCKET_NAME

aws s3api create-bucket --bucket $BUCKET_NAME --create-bucket-configuration LocationConstraint=ap-northeast-2
aws s3api put-bucket-versioning --bucket $BUCKET_NAME --versioning-configuration Status=Enabled

kops create cluster --zones ap-northeast-2a --topology=public --ssh-access=172.16.0.0/16,192.168.0.0/24,192.168.1.0/24,192.168.2.0/24,192.168.3.0/24 --network-cidr=192.168.1.0/24 --networking calico --ssh-public-key ~/.ssh/id_rsa.pub $NAME

kops create cluster --zones ap-northeast-2c --topology=public --ssh-access=172.16.0.0/16,192.168.0.0/24,192.168.1.0/24,192.168.2.0/24,192.168.3.0/24 --network-cidr=192.168.1.0/24 --networking calico --ssh-public-key ~/.ssh/id_rsa.pub $NAME

특정 vpc에 만들기 -> edit으로 수정할것!!

// yaml 파일 생성
kops get clusters ${CLUSTER_NAME} -o yaml > cluster-config.yaml
// yaml 파일 수정
vi cluster-config.yaml
// yaml 교체
kops replace -f cluster.yaml

클러스터 수정 (노드 갯수, 인스턴스 타입 등)

kops get cluster
kops edit cluster $NAME
# spec.networkID: vpc-0ec31c5f77f2af273
# spec.additionalNetworkCIDRs:   
	- 192.168.2.0/24
  - 192.168.3.0/24
# spec.subnets.cidr: 서브넷 cidr
# spec.subnets.id: 서브넷 id

kops edit ig --name=$NAME nodes-ap-northeast-2a
kops edit ig --name=$NAME master-ap-northeast-2a

kops update cluster --name $NAME --yes --admin

클러스터 생성 확인

//설치 진행상황 확인
kops validate cluster --wait 10m

kubectl cluster-info
kubectl get nodes --show-labels

kops 재인증

kops export kubecfg --admin 
// 오랜만에 재접속을 해보면 세션이 끊겨 아래와 같은 에러가 발생할 수 있는데요. 그럴 때는 위의 명령어를 실행해주면 됩니다.
// error: You must be logged in to the server (Unauthorized)

클러스터 삭제

kops delete cluster $NAME --yes

Required value: Please set the –state flag or export KOPS_STATE_STORE 오류

export KOPS_STATE_STORE=s3://yujo-apple-clone

pod 생성

yaml 파일 작성

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod
spec:
	containers:
	- name: my-app-container	//이 name 여러개 주면 container 여러개 띄우기
	- image: nginx:test 
		ports:
		- containerPort: 80
			protocol: TCP

생성 명령어

kubectl apply -f my-app-pod.yaml

생성된 pod들 확인

kubectl get pods

//주소 확인
kubectl get pods -o wide

//포드에 변화 생기면 콘솔 출력
kubectl get pods -w

//포드 로그 출력
kubectl logs ${POD_NAME}

세부사항 확인(ip)

kubectl describe pods my-app-pod

pod 실행

kubectl exec -it my-app-pod bash

//로그 확인
kubectl logs my-app-pod

//pod ip로 curl 명령 보내기
kubectl run -i --tty --rm debug --image=alicek106/ubuntu:curl --restart=Never curl 100.119.134.70 | grep Hello

오브젝트 삭제

kubectl delete -f my-app-pod.yaml
kubectl delete pod my-app-pod

모든 오브젝트 삭제

kubectl delete deployment,pod,rs --all

replicatset 생성

yaml 파일 작성

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-application
spec:
	//레플리카셋 정의
	replicas: 3			//동일한 포드 유지할 개수
	selector:
		matchLabels:
			app: my-app-pods-label			//key: value로 정의
	//포드 정의
  template:
  	metadata:
  		name: my-app-pod
  		labels:
  			app: my-app-pods-label
		spec:
      containers:
      - name: my-app-container
      - image: nginx:test 
        ports:
        - containerPort: 8080
          protocol: TCP

생성할 때: pod와 같은 생성 명령어

생성 확인 명령어 (–show-labels 옵션으로 라벨까지 확인 가능, -l app으로 필터링도 가능)

kubectl get rs(replicasets)

수정할 때: (pod 개수 늘리고 싶을 때) 그냥 yaml 수정하고 apply 다시 하면 됨

삭제할 때: pod와 같은 삭제 명령어

deployment 생성

Yaml 파일 작성

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      name: my-webserver
      labels:
        app: webserver
    spec:
      containers:
      - name: my-webserver
        image: alicek106/rr-test:echo-hostname
        ports:
        - containerPort: 80

생성할 때: pod와 같은 생성 명령어

생성 확인 명령어 (–show-labels 옵션으로 라벨까지 확인 가능, -l app으로 필터링도 가능)

kubectl get deployment(deploy)

수정할 때: (pod 개수 늘리고 싶을 때) 그냥 yaml 수정하고 apply 다시 하거나, kubectl edit deploy my-app

삭제할 때: pod와 같은 삭제 명령어

포드 개수 줄일때

kubectl scale --replicas=1 deployment ${DEPLOY_NAME}

버전 저장

kubectl apply -f deployment-nginx.yaml --record
  • replicaset 내의 pod에 변화가 생기면 이전 replicaset을 0 으로 줄이고 새로운 replicaset을 생성한다. 이는 replicaset 조회 시 deployment의 이름 뒤 해시값을 통해 알 수 있다.

  • 버전 조회

    //명령어
    kubectl rollout history deployment ${DEPLOYMENT_NAME}
      
    //결과
    deployment.apps/my-nginx-deployment
    REVISION  CHANGE-CAUSE
    1         kubectl apply --filename=deployment-nginx.yaml --record=true
    2         kubectl set image deployment my-nginx-deployment nginx=nginx:1.11 --record=true
    
  • 버전 롤백

    kubectl rollout undo deployment ${DEPLOYMENT_NAME} --to-revision=${VERSION}
    
  • 지금 돌아가고 있는 정보 조회

    kubectl describe deploy ${DEPLOYMENT_NAME}
    

기타 deploy 관련 명령어

## 업데이트
kubectl set image deployment my-app subway=[이미지]:[새로운버전] --record

## 배포 상태 확인
kubectl rollout status deployment my-app

## 이미지 태그 확인
kubectl get deployments my-app -oyaml | grep image

## 배포 히스토리 확인
kubectl rollout history deployment my-app

## 롤백
kubectl rollout undo deployment my-app
kubectl rollout undo deployment my-app --to-revision=1

## Scale out
kubectl scale deployment my-app --replicas=5

service 생성

yaml 파일 생성

apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-loadbalancer
  annotations:
  	service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
  externalTrafficPolicy: Local 	//처음 받은 노드에서 포드 실행
  ports:
    - name: web-port
      port: 8080
      targetPort: 80	// 고유한 ip 할당받아 사용할 포트 지정
      nodePort: 31000	// inbound 규칙 허용된 접속 가능한 포트 지정
  selector:
    app: webserver	// 라벨 지정
  type: loadbalancer		// 서비스 타입 지정

생성 확인 명령어(기본 kubernetes 존재)

kubectl get service(svc)

//yaml 파일 조회
kubectl get svc ${SERVICE_NAME} -o yaml

외부에서 요청 보내기

curl a01235b4b5b0e4166af809e977b0678c-1789225630.ap-northeast-2.elb.amazonaws.com --silent | grep jellllo

Ingress 생성

Yaml 파일 생성

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /	# [4]
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: alicek106.example.com                   # [1]
    http:
      paths:
      - path: /echo-hostname                     # [2]
        backend:
          serviceName: hostname-service          # [3]
          servicePort: 80
  • [1] 이 도메인으로 접근하는 요청에 대한 규칙 적용, (/|$)(.*) 는 .*를 통해 경로 전달
  • [2] 이 경로에 들어온 요청을 어느 서비스에 전달할지, /echo-hostname이라는 경로의 요청을 backend에 정의된 서비스에 전달
  • [3] path로 들어온 요청이 어느 서비스와 포트에 전달할지 지정
  • [4] host-servicedml /로 전달, /$2 로 하면 path 에서 획득한 경로 전달

1.9 version

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: zzimkkong-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: zzimkkong.kro.kr
    http:
      paths:
      - pathType: Prefix
        path: /
        backend:
          service:
            name: zzimkkong-lb
            port:
              number: 8080

생성확인 명령어

kubectl get ingress(ing)

Nginx 인그레스 컨트롤러 설치 - helm을 이용

# helm 설치
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash -s -- --version v3.2.2
helm repo add stable https://charts.helm.sh/stable/
helm repo update
helm repo list
helm search repo stable

# nginx 설치
kubectl create ns ctrl
helm install nginx-ingress stable/nginx-ingress --version 1.40.3 -n ctrl
kubectl get pod -n ctrl
kubectl get svc -n ctrl

# aws 도메인 검색 -> DNS 사이트에 등록
kubectl get svc -nctrl nginx-ingress-controller -ojsonpath="{.status.loadBalancer.ingress[0]}"
//모든 오브젝트 조회
kubectl get ing,svc --all-namespaces

nginx-ingress-controller가 연결한 서비스에 연결해줌, 이거는 플랫폼에서 적용해주는것
그거의 external-ip로 접근하면 됨

Nginx 인그레스 컨트롤러 설치 - github 이용

https://kubernetes.github.io/ingress-nginx/deploy 참고

kubectl apply -f https://raw.githubusercontent.com/ingress-nginx/controller-v0.35.0/deploy/static/provider/aws/deploy.yaml

TLS 설정

cert-manager 설치

kubectl create ns cert-manager

## cert-manager 관련 사용자 정의 리소스 생성
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.15.1/cert-manager.crds.yaml

## jetstack repo 추가
helm repo add jetstack https://charts.jetstack.io

## cert-manager 설치
helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v0.15.1

Cert-manager yaml 파일 생성

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata: 
    name: http-issuer

spec: 
    # 자동으로 인증서의 생성 및 연장을 관리해주는 타입
    acme:
        email: [이메일주소]
        
        # 어떤 acme 서버(인증서 발급 CA)를 사용할지 지정
        server: https://acme-v02.api.letsencrypt.org/directory

        # 사용자의 개인키를 저장할 Secret 리소스 이름을 지정
        privateKeySecretRef:
            name: issuer-key

        # 도메인 주소에 대한 소유권을 증명하기 위한 방법 선택. http 요청(http01)과 DNS Lookup(dns01) 방법이 있음            
        solvers:
        - http01:
            # Ingress 컨트롤러로 NGINX를 사용
            ingress: 
                class: nginx

cert-manager 실행

kubectl apply -f cluterissuer.yaml
kubectl get clusterissuer

인증서 ingress에 적용

apiVersion: extensions/v1beta1
kind: Ingress
metadata: 
    annotations:
        kubernetes.io/ingress.class: nginx
        # 앞서 생성한 발급자 지정
        cert-manager.io/cluster-issuer: http-issuer
    name: nginx-tls

spec: 
    rules: 
    - host: [외부에서 접근할 도메인] #이건 왠지 모르겠지만 주면 안된다!
      http:
        paths: 
        - path: /
          backend:        
            serviceName: subway-service
            servicePort: 80

    tls:
    - hosts:
      - [외부에서 접근할 도메인]
      secretName: nginx-tls
  • spec.tls.hosts.secretName

    Secret 리소스를 생성하지 않아도 cert-manager에서 자동으로 인증서를 발급받아서 Secret을 생성한다. 사용자는 생성될 Secret의 이름만 지정하면 된다.

인증서 적용 확인

watch kubectl get certificate
//True 되면 완성

scp -i ~/Downloads/KEY-zzimkkong.pem ~/Downloads/backend-0.0.1-SNAPSHOT.jar ubuntu@3.38.74.217:/home/ubuntu/

리소스 고가용성 확립

  • 사용량 확인
# 설치
helm install metrics-server stable/metrics-server --version 2.11.1 --namespace ctrl
# 설치 확인 (Running)
watch kubectl get pod -nctrl
# pod 사용량 확인
kubectl top pod
# node 사용량 확인
kubectl top node
  • Horizontal Pod Autoscaler 적용

    cpu 사용량에 따라 replicationController, replicaSet, deploy, statefulset의 파드 개수를 자동으로 관리

    메트릭이 사용자가 설정한 값과 일치하도록 컨트롤러 관리자의 --horizontal-pod-autoscaler-sync-period 플래그(기본값은 15초)에 의해 제어되는 주기를 가진 컨트롤 루프로 구현됨

    원하는 레플리카 수 = ceil[현재 레플리카 수 * ( 현재 메트릭 값 / 원하는 메트릭 값 )] 을 지키게 됨

  • yaml 파일 생성

    apiVersion: autoscaling/v1
    kind: HorizontalPodAutoscaler
    metadata:
      name: my-app
    spec:
      maxReplicas: 100
      minReplicas: 1
      scaleTargetRef:
        apiVersion: apps/v1
        kind: Deployment
        name: my-app
      targetCPUUtilizationPercentage: 50
    
  • 확인

    kubectl get hpa
    

EFK 적용하기

efk는 로그를 수집해서 유의미한 지표로 만들기 위함

# 아래 파일에 로그 저장됨
/var/lib/docker/containers/<CONTAINER_ID>/<CONTAINER_ID>-json.log

설치하기

helm fetch --untar stable/elastic-stack --version 2.0.1

$ vi elastic-stack/values.yaml
// 12 line
logstash:
  enabled: false  # 기존 true
// 29 line
fluent-bit:
  enabled: true   # 기존 false

$ vi elastic-stack/charts/fluent-bit/values.yaml
// 45 line
backend:
  type: es    # 기존 forward
  es:
    host: efk-elasticsearch-client   # 기존 elasticsearch 
// 226 line
input:
  tail:
    memBufLimit: 5MB
    parser: docker
    path: /var/log/containers/*.log
    ignore_older: ""
  systemd:
    enabled: true   # 기존 false
    filters:
      systemdUnit:
        - docker.service
        - kubelet.service
        # - node-problem-detector.service  --> 주석처리

$ vi elastic-stack/charts/kibana/values.yaml
// 79 line
ingress:
  enabled: true   # 기존 false
  hosts:
  - kibana.techcourse.kro.kr   # DNS 설정
  annotations:
    kubernetes.io/ingress.class: nginx

$ helm install efk ./elastic-stack
# NAME: efk
# LAST DEPLOYED: Sat Apr 17 20:15:13 2021
# NAMESPACE: default
# STATUS: deployed
# REVISION: 1
# NOTES:
# The elasticsearch cluster and associated extras have been installed.
# Kibana can be accessed:
# ...
# pod, svc, ingress가 정상적으로 실행되는지 확인
watch kubectl get pod,svc,ingress

  • Helm 이란?

    쿠버네티스 package managing tool로 npm과 유사하다.

    package format인 chart를 사용하는데 쿠버네티스 리소스를 describe 하는 파일들의 집합이다.

  • taints 문제 해결

    kubectl taint nodes --all node-role.kubernetes.io/master-

    https://github.com/calebhailey/homelab/issues/3

모니터링 도구 - 프로메테우스 적용

그라파나는 프로메테우스가 수집해온 정보를 시각화해줌(cpu 사용량, pod 활성화, node 메모리 사용량과 같은 하드웨어 쪽)

직접 메트릭을 수집하는 pull 방식

  • exporter: 실제 메트릭을 수집하는 수집기, 프로메테우스에 전달하는 역할
  • Prometheus: 메트릭을 수집하는 주체로 데이터 저장소, 쿼리엔진의 역할
  • grafana: 시각화 툴
  • Alert-manager: 알람 발생

image

템플릿 사용

  1. helm 설치

    helm fetch --untar stable/prometheus-operator --version 8.16.1

    helm install mon ./prometheus-operator

    watch kubectl --namespace default get pods -l "release=mon"

  2. yaml 파일 수정

vi prometheus-operator/values.yaml

// 495 line
grafana:

ingress:
enabled: true   # 기존 false
annotations:
kubernetes.io/ingress.class: nginx   # 추가
cert-manager.io/cluster-issuer: http-issuer
hosts:
- grafana.zzimkkong-k8s.kro.kr            # DNS 설정

tls:
- secretName: mon-grafana
hosts:
- grafana.zzimkkong-k8s.kro.kr

[infra] mysql docker에 띄우기

version: '3'
services:
  zzimkkong-db:
    image: library/mysql:5.7
    container_name: zzimkkong-db
    restart: always
    ports:
      - 13306:3306
    environment:
      MYSQL_ROOT_PASSWORD: 1234
      TZ: Asia/Seoul
    volumes:
      - ./zzimkkong-db/conf.d:/etc/mysql/conf.d    
      - ./zzimkkong-db/data:/var/lib/mysql
      - ./zzimkkong-db/init:/docker-entrypoint-initdb.d
    platform: linux/x86_64