쿠버네티스의 네트워크 형태는 내부용 서비스, 외부 노출용 서비스, 특수한 형태(내부에서 외부로 나갈 때) 등이 있다.
DB와 같이 외부로 노출되면 안되는 컨테이너는 어떻게 접근해야 할까 ?
✔️ Service - ClusterIP
쿠버네티스(클러스터) 내부에서만 사용 가능한 가상 IP를 가진 엔드포인트를 제공하는 로드밸런서 구성(L4)
ClusterIP는 클러스터 내부에 새로운 IP를 할당하고 여러개의 파드를 바라보는 로드밸런서 기능을 제공한다.
그리고 서비스 이름을 내부 도메인 서버에 등록하여 파드 간에 서비스 이름으로 통신할 수 있다.
클러스터 내부에서만 접근할 수 있으며, 외부에서 접근이 불가능하므로 포트 포워딩이나 프록시를 통해 접근해야 한다.
서비스 타입을 별도로 지정하지 않으면 ClusterIP가 기본으로 설정된다.
즉, ClusterIP가 각 파드의 앞단에 위치해 각 파드들의 IP 주소를 알지 못해도 해당 파드로 요청을 전달할 수 있게 해주는 역할을 해준다.
myweb-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: myweb-svc
spec:
selector:
app: web
ports:
- port: 80
targetPort: 8080
myweb-rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myweb-rs
spec:
replicas: 3
selector:
matchLabels: # 달라지는 부분
app: web
env: dev
template:
metadata:
labels:
app: web
env: dev
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
ports:
- containerPort: 8080
protocol: TCP
서비스는 셀렉터와 포트 관련 설정이 반드시 존재해야 한다.
서비스의 셀렉터를 레플리카셋에서 생성하는 파드의 템플릿에 매칭 해준다.
즉, 파드와 서비스 간의 관계는 레이블과 레이블 셀렉터로 맺게된다.
클라이언트가 서비스 리소스에 접근할 때 사용하는 포트가 80번이며 서비스는 셀렉터에 의해 셀렉팅되는 파드들을(이 정보는 서비스와 같은 이름을 갖는 엔드포인트가 가진다.) 연결시키며 그 때 8080포트를 사용한다.
클라이언트가 서비스의 ip 또는 DNS주소를 보고 80번으로 접속하면 셋중 하나로 포워딩해주는 형태이다.
vagrant@node1 ~/svc kubectl explain svc.spec.type
KIND: Service
VERSION: v1
FIELD: type <string>
DESCRIPTION:
type determines how the Service is exposed. Defaults to ClusterIP. Valid
options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
지정할 수 있는 타입은 ExternalName
, ClusterIP
, NodePort
, and LoadBalancer
이다.
✔️ sessionAffinity
svc.spec.sessionAffinity
sessionAffinity <string>
Supports "ClientIP" and "None". Used to maintain session affinity. Enable
client IP based session affinity. Must be ClientIP or None. Defaults to
None. More info:
https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
클라이언트가 로드밸런서에 요청을 하면 기본적으로 라운드 로빈으로 작동한다.
만약 VM이 워드프레스(WP)라고 하면 게시물을 CRUD하기 위해 로그인을 해야한다.
3개의 VM이 LB에 연결되어 있는 상황을 생각해보자
클라이언트가 LB를 타고 WP로 접속해 로그인을 하면 로그인 정보는 VM 시스템의 memory에 저장되어 있다,
로그인을 하는 순간 요청이 갈텐데 로그인 정보가 다른 WP에 전달되면 어떻게 될 것인가 ?
해당 VM의 memory에는 로그인 정보가 없기 때문에 로그인이 안된다.
📌 로그인 구현 방법
로그인 인증 정보를 메모리 상에 구현하는 것도 방법이지만, 로그인 정보를 DB에 쌓는 것도 방법이다.
DB에 갖다 놓으면 DB에 쿼리가 많이 가므로 VM을 하나로 통합해주는 memcached
, redis
(In Memory DB) 같은 것으로 구현해서 인증 정보를 memory DB에 저장한다.
In Memory DB
- 각 VM의 메모리를 통합해서 관리해주는 memory DB
DB 데이터는 보통 디스크에 저장하는데 이것은 메모리에 저장한다.
로드 밸런서 구성이 없었을 경우 가장 간단한 방법은 세션을 고정시키는 것이다.
특정 클라이언트는 무조건 특정 VM으로만 접근할 수 있도록 하는 것이다.
네트워크 연결 세션을 고정시키는 것을 sessionAffinity
라고 한다.
두가지 옵션이 있다.
ClientIP
- 클라이언트 ip에 따라서 세션을 고정 시켜준다.None(default)
- 세션 고정을 사용하지 않는다.
테스트 해보자myweb-svc-ses.yaml
apiVersion: v1
kind: Service
metadata:
name: myweb-svc-ses
spec:
type: ClusterIP
sessionAffinity: ClientIP
selector:
app: web
ports:
- port: 80
targetPort: 8080
ClientIp
를 기준으로 LB을 한다.
vagrant@node1 ~/svc kubectl create -f myweb-svc-ses.yaml -f myweb-rs.yaml
service/myweb-svc-ses created
replicaset.apps/myweb-rs created
vagrant@node1 ~/svc kubectl get svc,ep,rs,pod
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 4d4h
service/myweb-svc-ses ClusterIP 10.233.48.30 <none> 80/TCP 4s
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.100.100:6443 4d4h
endpoints/myweb-svc-ses 10.233.90.38:8080,10.233.92.80:8080,10.233.96.57:8080 4s
NAME DESIRED CURRENT READY AGE
replicaset.apps/myweb-rs 3 3 3 4s
NAME READY STATUS RESTARTS AGE
pod/myweb-rs-6dbvr 1/1 Running 0 4s
pod/myweb-rs-hvpm5 1/1 Running 0 4s
pod/myweb-rs-k4kzq 1/1 Running 0 4s
myweb-svc-ses
서비스의 ip가 있고 myweb-svc-ses
엔드포인트는 레이블 셀렉터에 의해 셀렉팅된 파드의 ip 정보를 가진다.
테스트 해보자
vagrant@node1 ~/svc kubectl run nettool -it --image ghcr.io/c1t1d0s7/network-multitool --rm
--rm
옵션을 붙인 후 실행한뒤 exit 하면 pod "nettool" deleted
이 출력된다.
어플리케이션이 종료되고 컨테이너가 종료되면 파드를 알아서 삭제해준다.
If you don't see a command prompt, try pressing enter.
/ # curl 10.233.48.30
Hello World!
myweb-rs-6dbvr
/ # curl 10.233.48.30
Hello World!
myweb-rs-6dbvr
/ # curl 10.233.48.30
Hello World!
myweb-rs-6dbvr
항상 같은 파드로만 연결되는 것을 확인할 수 있다.
✔️ Named Port
vagrant@k8s-node1 ~ kubectl explain rs.spec.template.spec.containers.
name <string>
If specified, this must be an IANA_SVC_NAME and unique within the pod. Each
named port in a pod must have a unique name. Name for the port that can be
referred to by services.
이름을 지정할 수 있다.
vagrant@k8s-node1 ~ kubectl explain svc.spec.ports
name <string>
The name of this port within the service. This must be a DNS_LABEL. All
ports within a ServiceSpec must have unique names. When considering the
endpoints for a Service, this must match the 'name' field in the
EndpointPort. Optional if only one ServicePort is defined on this service.
여기에도 이름을 지정할 수 있다.
myweb-rs-named.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myweb-rs-named
spec:
replicas: 3
selector:
matchLabels: # 달라지는 부분
app: web
env: dev
template:
metadata:
labels:
app: web
env: dev
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
ports:
- containerPort: 8080
protocol: TCP
name: web8080 # TCP의 8080을 가리키는 이름
rs.spec.template.spec.containers.porst.name
은 rs.spec.template.spec.containers.porst.containersPort
를 가리키는 이름이다.
myweb-svc-named.yaml
apiVersion: v1
kind: Service
metadata:
name: myweb-svc-named
spec:
selector:
app: web
ports:
- port: 80
targetPort: web8080
svc.spec.ports.targetPort
에는 rs.spec.template.spec.containers.porst.name
을 적어줄 수 있다.
vagrant@k8s-node1 ~/svc kubectl create -f myweb-rs-named.yaml
replicaset.apps/myweb-rs-named created
vagrant@k8s-node1 ~/svc kubectl create -f myweb-svc-named.yaml
service/myweb-svc-named created
vagrant@k8s-node1 ~/svc kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.100.100:6443 4d4h
myweb-svc-named 10.233.90.39:8080,10.233.92.83:8080,10.233.96.58:8080 7s
ep를 보면 간단하게 확인할 수 있다. ep에 정상적으로 8080 포트가 붙는다.
즉, 우리는 이 서비스에 port 번호를 지정하지 않고 web8080
이름을 지정했지만 알아서 8080 포트를 참조한다.
RS 포트에 이름을 부여하고 서비스의 타겟 포트에 해당 이름을 지정하면 포트 번호를 기억하지 않아도 이름만 알고있다면 이름으로 포트 번호를 참조할 수 있다.
✔️ Multi Port
컨테이너의 이미지에 따라 여러개의 포트를 쓸 수 있다.
두개의 포트를 쓰는 가장 일반적인 형태가 web에서 HTTP, HTTPS를 둘 다 지원하는 형태이다.
컨테이너 이미지를 만들 때 web의 경우 HTTP를 위해서 80번 포트 HTTPS를 위해서 443번 포트 2개를 열어 놓을 수 있다.Dockerfile
을 만들 때 expose 80
, expose 443
을 열어둘 수 있다.
물론 그전에 아파치가 두개의 포트를 사용하도록 세팅해놔야 한다.
어플리케이션이 포트를 여러개 사용한다고 하면 다음과 같이 구성할 수 있다.myweb-rs-multi.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myweb-rs-multi
spec:
replicas: 3
selector:
matchLabels: # 달라지는 부분
app: web
env: dev
template:
metadata:
labels:
app: web
env: dev
spec:
containers:
- name: myweb
image: ghcr.io/c1t1d0s7/go-myweb
ports:
- containerPort: 8080
protocol: TCP
name: http
- containerPort: 8443
protocol: TCP
name: https
myweb-svc-multi.yaml
apiVersion: v1
kind: Service
metadata:
name: myweb-svc-multi
spec:
selector:
app: web
ports:
- port: 80
targetPort: 8080
name: http
- port: 443
targetPort: 8443
name: https
서비스가 존재하고 서비스에 포트가 2개 열려 있다. (80번과 443번)
클라이언트는 80과 443 둘 중 하나로 들어올 수 있다.
파드들이 있을 때 파드도 역시 8080 포트와 8443 포트를 열어놓고 있으므로 80번으로 들어오면 8080으로 연결하고 443번으로 들어오면 8443으로 연결시킨다.
vagrant@k8s-node1 ~/svc kubectl describe svc myweb-svc-multi
Name: myweb-svc-multi
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=web
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.233.41.79
IPs: 10.233.41.79
Port: http 80/TCP
TargetPort: 8080/TCP
Endpoints: 10.233.90.40:8080,10.233.92.84:8080,10.233.96.59:8080
Port: https 443/TCP
TargetPort: 8443/TCP
Endpoints: 10.233.90.40:8443,10.233.92.84:8443,10.233.96.59:8443
Session Affinity: None
Events: <none>
8080 포트를 가진 ip 3개와 8443 포트를 가진 ip 3개가 생성된 것을 알 수 있다.
'DevOps > Kubernetes' 카테고리의 다른 글
[Kubernetes] Service - Service Discovery (2) (0) | 2022.05.21 |
---|---|
[Kubernetes] Service - Service Discovery (1) (0) | 2022.05.21 |
[Kubernetes] Service & DNS (서비스와 DNS) (0) | 2022.05.20 |
[Kubernetes] CronJob (크론잡) (0) | 2022.05.20 |
[Kubernetes] Job (잡) (0) | 2022.05.20 |
영차영차 성장 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!