kubectl 커맨드의 2가지 형식을 이해하고 실습해보기

Bakumando·2022년 5월 21일
0

Kubernetes

목록 보기
4/17
post-thumbnail

들어가기에 앞서...


0. 블로깅 목적

  • kubectl 커맨드의 2가지 형식을 이해한다.
  • 실습을 통해 각 kubectl 커맨드 방식의 차이와 활용법을 이해한다.

1. kubectl 커맨드의 2가지 형식을 이해한다.

  • 2편에서 kubectl 커맨드를 쭉 살펴보긴 했지만, 커맨드 형식에 대해선 언급하지 않았기 때문에, 이 부분도 짚고 넘어갈 필요가 있다고 생각하여 추가하였다.
  • kubectl 커맨드에는 명령형과 선언형이 있다.
  • 2가지가 장단이 있지만 선언형을 주로 활용하는 것이 추천된다.
  • 하지만 무조건 선언형만 쓰라는 얘기는 아니다. 선언형으로 불가능한 커맨드도 있다. 그런 것은 명령형으로 수행해야 한다.
    • 보통 CRUD 명령은 선언형 방식으로 수행하는 게 효율적이고, 그 외에 ssh 접속, 트러블슈팅과 관련된 명령어(top, log, attach, exec, port-foward, proxy) 들은 선언형으로 하는 것이 불가능하므로 명령형 커맨드를 사용해야 한다.

1) 명령형 vs 선언형

1. 명령형(Imperative)

  • 수행하고자 하는 액션을 지시한다.
  • 적은 리소스에 대해 빠르게 처리가 가능하다.
  • 알아야할 명령어가 상당히 많다.
  • 멱등성이 보장되지 않는다. (같은 명령어를 입력했을 때 다른 결과가 나올 수 있다는 뜻)
    • 가령 어떤 명령어로 오브젝트를 생성하였는데, 그대로 같은 명령어를 입력할 시 이미 생성된 오브젝트가 존재한다는 에러가 발생한다.
  • ex) 명령형 예시
$ kubectl create deployment grafana --image=grafana/grafana --port=3000
$ kubectl expose deployment grafana --type=NodePort --port=80 --target-port=3000

=> 직접적으로 명령어가 전달된다.

2. 선언형(Declarative)

  • 도달하고자 하는 상태(Desired state)를 선언한다.
  • yml를 활용하여 코드로 관리가 가능하다. (-> GitOps 활용)
    • 변경사항에 대한 체크 용이
    • 코드리뷰를 통한 협업 용이
  • 멱등성이 보장된다. (yml 파일을 기반으로 수행하기 때문에 설령 같은 이름의 오브젝트를 또 선언한다 할지라도 알아서 감지하여 에러를 뱉지 않고 같은 결과를 만들어낸다.)
  • 많은 오브젝트에 대해서도 yml 관리 방법에 따라 빠르게 처리 가능하다.
  • 알아야 할 명령어 수가 상대적으로 적다.
  • ex) 선언형 예시
$ kubectl apply -f deployment.yml

=> 컨트롤러가 yml 파일에 선언된 오브젝트를 확인한 뒤, api 서버를 통해 실제 클러스터 상태를 업데이트 해줌


2. 실습을 통해 각 kubectl 커맨드 방식의 차이와 활용법을 이해한다.

  • 참고로 .sh, .yml 파일을 만들어 각 명령형, 선언형에 맞는 커맨드를 미리 준비할 것이다.

1) kubectl 명령형 커맨드 실습

  • imperative.sh 파일을 다음과 같은 내용으로 채운다.
kubectl create deployment grafana --image=grafana/grafana --port=3000
kubectl expose deployment grafana --type=NodePort --port=80 --target-port=3000
minikube service grafana
  • cat imperative.sh를 입력하면 내용을 볼 수 있다.
    • create {리소스타입} {오브젝트명} --image={이미지명} --port={오브젝트 포트넘버} 는 쿠버네티스 오브젝트를 생성하는 커맨드이다.
      • 어떤 API 리소스(템플릿)를 바탕으로 오브젝트를 만들건지, 오브젝트 네이밍은 어떻게 할 것인지, 사용할 이미지는 무엇인지, 그리고 몇번 포트를 줄 것인지를 결정하게 된다.
    • expose {리소스타입} {오브젝트명} --type={서비스타입} --port={노드포트넘버} --target-port={오브젝트 포트넘버}
      • 생성된 오브젝트 그자체에 다이렉트로 접근은 불가능하므로, 서비스타입을 정해 포트를 새롭게 열고 오브젝트 포트를 대상으로 포트 바인딩하여 접근할 수 있게 하는 것이다. 바인딩 결과 클러스터 ip에 랜덤한 포트가 붙은 url이 생긴다. 즉, 이 url은 특정타입 서비스(여기선 Nodeport 타입 서비스)를 매핑하고 있는 상태가 된다. 이제 클러스터 내부에서 해당 url을 통해 오브젝트 ip에 접근이 가능해진다.
    • minikube service {리소스명}
      • 위에서 Nodeport 매핑 url을 expose하였으나, 그럼에도 클러스터 외부에서는 아직도 접근 불가능하다.(m1의 문제)
      • m1에선 클러스터 외부에서 접근하기 위해선 추가적으로 포트 포워딩이 필요하다. minikube service라는 명령어가 이를 간편하게 처리해준다. (단, 포트 포워딩 하는 port넘버는 랜덤으로 지정된다.)

  • ./imperative.sh 로 커맨드를 실행해보자
    • grafana라는 이름을 가진 deployment 리소스타입의 오브젝트가 create되고, 이를 바인딩하는 노드포트가 expose되었음을 알 수 있다.
    • 그 다음에 grafana 서비스 터널을 시작하는 중이라는 문구아래가 이제 minikube service grafana 커맨드가 실질적으로 수행한 결과이다. 192.168.49.2:30989(노드 IP + 노드포트)의 조합이다. 이를 포트 포워딩한 로컬 URL(127.0.0.1:51515_)이 생성되었음을 알 수 있다.
    • 참고로 TARGET PORT 80은 grafana가 구동되고 있는 Pod의 포트넘버를 뜻한다.

  • 새로운 터미널을 열어 상태를 확인해보자.
  • kubectl get all
    • 생성된 grafana 오브젝트와 이를 바인딩한 grafana 노드포트, 노드포트를 매핑하고 있는 포트 등을 확인할 수 있다.

  • curl http://127.0.0.1:51515
    • 포트포워딩된 url에 정상적으로 접근이 가능함을 알 수 있다.
    • 트래픽 흐름 순서 정리:
      1. 127.0.0.1:51515 (localhost)
      2. 192.168.49.2:30989 (NodePort)
      3. 10.96.254.142:80(ClusterIP)
        => grafana Pod
    • 3가지 경로를 거쳐 최종적으로 grafana Pod 주소에 도달하게 된다.
    • 이 흐름에 대해선 Service 리소스 실습 파트에서 다시 자세히 다룰 예정이다.

  • 명령형 커맨드로 삭제도 진행해보자
  • 그전에 get all 명령어로 봤던 오브젝트의 이름을 참고하여 아래와 같은 삭제 커맨드를 연달아 입력하자.
  • kubectl delete deployment.apps/grafana
  • kubectl delete service/grafana
    • 삭제가 잘 수행되었다.
    • kubectl get all 커맨드를 통해, 확실히 삭제가 완료되었다는 것도 확인된다.

2) kubectl 선언형 커맨드 실습

  • 우선 실습을 위해 아래 파일들을 준비해주자.

1번 파일 - deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
  labels:
    app: grafana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
        - name: grafana
          image: grafana/grafana:latest
          ports:
            - name: http
              containerPort: 3000

2번 파일 - service.yml

apiVersion: v1
kind: Service
metadata:
  name: grafana
  labels:
    app: grafana
spec:
  type: NodePort
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 3000
  selector:
    app: grafana

3번 파일 - declarative.sh

kubectl apply -f deployment.yml
kubectl apply -f service.yml
minikube service grafana

4번 파일 - clean.sh

kubectl delete -f deployment.yml
kubectl delete -f service.yml

  • 이제 실습에 들어가보자.
  • 먼저 선언형 커맨드가 담긴 파일이 잘 준비되었는지 확인해본다.
  • cat declarative.sh
    • 정상 출력된다.

  • 그럼 선언형 커맨드를 실행해보자!
  • ./declarative.sh
    • deployment와 service가 모두 정상적으로 create되었고, 포트 바인딩과 포트 매핑, 포트 포워딩까지 모두 문제 없이 이루어져 URL도 출력되었다.

  • curl http://127.0.0.1:53010
    • url에 대한 접근도 잘 이루어진다.
    • 직접 접속도 잘되었다.

  • kubectl get all
    • 오브젝트 조회도 정상적으로 출력되었다.

  • 선언형 커맨드는 몇번을 다시 실행해도 문제가 없다.
  • ./declarative.sh
    • 이렇게 unchanged라는 결과만 보여줄 뿐 계속해서 커맨드가 전달이 된다.
    • 만약 오브젝트의 상태를 수정하고 싶다면 따로 수정 명령어를 배우는 게 아니라, 그냥 yml 파일을 수정한 다음에 다시 위와 같은 커맨드를 입력해주면 된다.
    • 가령 deployment.yml 파일의 replicas를 1 -> 2로 바꾸고 다시 실행을 해보자.
    • service는 변경되지 않았기 때문에 여전히 unchanged지만 deployment는 configured로 출력된 걸 볼 수 있다.
    • 그리고 오브젝트 조회를 해보면
    • pod, deployment, replicas가 전부 2개가 된 걸 확인할 수 있다.

  • ./clean.sh
    • 선언형 커맨드로 삭제도 손쉽게 완료되었다.
    • 오브젝트 조회 시에도 깨끗해진 걸 알 수 있다.

참고 1: 만약 .sh 파일을 실행에 문제가 있다면?

  • 권한 오류로 permission denied가 발생할 수 있다.
  • 그럴 땐 권한 부여가 필요하다.
  • 실행 권한을 줘야하기 때문에 chmod +x {파일명} 을 입력해 주면 된다.

참고 2: Service 타입의 종류는?

  • ClusterIP: 클러스터 내부 통신용이다.
  • NodePort: 클러스터 내부 / 외부 통신용, 노드의 포트를 사용한다. (30000-32767)
  • LoadBalancer: 클라우드 프로바이더를 사용하는 경우 클라우드 로드밸런서를 사용하여 외부로 노출시킨다.
  • ExternalName: 학습이 필요하다.
profile
그렇게 바쿠만도는 개발에 퐁당 빠지고 말았답니다.

0개의 댓글