쿠버네티스 수동 설치
쿠버네티스 설치는 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 명령어를 가장 많이 이용한다.
cfssl, cfssljson 명령어는 cfssl respository 에서 공식 배포 된다.
1 2 3 4 5 6 |
]$ wget -q --show-progress --https-only --timestamping \ https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 \ https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 ]$ chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 ]$ sudo mv cfssl_linux-amd64 /usr/local/bin/cfssl ]$ sudo mv cfssljson_linux-amd64 /usr/local/bin/cfssljson |
kubectl 명령어도 다음과 같이 설치를 해준다. kubectl 명령어 설치는 Kubernetes 메뉴얼에 잘 나와 있다.
1 2 3 |
]$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" ]$ chmod +x kubectl ]$ sudo mv kubectl /usr/local/bin/ |
쿠버네티스 수동 설치를 위한 자원들
앞에서 쿠버네티스 컨트롤 플레인, 워커, 로드 밸런서에 대해서 언급을 했었는데 자세하게는 다음과 같다.
호스트 이름 | 도메인 | 아이피 |
---|---|---|
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 키를 셀프로 만드는 것이다.
셀프 Root CA 는 모든 인증서의 기반이 된다.
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 39 40 |
]$ cat > ca-config.json <<EOF { "signing": { "default": { "expiry": "8760h" }, "profiles": { "kubernetes": { "usages": ["signing", "key encipherment", "server auth", "client auth"], "expiry": "8760h" } } } } EOF ]$ cat > ca-csr.json <<EOF { "CN": "Kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "CA", "ST": "Oregon" } ] } EOF ]$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca 2021/04/16 10:39:10 [INFO] generating a new CA key and certificate from CSR 2021/04/16 10:39:10 [INFO] generate received request 2021/04/16 10:39:10 [INFO] received CSR # csr 받음 2021/04/16 10:39:10 [INFO] generating key: rsa-2048 2021/04/16 10:39:10 [INFO] encoded CSR # csr 인코딩 2021/04/16 10:39:10 [INFO] signed certificate with serial number 218079751927709843791408092001643512042320999306 |
출력 메시지를 자세히보면 new CA key 와 CSR 를 기반으로 new certificate 를 생성중이라고 시작하고 ‘CSR 받았다’ 그리고 ‘CSR 인코드’ 라고 나온다. 생성된 파일은 다음과 같다.
1 2 3 4 5 |
$ ls -lh total 20K -rw-r--r-- 1 systemv systemv 1005 Apr 16 10:39 ca.csr -rw------- 1 systemv systemv 1.7K Apr 16 10:39 ca-key.pem -rw-rw-r-- 1 systemv systemv 1.4K Apr 16 10:39 ca.pem |
ca.pem 은 인증서이며 ca-key.pem 은 Root CA 에서 사용할 개인키(Private Key) 다. ca.csr 은 인증 요청서. Self Root CA 조차도 Key 를 요청하기는 것이기 때문에 CSR 이 필요하게 돼, ca.csr 이 만들어진다.
이 Self Root CA 는 Self Root CA 기관을 하나 만들었다고 생각하면 된다.
apiserver 클러스터 관리자 인증을 위한 클라이언트 인증서
마치 SSH 인증서를 사용한 인증 로그인이 가능하듯이 apiserver 클러스터 관리자 인증을 위해서 별도의 인증서가 필요한 모양이다. 이를 다음과 같이 생성해 준다.
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 |
$ cat > admin-csr.json <<EOF { "CN": "admin", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:masters", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ admin-csr.json | cfssljson -bare admin 2021/04/16 11:00:23 [INFO] generate received request 2021/04/16 11:00:23 [INFO] received CSR 2021/04/16 11:00:23 [INFO] generating key: rsa-2048 2021/04/16 11:00:23 [INFO] encoded CSR 2021/04/16 11:00:23 [INFO] signed certificate with serial number 115687386704811718443950929337723184713983594790 2021/04/16 11:00:23 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); specifically, section 10.2.3 ("Information Requirements"). |
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 을 만들어 준다.
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" $ for instance in kworker1 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개에 대해서 각각 만들어 준다.
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 |
$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=kworker1.systemv.local,kworker1,192.168.96.49 \ -profile=kubernetes \ kworker1-csr.json | cfssljson -bare kworker1 2021/04/16 11:25:47 [INFO] generate received request 2021/04/16 11:25:47 [INFO] received CSR 2021/04/16 11:25:47 [INFO] generating key: rsa-2048 2021/04/16 11:25:48 [INFO] encoded CSR 2021/04/16 11:25:48 [INFO] signed certificate with serial number 538816406986381230934384726276464293616164354584 $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=kworker2.systemv.local,kworker2,192.168.96.50 \ -profile=kubernetes \ kworker2-csr.json | cfssljson -bare kworker2 2021/04/16 11:26:59 [INFO] generate received request 2021/04/16 11:26:59 [INFO] received CSR 2021/04/16 11:26:59 [INFO] generating key: rsa-2048 2021/04/16 11:27:00 [INFO] encoded CSR 2021/04/16 11:27:00 [INFO] signed certificate with serial number 597751742484889183781059204510460046544933863841 $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=kworker3.systemv.local,kworker3,192.168.96.51 \ -profile=kubernetes \ kworker3-csr.json | cfssljson -bare kworker3 2021/04/16 11:27:46 [INFO] generate received request 2021/04/16 11:27:46 [INFO] received CSR 2021/04/16 11:27:46 [INFO] generating key: rsa-2048 2021/04/16 11:27:47 [INFO] encoded CSR 2021/04/16 11:27:47 [INFO] signed certificate with serial number 9063887800656623067709796741742546672753221216 |
-hostname 에 콤마(,) 를 이용해 도메인, 호스트네임, IP 주소를 적고 있는데 이것은 SAN 확장을 위한 것이다. 멀티도메인 인증서에서 주로 많이 나오는 이야기인데, SAN 은 하나의 SSL 인증서에 여러개의 도메인을 사용할 수 있도록 해주는 X509 인증서의 확장이다.
이렇게 해서 생성한 인증서는 다음과 같다.
1 2 3 4 5 6 |
kworker1-key.pem kworker1.pem kworker2-key.pem kworker2.pem kworker3-key.pem kworker3.pem |
-hostname 으로 준 도메인,호스트네임,IP 주소가 인증서에 잘 있는지를 확인하는 방법은 다음과 같다.
1 2 3 4 5 |
$ openssl x509 -in kworker1.pem -text -noout ... X509v3 Subject Alternative Name: DNS:kworker1.systemv.local, DNS:kworker1, IP Address:192.168.96.49 ... |
TLS 인증서는 CSR 과 RSA Public Key 조합으로 구성되며 이것을 Root CA 의 Private Key 를 이용해 암호화 된 상태다. 따라서 그냥 텍스트 에디터로 열어보면 암호화 스트링을 볼수 있는데, openssl 명령어를 이용하면 위와같이 텍스트로 확인이 가능하다. -hostname 으로 준 도메인,호스트네임,IP 주소는 DNS 와 IP Address 로 인식이 되었다.
kube-controller-manager 와 통신을 위한 인증서
kube-controller-manager 와 통신을 위한 인증서를 생성해 준다.
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 |
$ cat > kube-controller-manager-csr.json <<EOF { "CN": "system:kube-controller-manager", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:kube-controller-manager", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager 2021/04/16 11:53:51 [INFO] generate received request 2021/04/16 11:53:51 [INFO] received CSR 2021/04/16 11:53:51 [INFO] generating key: rsa-2048 2021/04/16 11:53:51 [INFO] encoded CSR 2021/04/16 11:53:51 [INFO] signed certificate with serial number 707492023587759406906035528054497220180910976164 2021/04/16 11:53:51 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); specifically, section 10.2.3 ("Information Requirements"). |
“O”: “system:kube-controller-manager” 에 주목해야 한다.
kube-proxy 와 통신을 위한 인증서
kube-proxy 와 통신을 위한 인증서를 생성해 준다.
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 |
$ cat > kube-proxy-csr.json <<EOF { "CN": "system:kube-proxy", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:node-proxier", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-proxy-csr.json | cfssljson -bare kube-proxy 2021/04/16 11:56:31 [INFO] generate received request 2021/04/16 11:56:31 [INFO] received CSR 2021/04/16 11:56:31 [INFO] generating key: rsa-2048 2021/04/16 11:56:31 [INFO] encoded CSR 2021/04/16 11:56:31 [INFO] signed certificate with serial number 663577426807755857235217640760583940251020445314 2021/04/16 11:56:31 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); specifically, section 10.2.3 ("Information Requirements"). |
“O”: “system:node-proxier” 에 주목해야 한다.
kube-scheduler 와 통신을 위한 인증서
kube-scheduler 와 통신을 위한 인증서를 생성해 준다.
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 |
$ cat > kube-scheduler-csr.json <<EOF { "CN": "system:kube-scheduler", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:kube-scheduler", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-scheduler-csr.json | cfssljson -bare kube-scheduler 2021/04/16 11:59:13 [INFO] generate received request 2021/04/16 11:59:13 [INFO] received CSR 2021/04/16 11:59:13 [INFO] generating key: rsa-2048 2021/04/16 11:59:13 [INFO] encoded CSR 2021/04/16 11:59:13 [INFO] signed certificate with serial number 619651712283739066643157460188000366903756344982 2021/04/16 11:59:13 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); specifically, section 10.2.3 ("Information Requirements"). |
노드간 API Server 통신을 위한 인증서
Kubernetes API Server 인증서라고 설명되어 있지만 앞에 다이어그램을 보면, API Server 는 쿠버네티스 컨트롤 플레인 노드에 있게 된다. 그리고 로드 밸런서를 통해서 워커 노드와 통신을 하게 된다. 이뿐만 아니라 API Server 는 외부에 클라이언트와 Kubectl 을 통해서 통신도 해야 한다.
이를 위해서 인증서에는 쿠버네티스 컨트롤 플레인 서버들과 로드밸런서에 대해 각각 인증이 가능해야 한다.
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 |
$ DOMAIN=systemv.local $ KUBERNETES_HOSTNAMES=kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.svc.cluster.local $ cat > kubernetes-csr.json <<EOF { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=haproxy.${DOMAIN},192.168.96.7,192.168.96.46,192.168.96.47,192.168.96.48,127.0.0.1,10.32.0.1,${KUBERNETES_HOSTNAMES} \ -profile=kubernetes \ kubernetes-csr.json | cfssljson -bare kubernetes |
-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 문서에 설명된대로 키 페어를 사용하여 서비스 계정 토큰을 생성하고 서명합니다.
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 |
$ cat > service-account-csr.json <<EOF { "CN": "service-accounts", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF $ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ service-account-csr.json | cfssljson -bare service-account 2021/04/16 12:26:10 [INFO] generate received request 2021/04/16 12:26:10 [INFO] received CSR 2021/04/16 12:26:10 [INFO] generating key: rsa-2048 2021/04/16 12:26:10 [INFO] encoded CSR 2021/04/16 12:26:10 [INFO] signed certificate with serial number 657346270815993169700676646594372924490206343750 2021/04/16 12:26:10 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for websites. For more information see the Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); specifically, section 10.2.3 ("Information Requirements"). |
이제 필요한 인증서는 다 작성되었다.
인증서 배포
워커 노드에는 ca, kubelet 클라이언트 인증서, kubelet 클라이언트 키를 노드에 복사해 준다.
1 2 3 |
$ scp ca.pem kworker1.pem kworker1-key.pem kube-proxy.pem kube-proxy-key.pem kworker1.systemv.local:./ $ scp ca.pem kworker2.pem kworker2-key.pem kube-proxy.pem kube-proxy-key.pem kworker2.systemv.local:./ $ scp ca.pem kworker3.pem kworker3-key.pem kube-proxy.pem kube-proxy-key.pem kworker3.systemv.local:./ |
컨트롤 플레인 노드에는 ca, ca key, apiserver 인증서, apiserver key, service account key pair 를 노드에 복사해 준다.
1 2 3 |
$ scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem service-account-key.pem service-account.pem kmaster1.systemv.local:./ $ scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem service-account-key.pem service-account.pem kmaster2.systemv.local:./ $ scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem service-account-key.pem service-account.pem kmaster3.systemv.local:./ |
클라이언트 인증 설정
controller manager, kubelet, kube-proxy, scheduler 클라이언트 그리고 admin 사용자를 위한 kubeconfig 파일을 생성해준다.
쿠버네티스 공인 IP 주소
kubeconfig 는 쿠버네티스 API Server 에 접속이 필요하다. 고가용성을 위해서 API Server 앞에 외부 로드 밸런서에 IP 주소를 할당해서 사용했다. 이것은 아키텍쳐 다이어그램에서 로드밸런서가 쿠버네티스의 공인 IP가 되며 여기는 HAProxy 의 IP 주소다.
1 |
$ KUBERNETES_PUBLIC_ADDRESS=192.168.96.7 |
kubelet 쿠버네티스 설정 파일
Kubelets 를 위한 kubeconfig 파일을 생성할때, Kubelet의 노드 이름과 일치하는 클라이언트 인증서를 사용해야 한다. 이것은 쿠버네티스 Node Authorizer 로 인증할 수 있도록 해준다.
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 |
$ DOMAIN=systemv.local $ for instance in kworker1 kworker2 kworker3; do kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \ --kubeconfig=${instance}.kubeconfig kubectl config set-credentials system:node:${instance}.${DOMAIN} \ --client-certificate=${instance}.pem \ --client-key=${instance}-key.pem \ --embed-certs=true \ --kubeconfig=${instance}.kubeconfig kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:node:${instance}.${DOMAIN} \ --kubeconfig=${instance}.kubeconfig kubectl config use-context default --kubeconfig=${instance}.kubeconfig done Cluster "kubernetes-the-hard-way" set. User "system:node:kworker1" set. Context "default" created. Switched to context "default". Cluster "kubernetes-the-hard-way" set. User "system:node:kworker2" set. Context "default" created. Switched to context "default". Cluster "kubernetes-the-hard-way" set. User "system:node:kworker3" set. Context "default" created. Switched to context "default". |
kube-proxy 쿠버네티스 설정 파일
kube-proxy 서비스를 위한 설정 파일을 생성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \ --kubeconfig=kube-proxy.kubeconfig Cluster "kubernetes-the-hard-way" set. $ kubectl config set-credentials system:kube-proxy \ --client-certificate=kube-proxy.pem \ --client-key=kube-proxy-key.pem \ --embed-certs=true \ --kubeconfig=kube-proxy.kubeconfig User "system:kube-proxy" set. $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-proxy \ --kubeconfig=kube-proxy.kubeconfig Context "default" created. $ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig Switched to context "default". |
kube-proxy.kubeconfig 파일이 생성된다.
kube-controller-manager 쿠버네티스 설정 파일
kube-controller-manager 서비스를 위한 kubeconfig 파일을 생성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=kube-controller-manager.kubeconfig Cluster "kubernetes-the-hard-way" set. $ kubectl config set-credentials system:kube-controller-manager \ --client-certificate=kube-controller-manager.pem \ --client-key=kube-controller-manager-key.pem \ --embed-certs=true \ --kubeconfig=kube-controller-manager.kubeconfig User "system:kube-controller-manager" set. $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-controller-manager \ --kubeconfig=kube-controller-manager.kubeconfig Context "default" created. $ kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig Switched to context "default". |
kube-controller-manager.kubeconfig 파일이 생성된다.
kube-scheduler 쿠버네티스 설정 파일
kube-scheduler 서비스를 위한 kubeconfig 파일을 생성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=kube-scheduler.kubeconfig Cluster "kubernetes-the-hard-way" set. $ kubectl config set-credentials system:kube-scheduler \ --client-certificate=kube-scheduler.pem \ --client-key=kube-scheduler-key.pem \ --embed-certs=true \ --kubeconfig=kube-scheduler.kubeconfig User "system:kube-scheduler" set. $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=system:kube-scheduler \ --kubeconfig=kube-scheduler.kubeconfig Context "default" created. $ kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig Switched to context "default". |
kube-scheduler.kubeconfig 파일이 생성된다.
admin 사용자를 위한 kubeconfig 파일
admin 사용자를 위한 kubeconfig 파일을 생성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://127.0.0.1:6443 \ --kubeconfig=admin.kubeconfig Cluster "kubernetes-the-hard-way" set. $ kubectl config set-credentials admin \ --client-certificate=admin.pem \ --client-key=admin-key.pem \ --embed-certs=true \ --kubeconfig=admin.kubeconfig User "admin" set. $ kubectl config set-context default \ --cluster=kubernetes-the-hard-way \ --user=admin \ --kubeconfig=admin.kubeconfig Context "default" created. $ kubectl config use-context default --kubeconfig=admin.kubeconfig Switched to context "default". |
admin.kubeconfig 파일이 생성된다.
생성된 파일을 배포
워커 노드에는 node.kubeconfig 와 kube-proxy.kubeconfig 파일을 복사해 준다.
1 2 3 |
$ scp kworker1.kubeconfig kube-proxy.kubeconfig kworker1.systemv.local:./ $ scp kworker2.kubeconfig kube-proxy.kubeconfig kworker2.systemv.local:./ $ scp kworker3.kubeconfig kube-proxy.kubeconfig kworker3.systemv.local:./ |
컨트롤 플레인 노드에는 admin, kube-controller-manager, kube-scheduler 에 kubeconfig 파일을 배포해 준다.
1 2 3 |
$ scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig kmaster1.systemv.local:./ $ scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig kmaster2.systemv.local:./ $ scp admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig kmaster3.systemv.local:./ |
데이터 암호화 설정 및 키 생성
쿠버네티스트는 클러스터 상태, 애플리케이션 설정들, secret 등 다양한 데이터를 저장한다. 쿠버네티스는 클러스터 데이터를 암호화를 제공한다.
쿠버네티스 Secret 의 암호화를 위한 암호화 설정과 암호화 키 생성을 위한 설정을 작성해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ ENCRYPTION_KEY=$(head -c 32 /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 명령어로 패키지 설치 했다. 설정은 다음과 같이 간단하게 해줬다.
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 refresh 30 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.local 192.168.96.46:6443 check server kmaster2.systemv.local 192.168.96.47:6443 check server kmaster3.systemv.local 192.168.96.48:6443 check $ sudo systemctl restart haproxy |
etcd 클러스터 설치
etcd 는 key-value 로 데이터를 저장해주는 서버다. 쿠버네티스는 자체적인 데이터를 하나도 가지고 있지 않다. 전부다 외부에 데이터를 저장하는데 그것을 해주는 것이 etcd 다. 서버 한대만 가지고 데이터를 저장하는 것은 위험함으로 이것도 클러스터로 여러 서버를 하나로 묶는다.
다운로드 및 설치
1 2 3 4 |
$ wget -q --timestamping "https://github.com/etcd-io/etcd/releases/download/v3.4.15/etcd-v3.4.15-linux-amd64.tar.gz" $ tar xvzf etcd-v3.4.15-linux-amd64.tar.gz $ chmod +x etcd-v3.4.15-linux-amd64/etcd* $ sudo mv etcd-v3.4.15-linux-amd64/etcd* /usr/local/bin/ |
etcd 는 클러스터내에 서버들과 kmaster, kworker 서버들과 통신을 해야 한다. 하지만 쿠버네티스는 TLS 통신을 기반으로 하기 때문에 인증서를 함께 설치해 줘야 한다. 모든 서버와 통신을 위한 인증서로 Root CA 인증서와 kubernetes 인증서를 설치해 준다.
1 2 |
$ sudo mkdir -p /etc/etcd /var/lib/etcd $ sudo cp -v ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/ |
ubuntu20.04, centos 7 서버의 경우에 systemd 가 핵심이다. etcd 를 서비스 등록을 위해서 systemd 를 이용한다. 이 서버는 kmaster1 서버에 실행해준다.
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 |
$ INTERNAL_IP=$(hostname --ip-address) $ ETCD_NAME=$(hostname -s) $ cat <<EOF | sudo tee /etc/systemd/system/etcd.service [Unit] Description=etcd Documentation=https://github.com/coreos [Service] ExecStart=/usr/local/bin/etcd \\ --name ${ETCD_NAME} \\ --cert-file=/etc/etcd/kubernetes.pem \\ --key-file=/etc/etcd/kubernetes-key.pem \\ --peer-cert-file=/etc/etcd/kubernetes.pem \\ --peer-key-file=/etc/etcd/kubernetes-key.pem \\ --trusted-ca-file=/etc/etcd/ca.pem \\ --peer-trusted-ca-file=/etc/etcd/ca.pem \\ --peer-client-cert-auth \\ --client-cert-auth \\ --initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\ --listen-peer-urls https://${INTERNAL_IP}:2380 \\ --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\ --advertise-client-urls https://${INTERNAL_IP}:2379 \\ --initial-cluster-token etcd-cluster-0 \\ --initial-cluster kmaster1=https://${IP kmaster1}:2380,kmaster2=https://${IP master2}:2380,kmaster3=https://${IP master3}:2380 \\ --initial-cluster-state new \\ --data-dir=/var/lib/etcd Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF $ sudo systemctl daemon-reload $ sudo systemctl enable etcd.service --now |
INTERNAL IP 는 kmaster1 서버의 IP, ETCD_NAME 은 kmaster1 이며 각각의 kmaster1, kmater2, kmaster3 에 IP를 적어주면 된다. 각각 kmaster 에서 INTERNAL IP 와 ETC_NAME 을 바꿔가면서 해주면 된다.
모두 정상적으로 설치가 되었다면 다음과 같이 확인할 수 있다.
1 2 3 4 5 6 7 8 |
$ sudo ETCDCTL_API=3 /usr/local/bin/etcdctl member list \ > --endpoints=https://127.0.0.1:2379 \ > --cacert=/etc/etcd/ca.pem \ > --cert=/etc/etcd/kubernetes.pem \ > --key=/etc/etcd/kubernetes-key.pem 5b11cfa779be5990, started, kmaster3, https://192.168.96.48:2380, https://192.168.96.48:2379, false 735f2a33dbd756c4, started, kmaster1, https://192.168.96.46:2380, https://192.168.96.46:2379, false 9066c1507a7e5374, started, kmaster2, https://192.168.96.47:2380, https://192.168.96.47:2379, false |
ETCD 클러스터 설치와 설정으로 이것으로 끝이다.
Controll Plane(Master) 설치
컨트롤 플레인(Controll Plane) 혹은 마스터(Master) 노드는 쿠버네티스에 두뇌에 해당한다. 모든 명령과 연산은 모두 컨트롤 플레인에서 이루어진다. 클라이언트의 명령은 컨트롤 플레인의 api server 에서 받아서 워커 노드에 kubelet 이 실행하는 형태다. 데이터는 모두 etcd 에 저장된다.
컨트롤 플레인의 주요 구성요소는 다음과 같다.
- kube-apiserver
- kube-controller-manager
- kube-scheduler
- kubectl
다운로드 및 설치
바이너리 파일을 받아야 한다. github 에는 소스코드만 올라와 있어서 어디서 받을지 잠시 헷깔렸는데, dowloadkubernetes 에서 받을 수 있었다.
1 2 3 4 5 6 |
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kube-apiserver" $ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kube-controller-manager" $ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kube-scheduler" $ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" $ chmod +x kube-apiserver kube-controller-manager kuber-scheduler kubectl $ sudo mv -vv kube-apiserver kube-controller-manager kuber-scheduler kubectl /usr/local/bin/ |
쿠버네티스 API Server 설정
API Server 설정을 해보도록 하자. 쿠버네티스의 내부적인 데이터 이동은 암호화 통신을 기반으로 하기 때문에 인증서를 항상 달고 산다고 보면 된다.
1 2 3 4 |
$ sudo mkdir -p /var/lib/kubernetes/ $ sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \ service-account-key.pem service-account.pem \ encryption-config.yaml /var/lib/kubernetes/ |
이제 systemd 유닛 등록을 위한 파일을 다음과 같이 작성해 준다.
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 39 40 41 42 43 44 45 46 47 48 |
$ INTERNAL_IP=$(hostname --ip-address) $ cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service [Unit] Description=Kubernetes API Server Documentation=https://github.com/kubernetes/kubernetes [Service] ExecStart=/usr/local/bin/kube-apiserver \\ --advertise-address=${INTERNAL_IP} \\ --allow-privileged=true \\ --apiserver-count=3 \\ --audit-log-maxage=30 \\ --audit-log-maxbackup=3 \\ --audit-log-maxsize=100 \\ --audit-log-path=/var/log/audit.log \\ --authorization-mode=Node,RBAC \\ --bind-address=0.0.0.0 \\ --client-ca-file=/var/lib/kubernetes/ca.pem \\ --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\ --enable-swagger-ui=true \\ --etcd-cafile=/var/lib/kubernetes/ca.pem \\ --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\ --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\ --etcd-servers=https://${IP kmaster1}:2379,https://${IP master2}:2379,https://${IP master3}:2379 \\ --event-ttl=1h \\ --experimental-encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\ --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\ --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\ --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\ --kubelet-https=true \\ --runtime-config=api/all=true \\ --service-account-signing-key-file=/var/lib/kubernetes/service-account-key.pem \\ --service-account-issuer=api \\ --service-account-api-audiences=api,vault \\ --service-account-key-file=/var/lib/kubernetes/service-account.pem \\ --service-cluster-ip-range=10.32.0.0/24 \\ --service-node-port-range=30000-32767 \\ --tls-cert-file=/var/lib/kubernetes/kubernetes.pem \\ --tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \\ --v=2 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF $ sudo systemctl daemon-reload $ sudo systemctl enable kube-apiserver.service --now |
여기서 –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 파일이 필요하다. 이를 복사해 준다.
1 |
$ sudo mv -vv kube-controller-manager.kubeconfig /var/lib/kubernetes/ |
kube-controller-manager.service 파일을 다음과 같이 작성해 준다.
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 |
$ cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service [Unit] Description=Kubernetes Controller Manager Documentation=https://github.com/kubernetes/kubernetes [Service] ExecStart=/usr/local/bin/kube-controller-manager \\ --address=0.0.0.0 \\ --allocate-node-cidrs=true \\ --cluster-cidr=10.31.0.0/16 \\ --cluster-name=kubernetes \\ --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\ --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\ --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\ --leader-elect=true \\ --root-ca-file=/var/lib/kubernetes/ca.pem \\ --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\ --service-cluster-ip-range=10.32.0.0/24 \\ --use-service-account-credentials=true \\ --v=2 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF $ sudo systemctl daemon-reload $ sudo systemctl enable kube-controller-manager.service --now |
–cluster-cidr 은 kubeadm 을 이용해 설치할때에 –pod-network-cidr 에 해당한다.
쿠버네티스 Scheduler 설정
kubeconfig 를 복사 해준다.
1 |
sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/ |
kube-scheduler 를 위한 설정파일을 다음과 같이 작성해 준다.
1 2 3 4 5 6 7 8 9 |
$ sudo mkdir -p /etc/kubernetes/config $ cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml apiVersion: kubescheduler.config.k8s.io/v1beta1 kind: KubeSchedulerConfiguration clientConnection: kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig" leaderElection: leaderElect: true EOF |
kube-scheduler.service 유닛 파일을 다음과 같이 작성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ sudo cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service [Unit] Description=Kubernetes Scheduler Documentation=https://github.com/kubernetes/kubernetes [Service] ExecStart=/usr/local/bin/kube-scheduler \\ --config=/etc/kubernetes/config/kube-scheduler.yaml \\ --v=2 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF $ sudo systemctl daemon-reload $ sudo systemctl enable kube-scheduler.service --now |
Kubelet 인증을 위한 RBAC
쿠버네티스는 kubectl 을 이용해 명령을 받는다. 그러면 api server 에서 받고, 이것을 워커 서버에 kubelet 이 실행시키는 구조다. 문제는 api server 가 워커 노드에 kubelet 에 액세스를 할려면 RBAC 권한이 필요하다는 데 있다. RBAC(Role-Based Access Control) 는 사용자 역할 기반 접근 제어 방법이다.
system:kube-apiserver-to-kubelet ClusterRole 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" labels: kubernetes.io/bootstrapping: rbac-defaults name: system:kube-apiserver-to-kubelet rules: - apiGroups: - "" resources: - nodes/proxy - nodes/stats - nodes/log - nodes/spec - nodes/metrics verbs: - "*" EOF |
쿠버네티스 API 서버는 –kubelet-client-certificate 플래그에 정의된 클라이언트 인증서를 사용해 kubernetes 사용자로 Kubelet 에 인증한다.
앞에서 생성한 ClusterRole 을 kubernetes 사용자에게 바인딩(binding) 해준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system:kube-apiserver namespace: "" roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:kube-apiserver-to-kubelet subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: kubernetes EOF |
확인
컨트롤 플레인이 잘 설정 되었는지 다음과 같이 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ kubectl get componentstatuses --kubeconfig admin.kubeconfig -o yaml | egrep "kind|name|message" Warning: v1 ComponentStatus is deprecated in v1.19+ - message: ok kind: ComponentStatus name: controller-manager - message: ok kind: ComponentStatus name: scheduler - message: '{"health":"true"}' kind: ComponentStatus name: etcd-0 - message: '{"health":"true"}' kind: ComponentStatus name: etcd-2 - message: '{"health":"true"}' kind: ComponentStatus name: etcd-1 kind: List |
로드 밸런서에서 응답이 정상으로 나오는지도 확인할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 |
$ curl --cacert /var/lib/kubernetes/ca.pem https://haproxy.systemv.local:6443/version { "major": "1", "minor": "20", "gitVersion": "v1.20.6", "gitCommit": "8a62859e515889f07e3e3be6a1080413f17cf2c3", "gitTreeState": "clean", "buildDate": "2021-04-15T03:19:55Z", "goVersion": "go1.15.10", "compiler": "gc", "platform": "linux/amd64" } |
Worker 노드 설치
이제 워커 노드 작업을 진행 해야 한다. 여기서 한가지 결정해야 한다. 인터넷에 메뉴얼들을 보면 워커 노드의 컨테이너엔진을 RunC, Docker 등으로 나뉘어 있다. 여기서는 Docker 를 사용하는 것으로 진행 했다.
필수 패키지 및 Docker 설치
다음과 같이 필수 패키지를 설치해 준다. socat 은 kubectl port-forward 명령을 쓸때 사용한다고 한다.
1 2 3 |
$ sudo apt clean all $ sudo apt update $ sudo apt install socat conntrack ipset |
다음과 같이 Docker 를 설치 해준다. Docker 설치는 공식 메뉴얼 대로 해줬다.
1 2 3 4 5 6 7 8 9 10 11 12 |
$ sudo apt install \ apt-transport-https \ ca-certificates \ curl \ gnupg \ lsb-release $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg $ echo \ "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null $ sudo apt update $ sudo apt install docker-ce docker-ce-cli containerd.io |
일반 사용자도 Docker 를 이용할 수 있도록 해준다.
1 |
$ sudo usermod -aG docker systemv |
시스템 설정을 해준다. 이 설정은 쿠버네티스 문서에 나와 있다.
1 2 3 4 5 6 7 8 9 |
$ sudo cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf br_netfilter EOF $ sudo cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF $ sudo sysctl --system |
kubectl, kubelet, kube-proxy 설치.
워커 노드 작동을 위한 바이너리 다운로드 및 설치해 준다.
1 2 3 4 5 |
$ curl -LO https://dl.k8s.io/v1.20.6/bin/linux/amd64/kubectl $ curl -LO https://dl.k8s.io/v1.20.6/bin/linux/amd64/kubelet $ curl -LO https://dl.k8s.io/v1.20.6/bin/linux/amd64/kube-proxy $ chmod +x kubectl kube-proxy kubelet $ sudo mv kubectl kube-proxy kubelet /usr/local/bin |
설치 디렉토리를 생성해 준다.
1 2 3 4 |
$ sudo mkdir -p /var/lib/kubelet \ /var/lib/kube-proxy \ /var/lib/kubernetes \ /var/run/kubernetes |
kubelet 설정
1 2 3 |
$ sudo mv -vv kworker1-key.pem kworker1.pem /var/lib/kubelet/ $ sudo mv -vv kworker1.kubeconfig /var/lib/kubelet/kubeconfig $ sudo mv ca.pem /var/lib/kubernetes/ |
kubelet 설정 파일을 작성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration authentication: anonymous: enabled: false webhook: enabled: true x509: clientCAFile: "/var/lib/kubernetes/ca.pem" authorization: mode: Webhook clusterDomain: "cluster.local" clusterDNS: - "10.32.0.10" runtimeRequestTimeout: "15m" tlsCertFile: "/var/lib/kubelet/kworker1.pem" tlsPrivateKeyFile: "/var/lib/kubelet/kworker1-key.pem" EOF |
kubelet를 위한 systemd 유닛 파일을 작성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
$ cat <<EOF | sudo tee /etc/systemd/system/kubelet.service [Unit] Description=Kubernetes Kubelet Documentation=https://github.com/kubernetes/kubernetes After=containerd.service Requires=containerd.service [Service] ExecStart=/usr/local/bin/kubelet \\ --config=/var/lib/kubelet/kubelet-config.yaml \\ --container-runtime=docker \\ --image-pull-progress-deadline=2m \\ --kubeconfig=/var/lib/kubelet/kubeconfig \\ --network-plugin=cni \\ --register-node=true \\ --v=2 Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF |
kube-proxy 설정
proxy 설정 파일을 복사해 준다.
1 |
$ sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig |
kube-proxy-config.yaml 설정 파일을 만들어 준다.
1 2 3 4 5 6 7 8 |
$ cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration clientConnection: kubeconfig: "/var/lib/kube-proxy/kubeconfig" mode: "iptables" clusterCIDR: "10.31.0.0/16" EOF |
마지막으로 systemd 유닛 파일을 생성해 준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$ cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service [Unit] Description=Kubernetes Kube Proxy Documentation=https://github.com/kubernetes/kubernetes [Service] ExecStart=/usr/local/bin/kube-proxy \\ --config=/var/lib/kube-proxy/kube-proxy-config.yaml Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target EOF |
서비스를 기동해 준다.
1 2 |
$ sudo systemctl daemon-reload $ sudo systemctl enable kubelet.service kube-proxy.serivce --now |
이렇게 한 후에 kmaster1 서버에서 다음과 같이 노드를 확인해보자.
1 2 3 4 5 |
$ kubectl get nodes --kubeconfig admin.kubeconfig NAME STATUS ROLES AGE VERSION kworker1.systemv.local NotReady <none> 102m v1.20.6 kworker2.systemv.local NotReady <none> 65m v1.20.6 kworker3.systemv.local NotReady <none> 53m v1.20.6 |
NotReady 로 나와야 한다. 그리고 kubelet 로그에는 cni network 가 없다는 메시지가 올라 온다.
CNI 네트워크 설치
CNI 네트워크까지 수동으로 설치할려면 머리가 아프다. 그래서 그냥 간단하게 이것을 pod 컨테이너로 구현하면 된다. 그것도 제공해주는 yaml 파일을 이용해서.
여기서는 Calico 를 이용했다. 50 node 이하에서 사용할 경우를 가정했다. 50 노드 이상이면 Typha 를 설치해줘야 한다.
1 2 |
$ curl https://docs.projectcalico.org/manifests/calico.yaml -O $ kubectl apply -f calico.yaml |
시간이 좀 지난후에 노드를 살펴보고 Ready 가 되어 있을 것이다.
원격 접속을 위한 kubectl 설정
지금까지 kubectl 명령어를 사용할려면 admin.kubeconfig 파일이 있어야 했다. 이 설정파일은 –server=https://127.0.0.1:6443 으로 로컬 호스트 api server 를 지칭하도록 만들어 졌다. 만일 kubectl 명령어를 외부에서 사용할려면 어떻게 해야할까?
admin 사용자를 위한 설정을 다시 해주면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$ kubectl config set-cluster kubernetes-the-hard-way \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=https://haproxy.systemv.local:6443 Cluster "kubernetes-the-hard-way" set. $ kubectl config set-credentials admin \ --client-certificate=admin.pem \ --client-key=admin-key.pem User "admin" set. $ kubectl config set-context kubernetes-the-hard-way \ --cluster=kubernetes-the-hard-way \ --user=admin Context "kubernetes-the-hard-way" created. $ kubectl config use-context kubernetes-the-hard-way Switched to context "kubernetes-the-hard-way". $ kubectl get nodes NAME STATUS ROLES AGE VERSION kworker1.systemv.local Ready <none> 61m v1.20.6 kworker2.systemv.local Ready <none> 24m v1.20.6 kworker3.systemv.local Ready <none> 12m v1.20.6 |
위 내용을 자세히 보면 kubeconfig 파일을 작성할때에 어느 파일에 저장할지에 대한 –kubeconfig=admin.kubeconfig 옵션이 존재하지 않는다. 이렇게 될 경우에 사용자의 홈디렉토리에 .kube/config 파일에 저장이 된다.
그리고 kubectl 명령어는 명시적으로 –kubeconfig admin.kubeconfig 를 주지 않는 이상 .kube/config 파일을 자동으로 읽어서 수행하게 된다.
Kube-dns 설치.
kube-dns 는 pods 에서 도메인 네임을 처리해 주는 서비스라고 보면 된다. pods 도메인을 할당라고 리절빙을 해준다.
1 2 3 4 5 6 7 |
$ curl -LO https://storage.googleapis.com/kubernetes-the-hard-way/kube-dns.yaml $ vim kube-dns.yaml .... apiVersion: apps/v1 kind: Deployment .... $ kubectl apply -f kube-dns.yaml |
중간에 apiVersion: extension/v1 이라고 되어 있는 것을 위와 같이 바꿔 준다.
1 2 3 |
$ kubectl get pods -l k8s-app=kube-dns -n kube-system NAME READY STATUS RESTARTS AGE kube-dns-7854dfc8c7-d69g5 3/3 Running 0 7m37s |
정상적으로 작동되는 것을 볼 수 있다.
이로써 모든 설치가 완료 됐다.