- 운영체제는 MacOs m1을 활용한다.
- 쿠버네티스 배포판으로는 minikube를 활용하여 로컬 환경에서 실습을 진행할 예정이다. 이에 대한 세팅은 완료되었다고 가정한다.
- 만약 세팅이 되어있지 않다면 이전 시리즈의 kubernetes 개발 환경 세팅 실습을 참고한다.
- 참고로 본 쿠버네티스 실습 시리즈는 아래 학습 자료를 참고한다.
- kubectl 커맨드의 2가지 형식을 이해한다.
- 실습을 통해 각 kubectl 커맨드 방식의 차이와 활용법을 이해한다.
- 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 서버를 통해 실제 클러스터 상태를 업데이트 해줌
- 참고로 .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에 정상적으로 접근이 가능함을 알 수 있다.
- 트래픽 흐름 순서 정리:
- 127.0.0.1:51515 (localhost)
- 192.168.49.2:30989 (NodePort)
- 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: 학습이 필요하다.