CentOS7上手動部署入門級kubernetes

轉自:http://www.javashuo.com/article/p-rotlkfse-db.html

前言

翻看了不少的kubernetes的安裝教程,也反覆作了一些實驗,深感教程之複雜,因此決定寫一個極簡版本的安裝教程,目標在於用盡量少的參數啓動服務,而且剖析各組件關係,而後再在此基礎上逐步添加參數,實現功能完備;html

幹什麼

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

不幹什麼

  • 不經過軟件源安裝;
  • 不添加systemd服務;
  • 儘可能不作安全配置;不作bootstrap流程,不用kubeadm建立集羣;
  • 不作高可用配置;
  • 不用ipvs作負載均衡;
  • 不裝任何插件(dns, dashboard, etc);

說明:node

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

準備

架構概述

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

版本及依賴

etcd 3.3.8
docker 18.03.1-ce
flannel 0.10.0
kubernetes 1.10.5

etcd是一個基礎組件,沒有太複雜的依賴關係,沒什麼好說的;nginx

docker見以前的docker安裝流程git

flannel見以前的flannel安裝流程github

下載程序包

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

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

Server部署

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

整個server作的事情全都圍繞etcd轉,因此根本不須要系統的root權限,普通用戶足夠;bootstrap

安裝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
相關文章
相關標籤/搜索