CentOS7上手動部署入門級kubernetes


前言

翻看了很多的kubernetes的安裝教程,也反復做了一些實驗,深感教程之復雜,所以決定寫一個極簡版本的安裝教程,目標在於用盡可能少的參數啟動服務,並且剖析各組件關系,然后再在此基礎上逐步添加參數,實現功能完備;

干什么

  • 在server節點上啟動三個主要服務:apiserver、controller-manager、scheduler;
  • 在node節點上啟動兩個主要服務:kubelet、kube-proxy;
  • 試驗啟動容器,且測試容器內服務的功能;

不干什么

  • 不通過軟件源安裝;
  • 不添加systemd服務;
  • 盡量不做安全配置;不做bootstrap流程,不用kubeadm創建集群;
  • 不做高可用配置;
  • 不用ipvs做負載均衡;
  • 不裝任何插件(dns, dashboard, etc);

說明:

  1. 如果通過軟件源安裝,看似會簡化部署流程,但事實上會引入一些別的問題,因為一方面解決版本配套要添加合適的源比較麻煩,另一方面發布包在操作系統上做的一些配置也很復雜,為迎合它也需要做一些復雜的工作;(其實就是源里的東西版本低了不想用這種大實話我是絕對不會說的)
  2. 個人感覺安全配置是提高了kubernetes入門門檻的關鍵原因,本身安全認證授權就是非常復雜的機制,與業務獨立,一般人較少接觸,任何一個新入門kubernetes的人在瞎子摸象之前要先搞明白這些東西就要花很多功夫;
    但是,非安全的服務端口正在被deperecate,看一下今年4月社區contributer的表態,自己感受一下吧,https://github.com/kubernetes/kubernetes/pull/59018#issuecomment-381583629
    未來這一部分可能是造成本文失效的最主要原因; 

准備

架構概述

先大概知道一下架構,借用一張圖,來源:https://www.kubernetes.org.cn/4047.html

版本及依賴

etcd 3.3.8
docker 18.03.1-ce
flannel 0.10.0
kubernetes 1.10.5

etcd是一個基礎組件,沒有太復雜的依賴關系,沒什么好說的;

docker見之前的docker安裝流程

flannel見之前的flannel安裝流程

下載程序包

到官方changelog里找downloads:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.10.md

只下載server和node包就夠了,client包只有一個kubectl工具,在前兩個里都有帶;

Server部署

server有三個組件,apiserver是接口服務,對接etcd,做存儲邏輯的封裝,后面跟着controller-manager和scheduler,做一些后台控制邏輯;

整個server做的事情全都圍繞etcd轉,所以根本不需要系統的root權限,普通用戶足夠;

安裝server

解壓server包,程序都解壓到了kubernetes/server/bin路徑:

$ tar vfxz kubernetes-server-linux-amd64.tar.gz # 解壓
$ ls kubernetes/server/bin #看一下
$ cd kubernetes/server # 就在這個路徑下做server服務的管理

啟動apiserver

bin/kube-apiserver \
--cert-dir=etc/kubernetes/cert \
--insecure-bind-address=0.0.0.0 \ --insecure-port=18080 \ --service-cluster-ip-range=10.0.0.0/16 \ --etcd-servers=http://<etcd>:2379 \ --logtostderr=true

參數說明:

  1. --cert-dir:雖然我不想做安全配置,但奈何kubernetes的https server是必選項,一定要有TLS證書和密鑰才行,如果沒有提供自定義的證書密鑰,就會自動創建一個;如果不設這個參數,證書和密鑰會創建到/var/run/kubernetes下;
  2. --insecure-bind-address,--insecure-port:非安全服務地址和端口,以后會廢棄;
  3. --service-cluster-ip-range:service cluster ip是提供給service的虛擬ip,因而這里應當使用一個虛擬段,以免與物理ip段混用造成路由混亂;
  4. --etcd-servers:kubernetes強依賴etcd服務; 

生成的文件及數據

apiserver啟動后,在cert-dir下出現了自動創建的證書和密鑰:

$ file etc/kubernetes/cert/*
etc/kubernetes/cert/apiserver.crt: PEM certificate
etc/kubernetes/cert/apiserver.key: PEM RSA private key

在etcd上出現了一些數據:

# ETCDCTL_API=3 etcdctl get / --prefix --keys-only
/registry/apiregistration.k8s.io/apiservices/v1.

/registry/apiregistration.k8s.io/apiservices/v1.apps

/registry/apiregistration.k8s.io/apiservices/v1.authentication.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.authorization.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.autoscaling

/registry/apiregistration.k8s.io/apiservices/v1.batch

/registry/apiregistration.k8s.io/apiservices/v1.networking.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.rbac.authorization.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1.storage.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.admissionregistration.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.apiextensions.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.apps

/registry/apiregistration.k8s.io/apiservices/v1beta1.authentication.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.authorization.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.batch

/registry/apiregistration.k8s.io/apiservices/v1beta1.certificates.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.events.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.extensions

/registry/apiregistration.k8s.io/apiservices/v1beta1.policy

/registry/apiregistration.k8s.io/apiservices/v1beta1.rbac.authorization.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta1.storage.k8s.io

/registry/apiregistration.k8s.io/apiservices/v1beta2.apps

/registry/apiregistration.k8s.io/apiservices/v2beta1.autoscaling

/registry/namespaces/default

/registry/namespaces/kube-public

/registry/namespaces/kube-system

/registry/ranges/serviceips

/registry/ranges/servicenodeports

/registry/services/endpoints/default/kubernetes

/registry/services/specs/default/kubernetes

忽略掉前面的/register/apixxx,來看一下后面這些東西:

  • 三個namespace:default、kube-public、kube-system;
  • /register/ranges/serviceips:內容包含10.0.0.0/16,與--service-cluster-ip-range一致;
  • /registry/ranges/servicenodeports:內容包含30000-32767,查了一下代碼,這個值是--service-node-port-range參數的默認值,見https://github.com/kubernetes/kubernetes/blob/release-1.10/pkg/kubeapiserver/options/options.go
  • /registry/services/<endpoints,specs>/default/kubernetes:描述了同一個service(kubernetes)的endpoints信息及specs信息,endpoints內容可見server的外網ip,specs內容可見10.0.0.1,這是service-cluster-ip-range的第一個ip地址;

使用kubectl查看信息

apiserver提供了restapi以獲取和管理在etcd上的集群狀態信息;kubectl就是這個restapi的客戶端;

因為我們的服務端口不在默認的8080上,所以使用時要加一個-s參數:

查看namespace:

$ bin/kubectl -s 127.0.0.1:18080 get ns # 查看三個namespace,與etcd的/registry/namespaces對應
NAME          STATUS    AGE
default       Active    1h
kube-public   Active    1h
kube-system   Active    1h

查看service:

$ bin/kubectl -s 127.0.0.1:18080 get svc # 查看service,與/registry/services/specs/default/kubernetes對應
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.0.0.1     <none>        443/TCP   1h

查看endpionts:

$ bin/kubectl -s 127.0.0.1:18080 get ep #查看endpoints,與/registry/services/endpoints/default/kubernetes對應
NAME         ENDPOINTS            AGE
kubernetes   xxxxx:6443   1h

啟動controller-manager

注:這個命令雖然可以啟動controller-manager,但后續會出現一些問題,需要再添加參數,見“測試”部分對controller-manaer的調整

bin/kube-controller-manager \
        --master=127.0.0.1:18080 \
        --logtostderr=true

生成的數據

查看etcd,多出現一些節點:

/registry/events/kube-system/kube-controller-manager.153c40f68e70e209

/registry/serviceaccounts/default/default

/registry/serviceaccounts/kube-public/default

/registry/serviceaccounts/kube-system/default

/registry/services/endpoints/kube-system/kube-controller-manager

多出一個event,三個serviceaccounts(三個namespace下的default),以及一個endpoints;

使用kubectl查看信息

event就不看了,來看一下service account:

$ bin/kubectl -s 127.0.0.1:18080 get sa
NAME      SECRETS   AGE
default   0         3m

這里的SECRETS為0,表明沒有為這個service account生成secret,這個也是與安全相關的東西,先忽略,后面會遇到問題,然后我們再回來處理它;

再看一下endpoint:

$ bin/kubectl -s 127.0.0.1:18080 get ep --namespace kube-system
NAME                      ENDPOINTS   AGE
kube-controller-manager   <none>      7m

因為kube-controller-manager在kube-system下,所以需要多加一個namespace參數;

啟動scheduler

bin/kube-scheduler \
        --master=127.0.0.1:18080

生成的數據

查看etcd,多出現一些節點:

/registry/events/kube-system/kube-scheduler.153c41b5b3052d28

/registry/services/endpoints/kube-system/kube-scheduler

使用kubectl查看信息

再看一下endpoints

$ bin/kubectl -s 127.0.0.1:18080 get ep --namespkube-system
NAME                      ENDPOINTS   AGE
kube-controller-manager   <none>      15m
kube-scheduler            <none>      1m

查看集群狀態

$ bin/kubectl -s 127.0.0.1:18080 get cs
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok                  
scheduler            Healthy   ok                  
etcd-0               Healthy   {"health":"true"}  

至此,server就算部署好了

node部署

本想node的兩個服務組件也用非root用戶的,但試下來發現不行,kubelet需要與docker交互,而kube-proxy則要改iptables,都需要提供root權限;

安裝node

解壓node包,程序都解壓到了kubernetes/node/bin路徑:

# tar vfxz kubernetes-node-linux-amd64.tar.gz #解壓
# ls kubernetes/node/bin/ # 看一下
# cd kubernetes/node # 就在這個路徑下進行node服務的管理

啟動kubelet

kubelet的參數太多了,可能為了簡化kubelet的啟動腳本吧,引入了兩個配置文件,兩個,兩個......而且除這兩個文件外還要設置其它參數,設置其它參數,其它參數......faint

先生成訪問apiserver需要用的kubeconfig文件:

# KUBE_APISERVER="http://<apiserver>:18080"
#
# bin
/kubectl config set-cluster kubernetes \ --server=$KUBE_APISERVER \ --kubeconfig=etc/kubernetes/kubelet.kubeconfig #
# bin
/kubectl config set-context default \ --cluster=kubernetes \ --user=default-noauth \ --kubeconfig=etc/kubernetes/kubelet.kubeconfig #
# bin
/kubectl config use-context default --kubeconfig=etc/kubernetes/kubelet.kubeconfig #
# bin
/kubectl config view --kubeconfig=etc/kubernetes/kubelet.kubeconfig apiVersion: v1 clusters: - cluster: server: http://<apiserver>:18080 name: kubernetes contexts: - context: cluster: kubernetes user: "" name: default current-context: default kind: Config preferences: {} users: []

這個配置文件就聲明了一個以http://<apiserver>:18080為入口的名為“kubernetes”的集群,以及一個匿名訪問“kubernetes”集群的名為“default”的上下文,並聲明使用這個"default"上下文;

寫kubelet自身需要的配置文件(這個文件可以是yaml或json格式,因為很多教程用了json格式,所以這里我用一下yaml格式):

# cat etc/kubernetes/kubelet.config.yaml 
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: cgroupfs

kind和apiVersion都是定死的,可見https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/,以及代碼的這里:https://github.com/kubernetes/kubernetes/blob/release-1.10/pkg/kubelet/apis/kubeletconfig/v1beta1/register.go

cgroupDriver需要與docker的真實情況相符,通過以下命令查看:

# docker info|grep 'Cgroup Driver'
Cgroup Driver: cgroupfs

啟動kubelet:

bin/kubelet \
  --cert-dir=etc/kubernetes/cert \
  --kubeconfig=etc/kubernetes/kubelet.kubeconfig \
  --config=etc/kubernetes/kubelet.config.yaml \
  --pod-infra-container-image=<registry>/rhel7/pod-infrastructure:latest \
  --runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice \
  --logtostderr=true

參數說明:

  1. --cert-dir:與apiserver的參數道理相同;kubernete強制生成;
  2. --kubeconfig:訪問apiserver需要的config;
  3. --config:kubelet自己需要的config;
  4. --pod-infra-container-image:一個基礎容器鏡像,做為pod的第一個容器啟動,為每個pod提供network namespace;沒啥業務邏輯,理論上說就是啟動之后保持不退出即可,以后我可以自己寫一個試試。默認鏡像地址可能被牆,所以想辦法下載一個下來(docker官方registry上有),然后推到私有的registry比較好;
  5. --runtime-cgroups,--kubelet-cgroups:可能是因為我手動啟動kubelet,而不是做為systemd的service啟動的緣故吧,啟動之后會時而出現這個錯誤:Failed to get system container stats for "/user.slice/user-0.slice/,加上這兩個參數就好了;

生成的文件及數據

在cert-dir下出現了自動創建的證書和密鑰:

# file etc/kubernetes/cert/*
etc/kubernetes/cert/kubelet.crt: PEM certificate
etc/kubernetes/cert/kubelet.key: PEM RSA private key

多說一句,這個證書是node自己簽發的,肯定得不到apiserver的認可;當然因為我在這里不做安全集群,這就無所謂;但在安全集群里,kubelet的證書會由controller-manager簽發;

在etcd上出現了一些新的數據,除去events外,就一個最重要的minions數據:

/registry/minions/<node>

使用kubectl查看信息

$ bin/kubectl -s 127.0.0.1:18080 get node
NAME         STATUS    ROLES     AGE       VERSION
<node>   Ready     <none>    38m       v1.10.5

啟動kube-proxy

bin/kube-proxy \
        --master=<apiserver>:18080 \
        --proxy-mode=iptables \
        --logtostderr=true

參數說明:

  1. proxy-mode:當前默認使用的就是iptables模式,ipvs還是experiment功能;如果iptables不適用,則回退到userspace模式下;

至此,node部署完成;

測試

啟動應用

到apiserver上,執行以下命令,運行一個標准的nginx容器,為了省時間,我也把它拉下來,push到私有registry上了:

$ bin/kubectl -s 127.0.0.1:18080 run nginx --image=<registry>/nginx/nginx --port=80

pod啟動失敗,報錯:No API token found for service account "default"

之前遺留了一個問題,見service_account_without_secrets

於是調整一下controller-manager的參數,加上--service-account-private-key-file和--root-ca-file參數,重啟controller-manager:

$ bin/kube-controller-manager \
        --master=127.0.0.1:18080 \
        --service-account-private-key-file=etc/kubernetes/cert/apiserver.key \
        --root-ca-file=etc/kubernetes/cert/apiserver.crt \
        --logtostderr=true

再看一下service account的情況,secrets已經不是0了:

$ bin/kubectl -s 127.0.0.1:18080 get sa
NAME      SECRETS   AGE
default   1         2h

再重新試一下啟動nginx,並查看狀態;

$ bin/kubectl -s 127.0.0.1:18080 run nginx --image=<registry>/nginx/nginx --port=80  # 啟動
$ $ bin
/kubectl -s 127.0.0.1:18080 get deploy # 查看deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE nginx 1 1 1 1 1m
$ $ bin
/kubectl -s 127.0.0.1:18080 describe deploy nginx|grep NewReplicaSet # 查看deploy詳情 Progressing True NewReplicaSetAvailable NewReplicaSet: nginx-55cc995fdb (1/1 replicas created)
$ $ bin
/kubectl -s 127.0.0.1:18080 describe replicasets nginx-55cc995fdb |tail -1 # 查看replicaset詳情 Normal SuccessfulCreate 10m replicaset-controller Created pod: nginx-55cc995fdb-27t7z
$ $ bin
/kubectl -s 127.0.0.1:18080 get pod nginx-55cc995fdb-27t7z -o wide # 查看pod NAME READY STATUS RESTARTS AGE IP NODE nginx-55cc995fdb-27t7z 1/1 Running 0 11m 172.10.63.2 <node>

再到node上看一下容器狀態:

# docker ps
CONTAINER ID        IMAGE                                           COMMAND                  CREATED             STATUS              PORTS               NAMES
3e5e69a69950        <registry>/nginx/nginx                       "nginx -g 'daemon of…"   28 seconds ago      Up 27 seconds                           k8s_nginx_nginx-55cc995fdb-27t7z_default_1ca63ddd-7ab5-11e8-be61-3440b59f0098_0
ec311fee295d        <registry>/rhel7/pod-infrastructure:latest   "/pod"                   28 seconds ago      Up 27 seconds                           k8s_POD_nginx-55cc995fdb-27t7z_default_1ca63ddd-7ab5-11e8-be61-3440b59f0098_0

看以看到,同時啟動了兩個容器,一個是pod-infrastructure,另一個是nginx;如果進入這兩個容器內看一下的話,會發現它們的ip地址是同一個;

總結一下啟動應用時這些資源的關系:

  • 一個應用會對應一個deploy資源; 
  • 一個deploy會對應一個replicaset資源;
  • 一個replicaset會對應多個(默認為一個)pod資源;
  • 一個pod會在某個node上啟動至少兩個container,其中一個是pod-infrastructure運行容器,另一個是應用所在的運行容器;它們共享ip地址;

暴露服務

如果啟動的應用本身是個服務的話,還需要將服務地址暴露出來,在server(master)上運行:

$ bin/kubectl -s 127.0.0.1:18080  expose deployment nginx --type=NodePort --name=example-service
service "example-service" exposed
$
$ bin/kubectl -s 127.0.0.1:18080 describe services example-service
Name:                     example-service
Namespace:                default
Labels:                   run=nginx
Annotations:              <none>
Selector:                 run=nginx
Type:                     NodePort
IP:                       10.0.158.97
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30152/TCP
Endpoints:                172.10.63.2:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

從后面的服務描述中得到以下信息:

  • service的cluster_ip是10.0.158.97,這個地址在apiserver啟動時的--service-cluster-ip-range參數范圍內,也即etcd上/register/ranges/serviceips節點內配置的信息;
  • service的nodeport是30152,這個端口在apiserver啟動時的--service-node-port-range參數范圍內,也即etcd上/registry/ranges/servicenodeports節點內配置的信息;
  • service的endpoints是172.10.63.2:80,這是ip地址是容器內的地址;

測試服務

在server(master)上訪問node的30152端口,nginx服務正常:

$ curl <node>:30152
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    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>

在node上訪問10.0.158.97:80,nginx也服務正常;查看一下node的iptables,會發現有一條規則將target為10.0.158.97:80的流量轉發到了本機的30152端口;這是kueb-proxy做的事情;

在另一個節點(可以不是kubernetes node,只要求啟用了與node相同的flanneld)或容器(可以不是kubernetes pod,只要求容器所在節點啟用了與node相同的flanneld)內訪問172.10.63.2:80,nginx也服務正常;這就是flanneld做的事情了;

 

重點參考以下三篇:

  • https://www.jianshu.com/p/8358117a23bb
  • https://github.com/opsnull/follow-me-install-kubernetes-cluster
  • https://jimmysong.io/kubernetes-handbook/practice/install-kubernetes-on-centos.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM