本节内容:

  • Kubernetes简介
  • 环境信息
  • 创建TLS加密通信的证书和密钥
  • 下载和配置 kubectl(kubecontrol) 命令行工具
  • 创建 kubeconfig 文件
  • 创建高可用 etcd 集群
  • 部署 kubernetes master节点
  • 部署kubernetes node节点
  • 安装和配置 kube-dns 插件

一、Kubernetes简介

Kubernetes是谷歌开源的容器集群管理系统,是Google多年大规模容器管理技术Borg的 开源版本,主要功能包括:

  • 基于容器的应用部署、维护和滚动升级
  • 负载均衡和服务发现
  • 跨机器和跨地区的集群调度
  • 自动伸缩
  • 无状态服务和有状态服务
  • 广泛的Volume支持
  • 插件机制保证扩展性

之前尝试使用kubeadm自动化部署集群,使用yum去安装kubeadm等工具,但是不翻墙的情况下,这种方式在国内几乎是不可能安装成功的。于是改为采用二进制文件部署Kubernetes集群,同时开启了集群的TLS安全认证。本篇实践是参照opsnull的文章《创建 kubernetes 各组件 TLS 加密通信的证书和秘钥》,结合公司的实际情况进行部署的。

二、环境信息

主机名 操作系统版本 IP地址 角色 安装软件
node1 CentOS 7.0 172.16.7.151 Kubernetes Master、Node etcd 3.2.7、kube-apiserver、kube-scheduler、kube-controller-manager、kubelet、kube-proxy、etcd 3.2.7、flannel 0.7.1、docker 1.12.6
node2 CentOS 7.0 172.16.7.152 Kubernetes Node kubelet、kube-proxy、flannel 0.7.1、etcd 3.2.7、docker 1.12.6
node3 CentOS 7.0 172.16.7.153 Kubernetes Node kubelet、kube-proxy、flannel 0.7.1、etcd 3.2.7、docker 1.12.6
spark32 CentOS 7.0 172.16.206.32 Harbor docker-ce 17.06.1、docker-compose 1.15.0、harbor-online-installer-v1.1.2.tar

spark32主机是harbor私有镜像仓库,关于harbor的安装部署见之前的博客《企业级Docker Registry——Harbor搭建和使用》。

三、创建TLS加密通信的证书和密钥

kubernetes各组件需要使用TLS证书对通信进行加密,这里我使用CloudFlare的PKI工具集 cfssl 来生成CA和其它证书。

生成的CA证书和密钥文件如下:

  • ca-key.pem
  • ca.pem
  • kubernetes-key.pem
  • kubernetes.pem
  • kube-proxy.pem
  • kube-proxy-key.pem
  • admin.pem
  • admin-key.pem

各组件使用证书的情况如下:

  • etcd:使用ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kube-apiserver:使用ca.pem、kubernetes-key.pem、kubernetes.pem;
  • kubelet:使用ca.pem;
  • kube-proxy:使用ca.pem、kube-proxy-key.pem、kube-proxy.pem;
  • kubectl:使用ca.pem、admin-key.pem、admin.pem

kube-controller、kube-scheduler当前需要和kube-apiserver部署在同一台机器上且使用非安全端口通信,故不需要证书。

1. 安装CFSSL

有两种方式安装,一是二进制包安装,二是使用go命令安装。

(1)方式一:二进制包安装

# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
# chmod +x cfssl_linux-amd64
# mv cfssl_linux-amd64 /root/local/bin/cfssl # wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
# chmod +x cfssljson_linux-amd64
# mv cfssljson_linux-amd64 /root/local/bin/cfssljson # wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
# chmod +x cfssl-certinfo_linux-amd64
# mv cfssl-certinfo_linux-amd64 /root/local/bin/cfssl-certinfo # export PATH=/root/local/bin:$PATH

二进制包安装CFSSL

(2)方式二:使用go命令安装

# 下载地址:https://golang.org/dl/
[root@node1 ~]# cd /usr/local/
[root@node1 local]# wget https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz
[root@node1 local]# tar zxf go1..linux-amd64.tar.gz
[root@node1 local]# vim /etc/profile
# Go
export GO_HOME=/usr/local/go
export PATH=$GO_HOME/bin:$PATH
[root@node1 local]# source /etc/profile
# 查看版本信息
[root@node1 local]# go version
go version go1. linux/amd64

安装go(需要go 1.6+)

[root@node1 local]# go get -u github.com/cloudflare/cfssl/cmd/...
[root@node1 local]# ls /root/go/bin/
cfssl cfssl-bundle cfssl-certinfo cfssljson cfssl-newkey cfssl-scan mkbundle multirootca
[root@node1 local]# mv /root/go/bin/* /usr/local/bin/

安装cfssl

2. 创建CA

(1)创建 CA 配置文件

[root@node1 local]# mkdir /opt/ssl
[root@node1 local]# cd /opt/ssl/
[root@node1 ssl]# cfssl print-defaults config > config.json
[root@node1 ssl]# cfssl print-defaults csr > csr.json
[root@node1 ssl]# vim ca-config.json
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "8760h"
}
}
}
}

创建CA配置文件

部分字段说明:

  • ca-config.json:可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;
  • signing:表示该证书可用于签名其它证书;生成的 ca.pem 证书中 CA=TRUE;
  • server auth:表示client可以用该 CA 对server提供的证书进行验证;
  • client auth:表示server可以用该 CA 对client提供的证书进行验证。

(2)创建 CA 证书签名请求

[root@node1 ssl]# vim ca-csr.json
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size":
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}

创建CA证书签名请求

部分字段说明:

  • “CN”:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;
  • “O”:Organization,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);

(3)生成 CA 证书和私钥

[root@node1 ssl]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
// :: [INFO] generating a new CA key and certificate from CSR
// :: [INFO] generate received request
// :: [INFO] received CSR
// :: [INFO] generating key: rsa-
// :: [INFO] encoded CSR
// :: [INFO] signed certificate with serial number
[root@node1 ssl]# ls ca*
ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem

生成CA证书和私钥

3. 创建 Kubernetes 证书

(1)创建 kubernetes 证书签名请求

[root@node1 ssl]# vim kubernetes-csr.json
{
"CN": "kubernetes",
"hosts": [
"127.0.0.1",
"172.16.7.151",
"172.16.7.152",
"172.16.7.153",
"172.16.206.32",
"10.254.0.1",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local"
],
"key": {
"algo": "rsa",
"size":
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}

创建kubernetes证书签名请求

部分字段说明:

  • 如果 hosts 字段不为空则需要指定授权使用该证书的 IP 或域名列表,由于该证书后续被 etcd 集群和 kubernetes master 集群使用,所以上面分别指定了 etcd 集群、kubernetes master 集群的主机 IP;
  • 还需要添加kube-apiserver注册的名为 kubernetes 服务的 IP(一般是 kue-apiserver 指定的 service-cluster-ip-range 网段的第一个IP,如 10.254.0.1)

(2)生成 kubernetes 证书和私钥

[root@node1 ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes
// :: [INFO] generate received request
// :: [INFO] received CSR
// :: [INFO] generating key: rsa-
// :: [INFO] encoded CSR
// :: [INFO] signed certificate with serial number
[root@node1 ssl]# ls kubernetes*
kubernetes.csr kubernetes-csr.json kubernetes-key.pem kubernetes.pem

生成kubernetes证书和私钥

或者直接在命令行上指定相关参数:

# echo '{"CN":"kubernetes","hosts":[""],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes -hostname="127.0.0.1,172.16.7.151,172.16.7.152,172.16.7.153,172.16.206.32,10.254.0.1,kubernetes,kubernetes.default" - | cfssljson -bare kubernetes

4. 创建 Admin 证书

(1)创建 admin 证书签名请求

[root@node1 ssl]# vim admin-csr.json
{
"CN": "admin",
"hosts": [],
"key": {
"algo": "rsa",
"size":
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "system:masters",
"OU": "System"
}
]
}

创建admin证书签名请求

说明:

  • 后续 kube-apiserver 使用 RBAC 对客户端(如 kubelet、kube-proxy、Pod)请求进行授权;
  • kube-apiserver 预定义了一些 RBAC 使用的 RoleBindings,如 cluster-admin 将 Groupsystem:masters 与 Role cluster-admin 绑定,该 Role 授予了调用kube-apiserver 的所有 API的权限;
  • OU 指定该证书的 Group 为 system:masters,kubelet 使用该证书访问 kube-apiserver时 ,由于证书被 CA 签名,所以认证通过,同时由于证书用户组为经过预授权的system:masters,所以被授予访问所有 API 的权限。

(2)生成 admin 证书和私钥

[root@node1 ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin
// :: [INFO] generate received request
// :: [INFO] received CSR
// :: [INFO] generating key: rsa-
// :: [INFO] encoded CSR
// :: [INFO] signed certificate with serial number
// :: [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., from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2. ("Information Requirements").
[root@node1 ssl]# ls admin*
admin.csr admin-csr.json admin-key.pem admin.pem

生成admin证书和私钥

5. 创建 Kube-Proxy 证书

(1)创建 kube-proxy 证书签名请求

[root@node1 ssl]# vim kube-proxy-csr.json
{
"CN": "system:kube-proxy",
"hosts": [],
"key": {
"algo": "rsa",
"size":
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}

创建kube-proxy证书签名请求

说明:

  • CN 指定该证书的 User 为 system:kube-proxy;
  • kube-apiserver 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Rolesystem:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限。

(2)生成 kube-proxy 客户端证书和私钥

[root@node1 ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes  kube-proxy-csr.json | cfssljson -bare kube-proxy
// :: [INFO] generate received request
// :: [INFO] received CSR
// :: [INFO] generating key: rsa-
// :: [INFO] encoded CSR
// :: [INFO] signed certificate with serial number
// :: [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., from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2. ("Information Requirements").
[root@node1 ssl]# ls kube-proxy*
kube-proxy.csr kube-proxy-csr.json kube-proxy-key.pem kube-proxy.pem

生成kube-proxy客户端证书和私钥

6. 校验证书

以校验Kubernetes证书为例。

(1)使用openssl命令校验证书

[root@node1 ssl]# openssl x509 -noout -text -in kubernetes.pem
Certificate:
Data:
Version: (0x2)
Serial Number:
:ca:bb::::b1:db:aa::d7:a3:::b0:::a7:e8:5a
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=kubernetes
Validity
Not Before: Sep :: GMT
Not After : Sep :: GMT
Subject: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=kubernetes
...
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
::C1:1B::DD:9C:::EC:B6:::5E::AA:2E::F6:C5
X509v3 Subject Alternative Name:
DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster, DNS:kubernetes.default.svc.cluster.local, IP Address:127.0.0.1, IP Address:172.16.7.151, IP Address:172.16.7.152, IP Address:172.16.7.153, IP Address:172.16.206.32, IP Address:10.254.0.1
...

使用openssl命令校验证书

【说明】:

  • 确认 Issuer 字段的内容和 ca-csr.json 一致;
  • 确认 Subject 字段的内容和 kubernetes-csr.json 一致;
  • 确认 X509v3 Subject Alternative Name 字段的内容和 kubernetes-csr.json 一致;
  • 确认 X509v3 Key Usage、Extended Key Usage 字段的内容和 ca-config.json 中 kubernetesprofile 一致。

(2)使用 Cfssl-Certinfo 命令校验

[root@node1 ssl]# cfssl-certinfo -cert kubernetes.pem
{
"subject": {
"common_name": "kubernetes",
"country": "CN",
"organization": "k8s",
"organizational_unit": "System",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"k8s",
"System",
"kubernetes"
]
},
"issuer": {
"common_name": "kubernetes",
"country": "CN",
"organization": "k8s",
"organizational_unit": "System",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"k8s",
"System",
"kubernetes"
]
},
"serial_number": "",
"sans": [
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local",
"127.0.0.1",
"172.16.7.151",
"172.16.7.152",
"172.16.7.153",
"172.16.206.32",
"10.254.0.1"
],
"not_before": "2017-09-10T11:39:00Z",
"not_after": "2018-09-10T11:39:00Z",
"sigalg": "SHA256WithRSA",
"authority_key_id": "",
"subject_key_id": "79:48:C1:1B:81:DD:9C:75:4:EC:B6:35:26:5E:82:AA:2E:45:F6:C5",
...

使用 Cfssl-Certinfo 命令校验

7. 分发证书

将生成的证书和秘钥文件(后缀名为.pem)拷贝到所有机器的 /etc/kubernetes/ssl 目录下备用:

[root@node1 ssl]# mkdir -p /etc/kubernetes/ssl
[root@node1 ssl]# cp *.pem /etc/kubernetes/ssl
[root@node1 ssl]# scp -p *.pem root@172.16.7.152:/etc/kubernetes/ssl/
[root@node1 ssl]# scp -p *.pem root@172.16.7.153:/etc/kubernetes/ssl/
[root@node1 ssl]# scp -p *.pem root@172.16.206.32:/etc/kubernetes/ssl/

分发证书

四、下载和配置 kubectl(kubecontrol) 命令行工具

1. 下载kubectl

[root@node1 local]# wget https://dl.k8s.io/v1.6.0/kubernetes-client-linux-amd64.tar.gz
[root@node1 local]# tar zxf kubernetes-client-linux-amd64.tar.gz
[root@node1 local]# cp kubernetes/client/bin/kube* /usr/bin/
[root@node1 local]# chmod +x /usr/bin/kube*

2. 创建 kubectl kubeconfig 文件

[root@node1 local]# cd /etc/kubernetes/
[root@node1 kubernetes]# export KUBE_APISERVER="https://172.16.7.151:6443"
# 设置集群参数
[root@node1 kubernetes]# kubectl config set-cluster kubernetes \
> --certificate-authority=/etc/kubernetes/ssl/ca.pem \
> --embed-certs=true \
> --server=${KUBE_APISERVER}
Cluster "kubernetes" set.
# 设置客户端认证参数
[root@node1 kubernetes]# kubectl config set-credentials admin \
> --client-certificate=/etc/kubernetes/ssl/admin.pem \
> --embed-certs=true \
> --client-key=/etc/kubernetes/ssl/admin-key.pem
User "admin" set.
# 设置上下文参数
[root@node1 kubernetes]# kubectl config set-context kubernetes \
> --cluster=kubernetes \
> --user=admin
Context "kubernetes" set
# 设置默认上下文
[root@node1 kubernetes]# kubectl config use-context kubernetes
Switched to context "kubernetes".
[root@node1 kubernetes]# ls ~/.kube/config
/root/.kube/config

创建 kubectl kubeconfig 文件

【说明】:

  • admin.pem 证书 OU 字段值为 system:masters,kube-apiserver 预定义的 RoleBinding cluster-admin 将 Groupsystem:masters 与 Role cluster admin 绑定,该 Role 授予了调用kube-apiserver 相关 API 的权限;
  • 生成的 kubeconfig 被保存到 ~/.kube/config 文件。

五、创建 kubeconfig 文件

kubelet、kube-proxy 等 Node 机器上的进程与 Master 机器的 kube-apiserver 进程通信时需要认证和授权。

kubernetes 1.4 开始支持由 kube-apiserver 为客户端生成 TLS 证书的 TLS Bootstrapping 功能,这样就不需要为每个客户端生成证书了;该功能当前仅支持为 kubelet 生成证书。

1. 创建 TLS Bootstrapping Token

(1)Token auth file

[root@node1 ssl]# export BOOTSTRAP_TOKEN=$(head -c  /dev/urandom | od -An -t x | tr -d ' ')
[root@node1 ssl]# cat > token.csv <<EOF
> ${BOOTSTRAP_TOKEN},kubelet-bootstrap,,"system:kubelet-bootstrap"
> EOF

将token.csv发到所有机器(Master 和 Node)的 /etc/kubernetes/ 目录。

[root@node1 ssl]# cp token.csv /etc/kubernetes/
[root@node1 ssl]# scp -p token.csv root@172.16.7.152:/etc/kubernetes/
[root@node1 ssl]# scp -p token.csv root@172.16.7.153:/etc/kubernetes/

(2)创建 kubelet bootstrapping kubeconfig 文件

[root@node1 ssl]# cd /etc/kubernetes
[root@node1 kubernetes]# export KUBE_APISERVER="https://172.16.7.151:6443"
# 设置集群参数
> --certificate-authority=/etc/kubernetes/ssl/ca.pem \
> --embed-certs=true \
> --server=${KUBE_APISERVER} \
> --kubeconfig=bootstrap.kubeconfig
Cluster "kubernetes" set.
# 设置客户端认证参数
[root@node1 kubernetes]# kubectl config set-credentials kubelet-bootstrap \
> --token=${BOOTSTRAP_TOKEN} \
> --kubeconfig=bootstrap.kubeconfig
User "kubelet-bootstrap" set.
# 设置上下文参数
[root@node1 kubernetes]# kubectl config set-context default \
> --cluster=kubernetes \
> --user=kubelet-bootstrap \
> --kubeconfig=bootstrap.kubeconfig
Context "default" created.
# 设置默认上下文
[root@node1 kubernetes]# kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
Switched to context "default".

创建 kubelet bootstrapping kubeconfig 文件

【说明】:

  • --embed-certs 为 true 时表示将 certificate-authority 证书写入到生成的 bootstrap.kubeconfig 文件中;
  • 设置客户端认证参数时没有指定秘钥和证书,后续由 kube-apiserver 自动生成。

2. 创建 kube-proxy kubeconfig 文件

[root@node1 kubernetes]# export KUBE_APISERVER="https://172.16.7.151:6443"
# 设置集群参数
[root@node1 kubernetes]# kubectl config set-cluster kubernetes \
> --certificate-authority=/etc/kubernetes/ssl/ca.pem \
> --embed-certs=true \
> --server=${KUBE_APISERVER} \
> --kubeconfig=kube-proxy.kubeconfig
Cluster "kubernetes" set.
# 设置客户端认证参数
[root@node1 kubernetes]# kubectl config set-credentials kube-proxy \
> --client-certificate=/etc/kubernetes/ssl/kube-proxy.pem \
> --client-key=/etc/kubernetes/ssl/kube-proxy-key.pem \
> --embed-certs=true \
> --kubeconfig=kube-proxy.kubeconfig
User "kube-proxy" set.
# 设置上下文参数
[root@node1 kubernetes]# kubectl config set-context default \
> --cluster=kubernetes \
> --user=kube-proxy \
> --kubeconfig=kube-proxy.kubeconfig
Context "default" created.
# 设置默认上下文
[root@node1 kubernetes]# kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
Switched to context "default".

创建 kube-proxy kubeconfig 文件

【说明】:

  • 设置集群参数和客户端认证参数时 --embed-certs 都为 true,这会将 certificate-authority、client-certificate 和client-key 指向的证书文件内容写入到生成的 kube-proxy.kubeconfig 文件中;
  • kube-proxy.pem 证书中 CN 为 system:kube-proxy,kube-apiserver 预定义的 RoleBinding cluster-admin 将Usersystem:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限。

3. 分发 kubeconfig 文件

将两个 kubeconfig 文件分发到所有 Node 机器的 /etc/kubernetes/ 目录。

[root@node1 kubernetes]# scp -p bootstrap.kubeconfig root@172.16.7.152:/etc/kubernetes/
[root@node1 kubernetes]# scp -p kube-proxy.kubeconfig root@172.16.7.152:/etc/kubernetes/
[root@node1 kubernetes]# scp -p bootstrap.kubeconfig root@172.16.7.153:/etc/kubernetes/
[root@node1 kubernetes]# scp -p kube-proxy.kubeconfig root@172.16.7.153:/etc/kubernetes/

分发 kubeconfig 文件

六、创建高可用 etcd 集群

etcd 是 CoreOS 团队发起的开源项目,基于 Go 语言实现,做为一个分布式键值对存储,通过分布式锁,leader选举和写屏障(write barriers)来实现可靠的分布式协作。

kubernetes系统使用etcd存储所有数据。

CoreOS官方推荐集群规模5个为宜,我这里使用了3个节点。

1. 安装配置etcd集群

搭建etcd集群有3种方式,分别为Static, etcd Discovery, DNS Discovery。Discovery请参见官网。这里仅以Static方式展示一次集群搭建过程。

首先请做好3个节点的时间同步,方式有很多,请自行百度搜索。

(1)TLS 认证文件

需要为 etcd 集群创建加密通信的 TLS 证书,这里复用以前创建的 kubernetes 证书。

[root@node1 ssl]# cp ca.pem kubernetes-key.pem kubernetes.pem /etc/kubernetes/ssl

上面这步在之前做过,可以忽略不做。【注意】:kubernetes 证书的 hosts 字段列表中包含上面三台机器的 IP,否则后续证书校验会失败。

(2)下载二进制文件

到 https://github.com/coreos/etcd/releases 页面下载最新版本的二进制文件,并上传到/usr/local/目录下。

[root@node1 local]# tar xf etcd-v3.2.7-linux-amd64.tar
[root@node1 local]# mv etcd-v3.2.7-linux-amd64/etcd* /usr/local/bin/

etcd集群中另外两台机器也需要如上操作。

(3)创建 etcd 的 systemd unit 文件

配置文件模板如下,注意替换 ETCD_NAME 和 INTERNAL_IP 变量的值。

[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos [Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
--name ${ETCD_NAME} \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--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- \
--initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
--initial-cluster-state new \
--data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=
LimitNOFILE= [Install]
WantedBy=multi-user.target

针对上面几个配置参数做下简单的解释:

  • --name:方便理解的节点名称,默认为default,在集群中应该保持唯一,可以使用 hostname
  • --data-dir:服务运行数据保存的路径,默认为 ${name}.etcd
  • --snapshot-count:指定有多少事务(transaction)被提交时,触发截取快照保存到磁盘
  • --heartbeat-interval:leader 多久发送一次心跳到 followers。默认值是 100ms
  • --eletion-timeout:重新投票的超时时间,如果 follow 在该时间间隔没有收到心跳包,会触发重新投票,默认为 1000 ms
  • --listen-peer-urls:和同伴通信的地址,比如 http://ip:2380。如果有多个,使用逗号分隔。需要所有节点都能够访问,所以不要使用 localhost!
  • --listen-client-urls:对外提供服务的地址:比如 http://ip:2379,http://127.0.0.1:2379,客户端会连接到这里和 etcd 交互
  • --advertise-client-urls:对外公告的该节点客户端监听地址,这个值会告诉集群中其他节点
  • --initial-advertise-peer-urls:该节点同伴监听地址,这个值会告诉集群中其他节点
  • --initial-cluster:集群中所有节点的信息,格式为 node1=http://ip1:2380,node2=http://ip2:2380,…。注意:这里的 node1 是节点的 --name 指定的名字;后面的 ip1:2380 是 --initial-advertise-peer-urls 指定的值
  • --initial-cluster-state:新建集群的时候,这个值为new;假如已经存在的集群,这个值为 existing
  • --initial-cluster-token:创建集群的token,这个值每个集群保持唯一。这样的话,如果你要重新创建集群,即使配置和之前一样,也会再次生成新的集群和节点 uuid;否则会导致多个集群之间的冲突,造成未知的错误

所有以--init开头的配置都是在bootstrap集群的时候才会用到,后续节点的重启会被忽略。

[root@node1 local]# mkdir -p /var/lib/etcd
[root@node1 local]# cd /etc/systemd/system/
[root@node1 system]# vim etcd.service
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos [Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
--name node1 \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--initial-advertise-peer-urls https://172.16.7.151:2380 \
--listen-peer-urls https://172.16.7.151:2380 \
--listen-client-urls https://172.16.7.151:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://172.16.7.151:2379 \
--initial-cluster-token etcd-cluster- \
--initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
--initial-cluster-state new \
--data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=
LimitNOFILE= [Install]
WantedBy=multi-user.target

node1的etcd.service

[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos [Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
--name node2 \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--initial-advertise-peer-urls https://172.16.7.152:2380 \
--listen-peer-urls https://172.16.7.152:2380 \
--listen-client-urls https://172.16.7.152:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://172.16.7.152:2379 \
--initial-cluster-token etcd-cluster- \
--initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
--initial-cluster-state new \
--data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=
LimitNOFILE= [Install]
WantedBy=multi-user.target

node2的etcd.service

[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos [Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
EnvironmentFile=-/etc/etcd/etcd.conf
ExecStart=/usr/local/bin/etcd \
--name node3 \
--cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--peer-cert-file=/etc/kubernetes/ssl/kubernetes.pem \
--peer-key-file=/etc/kubernetes/ssl/kubernetes-key.pem \
--trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \
--initial-advertise-peer-urls https://172.16.7.153:2380 \
--listen-peer-urls https://172.16.7.153:2380 \
--listen-client-urls https://172.16.7.153:2379,https://127.0.0.1:2379 \
--advertise-client-urls https://172.16.7.153:2379 \
--initial-cluster-token etcd-cluster- \
--initial-cluster node1=https://172.16.7.151:2380,node2=https://172.16.7.152:2380,node3=https://172.16.7.153:2380 \
--initial-cluster-state new \
--data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=
LimitNOFILE= [Install]
WantedBy=multi-user.target

node3的etcd.service

【说明】:

  • 指定 etcd 的工作目录为 /var/lib/etcd,数据目录为 /var/lib/etcd,需在启动服务前创建这两个目录;
  • 为了保证通信安全,需要指定 etcd 的公私钥(cert-file和key-file)、Peers 通信的公私钥和 CA 证书(peer-cert-file、peer-key-file、peer-trusted-ca-file)、客户端的CA证书(trusted-ca-file);
  • 创建 kubernetes.pem 证书时使用的 kubernetes-csr.json 文件的 hosts 字段包含所有 etcd 节点的 INTERNAL_IP,否则证书校验会出错;
  • --initial-cluster-state 值为 new 时,--name 的参数值必须位于 --initial-cluster 列表中。

2. 启动 etcd 服务

集群中的节点都执行以下命令:

# systemctl daemon-reload
# systemctl enable etcd
# systemctl start etcd

3. 验证服务

etcdctl 是一个命令行客户端,它能提供一些简洁的命令,供用户直接跟 etcd 服务打交道,而无需基于 HTTP API 方式。这在某些情况下将很方便,例如用户对服务进行测试或者手动修改数据库内容。我们也推荐在刚接触 etcd 时通过 etcdctl 命令来熟悉相关的操作,这些操作跟 HTTP API 实际上是对应的。

在etcd集群任意一台机器上执行如下命令:

(1)查看集群健康状态

[root@node1 system]# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" cluster-health
member 31800ab6b566b2b is healthy: got healthy result from https://172.16.7.151:2379
member 9a0745d96695eec6 is healthy: got healthy result from https://172.16.7.153:2379
member e64edc68e5e81b55 is healthy: got healthy result from https://172.16.7.152:2379
cluster is healthy

查看etcd集群健康状态

结果最后一行为 cluster is healthy 时表示集群服务正常。

(2)查看集群成员,并能看出哪个是leader节点

[root@node1 system]# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" member list
31800ab6b566b2b: name=node1 peerURLs=https://172.16.7.151:2380 clientURLs=https://172.16.7.151:2379 isLeader=false
9a0745d96695eec6: name=node3 peerURLs=https://172.16.7.153:2380 clientURLs=https://172.16.7.153:2379 isLeader=false
e64edc68e5e81b55: name=node2 peerURLs=https://172.16.7.152:2380 clientURLs=https://172.16.7.152:2379 isLeader=true

(3)删除一个节点

# 如果你想更新一个节点的IP(peerURLS),首先你需要知道那个节点的ID
# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" member list
31800ab6b566b2b: name=node1 peerURLs=https://172.16.7.151:2380 clientURLs=https://172.16.7.151:2379 isLeader=false
9a0745d96695eec6: name=node3 peerURLs=https://172.16.7.153:2380 clientURLs=https://172.16.7.153:2379 isLeader=false
e64edc68e5e81b55: name=node2 peerURLs=https://172.16.7.152:2380 clientURLs=https://172.16.7.152:2379 isLeader=true
# 删除一个节点
# etcdctl --endpoints "http://192.168.2.210:2379" member remove 9a0745d96695eec6

七、部署 kubernetes master节点

kubernetes master 节点包含的组件:

  • kube-apiserver
  • kube-scheduler
  • kube-controller-manager

目前这三个组件需要部署在同一台机器上。

kube-scheduler、kube-controller-manager 和 kube-apiserver 三者的功能紧密相关;
同时只能有一个 kube-scheduler、kube-controller-manager 进程处于工作状态,如果运行多个,则需要通过选举产生一个 leader。

1. TLS 证书文件

检查之前生成的证书。

[root@node1 kubernetes]# ls /etc/kubernetes/ssl
admin-key.pem admin.pem ca-key.pem ca.pem kube-proxy-key.pem kube-proxy.pem kubernetes-key.pem kubernetes.pem

2. 下载二进制文件

有两种下载方式:

  • 方式一:从 github release 页面下载发布版 tarball,解压后再执行下载脚本。
[root@node1 local]# cd /opt/
[root@node1 opt]# wget https://github.com/kubernetes/kubernetes/releases/download/v1.6.0/kubernetes.tar.gz
[root@node1 opt]# tar zxf kubernetes.tar.gz
[root@node1 opt]# cd kubernetes/
[root@node1 kubernetes]# ./cluster/get-kube-binaries.sh
Kubernetes release: v1.6.0
Server: linux/amd64 (to override, set KUBERNETES_SERVER_ARCH)
Client: linux/amd64 (autodetected) Will download kubernetes-server-linux-amd64.tar.gz from https://storage.googleapis.com/kubernetes-release/release/v1.6.0
Will download and extract kubernetes-client-linux-amd64.tar.gz from https://storage.googleapis.com/kubernetes-release/release/v1.6.0
Is this ok? [Y]/n
y
...

从github release页面下载

  • 方式二:从 CHANGELOG页面 下载 client 或 server tarball 文件。server 的 tarball kubernetes-server-linux-amd64.tar.gz 已经包含了 client(kubectl) 二进制文件,所以不用单独下载kubernetes-client-linux-amd64.tar.gz文件。
wget https://dl.k8s.io/v1.6.0/kubernetes-server-linux-amd64.tar.gz
tar -xzvf kubernetes-server-linux-amd64.tar.gz
...
cd kubernetes
tar -xzvf kubernetes-src.tar.gz

从 CHANGELOG页面 下载 client 或 server tarball 文件

将二进制文件拷贝到指定路径:

[root@node1 kubernetes]# pwd
/opt/kubernetes
[root@node1 kubernetes]# cd server/
[root@node1 server]# cp -r kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler,kubectl,kube-proxy,kubelet} /usr/local/bin/

3. 配置和启动 kube-apiserver

(1)创建 kube-apiserver的service配置文件

在/usr/lib/systemd/system/下创建kube-apiserver.service,内容如下:

[Unit]
Description=Kubernetes API Service
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
After=etcd.service [Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/apiserver
ExecStart=/usr/local/bin/kube-apiserver \
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_ETCD_SERVERS \
$KUBE_API_ADDRESS \
$KUBE_API_PORT \
$KUBELET_PORT \
$KUBE_ALLOW_PRIV \
$KUBE_SERVICE_ADDRESSES \
$KUBE_ADMISSION_CONTROL \
$KUBE_API_ARGS
Restart=on-failure
Type=notify
LimitNOFILE= [Install]
WantedBy=multi-user.target

kube-apiserver.service

上面的配置文件中用到的/etc/kubernetes/config文件的内容为:

###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
# kube-apiserver.service
# kube-controller-manager.service
# kube-scheduler.service
# kubelet.service
# kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true" # journal message level, is debug
KUBE_LOG_LEVEL="--v=0" # Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow-privileged=true" # How the controller-manager, scheduler, and proxy find the apiserver
#KUBE_MASTER="--master=http://domainName:8080"
KUBE_MASTER="--master=http://172.16.7.151:8080"

config

该配置文件同时被kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、kube-proxy使用。

创建apiserver配置文件/etc/kubernetes/apiserver:

###
## kubernetes system config
##
## The following values are used to configure the kube-apiserver
##
#
## The address on the local server to listen to.
#KUBE_API_ADDRESS="--insecure-bind-address=sz-pg-oam-docker-test-001.tendcloud.com"
KUBE_API_ADDRESS="--advertise-address=172.16.7.151 --bind-address=172.16.7.151 --insecure-bind-address=172.16.7.151"
#
## The port on the local server to listen on.
#KUBE_API_PORT="--port=8080"
#
## Port minions listen on
#KUBELET_PORT="--kubelet-port=10250"
#
## Comma separated list of nodes in the etcd cluster
KUBE_ETCD_SERVERS="--etcd-servers=https://172.16.7.151:2379,https://172.16.7.152:2379,https://172.16.7.153:2379"
#
## Address range to use for services
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16"
#
## default admission control policies
KUBE_ADMISSION_CONTROL="--admission-control=ServiceAccount,NamespaceLifecycle,NamespaceExists,LimitRanger,ResourceQuota"
#
## Add your own!
KUBE_API_ARGS="--authorization-mode=RBAC --runtime-config=rbac.authorization.k8s.io/v1beta1 --kubelet-https=true --experimental-bootstrap-token-auth --token-auth-file=/etc/kubernetes/token.csv --service-node-port-range=30000-32767 --tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem --client-ca-file=/etc/kubernetes/ssl/ca.pem --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem --etcd-cafile=/etc/kubernetes/ssl/ca.pem --etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem --etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem --enable-swagger-ui=true --apiserver-count=3 --audit-log-maxage=30 --audit-log-maxbackup=3 --audit-log-maxsize=100 --audit-log-path=/var/lib/audit.log --event-ttl=1h"

apiserver

【说明】:

  • --authorization-mode=RBAC 指定在安全端口使用 RBAC 授权模式,拒绝未通过授权的请求;
  • kube-scheduler、kube-controller-manager 一般和 kube-apiserver 部署在同一台机器上,它们使用非安全端口和 kube-apiserver通信;
  • kubelet、kube-proxy、kubectl 部署在其它 Node 节点上,如果通过安全端口访问 kube-apiserver,则必须先通过 TLS 证书认证,再通过 RBAC 授权;
  • kube-proxy、kubectl 通过在使用的证书里指定相关的 User、Group 来达到通过 RBAC 授权的目的;
  • 如果使用了 kubelet TLS Boostrap 机制,则不能再指定 --kubelet-certificate-authority、--kubelet-client-certificate 和 --kubelet-client-key 选项,否则后续 kube-apiserver 校验 kubelet 证书时出现 ”x509: certificate signed by unknown authority“ 错误;
  • --admission-control 值必须包含 ServiceAccount;
  • --bind-address 不能为 127.0.0.1;
  • runtime-config配置为rbac.authorization.k8s.io/v1beta1,表示运行时的apiVersion;
  • --service-cluster-ip-range 指定 Service Cluster IP 地址段,该地址段不能路由可达;
  • 缺省情况下 kubernetes 对象保存在 etcd /registry 路径下,可以通过 --etcd-prefix 参数进行调整。

(2)启动kube-apiserver

# systemctl daemon-reload
# systemctl enable kube-apiserver
# systemctl start kube-apiserver

启动过程中可以观察日志:

# tail -f /var/log/message

4. 配置和启动 kube-controller-manager

(1)创建 kube-controller-manager 的service配置文件

在/usr/lib/systemd/system/下创建kube-controller-manager.service,内容如下:

Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes [Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/controller-manager
ExecStart=/usr/local/bin/kube-controller-manager \
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_MASTER \
$KUBE_CONTROLLER_MANAGER_ARGS
Restart=on-failure
LimitNOFILE= [Install]
WantedBy=multi-user.target

kube-controller-manager.service

创建kube-controller-manager配置文件/etc/kubernetes/controller-manager:

# vim /etc/kubernetes/controller-manager
###
# The following values are used to configure the kubernetes controller-manager # defaults from config and apiserver should be adequate # Add your own!
KUBE_CONTROLLER_MANAGER_ARGS="--address=127.0.0.1 --service-cluster-ip-range=10.254.0.0/16 --cluster-name=kubernetes --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem --cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem --service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem --root-ca-file=/etc/kubernetes/ssl/ca.pem --leader-elect=true"

controller-manager

【说明】:

  • --service-cluster-ip-range 参数指定 Cluster 中 Service 的CIDR范围,该网络在各 Node 间必须路由不可达,必须和 kube-apiserver 中的参数一致;
  • --cluster-signing-* 指定的证书和私钥文件用来签名为 TLS BootStrap 创建的证书和私钥;
  • --root-ca-file 用来对 kube-apiserver 证书进行校验,指定该参数后,才会在Pod 容器的 ServiceAccount 中放置该 CA 证书文件;
  • --address 值必须为 127.0.0.1,因为当前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台机器,否则:
# kubectl get componentstatuses
NAME STATUS MESSAGE ERROR
scheduler Unhealthy Get http://127.0.0.1:10251/healthz: dial tcp 127.0.0.1:10251: getsockopt: connection refused
controller-manager Healthy ok
etcd- Unhealthy Get http://172.20.0.113:2379/health: malformed HTTP response "\x15\x03\x01\x00\x02\x02"
etcd- Healthy {"health": "true"}
etcd- Healthy {"health": "true"}

参考:https://github.com/kubernetes-incubator/bootkube/issues/64

(2)启动 kube-controller-manager

# systemctl daemon-reload
# systemctl enable kube-controller-manager
# systemctl start kube-controller-manager

启动 kube-controller-manager

5. 配置和启动 kube-scheduler

(1)创建 kube-scheduler的serivce配置文件

在/usr/lib/systemd/system/下创建kube-scheduler.service,内容如下:

[Unit]
Description=Kubernetes Scheduler Plugin
Documentation=https://github.com/GoogleCloudPlatform/kubernetes [Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/scheduler
ExecStart=/usr/local/bin/kube-scheduler \
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_MASTER \
$KUBE_SCHEDULER_ARGS
Restart=on-failure
LimitNOFILE= [Install]
WantedBy=multi-user.target

kube-scheduler.service

创建kube-scheduler配置文件/etc/kubernetes/scheduler:

# vim /etc/kubernetes/scheduler
###
# kubernetes scheduler config # default config should be adequate # Add your own!
KUBE_SCHEDULER_ARGS="--leader-elect=true --address=127.0.0.1"

scheduler

【说明】:

  • --address 值必须为 127.0.0.1,因为当前 kube-apiserver 期望 scheduler 和 controller-manager 在同一台机器。

(2)启动 kube-scheduler

# systemctl daemon-reload
# systemctl enable kube-scheduler
# systemctl start kube-scheduler

启动 kube-scheduler

6. 验证 master 节点功能

# kubectl get componentstatuses
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd- Healthy {"health": "true"}
etcd- Healthy {"health": "true"}
etcd- Healthy {"health": "true"}

验证 master 节点功能

八、部署kubernetes node节点

kubernetes node 节点包含如下组件:

  • Docker 1.12.6
  • Flanneld
  • kubelet
  • kube-prox

1. 安装Docker

参见之前的文章《Docker镜像和容器》。

2. 安装配置Flanneld

(1)Flannel介绍

  • Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。
  • 在默认的Docker配置中,每个节点上的Docker服务会分别负责所在节点容器的IP分配。这样导致的一个问题是,不同节点上容器可能获得相同的内外IP地址。
  • Flannel的设计目的就是为集群中的所有节点重新规划IP地址的使用规则,从而使得不同节点上的容器能够获得“同属一个内网”且”不重复的”IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。
  • Flannel实质上是一种“覆盖网络(overlay network)”,也就是将TCP数据包装在另一种网络包里面进行路由转发和通信,目前已经支持udp、vxlan、host-gw、aws-vpc、gce和alloc路由等数据转发方式,默认的节点间数据通信方式是UDP转发。

在Flannel的GitHub页面有如下的一张原理图:

  • 数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。(Flannel通过ETCD服务维护了一张节点间的路由表);
  • 源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡;
  • 最后就像本机容器通信一样由docker0路由到目标容器,这样整个数据包的传递就完成了。

(2)安装配置flannel

我这里使用yum安装,安装的版本是0.7.1。集群中的3台node都需要安装配置flannel。

# yum install -y flannel
# rpm -ql flannel
/etc/sysconfig/flanneld
/run/flannel
/usr/bin/flanneld
/usr/bin/flanneld-start
/usr/lib/systemd/system/docker.service.d/flannel.conf
/usr/lib/systemd/system/flanneld.service
/usr/lib/tmpfiles.d/flannel.conf
/usr/libexec/flannel
/usr/libexec/flannel/mk-docker-opts.sh
...

安装flannel

修改flannel配置文件:

# vim /etc/sysconfig/flanneld
# Flanneld configuration options # etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="https://172.16.7.151:2379,https://172.16.7.152:2379,https://172.16.7.153:2379" # etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/kube-centos/network" # Any additional options that you want to pass
#FLANNEL_OPTIONS=""
FLANNEL_OPTIONS="-etcd-cafile=/etc/kubernetes/ssl/ca.pem -etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem -etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem"

/etc/sysconfig/flanneld

【说明】:

  • etcd的地址FLANNEL_ETCD_ENDPOINT
  • etcd查询的目录,包含docker的IP地址段配置。FLANNEL_ETCD_PREFIX

(3)在etcd中初始化flannel网络数据

多个node上的Flanneld依赖一个etcd cluster来做集中配置服务,etcd保证了所有node上flanned所看到的配置是一致的。同时每个node上的flanned监听etcd上的数据变化,实时感知集群中node的变化。

执行下面的命令为docker分配IP地址段:

# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" mkdir /kube-centos/network
# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" mk /kube-centos/network/config '{"Network": "172.30.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" }}'
{"Network": "172.30.0.0/16", "SubnetLen": , "Backend": { "Type": "vxlan" }}

(4)启动flannel

集群中的3台node都启动flannel:

# systemctl daemon-reload
# systemctl start flanneld

启动flannel

启动完成后,会在/run/flannel/目录下生成两个文件,以node1为例:

# ls /run/flannel/
docker subnet.env
# cd /run/flannel/
[root@node1 flannel]# cat docker
DOCKER_OPT_BIP="--bip=172.30.51.1/24"
DOCKER_OPT_IPMASQ="--ip-masq=true"
DOCKER_OPT_MTU="--mtu=1450"
DOCKER_NETWORK_OPTIONS=" --bip=172.30.51.1/24 --ip-masq=true --mtu=1450"
# cat subnet.env
FLANNEL_NETWORK=172.30.0.0/
FLANNEL_SUBNET=172.30.51.1/
FLANNEL_MTU=
FLANNEL_IPMASQ=false

现在查询etcd中的内容可以看到:

# etcdctl --ca-file=/etc/kubernetes/ssl/ca.pem --cert-file=/etc/kubernetes/ssl/kubernetes.pem --key-file=/etc/kubernetes/ssl/kubernetes-key.pem --endpoints "https://172.16.7.151:2379" ls /kube-centos/network/subnets
/kube-centos/network/subnets/172.30.51.0-
/kube-centos/network/subnets/172.30.29.0-
/kube-centos/network/subnets/172.30.19.0-

设置docker0网桥的IP地址(集群中node节点都需要设置):

# source /run/flannel/subnet.env
# ifconfig docker0 $FLANNEL_SUBNET

这样docker0和flannel网桥会在同一个子网中,查看node1主机网卡:

docker0: flags=<UP,BROADCAST,MULTICAST>  mtu
inet 172.30.51.1 netmask 255.255.255.0 broadcast 172.30.51.255
flannel.: flags=<UP,BROADCAST,RUNNING,MULTICAST> mtu
inet 172.30.51.0 netmask 255.255.255.255 broadcast 0.0.0.0

重启docker:

# systemctl restart docker

【注意】:经过测试,docker 17.06.1-ce版本重启后,docker0网桥又会被重置为172.17.0.1,docker 1.12.6版本测试是不会有问题的。

如果想重新设置flannel,先停止flanneld,清理etcd里的数据,然后 ifconfig flannel.1 down,然后启动flanneld,会重新生成子网,并up flannel.1网桥设备。

(5)测试跨主机容器通信

分别在node1和node2上启动一个容器,然后ping对方容器的地址:

[root@node1 flannel]# docker run -i -t centos /bin/bash
[root@38be151deb71 /]# yum install net-tools -y
[root@38be151deb71 /]# ifconfig
eth0: flags=<UP,BROADCAST,RUNNING,MULTICAST> mtu
inet 172.30.51.2 netmask 255.255.255.0 broadcast 0.0.0.0 [root@node2 flannel]# docker run -i -t centos /bin/bash
[root@90e85c215fda /]# yum install net-tools -y
[root@90e85c215fda /]# ifconfig
eth0: flags=<UP,BROADCAST,RUNNING,MULTICAST> mtu
inet 172.30.29.2 netmask 255.255.255.0 broadcast 0.0.0.0
[root@90e85c215fda /]# ping 172.16.51.2
PING 172.16.51.2 (172.16.51.2) () bytes of data.
bytes from 172.16.51.2: icmp_seq= ttl= time=1.00 ms
bytes from 172.16.51.2: icmp_seq= ttl= time=1.29 ms

测试跨主机容器通信

(6)补充:下载二进制包安装flannel

从官网 https://github.com/coreos/flannel/releases 下载的flannel release 0.7.1,并将下载的文件上传到服务器的/opt/flannel/目录下。

# mkdir flannel
# cd flannel/
# tar xf flannel-v0.7.1-linux-amd64.tar
# ls
flanneld flannel-v0.7.1-linux-amd64.tar mk-docker-opts.sh README.md

下载flannel二进制包

mk-docker-opts.sh是用来Generate Docker daemon options based on flannel env file。

执行 ./mk-docker-opts.sh -i 将会生成如下两个文件环境变量文件。

Flannel的文档中有写Docker Integration:
Docker daemon accepts --bip argument to configure the subnet of the docker0 bridge. It also accepts --mtu to set the MTU for docker0 and veth devices that it will be creating.
Because flannel writes out the acquired subnet and MTU values into a file, the script starting Docker can source in the values and pass them to Docker daemon:

source /run/flannel/subnet.env
docker daemon --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU} &

Systemd users can use EnvironmentFile directive in the .service file to pull in /run/flannel/subnet.env

3. 安装和配置 kubelet

kubelet 启动时向 kube-apiserver 发送 TLS bootstrapping 请求,需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper cluster 角色(role), 然后 kubelet 才能有权限创建认证请求(certificate signing requests):

# cd /etc/kubernetes
[root@node1 kubernetes]# kubectl create clusterrolebinding kubelet-bootstrap \
> --clusterrole=system:node-bootstrapper \
> --user=kubelet-bootstrap
clusterrolebinding "kubelet-bootstrap" created

【注意】:以上这步只需要在kubernetes node集群中的一台执行一次就可以了。

【说明】:

  • --user=kubelet-bootstrap 是在 /etc/kubernetes/token.csv 文件中指定的用户名,同时也写入了/etc/kubernetes/bootstrap.kubeconfig 文件。

(1)下载最新的 kubelet 和 kube-proxy 二进制文件

这个在之前安装kubernetes master时已经下载好了二进制文件,只需要复制到相应目录即可。

[root@node1 kubernetes]# cd /opt/kubernetes/server/kubernetes/server/bin/
[root@node1 bin]# scp -p kubelet root@172.16.7.152:/usr/local/bin/
[root@node1 bin]# scp -p kube-proxy root@172.16.7.152:/usr/local/bin/
[root@node1 bin]# scp -p kubelet root@172.16.7.153:/usr/local/bin/
[root@node1 bin]# scp -p kube-proxy root@172.16.7.153:/usr/local/bin/

(2)配置kubelet

以下操作需要在集群的kubernetes node节点上都要运行,下面以node1服务器为例:

a.创建 kubelet 的service配置文件:

在/usr/lib/systemd/system/下创建文件kubelet.serivce:

[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service [Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/local/bin/kubelet \
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBELET_API_SERVER \
$KUBELET_ADDRESS \
$KUBELET_PORT \
$KUBELET_HOSTNAME \
$KUBE_ALLOW_PRIV \
$KUBELET_POD_INFRA_CONTAINER \
$KUBELET_ARGS
Restart=on-failure [Install]
WantedBy=multi-user.target

kubelet.service

b.创建kubelet配置文件

创建kubelet工作目录(必须创建,否则kubelet启动不了):

# mkdir /var/lib/kubelet

创建kubelet配置文件:

###
## kubernetes kubelet (minion) config
#
## The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=172.16.7.151"
#
## The port for the info server to serve on
#KUBELET_PORT="--port=10250"
#
## You may leave this blank to use the actual hostname
KUBELET_HOSTNAME="--hostname-override=172.16.7.151"
#
## location of the api-server
KUBELET_API_SERVER="--api-servers=http://172.16.7.151:8080"
#
## pod infrastructure container
#KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=sz-pg-oam-docker-hub-001.tendcloud.com/library/pod-infrastructure:rhel7"
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure"
#
## Add your own!
KUBELET_ARGS="--cgroup-driver=systemd --cluster-dns=10.254.0.2 --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig --kubeconfig=/etc/kubernetes/kubelet.kubeconfig --require-kubeconfig --cert-dir=/etc/kubernetes/ssl --cluster-domain=cluster.local. --hairpin-mode promiscuous-bridge --serialize-image-pulls=false"

/etc/kubernetes/kubelet

【注意】:将配置文件中的IP地址更改为你的每台node节点的IP地址(除了--api-servers=http://172.16.7.151:8080这个ip地址是不用改的)。

【说明】:

  • --address 不能设置为 127.0.0.1,否则后续 Pods 访问 kubelet 的 API 接口时会失败,因为 Pods 访问的 127.0.0.1 指向自己而不是 kubelet;如果设置了 --hostname-override 选项,则 kube-proxy 也需要设置该选项,否则会出现找不到 Node 的情况;
  • KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.access.redhat.com/rhel7/pod-infrastructure",这个是一个基础容器,每一个Pod启动的时候都会启动一个这样的容器。如果你的本地没有这个镜像,kubelet会连接外网把这个镜像下载下来。最开始的时候是在Google的registry上,因此国内因为GFW都下载不了导致Pod运行不起来。现在每个版本的Kubernetes都把这个镜像打包,你可以提前传到自己的registry上,然后再用这个参数指定。
  • --experimental-bootstrap-kubeconfig 指向 bootstrap kubeconfig 文件,kubelet 使用该文件中的用户名和 token 向 kube-apiserver 发送 TLS Bootstrapping 请求;
  • 管理员通过了 CSR 请求后,kubelet 自动在 --cert-dir 目录创建证书和私钥文件(kubelet-client.crt 和 kubelet-client.key),然后写入 --kubeconfig 文件;
  • 建议在 --kubeconfig 配置文件中指定 kube-apiserver 地址,如果未指定 --api-servers 选项,则必须指定 --require-kubeconfig 选项后才从配置文件中读取 kube-apiserver 的地址,否则 kubelet 启动后将找不到 kube-apiserver (日志中提示未找到 API Server),kubectl get nodes 不会返回对应的 Node 信息;
  • --cluster-dns 指定 kubedns 的 Service IP(可以先分配,后续创建 kubedns 服务时指定该 IP),--cluster-domain 指定域名后缀,这两个参数同时指定后才会生效。

(3)启动kubelet

# systemctl daemon-reload
# systemctl enable kubelet
# systemctl start kubelet

启动kubelet

(4)通过 kubelet 的 TLS 证书请求

kubelet 首次启动时向 kube-apiserver 发送证书签名请求,必须通过后 kubernetes 系统才会将该 Node 加入到集群。

a. 查看未授权的 CSR 请求

# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-fv3bj 49s kubelet-bootstrap Pending

b. 通过 CSR 请求

# kubectl certificate approve csr-fv3bj
certificatesigningrequest "csr-fv3bj" approved
[root@node1 kubernetes]# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-fv3bj 42m kubelet-bootstrap Approved,Issued
# kubectl get nodes
NAME STATUS AGE VERSION
172.16.7.151 Ready 18s v1.6.0

c. 查看自动生成的 kubelet kubeconfig 文件和公私钥

[root@node1 kubernetes]# ls -l /etc/kubernetes/kubelet.kubeconfig
-rw-------. root root Sep : /etc/kubernetes/kubelet.kubeconfig
[root@node1 kubernetes]# ls -l /etc/kubernetes/ssl/kubelet*
-rw-r--r--. root root Sep : /etc/kubernetes/ssl/kubelet-client.crt
-rw-------. root root Sep : /etc/kubernetes/ssl/kubelet-client.key
-rw-r--r--. root root Sep : /etc/kubernetes/ssl/kubelet.crt
-rw-------. root root Sep : /etc/kubernetes/ssl/kubelet.key

在集群中其它的kubernetes node节点上操作完成后,查看集群kubernetes node情况如下:

# kubectl get csr
NAME AGE REQUESTOR CONDITION
csr-5n72m 3m kubelet-bootstrap Approved,Issued
csr-clwzj 16m kubelet-bootstrap Approved,Issued
csr-fv3bj 4h kubelet-bootstrap Approved,Issued
# kubectl get nodes
NAME STATUS AGE VERSION
172.16.7.151 Ready 4h v1.6.0
172.16.7.152 Ready 6m v1.6.0
172.16.7.153 Ready 12s v1.6.0

【问题】:切记每台node节点上的kubelet配置文件/etc/kubernetes/kubelet中的ip地址要改正确,否则会出现加入不了的情况。我在将node1节点的/etc/kubernetes/kubelet远程复制到node2节点上,没有修改ip,直接启动了,配置文件中写的ip地址是node1的ip地址,这就造成了node2节点并没有加入进来。采取的恢复操作是:

[root@node2 ~]# systemctl stop kubelet
[root@node2 ~]# cd /etc/kubernetes
[root@node2 kubernetes]# rm -f kubelet.kubeconfig
[root@node2 kubernetes]# rm -rf ~/.kube/cache
# 修改/etc/kubernetes/kubelet中的ip地址
[root@node2 kubernetes]# vim /etc/kubernetes/kubelet
[root@node2 ~]# systemctl start kubelet

这样,再次启动kubelet时,kube-apiserver才收到证书签名请求。

4. 配置 kube-proxy

上面第3步中已经把kube-proxy复制到了kubernetes node节点的/usr/local/bin/目录下了,下面开始做配置。每台kubernetes node节点都需要做如下的操作。

(1)创建 kube-proxy 的service配置文件

在/usr/lib/systemd/system/目录下创建kube-proxy.service:

[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target [Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/local/bin/kube-proxy \
$KUBE_LOGTOSTDERR \
$KUBE_LOG_LEVEL \
$KUBE_MASTER \
$KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE= [Install]
WantedBy=multi-user.target

kube-proxy.service

(2)创建kube-proxy配置文件/etc/kubernetes/proxy

【注意】:需要修改每台kubernetes node的ip地址。以下以node1主机为例:

# vim /etc/kubernetes/proxy
###
# kubernetes proxy config # default config should be adequate # Add your own!
KUBE_PROXY_ARGS="--bind-address=172.16.7.151 --hostname-override=172.16.7.151 --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig --cluster-cidr=10.254.0.0/16"

/etc/kubernetes/proxy

【说明】:

  • --hostname-override 参数值必须与 kubelet 的值一致,否则 kube-proxy 启动后会找不到该 Node,从而不会创建任何 iptables 规则;
  • kube-proxy 根据 --cluster-cidr 判断集群内部和外部流量,指定 --cluster-cidr 或 --masquerade-all 选项后 kube-proxy 才会对访问 Service IP 的请求做 SNAT;
  • --kubeconfig 指定的配置文件嵌入了 kube-apiserver 的地址、用户名、证书、秘钥等请求和认证信息;
  • 预定义的 RoleBinding cluster-admin 将User system:kube-proxy 与 Role system:node-proxier 绑定,该 Role 授予了调用 kube-apiserver Proxy 相关 API 的权限。

(3)启动 kube-proxy

# systemctl daemon-reload
# systemctl enable kube-proxy
# systemctl start kube-proxy

启动 kube-proxy

5. 验证测试

创建一个niginx的service试一下集群是否可用。

# kubectl run nginx --replicas= --labels="run=load-balancer-example" --image=docker.io/nginx:latest --port=
deployment "nginx" created
# kubectl expose deployment nginx --type=NodePort --name=example-service
service "example-service" exposed
# kubectl describe svc example-service
Name: example-service
Namespace: default
Labels: run=load-balancer-example
Annotations: <none>
Selector: run=load-balancer-example
Type: NodePort
IP: 10.254.67.61
Port: <unset> /TCP
NodePort: <unset> /TCP
Endpoints: 172.30.32.2:,172.30.87.2:
Session Affinity: None
Events: <none> # kubectl get all
NAME READY STATUS RESTARTS AGE
po/nginx--nlsj1 / Running 5m
po/nginx--xr7zk / Running 5m NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/example-service 10.254.67.61 <nodes> :/TCP 1m
svc/kubernetes 10.254.0.1 <none> /TCP 5h NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deploy/nginx 5m NAME DESIRED CURRENT READY AGE
rs/nginx- 5m # curl "10.254.67.61:80"
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 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>

创建一个niginx的service试一下集群是否可用

浏览器输入172.16.7.151:32201或172.16.7.152:32201或者172.16.7.153:32201都可以得到nginx的页面。

查看运行的容器(在node1和node2上分别运行了一个pod):

# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7d2ef8e34e43 docker.io/nginx@sha256:fc6d2ef47e674a9ffb718b7ac361ec4e421e3a0ef2c93df79abbe4e9ffb5fa08 "nginx -g 'daemon off" minutes ago Up minutes k8s_nginx_nginx--xr7zk_default_c628f12f--11e7-9acc-005056b7609a_0
5bbb98fba623 registry.access.redhat.com/rhel7/pod-infrastructure "/usr/bin/pod" minutes ago Up minutes k8s_POD_nginx--xr7zk_default_c628f12f--11e7-9acc-005056b7609a_0

如果想删除刚才创建的deployment:

# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx 2m
# kubectl delete deployment nginx
deployment "nginx" deleted

九、安装和配置 kube-dns 插件

1. kube-dns是什么

刚才在上一步中创建了个Nginx deployment,得到了两个运行nginx服务的Pod。待Pod运行之后查看一下它们的IP,并在k8s集群内通过podIP和containerPort来访问Nginx服务。

获取Pod IP:

# kubectl get pod -o yaml -l run=load-balancer-example|grep podIP
podIP: 172.30.32.2
podIP: 172.30.87.2

然后在Kubernetes集群的任一节点上就可以通过podIP在k8s集群内访问Nginx服务了。

# curl "172.30.32.2:80"

但是这样存在几个问题:

  • 每次收到获取podIP太扯了,总不能每次都要手动改程序或者配置才能访问服务吧,要怎么提前知道podIP呢?
  • Pod在运行中可能会重建,Pod的IP地址会随着Pod的重启而变化,并 不建议直接拿Pod的IP来交互
  • 如何在多个Pod中实现负载均衡嘞?

使用k8s Service就可以解决。Service为一组Pod(通过labels来选择)提供一个统一的入口,并为它们提供负载均衡和自动服务发现。

所以紧接着就创建了个service:

# kubectl expose deployment nginx --type=NodePort --name=example-service

创建之后,仍需要获取Service的Cluster-IP,再结合Port访问Nginx服务。
获取IP:

# kubectl get service example-service
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
example-service 10.254.67.61 <nodes> :/TCP 1h

在集群内访问Service:

# curl "10.254.67.61:80" 

而在Kubernetes cluster外面,则只能通过http://node-ip:32201来访问。

虽然Service解决了Pod的服务发现和负载均衡问题,但存在着类似的问题:不提前知道Service的IP,还是需要改程序或配置啊。kube-dns就是用来解决上面这个问题的。

kube-dns可以解决Service的发现问题,k8s将Service的名称当做域名注册到kube-dns中,通过Service的名称就可以访问其提供的服务。也就是说其他应用能够直接使用服务的名字,不需要关心它实际的 ip 地址,中间的转换能够自动完成。名字和 ip 之间的转换就是 DNS 系统的功能。

kubu-dns 服务不是独立的系统服务,而是一种 addon ,作为插件来安装的,不是 kubernetes 集群必须的(但是非常推荐安装)。可以把它看做运行在集群上的应用,只不过这个应用比较特殊而已。

2. 安装配置kube-dns

官方的yaml文件目录:https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns。

kube-dns 有两种配置方式,在 1.3 之前使用 etcd + kube2sky + skydns 的方式,在 1.3 之后可以使用 kubedns + dnsmasq 的方式。

该插件直接使用kubernetes部署,实际上kube-dns插件只是运行在kube-system命名空间下的Pod,完全可以手动创建它。官方的配置文件中包含以下镜像:

gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.
gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.
gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.

(1)下载yaml文件

从 https://github.com/rootsongjc/follow-me-install-kubernetes-cluster/tree/master/manifests/kubedns 下载 kubedns-cm.yaml、kubedns-sa.yaml、kubedns-controller.yaml和kubedns-svc.yaml这4个文件下来,并上传到/opt/kube-dns/目录下。

# mkdir /opt/kube-dns
# cd /opt/kube-dns/
# ls kubedns-*
kubedns-cm.yaml kubedns-controller.yaml kubedns-sa.yaml kubedns-svc.yaml

修改kubedns-controller.yaml文件,将其中的镜像地址改为时速云的地址:

index.tenxcloud.com/jimmy/k8s-dns-dnsmasq-nanny-amd64:1.14.
index.tenxcloud.com/jimmy/k8s-dns-kube-dns-amd64:1.14.
index.tenxcloud.com/jimmy/k8s-dns-sidecar-amd64:1.14.
  • kubeDNS:提供了原来 kube2sky + etcd + skyDNS 的功能,可以单独对外提供 DNS 查询服务
  • dnsmasq: 一个轻量级的 DNS 服务软件,可以提供 DNS 缓存功能。kubeDNS 模式下,dnsmasq 在内存中预留一块大小(默认是 1G)的地方,保存当前最常用的 DNS 查询记录,如果缓存中没有要查找的记录,它会到 kubeDNS 中查询,并把结果缓存起来。

(2)系统预定义的 RoleBinding

预定义的 RoleBinding system:kube-dns 将 kube-system 命名空间的 kube-dns ServiceAccount 与 system:kube-dns Role 绑定, 该 Role 具有访问 kube-apiserver DNS 相关 API 的权限。

[root@node1 ~]# kubectl get clusterrolebindings system:kube-dns -o yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: --14T00::08Z
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:kube-dns
resourceVersion: ""
selfLink: /apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindingssystem%3Akube-dns
uid: 18fa2aff-98e6-11e7-a153-005056b7609a
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kube-dns
subjects:
- kind: ServiceAccount
name: kube-dns
namespace: kube-system

kubedns-controller.yaml 中定义的 Pods 时使用了 kubedns-sa.yaml 文件定义的 kube-dns ServiceAccount,所以具有访问 kube-apiserver DNS 相关 API 的权限。

(3)配置 kube-dns ServiceAccount

无需修改。

(4)配置 kube-dns 服务

# diff kubedns-svc.yaml.base kubedns-svc.yaml
30c30
< clusterIP: __PILLAR__DNS__SERVER__
---
> clusterIP: 10.254.0.2

【说明】:

  • spec.clusterIP = 10.254.0.2,即明确指定了 kube-dns Service IP,这个 IP 需要和 kubelet 的 --cluster-dns 参数值一致。

(5)配置 kube-dns Deployment

# diff kubedns-controller.yaml.base kubedns-controller.yaml

【说明】:

  • 使用系统已经做了 RoleBinding 的 kube-dns ServiceAccount,该账户具有访问 kube-apiserver DNS 相关 API 的权限。

(6)执行所有定义文件

# pwd
/opt/kube-dns
# ls
kubedns-cm.yaml kubedns-controller.yaml kubedns-sa.yaml kubedns-svc.yaml
# kubectl create -f .
configmap "kube-dns" created
deployment "kube-dns" created
serviceaccount "kube-dns" created
service "kube-dns" created

在3台node节点上查看生成的kube-dns相关pod和container:

[root@node2 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9b1dbfde7eac index.tenxcloud.com/jimmy/k8s-dns-sidecar-amd64@sha256:947271f3e08b1fd61c4b26478f08d3a8f10bbca90d4dec067e3b33be08066970 "/sidecar --v=2 --log" hours ago Up hours k8s_sidecar_kube-dns--6vnsj_kube-system_efb96c05--11e7-9acc-005056b7609a_0
a455dc0a9b55 index.tenxcloud.com/jimmy/k8s-dns-dnsmasq-nanny-amd64@sha256:b253876345427dbd626b145897be51d87bfd535e2cd5d7d166deb97ea37701f8 "/dnsmasq-nanny -v=2 " hours ago Up hours k8s_dnsmasq_kube-dns--6vnsj_kube-system_efb96c05--11e7-9acc-005056b7609a_0
7f18c10c8d60 index.tenxcloud.com/jimmy/k8s-dns-kube-dns-amd64@sha256:94426e872d1a4a0cf88e6c5cd928a1acbe1687871ae5fe91ed751593aa6052d3 "/kube-dns --domain=c" hours ago Up hours k8s_kubedns_kube-dns--6vnsj_kube-system_efb96c05--11e7-9acc-005056b7609a_0
a6feb213296b registry.access.redhat.com/rhel7/pod-infrastructure "/usr/bin/pod" hours ago Up hours k8s_POD_kube-dns--6vnsj_kube-system_efb96c05--11e7-9acc-005056b7609a_0

3. 检查 kube-dns 功能

上面是通过 kubectl run 来启动了第一个Pod,但是并不支持所有的功能。使用kubectl run在设定很复杂的时候需要非常长的一条语句,敲半天也很容易出错,也没法保存,在碰到转义字符的时候也经常会很抓狂,所以更多场景下会使用yaml或者json文件,而使用kubectl create或者delete就可以利用这些yaml文件。通过 kubectl create -f file.yaml 来创建资源。kubectl run 并不是直接创建一个Pod,而是先创建一个Deployment资源 (replicas=1),再由Deployment来自动创建Pod。

新建一个 Deployment:

[root@node1 kube-dns]# vim my-nginx.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas:
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: docker.io/nginx:latest
ports:
- containerPort:

my-nginx.yaml

Export 该 Deployment,生成 my-nginx 服务:

# kubectl expose deploy my-nginx
service "my-nginx" exposed
# kubectl get services --all-namespaces |grep my-nginx
default my-nginx 10.254.34.181 <none> /TCP 26s

创建另一个 Pod,查看 /etc/resolv.conf 是否包含 kubelet 配置的 --cluster-dns 和 --cluster-domain,是否能够将服务my-nginx 解析到 Cluster IP 10.254.34.181。

[root@node1 kube-dns]# vim dns-test-busybox.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox
command:
- sleep
- ""
imagePullPolicy: IfNotPresent
name: busybox
restartPolicy: Always

dns-test-busybox.yaml

[root@node1 kube-dns]# kubectl create -f dns-test-busybox.yaml
pod "busybox" created
[root@node1 kube-dns]# kubectl exec -ti busybox -- nslookup kubernetes.default
Server: 10.254.0.2
Address : 10.254.0.2 kube-dns.kube-system.svc.cluster.local Name: kubernetes.default
Address : 10.254.0.1 kubernetes.default.svc.cluster.local kubectl exec -ti busybox -- ping my-nginx
PING my-nginx (10.254.34.181): data bytes kubectl exec -ti busybox -- ping kubernetes
PING kubernetes (10.254.0.1): data bytes kubectl exec -ti busybox -- ping kube-dns.kube-system.svc.cluster.local
PING kube-dns.kube-system.svc.cluster.local (10.254.0.2): data bytes

从结果来看,service名称可以正常解析。

另外,使用kubernetes的时候建议不要再用docker命令操作。

二进制方式部署Kubernetes 1.6.0集群(开启TLS)的更多相关文章

  1. Kubeadm搭建高可用(k8s)Kubernetes v1.24.0集群

    文章转载自:https://i4t.com/5451.html 背景 Kubernetes 1.24新特性 从kubelet中移除dockershim,自1.20版本被弃用之后,dockershim组 ...

  2. 使用二进制的方式部署 K8S-1.16 高可用集群

    一.项目介绍 项目致力于让有意向使用原生kubernetes集群的企业或个人,可以方便的.系统的使用二进制的方式手工搭建kubernetes高可用集群.并且让相关的人员可以更好的理解kubernete ...

  3. 使用 Kubeadm 安装部署 Kubernetes 1.12.1 集群

    手工搭建 Kubernetes 集群是一件很繁琐的事情,为了简化这些操作,就产生了很多安装配置工具,如 Kubeadm ,Kubespray,RKE 等组件,我最终选择了官方的 Kubeadm 主要是 ...

  4. 使用kubeadm部署K8S v1.17.0集群

    kubeadm部署K8S集群 安装前的准备 集群机器 172.22.34.34 K8S00 172.22.34.35 K8S01 172.22.34.36 K8S02 注意: 本文档中的 etcd . ...

  5. 【葵花宝典】lvs+keepalived部署kubernetes(k8s)高可用集群

    一.部署环境 1.1 主机列表 主机名 Centos版本 ip docker version flannel version Keepalived version 主机配置 备注 lvs-keepal ...

  6. 利用 kubeasz 给 suse 12 部署 kubernetes 1.20.1 集群

    文章目录 1.前情提要 2.环境准备 2.1.环境介绍 2.2.配置静态网络 2.3.配置ssh免密 2.4.批量开启模块以及创建文件 2.5.安装ansible 2.5.1.安装pip 2.5.2. ...

  7. 部署Redis Cluster 6.0 集群并开启密码认证 和 Redis-cluster-proxy负载

    部署Redis Cluster集群并开启密码认证 如果只想简单的搭建Redis Cluster,不需要设置密码和公网访问,可以参考官方文档. 节点介绍 Cluster模式推荐最少有6个节点,本次实验搭 ...

  8. 利用ansible来做kubernetes 1.10.3集群高可用的一键部署

    请读者务必保持环境一致 安装过程中需要下载所需系统包,请务必使所有节点连上互联网. 本次安装的集群节点信息 实验环境:VMware的虚拟机 IP地址 主机名 CPU 内存 192.168.77.133 ...

  9. 二进制包部署Kubernetes集群

    今天这篇文章教给大家如何快速部署一套Kubernetes集群.K8S集群部署有几种方式:kubeadm.minikube和二进制包.前两者属于自动部署,简化部署操作,我们这里强烈推荐初学者使用二进制包 ...

随机推荐

  1. 利用Confluence搭建企业Wiki

    Confluence安装与部署 下载安装包及破解包 安装包下载地址:https://www.atlassian.com/software/confluence/download-archives 破解 ...

  2. go语言从零学起(二)--list循环删除元素(转载)

    本篇系转载 在使用go的container/list的package时,你可能会无意间踩一个小坑,那就是list的循环删除元素. list删除元素,直观写下来的代码如下: package main i ...

  3. UIApplication概述

    1.通过类方法sharedApplication可以获得唯一实例 2.可以打开mail或者email,通过openUrl方法. 3.指定UIApplicationDelegate可以跟踪各种应用状态. ...

  4. 如何在Mongodb中实现数据超时自动删除功能?

    在工作过程中,我们难免会遇到这样的问题,我们想保存一些数据,但是我们对这些数据的要求并不高,有时候往往只是想要某个时间范围内的数据,比如我们如果永远只关心从当前时间往前推半年内的数据特性,那么我们就不 ...

  5. 如何给list清空

    1.list = new ArrayList<String>(): 2.list.clear(); 3.list=null;

  6. wav文件格式及ffmpeg处理命令

    wav文件头详解 符合RIFF(Resource Interchange File Format)规范的wav文件的文件头记录了音频流的编码参数等基本信息.wav文件由多个块组成,至少包含RIFF标志 ...

  7. [CQOI2009]DANCE跳舞(ISAP写法)

    https://daniu.luogu.org/problemnew/show/3153 #include<queue> #include<cstdio> #include&l ...

  8. 解除单个文件的与svn服务器的关联

    有些文件和个人开发环境有关不需要和svn服务器做同步,可以取消其和svn服务的关联. 右键选中要取消关联的文件,右键菜单 Tortoise SVN  --->   unversion and a ...

  9. 在EF6.0中打印数据库操作日志

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  10. 配置SpringBoot-从日志系统配置说起

    大小系统都需要打日志. 系统在不同环境下对日志的配置要求是不一样的 比如 开发本地: 直接输出到控制台 生产环境: 输出到文件或者额外的日志收集系统, 比如 graylog. (本文不探讨具体日志系统 ...