저는 OpenStack 을 오랫동안 해왔기 때문에 OpenStack 의 Neutron 로 비교하면 이해가 더 쉽게 되더라고요.
먼저 네트워크 대역을 만들기 위해서는 VPC 가 필요합니다. virtual 하게 만드는 것이기 때문에 10.0.0.0/16 과 같이 사설망 대역을 사용합니다.
그리고 외부와의 통신을 위해서는 Internet gateway (IGW)를 생성합니다. 이것은 software gateway 라고 생각이 들고 여기까지는 VPC 와 상관이 없습니다. 그래서 이해하기 쉽게 IGW 를 VPC 바깥에 그렸습니다.
IGW 를 VPC 와 연결 (associate) 하는 순간에 port 가 생성되어 연결될 것으로 보입니다.
VPC 내에는 subnet 을 만들어 Network 대역을 다시 나눌 수 있는데 subent 은 10.0.0.0/24 처럼 VPC 대역 내여야 하고, 하나의 subent 은 AZ 의 하나에만 할당될 수 있습니다. AZ 내에 하나만 할 당 할 수 있는 이유는 EC2 VM 을 만들면 VPC 내의 Subnet 을 선택하고, VM에 ip 를 할당하면서 해당 IP 에 대해서 s/w 적으로 연결이 될텐데 여러 AZ 에 대역이 펼쳐져 있으면 통신이 꼬일 수 밖에 없습니다.
그러므로 실제로 중요한 것은 VPC 라기 보다는 Subnet 입니다. Subnet 의 정보를 가지고 다 처리를 하는데 다만 통합적으로 관리할려고 VPC 라는 개념을 가져온 것 뿐입니다.
Subnet 은 라우팅을 잡아야 하는데, 내부적으로는 VM 을 관리하는 Baremetal Host 서버에 라우팅을 세팅하기 위한 방법이 필요합니다. (혹은 Baremetal 에 떠 있는 software network switch 에서 라우팅을 세팅)
그래서 라우팅 테이블을 만들고 Subnet 에 연결(assocation) 해주면 그 라우팅 정보를 가지고 있다가 VM 이 생성될 때 세팅을 해줄 수 있습니다.
정리해보면 다음과 같습니다.
1. IGW 는 VPC 에 attach 하는 부분이 필요하다. (router 에서 인터넷으로 가는 GW port 를 연결하는 작업)
2. Route table 을 Subnet 에 association 하는 부분이 필요하다 ( VM 이 생성될 때 통신될 수 있도록 라우팅을 세팅해야 하므로 해당 정보를 이용)
AWS 가 내부적으로 어떻게 구성되어 있는 지는 모르겠습니다. 하지만 OpenStack Neutron 을 보면 저런 방법을 사용하고 있기 때문에 아마도 동작 방식은 비슷하지 않을까 생각합니다.
Istio 는 Kubernetes 에만 올릴 수 있고 Control plane 이 여러 pod 로 구성되어 있습니다.
1.4 버전까지는 아래 다이어그램에서 보이듯이 Control 에 해당하는 컴포넌트들이 각각의 프로세스로 실행되었습니다.
하지만 1.5 버전 부터는 (현재는 1.6 버전이 최신입니다) 아래와 같이 New Architecture 로 변경되었습니다.
모든 컴포넌트가 istiod 라는 하나의 프로세스로 합쳐지고 Mixer 가 없어지고 Pilot 이 Mixer 의 기능까지도 함께 수행하는 것으로 되어 있습니다.
Istio 에서는 각 서비스를 관리하는 것도 중요하지만 사실 더 중요한 것은 여기 다이어그램에는 표시되지 않은 Ingress traffic 을 처리하는 Gateway 서버입니다. 모든 트래픽을 받아는 주는 관문 역할을 해야 하고, 백엔드의 Envoy Proxy 와도 연계되어야 하기 때문에 Isito 를 도입한다면 중요하게 고려되어야 할 서비스입니다.
[ 1 day ] OpenStack 소개 (2H) - OpenStack 배경, 구성요소, 아키텍처, 기능 등을 설명 OpenStack 설치 실습 (4H) - OpenStack 기본 설정 설치 (cinder 제외, tenant network 로 설정) OpenStack Cli 실습 (2H) - OpenStack Cli 활용 실습
[ 2 day ] OpenStack Identity 관리 (1H) - Keystone 설정, 기능 설명과 명령어를 실습 OpenStack Image 관리 (4H) - Glance 설정, 기능 설명 - Diskimage-builder 를 활용한 이미지 생성, cloud-init 활용 - 명령어 실습 Ceph 스토리지 관리 (4H) - Ceph 스토리지 기본 설명 및 설치 실습 - Cinder 설치, 설정, 기능 설명 - 명령어 실습
[ 3 day ] OpenStack 네트워크 관리 (8H) - Neutron 기능 설명 - provider network 설정, 기능 설명 - tenant network 설정, 기능 설명 - OVN (Open Vistual Network) 설명
[ 4 day ] OpenStack Compute 관리 (4H) - Nova Compute 설정, 기능 설명 - Live migration, evacuation, 노드 활성화,비활성화 실습 OpenStack 종합 실습 (4H) - 로그를 통한 문제 해결 방법 - Custum image 생성, 등록, VM 인스턴스 생성 등의 모든 기능을 실습
Kubernetes Upgrade 는 고려해야 할 사항이 많지만 Kubespray 를 활용하면 의외로 간단할 수 도 있다.
Kubespray 는 Kubernetes 설치에 kubeadm 을 활용하기 때문에 업그레이드는 Kubernetes 의 바로 윗단계 마이너버전까지만 가능하다. 예를 들어 지금 Kubernetes 버전이 v1.14.x 이면 v1.16.x 로는 업그레이드를 못한다. v1.16.x 로 업그레이드를 하기 위해서는 v1.15.x 로 업그레이드를 하고, v1.16.x 로 다시 업그레이드를 해야 한다.
마침 업그레이드가 필요했고 현재 설치된 Kubernetes 버전이 v1.14.3 이므로 v1.15.6 으로 업그레이드를 먼저 진행하였다.
Every 2.0s: kubectl get nodes Thu Jan 9 16:15:35 2020
NAME STATUS ROLES AGE VERSION
k1-master01 Ready master 323d v1.15.6
k1-master02 Ready master 323d v1.15.6
k1-master03 Ready master 323d v1.15.6
k1-node01 Ready ingress,node 323d v1.14.3
k1-node02 Ready node 323d v1.14.3
k1-node03 Ready node 322d v1.14.3
k1-node04 Ready node 323d v1.14.3
k1-node05 Ready node 323d v1.14.3
이제 Worker 를 업그레이드 할 차례인데, Worker 를 업그레이드는 고려해야 할 사항이 있다. 노드를 업그레이드 하기 위해서는 drain 을 하여 Pod 를 eviction 하는데 Kubespray 는 evction 이 완료될 때 까지 기다린다. DaemonSet 은 무시하는 옵션이 있지만 PodDisruptionBudget 은 policy 에 걸리면 pod 가 terminating 상태에서 완료되지 않기 때문에 timeout 으로 업그레이드에 실패하게 된다. 여기서는 elasticsearch 가 문제가 있어 timeout 나서 실패하였다.
# kubectl get PodDisruptionBudget -A
NAMESPACE NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
istio-system istio-egressgateway 1 N/A 0 76d
istio-system istio-galley 1 N/A 0 76d
istio-system istio-ingressgateway 1 N/A 0 76d
istio-system istio-pilot 1 N/A 0 76d
istio-system istio-policy 1 N/A 0 76d
istio-system istio-telemetry 1 N/A 0 76d
logging elasticsearch-k1-master-pdb N/A 1 0 71d
elasticsearch 문제를 해결하고 다시 k1-node01 이라는 Worker 노드 1대만 업그레이드를 진행한다.
CNCF 프로젝트 중에 하나로, RBAC 으로 권한에 제한을 두었다고 하더라도 해당 권한의 사용자 컨테이너가 탈취당한다면 컨테이너 안에서 Docker 명령이나 Kubectl 명령으로 새로운 자원을 띄우는 것이 가능하다. 이런 비정상 상황에서 자원 생성을 막을 수 있는 것이 OPA의 gatekeeper 이다.
Kubernetes 의 모든 metadata 는 etcd 에 저장됩니다. etcd 를 안정적으로 가져가기 위해 3 node 로 cluster 를 구성하는 방법을 사용합니다. 대부분의 문서는 일반적으로 etcd 를 백업 / 복구로 설명하고 있는데, 그 보다 서버 fault 가 나서 etcd 를 1 node 나 2 node 로 임시 운영해야 할 경우에 대해서 설명하고자 합니다.
[ etcd 노드 리스트 ]
k2-master01 : etcd1
k2-master02 : etcd2
k2-master03 : etcd3
먼저 etcdctl 명령어를 사용하기 쉽게 alias 로 다음과 같이 등록을 해줍니다. 각 노드 마다 endpoint ip 와 인증서 파일은 이름을 적절히 변경해 주어야 합니다.
# vi ~/.bash_aliases
alias etcdctl="export ETCDCTL_API=3; etcdctl --endpoints=[192.168.30.151:2379] --cacert=\"/etc/ssl/etcd/ssl/ca.pem\" --cert=\"/etc/ssl/etcd/ssl/admin-k2-master01.pem\" --key=\"/etc/ssl/etcd/ssl/admin-k2-master01-key.pem\" "
# . ~/.bash_aliases
멤버 조회를 하면 다음과 같이 3개 노드가 정상적으로 나타납니다.
# etcdctl --write-out=table member list
+------------------+---------+-------+-----------------------------+-----------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+-------+-----------------------------+-----------------------------+
| 683a3374b573e7c7 | started | etcd1 | https://192.168.30.151:2380 | https://192.168.30.151:2379 |
| 6f89416652d7e034 | started | etcd2 | https://192.168.30.152:2380 | https://192.168.30.152:2379 |
| d9c4159e72350017 | started | etcd3 | https://192.168.30.153:2380 | https://192.168.30.153:2379 |
+------------------+---------+-------+-----------------------------+-----------------------------+
모든 노드의 etcd 를 stop 하여 3대 모두 장애상황으로 만듭니다.
# systemctl stop etcd
3대 노드의 etcd 가 모두 장애로 다운되었고, 빠른 서비스를 위해 etcd1 만 띄운다고 하면, etcd db 에 이미 기존의 3대 cluster member 를 인식하고 있기 때문에 etcd1 만 실행시키는 것을 실패하게 됩니다. 이 때는 기존의 db (백업 db 가 아닌 운영되고 있었던 db) 를 가지고 restore 를 하여 member 정보를 새롭게 update 할 수 있습니다. 그리고 snapshot 으로 백업된 db 정보가 아니므로 hash check 도 skip 을 해야 합니다.
k2-master01 노드에서 작업
먼저, db 를 복사해 옵니다. (etcd 의 data dir 는 /var/lib/etcd 라고 가정합니다.) 그리고 /var/lib/etcd 디렉토리는 삭제합니다.
# etcdctl --write-out=table member list
+------------------+---------+-------+-----------------------------+-----------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+-------+-----------------------------+-----------------------------+
| 683a3374b573e7c7 | started | etcd1 | https://192.168.30.151:2380 | https://192.168.30.151:2379 |
+------------------+---------+-------+-----------------------------+-----------------------------+
etcd 3 node cluster 원상 복구
k2-master01 서버에서 작업
etcd cluster 를 다시 3 node 로 만들기 위해 etcd2 member 를 add 합니다.
# etcdctl member add etcd2 --peer-urls=https://192.168.30.152:2380
Member d8f69cbfe4c8a34f added to cluster a6552752c1542947
ETCD_NAME="etcd2"
ETCD_INITIAL_CLUSTER="etcd1=https://192.168.30.151:2380,etcd2=https://192.168.30.152:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
k2-master02 서버에서 작업
etcd.env 에서 initial cluster 값에 자기 자신인 etcd2 를 추가합니다. initial cluster state 는 existing 으로 그대로 진행합니다.
# vi /etc/etcd.env
ETCD_INITIAL_CLUSTER=etcd1=https://192.168.30.151:2380,etcd2=https://192.168.30.152:2380
etcd2 의 data dir 를 삭제하고 etcd2 ㅇ를 실행합니다.
# rm -rf /var/lib/etcd/
# systemctl start etcd
member 조회를 하면 정상적으로 etcd2 가 추가된 것을 알 수 있습니다.
# etcdctl --write-out=table member list
+------------------+---------+-------+-----------------------------+-----------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+-------+-----------------------------+-----------------------------+
| 683a3374b573e7c7 | started | etcd1 | https://192.168.30.151:2380 | https://192.168.30.151:2379 |
| d8f69cbfe4c8a34f | started | etcd2 | https://192.168.30.152:2380 | https://192.168.30.152:2379 |
+------------------+---------+-------+-----------------------------+-----------------------------+
k2-master01 서버에서 작업
etcd3 member 를 add 합니다.
# etcdctl member add etcd3 --peer-urls=https://192.168.30.153:2380
Member 63f37d54154a6e23 added to cluster a6552752c1542947
ETCD_NAME="etcd3"
ETCD_INITIAL_CLUSTER="etcd3=https://192.168.30.153:2380,etcd1=https://192.168.30.151:2380,etcd2=https://192.168.30.152:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"
k2-master03 서버에서 작업
etcd3 의 data dir 삭제 및 etcd3 를 start 합니다.
# rm -rf /var/lib/etcd
# systemctl start etcd
정상적으로 member 가 추가되었음을 알 수 있습니다.
# etcdctl --write-out=table member list
+------------------+---------+-------+-----------------------------+-----------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+-------+-----------------------------+-----------------------------+
| 63f37d54154a6e23 | started | etcd3 | https://192.168.30.153:2380 | https://192.168.30.153:2379 |
| 683a3374b573e7c7 | started | etcd1 | https://192.168.30.151:2380 | https://192.168.30.151:2379 |
| d8f69cbfe4c8a34f | started | etcd2 | https://192.168.30.152:2380 | https://192.168.30.152:2379 |
+------------------+---------+-------+-----------------------------+-----------------------------+
마지막으로 설정을 변경해야 할 것이 있습니다. 언제든지 etcd 는 restart 를 할 수 있으므로 etcd1 과 etcd2 의 initial cluster state 를 existing 으로 변경하고 initial cluster 의 멤버를 3대로 모두 지정해야 합니다.
k2-master02 서버에서 작업
etcd2 옵션의 initial cluster 값에 etcd3 를 포함
# vi /etc/etcd.env
ETCD_INITIAL_CLUSTER=etcd1=https://192.168.30.151:2380,etcd2=https://192.168.30.152:2380,etcd3=https://192.168.30.153:2380
k2-master01 서버에서 작업
etcd1 옵션의 initial cluster state 를 existing 으로 변경하고 initial cluster 값에 etcd2 와 etcd3 를 포함
# vi /etc/etcd.env
ETCD_INITIAL_CLUSTER_STATE=existing
ETCD_INITIAL_CLUSTER=etcd1=https://192.168.30.151:2380,etcd2=https://192.168.30.152:2380,etcd3=https://192.168.30.153:2380
Ingress Controller 를 사용하여 같은 도메인에서 subpath 로 웹서비스를 분리하는 방법은 각 서비스마다 Ingress 를 만들고, '/' path 는 무조건 포함시키는 방법입니다. 한가지 단점은 root path '/' 는 처음 만든 Ingress 가 먼저 선점하게 됩니다.
물론 이 방법은 '/' 가 경로가 반드시 필요한 서비스가 2개 이상이면 적용할 수 없지만 그런 경우는 없다고 가정합니다.
예를 들어 Jupyterlab 은 '/' 경로를 반드시 필요로 합니다. '/jupyter' 를 호출할 때 jupyterlab 이 호출되게 Ingress 의 subpath 를 지정하려면 다음과 같이 작성하면 됩니다.
여기서 두번째에 들어가 '/' 는 소용이 없습니다. Nginx 에서는 처음 입력된 순서에 의해서 location 이 결정되기 때문에 'http://jupyterlab.myserver/' 를 호출하면 첫번째 생성한 jupyterlab ingress 가 적용되기 때문입니다.
웹서비스를 인터넷에 노출시키기 위해서는 Cloud Provider 가 제공하는 Load Balancer 나 On-Prem 에서 Load Balancer 나 Ingress Controller 를 사용해야 합니다. Ingress Controller 를 사용할 때 원래의 Client IP 를 확인하는 방법은 다음과 같습니다.
여기서 사용하는 Ingress Controller 와 Pod 의 Web Server 는 Nginx 를 사용했습니다.
1. [ 인터넷 ] ---> [ Ingress Controller ] ---> [ Pod (Web Server) ]
가장 기본적인 방법으로 추가 세팅없이도 바로 original client ip 를 알 수 있습니다.
set_real_ip_from 은 ingress controller 가 실행될 수 있는 서버의 cidr 입니다.
2. [ 인터넷 ] ---> [ Google or Azure LB ] ---> [ Pod (Web Server) ]
Cloud Provider (Google or Azure) 의 LB 를 이용하고 Service 의 LoadBalancer type 으로 웹서비스를 한다면 아래와 같이 Service 의 externalTrafficPolicy 값을 local 로 지정하면 됩니다. local 로 지정하면 LB 에서 바로 Web Server Pod 가 떠 있는 서버로만 접근되기 때문에 다른 서버를 통해서 연결될 때 client ip 가 해당 서버의 ip 로 들어가는 것을 방지해 줍니다.
---
kind: Service
apiVersion: v1
metadata:
name: example-service
spec:
ports:
- port: 8765
targetPort: 9376
selector:
app: example
type: LoadBalancer
externalTrafficPolicy: Local
Azure 부분은 한국 MS 의 박인혜 차장께서 알려주셨습니다.
3. [ 인터넷 ] ---> [ AWS ELB (classic lb) ] ---> [ Pod (Web Server) ]
Classic LB 에 proxy protocol 을 enable 하고, Service 의 annotations 에 다음과 같이 설정합니다.
AWS NLB 의 각 타겟 그룹에 Proxy Protocol V2 를 설정한 후, Ingress Controller 의 Service annotations 에 다음과 같이 설정합니다.
# by default the type is elb (classic load balancer).
service.beta.kubernetes.io/aws-load-balancer-type: nlb
# Enable PROXY protocol
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: 'tcp'