Gurumee 2021. 7. 25. 19:42
반응형

이 문서는 인프런 강의 "데브옵스를 위한 쿠버네티스 마스터"을 듣고 작성되었습니다. 최대한 요약해서 강의 내용을 최소로 하는데 목표를 두고 있어서, 더 친절하고 정확한 내용을 원하신다면 강의를 구매하시는 것을 추천드립니다. => 강의 링크

네임스페이스

namespace란 물리 쿠버네티스 클러스터 내에서 동작하는 가상 쿠버네티스 클러스터이다. 약간 실제 물리서버에서 동작하는 VM과 성격이 비슷한데, 쉽게 생각해서 리소스들을 분리된 영역으로 격리시켜 관리하는 기술이다.

 

기본적으로 쿠버네티스 클러스터에는 다음의 4가지 네임스페이스가 자동으로 생성된다.

$ kubectl get namespace
NAME              STATUS   AGE
default           Active   15d
kube-node-lease   Active   15d
kube-public       Active   15d
kube-system       Active   15d

 

이제 namespace 하나를 생성해보자. 터미널에 다음과 같이 ns-test.yaml을 만든다.

 

src/ch08/k8s/ns-test.yaml

apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  name: ns-test
spec: {}
status: {}

 

이제 터미널에 다음을 입력한다.

$ kubectl create -f ns-test.yaml
namespace/ns-test created

 

namespace가 잘 만들어졌는지 확인해보자.

$ kubectl get ns
NAME              STATUS   AGE
default           Active   15d
kube-node-lease   Active   15d
kube-public       Active   15d
kube-system       Active   15d
# 추가되었다.
ns-test           Active   39s

 

이제 namespace 안에 pod을 하나 만들어보자. 터미널에 다음을 입력한다.

$ kubectl run nginx --image=nginx -n ns-test
pod/nginx created

 

이제 pod이 잘 생성되었는지 확인해보자.

$ kubectl get pod
No resources found in default namespace.

 

분명 리소스를 생성했는데 안보인다. 왜냐하면, 기본적으로 namespace는 "default"로 잡혀있다. 내가 원하는 namespace의 리소스를 확인하고 싶다면 다음과 같이 입력하면 된다.

# kubectl get <resource> -n <namespace>
$ kubectl get pod -n ns-test
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          5m8s

 

namespace 역시 다음과 같이 삭제할 수 있다.

$ kubectl delete -f ns-test.yaml 
namespace "ns-test" deleted

서비스

ServicePod 집합에서 실행 중인 애플리케이션을 네트워크 서비스로 노출하는 추상화된 방법이다. Pod는 일시적으로 생성되는 컨테이너의 집합인데, 실제 리소스 생성/수정/삭제 시에, IP 주소가 지속적으로 변동되기 떄문에 이를 추적하고 부하를 분산시켜야 한다. 이를 해결하기 위한 것이 바로 Service이다.

 

서비스는 다음과 같이 구성할 수 있다.

 

src/ch08/k8s/simple-app-svc-v1.yaml

# service
apiVersion: v1
kind: Service
metadata:
  name: simple-app-svc
spec:
  sessionAffinity: ClientIP
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: simple-app

---
# deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simple-app-dep
  labels:
    app: simple-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: simple-app
  template:
    metadata:
      labels:
        app: simple-app
    spec:
      containers:
      - name: simple-app
        image: gurumee92/simple-app:v1
        ports:
        - containerPort: 8080

 

위의 코드는 Service이고, 아래 --- 아래 코드는 Deployment이다. 즉 위의 코드는 simple-app 컨테이너 3개를 구동시키는 Deployment와, 이를 외부로 노출시키는 Service를 구동시키는 것이다. 다음 명령어로 만들 수 있다.

 

$ pwd
/home/gurumee/k8s/ch08

$ kubectl create -f simple-app-svc.yaml
service/simple-app-svc created
deployment.apps/simple-app-dep created

 

다음 명령어로 생성된 Service 목록을 확인할 수 있다.

$ kubectl get svc
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes       ClusterIP   10.96.0.1        <none>        443/TCP   6d11h
simple-app-svc   ClusterIP   10.105.112.199   <none>        80/TCP    50s

 

여기서 생성된 Pod들을 보자.

$ kubectl get pod -o wide
NAME                              READY   STATUS    RESTARTS   AGE   IP          NODE     NOMINATED NODE   READINESS GATES
simple-app-dep-5d4c8d678b-hcqzj   1/1     Running   0          21m   10.32.0.3   slave2   <none>           <none>
simple-app-dep-5d4c8d678b-jljj2   1/1     Running   0          21m   10.32.0.2   slave2   <none>           <none>
simple-app-dep-5d4c8d678b-p5sdn   1/1     Running   0          21m   10.32.0.4   slave2   <none>           <none>

 

이 때 IP를 기억해두자. 서비스의 정보를 확인한다.

$ kubectl describe svc simple-app-svc
Name:              simple-app-svc
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=simple-app
# 기본 타입은 ClusterIP이다.
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.105.112.199
IPs:               10.105.112.199
Port:              <unset>  80/TCP
TargetPort:        8080/TCP
# 엔드포인트에 Pod들의 IP가 할당된 것을 확인할 수 있다.
Endpoints:         10.32.0.2:8080,10.32.0.3:8080,10.32.0.4:8080
Session Affinity:  ClientIP
Events:            <none>

 

여기서 EndpointsPod들의 IP가 할당된 것을 확인할 수 있다. 또한 아무 타입 지정을 하지 않으면 ClusterIP로 지정된다. 이게 모든 서비스의 원형 타입이라고 생각하면 된다. 현재 이렇게 분산된 경우, 한 가지만 더 알아두자. 현재 설정으로는 서비스 세션이 고정되지 않는다. 이 때 세션을 고정하기 위한 설정이 있는데 바로 sessionAffinity이다. 다음과 같이 수정한다.

 

src/ch08/k8s/simple-app-svc-v2.yaml

apiVersion: v1
kind: Service
metadata:
  name: simple-app-svc
spec:
  # 세션을 고정하기 위한 설정
  sessionAffinity: ClientIP

  # ...
# ...

 

이렇게 하면, 한 번 요청을 한 경우, 이후부터의 요청은 최초 요청한 Pod에게 요청을 지속적으로 발생하게 된다. 또한 외부 IP와 연결이 필요한 경우는 Service와 더불어서 Endpoints라는 리소스가 필요하다. 이런 식으로 외부 IP와 연동할 수 있다.

 

서비스

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  ports:
  - port: 80

 

엔드포인트

apiVersion: v1
kind: Endpoints
metadata: 
  name: external-service
subnets:
  - addresses:
    - ip: 11.11.11.11
      ip: 22.22.22.22
    ports:
    - port: 80

 

다음 절부터는 VM이 아닌 GCP 혹은 EKS 등의 Public Cloud가 제공하는 쿠버네티스 클러스터를 이용하자.

서비스를 expose하는 방법 (1) NodePort

서비스를 노출하는 3가지 방법 중 하나이다. 노드 자체 포트를 사용하여 Pod로 리다이렉트 한다. 다음과 같이 서비스 파일을 생성한다.

 

src/k8s/ch08/simple-app-np.yaml

apiVersion: v1
kind: Service
metadata:
  name: simple-app-np
spec:
  type: NodePort
  selector:
    app: simple-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080
    nodePort: 30001

 

위에서 typeNodePort로 지정되고, ports에서 nodePort가 "30001"로 지정된 것을 확인할 수 있다. 터미널에서 다음과 같이 입력하면 서비스를 생성할 수 있다.

 

$ kubectl create -f simple-app-np.yaml
service/simple-app-np created

 

또한 서비스 상태를 확인해보자.

$ kubectl get svc
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.112.0.1      <none>        443/TCP        7m26s
simple-app-np   NodePort    10.112.13.203   <none>        80:30001/TCP   34s

 

여기서 80:30001 로 묶여 있는데, Compute Engine 대시보드로 가보면 다음 노드들이 구성된 것을 확인할 수 있다.

이 때 해당 노드 IP:nodePort로 구동되는 Pod들을 호출할 수 있다. 호출 이전에 방화벽 정책을 통해 NodePort에 대해서 외부로 공개해주어야 한다.

$ gcloud compute firewall-rules create simple-app-svc-rules --allow=tcp:30001

 

그 후 노드 IP:노드 포트로 요청을 할 수가 있다.

# curl <노드 IP>:<노드 포트>
$ curl 34.64.75.5:30001
Hello World v1%  

$ curl 34.64.132.245:30001
Hello World v1%  

$ curl 34.64.143.114:30001
Hello World v1%  

서비스를 expose하는 방법 (2) LoadBalancer

서비스를 노출하는 3가지 방법 중 하나이다. 외부 게이트웨이를 사용해 노드 포트로 리다이렉트 한다.

 

src/ch08/k8s/simple-app-lb.yaml

apiVersion: v1
kind: Service
metadata:
  name: simple-app-lb
spec:
  type: LoadBalancer
  selector:
    app: simple-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

 

여기서 typeLoadBalancer인 것을 알아두자. 그리고 nodePort를 지정하지 않으면 30000 번대 포트에서 무작위로 선택이 된다. 한 번 만들어두자.

$ kubectl create -f simple-app-lb.yaml
service/simple-app-lb created

 

그 다음 시간이 좀 지나서 확인해보면 다음과 같이 생성됨을 확인할 수 있다.

$ kubectl get svc
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
kubernetes      ClusterIP      10.112.0.1      <none>          443/TCP        5m59s
simple-app-lb   LoadBalancer   10.112.15.122   34.64.122.149   80:31694/TCP   74s

 

여기에 EXTERNAL-IP에 IP가 할당된 것을 확인할 수 있다. 애는 GCP 네트워크 서비스에서 제공하는 "부하 분산" 즉 LoadBalacner이다. 다음과 같이 확인할 수 있다.

외부 IP가 동일하다. 이제 이 IP로 호출할 수 있다. 이떄는 nodePort가 아닌 port로 지정된 포트 번호로 호출해야 한다.

$ curl 34.64.122.149:80
Hello World v1% 

서비스를 expose하는 방법 (3) Ingress

서비스를 노출하는 3가지 방법 중 하나이다. 하나의 IP 주소를 통해 여러 서비스를 제공하는 특별한 메커니즘이다. 인그레스 방법은 설정이 조금 빡세다. 먼저 NodePort용 서비스를 생성해둔다.

$ kubectl create -f simple-app-np.yaml
service/simple-app-np created

 

그 후 다음과 같이 인그레스를 만드는 설정 파일을 생성한다.

 

src/ch08/k8s/simple-app-ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: simple-app-ingress
spec:
  rules:
  - host: gurumee.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: simple-app-np
            port:
              number: 80

 

hostPod들이 구성된 도메인을 연결해준다. 그 후 http.paths.path를 지정해주고 https.paths.backend.service.namePod을 연결해준다. 이제 터미널에 다음을 입력하여 인그레스를 생성한다.

$ kubectl create -f simple-app-ingress.yaml
ingress.networking.k8s.io/simple-app-ingress created

 

다음과 같이 확인할 수 있다.

$ kubectl get ingress
NAME                 CLASS    HOSTS         ADDRESS         PORTS   AGE
simple-app-ingress   <none>   gurumee.com   34.149.182.51   80      94s

 

이제 이 주소로 curl을 날려보자. (여기서는 kubectl이 실행되는 터미널에서 진행해야 한다.)

$ curl 34.149.182.51
response 404 (backend NotFound), service rules for the path non-existent 

 

gurumee.com이 없기 때문에 404가 뜬다. 이걸 허용하려면 원래 도메인 설정이 필요하나 귀찮으니까 /etc/hosts에서 다음과 같이 수정한다.

# Kubernetes-managed hosts file.
127.0.0.1       localhost

# 수정된 곳
# ingress IP    매핑된 host
34.149.182.51   gurumee.com

::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
172.17.0.4      cs-595878838943-default-boost-l8dqx

 

그 후 이제는 "Host"에 curl을 날려보자.

$ curl gurumee.com
Hello World v1

kubectl이 실행되는 터미널이 아니라면 어차피 외부에 지정된 도메인이 아니므로 이런식으로 호출은 불가능하다.

728x90
반응형