실습 환경
- CSP : AWS
- OS : Ubuntu 22.04
- SW : k3s
AWS 실습 환경 배포
## KANS 배포 CloudFormation 설치
$ curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-6w.yaml
$ aws cloudformation deploy --template-file kans-6w.yaml --stack-name mylab --parameter-overrides KeyName=my-ec2-keypair SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2
## CloudFormation으로 배포된 인스턴스 중 작업용 (bastion)서버 IP 확인
$ aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output tet --region ap-northeast-2
## CloudFormation 상태 확인
while true; do
date
AWS_PAGER="" aws cloudformation list-stacks \
--stack-status-filter CREATE_IN_PROGERSS CREATE_COMPLETE CREATE_FAILED DELETE_ID_PROGRESS DELETE_FAILED \
--query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
--output table
sleep 1
done
설치된 K3S Cluster 확인
1) Node 확인
(⎈|default:N/A) root@k3s-s:~# k get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k3s-s Ready control-plane,master 4m1s v1.30.5+k3s1 192.168.10.10 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
k3s-w1 Ready <none> 3m46s v1.30.5+k3s1 192.168.10.101 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
k3s-w2 Ready <none> 3m52s v1.30.5+k3s1 192.168.10.102 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
k3s-w3 Ready <none> 3m50s v1.30.5+k3s1 192.168.10.103 <none> Ubuntu 22.04.5 LTS 6.8.0-1015-aws containerd://1.7.21-k3s2
2) Pod 확인
(⎈|default:N/A) root@k3s-s:~# k get pod -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-7b98449c4-hwpj6 1/1 Running 0 21m
kube-system local-path-provisioner-6795b5f9d8-qltcc 1/1 Running 0 21m
kube-system metrics-server-cdcc87586-znrcm 1/1 Running 0 21m
3) IP 정보 확인 (Flannel 관련 정보만 기재)
$ ip -c addr
3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 8951 qdisc noqueue state UNKNOWN group default
link/ether 96:62:55:fd:ba:92 brd ff:ff:ff:ff:ff:ff
inet 172.16.0.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::9462:55ff:fefd:ba92/64 scope link
valid_lft forever preferred_lft forever
$ ip -c route
172.16.1.0/24 via 172.16.1.0 dev flannel.1 onlink
172.16.2.0/24 via 172.16.2.0 dev flannel.1 onlink
172.16.3.0/24 via 172.16.3.0 dev flannel.1 onlink
$ cat /run/flannel/subnet.env
FLANNEL_NETWORK=172.16.0.0/16
FLANNEL_SUBNET=172.16.0.1/24
FLANNEL_MTU=8951
FLANNEL_IPMASQ=true
4) 서비스와 엔드포인트 확인
(⎈|default:N/A) root@k3s-s:~# k get svc,ep -A
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default service/kubernetes ClusterIP 10.10.200.1 <none> 443/TCP 27m
kube-system service/kube-dns ClusterIP 10.10.200.10 <none> 53/UDP,53/TCP,9153/TCP 27m
kube-system service/metrics-server ClusterIP 10.10.200.55 <none> 443/TCP 27m
NAMESPACE NAME ENDPOINTS AGE
default endpoints/kubernetes 192.168.10.10:6443 27m
kube-system endpoints/kube-dns 172.16.0.2:53,172.16.0.2:53,172.16.0.2:9153 27m
kube-system endpoints/metrics-server 172.16.1.2:10250 27m
Ingress 에 대해
- service type은 아니지만 Service 앞에서 Smart router 및 entry porint 역할을 하는 오브젝트
- 외부에서 접근 가능한 URL, Load Balancing, SSL Termination 등을 통해 Service에 대한 HTTP 기반 접근을 제공
- 클러스터에 하나 이상의 실행 중인 Ingress Controller가 필요 (ex. aws-lb-controller, nginx ingress)
- L7 영역에서 실행되고, smart router 역활을 수행
- Ingress On AWS : ALB 기준
- 트래픽 모드
- IP > Cluster IP
- Instance > NodePort
- 트래픽 모드
Ingress Controller
- Ingress를 사용하기 위해서는 Controller가 필요합니다.
- AWS EKS에서 Ingress를 생성할 때는 기본적으로 AWS LB Controller를 통해 ALB를 Provisioning 합니다.
- Nginx 같은 자체 프록시를 통해서도 Ingress를 사용할 수 있지만, nginx controller를 설치해서 nginx 파드를 로드 밸런서용으로 생성해서 사용합니다.
(이 경우 Proxy도 관리해야 해서 운영 리소스가 소모된다. 때문에 일반적으로는 ALB Controller를 사용하는게 일반적) - Kubernetes 나 다른 컨테이너 환경에서 reverse proxy 혹은 loadbalancer 역할을 하는 리소스
- Service 인그레스 리소스가 동작하려면 클러스터에 인그레스 컨트롤러가 있어야 합니다.
- Kube-controller-manager 바이너리의 일부로 실행되는 다른 유형의 컨트롤러와 달리 Ingress Controller는 클러스터와 함께 자동으로 시작되지 않습니다.
- Nginx Ingress Controller, Istio Ingress, Ambassador, …
클러스터 내부를 외부에 노출 - 발전 단계
- 1단계
파드 생성 - K8S 클러스터 내부에서만 접속
- 2단계
서비스(Cluster Type) 연결 : K8S 클러스터 내부에서만 접속
- 동일한 어플리케이션의 다수 파드 접속을 용이하게 하기 위한 서비스 접속
- 3단계
서비스(NodePort Type)연결 : 외부 클라이언트가 서비스를 통해서 클러스터 내부의 파드로 접속
- 서비스의 일부 단점을 보완한 서비스인 (LoadBalancer Type도 존재)
- 4단계
인프레스 컨트롤러 파드 - 서비스 앞단에 HTTP 고급 라우팅 등 기능 동작을 위한 배치
- 인그레스가 적용된 인그레스 컨트롤러 파드를 앞단에 배치하여 고급 라우팅 등 기능을 제공
- 5단계
인그레스 컨트롤러 파드 이중화 구성 : Active - Standby의 HA 구성을 통해 장애 대비
- 6단계
인그레스 컨트롤러 파드를 외부에 노출 : 인그레스 컨트롤러 파드를 외부에서 접속하기 위해서 노출
- 인그레스 컨트롤러 노출 시 서비스(NodePort)보다 더 많은 기능을 제공하는 LoadBalancer Type이 권장
- 7단계
인그레스와 파드간 내부 연결의 효울화 방안 : 인그레스 컨트롤러 파드 (L7)에서 서비스 파드의 IP로 직접 연결
- 인그레스 컨트롤러 파드는 K8S API서버로부터 서비스의 엔트포인트 정보를 획득하고 바로 엔트포인트로 연결
- 지원되는 인그레스 컨트롤러 : Nginx, Traefix등 현재 대부분의 인그레스 컨트롤러가 지원
Ingress 실습
Ingress nginx Controller 설치
- ingress-nginx-values.yaml
controller:
service:
type: NodePort
nodePorts:
http: 30080
https: 30443
nodeSelector:
kubernetes.io/hostname: "k3s-s"
metrics:
enabled: true
serviceMonitor:
enabled: true
- helm 레포 업데이트 및 values.yaml 파일을 이용해 install
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
$ helm repo update
$ kubectl create ns ingress
$ helm install ingress-nginx ingress-nginx/ingress-nginx -f ingress-nginx-values.yaml --namespace ingress --version 4.11.2
- 설치 확인
(⎈|default:N/A) root@k3s-s:~# kubectl get all -n ingress
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-controller-979fc89cf-kn8p5 0/1 Running 0 15s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller NodePort 10.10.200.21 <none> 80:30080/TCP,443:30443/TCP 15s
service/ingress-nginx-controller-admission ClusterIP 10.10.200.142 <none> 443/TCP 15s
service/ingress-nginx-controller-metrics ClusterIP 10.10.200.73 <none> 10254/TCP 15s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 0/1 1 0 15s
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-979fc89cf 1 1 0 15s
(⎈|default:N/A) root@k3s-s:~# kc describe cm -n ingress ingress-nginx-controller
Name: ingress-nginx-controller
Namespace: ingress
Labels: app.kubernetes.io/component=controller
app.kubernetes.io/instance=ingress-nginx
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/part-of=ingress-nginx
app.kubernetes.io/version=1.11.2
helm.sh/chart=ingress-nginx-4.11.2
Annotations: meta.helm.sh/release-name: ingress-nginx
meta.helm.sh/release-namespace: ingress
Data
====
allow-snippet-annotations:
----
false
BinaryData
====
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CREATE 2m44s nginx-ingress-controller ConfigMap ingress/ingress-nginx-controller
- externalTrafficPolicy 설정
## Client IP 확인을 위해 설정
$ kubectl patch svc -n ingress ingress-nginx-controller -p '{"spec":{"externalTrafficPolicy": "Local"}}'
(⎈|default:N/A) root@k3s-s:~# kc describe svc -n ingress ingress-nginx-controller
Name: ingress-nginx-controller
Namespace: ingress
Labels: app.kubernetes.io/component=controller
app.kubernetes.io/instance=ingress-nginx
app.kubernetes.io/managed-by=Helm
app.kubernetes.io/name=ingress-nginx
app.kubernetes.io/part-of=ingress-nginx
app.kubernetes.io/version=1.11.2
helm.sh/chart=ingress-nginx-4.11.2
Annotations: meta.helm.sh/release-name: ingress-nginx
meta.helm.sh/release-namespace: ingress
Selector: app.kubernetes.io/component=controller,app.kubernetes.io/instance=ingress-nginx,app.kubernetes.io/name=ingress-nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.10.200.21
IPs: 10.10.200.21
Port: http 80/TCP
TargetPort: http/TCP
NodePort: http 30080/TCP
Endpoints: 172.16.0.3:80
Port: https 443/TCP
TargetPort: https/TCP
NodePort: https 30443/TCP
Endpoints: 172.16.0.3:443
Session Affinity: None
External Traffic Policy: Local
Events: <none>
- Ingress nginx version 설치
(⎈|default:N/A) root@k3s-s:~# POD_NAMESPACE=ingress
POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o name)
(⎈|default:N/A) root@k3s-s:~# k exec $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version
-------------------------------------------------------------------------------
NGINX Ingress controller
Release: v1.11.2
Build: 46e76e5916813cfca2a9b0bfdc34b69a0000f6b9
Repository: https://github.com/kubernetes/ingress-nginx
nginx version: nginx/1.25.5
-------------------------------------------------------------------------------
Ingress 실습 및 통신 흐름
실습 대상 svc, pod 배포
- svc1-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1-websrv
spec:
replicas: 1
selector:
matchLabels:
app: websrv
template:
metadata:
labels:
app: websrv
spec:
containers:
- name: pod-web
image: nginx
---
apiVersion: v1
kind: Service
metadata:
name: svc1-web
spec:
ports:
- name: web-port
port: 9001
targetPort: 80
selector:
app: websrv
type: ClusterIP
- svc2-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy2-guestsrv
spec:
replicas: 2
selector:
matchLabels:
app: guestsrv
template:
metadata:
labels:
app: guestsrv
spec:
containers:
- name: pod-guest
image: gcr.io/google-samples/kubernetes-bootcamp:v1
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc2-guest
spec:
ports:
- name: guest-port
port: 9002
targetPort: 8080
selector:
app: guestsrv
type: NodePort
- svc3-pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy3-adminsrv
spec:
replicas: 3
selector:
matchLabels:
app: adminsrv
template:
metadata:
labels:
app: adminsrv
spec:
containers:
- name: pod-admin
image: k8s.gcr.io/echoserver:1.5
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc3-admin
spec:
ports:
- name: admin-port
port: 9003
targetPort: 8080
selector:
app: adminsrv
- 생성 확인
$ kubectl apply -f svc1-pod.yaml,svc2-pod.yaml,svc3-pod.yaml
$ kubectl get pod,svc,ep -o wide
Ingress 정책 생성 (Path 기반)
- ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-1
annotations:
#nginx.ingress.kubernetes.io/upstream-hash-by: "true"
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc1-web
port:
number: 80
- path: /guest
pathType: Prefix
backend:
service:
name: svc2-guest
port:
number: 8080
- path: /admin
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
- 배포 및 생성 확인
- ingress.yaml에 적용된 정책이 nginx.conf에도 적용되었는지 확인합니다.
(⎈|default:N/A) root@k3s-s:~# kubectl exec deploy/ingress-nginx-controller -n ingress -it -- cat /etc/nginx/nginx.conf | grep 'location /' -A5
location /guest/ {
set $namespace "default";
set $ingress_name "ingress-1";
set $service_name "svc2-guest";
set $service_port "8080";
--
location /admin/ {
set $namespace "default";
set $ingress_name "ingress-1";
set $service_name "svc3-admin";
set $service_port "8080";
--
location / {
set $namespace "default";
set $ingress_name "ingress-1";
set $service_name "svc1-web";
set $service_port "80";
인그레스를 통한 접속 확인
$ echo -e "Ingress1 sv1-web URL = http://$(curl -s ipinfo.io/ip):30080"
$ echo -e "Ingress1 sv2-guest URL = http://$(curl -s ipinfo.io/ip):30080/guest"
$ echo -e "Ingress1 sv3-admin URL = http://$(curl -s ipinfo.io/ip):30080/admin"
~ ➤ curl -s http://54.180.246.215:30080
<!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>
~ ➤ curl -s http://54.180.246.215:30080/guest
Hello Kubernetes bootcamp! | Running on: deploy2-guestsrv-cbb5d6665-7p4sh | v=1
~ ➤ curl -s http://54.180.246.215:30080/admin
Hostname: deploy3-adminsrv-77b7c78b98-k245g
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.0 - lua: 10008
Request Information:
client_address=172.16.0.3
method=GET
real path=/admin
query=
request_version=1.1
request_uri=http://54.180.246.215:8080/admin
Request Headers:
accept=*/*
host=54.180.246.215:30080
user-agent=curl/8.7.1
x-forwarded-for=124.54.31.185
x-forwarded-host=54.180.246.215:30080
x-forwarded-port=80
x-forwarded-proto=http
x-forwarded-scheme=http
x-real-ip=124.54.31.185
x-request-id=975ddd6bf4caa5ddcdd4ee5d312a6ddf
x-scheme=http
Request Body:
-no body in request-
분산 접속 확인
- /guest 접속
- /admin 접속
위 결과를 통해 ingress가 단순 routing 뿐만 아니라 부하 분산도 가능한 것으로 확인하였습니다.
Ingress 정책 생성 (Host 기반)
- ingress 정책 파일 생성
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-2
spec:
ingressClassName: nginx
rules:
- host: ssungz.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
- host: "*.ssungz.net"
http:
paths:
- path: /echo
pathType: Prefix
backend:
service:
name: svc3-admin
port:
number: 8080
- 자원 생성
# 생성
$ kubectl apply -f ingress2.yaml,svc3-pod.yaml
- 생성된 자원 확인
- 생성된 ingress로 접속 확인
## Local PC에서 수행
~ ➤ MYDOMAIN1=ssungz.net
~ ➤ MYDOMAIN2=test.ssungz.net
~ ➤ echo $MYIP $MYDOMAIN1 $MYDOMAIN2
54.180.246.215 ssungz.net test.ssungz.net
## Ingress로 접속 수행
MYDOMAIN2는 접속 Path가 /echo로 되어있기 때문에 / 와 /admin으로 접속 시 404 not found 에러가 발생합니다.
Canary Upgrade
- 부분적 롤아웃: 새 버전을 전체 사용자가 아닌 일부 사용자에게만 먼저 제공합니다.
- 리스크 최소화: 문제 발생 시 영향을 받는 사용자 수가 제한적이어서 전체 시스템에 미치는 영향을 최소화할 수 있습니다.
- 실시간 모니터링: 새 버전의 성능, 오류율, 사용자 반응 등을 지속적으로 관찰합니다.
- 빠른 롤백: 문제 발생 시 신속하게 이전 버전으로 되돌릴 수 있습니다.
- 점진적 확장: 새 버전이 안정적이라고 판단되면 점차 더 많은 사용자에게 확대 적용합니다.
- A/B 테스팅: 새 기능의 효과를 실제 환경에서 측정할 수 있습니다.
- 사용자 경험 보호: 대규모 장애 없이 새로운 기능을 안전하게 도입할 수 있습니다.
실습은 서로 다른 버전의 pod를 배포하고 가중치를 통해 트래픽을 교체하는 방법입니다.
- 실습 자원 생성
[as-is]
apiVersion: apps/v1
kind: Deployment
metadata:
name: dp-v1
spec:
replicas: 3
selector:
matchLabels:
app: svc-v1
template:
metadata:
labels:
app: svc-v1
spec:
containers:
- name: pod-v1
image: k8s.gcr.io/echoserver:1.5
ports:
- containerPort: 8080
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
name: svc-v1
spec:
ports:
- name: web-port
port: 9001
targetPort: 8080
selector:
app: svc-v1
[to-be]
apiVersion: apps/v1
kind: Deployment
metadata:
name: dp-v2
spec:
replicas: 3
selector:
matchLabels:
app: svc-v2
template:
metadata:
labels:
app: svc-v2
spec:
containers:
- name: pod-v2
image: k8s.gcr.io/echoserver:1.6
ports:
- containerPort: 8080
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Service
metadata:
name: svc-v2
spec:
ports:
- name: web-port
port: 9001
targetPort: 8080
selector:
app: svc-v2
- 자원 생성 확인
- 배포 자원 버전 확인
- 인그레스 생성
[canary-ingress1.yaml]
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-canary-v1
spec:
ingressClassName: nginx
rules:
- host: ssungz.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-v1
port:
number: 8080
[canary-ingress2.yaml]
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-canary-v2
annotations:
nginx.ingress.kubernetes.io/canary: "true"
## 가증치를 10%로 주어 10번에 1번은 svc2를 타도록 설정
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
ingressClassName: nginx
rules:
- host: ssungz.net
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-v2
port:
number: 8080
배포 및 접속 테스트
$ kubectl apply -f canary-ingress1.yaml,canary-ingress2.yaml
$ for i in {1..100}; do curl -s $MYDOMAIN1:30080 | grep nginx ; done | sort | uniq -c | sort -nr
가중치 10% > 50%로 변경해봅니다.
$ annotate --overwrite ingress ingress-canary-v2 nginx.ingress.kubernetes.io/canary-weight=50
이런 식으로 가중치를 조정하여 순차적으로 AS-IS에서 TO-BE로 트래픽을 넘겨줄 수 있습니다.
'Cloud > Kubernetes' 카테고리의 다른 글
[KANS] istio install + expose (0) | 2024.10.20 |
---|---|
[KANS] Gateway API (3) | 2024.10.13 |
[KANS] LoadBalancer (2) | 2024.10.05 |
[KANS] Service : ClusterIP, NodePort (1) | 2024.09.28 |
[KANS] Calico 네트워크 모드 (1) | 2024.09.15 |