# 部署node节点 kubernetes node 节点包含如下组件: + Flanneld:参考我之前写的文章[Kubernetes基于Flannel的网络配置](http://rootsongjc.github.io/blogs/kubernetes-network-config/),之前没有配置TLS,现在需要在serivce配置文件中增加TLS配置。 + Docker1.12.5:docker的安装很简单,这里也不说了。 + kubelet + kube-proxy 下面着重讲`kubelet`和`kube-proxy`的安装,同时还要将之前安装的flannel集成TLS验证。 **注意**:每台 node 上都需要安装 flannel,master 节点上可以不必安装。 ## 目录和文件 我们再检查一下三个节点上,经过前几步操作生成的配置文件。 ``` bash $ 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 $ ls /etc/kubernetes/ apiserver bootstrap.kubeconfig config controller-manager kubelet kube-proxy.kubeconfig proxy scheduler ssl token.csv ``` ## 配置Flanneld 参考我之前写的文章[Kubernetes基于Flannel的网络配置](http://rootsongjc.github.io/blogs/kubernetes-network-config/),之前没有配置TLS,现在需要在serivce配置文件中增加TLS配置。 直接使用yum安装flanneld即可。 ```shell yum install -y flannel ``` service配置文件`/usr/lib/systemd/system/flanneld.service`。 ```ini [Unit] Description=Flanneld overlay address etcd agent After=network.target After=network-online.target Wants=network-online.target After=etcd.service Before=docker.service [Service] Type=notify EnvironmentFile=/etc/sysconfig/flanneld EnvironmentFile=-/etc/sysconfig/docker-network ExecStart=/usr/bin/flanneld-start \ -etcd-endpoints=${ETCD_ENDPOINTS} \ -etcd-prefix=${ETCD_PREFIX} \ $FLANNEL_OPTIONS ExecStartPost=/usr/libexec/flannel/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker Restart=on-failure [Install] WantedBy=multi-user.target RequiredBy=docker.service ``` `/etc/sysconfig/flanneld`配置文件。 ```ini # Flanneld configuration options # etcd url location. Point this to the server where etcd runs ETCD_ENDPOINTS="https://172.20.0.113:2379,https://172.20.0.114:2379,https://172.20.0.115:2379" # etcd config key. This is the configuration key that flannel queries # For address range assignment ETCD_PREFIX="/kube-centos/network" # Any additional options that you want to pass FLANNEL_OPTIONS="-etcd-cafile=/etc/kubernetes/ssl/ca.pem -etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem -etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem" ``` 在FLANNEL_OPTIONS中增加TLS的配置。 **在etcd中创建网络配置** 执行下面的命令为docker分配IP地址段。 ```shell etcdctl --endpoints=https://172.20.0.113:2379,https://172.20.0.114:2379,https://172.20.0.115:2379 \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/kubernetes/ssl/kubernetes.pem \ --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \ mkdir /kube-centos/network etcdctl --endpoints=https://172.20.0.113:2379,https://172.20.0.114:2379,https://172.20.0.115:2379 \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/kubernetes/ssl/kubernetes.pem \ --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \ mk /kube-centos/network/config '{"Network":"172.30.0.0/16","SubnetLen":24,"Backend":{"Type":"vxlan"}}' ``` 如果你要使用`host-gw`模式,可以直接将vxlan改成`host-gw`即可。 **配置Docker** Flannel的[文档](https://github.com/coreos/flannel/blob/master/Documentation/running.md)中有写**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. Since 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` 如果你不是使用yum安装的flanneld,那么需要下载flannel github release中的tar包,解压后会获得一个**mk-docker-opts.sh**文件。 这个文件是用来`Generate Docker daemon options based on flannel env file`。 执行`./mk-docker-opts.sh -i`将会生成如下两个文件环境变量文件。 /run/flannel/subnet.env ``` FLANNEL_NETWORK=172.30.0.0/16 FLANNEL_SUBNET=172.30.46.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=false ``` /run/docker_opts.env ``` DOCKER_OPT_BIP="--bip=172.30.46.1/24" DOCKER_OPT_IPMASQ="--ip-masq=true" DOCKER_OPT_MTU="--mtu=1450" ``` **设置docker0网桥的IP地址** ```shell source /run/flannel/subnet.env ifconfig docker0 $FLANNEL_SUBNET ``` 这样docker0和flannel网桥会在同一个子网中,如 ``` 6: docker0: mtu 1500 qdisc noqueue state DOWN link/ether 02:42:da:bf:83:a2 brd ff:ff:ff:ff:ff:ff inet 172.30.38.1/24 brd 172.30.38.255 scope global docker0 valid_lft forever preferred_lft forever 7: flannel.1: mtu 1450 qdisc noqueue state UNKNOWN link/ether 9a:29:46:61:03:44 brd ff:ff:ff:ff:ff:ff inet 172.30.38.0/32 scope global flannel.1 valid_lft forever preferred_lft forever ``` 同时在 docker 的配置文件 [docker.service](../systemd/docker.service) 中增加环境变量配置: ```ini EnvironmentFile=-/run/flannel/docker EnvironmentFile=-/run/docker_opts.env EnvironmentFile=-/run/flannel/subnet.env ``` 防止主机重启后 docker 自动重启时加载不到该上述环境变量。 **启动docker** 重启了docker后还要重启kubelet,这时又遇到问题,kubelet启动失败。报错: ``` Mar 31 16:44:41 sz-pg-oam-docker-test-002.tendcloud.com kubelet[81047]: error: failed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd" ``` 这是kubelet与docker的**cgroup driver**不一致导致的,kubelet启动的时候有个`—cgroup-driver`参数可以指定为"cgroupfs"或者“systemd”。 ``` --cgroup-driver string Driver that the kubelet uses to manipulate cgroups on the host. Possible values: 'cgroupfs', 'systemd' (default "cgroupfs") ``` **启动flannel** ```shell systemctl daemon-reload systemctl start flanneld systemctl status flanneld ``` 现在查询etcd中的内容可以看到: ```bash $etcdctl --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/kubernetes/ssl/kubernetes.pem \ --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \ ls /kube-centos/network/subnets /kube-centos/network/subnets/172.30.14.0-24 /kube-centos/network/subnets/172.30.38.0-24 /kube-centos/network/subnets/172.30.46.0-24 $etcdctl --endpoints=${ETCD_ENDPOINTS} \ --ca-file=/etc/kubernetes/ssl/ca.pem \ --cert-file=/etc/kubernetes/ssl/kubernetes.pem \ --key-file=/etc/kubernetes/ssl/kubernetes-key.pem \ get /kube-centos/network/config { "Network": "172.30.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" } } $etcdctl get /kube-centos/network/subnets/172.30.14.0-24 {"PublicIP":"172.20.0.114","BackendType":"vxlan","BackendData":{"VtepMAC":"56:27:7d:1c:08:22"}} $etcdctl get /kube-centos/network/subnets/172.30.38.0-24 {"PublicIP":"172.20.0.115","BackendType":"vxlan","BackendData":{"VtepMAC":"12:82:83:59:cf:b8"}} $etcdctl get /kube-centos/network/subnets/172.30.46.0-24 {"PublicIP":"172.20.0.113","BackendType":"vxlan","BackendData":{"VtepMAC":"e6:b2:fd:f6:66:96"}} ``` ## 安装和配置 kubelet kubelet 启动时向 kube-apiserver 发送 TLS bootstrapping 请求,需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper cluster 角色(role), 然后 kubelet 才能有权限创建认证请求(certificate signing requests): ``` bash $ cd /etc/kubernetes $ kubectl create clusterrolebinding kubelet-bootstrap \ --clusterrole=system:node-bootstrapper \ --user=kubelet-bootstrap ``` + `--user=kubelet-bootstrap` 是在 `/etc/kubernetes/token.csv` 文件中指定的用户名,同时也写入了 `/etc/kubernetes/bootstrap.kubeconfig` 文件; ### 下载最新的 kubelet 和 kube-proxy 二进制文件 ``` bash $ 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 $ cp -r ./server/bin/{kube-proxy,kubelet} /usr/local/bin/ ``` ### 创建 kubelet 的service配置文件 文件位置`/usr/lib/systemd/system/kubelet.service`。 ```ini [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的配置文件`/etc/kubernetes/kubelet`。其中的IP地址更改为你的每台node节点的IP地址。 注意:`/var/lib/kubelet`需要手动创建。 ``` bash ### ## 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.20.0.113" # ## 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.20.0.113" # ## location of the api-server KUBELET_API_SERVER="--api-servers=http://172.20.0.113:8080" # ## pod infrastructure container KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=sz-pg-oam-docker-hub-001.tendcloud.com/library/pod-infrastructure:rhel7" # ## 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" ``` + `--address` 不能设置为 `127.0.0.1`,否则后续 Pods 访问 kubelet 的 API 接口时会失败,因为 Pods 访问的 `127.0.0.1` 指向自己而不是 kubelet; + 如果设置了 `--hostname-override` 选项,则 `kube-proxy` 也需要设置该选项,否则会出现找不到 Node 的情况; + `--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` 指定域名后缀,这两个参数同时指定后才会生效; + `—kubeconfig=/etc/kubernetes/kubelet.kubeconfig `中指定的`kubelet.kubeconfig`文件在第一次启动kubelet之前并不存在,请看下文,当通过CSR请求后会自动生成`kubelet.kubeconfig`文件,如果你的节点上已经生成了`~/.kube/config`文件,你可以将该文件拷贝到该路径下,并重命名为`kubelet.kubeconfig`,所有node节点可以共用同一个kubelet.kubeconfig文件,这样新添加的节点就不需要再创建CSR请求就能自动添加到kubernetes集群中。同样,在任意能够访问到kubernetes集群的主机上使用`kubectl —kubeconfig`命令操作集群时,只要使用`~/.kube/config`文件就可以通过权限认证,因为这里面已经有认证信息并认为你是admin用户,对集群拥有所有权限。 完整 unit 见 [kubelet.service](./systemd/kubelet.service) ### 启动kublet ``` bash $ systemctl daemon-reload $ systemctl enable kubelet $ systemctl start kubelet $ systemctl status kubelet ``` ### 通过 kublet 的 TLS 证书请求 kubelet 首次启动时向 kube-apiserver 发送证书签名请求,必须通过后 kubernetes 系统才会将该 Node 加入到集群。 查看未授权的 CSR 请求 ``` bash $ kubectl get csr NAME AGE REQUESTOR CONDITION csr-2b308 4m kubelet-bootstrap Pending $ kubectl get nodes No resources found. ``` 通过 CSR 请求 ``` bash $ kubectl certificate approve csr-2b308 certificatesigningrequest "csr-2b308" approved $ kubectl get nodes NAME STATUS AGE VERSION 10.64.3.7 Ready 49m v1.6.1 ``` 自动生成了 kubelet kubeconfig 文件和公私钥 ``` bash $ ls -l /etc/kubernetes/kubelet.kubeconfig -rw------- 1 root root 2284 Apr 7 02:07 /etc/kubernetes/kubelet.kubeconfig $ ls -l /etc/kubernetes/ssl/kubelet* -rw-r--r-- 1 root root 1046 Apr 7 02:07 /etc/kubernetes/ssl/kubelet-client.crt -rw------- 1 root root 227 Apr 7 02:04 /etc/kubernetes/ssl/kubelet-client.key -rw-r--r-- 1 root root 1103 Apr 7 02:07 /etc/kubernetes/ssl/kubelet.crt -rw------- 1 root root 1675 Apr 7 02:07 /etc/kubernetes/ssl/kubelet.key ``` 注意:假如你更新kubernetes的证书,只要没有更新`token.csv`,当重启kubelet后,该node就会自动加入到kuberentes集群中,而不会重新发送`certificaterequest`,也不需要在master节点上执行`kubectl certificate approve`操作。前提是不要删除node节点上的`/etc/kubernetes/ssl/kubelet*`和`/etc/kubernetes/kubelet.kubeconfig`文件。否则kubelet启动时会提示找不到证书而失败。 ## 配置 kube-proxy **创建 kube-proxy 的service配置文件** 文件路径`/usr/lib/systemd/system/kube-proxy.service`。 ```ini [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=65536 [Install] WantedBy=multi-user.target ``` kube-proxy配置文件`/etc/kubernetes/proxy`。 ``` bash ### # kubernetes proxy config # default config should be adequate # Add your own! KUBE_PROXY_ARGS="--bind-address=172.20.0.113 --hostname-override=172.20.0.113 --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig --cluster-cidr=10.254.0.0/16" ``` + `--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 的权限; 完整 unit 见 [kube-proxy.service](./systemd/kube-proxy.service) ### 启动 kube-proxy ``` bash $ systemctl daemon-reload $ systemctl enable kube-proxy $ systemctl start kube-proxy $ systemctl status kube-proxy ``` ## 验证测试 我们创建一个niginx的service试一下集群是否可用。 ```bash $ kubectl run nginx --replicas=2 --labels="run=load-balancer-example" --image=sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx:1.9 --port=80 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: Selector: run=load-balancer-example Type: NodePort IP: 10.254.62.207 Port: 80/TCP NodePort: 32724/TCP Endpoints: 172.30.60.2:80,172.30.94.2:80 Session Affinity: None Events: $ curl "10.254.62.207:80" Welcome to nginx!

Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.

``` 提示:上面的测试示例中使用的nginx是我的私有镜像仓库中的镜像`sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx:1.9`,大家在测试过程中请换成自己的nginx镜像地址。 访问`172.20.0.113:32724`或`172.20.0.114:32724`或者`172.20.0.115:32724`都可以得到nginx的页面。 ![welcome-nginx](http://olz1di9xf.bkt.clouddn.com/kubernetes-installation-test-nginx.png)