티스토리 뷰

이전까지 쿠버네티스에서 컨테이너를 구성하는 가장 중요한 요소인 파드, 레플리카셋, 그리고 디플로이먼트에 대해서 알아봤다. 그러나 디플로이먼트를 통해 생성된 파드에 어떻게 접근할 수 있을지에 대해서는 아직 알아보지 않았다. 이전에는 kubectl describe 명령어로 파드의 내부 IP를 직접 확인한 뒤 파드로 직접 접근할 수는 있었지만, 이 방법은 로컬 개발 환경 또는 쿠버네티스 클러스터 내부에서만 사용할 수 있었다.

게다가 도커 컨테이너와 마찬가지로 파드의 IP는 영속적이지 않아 항상 변할 수 있다는 점도 유의해야 한다. 여러 개의 디플로이먼트를 하나의 완벽한 애플리케이션으로 연동하려면 파드 IP가 아닌, 서로를 발견(Discovery)할 수 있는 다른 방법이 필요하다.

도커 컨테이너는 -p 옵션으로 손쉽게 컨테이너를 외부로 노출할 수 있다. 즉, 컨테이너가 생성됨과 동시에 외부로 노출되는 방식이다. 또한 오버레이 네트워크나 도커 사용자 정의 네트워크, docker run --link 옵션으로 컨테이너들이 서로를 이름으로 접근할 수도 있다.

docker run -d --name myapp -p 80:80 nginx:latest
docker run -itd --name myapp2 --link myapp:nginx ubuntu:16.04

docker network create mynetwork
docker run mycontainer --network mynetwork --net-alias mycon ubuntu:16.04

그러나 쿠버네티스에서는 파드에 접근하도록 정의하는 방법이 도커와 약간 다르다.

쿠버네티스는 docker run -p 명령어와는 달리 디플로이먼트를 생성할 때 파드를 외부로 노출하지 않으며, 디플로이먼트의 YAML 파일에는 단지 파드의 애플리케이션이 사용할 내부 포트만 정의한다. 이전의 Nginx 디플로이먼트를 생성했을 때 사용했던 YAML 파일 중에서 containerPort 항목이 바로 내부 포트이다. 

그러나 YAML 파일에서 containerPort 항목을 정의했다고 해서 이 파드가 바로 외부로 노출되는 것은 아니다. 이 포트를 외부로 노출해 사용자들이 접근하거나, 다른 디플로이먼트의 파드들이 내부적으로 접근하려면 '서비스(service)'라고 부르는 별도의 쿠버네티스 오브젝트를 생성해야 한다. 서비스는 파드에 접근하기 위한 규칙을 정의하기 때문에 쿠버네티스에서 애플리케이션을 배포하기 위해서는 반드시 알아야 할 오브젝트이다. 서비스에는 다양한 기능이 있지만, 핵심 기능만 나열해 보면 아래와 같다.

  • 여러 개의 파드에 쉽게 접근할 수 있도록 고유한 도메인 이름을 부여한다.
  • 여러 개의 파드에 접근할 때, 요청을 분산하는 로드 밸런서 기능을 수행한다.
  • 클라우드 플랫폼의 로드 밸런서, 클러스터 노드의 포트 등을 통해 파드를 외부로 노출한다.
서비스(Service) 종류

서비스의 개념과 사용 방법을 익히기 위해 파드와 서비스를 연결해보도록 하자. 서비스를 생성하기 앞서 아래의 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

kubectl get pods -o wide 명령어를 이용해 파드의 IP를 확인한 뒤, curl 등과 같은 도구로 HTTP 요청을 보내 파드의 이름을 확인할 수 있다.

파드에 접근할 수 있는 규칙을 정의하는 서비스 리소스를 새롭게 생성해보겠다. 한 가지 알아둬야 할 점은 쿠버네티스의 서비스는 파드에 어떻게 접근할 것이냐에 따라 종류가 여러 개로 세분화되어 있다는 점이다. 따라서 목적에 맞는 서비스의 종류를 선택해야 한다.

서비스의 종류에 따라 파드에 접근할 수 있는 방법이 달라지기 때문에 서비스의 종류는 반드시 알아둬야 한다. 서비스는 여러 가지 종류가 있으나 주로 사용하는 서비스 타입은 크게 3가지이다.

  • ClusterIP 타입 : 쿠버네티스 내부에서만 파드들에 접근할 때 사용한다. 외부로 파드를 노출하지 않기 때문에 쿠버네티스 클러스터 내부에서만 사용되는 파드에 적합하다.
  • NodePort 타입 : 파드에 접근할 수 있는 포트를 클러스터의 모든 노드에 동일하게 개방한다. 따라서 외부에서 파드에 접근할 수 있는 서비스 타입이다. 접근할 수 있는 포트는 랜덤으로 정해지지만, 특정 포트로 접근하도록 설정할 수도 있다.
  • LoadBalancer 타입 : 클라우드 플랫폼에서 제공하는 로드 밸런서를 동적으로 프로비저닝해 포드에 연결한다. NodePort 타입과 마찬가지로 외부에서 파드에 접근할 수 있는 서비스 타입이다. 일반적으로 AWS, GCP 등과 같은 클라우드 플랫폼 환경에서만 사용할 수 있다.

예를 들어, 앞서 생성했던 파드를 내부에서만 접근하고 싶다면 ClusterIP 타입의 서비스를 사용할 수 있을 것이고 외부에서도 파드에 접근하고 싶다면 NodePort 타입을, 실제 운영 환경에서는 LoadBalancer 타입을 사용하면 된다.

ClusterIP 타입의 서비스 

지금 당장 모든 서비스의 종류와 특징을 세세하게 공부하는 것은 비효율적이다. 그래서 지금은 가장 간단하게 사용해 볼 수 있는 ClusterIP 타입의 서비스를 먼저 사용해 보겠다. 아래의 내용으로 새로운 yaml 파일을 작성해 보자.

apiVersion: v1
kind: Service
metadata:
  name: hostname-svc-clusterip
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: ClusterIP

특별한 것은 없지만, 한 가지 설정만 보고 넘어가자.

  • spec.selector : selector 항목은 이 서비스에서 어떠한 라벨을 가지는 파드에 접근할 수 있게 만들 것인지 결정한다. 

위 YAML 파일을 이용해 서비스를 생성해 보겠다. 이전에 리소스를 생성했던 방법과 동일하게 kubectl apply -f 명령어를 사용하면 된다.

생성된 서비스의 목록을 확인해 보면 위와 같다. 생성한 적이 없는 kubernetes라는 이름의 서비스가 생성돼 있는데, 이 서비스는 파드 내부에서 쿠버네티스 API에 접근하기 위한 서비스이므로 지금 당장을 알 필요가 없다.

hostname-sve-clusterip라는 이름의 서비스가 생성돼 있는 것을 볼 수 있다. 이 서비스를 사용해 파드에 접근하는 방법은 매우 간단하다. 출력 내용 중에서 CLUSTER-IP 항목의 IP와 PORT 항목의 포트를 통해 요청을 보내면 된다. 이 IP는 쿠버네티스 클러스터에서만 사용할 수 있는 내부 IP로, 이 IP를 통해 서비스에 연결된 파드에 접근할 수 있다.

또한 서비스에는 IP 뿐만 아니라 서비스 이름 그 자체로도 접근할  수 있다. 쿠버네티스는 애플리케이션이 서비스나 파드를 쉽게 찾을 수 있도록 내부 DNS를 구동하고 있으며 파드들은 자동으로 이 DNS를 사용하도록 설정되기 때문이다. 실제로 여러 파드가 클러스터 내부에서 서로를 찾아 연결해야 할 때는 서비스의 이름과 같은 도메인 이름을 사용하는 것이 일반적이다. 즉, 파드가 서로 상호작용해야 할 때는 파드의 IP를 알 필요가 없으며, 대신 파드와 연결된 서비스 이름을 사용함으로써 간단히 파드에 접근할 수 있다.

위처럼 Cluster IP 타입의 서비스를 생성해 파드에 접근하는 과정을 다시 정리해 보면 아래의 그림과 같다.

  1. 특정 라벨을 가지는 파드를 서비스와 연결하기 위해 서비스의 YAML 파일에 selector 항목을 정의한다.
  2. 파드에 접근할 때 사용하는 포트를 YAML 파일의 targetPort 항목에 정의한다. 
  3. 서비스를 생성할 때, YAML 파일의 port 항목에 8080을 명시해 서비스의 Cluster IP와 8080 포트로 접근할 수 있게 설정한다.
  4. kubectl apply -f 명령어로 ClusterIP 타입의 서비스가 생성되면 서비스는 쿠버네티스 클러스터 내부에서만 사용할 수 있는 고유한 내부 IP를 할당받는다.
  5. 쿠버네티스 클러스터에서 서비스의 내부 IP 또는 서비스 이름으로 파드에 접근할 수 있다.

서비스를 삭제하려면 kubectl delete 명령어를 사용한다.

kubectl delete -f hostname-svc-clusterip.yaml
NodePort 타입의 서비스 

ClusterIP 타입의 서비스는 내부에서만 접근 가능하지만, NodePort 타입의 서비스는 클러스터 외부에서도 접근할 수 있다. 단, NodePort라는 이름에서 알 수 있듯이 NodePort 타입의 서비스는 모든 노드의 특정 포트를 개방해 서비스에 접근하는 방식이다. NodePort 타입의 서비스를 생성하기 위해 아래의 내용으로 YAML 파일을 작성해 보자.

apiVersion: v1
kind: Service
metadata: 
  name: hostname-svc-nodeport
spec: 
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: NodePort

ClusterIP 타입의 서비스를 생성했을 때 사용한 YAML 파일과 비교했을 때, type 항목을 NodePort로 설정한 점을 제외하고는 모두 동일하다. NodePort는 ClusterIP와 동작 방법이 다른 것일 뿐, 동일한 서비스 리소스이기 때문에 라벨 실렉터, 포트 설정 등과 같은 기본 항목의 사용 방법은 모두 같다.

작성한 YAML 파일을 이용해 NodePort 타입의 서비스를 생성해 보자.

서비스의 목록을 확인해 보면 NodePort 타입의 서비스가 생성됐음을 알 수 있다. PORT 항목에 출력된 31156이라는 숫자는 모든 노드에서 동일하게 접근할 수 있는 포트를 의미한다.

이 값은 30000 ~ 32768 중 랜덤으로 할당되지만, YAML 파일에 nodePort 항목을 정의하면 원하는 포트를 선택할 수도 있다.

apiVersion: v1
kind: Service
metadata: 
  name: hostname-svc-nodeport
spec: 
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
      nodePort: 31000
  selector:
    app: webserver
  type: NodePort

그렇지만 실제 운영 환경에서 NodePort로 서비스를 외부에 제공하는 경우는 많지 않다. NodePort에서 포트 번호를 80 또는 443으로 설정하기에는 적절하지 않으며 SSL 인증서 적용, 라우팅 등과 같은 복잡한 설정을 서비스에 적용하기가 어렵기 때문이다. 따라서 NodePort 서비스 그 자체를 통해 서비스를 외부로 제공하기보다는 '인그레스(Ingress)'라고 부르는 쿠버네티스의 오브젝트에서 간접적으로 사용되는 경우가 많다.

NodePort 타입 서비스의 트래픽 흐름을 그림으로 나타내면 위와 같다.

Ref : 시작하세요! 도커/쿠버네티스

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함