공부하고 기록하는, 경제학과 출신 개발자의 노트

학습일지/kubernetes

Kubernetes Deep Dive - (7). Service

inspirit941 2021. 10. 3. 14:22
반응형

Service

스크린샷 2021-09-13 오후 7 50 22

  • 외부에서 사용자가 Application Pod에 접근할 수 있도록 하는 k8s Object.

스크린샷 2021-09-13 오후 7 51 33

  • 클라이언트의 요청을 받고 pod에게 로드밸런싱으로 트래픽을 전달하는 역할도 겸한다.

스크린샷 2021-09-13 오후 7 52 15

  • 실제로 사용자 (클라이언트)가 사용하게 될 ip주소와 Port가 Endpoint.

Service Types

스크린샷 2021-09-13 오후 9 32 57

  • Service에는 여러 타입이 있고, 각 타입마다 애플리케이션을 어떻게 / 어디로 expose 시킬 것인지의 Rule이 조금씩 다름.
  • Service Type의 디폴트는 ClusterIP. CKA에는 LoadBalancer와 ExternalName도 범위에 들어간다고 함

ClusterIP

스크린샷 2021-09-14 오전 11 17 18

  • 클러스터 내에서 Pod끼리 통신하고자 할 때 사용. 클러스터 외부 노출이 아니라 클러스터 내부에서의 노출임.
  • 즉 Same cluster & different Namespace일 때 사용 가능함.
  • frontend / backend 서비스가 같은 클러스터에 있다고 가정하면
    • 클라이언트에 해당하는 frontend pod가 backend Service에 request
    • backend Service는 각 pod로 트래픽 로드 밸런싱 수행

NodePort

스크린샷 2021-09-14 오전 11 20 08

  • 클러스터 외부와 연결할 수 있도록 ip와 포트를 노출하는 역할. 클러스터 외부의 클라이언트는 nodePort를 통해 클라이언트에 요청을 날릴 수 있다.

LoadBalancer

스크린샷 2021-09-14 오후 12 18 56

  • 클러스터 외부와 연결할 수 있도록 ip와 포트를 노출하지만, Cloud Load balancer를 필요로 하는 객체.
  • 즉 public cloud 위에서 k8s가 돌고 있으며, 해당 cloud provider가 로드 밸런서를 지원하고 있을 때 사용 가능.
    • AWS나 GCP, Azure 등의 클라우드 벤더에서 사용 가능함
  • k8s가 로드 밸런서를 생성한 뒤 클러스터의 service로 붙이는 개념.

ExternalName service는 LoadBalancer와 거의 비슷함. CName을 설정해서 해당 이름으로 LoadBalancer에 바로 접근할 수 있는 게 차이점.


먼저 Deployment부터 생성해 준다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-server
  labels:
    app: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
  1. ClusterIP Service
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: ClusterIP
  selector:
    app: frontend # 어떤 pod의 Service 역할을 할지 지정하는 것.
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

스크린샷 2021-09-14 오후 1 36 32

이렇게 두 개를 kubectl에 apply했을 경우, Service의 상세정보에서 Endpoint를 확인해보면

  • pod의 ip주소 + clusterIP에서 정의한 targetPort

형태로 정의되어 있는 걸 볼 수 있다.

cf. ClusterIP 형태로 service를 정의했기 때문에, 클러스터 외부에서 해당 ip주소로 요청을 보낼 수 없다.


아래의 pod는 clusterIP의 작동여부를 직접 확인하기 위해 우회용 pod를 생성한 것.

apiVersion: v1
kind: Pod
metadata:
  name: pod-svc-test
spec:
  containers:
    - name: busybox
      image: radial/busyboxplus:curl
      command:
        - sleep
        - '3600'
  • 해당 pod을 실행하는 식으로 - kubectl exec pod-svc-test -- curl nginx-service:80 실행하면 제대로 nginx 결과를 리턴한다.

NodePort를 사용해서 클러스터 외부에서도 접근 가능하도록 service 생성하기.

apiVersion: v1
kind: Service
metadata:
  name: nginx-service-nodeport
spec:
  type: NodePort 
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80 # 컨테이너가 80포트에서 실행중이므로 80
      nodePort: 30099
      # 반드시 5 digit을 사용해야 한다. define IP to the Service.

스크린샷 2021-09-14 오후 2 57 22

localhost:30090으로 접근하면 nginx로 연결되는 것을 확인할 수 있다.

  • Cluster IP(master / worker node)로도 접근 가능하다.
    • 단 AWS 인스턴스를 사용했을 경우 Inbound Rule이 설정되어 있어야 하고, GCP의 경우 Security Group이 인터넷 접근을 허용해야 한다.

Discover k8s Service

스크린샷 2021-09-14 오후 3 02 11

  • k8s는 기본적으로 DNS 서비스를 제공한다. 클러스터에 있는 애플리케이션에 쉽게 접근할 수 있도록.

스크린샷 2021-09-14 오후 3 02 52

  • fqDN의 경우 해당 Service의 어느 namespace라도 접근할 수 있다.
  • 동일한 namespace 내의 pod를 호출할 경우 service name 도메민만으로도 호출이 가능함.

스크린샷 2021-09-14 오후 4 17 28

클러스터 내부 (pod-svc-test)에서 service로 접근하려면

  • kubectl 커맨드로 <서비스명>:<포트명> (nginx-service:8080) 을 사용할 수 있었다.

DNS pod를 새로 생성하면

apiVersion: v1
kind: Pod
metadata:
  name: svc-test-dns
  namespace: service-namespace # 별도의 namespace에 pod 생성
spec:
  containers:
    - name: busybox-svc
      image: radial/busyboxplus:curl
      command:
        - sleep
        - '3600'

이전에 정의한 nginx-service와 namespace가 다르기 때문에, kubectl exec 커멘드를 그대로 사용하면 에러가 발생한다.

스크린샷 2021-09-14 오후 4 22 12

  • kubectl exec -n service-namespace svc-test-dns -- curl nginx-service.default.svc.cluster.local:8080
  • namespace 명시, 호출하려는 서비스의 fqdn를 명시할 경우 정상적으로 호출이 가능하다.

Manage Service in k8s - ingress Controller

스크린샷 2021-09-14 오후 6 13 30

Pod로 들어오는 모든 트래픽을 Ingress라고 부른다. ingress가 있을 경우

  • 클라이언트의 모든 요청은 일단 ingress로 넘어간다
  • ingress에서 service(s)로 트래픽을 넘겨준다.

물론 nodePort나 LoadBalancer Service를 사용할 수도 있으나,

ingressController에서는 추가적으로 여러 기능을 지원한다.

  • Abstract interface 제공
  • More Secure Traffic
  • Additional Functionality 제공. (SSL Termination, Load balancing, Namebase Virtual Hosting)

cf. NameBase Virtual Hosting: 서로 다른 service별로 resource / pod를 할당해주는 기능

스크린샷 2021-09-14 오후 6 16 46

ingress Controller의 개수는 제한이 없다.

스크린샷 2021-09-14 오후 7 55 38

  • ingress별로 rule을 정의할 수 있으며, 해당 rule에 대응되는 backend로 트래픽을 routing.
  • 예시에서는 http path을 기준으로 정함.

스크린샷 2021-09-14 오후 7 59 03

cf. service의 name 필드도 nginx-port로 정의해야 한다.

  • NamedPort. Service 내에서 Port의 이름을 지정할 수 있다.
  • service에서 port 정의하는 부분에 name을 지정하고, ingress의 backend 정의하는 영역에 해당 name을 지정하면 된다.
  • 이렇게 정의하면, rule에서 protocol / port번호 등을 굳이 지정할 필요가 없게 됨. service에서 정의한 부분을 가져와 쓰면 되기 때문

즉 각 객체가 담당하는 영역 / 통신하는 상대방은 다음과 같다.

Ingress -> Service. Service -> Deployment.


  1. 먼저 nginx의 Deployment를 생성한다.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-official-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-official
  template:
    metadata:
      labels:
        app: nginx-official
    spec:
      containers:
        - name: nginx-official
          image: 'nginx:latest'
          ports:
            - containerPort: 8080
  1. Service를 생성한다.
apiVersion: v1
kind: Service
metadata:
  name: nginx-official-service
spec:
  type: NodePort # aws나 gcp같은 클라우드 벤더사라면 LoadBalancer를 사용할 수 있다.
  ports:
    - protocol: TCP
      port: 80 # service가 접근할 Port
      nodePort: 31303 # 외부에 노출할 port
  selector:
    app: nginx-official # deployment의 app 필드와 일치.
  1. 또다른 종류의 Deployment, Service를 생성한다.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: magicalnginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: magical-nginx
  template:
    metadata:
      labels:
        app: magical-nginx
    spec:
      containers:
        - name: magical-nginx
          image: 'anshuldevops/magicalnginx:latest' # custom image 생성.
          ports:
            - name: nginx-port
              containerPort: 3000
apiVersion: v1
kind: Service
metadata:
  name: magical-nginx
spec:
  type: NodePort
  ports:
    - protocol: TCP
      port: 80
      nodePort: 31304
      name: http
  selector:
    app: magical-nginx # selector를 새로 생성한 Deployment로 정의한다.

결과

스크린샷 2021-09-14 오후 8 22 30

여기에 IngressController를 붙이면

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-rules
spec:
  rules:
  - host: nginx-official.example.com # 해당 호스트로 접근했을 경우 nginx-official-service로 접근
    http:
      paths:
      - path: /
        pathType: Exact
        backend:
          service:
            name: nginx-official-service
            port:
              number: 80
  - host: magical-nginx.example.com # 해당 호스트로 접근했을 경우 magical-nginx로 접근
    http:
      paths:
      - path: /
        pathType: Exact
        backend:
          service:
            name: magical-nginx
            port:
              number: 80

스크린샷 2021-09-15 오전 7 50 18

host가 정상적으로 등록되어 있다.

스크린샷 2021-09-15 오전 7 51 44

ip주소와 host명을 붙여서 호출하면, 정상적으로 작동되는 것을 확인할 수 있다.

반응형