여러개의 클러스터를 동시에 사용하는 경우 클러스터를 변경해서 배포해야 함.
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://35.226.187.168
name: gke_continual-tine-390202_us-central1-c_development
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://35.238.86.227
name: gke_continual-tine-390202_us-central1-c_production
...
클러스터 이름은 gke_continual-tine...<namespace_name>
형태로 매우 길다.
이때 클러스터 이름을 $ vi ~/.kube/config
명령을 통해 변경 가능하다.
$ kubectl config use-context <cluster_name>
$ kubectl config use-context gke_continual-tine-390202_us-central1-c_production
Switched to context "gke_continual-tine-390202_us-central1-c_production".
$ kubectl config current-context
gke_continual-tine-390202_us-central1-c_production
멀티 클러스터를 운영하는 환경에서는 내가 실행하는 명령이 어떤 클러스터로 전송되는지 확인!
$ kubectl create nampespace <namespace_name>
$ kubectl get namespace
를 통해 현재 cluster의 namespace 확인이 가능하다.
각 클러스터 development
, production
에는 order, payment, delivery
namespace가 존재함.
$ vi service/02_service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: order-app
namespace: order
spec:
type: NodePort
selector:
app: order
version: "1.0"
ports:
- port: 80
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: payment-app
namespace: payment
spec:
type: NodePort
selector:
app: payment
version: "1.0"
ports:
- port: 80
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: delivery-app
namespace: delivery
spec:
type: NodePort
selector:
app: delivery
version: "1.0"
ports:
- port: 80
targetPort: 8080
각 namespace별로 Service를 작성하고 다른 namespace의 pod끼리 통신하기 위해 type은 NodePort로 설정하였다.
$ kubectl apply -f service/02_service-nodeport.yaml
서비스 배포.
$ vi service/02_communicate-another-namespace-service.yaml
apiVersion: v1
kind: Service
metadata:
name: payment
namespace: order
spec:
type: ExternalName
externalName: payment-app.payment.svc.cluster.local # 외부 목적지로 가기 위한 service
ports:
- port: 80
---
apiVersion: v1
kind: Service
metadata:
name: delivery
namespace: order
spec:
type: ExternalName
externalName: delivery-app.delivery.svc.cluster.local
ports:
- port: 80
order
namespace에서 payment, delivery
namespace에 접근하는 용도의 Service를 배포.
Type은 ExternalName
, exteralName은 <service_name>.<namespace_name>.svc.cluster.local
로 지정.
order
namespace의 payment
service가 service
namespace로 서비스 호출.
$kubectl create configmap port-config -n order --from-file=configs/ORDER_HTTP_PORT
$kubectl create configmap port-config -n payment --from-file=configs/PAYMENT_HTTP_PORT
$kubectl create configmap port-config -n delivery --from-file=configs/DELIVERY_HTTP_PORT
..._HTTP_PORT
file은 8080
값만 저장되어 있으며 8080포트를 지정하는 ConfigMap을 각 namespace별로 생성함.
$ kubectl apply -f <directory/yaml_file>
$ vi deployment/03_deployment-order.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-1.0
namespace: order
spec:
selector:
matchLabels:
app: order
version: "1.0"
template:
metadata:
labels:
app: order
version: "1.0"
spec:
containers:
- name: order
image: yoonjeong/snackbar-order:1.0
resources:
limits:
memory: "64Mi"
cpu: "50m"
ports:
- containerPort: 8080
env:
- name: PORT
valueFrom:
configMapKeyRef:
key: ORDER_HTTP_PORT
name: port-config # 저장한 configmap name
ConfigMap으로 지정한 ORDER_HTTP_PORT
를 PORT라는 환경 변수로 container에 전달.
payment, delivery도 내용은 동일.
$ kubectl apply -f deployment/03_deployment-order.yaml
deployment를 배포하고
$ kubectl get pod -n order -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
order-1.0-bb47dbf9-8q2nq 1/1 Running 0 61s 10.100.0.5 gke-production-default-pool-c4f7f203-x7hg <none> <none>
$ kubectl get endpoints order-app -n order
NAME ENDPOINTS AGE
order-app 10.100.0.5:8080 130m
endpoint가 할당됨.
$ kubectl port-forward <pod_name> <local_port>:<container_port>
혹은
$ kubectl port-forward service/<service_name> <local_port>:<container_port>
포트 포워드는 요청을 통해 해당 서비스가 제대로 동작하는지 확인할 때 사용.
$ kubectl port-forward service/order-app 8080:80 -n order
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
$ curl localhost:8080
Welcome to Snackbar!
Order what you want!
===== Host Info =====
HostIP: 10.100.0.5
HostName: order-1.0-bb47dbf9-8q2nq
제대로 동작한다면 기존에 설정한 값이 리턴될 것.
$ kubectl exec <pod_name> -- curl -sv <service_name>.<namespace_name>.svc.cluster.local
FQND(fully qualified domain name)을 통해 다른 namespace로 접근 가능.
$ kubectl exec delivery-1.0-78596677ff-xt6hm -n delivery -- curl -sv order-app.order.svc.cluster.local
* Trying 10.104.15.145:80...
* Connected to order-app.order.svc.cluster.local (10.104.15.145) port 80 (#0)
> GET / HTTP/1.1
> Host: order-app.order.svc.cluster.local
> User-Agent: curl/7.79.1
> Welcome to Snackbar!
Order what you want!
===== Host Info =====
HostIP: 10.100.0.5
HostName: order-1.0-bb47dbf9-8q2nqAccept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Date: Sun, 02 Jul 2023 07:16:06 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 118
<
{ [118 bytes data]
* Connection #0 to host order-app.order.svc.cluster.local left intact
$ kubectl exec <pod_name> -n <namespace_name> \
-- curl -sv <service_name>.<namespace_name>.svc.cluster.local
FQDN과의 차이점이라면 FQDN은 현재 pod -> <요청을 보내고자 하는 service_name>.<요청을 보내고자 하는 namespace_name>이라면 ExternalName을 사용하는 경우
현재 pod -> <현재 pod의 service_name>.<현재 namespace_name>이다.
apiVersion: v1
kind: Service
metadata:
name: delivery
namespace: order
spec:
type: ExternalName
externalName: delivery-app.delivery.svc.cluster.local
ports:
- port: 80
위는 order -> payment로 요청을 보내기 위한 Service의 설정 일부분이다.
$ kubectl get svc -n order
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
delivery ExternalName <none> delivery-app.delivery.svc.cluster.local 80/TCP 169m
order-app NodePort 10.104.15.145 <none> 80:32666/TCP 177m
payment ExternalName <none> payment-app.payment.svc.cluster.local 80/TCP 169m
$ kubectl exec order-1.0-bb47dbf9-8q2nq -n order -- curl -sv delivery.order.svc.cluster.local
* Trying 10.104.3.6:80...
* Connected to delivery.order.svc.cluster.local (10.104.3.6) port 80 (#0)
> GET / HTTP/1.1
> Host: delivery.order.svc.cluster.local
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Date: Sun, 02 Jul 2023 07:29:40 GMT
< Connection: keep-alive
< Keep-Alive: timeout=5
< Content-Length: 119
<
{ [119 bytes data]
* Connection #0 to host delivery.order.svc.cluster.local left intact
Welcome to Snackbar!
Check Delievery!
===== Host Info =====
HostIP: 10.100.1.9
HostName: delivery-1.0-78596677ff-xt6hm
order
namespace의 delivery 서비스를 사용해 동일 namespace의 pod, service를 통해 다른 namespace로 요청을 보낼 수 있다.
curl -H "Host: <host_path>" --request GET $INGRESS_IP/<endpoint>
Ingress를 통해 외부 IP로부터 접근이 가능해진다.
export INGRESS_IP=$(kubectl get ingress <ingress_name> -n <namespace_name> -o jsonpath="{.status.loadBalancer.ingress[0].ip}")
위의 방식으로 INGRESS_IP를 환경 변수로 설정 가능하다.
$ vi ingress/ingress-snackbar.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: snackbar
namespace: order
spec:
rules:
- host: order.fast-snackbar.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: order-app
port:
number: 80
$ kubectl apply -f ingress/ingress-snackbar.yaml
ingress.networking.k8s.io/snackbar created
$ kubectl get ingress snackbar -n order
NAME CLASS HOSTS ADDRESS PORTS AGE
snackbar <none> order.fast-snackbar.com 34.160.230.242 80 21m
order.fast-snackbar.com
를 사용하여 외부로부터 접근.
$ curl -H "Host: order.fast-snackbar.com" --request GET $INGRESS_IP/menus
We have 4 snacks!
1. Pizza: 10,000
2. Burger: 5,000
3. Coke: 1,000
4. Juice: 1000
===== Host Info =====
HostIP: 10.100.0.5
HostName: order-1.0-bb47dbf9-8q2nq
INGRESS_IP를 통해 접근하면 order
namespace의 service인 order-app
에 접근하고 order
namespace의 pod인 order-1.0-bb47dbf9-8q2nq
가 응답하는 것을 확인할 수 있다.
이때 order
namespace의 pod인 order-1.0-bb47dbf9-8q2nq
는 payment
namespace에 요청한 결과를 얻어 사용자에게 제공한다.
새로운 버전이 준비된 상태에서 한번에 트래픽을 전환하여 새로운 pod로 요청이 가도록 하는 방식. 그린 pod(new pod)를 먼저 준비하고 service object의 목적지를 그린 pod로 변경한다. selector를 변경하게 되면 서비스의 요청이 새로운 pod로 전달된다.
블루 pod(old pod)로는 요청이 전달되지 않으므로 롤백용으로 남기거나 삭제한다.
selector를 이용해 트래픽을 바꾸는 것
$ vi deployment/05_deployment-order.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-2.0
namespace: order
spec:
selector:
matchLabels:
app: order
version: "2.0"
template:
metadata:
labels:
app: order
version: "2.0"
spec:
containers:
- name: order
image: yoonjeong/snackbar-order:2.0
resources:
limits:
memory: "64Mi"
cpu: "50m"
ports:
- containerPort: 8080
env:
- name: PORT
valueFrom:
configMapKeyRef:
key: ORDER_HTTP_PORT
name: port-config
기존의 03_deployment-order.yaml
에서 버전만 1.0 -> 2.0으로 변경함.
$ kubectl apply -f deployment/05_deployment-order.yaml
deployment.apps/order-2.0 created
새로운 버전을 배포하면
$ kubectl get pod -n order
NAME READY STATUS RESTARTS AGE
order-1.0-bb47dbf9-8q2nq 1/1 Running 0 5h4m
order-2.0-6c4c75d65c-jzknb 1/1 Running 0 68s
$ kubectl get deployment -n order
NAME READY UP-TO-DATE AVAILABLE AGE
order-1.0 1/1 1 1 5h3m
order-2.0 1/1 1 1 21s
기존의 1.0 버전과 2.0 버전이 공존한다.
$ vi service/05_service-blue-green.yaml
apiVersion: v1
kind: Service
metadata:
name: order-app
namespace: order
spec:
type: NodePort
selector:
app: order
version: "2.0"
ports:
- port: 80
targetPort: 8080
기존 02_service-nodeport.yaml
와 달리 selector
에서 version이 1.0 -> 2.0으로 바뀜.
$ kubectl apply -f service/05_service-blue-green.yaml
service/order-app configured
배포 후
$ kubectl get svc -n order -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
delivery ExternalName <none> delivery-app.delivery.svc.cluster.local 80/TCP 7h15m <none>
order-app NodePort 10.104.15.145 <none> 80:32666/TCP 7h23m app=order,version=2.0
payment ExternalName <none> payment-app.payment.svc.cluster.local 80/TCP 7h15m <none>
$ curl -sv -H "Host: order.fast-snackbar.com" --request GET $INGRESS_IP
* Trying 34.160.230.242:80...
* Connected to 34.160.230.242 (34.160.230.242) port 80 (#0)
> GET / HTTP/1.1
> Host: order.fast-snackbar.com
> User-Agent: curl/7.85.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Date: Sun, 02 Jul 2023 11:54:24 GMT
< Content-Length: 115
< Via: 1.1 google
<
New Order 2.0!!
Order what you want!
===== Host Info =====
HostIP: 10.100.0.7
HostName: order-2.0-6c4c75d65c-jzknb* Connection #0 to host 34.160.230.242 left intact
버전이 2.0으로 바뀐것을 확인.
블루그린과 달리 selector
를 변경하지 않고 n개의 pod를 연결하여 pod 개수를 유지시키며 다른 버전의 pod를 비교하여 test를 진행함.
$ vi service/06_service-canary.yaml
apiVersion: v1
kind: Service
metadata:
name: order-app
namespace: order
spec:
type: NodePort
selector:
app: order
ports:
- port: 80
targetPort: 8080
selector
에 버전 정보가 존재하지 않음.
$ kubectl apply -f service/06_service-canary.yaml
service/order-app configured
$ kubectl get svc -n order -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
delivery ExternalName <none> delivery-app.delivery.svc.cluster.local 80/TCP 7h35m <none>
order-app NodePort 10.104.15.145 <none> 80:32666/TCP 7h44m app=order
payment ExternalName <none> payment-app.payment.svc.cluster.local 80/TCP 7h35m <none>
$ kubectl scale deployment order-1.0 -n order --replicas=10
$ kubectl scale deployment order-2.0 -n order --replicas=0
--replicas
를 사용하여 해당 버전의 원하는 만큼 pod를 배포한다.
$ kubectl scale deployment order-2.0 -n order --replicas=2
$ kubectl scale deployment order-1.0 -n order --replicas=8
2.0 버전의 pod를 2개로 늘리고 1.0 버전의 pod를 8개로 감소시킴.
$ for i in {1..10};
> do curl -sv -H "Host: order.fast-snackbar.com" --request GET $INGRESS_IP
> done
* Trying 34.160.230.242:80...
* Connected to 34.160.230.242 (34.160.230.242) port 80 (#0)
> GET / HTTP/1.1
> Host: order.fast-snackbar.com
> User-Agent: curl/7.85.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Date: Sun, 02 Jul 2023 12:19:29 GMT
< Content-Length: 119
< Via: 1.1 google
<
Welcome to Snackbar!
Order what you want!
===== Host Info =====
HostIP: 10.100.1.11
HostName: order-1.0-bb47dbf9-qnnv5* Connection #0 to host 34.160.230.242 left intact
* Trying 34.160.230.242:80...
* Connected to 34.160.230.242 (34.160.230.242) port 80 (#0)
> GET / HTTP/1.1
> Host: order.fast-snackbar.com
> User-Agent: curl/7.85.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Date: Sun, 02 Jul 2023 12:19:30 GMT
< Content-Length: 116
< Via: 1.1 google
<
New Order 2.0!!
Order what you want!
===== Host Info =====
HostIP: 10.100.0.12
HostName: order-2.0-6c4c75d65c-dhdkk* Connection #0 to host 34.160.230.242 left intact
* Trying 34.160.230.242:80...
* Connected to 34.160.230.242 (34.160.230.242) port 80 (#0)
새로운 버전의 pod을 배포한 후에 트래픽의 반응을 확인.
블루그린의 경우 새 버전을 배포하였을 때 오류가 발생한다면 치명적임.
일부 pod에서 문제가 없다면 전체 pod로 배포.
리스크를 줄일 수 있음.
$ kubectl delete ingress snackbar -n order
$ kubectl delete service order -n order
$ kubectl delete service payment -n order
$ kubectl delete service delivery -n order
$ kubectl delete deployment order -n order
$ kubectl delete deployment payment -n payment
$ kubectl delete deployment delivery -n delivery