Kubernetes API 서버는 http 를 통해서 쿠버네티스에 대한 연산을 제공해 준다. kubectl 명령어로 실행되는 것들은 모두 API 서버를 거쳐서 이루어진다. 하지만 API 서버는 인증서를 기반으로 통신이 이루어지는데, 이 인증서에 기재된 도메인이나 IP가 아니면 통신이 이루어지지 않는다.
프로메테우스(Prometheus)는 모니터링 시스템을 말한다. 프로메테우스는 파일 기반의 타임시리즈(Time-Series) 데이터베이스다. 시스템의 메트릭스들을 수집하기 위해서는 익스포터(Exportor) 를 설치해야 한다. 이외에도 알람을 전달해주는 AlertManager 도 있는데, 전체적인 아키텍쳐는 다음과 같다.
프로메테우스는 쿠버네티스에서도 설치가 가능한데, 이글은 쿠버네티스에 프로메테우스 설치에 대한 글이다.
환경
환경은 다음과 같다.
Kubernetes 버전: 1.20
Kubernetes Nodes: Master 3개, Worker 3개
Prometheus 설치 방법: Helm Operator
설치
프로메테우스(Prometheus) 설치는 매우 다양한데, 검색을 해보면 Helm 을 이용한 방법 그중에서도 오퍼레이터(Operator) 를 이용한 방법이 많이 소개 되어 있다. 여기서도 이 오퍼레이터를 이용한 방법을 사용하고자 한다.
Prometheus Operator 로 검색을 해보면 github 저장소를 찾을 수 있다.
중간에 보면 Prometheus Operator vs kube-prometheus vs community helm chart 가 보인다. 자세히 읽어보면 쿠버네티스에 설치할 수 있는 방법이 세 가지로 나뉜다는 것을 알수 있다.
이중에서 나는 Helm chart 를 이용한 방법을 이용할 생각이다.
노드 레이블 설정
노드에 레이블을 설정하게 되면 쿠버네티스에 앱을 배포할때에 레이블을 지정함으로써 특정 노드에 생성되도록 강제할 수 있다. 프로메테우스 오퍼레이터 설치를 특정 노드에 하기 위해서 레이블을 부여할 생각이다. 대상 노드는 kworker3.systemv.local 노드이며 다음과 같이 레이블을 할당해 줬다.
많은 챠트가 존재하는데, 여기서 설치 대상은 kube-prometheus-stack 이다.
설정을 하기위해서 프로메테우스 오퍼레이터의 구성을 살펴볼 필요가 있다.
Prometheus – 프로메테우스 리소스 정의가 되어 있다. 프로메테우스를 위한 파드(Pod) 의 리플리카(Replica) 갯수, 퍼시스턴스 볼륨 구성등이다. 프로메테이스 오퍼레이터는 파드를 StatefulSet 으로 배포 한다. 그리고 어떤 애플리케이션, 혹은 리소스를 모니터링할 것이지를 지정하는 것인데, 이것은 ServiceMonitor 로 설정이 이루어 진다.
ServiceMonitor – 프로메테우스 오퍼레이터는 어노테이션 기반의 서비스 디스커버리를 지원하지 않으며 대신 PodMonitor, ServiceMonitor 를 이용한다. ServiceMonitor는 애플리케이션이나 서비스의 리소스를 모니터링할 것인지를 지정한다. 쿠버네티스의 NodeSelector 처럼 LableSelector 로 서비스의 리소스를 선택할 수 있고, 엔드포인트(EndPoint) 를 통해서 애플리케이션의 메트릭을 수집할 수 있다. ServiceMonitor 는 rule 을 기반으로 Prometheus의 모니터링 대상이 되는 ServiceMonitor를 scan하여 해당 정보를 Secret으로 배포한다. 그리고 이 Secret을 Prometheus StatefulSet에 마운트한다. 이런 방식으로 Prometheus 팟은 자신이 모니터링할 Service가 무엇인지 알 수 있다.
Altermanager – 알람 매니저 이다. 프로메테우스 컴포넌트중에 하나다.
PodMonitor – 파드에 대한 모니터다. 역시나 LabelSelector 를 통해서 모니터링하고자 하는 파드를 지정할 수 있다.
위 내용을 잘 알야하는 이유는 kube-prometheus-stack 디렉토리에 values.yaml 파일에 구조와 연관이 있다.
values.yaml 파일 편집
프로메테우스 오퍼레이터를 Helm 으로 설치할 때에는 values.yaml 파일의 설정을 참고하도록 되어 있다. values.yaml 에는 altermanager, Grafana, Prometheus 등에 대한 설정 값들이 들어가 있다. 앞에서 특정 노드에 배포하도록 하기 위해서 worker3.systemv.local 노드에 레이블링을 해줬기 때문에 이들 컴포넌트의 NodeSeletor 를 지정해 줘야 한다.
kube-prometheus-stack has been installed.Check its status by running:
kubectl--namespacemonitoring get pods-l"release=promethus"
Visit https://github.com/prometheus-operator/kube-prometheus for instructions on how to create & configure Alertmanager and Prometheus instances using the Operator.
확인
이제 확인을 해보자.
프로메테우스 설치 확인
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$kubectl get pod-nmonitoring-owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
인그레스(Ingress) 는 쿠버네티스에 설치하는 클러스터 내의 서비스에 대한 외부 접근을 관리하는 API 오브젝트이며, 일반적으로 HTTP를 관리한다. 인그레스를 위해서는 인그레스 오브젝트를 설치해야 하는데, 이를 구현한 것들이 꽤 있지만 nginx 가 대표적이다.
여기서는 Ingress-Nginx 에 대해서 간단히 알아 본다.
설치전 고려사항
첫째로 일단 쿠버네티스에 서비스(Service) 에 고려 해야 한다. 쿠버네티스의 서비스는 클러스터내에 접속지점을 생성해 준다. ClusterIP, LoadBalancer, NodePort 세 가지의 타입이 존재한다. 문제는 LoadBalancer 타입인데, 이 타입을 지정해주면 EXTERNAL-IP 가 할당되어야 하지만 일반적인 환경에서는 할당되지 않는다.
LoadBalancer 는 클라우드 서비스 제공 사업자를 위한 것으로 AWS, GCP, Azure 클라우드에서 제공하는 로드밸런서를 위한 것이다. 그래서 일반적인 베어메탈(Bare Metal) 이나 VM 환경(VMware, VirtualBox, KVM) 에서 쿠버네티스를 구성한 상태에서 LoadBalancer 를 사용하게 되면 EXTERNAL-IP 할당되지 않는다.
이 deploy.yaml 은 baremetal 버전인데, 여기의 서비스 타입은 NodePort 로 되어 있다. 필자는 앞에서도 말했지만 Metallb 를 설치했기 때문에 LoadBalancer 타입으로 변경을 해줘야 한다. deploy.yaml 파일을 열어서 바꿔 준다.
NodePort 를 LoadBalancer 로 바꾼다
diff
1
2
3
4
5
6
7
8
9
10
11
12
spec:
- type: NodePort
+ type: LoadBalancer
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
- name: https
port: 443
protocol: TCP
targetPort: https
서비스 타입을 바꿨다면 이제 설치를 해 준다.
ingress-nginx 설치
ZSH
1
$kubectl apply-fdeploy.yaml
이 방법으로 설치를 하게 되면 다음과 같은 특징을 갖게 된다.
ingress-nginx 네임스페이스가 생성 된다.
LoadBalancer 타입이며, Metallb 으로 인해 EXTERNAL-IP 가 할당 된다.
apiVersion 이 networking.k8s.io/v1 최신 버전을 지원 한다.
ingress-nginx 의 서비스
ZSH
1
2
3
4
5
$kubectl get svc-A
NAMESPACENAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)AGE
쿠버네티스(Kubernetes) 의 서비스(Service) 에 타입에는 Loadbalancer, NodePort 등을 지원한다. 문제는 Loadbalancer 는 클라우드(Cloud) 서비스 사업자를 위한 것으로 AWS, GCP, Azure 에서 제공하는 Loadbalancer 를 위한 것이다.
그런데, 많은 쿠버네티스의 사례를 살펴보면 Loadbalancer 를 사용한 사례가 아주 많다. 그래서 굳이 클라우드 서비스 사업자가 아니라고 하더라도 Loadbalancer 를 사용할 수 있도록 해보자해서 만들어진게 바로 Metallb 이다.
필자는 KVM 리눅스 가상화에 쿠버네티스를 설치했다. KVM 가상 시스템의 게스트 OS 들은 브릿지 네트워크 모드로 이기 때문에 필자의 공유기에서 자동으로 아이피를 할당 받거나, 공유기의 대역에 IP를 고정으로 사용하기도 한다. 필자의 공유기는 192.168.96.0/20 대역폭을 사용하도록 세팅을 해놨기 때문에 할당 가능한 IP는 차고도 넘친다.
Metalb 는 이렇게 외부에서 접속이 가능한 아이피 대역을 할당해 사용한다.
설치
설치를 위해서 필요한게 있는데, CNI 가 먼저 있어야 한다. Flannel, Calico 와 같은 CNI 가 설치가 먼저 되어 있어야 한다. 설치는 설치를 위한 메니페스토 파일을 이용한다.
쿠버네티스 설치는 kubeadm 명령어를 이용하면 손쉽게 자동으로 이루어 진다. HA 를 위한 설치에서는 좀 더 손이 더 가겠지만 어쨌거나 그것도 kubeadm 을 이용한다. 쿠버네티스 설치에 많은 시간을 들이는 것이 그렇게 현명해 보이지는 않을 수 있지만 쿠버네티스의 기본적인 구조를 이해하는데 수동 설치만큼 유용한 것도 없다.
인터넷을 검색해보면 쿠버네티스 수동 설치는 보통 ‘Hard way’ 로 많이 나온다. ‘힘든 방법’ 등으로 번역할 수 있는데, 하나하나 수동으로 설치를 하게 된다. 이 글이 어렵다면 ‘Kubernetes Hard way’ 로 검색하면 많은 자료를 얻을 수 있다.
Requirements
쿠버네티스 수동 설치의 최종적인 모습은 다음과 같다.
위 그림은 ‘고가용성 토폴로지 선택‘ 문서에 나온 것이다. 컨트롤 플레인 노드는 적어도 3개, 워커 노드도 적어도 3개 그리고 중간에 로드 밸런서(Load balancer) 가 필요하다. 정리를 하면 다음과 같다.
컨트롤 플레인 노드(Control plane Node): 3개
워커 노드(Worker Node): 3개
로드 밸런서(Load balancer): 1개
이를 위해서 필자는 KVM 으로 VM 호스트를 6개를 만들었고 로드 밸런서는 KVM 호스트 컴퓨터에 HAProxy 를 구성하는 것으로 결정 했다.
툴 설치
쿠버네티스는 내부에 많은 프로그램들로 구성된다. 이들 프로그램들 간의 통신은 보안 통신을 기반으로 하는데, 이를 위해서는 TLS 인증서가 필요하다. 생성하는 방법은 다양하지만 CloudFlare 에서 만든 cfssl, cfssljson 명령어를 가장 많이 이용한다.
앞에서 쿠버네티스 컨트롤 플레인, 워커, 로드 밸런서에 대해서 언급을 했었는데 자세하게는 다음과 같다.
호스트 이름
도메인
아이피
kmaster1
kmaster1.systemv.local
192.168.96.46
kmaster2
kmaster2.systemv.local
192.168.96.47
kmaster3
kmaster3.systemv.local
192.168.96.48
haproxy
haproxy.systemv.local
192.168.96.7
kworker1
kworker1.systemv.local
192.168.96.49
kworker2
kworker2.systemv.local
192.168.96.50
kworker3
kworker3.systemv.local
192.168.96.51
도메인은 내부 도메인네임서버를 운영하고 있어 이를 이용했다. 운영체제는 우분투(Ubuntu) 20.04 LTS 다. KVM 의 네트워크는 NAT 가 아니라 브릿지(Bridge) 네트워크로 구성해 KVM 게스트를 외부에서도 자유롭게 접속되도록 구성 했다. KVM의 게스트는 공유기 IP 대역에서 고정IP로 설정을 했다.
TLS 인증서 생성하기
CA(Certificate Authority) 작성하기
쿠버네티스에서 사용하는 인증서는 Self Sign 인증서다. 보통 인증서라고 하면 HTTPS 통신을 위한 인증서를 많이 들어봤을 것이다. 이 인증서를 발급받기 위해서는 여러 과정이 필요한데, 이 과정을 자세히 알고 있다면 이 과정이 이해하기 쉽다.
HTTPS 인증서를 발급 받기 위해서는 CSR 을 먼저 작성해야 한다. 하지만 그 전에 필요한 것이 CA 다. 보통은 CSR 을 작성해 CA 기관에 제출하는 형태로 진행이되지만 Self Sign 할때에는 CA 가 없다. 공인 CA에서 Self Sign 인증서를 발급해줄리도 만무하고… 그래서 CA 를 먼저 생성해 준다.
Root CA 의 역할은 제출받은 CSR 를 받아서 개인키를 돌려준다. 보통 HTTP 인증서를 발급받을때에 Root CA 는 기관에서 하기 때문에 돈을 주고 해달라고 요청을 하게 된다. 하지만 Self Sign 인증서를 작성할때에는 Root CA 기관의 CA 키가 있다고 가정하고 그 CA Key + CSR 를 돌려서 개인키를 발급받게 된다. 요약하자면 공인 Root CA 에서 사용하는 CA 키를 셀프로 만드는 것이다.
O, 그러니까 조직(Oganization) 이 “system:masters” 라는 사실에 주목해야 한다.
이렇게하면 admin-key.pem, admin.pem 이 생성된다.
Kubelet 클라이언트 인증서
Kubelet 은 쿠버네티스 클러스터에 각 노드에 설치되는 에이전트다. 이는 파드(Pod) 에 컨테이너가 실행되도록 해준다.
쿠버네티스는 노드 인증(Node Authorizer) 라 부르는, 특히 Kubelets 에 의한 API 요청 인증에 특별한 목적의 인증 모드를 사용한다. 노드 인증(Node Authorizer) 로 인증되기 위해, Kubelets 는 반드시 system:node:<nodename> 의 이름을 가진 system:nodes 그룹에 그들이 있는 것처럼 확인된 자격증명을 사용해야 한다.
따라서 nodename 이 반드시 있어야 한다. 만일 DNS 서버가 없다고 해도 괜찮다. 쿠버네티스 클러스터를 위한 서버들에 /etc/hosts 에 정적으로 도메인을 할당하면 되며, 호스트도 마찬가지로 정적으로 넣어줘도 된다.
앞에서 워커 노드의 경우에 worker1, worker2, worker3, 3개의 worker 노드가 있다. 이에 대해서 CSR 을 만들어 준다.
Kubelet 클라이언트 인증서를 위한 CSR 요청서
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
$DOMAIN="systemv.local"
$forinstance inkworker1 kworker2 kworker3;do
cat>${instance}-csr.json<<EOF
{
"CN":"system:node:${instance}.${DOMAIN}",
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"US",
"L":"Portland",
"O":"system:nodes",
"OU":"Kubernetes The Hard Way",
"ST":"Oregon"
}
]
}
EOF
done
$cat kworker1-csr.json
{
"CN":"system:node:kworker1",
"key":{
"algo":"rsa",
"size":2048
},
"names":[
{
"C":"US",
"L":"Portland",
"O":"system:nodes",
"OU":"Kubernetes The Hard Way",
"ST":"Oregon"
}
]
}
CSR 은 인증서를 요청하기 위한 문서라고 보면 된다. 여기에는 요청에 필요한 각종정보를 적게 되어 있는데, O 의 경우에 조직(Oganization) 을 뜻하며 CN 은 Company Name 이다. 이것을 Kubelets 에서는 system:nodes 조직, system:nodes:worker1.systemv.local 회사등으로 인식한다고 보면 된다. CN 에 node 이름을 도메인명으로 했다는 것을 잘 봐둬야 한다.
이제 인증서를 만들어야 하는데, Self Root CA 와 CSR 를 조합해서 만든다. 단, 여기서 주의해야 하는 것은 호스트네임(hostname) 을 줘야 한다. 호스트네임은 콤마(,) 를 구분자로 여러개를 줄 수 있는데 여기서는 호스트네임, 아이피를 주고 생성한다. worker 서버 3개에 대해서 각각 만들어 준다.
2021/04/1611:27:47[INFO]signedcertificate with serial number9063887800656623067709796741742546672753221216
-hostname 에 콤마(,) 를 이용해 도메인, 호스트네임, IP 주소를 적고 있는데 이것은 SAN 확장을 위한 것이다. 멀티도메인 인증서에서 주로 많이 나오는 이야기인데, SAN 은 하나의 SSL 인증서에 여러개의 도메인을 사용할 수 있도록 해주는 X509 인증서의 확장이다.
이렇게 해서 생성한 인증서는 다음과 같다.
Kubelet 클라이언트 인증서
1
2
3
4
5
6
kworker1-key.pem
kworker1.pem
kworker2-key.pem
kworker2.pem
kworker3-key.pem
kworker3.pem
-hostname 으로 준 도메인,호스트네임,IP 주소가 인증서에 잘 있는지를 확인하는 방법은 다음과 같다.
TLS 인증서는 CSR 과 RSA Public Key 조합으로 구성되며 이것을 Root CA 의 Private Key 를 이용해 암호화 된 상태다. 따라서 그냥 텍스트 에디터로 열어보면 암호화 스트링을 볼수 있는데, openssl 명령어를 이용하면 위와같이 텍스트로 확인이 가능하다. -hostname 으로 준 도메인,호스트네임,IP 주소는 DNS 와 IP Address 로 인식이 되었다.
Kubernetes API Server 인증서라고 설명되어 있지만 앞에 다이어그램을 보면, API Server 는 쿠버네티스 컨트롤 플레인 노드에 있게 된다. 그리고 로드 밸런서를 통해서 워커 노드와 통신을 하게 된다. 이뿐만 아니라 API Server 는 외부에 클라이언트와 Kubectl 을 통해서 통신도 해야 한다.
이를 위해서 인증서에는 쿠버네티스 컨트롤 플레인 서버들과 로드밸런서에 대해 각각 인증이 가능해야 한다.
-hostname 옵션에 보면 haproxy 의 도메인과 IP 주소를 연다라 입력해줬고, 그 이후에 kmaster1~3 까지 IP 주소를 입력해 줬다. 그리고 localhost 에 해당하는 127.0.0.1 을 입력해주고 10.32.0.1 을 입력해줬다. 이 주소는 후에 컨트롤 플레인을 만들때에 사용할 Cluster IP 주소 대역 10.32.0.0/16 에 첫번째 주소다.
쿠버네티스 API 서버는 내부 DNS 네임에 kubernetes 를 Cluster IP 대역의 첫번째 IP와 묶어서 저장해놓는다. 따라서 이 10.32.0.1 주소는 Cluster IP 대역과 연관이 돼 있다는 것을 알아야 한다.
Service Account Key Pair
Kubernetes Controller Manager는 managing service accounts 문서에 설명된대로 키 페어를 사용하여 서비스 계정 토큰을 생성하고 서명합니다.
controller manager, kubelet, kube-proxy, scheduler 클라이언트 그리고 admin 사용자를 위한 kubeconfig 파일을 생성해준다.
쿠버네티스 공인 IP 주소
kubeconfig 는 쿠버네티스 API Server 에 접속이 필요하다. 고가용성을 위해서 API Server 앞에 외부 로드 밸런서에 IP 주소를 할당해서 사용했다. 이것은 아키텍쳐 다이어그램에서 로드밸런서가 쿠버네티스의 공인 IP가 되며 여기는 HAProxy 의 IP 주소다.
Kubernetes 공인 IP
ZSH
1
$KUBERNETES_PUBLIC_ADDRESS=192.168.96.7
kubelet 쿠버네티스 설정 파일
Kubelets 를 위한 kubeconfig 파일을 생성할때, Kubelet의 노드 이름과 일치하는 클라이언트 인증서를 사용해야 한다. 이것은 쿠버네티스 Node Authorizer 로 인증할 수 있도록 해준다.
쿠버네티스트는 클러스터 상태, 애플리케이션 설정들, secret 등 다양한 데이터를 저장한다. 쿠버네티스는 클러스터 데이터를 암호화를 제공한다.
쿠버네티스 Secret 의 암호화를 위한 암호화 설정과 암호화 키 생성을 위한 설정을 작성해준다.
encryption-config.yaml 파일 작성
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ENCRYPTION_KEY=$(head-c32/dev/urandom|base64)
$cat>encryption-config.yaml<<EOF
kind:EncryptionConfig
apiVersion:v1
resources:
-resources:
-secrets
providers:
-aescbc:
keys:
-name:key1
secret:${ENCRYPTION_KEY}
-identity:{}
EOF
이것을 컨트롤 플레인에 배포 해 준다.
HAProxy 서버 설치 및 설정
HAProxy 는 컨트롤 플레인을 로드 밸런싱을 해주는 역할을 한다. 다이어그램을 보면 워커와 컨트롤 플레인을 연결해주는 것으로 나오는데, 워커가 컨트롤 플레인을 하나의 도메인으로 연결하기 위한 것이기도 하다.
컨트롤 플레인은 외부에 통신을 6443 포트를 이용하게 된다. 도메인, IP 가 다른 컨트롤 플레인을 하나의 도메인 haproxy.systemv.local:6443 로 묶게 된다.
설치는 Ubuntu 20.04 서버에 APT 명령어로 패키지 설치 했다. 설정은 다음과 같이 간단하게 해줬다.
haproxy 설정
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$sudo vim/etc/haproxy/haproxy.cfg
listen stats
bind*:9000
stats enable
stats realm Haproxy\Statistics
stats uri/haproxy_stats
stats auth admin:password
stats refresh30
mode http
frontend k8s
bind*:6443
default_backend k8s
mode tcp
option tcplog
backend k8s
balance roundrobin
#balance source
mode tcp
option tcplog
option tcp-check
server kmaster1.systemv.local192.168.96.46:6443check
server kmaster2.systemv.local192.168.96.47:6443check
server kmaster3.systemv.local192.168.96.48:6443check
$sudo systemctl restart haproxy
etcd 클러스터 설치
etcd 는 key-value 로 데이터를 저장해주는 서버다. 쿠버네티스는 자체적인 데이터를 하나도 가지고 있지 않다. 전부다 외부에 데이터를 저장하는데 그것을 해주는 것이 etcd 다. 서버 한대만 가지고 데이터를 저장하는 것은 위험함으로 이것도 클러스터로 여러 서버를 하나로 묶는다.
etcd 는 클러스터내에 서버들과 kmaster, kworker 서버들과 통신을 해야 한다. 하지만 쿠버네티스는 TLS 통신을 기반으로 하기 때문에 인증서를 함께 설치해 줘야 한다. 모든 서버와 통신을 위한 인증서로 Root CA 인증서와 kubernetes 인증서를 설치해 준다.
INTERNAL IP 는 kmaster1 서버의 IP, ETCD_NAME 은 kmaster1 이며 각각의 kmaster1, kmater2, kmaster3 에 IP를 적어주면 된다. 각각 kmaster 에서 INTERNAL IP 와 ETC_NAME 을 바꿔가면서 해주면 된다.
모두 정상적으로 설치가 되었다면 다음과 같이 확인할 수 있다.
etcd 확인하기
ZSH
1
2
3
4
5
6
7
8
$sudo ETCDCTL_API=3/usr/local/bin/etcdctl member list\
컨트롤 플레인(Controll Plane) 혹은 마스터(Master) 노드는 쿠버네티스에 두뇌에 해당한다. 모든 명령과 연산은 모두 컨트롤 플레인에서 이루어진다. 클라이언트의 명령은 컨트롤 플레인의 api server 에서 받아서 워커 노드에 kubelet 이 실행하는 형태다. 데이터는 모두 etcd 에 저장된다.
컨트롤 플레인의 주요 구성요소는 다음과 같다.
kube-apiserver
kube-controller-manager
kube-scheduler
kubectl
다운로드 및 설치
바이너리 파일을 받아야 한다. github 에는 소스코드만 올라와 있어서 어디서 받을지 잠시 헷깔렸는데, dowloadkubernetes 에서 받을 수 있었다.
여기서 –service-cluster-ip-range 를 잘 봐야 한다. 이것은 쿠버네티스 내부의 네트워크 주소 범위에 속한다. 앞에서 kubernetes 의 인증서를 작성할때에 이 주소 범위의 맨 앞의 주소인 10.32.0.1 을 넣어줬었다. 반드시 인증서에 포함되는 주소를 적어줘야 한다.
–service-account-signing-key-file, –service-account-issuer, –service-account-api-audiences 은 새롭게 추가된 기능으로 자세한 사항을 확인해 볼 필요가 있다.
쿠버네티스 Controller Manager 설정
앞에 api server 설치를 위해서 인증서를 모두 복사를 해뒀다. 컨틀로러 매니저를 위해서 kubeconfig 파일이 필요하다. 이를 복사해 준다.
쿠버네티스는 kubectl 을 이용해 명령을 받는다. 그러면 api server 에서 받고, 이것을 워커 서버에 kubelet 이 실행시키는 구조다. 문제는 api server 가 워커 노드에 kubelet 에 액세스를 할려면 RBAC 권한이 필요하다는 데 있다. RBAC(Role-Based Access Control) 는 사용자 역할 기반 접근 제어 방법이다.
지금까지 kubectl 명령어를 사용할려면 admin.kubeconfig 파일이 있어야 했다. 이 설정파일은 –server=https://127.0.0.1:6443 으로 로컬 호스트 api server 를 지칭하도록 만들어 졌다. 만일 kubectl 명령어를 외부에서 사용할려면 어떻게 해야할까?
“com.docker.network.bridge.name”: “docker0” 로 Docker Bridge 가 docker0 호스트 네트워크 인터페이스라는 걸 말해주고 있으며 여기에 붙은 Container 인 doc1 에 대한 정보를 보여주고 있다. doc1 컨테이너의 IP 는 172.17.0.2/16 이다.
bridge 명령어에서는 아무것도 안나온다. 이제 Docker 컨테이너를 하나 실행해 본다.
doc1 컨테이너의 네트워크 인터페이스 eth0 는 8번이다. 그리고 if9 로 인터페이스 9번을 가리키고 있다고 명시하고 있다. 아래 호스트 네트워크 인터페이스를 보면 veth 라고 나오는데, if8 로 인터페이스 8번을 가리고 있다. 인터페이스 8번은 doc1 컨테이너의 네트워크 인터페이스를 말한다.
ip 명령어를 통해서 veth 인터페이스만 뽑아 볼 수 있다. veth 는 가상 이더넷(Virtual Ethernet) 인데, Docker 컨테이너가 생성될때마다 가상 이더넷 카드가 호스트에 하나 생성되고 이런 가상 이더넷은 Docker 컨테이너의 기본 네트워크 인터페이스 카드와 연결된다.
docker0 는 Docker 에 기본 Bridge 인터페이스이며 Docker 컨테이너가 하나도 없으면 DOWN 상태가 되며 단 하나의 컨테이너가 실행될 경우에 UP 상태로 변경되고 가상 이더넷을 생성하고 Bridge 에 붙이게 된다.
Kubernetes 네트워크
Kubernetes 를 설치하고 난후에 상태는 Docker 만 설치한 것과 동일하다. Kubernetes 는 CNI 를 설치를 해줘야 한다. CNI 는 Kubernetes 에 네트워크를 담당할 기능을 붙이기 위한 인터페이스로 다양한 네트워크 기능들을 제공하는 프로그램들이 있다.
Flannel, Weave, Calico 등등이 자주 쓰인다.
한가지 의문(?) 혹은 문제는 이 쿠버네티스 CNI 들은 구조가 모두 다르다. 테스트를 위해서 Flannel 을 사용했다.
backend-type: vxlan 이라고 나오고 있으며 VTEP 도 보인다. 이 장치에 대한 Mac 주소도 나오는데, 이것은 flannel.1 에 있는것과 같다.
파드의 네트워크는 veth 장치를 통해서 패킷이 나가고 이것을 cni0 브릿지가 받는다. 그리고 flannel 이 이것을 flannel.1 장치로 보내게 된다.
이렇게 하는 이유가 있는데, Flannel 은 L2 Layer 스위치다. L2 Layer 스위치는 ARP 라우팅만 가능하다. 파드(Pod) 가 같은 호스트에 있는 경우에는 ARP 라우팅만으로 서로 통신이 가능하겠지만 호스트가 다를 경우, IP 대역이 변경될 경우에는 원격 호스트에 있는 파드를 ARP 라우팅만으로 찾을 수 없다.
그래서 Flannel 은 L3 Layer 계층의 가상의 스위칭 장비를 만들고, 이렇게 만들어진 각 호스트의 가상의 스위칭을 하나로 연결하는데 이것이 바로 VXLAN 이다. 가상의 스위칭 장비가 flannel.1 네트워크 인터페이스 이다. 이때, cni0 에서 올라온 데이터는 L2 Layer 에서 만든 프레임(Frame)으로 이것을 UDP 패킷형태로 IP 를 붙여 캡슐화 한다. 그리면 flannel.1 인터페이스는 다른 호스트와 연결된 또 다른 flannel.1 인터페이스로 브로드캐스팅을 한다.
다른 호스트로 받은 UDP 패킷은 flannel.1 장치에 의해서 까지고(디캡슐화) 자신의 네트워크 대역과 비교하는데, 맞으면 받은 프레임에 destination mac address 를 자신의 mac address 로 넣고 다시 UDP로 캡슐화해 돌려보낸다. 이렇게 함으로써 호스트가 다른 파드의 이더넷 주소(맥주소)를 얻게되어 연결이 이루어지는데, 이게 VXLAN 의 동작 방법이다.
VXLAN 는 L2 Layer 프레임을 UDP L3 Layer 로 캡슐화해 브로드캐스팅하고 목적지 맥주소를 얻는데 있다.
Flannel 은 이렇게 작동하지만 Calico 는 또 다르다. Calico 는 BGP 연결을 통해서 아예 L3 Layer 를 구현 한다. 완전한 Pure L3 Switch 기능을 제공하기 때문에 처음부터 IP 라우팅이 가능해진다. Calico 는 Tunnel.0 인터페이스를 통해서 서로 다른 호스트와 Peer to Peer 연결되어 있어서 IP 라우팅만으로 대상 호스트의 파드를 알아낼 수 있다. 그래서 동작 방법은 (Flannel 보다) 훨씬 간단하다.
kubectl top node Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io)
Kubectl top nodes 오류
ZSH
1
2
$kubectl top pod
Error from server(ServiceUnavailable):the server iscurrently unable tohandle the request(get pods.metrics.k8s.io)
메트릭 서버(Metric Server) 의 파드(Pod)가 정상적으로 Running 상태라 하더라도 이와같은 오류 메시지를 만날 수 있다. 이 오류는 kube-apiserver 의 로그에 다음과 같이 관련 오류가 나온다.
systemctl status kube-apiserver
ZSH
1
Apr2315:49:33kmaster1.systemv.localkube-apiserver[4598]:E042315:49:33.3309674598available_controller.go:508]v1beta1.metrics.k8s.iofailed with:Operation cannot be fulfilled on apiservices.apir>Apr2315:49:38kmaster1.systemv.localkube-apiserver[4598]:E042315:49:38.3322274598available_controller.go:508]v1beta1.metrics.k8s.iofailed with:failing ormissing response from https://10.32.0.>Apr 23 15:49:43 kmaster1.systemv.local kube-apiserver[4598]: E0423 15:49:43.333609 4598 available_controller.go:508] v1beta1.metrics.k8s.io failed with: failing or missing response from https://10.32.0.
뒤쪽에 삭제된 부분은 “net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)” 이다. 응답커넥션을 맺다가 안되서 timeout 으로 끝난 것이다.
이것은 kube-apiserver 다음의 커맨드 파라메터를 추가해줘야 한다.
–enable-aggregator-routing=true
메트릭 서버의 0.4.3 버전부터는 쿠버네티스의 Aggregator Layer 를 이용한다. API 서버가 실행중인 호스트에 kube-proxy 가 없을 경우에 위 파라메터를 추가해줘야 한다.