Table of Contents |
---|
개요
https://seversky.atlassian.net/wiki/spaces/CHT/pages/527925259/Creating+a+single+control-plane+cluster+with+kubeadm#Testing-a-simple-Nginx-deployment 에서 실습했던 내용에서 조금 더 확장하여 이해하여 본다.
...
즉, Service discovery 방식으로 해결하게 되는데, …to do
외부에서의 Pod 접속 테스트
nginx-deployment.yaml
편의를 위해 test namespace를 생성한다즉 도메인 방식으로 접근한다.
기본적으로 K8s를 Initialize하게 되면, coredns pod가 생성되며, 생성된 모든 pod는 여기 등록되어 아래와 같이 kube-dns
서비스로 dns 쿼리하여 pod IP를 알아낸 후 통신을 연결하게 된다.
Code Block | ||
---|---|---|
| ||
apiVersion:$ v1kubectl kind:get Namespacesvc metadata:-A NAMESPACE name: test |
nginx pod를 2개(replicas: 2
)로 생성한다.
Code Block | ||
---|---|---|
| ||
apiVersion: apps/v1 kind: Deployment metadata: name: nginx namespace: testNAME labels: app: nginx spec: replicas: 2 selector: matchLabels: TYPE app: nginx template: CLUSTER-IP EXTERNAL-IP metadata: PORT(S) labels: app: nginx AGE default spec: containers: -kubernetes name: nginx image: nginx:stable-alpine3.17-slim ClusterIP ports: 10.96.0.1 - containerPort: 80 |
각각의 타입 별 테스트를 위해,
ClusterIP, LB, NodePort 타입의 서비스들을 각각 생성하여 본다.
Code Block | ||
---|---|---|
| ||
apiVersion: v1 kind: Service metadata: name: nginx-svc namespace: test spec: selector: <none> 443/TCP app: nginx type: ClusterIP13d kube-system ports: - name: http kube-dns port: 80 # targetPort를 지정하지 않을 경우, targetPort=port 설정 값. externalIPs:ClusterIP - 10.2096.20.23610 --- apiVersion: v1 kind: Service metadata: <none> name: nginx-svc-lb namespace: test spec: selector: app: nginx type: LoadBalancer ports: - name: http port: 8080 protocol: TCP targetPort: 80 53/UDP,53/TCP,9153/TCP 13d |
정리하자면, 각각의 Pod는 언제든 IP가 변경될 수 있으며 할당된 IP는 label selector로 지정된 서비스를 통해 도메인으로 통신하게 된다.
이제 아래 예제를 통해 이해를 높여보자.
외부에서의 Pod 접속 테스트
nginx-deployment.yaml
편의를 위해 test namespace를 생성한다.
Code Block | ||
---|---|---|
| ||
apiVersion: v1
kind: Namespace
metadata:
name: test |
nginx pod를 2개(replicas: 2
)로 생성한다.
Code Block | ||
---|---|---|
| ||
apiVersion: apps/v1 kind: Deployment metadata: name: nginx namespace: test labels: app: nginx spec: replicas: 2 selector: matchLabels: #nodePort: 30036app: nginx # 생략이 시,label을 임의값 지정됨.(30000-32767) externalIPs: - 10.20.2.235 --- apiVersion: v1 kind: ServiceService object에 지정해야 서비스 도메인을 통한 통신 연결이 수행됨. template: metadata: name: nginx-svc-np namespacelabels: test spec: selector: app: nginx type: NodePort spec: ports: - namecontainers: http - portname: 8080nginx protocol: TCP image: nginx:stable-alpine3.17-slim targetPort: 80 ports: #nodePort: 30037 # 생략 시, 임의값 지정됨.(30000-32767) externalIPs: - 10.20.2.236 |
...
containerPort: 80 |
각각의 타입 별 테스트를 위해,
ClusterIP, LB, NodePort 타입의 서비스들을 각각 생성하여 본다.
Code Block | ||
---|---|---|
| ||
$apiVersion: kubectlv1 getkind: poService -nmetadata: test -o wide NAMEname: nginx-svc namespace: test spec: selector: app: nginx # 위 matchLabels에 지정되었던 label 지정. READY type: ClusterIP STATUS ports: RESTARTS AGE- name: http IP port: 80 # targetPort를 지정하지 않을 경우, NODEtargetPort=port 설정 값. externalIPs: - 10.20.2.235 - 10.20.2.236 --- apiVersion: v1 kind: Service metadata: name: nginx-svc-lb namespace: test spec: selector: app: nginx # NOMINATED위 NODEmatchLabels에 지정되었던 label READINESS지정. GATES busybox-9ff887cc8-f5m46 type: LoadBalancer 1/1 ports: Running - 0name: http port: 8080 128m 10.244.1.16 test-k8s-worker01-centos8.snetsystems.com protocol: TCP <none> targetPort: 80 <none> nginx-6c8cf466cd-mpfz4 #nodePort: 30036 # 1/1생략 시, 임의값 지정됨.(30000-32767) Running externalIPs: 0 - 10.20.2.235 129m - 10.24420.12.15236 test--k8s-worker01-centos8.snetsystems.com <none> - apiVersion: v1 kind: Service metadata: name: nginx-svc-np namespace: test spec: <none> nginx-6c8cf466cd-xgbd7 selector: 1/1 app: nginx # Running위 matchLabels에 지정되었던 0label 지정. type: NodePort ports: 129m 10.244.2.14 test-k8s-worker02-centos8.snetsystems.com- name: http <none> port: 8081 <none> | ||
Code Block | ||
| ||
$ kubectl get svc -n test NAME protocol: TCP targetPort: 80 #nodePort: TYPE30037 # 생략 시, 임의값 지정됨.(30000-32767) externalIPs: CLUSTER -IP 10.20.2.235 EXTERNAL-IP PORT(S) 10.20.2.236 |
모두 문제없이 생성된다면, 아래와 같을 것이다.
Code Block | ||
---|---|---|
| ||
$ kubectl get po -n test -o wide NAME AGE nginx-svc ClusterIP 10.105.132.127 10.20.2.236 READY 80/TCP STATUS RESTARTS AGE 131m nginx-svc-lbIP LoadBalancer 10.110.214.153 10.20.2.235 8080:31558/TCPNODE 131m nginx-svc-np NodePort 10.97.16.76 10.20.2.236 8080:31511/TCP 131m |
위에서 EXTERNAL-IP
를 각각 지정하여 두었기 때문에, EXTERNAL-IP
를 통하여 각 80, 8080 포트로 접속이 가능하다.
각각의 curl 결과:
Code Block | ||
---|---|---|
| ||
$ curl http://10.20.2.236 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html> |
Code Block | ||
---|---|---|
| ||
$ curl http://10.20.2.236:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html> |
Code Block | ||
---|---|---|
| ||
$ curl http://10.20.2.235:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html> |
또한, 각각의 서비스는 label selector app: nginx
로 지정하였기 때문에 deploy로 생성한 2개의 nginx pod에 Load balancing 된다.
nginx-6c8cf466cd-xgbd7 로그:
Code Block |
---|
10.244.2.1 - - [27/Jun/2023:08:30:40 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:31:58 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:14 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" NOMINATED NODE READINESS GATES nginx-6c8cf466cd-mpfz4 1/1 Running 0 129m 10.244.1.15 test-k8s-worker01-centos8.snetsystems.com <none> <none> nginx-6c8cf466cd-xgbd7 1/1 Running 0 129m 10.244.2.14 test-k8s-worker02-centos8.snetsystems.com <none> <none> |
Code Block | ||
---|---|---|
| ||
$ kubectl get svc -n test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc ClusterIP 10.105.132.127 10.20.2.235,10.20.2.236 80/TCP 45h
nginx-svc-lb LoadBalancer 10.110.214.153 10.20.2.235,10.20.2.236 8080:31558/TCP 45h
nginx-svc-np NodePort 10.97.16.76 10.20.2.235,10.20.2.236 8081:31511/TCP 45h |
위에서 externalIPs
를 각각 지정하여 두었기 때문에, 각각의 externalIPs
를 통하여 각 80, 8080 포트로 접속이 가능하다.
가능한 조합의 curl 테스트:
Code Block | ||
---|---|---|
| ||
$ curl http://10.20.2.235
$ curl http://10.20.2.236
$ curl http://10.20.2.235:8080
$ curl http://10.20.2.236:8080
$ curl http://10.20.2.236:8081
$ curl http://10.20.2.236:8081
# 물론 LoadBalancer와 NodePort Type 서비스는 nodeport로도 접근이 가능하다.
$ curl http://10.20.2.235:31558
$ curl http://10.20.2.236:31558
$ curl http://10.20.2.235:31511
$ curl http://10.20.2.236:31511
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
</html> |
또한, 각각의 서비스는 label selector app: nginx
로 지정하였기 때문에 deploy로 생성한 2개의 nginx pod에 Load balancing 된다.
nginx-6c8cf466cd-xgbd7 로그:
Code Block | ||
---|---|---|
| ||
10.244.2.1 - - [27/Jun/2023:08:3430:1840 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:3431:1958 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:2014 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:2018 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:4019 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" |
nginx-6c8cf466cd-mpfz4 로그:
Code Block |
---|
10.244.2.1 - - [27/Jun/2023:08:3034:4020 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:3134:5820 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:1440 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" |
nginx-6c8cf466cd-mpfz4 로그:
Code Block | ||
---|---|---|
| ||
10.244.2.1 - - [27/Jun/2023:08:3430:1840 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:3431:1958 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:2014 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:2018 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" 10.244.2.1 - - [27/Jun/2023:08:34:4019 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" |
내부 Pod간 통신
...
10.244.2.1 - - [27/Jun/2023:08:34:20 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-"
10.244.2.1 - - [27/Jun/2023:08:34:20 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-"
10.244.2.1 - - [27/Jun/2023:08:34:40 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.61.1" "-" |
내부 Pod간 통신
앞서 개요에서 언급하였듯이, 모든 Pod들은 IP 대신 도메인으로 통신할 수 있다.
즉, Pod간 internal 통신을 위해서는 서비스명으로 access한다.
관련 테스트를 위해 test namespace에 아래와 같은 pod를 하나 생성하자.
Code Block | ||
---|---|---|
| ||
apiVersion: apps/v1
kind: Deployment
metadata:
name: busybox
namespace: test
labels:
app: busybox
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox
command:
- "/bin/sh"
- "-c"
- "sleep inf" |
정상 생성되면 아래와 같을 것이다.
Code Block | ||
---|---|---|
| ||
$ kubectl get po -n test -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
busybox-9ff887cc8-f5m46 1/1 Running 0 46h 10.244.1.16 test-k8s-worker01-centos8.snetsystems.com <none> <none>
nginx-6c8cf466cd-mpfz4 1/1 Running 0 46h 10.244.1.15 test-k8s-worker01-centos8.snetsystems.com <none> <none>
nginx-6c8cf466cd-xgbd7 1/1 Running 0 46h 10.244.2.14 test-k8s-worker02-centos8.snetsystems.com <none> <none> |
busybox 내부 쉘로 진입하여 각 pod ip로 접근해보면, 당연하게도 overlay networking을 통해 잘 접근된다는 것을 볼 수 있다.
Code Block | ||
---|---|---|
| ||
# kubectl exec -n test -it busybox-9ff887cc8-f5m46 -- sh
/ # ping 10.244.2.14
PING 10.244.2.14 (10.244.2.14): 56 data bytes
64 bytes from 10.244.2.14: seq=0 ttl=62 time=1.243 ms
64 bytes from 10.244.2.14: seq=1 ttl=62 time=0.887 ms
^C
--- 10.244.2.14 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.887/1.065/1.243 ms
--- 10.244.2.15 ping statistics ---
7 packets transmitted, 0 packets received, 100% packet loss
/ #
/ # ping 10.244.1.15
PING 10.244.1.15 (10.244.1.15): 56 data bytes
64 bytes from 10.244.1.15: seq=0 ttl=64 time=0.322 ms
^C
--- 10.244.1.15 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.322/0.322/0.322 ms |
Info |
---|
CRI-O runtime의 경우 보안 상 pod 내부에서 ping이 나가는 것이 막혀있다. 이후, systemctl restart crio를 통해 crio를 재시작하고, 자세한 내용은 https://flavono123.github.io/posts/crio-capabilities-bug/ 을 참고 하기 바란다. |
그러나, IP는 언제든 바뀔 수 있으므로, 이번에는 미리 만들어둔 Service를 통해 접근해보도록 하자.
당연한 말이지만, Service는 Pod가 아니라 pod의 특정 포트를 cluster ip와 포트로 연결해주는 일종의 정책이므로, 위의 ping은 도달하지 않는다.
아래와 같이 telnet을 통해 서비스 포트로 접근이 가능하다는 것을 확인할 수 있다.
Code Block | ||
---|---|---|
| ||
/ # telnet nginx-svc 80
Connected to nginx-svc
/ # telnet nginx-svc-lb 8080
Connected to nginx-svc-lb
# telnet nginx-svc-np 8081
Connected to nginx-svc-np |
그럼 어떻게 pod에서 서비스 포트로 접근이 가능할까?
nslookup을 통해 도메인 찾기를 해보자.
Code Block | ||
---|---|---|
| ||
/ # nslookup nginx-svc
Server: 10.96.0.10
Address: 10.96.0.10:53
** server can't find nginx-svc.svc.cluster.local: NXDOMAIN
Name: nginx-svc.test.svc.cluster.local
Address: 10.105.132.127 |
위의 10.96.0.10:53
은 kube-dns
서비스의 CLUSTER-IP
이다.(kubectl get svc -n kube-system
명령으로 확인할 수 있다.)
10.105.132.127
는 nginx-svc
서비스의 CLUSTER-IP
이다.
즉, CLUSTER-IP: 10.105.132.127
에 대하여 nginx-svc.test.svc.cluster.local
으로 도메인이 등록되어 있음을 알 수 있으며, nginx-svc
는 label selector로 각 pod와 연결된다는 것을 추정할 수 있을 것이다.
참고로, 다른 domain의 pod에 접근하기 위해서는 서비스명.도메인명
으로 사용하면 된다.
Code Block | ||
---|---|---|
| ||
/ # telnet kube-dns.kube-system 53
Connected to kube-dns.kube-system
Connection closed by foreign host |
Headless Service
위 Service 예제에서는 편의상 externalIPs
를 모두 할당하였다.
하지만,
Pod 간 통신 즉 클러스터 내 통신만을 하기 때문에 굳이
externalIPs
를 할당할 필요가 없다.클러스터된 Pod가 2개 이상이고, 이들 pod들에 load balancing 하여 request할 필요가 없다.
등의 경우, Headless 서비스를 생성하면 되는데, 클러스터 내에서 DNS 쿼리를 하면, 앞 장에서 살펴본 CLUSTER-IP
가 할당되지 않기 때문에 해당 Pod의 IP를 직접 제공해준다.
만약 2개 이상의 Pod가 seletor로 연결되어 있다면, 랜덤한 순서로 이 서비스에 연결된 모든 IP 리스트를 리턴하게 된다. 말그대로, DNS 서버 역할만 수행한다.
자세한 내용은 구글 검색 등을 통해 살펴보기를 바라며, 아래와 같이 clusterIP: None
을 명시적으로 붙여주면 된다.
Code Block | ||
---|---|---|
| ||
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: test
spec:
selector:
app: nginx # 위 matchLabels에 지정되었던 label 지정.
type: ClusterIP
clusterIP: None
ports:
- name: http
port: 80 # targetPort를 지정하지 않을 경우, targetPort=port 설정 값. |
생성 후, 앞 장의 내용대로 nslookup
테스트를 해보면 이해가 될 것이다.
정리
이 실습들을 통해 모던 어플리케이션 시스템을 어떻게 K8s 상에 구축할 것 인가에 대한 통상의 개념을 알아보았다.
다음에는 아래와 같은 완성된 형태의 어플리케이션 서비스의 Server side를 deploy 해볼 것이다.
...