├── README.md ├── pic ├── up500课程结构图.png ├── up500课程结构图.vsdx ├── 002-openfiler-index.png ├── 001-openfiler-signin.png ├── 001-kubernetes-dashboard.png ├── 004-openfiler-create-pv.png ├── 005-openfiler-create-pv.png ├── 006-openfiler-create-pv.png ├── 010-openfiler-create-lv.png ├── 007-openfiler-create-share.png ├── 008-openfiler-create-share.png ├── 009-openfiler-create-share.png ├── 018-openfiler-create-share.png ├── 013-openfiler-create-share-vg.png ├── 011-openfiler-create-share-dir.png ├── 012-openfiler-create-share-dir.png ├── 017-openfiler-create-share-dir.png ├── 019-openfiler-create-share-nfs.png ├── 003-openfiler-nfs-service-enable.png ├── 016-openfiler-create-share-dir-vg.png ├── 014-openfiler-create-network-access.png └── 015-openfiler-create-share-dir-default.png ├── scripts ├── init-k8s.sh └── init-k8s-3nodes.sh ├── Kubernetes 1.4 高级课程-资源管理.md ├── Kubernetes 1.4 高级课程-集群管理.md └── Kubernetes 1.4 基础课程.md /README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes-basic 2 | 3 | **尚观Kubernetes 1.4 课程** 4 | -------------------------------------------------------------------------------- /pic/up500课程结构图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/up500课程结构图.png -------------------------------------------------------------------------------- /pic/up500课程结构图.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/up500课程结构图.vsdx -------------------------------------------------------------------------------- /pic/002-openfiler-index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/002-openfiler-index.png -------------------------------------------------------------------------------- /pic/001-openfiler-signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/001-openfiler-signin.png -------------------------------------------------------------------------------- /pic/001-kubernetes-dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/001-kubernetes-dashboard.png -------------------------------------------------------------------------------- /pic/004-openfiler-create-pv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/004-openfiler-create-pv.png -------------------------------------------------------------------------------- /pic/005-openfiler-create-pv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/005-openfiler-create-pv.png -------------------------------------------------------------------------------- /pic/006-openfiler-create-pv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/006-openfiler-create-pv.png -------------------------------------------------------------------------------- /pic/010-openfiler-create-lv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/010-openfiler-create-lv.png -------------------------------------------------------------------------------- /pic/007-openfiler-create-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/007-openfiler-create-share.png -------------------------------------------------------------------------------- /pic/008-openfiler-create-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/008-openfiler-create-share.png -------------------------------------------------------------------------------- /pic/009-openfiler-create-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/009-openfiler-create-share.png -------------------------------------------------------------------------------- /pic/018-openfiler-create-share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/018-openfiler-create-share.png -------------------------------------------------------------------------------- /pic/013-openfiler-create-share-vg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/013-openfiler-create-share-vg.png -------------------------------------------------------------------------------- /pic/011-openfiler-create-share-dir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/011-openfiler-create-share-dir.png -------------------------------------------------------------------------------- /pic/012-openfiler-create-share-dir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/012-openfiler-create-share-dir.png -------------------------------------------------------------------------------- /pic/017-openfiler-create-share-dir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/017-openfiler-create-share-dir.png -------------------------------------------------------------------------------- /pic/019-openfiler-create-share-nfs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/019-openfiler-create-share-nfs.png -------------------------------------------------------------------------------- /pic/003-openfiler-nfs-service-enable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/003-openfiler-nfs-service-enable.png -------------------------------------------------------------------------------- /pic/016-openfiler-create-share-dir-vg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/016-openfiler-create-share-dir-vg.png -------------------------------------------------------------------------------- /pic/014-openfiler-create-network-access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/014-openfiler-create-network-access.png -------------------------------------------------------------------------------- /pic/015-openfiler-create-share-dir-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kissingwolf/kubernetes-basic/HEAD/pic/015-openfiler-create-share-dir-default.png -------------------------------------------------------------------------------- /scripts/init-k8s.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | Kubeadmin_Init_Token="0a349c.013fd0942f0c8498" 3 | Foundation_Num="0" 4 | rht-vmctl reset master 5 | rht-vmctl reset nodea 6 | rht-vmctl reset nodeb 7 | sleep 10 8 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "systemctl disable firewalld " ; done 9 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "systemctl stop firewalld " ; done 10 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "systemctl disable NetworkManager " ; done 11 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "systemctl stop NetworkManager " ; done 12 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "setenforce 0 " ; done 13 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config " ; done 14 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "grep "SELINUX=" /etc/selinux/config " ; done 15 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "yum install wget bzip2 net-tools -y " ; done 16 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "wget http://classroom.example.com/materials/kubernetes-1.4.repo -O /etc/yum.repos.d/k8s.repo " ; done 17 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "yum install docker-engine kubeadm kubectl kubelet kubernetes-cni -y " ; done 18 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "systemctl enable docker kubelet " ; done 19 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "systemctl start docker " ; done 20 | ssh root@master$Foundation_Num "wget http://classroom.example.com/materials/k8s-imgs/k8s-1.4-master-img.tbz " 21 | for i in nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "wget http://classroom.example.com/materials/k8s-imgs/k8s-1.4-node-img.tbz " ; done 22 | ssh root@master$Foundation_Num "tar -jxf k8s-1.4-master-img.tbz" 23 | ssh root@master$Foundation_Num 'for i in ./k8s-1.4-master-img/*.img ; do docker load -i $i ; done' 24 | for i in nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "tar -jxf k8s-1.4-node-img.tbz " ; done 25 | for i in nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i 'for i in ./k8s-1.4-node-img/*.img ; do docker load -i $i ; done' ; done 26 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "getenforce " ; done 27 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i "systemctl start kubelet " ; done 28 | ssh root@master$Foundation_Num "kubeadm init --token $Kubeadmin_Init_Token" 29 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 30 | for i in nodea$Foundation_Num nodeb$Foundation_Num ; do ssh root@$i " kubeadm join --token $Kubeadmin_Init_Token 172.25.0.10 " ; done 31 | ssh root@master$Foundation_Num "kubectl get nodes" 32 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 33 | ssh root@master$Foundation_Num "kubectl apply -f http://classroom.example.com/materials/k8s-conf/weave-kube" 34 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 35 | ssh root@master$Foundation_Num "kubectl apply -f http://classroom.example.com/materials/k8s-conf/kubernetes-dashboard.yaml" 36 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 37 | -------------------------------------------------------------------------------- /scripts/init-k8s-3nodes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | Kubeadmin_Init_Token="0a349c.013fd0942f0c8498" 3 | Foundation_Num="0" 4 | rht-vmctl reset master 5 | rht-vmctl reset nodea 6 | rht-vmctl reset nodeb 7 | rht-vmctl reset nodec 8 | sleep 10 9 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "systemctl disable firewalld " ; done 10 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "systemctl stop firewalld " ; done 11 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "systemctl disable NetworkManager " ; done 12 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "systemctl stop NetworkManager " ; done 13 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "setenforce 0 " ; done 14 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config " ; done 15 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "grep "SELINUX=" /etc/selinux/config " ; done 16 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "yum install wget bzip2 net-tools -y " ; done 17 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "wget http://classroom.example.com/materials/kubernetes-1.4.repo -O /etc/yum.repos.d/k8s.repo " ; done 18 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "yum install docker-engine kubeadm kubectl kubelet kubernetes-cni -y " ; done 19 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "systemctl enable docker kubelet " ; done 20 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "systemctl start docker " ; done 21 | ssh root@master$Foundation_Num "wget http://classroom.example.com/materials/k8s-imgs/k8s-1.4-master-img.tbz " 22 | for i in nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "wget http://classroom.example.com/materials/k8s-imgs/k8s-1.4-node-img.tbz " ; done 23 | ssh root@master$Foundation_Num "tar -jxf k8s-1.4-master-img.tbz" 24 | ssh root@master$Foundation_Num 'for i in ./k8s-1.4-master-img/*.img ; do docker load -i $i ; done' 25 | for i in nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "tar -jxf k8s-1.4-node-img.tbz " ; done 26 | for i in nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i 'for i in ./k8s-1.4-node-img/*.img ; do docker load -i $i ; done' ; done 27 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "getenforce " ; done 28 | for i in master$Foundation_Num nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i "systemctl start kubelet " ; done 29 | ssh root@master$Foundation_Num "kubeadm init --token $Kubeadmin_Init_Token" 30 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 31 | for i in nodea$Foundation_Num nodeb$Foundation_Num nodec$Foundation_Num ; do ssh root@$i " kubeadm join --token $Kubeadmin_Init_Token 172.25.0.10 " ; done 32 | ssh root@master$Foundation_Num "kubectl get nodes" 33 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 34 | ssh root@master$Foundation_Num "kubectl apply -f http://classroom.example.com/materials/k8s-conf/weave-kube" 35 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 36 | ssh root@master$Foundation_Num "kubectl apply -f http://classroom.example.com/materials/k8s-conf/kubernetes-dashboard.yaml" 37 | ssh root@master$Foundation_Num "kubectl get pod --all-namespaces" 38 | -------------------------------------------------------------------------------- /Kubernetes 1.4 高级课程-资源管理.md: -------------------------------------------------------------------------------- 1 | # Kubernetes 1.4 高级课程 2 | 3 | ###### kissingwolf@gmail.com 4 | 5 | [TOC] 6 | 7 | ## Kubernetes 资源管理 8 | 9 | Kubernetes 资源管理包括Compute Resources(计算资源)、Limit Range(限制范围)、QoS(服务质量控制)和 Resource Quota (资源配额)等方面。 10 | 11 | ### Compute Resources 计算资源管理 12 | 13 | #### 资源分类说明 14 | 15 | Compute Resources 计算资源配置分为两种,Resource Requrests (资源请求)和Resource Limits (资源限制)。 16 | 17 | * Resource Requrests (资源请求),简称Requests ,表示希望分配给容器的最低保证其运行的资源量。 18 | * Resource Limits (资源限制),简称Limits ,表示最多分配给容器运行的资源量上限。 19 | 20 | 在Kubernetes 中,计算资源分为CPU和Memory(内存)两种。这两种资源都有其基本的分配单位: 21 | 22 | * CPU 基本分配单位为Cores(核心数),一个Core(核心)标记为单位 “1” ,CPU资源最多支持三位小数分配,比如1.5 代表分配1.5个核心。另外可以使用m为单位代表1/1000的核心,比如100m代表0.1 核心。官方推荐使用100m的形式代表0.1 cpu core。 23 | * Memory(内存)基本分配单位为Bytes(字节数),Memory(内存)以整数加上容量单位来表示,十进制单位K、M、G、T、P、E,二进制单位Ki、Mi、Gi、Ti、Pi、Ei。需要注意m和M不是同一单位,m表示千分之一单位(mili unit),M表示十进制1000。 24 | 25 | 在Pod配置中,计算资源配置项有四个: 26 | 27 | * spec.container[].resources.requests.cpu 28 | * spec.container[].resources.requests.memory 29 | * spec.container[].resources.limits.cpu 30 | * spec.container[].resources.limits.memory 31 | 32 | 四个配置项分别对应容器中的CPU和Memory(内存)的Requests和Limits。 33 | 34 | 其特点如下: 35 | 36 | * Requests和Limits都是可选配置,如果在创建Pod时没有指定,则继承Cluster配置。 37 | * 如果仅仅设置Limits而没有设置Requests,则Requests等于Limits。 38 | * 任何情况下Limits应该大于等于Requests。 39 | * Pod中的容器Requests是启动容器的最小资源需求,如果Node无法满足创建Pod容器的Requests,则该Node上无法创建该Pod容器。 40 | 41 | #### 配置资源例子 42 | 43 | 我们来看一个Pod配置的例子,创建一个配置文件pod-resource.yaml,内容如下: 44 | 45 | ```yaml 46 | apiVersion: v1 47 | kind: Pod 48 | metadata: 49 | name: test-pod-resource 50 | spec: 51 | containers: 52 | - name: nginx 53 | image: nginx:latest 54 | imagePullPolicy: IfNotPresent 55 | resources: 56 | requests: 57 | memory: "64Mi" 58 | cpu: "250m" 59 | limits: 60 | memory: "128Mi" 61 | cpu: "500m" 62 | ``` 63 | 64 | 我们可以通过kubectl create 创建Pod : 65 | 66 | ```shell 67 | [root@master0 ~]# kubectl create -f pod-resource.yaml 68 | pod "test-pod-resource" created 69 | ``` 70 | 71 | 可以通过kubectl describe 查看创建Pod的具体信息: 72 | 73 | ```shell 74 | [root@master0 ~]# kubectl describe pod test-pod-resource 75 | Name: test-pod-resource 76 | Namespace: default 77 | Node: nodec0.example.com/172.25.0.13 78 | Start Time: Mon, 12 Dec 2016 14:46:53 +0800 79 | Labels: 80 | Status: Running 81 | IP: 10.38.0.12 82 | Controllers: 83 | Containers: 84 | nginx: 85 | Container ID: docker://afd8950ec7322705b1e7114dc13d982ed220ecbe226ce92192e54967a0008144 86 | Image: nginx:latest 87 | Image ID: docker://sha256:abf312888d132e461c61484457ee9fd0125d666672e22f972f3b8c9a0ed3f0a1 88 | Port: 89 | Limits: 90 | cpu: 500m 91 | memory: 128Mi 92 | Requests: 93 | cpu: 250m 94 | memory: 64Mi 95 | State: Running 96 | Started: Mon, 12 Dec 2016 14:46:57 +0800 97 | Ready: True 98 | Restart Count: 0 99 | Volume Mounts: 100 | /var/run/secrets/kubernetes.io/serviceaccount from default-token-k5azo (ro) 101 | Environment Variables: 102 | Conditions: 103 | Type Status 104 | Initialized True 105 | Ready True 106 | PodScheduled True 107 | Volumes: 108 | default-token-k5azo: 109 | Type: Secret (a volume populated by a Secret) 110 | SecretName: default-token-k5azo 111 | QoS Class: Burstable 112 | Tolerations: 113 | Events: 114 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 115 | --------- -------- ----- ---- ------------- -------- ------ ------- 116 | 1m 1m 1 {default-scheduler } Normal Scheduled Successfully assigned test-pod-resource to nodec0.example.com 117 | 1m 1m 1 {kubelet nodec0.example.com} spec.containers{nginx} Normal Pulled Container image "nginx:latest" already present on machine 118 | 1m 1m 1 {kubelet nodec0.example.com} spec.containers{nginx} Normal Created Created container with docker id afd8950ec732; Security:[seccomp=unconfined] 119 | 1m 1m 1 {kubelet nodec0.example.com} spec.containers{nginx} Normal Started Started container with docker id afd8950ec732 120 | ``` 121 | 122 | 可以看到如下有用的信息: 123 | 124 | ```config 125 | Limits: 126 | cpu: 500m 127 | memory: 128Mi 128 | Requests: 129 | cpu: 250m 130 | memory: 64Mi 131 | ``` 132 | 133 | 代表此Pod中nginx容器最低需要64MiB的内存和0.25 CPU Core,最大使用资源为128 MiB的内存和0.5 CPU Core。 134 | 135 | #### 资源分配过量例子 136 | 137 | 如果Kubernetes 集群调度器在集群中找不到合适的Node节点来运行Pod,那么Pod将会处在未调度状态,直到Kubernetes集群调度器找到合适的Node节点运行Pod为止。每次Kubernetes 调度器失败都会产生一个event 事件。例如我们要创建的Pod所需资源远远超过了Node能够分配的最大资源。 138 | 139 | 我们来演示Pod资源超过Node节点资源的情况,首先确定Node节点的资源状态: 140 | 141 | ```shell 142 | [root@master0 ~]# kubectl describe node |grep cpu 143 | cpu: 2 144 | cpu: 2 145 | cpu: 2 146 | cpu: 2 147 | cpu: 2 148 | cpu: 2 149 | cpu: 2 150 | cpu: 2 151 | [root@master0 ~]# kubectl describe node |grep memory: 152 | memory: 1884120Ki 153 | memory: 1884120Ki 154 | memory: 1016792Ki 155 | memory: 1016792Ki 156 | memory: 1016792Ki 157 | memory: 1016792Ki 158 | memory: 1016792Ki 159 | memory: 1016792Ki 160 | ``` 161 | 162 | 所有Node 节点最多支持CPU Core为2 ,最多支持Memory为1016792KiB。我们只需要创建超过其中一只的资源就可以演示错误。 163 | 164 | 我们先停止之前创建的Pod test-pod-resource: 165 | 166 | ```shell 167 | [root@master0 ~]# kubectl delete -f pod-resource.yaml 168 | ``` 169 | 170 | 然后修改配置文件pod-resource.yaml : 171 | 172 | ```yaml 173 | apiVersion: v1 174 | kind: Pod 175 | metadata: 176 | name: test-pod-resource 177 | spec: 178 | containers: 179 | - name: nginx 180 | image: nginx:latest 181 | imagePullPolicy: IfNotPresent 182 | resources: 183 | requests: 184 | cpu: "3" 185 | ``` 186 | 187 | 创建Pod: 188 | 189 | ```shell 190 | [root@master0 ~]# kubectl create -f pod-resource.yaml 191 | pod "test-pod-resource" created 192 | ``` 193 | 194 | 此Pod 会呈现pending状态 195 | 196 | ```shell 197 | [root@master0 ~]# kubectl get pod 198 | NAME READY STATUS RESTARTS AGE 199 | test-pod-resource 0/1 Pending 0 32s 200 | ``` 201 | 202 | 查看详细信息 203 | 204 | ```shell 205 | [root@master0 ~]# kubectl describe pod test-pod-resource 206 | Name: test-pod-resource 207 | Namespace: default 208 | Node: / 209 | Labels: 210 | Status: Pending 211 | IP: 212 | Controllers: 213 | Containers: 214 | nginx: 215 | Image: nginx:latest 216 | Port: 217 | Requests: 218 | cpu: 3 219 | Volume Mounts: 220 | /var/run/secrets/kubernetes.io/serviceaccount from default-token-k5azo (ro) 221 | Environment Variables: 222 | Conditions: 223 | Type Status 224 | PodScheduled False 225 | Volumes: 226 | default-token-k5azo: 227 | Type: Secret (a volume populated by a Secret) 228 | SecretName: default-token-k5azo 229 | QoS Class: Burstable 230 | Tolerations: 231 | Events: 232 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 233 | --------- -------- ----- ---- ------------- -------- ------ ------- 234 | 235 | 13s 13s 1 {default-scheduler } Warning FailedSchedulingpod (test-pod-resource) failed to fit in any node 236 | fit failure on node (nodea0.example.com): Insufficient cpu 237 | fit failure on node (nodec0.example.com): Insufficient cpu 238 | fit failure on node (nodeb0.example.com): Insufficient cpu 239 | fit failure on node (master0.example.com): Insufficient cpu, PodToleratesNodeTaints 240 | ``` 241 | 242 | 删除已创建的Pod 243 | 244 | ```shell 245 | [root@master0 ~]# kubectl delete -f pod-resource.yaml 246 | pod "test-pod-resource" deleted 247 | ``` 248 | 249 | 调整配置文件 250 | 251 | ```yaml 252 | apiVersion: v1 253 | kind: Pod 254 | metadata: 255 | name: test-pod-resource 256 | spec: 257 | containers: 258 | - name: nginx 259 | image: nginx:latest 260 | imagePullPolicy: IfNotPresent 261 | resources: 262 | requests: 263 | cpu: "1" 264 | ``` 265 | 266 | 重新创建Pod,在可用资源框架下运行 267 | 268 | ```shell 269 | [root@master0 ~]# kubectl create -f pod-resource.yaml 270 | pod "test-pod-resource" created 271 | 272 | [root@master0 ~]# kubectl get pod 273 | NAME READY STATUS RESTARTS AGE 274 | test-pod-resource 1/1 Running 0 7s 275 | ``` 276 | 277 | 如果我们在工作环境中碰到相同的问题,我们可以采取如下解决方案: 278 | 279 | * 添加更多的Node节点资源,横向的扩展Kubernetes 集群资源。 280 | * 停止当前正在运行的不必要Pod,释放可用的Kubernetes 集群资源。 281 | * 检查配置错误,重新启动Pod 282 | 283 | 如果在Pod运行过程中,Pod内的容器需求资源超过Limits分配的资源时,Pod内容器将被Kubernetes 调度器重启,以使资源被释放后重新分配。 284 | 285 | #### 查看资源分配状态 286 | 287 | 通过kubectl describe 命令我们可以查看Node节点上资源分配的状态。 288 | 289 | 首先确认Pod运行在哪个Node节点上 290 | 291 | ```shell 292 | [root@master0 ~]# kubectl describe pod test-pod-resource | grep Node: 293 | Node: nodea0.example.com/172.25.0.11 294 | ``` 295 | 296 | 然后查看Node的具体信息 297 | 298 | ```shell 299 | [root@master0 ~]# kubectl describe node nodea0 300 | Name: nodea0.example.com 301 | Labels: beta.kubernetes.io/arch=amd64 302 | beta.kubernetes.io/os=linux 303 | kubernetes.io/hostname=nodea0.example.com 304 | Taints: 305 | CreationTimestamp: Tue, 22 Nov 2016 12:49:21 +0800 306 | Phase: 307 | Conditions: 308 | Type Status LastHeartbeatTime LastTransitionTime Reason Message 309 | ---- ------ ----------------- ------------------ ------ ------- 310 | OutOfDisk False Mon, 12 Dec 2016 16:36:39 +0800 Fri, 25 Nov 2016 14:59:35 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available 311 | MemoryPressure False Mon, 12 Dec 2016 16:36:39 +0800 Tue, 22 Nov 2016 12:49:21 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available 312 | DiskPressure False Mon, 12 Dec 2016 16:36:39 +0800 Tue, 22 Nov 2016 12:49:21 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure 313 | Ready True Mon, 12 Dec 2016 16:36:39 +0800 Tue, 22 Nov 2016 12:49:21 +0800 KubeletReady kubelet is posting ready status 314 | Addresses: 172.25.0.11,172.25.0.11 315 | Capacity: 316 | alpha.kubernetes.io/nvidia-gpu: 0 317 | cpu: 2 318 | memory: 1016792Ki 319 | pods: 110 320 | Allocatable: 321 | alpha.kubernetes.io/nvidia-gpu: 0 322 | cpu: 2 323 | memory: 1016792Ki 324 | pods: 110 325 | System Info: 326 | Machine ID: ecd4a28875734de9bf2cb5e40cbf88da 327 | System UUID: 9E450584-DC5A-4B7C-809E-25D67846B219 328 | Boot ID: e3d4638d-f003-41e7-a10e-14502d9b39b4 329 | Kernel Version: 3.10.0-327.el7.x86_64 330 | OS Image: Red Hat Enterprise Linux Server 7.2 (Maipo) 331 | Operating System: linux 332 | Architecture: amd64 333 | Container Runtime Version: docker://1.12.2 334 | Kubelet Version: v1.4.0 335 | Kube-Proxy Version: v1.4.0 336 | ExternalID: nodea0.example.com 337 | Non-terminated Pods: (5 in total) 338 | Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits 339 | --------- ---- ------------ ---------- --------------- ------------- 340 | default test-pod-resource 1 (50%) 0 (0%) 0 (0%) 0 (0%) 341 | kube-system kube-proxy-amd64-ick75 0 (0%) 0 (0%) 0 (0%) 0 (0%) 342 | kube-system kubernetes-dashboard-1171352413-yuqpa 0 (0%) 0 (0%) 0 (0%) 0 (0%) 343 | kube-system monitoring-influxdb-3276295126-9690a 0 (0%) 0 (0%) 0 (0%) 0 (0%) 344 | kube-system weave-net-fphro 20m (1%) 0 (0%) 0 (0%) 0 (0%) 345 | Allocated resources: 346 | (Total limits may be over 100 percent, i.e., overcommitted. 347 | CPU Requests CPU Limits Memory Requests Memory Limits 348 | ------------ ---------- --------------- ------------- 349 | 1020m (51%) 0 (0%) 0 (0%) 0 (0%) 350 | No events. 351 | ``` 352 | 353 | 可以看到以下有用信息 354 | 355 | ```config 356 | default test-pod-resource 1 (50%) 0 (0%) 0 (0%) 0 (0%) 357 | 358 | Allocated resources: 359 | (Total limits may be over 100 percent, i.e., overcommitted. 360 | CPU Requests CPU Limits Memory Requests Memory Limits 361 | ------------ ---------- --------------- ------------- 362 | 1020m (51%) 0 (0%) 0 (0%) 0 (0%) 363 | ``` 364 | 365 | 可以获得Pod占用的资源和Node当前资源的使用情况。 366 | 367 | -------------------------------------------------------------------------------- /Kubernetes 1.4 高级课程-集群管理.md: -------------------------------------------------------------------------------- 1 | # Kubernetes 1.4 高级课程 2 | 3 | ###### kissingwolf@gmail.com 4 | 5 | [TOC] 6 | 7 | ## Kubernetes 集群管理 8 | 9 | ### Node 节点管理 10 | 11 | 在前面基础课程中,我们介绍了Kubernetes 集群是由两种基础设备组成的:Master和Node。Master负责管理和维护Kubernetes集群信息,并向Node下放任务和接收反馈信息。Node负责集群负载,在Node上真实的运行Kubernetes Pod 以及容器实例。 12 | 13 | 随着Kubernetes集群的构建,Node节点数量会不断增加,Node节点由于其自身的故障或网络原因也会出现离线或剔除。我们就需要对其做相应的操作。 14 | 15 | #### Node节点的隔离和恢复 16 | 17 | 在硬件或网络维护的时候,我们需要将某些Node 节点进行隔离,使其脱离Kubernetes 集群的调度。Node 节点脱离Kubernetes集群调度的目的是为了避免Master 再为其分配Pod 运行,原有运行在其上的Pod 并不会自动迁移到其它没有脱离Kubernetes 集群的Node节点上。如果希望Node 节点离线,并快速将脱离Kubernetes 调度的Node 节点上Pod 转移,需要手工完成操作。 18 | 19 | 隔离和恢复Node 节点有两种方法,使用配置文件或手工执行命令。 20 | 21 | 首先我们查看当前的Kubernetes 集群节点状态: 22 | 23 | ```shell 24 | [root@master0 ~]# kubectl get node 25 | NAME STATUS AGE 26 | master0.example.com Ready 15d 27 | nodea0.example.com Ready 15d 28 | nodeb0.example.com Ready 15d 29 | ``` 30 | 31 | 你看到结果应该和上面类似,masterN 是Kubernetes 集群的Master ,nodeaN 和 nodebN 是Kubernetes 集群中的Node 节点。整个环境是在基础课程中就安装完成的标准环境,如果你的环境有问题,请在你的物理机上修改并运行init_k8s.sh,初始化Kubernetes试验环境。 32 | 33 | 使用配置文件方法,首先需要创建配置文件unsheduleable_node.yaml,内容如下: 34 | 35 | ```yaml 36 | apiVersion: v1 # 配置版本 37 | kind: Node # 配置类型 38 | metadata: # 元数据 39 | name: nodeb0.example.com # 配置文件元数据名 40 | labels: # 标签名 41 | kubernetes.io/hostname: nodeb0.example.com # 需要隔离的 Node 节点 42 | spec: 43 | unschedulable: true # spec.unschedulable 设置为true 时,Node 节点被隔离, 44 | # 设置为false时,Node 节点恢复。 默认值为false 45 | ``` 46 | 47 | 在默认情况下,nodebN.example.com 节点的状态和信息如下: 48 | 49 | ```shell 50 | [root@master0 ~]# kubectl describe node nodeb0.example.com 51 | Name: nodeb0.example.com 52 | Labels: beta.kubernetes.io/arch=amd64 53 | beta.kubernetes.io/os=linux 54 | kubernetes.io/hostname=nodeb0.example.com 55 | Taints: 56 | CreationTimestamp: Tue, 22 Nov 2016 12:49:20 +0800 57 | Phase: 58 | Conditions: 59 | Type Status LastHeartbeatTime LastTransitionTime Reason Message 60 | ---- ------ ----------------- ------------------ ------ ------- 61 | OutOfDisk False Wed, 07 Dec 2016 14:05:44 +0800 Wed, 23 Nov 2016 16:36:20 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available 62 | MemoryPressure False Wed, 07 Dec 2016 14:05:44 +0800 Tue, 22 Nov 2016 12:49:19 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available 63 | DiskPressure False Wed, 07 Dec 2016 14:05:44 +0800 Tue, 22 Nov 2016 12:49:19 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure 64 | Ready True Wed, 07 Dec 2016 14:05:44 +0800 Wed, 23 Nov 2016 16:36:20 +0800 KubeletReady kubelet is posting ready status 65 | Addresses: 172.25.0.12,172.25.0.12 66 | Capacity: 67 | alpha.kubernetes.io/nvidia-gpu: 0 68 | cpu: 2 69 | memory: 1016792Ki 70 | pods: 110 71 | Allocatable: 72 | alpha.kubernetes.io/nvidia-gpu: 0 73 | cpu: 2 74 | memory: 1016792Ki 75 | pods: 110 76 | System Info: 77 | Machine ID: ecd4a28875734de9bf2cb5e40cbf88da 78 | System UUID: C7207044-587A-4F66-9AF7-7E7262AD9DA9 79 | Boot ID: 3ddb17ed-0d77-4e17-91d9-b36301640f11 80 | Kernel Version: 3.10.0-327.el7.x86_64 81 | OS Image: Red Hat Enterprise Linux Server 7.2 (Maipo) 82 | Operating System: linux 83 | Architecture: amd64 84 | Container Runtime Version: docker://1.12.2 85 | Kubelet Version: v1.4.0 86 | Kube-Proxy Version: v1.4.0 87 | ExternalID: nodeb0.example.com 88 | Non-terminated Pods: (4 in total) 89 | Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits 90 | --------- ---- ------------ ---------- --------------- ------------- 91 | kube-system kube-proxy-amd64-gzmv5 0 (0%) 0 (0%) 0 (0%) 0 (0%) 92 | kube-system monitoring-grafana-927606581-45lpl 0 (0%) 0 (0%) 0 (0%) 0 (0%) 93 | kube-system monitoring-influxdb-3276295126-ec2nf 0 (0%) 0 (0%) 0 (0%) 0 (0%) 94 | kube-system weave-net-cfenz 20m (1%) 0 (0%) 0 (0%) 0 (0%) 95 | Allocated resources: 96 | (Total limits may be over 100 percent, i.e., overcommitted. 97 | CPU Requests CPU Limits Memory Requests Memory Limits 98 | ------------ ---------- --------------- ------------- 99 | 20m (1%) 0 (0%) 0 (0%) 0 (0%) 100 | ``` 101 | 102 | 请保证你的配置信息与实际nodebN.example.com 节点的信息信息中Name和Lables显示相关项目一致。 103 | 104 | 通过kubectl replace 命令修改Node 状态: 105 | 106 | ```shell 107 | [root@master0 ~]# kubectl replace -f unsheduleable_node.yaml 108 | node "nodeb0.example.com" replaced 109 | ``` 110 | 111 | 随后查看Node 状态,可以看到Node 节点 nodebN.example.com 的状态由之前的Ready 转化为Ready,SchedulingDisabled。 112 | 113 | ```shell 114 | [root@master0 ~]# kubectl get node 115 | NAME STATUS AGE 116 | master0.example.com Ready 15d 117 | nodea0.example.com Ready 15d 118 | nodeb0.example.com Ready,SchedulingDisabled 15d 119 | ``` 120 | 121 | 之后再建立Pod,Kubernetes 集群将不再分配给Node 节点 nodebN.example.com 。 122 | 123 | 使用命令行直接操作也是可以的,命令为kubectl patch: 124 | 125 | ```shell 126 | [root@master0 ~]# kubectl get node 127 | NAME STATUS AGE 128 | master0.example.com Ready 15d 129 | nodea0.example.com Ready 15d 130 | nodeb0.example.com Ready,SchedulingDisabled 15d 131 | 132 | [root@master0 ~]# kubectl patch node nodea0.example.com -p '{"spec": {"unschedulable": true}}' 133 | "nodea0.example.com" patched 134 | 135 | [root@master0 ~]# kubectl get node 136 | NAME STATUS AGE 137 | master0.example.com Ready 15d 138 | nodea0.example.com Ready,SchedulingDisabled 15d 139 | nodeb0.example.com Ready,SchedulingDisabled 15d 140 | 141 | [root@master0 ~]# kubectl patch node nodea0.example.com -p '{"spec": {"unschedulable": false}}' 142 | "nodea0.example.com" patched 143 | 144 | [root@master0 ~]# kubectl patch node nodeb0.example.com -p '{"spec": {"unschedulable": false}}' 145 | "nodeb0.example.com" patched 146 | 147 | [root@master0 ~]# kubectl get node 148 | NAME STATUS AGE 149 | master0.example.com Ready 15d 150 | nodea0.example.com Ready 15d 151 | nodeb0.example.com Ready 15d 152 | ``` 153 | 154 | Kubernetes 1.4 中使用更加简洁的方式配置Node 节点的隔离和恢复,kubectl 新的子命令cordon和uncordon 同样可以完成隔离和恢复任务: 155 | 156 | ```shell 157 | [root@master0 ~]# kubectl cordon nodeb0.example.com 158 | node "nodeb0.example.com" cordoned 159 | [root@master0 ~]# kubectl get node 160 | NAME STATUS AGE 161 | master0.example.com Ready 15d 162 | nodea0.example.com Ready 15d 163 | nodeb0.example.com Ready,SchedulingDisabled 15d 164 | [root@master0 ~]# kubectl uncordon nodeb0.example.com 165 | node "nodeb0.example.com" uncordoned 166 | [root@master0 ~]# kubectl get node 167 | NAME STATUS AGE 168 | master0.example.com Ready 15d 169 | nodea0.example.com Ready 15d 170 | nodeb0.example.com Ready 15d 171 | ``` 172 | 173 | #### Node节点的添加和剔除 174 | 175 | 首先,我们必须知道在Kubernetes 集群中,我们碰到最多且最棘手的问题是服务器容量不足,这时我们需要购买新的服务器,添加新的Node 节点,以达到系统水平扩展的目的。 176 | 177 | 在Kubernetes 1.4 集群中,添加新的节点非常简单,只需要在新Node 节点上安装 Kubernetes 1.4 软件,配置其基础运行环境后执行kubeadm jion 命令就可以将其添加到已经存在的Kubernetes 1.4 集群中。 178 | 179 | 接下来我们在现有的Kubernetes 1.4 集群中添加一台新的Node 节点 nodecN.example.com 。 在我们的实验环境中基础设备已经为大家预先配置。 180 | 181 | 启动nodec 虚拟机: 182 | 183 | ```shell 184 | [kiosk@foundation0 Desktop]$ rht-vmctl reset nodec 185 | Are you sure you want to reset nodec? (y/n) y 186 | Powering off nodec. 187 | Resetting nodec. 188 | Creating virtual machine disk overlay for up500-nodec-vda.qcow2 189 | Starting nodec. 190 | ``` 191 | 192 | 连接nodec 虚拟机,请注意你的设备foundation号: 193 | 194 | ```shell 195 | [kiosk@foundation0 Desktop]$ ssh root@nodec0 196 | Last login: Sat Jun 4 14:39:46 2016 from 172.25.0.250 197 | [root@nodec0 ~]# 198 | ``` 199 | 200 | 按照基础课程中讲到的方法初始化Node 节点 nodecX.example.com 的环境 201 | 202 | ```shell 203 | [root@nodec0 ~]# systemctl stop firewalld 204 | 205 | [root@nodec0 ~]# systemctl disable NetworkManager 206 | 207 | [root@nodec0 ~]# systemctl stop NetworkManager.service 208 | 209 | [root@nodec0 ~]# setenforce 0 210 | 211 | [root@nodec0 ~]# sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config 212 | 213 | [root@nodec0 ~]# yum install wget bzip2 net-tools -y 214 | 215 | [root@nodec0 ~]# wget http://classroom.example.com/materials/kubernetes-1.4.repo -O /etc/yum.repos.d/k8s.rep 216 | 217 | [root@nodec0 ~]# yum install docker-engine kubeadm kubectl kubelet kubernetes-cni -y 218 | 219 | [root@nodec0 ~]# systemctl enable docker kubelet 220 | 221 | [root@nodec0 ~]# systemctl enable docker 222 | 223 | [root@nodec0 ~]# wget http://classroom.example.com/materials/k8s-imgs/k8s-1.4-node-img.tbz 224 | 225 | [root@nodec0 ~]# for i in ./k8s-1.4-node-img/*.img ; do docker load -i $i ; done 226 | 227 | [root@nodec0 ~]# wget http://classroom.example.com/materials/k8s-imgs/heapster-img.tbz 228 | 229 | [root@nodec0 ~]# tar -jxf heapster-img.tbz 230 | 231 | [root@nodec0 ~]# for i in ./heapster/*.img ; do docker load -i $i ; done 232 | ``` 233 | 234 | 将Node 节点 nodecX.example.com 添加入现有的Kubernetes 1.4 集群,请将token值替换为你自己的: 235 | 236 | ```shell 237 | [root@nodec0 ~]# kubeadm join --token=0a349c.013fd0942f0c8498 172.25.0.10 238 | validating provided token 239 | created cluster info discovery client, requesting info from "http://172.25.0.10:9898/cluster-info/v1/?token-id=0a349c" 240 | cluster info object received, verifying signature using given token 241 | cluster info signature and contents are valid, will use API endpoints [https://172.25.0.10:443] 242 | created API client to obtain unique certificate for this node, generating keys and certificate signing request 243 | received signed certificate from the API server, generating kubelet configuration 244 | created "/etc/kubernetes/kubelet.conf" 245 | 246 | Node join complete: 247 | * Certificate signing request sent to master and response 248 | received. 249 | * Kubelet informed of new secure connection details. 250 | 251 | Run 'kubectl get nodes' on the master to see this machine join. 252 | ``` 253 | 254 | 在Master 节点上执行kubectl get node 可以看到新的节点已经加入: 255 | 256 | ```shell 257 | [root@master0 ~]# kubectl get nodes 258 | NAME STATUS AGE 259 | master0.example.com Ready 15d 260 | nodea0.example.com Ready 15d 261 | nodeb0.example.com Ready 2h 262 | nodec0.example.com Ready 10s 263 | ``` 264 | 265 | 需要注意的是token值,如果你忘记在之前基础课程创建Kubernetes 1.4 集群时备份token值了,也没有关系,在Master 节点执行如下指令,可以得到当前Kubernetes 1.4 集群的token值。 266 | 267 | ```shell 268 | [root@master0 ~]# kubectl -n kube-system get secret clusterinfo -o yaml | grep token-map | awk '{print $2}' | base64 -d | sed "s|{||g;s|}||g;s|:|.|g;s/\"//g;" | xargs echo 269 | 0a349c.013fd0942f0c8498 270 | ``` 271 | 272 | 剔除Node 节点的操作在生产环境中使用相对较少,我们采取的剔除步骤是,首先将其隔离,然后使用kubectl delete node 命令将其删除。但是要注意的是,我们这样做的目的是使其离线后修复其硬件故障,故障修复后重新启动这个Node 节点,节点会重新自动加回集群。 273 | 274 | 以下操作请注意将设备号设为你自己的设备号: 275 | 276 | ```shell 277 | 查看当前Node 节点状态 278 | [root@master0 ~]# kubectl get node 279 | NAME STATUS AGE 280 | master0.example.com Ready 15d 281 | nodea0.example.com Ready 15d 282 | nodeb0.example.com Ready 20h 283 | nodec0.example.com Ready 18h 284 | 285 | 将Node 节点 nodec0.example.com 隔离 286 | [root@master0 ~]# kubectl cordon nodec0.example.com 287 | node "nodec0.example.com" cordoned 288 | 289 | 查看当前Node 节点状态 290 | [root@master0 ~]# kubectl get node 291 | NAME STATUS AGE 292 | master0.example.com Ready 15d 293 | nodea0.example.com Ready 15d 294 | nodeb0.example.com Ready 20h 295 | nodec0.example.com Ready,SchedulingDisabled 18h 296 | 297 | 删除Node 节点 nodec0.example.com 298 | [root@master0 ~]# kubectl delete node nodec0.example.com 299 | node "nodec0.example.com" deleted 300 | [root@master0 ~]# kubectl get node 301 | NAME STATUS AGE 302 | master0.example.com Ready 15d 303 | nodea0.example.com Ready 15d 304 | nodeb0.example.com Ready 20h 305 | 306 | 重启Node 节点 nodec0.example.com 307 | [root@nodec0 ~]# reboot 308 | Connection to nodec0 closed by remote host. 309 | Connection to nodec0 closed. 310 | 311 | Node 节点 nodec0.example.com 重启后会自动加回集群 312 | [root@master0 ~]# kubectl get node 313 | NAME STATUS AGE 314 | master0.example.com Ready 15d 315 | nodea0.example.com Ready 15d 316 | nodeb0.example.com Ready 20h 317 | nodec0.example.com Ready 41s 318 | ``` 319 | 320 | #### Node 节点信息说明 321 | 322 | 我们有两种命令行获取Node 节点信息的方法: 323 | 324 | * kubectl get node Node_Name -o Output_Format 325 | * kubectl describe node Node_Name 326 | 327 | 首先看get 子命令方法: 328 | 329 | ```shell 330 | YAML 格式输出 331 | [root@master0 ~]# kubectl get node nodea0.example.com -o yaml 332 | apiVersion: v1 333 | kind: Node 334 | metadata: 335 | annotations: 336 | volumes.kubernetes.io/controller-managed-attach-detach: "true" 337 | creationTimestamp: 2016-11-22T04:49:21Z 338 | labels: 339 | beta.kubernetes.io/arch: amd64 340 | beta.kubernetes.io/os: linux 341 | kubernetes.io/hostname: nodea0.example.com 342 | name: nodea0.example.com 343 | resourceVersion: "218660" 344 | selfLink: /api/v1/nodes/nodea0.example.com 345 | uid: 08a3d801-b06f-11e6-8ef8-52540000000a 346 | spec: 347 | externalID: nodea0.example.com 348 | status: 349 | addresses: 350 | - address: 172.25.0.11 351 | type: LegacyHostIP 352 | - address: 172.25.0.11 353 | type: InternalIP 354 | allocatable: 355 | alpha.kubernetes.io/nvidia-gpu: "0" 356 | cpu: "2" 357 | memory: 1016792Ki 358 | pods: "110" 359 | capacity: 360 | alpha.kubernetes.io/nvidia-gpu: "0" 361 | cpu: "2" 362 | memory: 1016792Ki 363 | pods: "110" 364 | conditions: 365 | - lastHeartbeatTime: 2016-12-08T03:28:08Z 366 | lastTransitionTime: 2016-11-25T06:59:35Z 367 | message: kubelet has sufficient disk space available 368 | reason: KubeletHasSufficientDisk 369 | status: "False" 370 | type: OutOfDisk 371 | - lastHeartbeatTime: 2016-12-08T03:28:08Z 372 | lastTransitionTime: 2016-11-22T04:49:21Z 373 | message: kubelet has sufficient memory available 374 | reason: KubeletHasSufficientMemory 375 | status: "False" 376 | type: MemoryPressure 377 | - lastHeartbeatTime: 2016-12-08T03:28:08Z 378 | lastTransitionTime: 2016-11-22T04:49:21Z 379 | message: kubelet has no disk pressure 380 | reason: KubeletHasNoDiskPressure 381 | status: "False" 382 | type: DiskPressure 383 | - lastHeartbeatTime: 2016-12-08T03:28:08Z 384 | lastTransitionTime: 2016-11-22T04:49:21Z 385 | message: kubelet is posting ready status 386 | reason: KubeletReady 387 | status: "True" 388 | type: Ready 389 | daemonEndpoints: 390 | kubeletEndpoint: 391 | Port: 10250 392 | images: 393 | - names: 394 | - kubernetes/heapster:canary 395 | sizeBytes: 1028534191 396 | - names: 397 | - kissingwolf/hpa-example:latest 398 | sizeBytes: 480748530 399 | - names: 400 | - kubernetes/heapster_influxdb:v0.6 401 | - kubernetes/heapster_influxdb@sha256:70b34b65def36fd0f54af570d5e72ac41c3c82e086dace9e6a977bab7750c147 402 | sizeBytes: 271083718 403 | - names: 404 | - gcr.io/google_containers/kube-proxy-amd64:v1.4.0 405 | sizeBytes: 202773136 406 | - names: 407 | - weaveworks/weave-kube:1.7.2 408 | sizeBytes: 196546754 409 | - names: 410 | - nginx:1.11.5 411 | - nginx:latest 412 | sizeBytes: 181440028 413 | - names: 414 | - nginx:1.10.2 415 | sizeBytes: 180664743 416 | - names: 417 | - gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0 418 | sizeBytes: 86267953 419 | - names: 420 | - weaveworks/weave-npc:1.7.2 421 | sizeBytes: 76333700 422 | - names: 423 | - gcr.io/google_containers/pause-amd64:3.0 424 | sizeBytes: 746888 425 | nodeInfo: 426 | architecture: amd64 427 | bootID: 97cae03d-c66b-4719-b166-b2aad1ed02d5 428 | containerRuntimeVersion: docker://1.12.2 429 | kernelVersion: 3.10.0-327.el7.x86_64 430 | kubeProxyVersion: v1.4.0 431 | kubeletVersion: v1.4.0 432 | machineID: ecd4a28875734de9bf2cb5e40cbf88da 433 | operatingSystem: linux 434 | osImage: Red Hat Enterprise Linux Server 7.2 (Maipo) 435 | systemUUID: 9E450584-DC5A-4B7C-809E-25D67846B219 436 | 437 | JSON 格式输出 438 | [root@master0 ~]# kubectl get node nodea0.example.com -o json 439 | { 440 | "kind": "Node", 441 | "apiVersion": "v1", 442 | "metadata": { 443 | "name": "nodea0.example.com", 444 | "selfLink": "/api/v1/nodes/nodea0.example.com", 445 | "uid": "08a3d801-b06f-11e6-8ef8-52540000000a", 446 | "resourceVersion": "218660", 447 | "creationTimestamp": "2016-11-22T04:49:21Z", 448 | "labels": { 449 | "beta.kubernetes.io/arch": "amd64", 450 | "beta.kubernetes.io/os": "linux", 451 | "kubernetes.io/hostname": "nodea0.example.com" 452 | }, 453 | "annotations": { 454 | "volumes.kubernetes.io/controller-managed-attach-detach": "true" 455 | } 456 | }, 457 | "spec": { 458 | "externalID": "nodea0.example.com" 459 | }, 460 | "status": { 461 | "capacity": { 462 | "alpha.kubernetes.io/nvidia-gpu": "0", 463 | "cpu": "2", 464 | "memory": "1016792Ki", 465 | "pods": "110" 466 | }, 467 | "allocatable": { 468 | "alpha.kubernetes.io/nvidia-gpu": "0", 469 | "cpu": "2", 470 | "memory": "1016792Ki", 471 | "pods": "110" 472 | }, 473 | "conditions": [ 474 | { 475 | "type": "OutOfDisk", 476 | "status": "False", 477 | "lastHeartbeatTime": "2016-12-08T03:28:08Z", 478 | "lastTransitionTime": "2016-11-25T06:59:35Z", 479 | "reason": "KubeletHasSufficientDisk", 480 | "message": "kubelet has sufficient disk space available" 481 | }, 482 | { 483 | "type": "MemoryPressure", 484 | "status": "False", 485 | "lastHeartbeatTime": "2016-12-08T03:28:08Z", 486 | "lastTransitionTime": "2016-11-22T04:49:21Z", 487 | "reason": "KubeletHasSufficientMemory", 488 | "message": "kubelet has sufficient memory available" 489 | }, 490 | { 491 | "type": "DiskPressure", 492 | "status": "False", 493 | "lastHeartbeatTime": "2016-12-08T03:28:08Z", 494 | "lastTransitionTime": "2016-11-22T04:49:21Z", 495 | "reason": "KubeletHasNoDiskPressure", 496 | "message": "kubelet has no disk pressure" 497 | }, 498 | { 499 | "type": "Ready", 500 | "status": "True", 501 | "lastHeartbeatTime": "2016-12-08T03:28:08Z", 502 | "lastTransitionTime": "2016-11-22T04:49:21Z", 503 | "reason": "KubeletReady", 504 | "message": "kubelet is posting ready status" 505 | } 506 | ], 507 | "addresses": [ 508 | { 509 | "type": "LegacyHostIP", 510 | "address": "172.25.0.11" 511 | }, 512 | { 513 | "type": "InternalIP", 514 | "address": "172.25.0.11" 515 | } 516 | ], 517 | "daemonEndpoints": { 518 | "kubeletEndpoint": { 519 | "Port": 10250 520 | } 521 | }, 522 | "nodeInfo": { 523 | "machineID": "ecd4a28875734de9bf2cb5e40cbf88da", 524 | "systemUUID": "9E450584-DC5A-4B7C-809E-25D67846B219", 525 | "bootID": "97cae03d-c66b-4719-b166-b2aad1ed02d5", 526 | "kernelVersion": "3.10.0-327.el7.x86_64", 527 | "osImage": "Red Hat Enterprise Linux Server 7.2 (Maipo)", 528 | "containerRuntimeVersion": "docker://1.12.2", 529 | "kubeletVersion": "v1.4.0", 530 | "kubeProxyVersion": "v1.4.0", 531 | "operatingSystem": "linux", 532 | "architecture": "amd64" 533 | }, 534 | "images": [ 535 | { 536 | "names": [ 537 | "kubernetes/heapster:canary" 538 | ], 539 | "sizeBytes": 1028534191 540 | }, 541 | { 542 | "names": [ 543 | "kissingwolf/hpa-example:latest" 544 | ], 545 | "sizeBytes": 480748530 546 | }, 547 | { 548 | "names": [ 549 | "kubernetes/heapster_influxdb:v0.6", 550 | "kubernetes/heapster_influxdb@sha256:70b34b65def36fd0f54af570d5e72ac41c3c82e086dace9e6a977bab7750c147" 551 | ], 552 | "sizeBytes": 271083718 553 | }, 554 | { 555 | "names": [ 556 | "gcr.io/google_containers/kube-proxy-amd64:v1.4.0" 557 | ], 558 | "sizeBytes": 202773136 559 | }, 560 | { 561 | "names": [ 562 | "weaveworks/weave-kube:1.7.2" 563 | ], 564 | "sizeBytes": 196546754 565 | }, 566 | { 567 | "names": [ 568 | "nginx:1.11.5", 569 | "nginx:latest" 570 | ], 571 | "sizeBytes": 181440028 572 | }, 573 | { 574 | "names": [ 575 | "nginx:1.10.2" 576 | ], 577 | "sizeBytes": 180664743 578 | }, 579 | { 580 | "names": [ 581 | "gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0" 582 | ], 583 | "sizeBytes": 86267953 584 | }, 585 | { 586 | "names": [ 587 | "weaveworks/weave-npc:1.7.2" 588 | ], 589 | "sizeBytes": 76333700 590 | }, 591 | { 592 | "names": [ 593 | "gcr.io/google_containers/pause-amd64:3.0" 594 | ], 595 | "sizeBytes": 746888 596 | } 597 | ] 598 | } 599 | } 600 | ``` 601 | 602 | 接下来看describe 子命令方法: 603 | 604 | ```shell 605 | [root@master0 ~]# kubectl describe node nodea0.example.com 606 | Name: nodea0.example.com 607 | Labels: beta.kubernetes.io/arch=amd64 608 | beta.kubernetes.io/os=linux 609 | kubernetes.io/hostname=nodea0.example.com 610 | Taints: 611 | CreationTimestamp: Tue, 22 Nov 2016 12:49:21 +0800 612 | Phase: 613 | Conditions: 614 | Type Status LastHeartbeatTime LastTransitionTime Reason Message 615 | ---- ------ ----------------- ------------------ ------ ------- 616 | OutOfDisk False Thu, 08 Dec 2016 11:34:48 +0800 Fri, 25 Nov 2016 14:59:35 +0800 KubeletHasSufficientDisk kubelet has sufficient disk space available 617 | MemoryPressure False Thu, 08 Dec 2016 11:34:48 +0800 Tue, 22 Nov 2016 12:49:21 +0800 KubeletHasSufficientMemory kubelet has sufficient memory available 618 | DiskPressure False Thu, 08 Dec 2016 11:34:48 +0800 Tue, 22 Nov 2016 12:49:21 +0800 KubeletHasNoDiskPressure kubelet has no disk pressure 619 | Ready True Thu, 08 Dec 2016 11:34:48 +0800 Tue, 22 Nov 2016 12:49:21 +0800 KubeletReady kubelet is posting ready status 620 | Addresses: 172.25.0.11,172.25.0.11 621 | Capacity: 622 | alpha.kubernetes.io/nvidia-gpu: 0 623 | cpu: 2 624 | memory: 1016792Ki 625 | pods: 110 626 | Allocatable: 627 | alpha.kubernetes.io/nvidia-gpu: 0 628 | cpu: 2 629 | memory: 1016792Ki 630 | pods: 110 631 | System Info: 632 | Machine ID: ecd4a28875734de9bf2cb5e40cbf88da 633 | System UUID: 9E450584-DC5A-4B7C-809E-25D67846B219 634 | Boot ID: 97cae03d-c66b-4719-b166-b2aad1ed02d5 635 | Kernel Version: 3.10.0-327.el7.x86_64 636 | OS Image: Red Hat Enterprise Linux Server 7.2 (Maipo) 637 | Operating System: linux 638 | Architecture: amd64 639 | Container Runtime Version: docker://1.12.2 640 | Kubelet Version: v1.4.0 641 | Kube-Proxy Version: v1.4.0 642 | ExternalID: nodea0.example.com 643 | Non-terminated Pods: (6 in total) 644 | Namespace Name CPU Requests CPU Limits Memory Requests Memory Limits 645 | --------- ---- ------------ ---------- --------------- ------------- 646 | kube-system heapster-3901806196-8c2rj 0 (0%) 0 (0%) 0 (0%) 0 (0%) 647 | kube-system kube-proxy-amd64-ick75 0 (0%) 0 (0%) 0 (0%) 0 (0%) 648 | kube-system kubernetes-dashboard-1171352413-yuqpa 0 (0%) 0 (0%) 0 (0%) 0 (0%) 649 | kube-system monitoring-grafana-927606581-ruy2u 0 (0%) 0 (0%) 0 (0%) 0 (0%) 650 | kube-system monitoring-influxdb-3276295126-dmbtu 0 (0%) 0 (0%) 0 (0%) 0 (0%) 651 | kube-system weave-net-fphro 20m (1%) 0 (0%) 0 (0%) 0 (0%) 652 | Allocated resources: 653 | (Total limits may be over 100 percent, i.e., overcommitted. 654 | CPU Requests CPU Limits Memory Requests Memory Limits 655 | ------------ ---------- --------------- ------------- 656 | 20m (1%) 0 (0%) 0 (0%) 0 (0%) 657 | Events: 658 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 659 | --------- -------- ----- ---- ------------- -------- ------ ------- 660 | 49m 49m 1 {kubelet nodea0.example.com} Normal Starting Starting kubelet. 661 | 49m 48m 13 {kubelet nodea0.example.com} Normal NodeHasSufficientDisk Node nodea0.example.com status is now: NodeHasSufficientDisk 662 | 49m 48m 13 {kubelet nodea0.example.com} Normal NodeHasSufficientMemory Node nodea0.example.com status is now: NodeHasSufficientMemory 663 | 49m 48m 13 {kubelet nodea0.example.com} Normal NodeHasNoDiskPressure Node nodea0.example.com status is now: NodeHasNoDiskPressure 664 | 48m 48m 1 {kubelet nodea0.example.com} Warning Rebooted Node nodea0.example.com has been rebooted, boot id: 97cae03d-c66b-4719-b166-b2aad1ed02d5 665 | 47m 47m 1 {kube-proxy nodea0.example.com} Normal Starting Starting kube-proxy. 666 | ``` 667 | 668 | Node 节点信息输出很清晰,很容易理解其意义。上课时我们会逐步介绍每部分的含义和作用。 669 | 670 | ### Namespace 名字空间管理 671 | 672 | 在Kubernetes 集群中,环境共享和隔离是通过Linux Namespace 名字空间管理来完成的。 673 | 674 | 生产环境下,公司会有很多需要线上运行资源的部门,大公司会有电商事业部、网游事业部、手游事业部和门户事业部等等,小公司会有开发部、测试部和运维部等等,创业公司也会有开发测试组和安全运维支持组等等。不同的部门和工作组使用同一套Kubernetes 集群环境是最经济和环保的选择,但是如何做到不同部门和工作组之间互不干扰和睦相处呢?这时就需要在Kubernetes 集群中划分不同的运行隔离环境,将不同的部门和工作组限制在不同的隔离环境中,使其创建、修改和删除资源对象的操作相对独立,这样也有利于公司内部实现敏捷开发和敏捷部署,并减少由于部门间的误操作导致故障。 675 | 676 | #### 默认Namespace 677 | 678 | Kubernetes 1.4 中默认已经创建了两个Namespace: 679 | 680 | * default 681 | * kube-system 682 | 683 | 我们可以通过kubectl get namespace 命令查看到: 684 | 685 | ```shell 686 | [root@master0 ~]# kubectl get namespace 687 | NAME STATUS AGE 688 | default Active 16d 689 | kube-system Active 16d 690 | 691 | [root@master0 ~]# kubectl get namespace -o yaml 692 | apiVersion: v1 693 | items: 694 | - apiVersion: v1 695 | kind: Namespace 696 | metadata: 697 | creationTimestamp: 2016-11-22T04:48:50Z 698 | name: default 699 | resourceVersion: "6" 700 | selfLink: /api/v1/namespaces/default 701 | uid: f6501df8-b06e-11e6-8ef8-52540000000a 702 | spec: 703 | finalizers: 704 | - kubernetes 705 | status: 706 | phase: Active 707 | - apiVersion: v1 708 | kind: Namespace 709 | metadata: 710 | creationTimestamp: 2016-11-22T04:48:50Z 711 | name: kube-system 712 | resourceVersion: "9" 713 | selfLink: /api/v1/namespaces/kube-system 714 | uid: f66c62f8-b06e-11e6-8ef8-52540000000a 715 | spec: 716 | finalizers: 717 | - kubernetes 718 | status: 719 | phase: Active 720 | kind: List 721 | metadata: {} 722 | ``` 723 | 724 | Namespace 名字空间的具体信息可以使用kubectl describe namespace 命令查看: 725 | 726 | ```shell 727 | [root@master0 ~]# kubectl describe namespace 728 | Name: default 729 | Labels: 730 | Status: Active 731 | 732 | No resource quota. 733 | 734 | No resource limits. 735 | 736 | 737 | Name: kube-system 738 | Labels: 739 | Status: Active 740 | 741 | No resource quota. 742 | 743 | No resource limits. 744 | ``` 745 | 746 | 当前系统没有预设置的情况下,我们都是工作在default 名字空间的,在早期的Kubernetes 集群中如果设置过其他名字空间并设置其为默认名字空间,可以使用kubectl namespace 子命令查看: 747 | 748 | ```shell 749 | [root@master0 ~]# kubectl namespace 750 | error: namespace has been superseded by the context.namespace field of .kubeconfig files. See 'kubectl config set-context --help' for more details. 751 | ``` 752 | 753 | 以上回显就是没有设置默认名字空间,同时也提示了设置用户默认名字空间的方法。 754 | 755 | 到Kubernetes 1.4的时候,kubectl namespace 命令已经被弃用了,你可以看到如下信息: 756 | 757 | ```shell 758 | [root@master0 ~]# kubectl namespace -h 759 | Deprecated: This command is deprecated, all its functionalities are covered by "kubectl config set-context" 760 | Usage: 761 | kubectl namespace [namespace] [options] 762 | 763 | Use "kubectl options" for a list of global command-line options (applies to all commands). 764 | ``` 765 | 766 | Kubernetes 1.4 以后,建议使用kubectl config set-context 来完成名字空间的所有操作。 767 | 768 | kube-system 名字空间是Kubernetes 1.4 集群系统环境的名字空间,尽量不要去自定义配置这个名字空间内的运行环境和资源。 769 | 770 | ```shell 771 | [root@master0 ~]# kubectl get pod --namespace=kube-system 772 | NAME READY STATUS RESTARTS AGE 773 | etcd-master0.example.com 1/1 Running 8 16d 774 | heapster-3901806196-8c2rj 1/1 Running 6 14d 775 | kube-apiserver-master0.example.com 1/1 Running 14 16d 776 | kube-controller-manager-master0.example.com 1/1 Running 8 16d 777 | kube-discovery-982812725-nghjq 1/1 Running 8 16d 778 | kube-dns-2247936740-f32d9 3/3 Running 24 16d 779 | kube-proxy-amd64-ick75 1/1 Running 3 12d 780 | kube-proxy-amd64-px2ms 1/1 Running 6 14d 781 | kube-proxy-amd64-t93vd 1/1 Running 1 23h 782 | kube-proxy-amd64-xg7bg 1/1 Running 0 2h 783 | kube-scheduler-master0.example.com 1/1 Running 10 16d 784 | kubernetes-dashboard-1171352413-yuqpa 1/1 Running 6 14d 785 | monitoring-grafana-927606581-ruy2u 1/1 Running 0 23h 786 | monitoring-influxdb-3276295126-dmbtu 1/1 Running 1 23h 787 | weave-net-b1z6l 2/2 Running 1 2h 788 | weave-net-fphro 2/2 Running 6 12d 789 | weave-net-fqman 2/2 Running 4 23h 790 | weave-net-kpvob 2/2 Running 12 14d 791 | 792 | [root@master0 ~]# kubectl get deployment --namespace=kube-system 793 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 794 | heapster 1 1 1 1 14d 795 | kube-discovery 1 1 1 1 16d 796 | kube-dns 1 1 1 1 16d 797 | kubernetes-dashboard 1 1 1 1 16d 798 | monitoring-grafana 1 1 1 1 14d 799 | monitoring-influxdb 1 1 1 1 14d 800 | 801 | [root@master0 ~]# kubectl get service --namespace=kube-system 802 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 803 | heapster 100.77.26.79 80/TCP 14d 804 | kube-dns 100.64.0.10 53/UDP,53/TCP 16d 805 | kubernetes-dashboard 100.77.127.110 80/TCP 16d 806 | monitoring-grafana 100.70.101.80 80/TCP 14d 807 | monitoring-influxdb 100.64.163.255 8086/TCP 14d 808 | ``` 809 | 810 | 我们通过--namespace 参数可以在kubectl 命令中指定操作的名字空间,默认不指定的时候是default名字空间。 811 | 812 | #### 创建自定义Namespace 813 | 814 | 创建自定义的名字空间有两种命令行方法: 815 | 816 | * 通过配置文件创建 817 | * 命令行直接创建 818 | 819 | 我们在接下来的实验中将建立两个新的名字空间,分别分配给开发组development和运维组systemadm。 820 | 821 | ##### 配置文件创建Namespace 822 | 823 | 我们使用配置文件创建开发组development的名字空间development ,配置文件名为namespace-dev.yaml,内容如下: 824 | 825 | ```yaml 826 | apiVersion: v1 827 | kind: Namespace 828 | metadata: 829 | name: development 830 | ``` 831 | 832 | 然后使用 kubectl create 命令创建名字空间: 833 | 834 | ```shell 835 | [root@master0 ~]# kubectl create -f namespace-dev.yaml 836 | namespace "development" created 837 | 838 | [root@master0 ~]# kubectl get namespace 839 | NAME STATUS AGE 840 | default Active 16d 841 | development Active 5s 842 | kube-system Active 16d 843 | ``` 844 | 845 | 可以看到新的名字空间development 已经创建。 846 | 847 | ##### 命令行创建Namespace 848 | 849 | 命令行创建使用命令kubectl create namespace Namespace_name 就可以了,接下来我们创建名字空间systemadm : 850 | 851 | ```shell 852 | [root@master0 ~]# kubectl create namespace systemadm 853 | namespace "systemadm" created 854 | 855 | [root@master0 ~]# kubectl get namespace 856 | NAME STATUS AGE 857 | default Active 16d 858 | development Active 2m 859 | kube-system Active 16d 860 | systemadm Active 56s 861 | ``` 862 | 863 | 可以看到很方便的就创建好了。 864 | 865 | #### 创建Context运行环境 866 | 867 | 仅仅有名字空间并不能方便我们对用户和组的管理,我们还需要设置Context 运行环境,并且设置Context和名字空间的关联,这样组织结构就明细了。 868 | 869 | 我们创建两个运行环境:uplooking-dev和uplooking-sysadm,并将其关联到之前创建的两个名字空间,使用的命令是kubctl config set-context : 870 | 871 | ```shell 872 | [root@master0 ~]# kubectl config set-context uplooking-dev --namespace=development 873 | context "uplooking-dev" set. 874 | 875 | [root@master0 ~]# kubectl config view 876 | apiVersion: v1 877 | clusters: [] 878 | contexts: 879 | - context: 880 | cluster: "" 881 | namespace: development 882 | user: "" 883 | name: uplooking-dev 884 | current-context: "" 885 | kind: Config 886 | preferences: {} 887 | users: [] 888 | 889 | [root@master0 ~]# kubectl config set-context uplooking-sysadm --namespace=systemadm 890 | context "uplooking-sysadm" set. 891 | 892 | [root@master0 ~]# kubectl config view 893 | apiVersion: v1 894 | clusters: [] 895 | contexts: 896 | - context: 897 | cluster: "" 898 | namespace: development 899 | user: "" 900 | name: uplooking-dev 901 | - context: 902 | cluster: "" 903 | namespace: systemadm 904 | user: "" 905 | name: uplooking-sysadm 906 | current-context: "" 907 | kind: Config 908 | preferences: {} 909 | users: [] 910 | ``` 911 | 912 | kubectl config 命令其实就是创建和配置 ~/.kube/config 文件,kubectl config view 显示的也是此文件。因此,你也可以手工修改此文件。 913 | 914 | #### 切换Context 运行环境 915 | 916 | 我们可以通过kubectl config get-contexts 命令查看当前用户所处的运行环境,可以通过kubectl config use-context 命令切换用户所处的运行环境。每个运行环境的名字空间各不相同,各自之间不像话影响。 917 | 918 | ```shell 919 | [root@master0 ~]# kubectl config use-context uplooking-dev 920 | switched to context "uplooking-dev". 921 | 922 | [root@master0 ~]# kubectl config get-contexts 923 | CURRENT NAME CLUSTER AUTHINFO NAMESPACE 924 | uplooking-sysadm systemadm 925 | * uplooking-dev development 926 | 927 | [root@master0 ~]# kubectl config use-context uplooking-sysadm 928 | switched to context "uplooking-sysadm". 929 | 930 | [root@master0 ~]# kubectl config get-contexts 931 | CURRENT NAME CLUSTER AUTHINFO NAMESPACE 932 | uplooking-dev development 933 | * uplooking-sysadm systemadm 934 | ``` 935 | 936 | 由于kubectl config 是通过操作配置文件 ~/.kube/config 来切换环境的,所以你重新连接或重新启动都不会影响你上次设置的运行环境。 937 | 938 | #### 独立Context运行环境测试 939 | 940 | 首先查看当前Context 运行环境: 941 | 942 | ```shell 943 | [root@master0 ~]# kubectl config get-contexts 944 | CURRENT NAME CLUSTER AUTHINFO NAMESPACE 945 | * uplooking-sysadm systemadm 946 | uplooking-dev development 947 | ``` 948 | 949 | 我们当前使用的uplooking-sysadm 运行环境,在systemadm 名字空间中。 950 | 951 | 此时创建的Kubernetes 集群资源都工作在systemadm名字空间中,我们创建一个服务测试一下,服务的配置文件名为my-nginx.yaml ,其内容如下: 952 | 953 | ```yaml 954 | apiVersion: extensions/v1beta1 955 | kind: Deployment 956 | metadata: 957 | name: nginx-deployment 958 | spec: 959 | replicas: 2 960 | template: 961 | metadata: 962 | labels: 963 | app: nginx 964 | spec: 965 | containers: 966 | - name: nginx 967 | image: nginx:latest 968 | imagePullPolicy: IfNotPresent 969 | ports: 970 | - containerPort: 80 971 | --- 972 | apiVersion: v1 973 | kind: Service 974 | metadata: 975 | name: nginx-service 976 | spec: 977 | ports: 978 | - port: 8000 979 | targetPort: 80 980 | protocol: TCP 981 | selector: 982 | app: nginx 983 | type: LoadBalancer 984 | ``` 985 | 986 | 通过kubectl create 命令创建: 987 | 988 | ```shell 989 | [root@master0 ~]# kubectl create -f my-nginx.yaml 990 | deployment "nginx-deployment" created 991 | service "nginx-service" created 992 | ``` 993 | 994 | 通过 kubectl get 命令查看信息: 995 | 996 | ```shell 997 | [root@master0 ~]# kubectl get pod 998 | NAME READY STATUS RESTARTS AGE 999 | nginx-deployment-2273492681-5nykp 1/1 Running 0 12s 1000 | nginx-deployment-2273492681-6lv22 0/1 ContainerCreating 0 12s 1001 | [root@master0 ~]# kubectl get service 1002 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 1003 | nginx-service 100.66.253.187 8000/TCP 21s 1004 | [root@master0 ~]# kubectl get deployment 1005 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 1006 | nginx-deployment 2 2 2 1 29s 1007 | ``` 1008 | 1009 | 这时,如果我们切换Context 运行环境到uplooking-dev ,我们就会发现默认环境下没有任何Pod 、Service和Deployment: 1010 | 1011 | ```shell 1012 | [root@master0 ~]# kubectl config use-context uplooking-dev 1013 | switched to context "uplooking-dev". 1014 | [root@master0 ~]# kubectl get pod 1015 | [root@master0 ~]# kubectl get service 1016 | [root@master0 ~]# kubectl get deployment 1017 | ``` 1018 | 1019 | 这样开发组和运维组就相对独立的配置和管理自己的资源环境了。 1020 | 1021 | 当然,作为管理人员,我们还是可以看到所有名字空间下运行的资源的。你需要在命令后加上--all-namespaces, 就像这样: 1022 | 1023 | ```shell 1024 | [root@master0 ~]# kubectl get pod --all-namespaces 1025 | NAMESPACE NAME READY STATUS RESTARTS AGE 1026 | kube-system etcd-master0.example.com 1/1 Running 8 16d 1027 | kube-system heapster-3901806196-8c2rj 1/1 Running 6 14d 1028 | kube-system kube-apiserver-master0.example.com 1/1 Running 14 16d 1029 | kube-system kube-controller-manager-master0.example.com 1/1 Running 8 16d 1030 | kube-system kube-discovery-982812725-nghjq 1/1 Running 8 16d 1031 | kube-system kube-dns-2247936740-f32d9 3/3 Running 24 16d 1032 | kube-system kube-proxy-amd64-ick75 1/1 Running 3 12d 1033 | kube-system kube-proxy-amd64-px2ms 1/1 Running 6 14d 1034 | kube-system kube-proxy-amd64-t93vd 1/1 Running 1 1d 1035 | kube-system kube-proxy-amd64-xg7bg 1/1 Running 0 3h 1036 | kube-system kube-scheduler-master0.example.com 1/1 Running 10 16d 1037 | kube-system kubernetes-dashboard-1171352413-yuqpa 1/1 Running 6 15d 1038 | kube-system monitoring-grafana-927606581-ruy2u 1/1 Running 0 1d 1039 | kube-system monitoring-influxdb-3276295126-dmbtu 1/1 Running 1 1d 1040 | kube-system weave-net-b1z6l 2/2 Running 1 3h 1041 | kube-system weave-net-fphro 2/2 Running 6 12d 1042 | kube-system weave-net-fqman 2/2 Running 4 1d 1043 | kube-system weave-net-kpvob 2/2 Running 12 14d 1044 | systemadm nginx-deployment-2273492681-5nykp 1/1 Running 0 6m 1045 | systemadm nginx-deployment-2273492681-6lv22 0/1 ImagePullBackOff 0 6m 1046 | ``` 1047 | 1048 | 回显部分就会标注是哪个Namespace 名字空间里运行的Kubernetes 集群资源。 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | -------------------------------------------------------------------------------- /Kubernetes 1.4 基础课程.md: -------------------------------------------------------------------------------- 1 | # Kubernetes 1.4 基础课程 2 | 3 | ##### kissingwolf@gmail.com 4 | 5 | [TOC] 6 | 7 | ## Kubernetes 介绍 8 | 9 | ### Kubernetes的发展历史 10 | 11 | Kubernetes是一个开源的用于管理大量异构主机组成的云平台中容器的应用,Kubernetes的目标是让部署容器化的应用及微服务简单且高效。Kubernetes提供了应用部署、规划、更新和维护的软件集合,它的核心特点之一就是保证云平台中的容器按照用户的期望自动化的运行,云平台管理人员仅仅需要加载一个微型服务,规划器会自动找到合适的位置高可用的运行这个微服务。 12 | 13 | 在Docker作为高级容器引擎快速发展的之前,Google很早就致力于容器技术及集群方面的积累。在Google内部容器技术已经应用了很多年,Borg系统运行管理着成千上万的Google内部容器应用和微服务,在Borg的支持下,无论是谷歌搜索,还是Gmail,以及谷歌地图和YouTube视频,都可以从庞大的数据中心中自动化的获取技术资源来支撑其服务高性能且稳定的运行。Borg项目是Kubernetes项目的前身,Kubernetes正是在Borg的基础上发展、构建、创新而来的。 14 | 15 | 作为集群管理器出现的Borg系统,在其系统中运行着众多集群,而每个集群又由上万台服务器联接组成,Borg每时每刻都在处理来自众多应用程序所提交的成百上千的工作请求(Job), Borg对这些工作请求(Job)进行接收、调度、启动,并对服务器上的容器进行启动、关闭、释放和监控。Borg论文中提到的三大优势: 16 | 17 | * 为终端用户隐藏环境部署、资源管理和错误处理过程,终端用户仅需要关注应用的开发。 18 | * 全局服务高可用、高可靠和性能监控。 19 | * 自动化的将负载到成千上万的异构的服务器组成的集群中。 20 | 21 | Kubernetes于2014年6月在旧金山发布,其在希腊语中意思是船长或领航员,这也与它在容器集群管理中的角色吻合,即作为装载了集装箱(Container)的众多货船的指挥者,负担着全局调度和运行监控的职责。Kubernetes也经常写作k8s,8代表从k到s有“ubernete”8个字符。 22 | 23 | Kubernetes对云环境中的资源进行了更高层次的抽象,通过将容器进行细致的组合,将最终的应用服务交给用户。Kubernetes在模型建立之初就考虑了容器在异构服务器上连接的要求,支持多种网络解决方案,并可以在服务层面上构建集群范围的软件定义网络(SDN)环境。其目的是将服务发现和负载均衡机制放置到容器可达的范围内,这种透明的方式便利了各个服务之间的通信,并为微服务架构提供了平台基础。 24 | 25 | Kubernetes于2015年7月发布了1.0版本,在2016年10月发布了1.4版本。新的1.4版本的Kubernetes更加简单高效,并且支持了最新的Docker 1.12中的新功能。 26 | 27 | ### Kubernnetes 是什么 28 | 29 | Kubernetes是一种用于容器集群的自动化部署、扩容以及运维的开源平台。与其竞争的容器集群管理开源平台还包括Mesos这样的重量级产品。 30 | 31 | 使用Kubernetes可以快速高效地响应客户需求,其特点如下: 32 | 33 | * 动态地对应用进行扩容。 34 | * 无缝地发布和更新应用程序及服务。 35 | * 按需分配资源以优化硬件使用。 36 | 37 | Kubernetes的出现是为了减轻系统运维人员在公有云及私有云上编配和运行应用的负担。 38 | 39 | Kubernetes从开发之初就致力于将其打造为简洁、可移植、可扩展且可自愈的系统平台,具体说明如下: 40 | 41 | * 简洁:轻量级,简单,易上手 42 | * 可移植:公有,私有,混合,多重云(multi-cloud) 43 | * 可扩展:模块化, 插件化, 可挂载, 可组合 44 | * 可自愈: 自动布置, 自动重启, 自动复制 45 | * 以应用程序为中心的管理: 将抽象级别从在虚拟硬件上运行操作系统上升到了在使用特定逻辑资源的操作系统上运行应用程序。这在提供了Paas的简洁性的同时拥有IssS的灵活性,并且相对于运行[12-factor](http://12factor.net/)应用程序有过之而无不及。 46 | * 开发和运维的关注点分离:提供构建和部署的分离;这样也就将应用从基础设施中解耦。 47 | * 敏捷的应用创建和部署: 相对使用虚拟机镜像,容器镜像的创建更加轻巧高效。 48 | * 持续开发,持续集成以及持续部署:提供频繁可靠地构建和部署容器镜像的能力,同时可以快速简单地回滚(因为镜像是固化的)。 49 | * 松耦合,分布式,弹性,自由的微服务:应用被分割为若干独立的小型程序,可以被动态地部署和管理 -- 而不是一个运行在单机上的超级臃肿的大程序。 50 | * 开发,测试,生产环境保持高度一致:无论是再笔记本电脑还是服务器上,都采用相同方式运行。 51 | * 兼容不同的云平台或操作系统上:可运行与Ubuntu,RHEL,on-prem或者Google Container Engine,覆盖了开发,测试和生产的各种不同环境。 52 | * 资源分离:带来可预测的程序性能。 53 | * 资源利用: 高性能,大容量。 54 | 55 | ### Kubernetes 不是什么 56 | 57 | Kubernetes不是平台即服务(PaaS)。 58 | 59 | * Kubernetes并不对支持的应用程序类型有任何限制。 它并不指定应用框架,限制语言类型,也不仅仅迎合 [12-factor](http://12factor.net/)模式。 Kubernetes旨在支持各种多种多样的负载类型:只要一个程序能够在容器中运行,它就可以在Kubernetes中运行。 60 | * Kubernetes并不关注代码到镜像领域。它并不负责应用程序的构建。不同的用户和项目对持续集成流程都有不同的需求和偏好,所以Kubernetes分层支持持续集成但并不规定和限制它的工作方式。 61 | * 确实有不少PaaS系统运行在Kubernetes之上,比如[Openshift](https://github.com/openshift/origin)和[Deis](http://deis.io/)。同样你也可以将定制的PaaS系统,结合一个持续集成系统再Kubernetes上进行实施:只需生成容器镜像并通过Kubernetes部署。 62 | * 由于Kubernetes运行在应用层而不是硬件层,所以它提供了一些一般PaaS提供的功能,比如部署,扩容,负载均衡,日志,监控,等等。无论如何,Kubernetes不是一个单一应用,所以这些解决方案都是可选可插拔的。 63 | 64 | Kubernetes并不是单单的"编排系统";它排除了对编排的需要,“编排”的技术定义为按照指定流程执行一系列动作:执行A,然后B,然后C。相反,Kubernetes有一系列控制进程组成,持续地控制从当前状态到指定状态的流转。无需关注你是如何从A到C:只需结果如此。这样将使得系统更加易用,强大,健壮和弹性。 65 | 66 | ### Kubernetes的组织结构 67 | 68 | Kubernetes组织结构主要是由Node、Pod、Replication Controller、Deployment、Service等多种资源对象组成的。其资源对象属性均保存在etcd提供的键值对存储库中,通过kubectl工具完成对资源对象的增、删、查、改等操作。我们可以将Kubernetes视为一个高度自动化的资源对象控制系统,它通过跟踪和对比etcd键值对库中保存的“对象原始信息”和当前环境中“对象实时信息”的差异来实现自动化控制和自动化纠错等功能的。 69 | 70 | Kubernetes支持[Docker](http://www.docker.io/)和[Rocket](https://coreos.com/blog/rocket/)容器, 对其他的容器镜像格式和容器会在未来加入。 71 | 72 | #### Master 73 | 74 | Kubernetes中的Master是一台运行Kubernetes的主机,可以是物理机也可以是虚拟机,它是Kubernetes集群中的控制节点,它负责完成整个Kubernetes集群的创建、管理和控制,在Kubernetes集群中必不可少。 75 | 76 | 我们默认只能在Master上使用kubectl工具完成Kubernetes具体的操作命令,如果Master宕机或是离线,我们所有的控制命令都会失效。因此Master非常重要,在生产环境中,Master一般会配置高可用集群服务解决其单点故障问题。 77 | 78 | 自Kubernetes 1.4 开始Master上的关键服务都是以Docker 容器实例的方式运行的,包括etcd服务。具体服务和其功能如下: 79 | 80 | * Kubernetes API Server ( kube-apiserver),为kubernetes客户端提供HTTP Rest API接口的关键服务,是kubernetes中对象资源增、删、查、改等操作的唯一入口,同时也是kubernetes集群控制的入口。 81 | * Kubernetes Controller Manager ( kube-controller-manager),为Kubernetes提供统一的自动化控制服务。 82 | * Kubernetes Scheduler (kube-scheduler),为Kubernetes提供资源调度的服务,统一分配资源对象。 83 | * Etcd Server(etcd),为Kubernetes保存所有对象的键值对数据信息。 84 | 85 | #### Node 86 | 87 | 在Kubernetes集群中,除了Master之外其它运行Kubernetes服务的主机称之为Node,在早期Kubernetes中称其为Minion。Node也可以是物理机或虚拟机。Node是Kubernetes集群中任务的负载节点,Master经过特殊设置后也可以作为Node负载任务。Kubernetes一般是由多个Node组成的,我们建议Node的数量是N+2的。当某个Node宕机后,其上的负载任务将会被Master自动转移到其它的Node上。之所以使用N+2的配置,是为了在Node意外宕机时Kubernetes集群的负载不会突然被撑满,导致性能急剧下降。 88 | 89 | Kubernetes 1.4 开始Node上的关键服务都是以Docker 实例的方式运行的,具体服务和功能如下: 90 | 91 | * kubelet,负责Pod对应的容器实例的创建、启动、停止、删除等任务,接受Master传递的指令实现Kubernetes集群管理的基本功能。 92 | * kube-proxy,实现kubernetes service功能和负载均衡机制的服务。 93 | 94 | Node节点通过kubelet服务向Master注册,可以实现动态的在Kubernetes集群中添加和删除负载节点。已加入Kubernetes集群中的Node节点还会通过kubelet服务动态的向Master提交其自身的资源信息,例如主机操作系统、CPU、内存、Docker版本和网络情况。如果Master发现某Node超时未提交信息,Node会被判定为“离线”并标记为“不可用(Not Ready),随后Master会将此离线Node上原有Pod迁移到其它Node上。 95 | 96 | 97 | 98 | #### Pod 99 | 100 | 在Kubenetes中所有的容器均在Pod中运行,一个Pod可以承载一个或者多个相关的容器。同一个Pod中的容器会部署在同一个物理机器上并且能够共享资源。一个Pod也可以包含0个或者多个磁盘卷组(volumes),这些卷组将会以目录的形式提供给一个容器或者被所有Pod中的容器共享。对于用户创建的每个Pod,系统会自动选择那个健康并且有足够资源的机器,然后开始将相应的容器在那里启动,你可以认为Pod就是虚拟机。当容器创建失败的时候,容器会被节点代理(node agent)自动重启,这个节点代理(node agent)就是kubelet服务。在我们定义了副本控制器(replication controller)之后,如果Pod或者服务器故障的时候,容器会自动的转移并且启动。 101 | 102 | Pod是Kubernetes的基本操作单元,把相关的一个或多个容器构成一个Pod,通常Pod里的容器运行相同的应用。Pod包含的容器运行在同一个物理机器上,看作一个统一管理单元,共享相同的卷(volumes)和网络名字空间(network namespace)、IP和端口(Port)空间。在Kubernetes集群中,一个Pod中的容器可以和另一个Node上的Pod容器直接通讯。 103 | 104 | 用户可以自己创建并管理Pod,但是Kubernetes可以极大的简化管理操作,它能让用户指派两个常见的跟Pod相关的活动:1) 基于相同的Pod配置,部署多个Pod副本;2)当一个Pod或者它所在的机器发生故障的时候创建替换的Pod。Kubernetes的API对象用来管理这些行为,我们将其称作副本控制器(Replication Controller),它用模板的形式定义了Pod,然后系统根据模板实例化出一些Pod。Pod的副本集合可以共同组成应用、微服务,或者在一个多层应用中的某一层。一旦Pod创建好,Kubernetes系统会持续的监控他们的健康状态,和它们运行时所在的机器的健康状况。如果一个Pod因为软件或者机器故障,副本控制器(Replication Controller)会自动在健康的机器上创建一个新的Pod,来保证pod的集合处于冗余状态。 105 | 106 | #### Label 107 | 108 | Label用来给Kubernetes中的对象分组。Label通过设置键值对(key-value)方式在创建Kubernetes对象的时候附属在对象之上。一个Kubernetes对象可以定义多个Labels(key=value),并且key和value均由用户自己指定,同一组Label(key=value)可以指定到多个Kubernetes对象,Label可以在创建Kubernetes对象时设置,也可以在对象创建后通过kubectl或kubernetes api添加或删除。其他Kubernetes对象可以通过标签选择器(Label Selector)选择作用对象。你可以把标签选择器(Lebel Selector)看作关系查询语言(SQL)语句中的where条件限定词。 109 | 110 | Lable实现的是将指定的资源对象通过不同的Lable进行捆绑,灵活的实现多维度的资源分配、调度、配置和部署。 111 | 112 | 113 | 114 | #### Replication Controller(RC) 115 | 116 | Replication Controller确保任何时候Kubernetes集群中有指定数量的Pod副本(replicas)在运行, 如果少于指定数量的Pod副本(replicas),Replication Controller会启动新的容器(Container),反之会杀死多余的以保证数量不变。Replication Controller使用预先定义的Pod模板创建pods,一旦创建成功,Pod模板和创建的Pod没有任何关联,可以修改Pod模板而不会对已创建Pod有任何影响。也可以直接更新通过Replication Controller创建的pod。对于利用pod模板创建的pod,Replication Controller根据标签选择器(Label Selector)来关联,通过修改Pod的Label可以删除对应的Pod。 117 | 118 | Replication Controller的定义包含如下的部分: 119 | 120 | * Pod的副本数目(Replicas) 121 | * 用于筛选Pod的标签选择器(Label Selector) 122 | * 用于创建Pod的标准配置模板(Template) 123 | 124 | Replication Controller主要有如下用法: 125 | 126 | * 编排(Rescheduling):Replication Controller会确保Kubernetes集群中指定的pod副本(replicas)在运行, 即使在节点出错时。 127 | * 缩放(Scaling):通过修改Replication Controller的副本(replicas)数量来水平扩展或者缩小运行的pod。 128 | * 滚动升级(Rolling updates): Replication Controller的设计原则使得可以一个一个地替换Pod来滚动升级(rolling updates)服务。 129 | * 多版本任务(Multiple release tracks): 如果需要在系统中运行多版本(multiple release)的服务,Replication Controller使用Labels来区分多版本任务(multiple release tracks)。 130 | 131 | 由于Replication Controller 与Kubernetes API中的模块有同名冲突,所以从Kubernetes 1.2 开始并在Kubernetes 1.4 中它由另一个概念替换,这个新概念的名称为副本设置(Replica Set),Kubernetes官方将其称为”下一代RC“。Replicat Set 支持基于集合的Label Selector(set-based selector),而Replication Controller 仅仅支持基于键值对等式的Label Selector(equality-based selector)。此外,Replicat Set 在Kubernetes 1.4中也不再单独使用,它被更高层次的资源对象Deployment 使用,所以在Kubernetes 1.4中我们使用Deployment定义替换了之前的Replication Controller定义。 132 | 133 | #### Deployment 134 | 135 | 在Kubernetes 1.2开始,定义了新的概念Deployment用以管理Pod和替换Replication Controller。 136 | 137 | 你可以认为Deployment是新一代的副本控制器。其工作方式和配置基本与Replication Controller差不多,后面我们主要使用的副本控制器是Deployment。 138 | 139 | #### Horizontal Pod Autoscaler (HPA) 140 | 141 | Horizontal Pod Autoscaler 为Pod 横向自动扩容,简称HPA。Kubernetes可以通过RC或其替代对象监控Pod的负载变化情况,以此制定针对性方案调整目标Pod的副本数以增加其性能。 142 | 143 | HPA使用以下两种方法度量Pod的指标: 144 | 145 | * CPUUtilizationPercentage,目标Pod所有副本自身的CPU利用率的平均值 146 | * 应用自定义度量指标,例如每秒请求数(TPS) 147 | 148 | #### Service 149 | 150 | Kubernetes中Pod是可创建可销毁而且不可再生的。 Replication Controllers可以动态的创建、配置和销毁Pod。虽然我们可以设置Pod的IP,但是Pod的IP并不能得到稳定和持久化的保证。这将会导致一个凸出的问题,如果在Kubernetes集群中,有一些后端Pod(backends)为另一些前端Pod(frontend)提供服务或功能驱动,如何能保证前端(frontend)能够找到并且链接到后端(backends)。这就需要称之为服务(Service)的Kubernetes对象来完成任务了。 151 | 152 | Services也是Kubernetes的基本操作单元,是Kubernetes运行用户应用服务的抽象,每一个服务后面都有很多对应的容器来支持,通过Proxy的port和服务selector决定服务请求传递给后端提供服务的容器,对外表现为一个单一访问接口,外部不需要了解后端如何运行,这给扩展或维护后端带来很大的好处。 153 | 154 | Kubernetes中的每个Service其实就是我们称之为“微服务”的东西。 155 | 156 | Service同时还是Kubernetes分布式集群架构的核心,Service具有以下特性: 157 | 158 | - 拥有唯一指定的名字 159 | - 拥有虚拟IP 160 | - 能够提供某种网络或Socket服务 161 | - 能够将用户请求映射到提供服务的一组容器的应用上 162 | 163 | 一般情况下,Service通常是由多个Pod及相关服务进程来提供服务的,每个服务进程都具有一个独立的访问点(Endpoint 一组IP和端口),Kubernetes可以通过内建的透明负载均衡和故障恢复机制,排除突发故障并提供我们不间断的访问。 164 | 165 | #### Volume 166 | 167 | Volume是Pod中能够被多个容器访问的共享目录。Kubernetes中的Volume定义的Pod上,然后被一个Pod里的多个容器挂载到具体的目录上,Volume的生命周期与Pod的生命周期相同,但并不是与Pod中的容器相关,当Pod中的容器终止或重启,Volume中的数据不会丢失。Kubernetes中的Volume支持GlusterFS和Ceph这样的分布式文件系统和本地EmptyDir和HostPath这样的本地文件系统。 168 | 169 | #### Persistent Volume 170 | 171 | Persistent Volume 不同于前面提到的Volume ,Volume是分配给Pod的存储资源,而Persistent Volume是Kubernetes集群中的网络存储资源,我们可以在这个资源中划分子存储区域分配给Pod作为Volume使用。Persistent Volume 简称 PV,作为Pod 的Volume使用时,还需要分配Persistent Volume Claim 作为Volume,Persistent Volume Claim简称PVC。 172 | 173 | Persistent Volume 有以下特点: 174 | 175 | * Persistent Volume 只能是网络存储,并不挂接在任何Node,但可以在每个Node上访问到 176 | * Persistent Volume 并不是第一在Pod上,而是独立于Pod定义到Kubernetes集群上 177 | * Persistent Volume 目前支持的类型如下:NFS、RDB、iSCSI 、AWS ElasticBlockStore、GlusterFS和Ceph等 178 | 179 | #### Namespace 180 | 181 | Namespace 是Linux 中资源管理的重要概念和应用,它实现了多用户和多进程的资源隔离。Kubernetes中将集群中的资源对象运行在不同的Namespace下,形成了相对独立的不同分组、类型和资源群。 182 | 183 | 在Kubernetes 集群启动后,会创建第一个默认Namespace 叫做”default“。用户在创建自有的Pod、RC、Deployment 和Service 指定运行的Namespace,在不明确指定的情况下默认使用”default“。 184 | 185 | Kubernetes 集群的资源配额管理也是通过Namespace结合Linux 系统的Cgroup 来完成对不同用户Cpu使用量、内存使用量、磁盘访问速率等资源的分配。 186 | 187 | 188 | 189 | ## Kubernetes 安装和配置 190 | 191 | ### 课堂及实验环境说明 192 | 193 | 我们的环境结构如下,请根据自己的Foundation号调整IP和设备名 194 | 195 | ![up500课程结构图](pic/up500课程结构图.png) 196 | 197 | | 设备名 | 主机名 | 说明 | 198 | | :----------- | :------------ | :-------------------------- | 199 | | master | masterN | Kubernetes Master Node 虚拟主机 | 200 | | nodea | nodeaN | Kubernetes Node 虚拟机 | 201 | | nodeb | nodebN | Kubernetes Node 虚拟机 | 202 | | nodec | nodecN | Kubernetes Node 虚拟机 | 203 | | sharestorage | sharestorageN | 共享存储虚拟机 | 204 | 205 | ### 配置 Kubernetes 运行环境 206 | 207 | 安装Kubernetes的方法有很多,你可以从源代码编译安装,也可以通过kubernetes.io网站脚本安装,但是我们推荐大家使用包管理工具安装Kubernetes,这样可以更好的做到环境更迭和升级。 208 | 209 | 在课程环境中,我们已经做好了基于RedHat YUM的网络包管理环境,RHEL 7.2和Kubernetes 1.4的安装包环境都已就位,课程环境下的所有设备都已设置RHEL 7.2的REPO,但Kubernetes 1.4的REPO没有默认放入设备中,接下来我们将使用YUM RPM的方式安装Kubernetes 1.4。 210 | 211 | 需要注意的是,演示中的环境是在**Foundation 0** 设备上,大家做实验时请替换设备号为你自己所在设备的**Foundation** 号。 212 | 213 | Kubernetes的运行节点中有一个Master 节点的概念,Master节点上运行Kubernetes的管理服务和etcd数据中心服务。我们将使用master虚拟机安装配置Kubernetes的管理、etcd数据中心、proxy代理等服务。同时选择nodea和nodeb两个虚拟机运行Kubernetes上的资源容器服务。后面将使用nodec来添加和剔除节点。 214 | 215 | 首先我们先初始化虚拟机设备: 216 | 217 | ```shell 218 | [kiosk@foundation0 Desktop]$ rht-vmctl reset master 219 | Are you sure you want to reset master? (y/n) y 220 | Powering off master. 221 | Resetting master. 222 | Creating virtual machine disk overlay for up500-master-vda.qcow2 223 | Starting master. 224 | [kiosk@foundation0 Desktop]$ rht-vmctl reset nodea 225 | Are you sure you want to reset nodea? (y/n) y 226 | Powering off nodea. 227 | Resetting nodea. 228 | Creating virtual machine disk overlay for up500-nodea-vda.qcow2 229 | Starting nodea. 230 | [kiosk@foundation0 Desktop]$ rht-vmctl reset nodeb 231 | Are you sure you want to reset nodeb? (y/n) y 232 | Powering off nodeb. 233 | Resetting nodeb. 234 | Creating virtual m 235 | ``` 236 | 237 | 确认所有虚拟机设备正常启动后,通过 ssh 连接 master、nodea和nodeb 设备。你可以通过 **view master** 这样的桌面工具连接。根据你自己的设备号替换0。 238 | 239 | ```shell 240 | [kiosk@foundation0 Desktop]$ ssh root@master0 241 | Last login: Sat Jun 4 14:39:46 2016 from 172.25.0.250 242 | [root@master0 ~]# 243 | ``` 244 | 245 | ```shell 246 | [kiosk@foundation0 Desktop]$ ssh root@nodea0 247 | Last login: Sat Jun 4 14:39:46 2016 from 172.25.0.250 248 | [root@nodea0 ~]# 249 | ``` 250 | 251 | ```shell 252 | [kiosk@foundation0 Desktop]$ ssh root@nodeb0 253 | Last login: Sat Jun 4 14:39:46 2016 from 172.25.0.250 254 | [root@nodeb0 ~]# 255 | ``` 256 | 257 | #### 关闭防火墙 258 | 259 | 由于Kubernetes环境需要配置Iptables来完成端口映射和服务代理,所以我们需要关闭系统中的Firewalld防火墙。根据你自己的设备号替换0。 260 | 261 | ```shell 262 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl disable firewalld " ; done 263 | Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. 264 | Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service. 265 | Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. 266 | Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service. 267 | Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. 268 | Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service. 269 | ``` 270 | 271 | 因为要操作三台虚拟机,所以预先配置好ssh key是明智的选择。如果你没有配置,后续的操作会频繁的输入虚拟机密码。 272 | 273 | 上一条命令我们仅仅是关闭了Firewalld防火墙的启动开关,接下来我们需要将它停止运行并清除其配置的规则。根据你自己的设备号替换0。 274 | 275 | ```shell 276 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl stop firewalld " ; done 277 | ``` 278 | 279 | 注意此处是没有回显的,你可以自行测试。 280 | 281 | #### 关闭NetworkManager服务 282 | 283 | 由于Kubernetes环境中将调用IProute2设置网络,为了不使系统的自适应网络服务影响Kubernetes的网络环境,建议关闭NetowrkManager服务。根据你自己的设备号替换0。 284 | 285 | ```shell 286 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl disable NetworkManager " ; done 287 | Removed symlink /etc/systemd/system/multi-user.target.wants/NetworkManager.service. 288 | Removed symlink /etc/systemd/system/dbus-org.freedesktop.NetworkManager.service. 289 | Removed symlink /etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service. 290 | Removed symlink /etc/systemd/system/multi-user.target.wants/NetworkManager.service. 291 | Removed symlink /etc/systemd/system/dbus-org.freedesktop.NetworkManager.service. 292 | Removed symlink /etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service. 293 | Removed symlink /etc/systemd/system/multi-user.target.wants/NetworkManager.service. 294 | Removed symlink /etc/systemd/system/dbus-org.freedesktop.NetworkManager.service. 295 | Removed symlink /etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service. 296 | ``` 297 | 298 | 上一条命令我们仅仅是关闭了NetworkManager的启动开关,接下来我们需要将它停止运行。根据你自己的设备号替换0。 299 | 300 | ```shell 301 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl stop NetworkManager " ; done 302 | ``` 303 | 304 | #### 关闭SELinux系统安全机制 305 | 306 | 由于Kubernetes 1.4与SELinux在运行环境下有已知的Bug,所以我们要在安装Kubernetes 1.4之前关闭系统的SELinux安全机制。根据你自己的设备号替换0。 307 | 308 | ```shell 309 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "setenforce 0 " ; done 310 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config " ; done 311 | ``` 312 | 313 | #### 安装附加软件包 314 | 315 | 由于目前我们使用的虚拟化系统是最精简的RHEL7.2环境,所以为了后续操作比较方便,我们需要安装wget、bzip2和net-tools软件包。虚拟化系统的YUM环境默认支持RHEL7.2标准包的安装。根据你自己的设备号替换0。 316 | 317 | ```shell 318 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "yum install wget bzip2 net-tools -y " ; done 319 | Loaded plugins: product-id, search-disabled-repos, subscription-manager 320 | This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register. 321 | Repodata is over 2 weeks old. Install yum-cron? Or run: yum makecache fast 322 | Resolving Dependencies 323 | --> Running transaction check 324 | ---> Package bzip2.x86_64 0:1.0.6-13.el7 will be installed 325 | ---> Package net-tools.x86_64 0:2.0-0.17.20131004git.el7 will be installed 326 | ---> Package wget.x86_64 0:1.14-10.el7_0.1 will be installed 327 | --> Finished Dependency Resolution 328 | 329 | -- 中间显示较多,此处忽略 -- 330 | 331 | Complete! 332 | ``` 333 | 334 | #### 配置Kubernetes YUM源环境 335 | 336 | Kubernetes 是基于容器的应用平台,所以需要首先安装Docker。Kubernetes 1.4 支持Docker 1.12的新特性,所以我们建议安装最新的Docker 1.12以发挥其最大的性能。安装Docker1.12和Kubernetes 1.4在我们的实验环境下非常简单,下载环境中的YUM源配置就可以了: 337 | 338 | ```shell 339 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "wget http://classroom.example.com/materials/kubernetes-1.4.repo -O /etc/yum.repos.d/k8s.repo " ; done 340 | --2016-10-27 23:33:26-- http://classroom.example.com/materials/kubernetes-1.4.repo 341 | Resolving classroom.example.com (classroom.example.com)... 172.25.254.254 342 | Connecting to classroom.example.com (classroom.example.com)|172.25.254.254|:80... connected. 343 | HTTP request sent, awaiting response... 200 OK 344 | Length: 151 [application/x-troff-man] 345 | Saving to: ‘/etc/yum.repos.d/k8s.repo’ 346 | 347 | 0K 100% 13.1M=0s 348 | 349 | 2016-10-27 23:33:26 (13.1 MB/s) - ‘/etc/yum.repos.d/k8s.repo’ saved [151/151] 350 | 351 | --2016-10-27 23:33:26-- http://classroom.example.com/materials/kubernetes-1.4.repo 352 | Resolving classroom.example.com (classroom.example.com)... 172.25.254.254 353 | Connecting to classroom.example.com (classroom.example.com)|172.25.254.254|:80... connected. 354 | HTTP request sent, awaiting response... 200 OK 355 | Length: 151 [application/x-troff-man] 356 | Saving to: ‘/etc/yum.repos.d/k8s.repo’ 357 | 358 | 0K 100% 28.2M=0s 359 | 360 | 2016-10-27 23:33:26 (28.2 MB/s) - ‘/etc/yum.repos.d/k8s.repo’ saved [151/151] 361 | 362 | --2016-10-27 23:33:25-- http://classroom.example.com/materials/kubernetes-1.4.repo 363 | Resolving classroom.example.com (classroom.example.com)... 172.25.254.254 364 | Connecting to classroom.example.com (classroom.example.com)|172.25.254.254|:80... connected. 365 | HTTP request sent, awaiting response... 200 OK 366 | Length: 151 [application/x-troff-man] 367 | Saving to: ‘/etc/yum.repos.d/k8s.repo’ 368 | 369 | 0K 100% 33.8M=0s 370 | 371 | 2016-10-27 23:33:25 (33.8 MB/s) - ‘/etc/yum.repos.d/k8s.repo’ saved [151/151] 372 | ``` 373 | 374 | 如果是公网线上环境,建议设置如下两个源: 375 | 376 | ```shell 377 | [kevinzou@hk ~]$ cat /etc/yum.repos.d/docker.repo 378 | [dockerrepo] 379 | name=Docker Repository 380 | baseurl=https://yum.dockerproject.org/repo/main/centos/7/ 381 | enabled=1 382 | gpgcheck=1 383 | gpgkey=https://yum.dockerproject.org/gpg 384 | 385 | [kevinzou@hk ~]$ cat /etc/yum.repos.d/kubernetes.repo 386 | [kubernetes] 387 | name=Kubernetes 388 | baseurl=http://yum.kubernetes.io/repos/kubernetes-el7-x86_64 389 | enabled=1 390 | gpgcheck=1 391 | repo_gpgcheck=1 392 | gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg 393 | https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg 394 | ``` 395 | 396 | ### 安装 Kubernetes 1.4 397 | 398 | #### 安装软件包 399 | 400 | 我们需要安装如下软件包: 401 | 402 | | 软件包名 | 软件包说明 | 403 | | -------------- | ------------------------------ | 404 | | docker-engine | Docker Engine 软件包,提供底层容器支持 | 405 | | kubeadm | Kubernetes 1.4 新增 配置Cluster工具包 | 406 | | kubectl | Kubernetes Master环境配置管理工具包 | 407 | | kubelet | Kubernetes 主程序包 | 408 | | kubernetes-cni | Kubernetes 容器网络配置工具包 | 409 | 410 | 我们需要在所有节点上安装这些软件包,无论是Manager还是Node。 411 | 412 | ```shell 413 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "yum install docker-engine kubeadm kubectl kubelet kubernetes-cni -y " ; done 414 | Loaded plugins: product-id, search-disabled-repos, subscription-manager 415 | This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register. 416 | Resolving Dependencies 417 | --> Running transaction check 418 | ---> Package docker-engine.x86_64 0:1.12.2-1.el7.centos will be installed 419 | --> Processing Dependency: docker-engine-selinux >= 1.12.2-1.el7.centos for package: docker-engine-1.12.2-1.el7.centos.x86_64 420 | --> Processing Dependency: libcgroup for package: docker-engine-1.12.2-1.el7.centos.x86_64 421 | --> Processing Dependency: libseccomp.so.2()(64bit) for package: docker-engine-1.12.2-1.el7.centos.x86_64 422 | --> Processing Dependency: libltdl.so.7()(64bit) for package: docker-engine-1.12.2-1.el7.centos.x86_64 423 | ---> Package kubeadm.x86_64 0:1.5.0-0.alpha.0.1534.gcf7301f will be installed 424 | ---> Package kubectl.x86_64 0:1.4.0-0 will be installed 425 | ---> Package kubelet.x86_64 0:1.4.0-0 will be installed 426 | --> Processing Dependency: socat for package: kubelet-1.4.0-0.x86_64 427 | ---> Package kubernetes-cni.x86_64 0:0.3.0.1-0.07a8a2 will be installed 428 | --> Running transaction check 429 | ---> Package docker-engine-selinux.noarch 0:1.12.2-1.el7.centos will be installed 430 | -- 中间显示较多,此处忽略 -- 431 | libseccomp.x86_64 0:2.2.1-1.el7 432 | libsemanage-python.x86_64 0:2.1.10-18.el7 433 | libtool-ltdl.x86_64 0:2.4.2-20.el7 434 | policycoreutils-python.x86_64 0:2.2.5-20.el7 435 | python-IPy.noarch 0:0.75-6.el7 436 | setools-libs.x86_64 0:3.3.7-46.el7 437 | socat.x86_64 0:1.7.2.2-5.el7 438 | 439 | Complete! 440 | 441 | ``` 442 | 443 | 由于总所周知却不可描述的原因,你可能无法在大陆网络环境下安装这些软件包,请自备梯子。 444 | 445 | #### 启动Docker服务 446 | 447 | 设置docker随系统启动 448 | 449 | ```shell 450 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl enable docker kubelet " ; done 451 | Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service. 452 | Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service. 453 | Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service. 454 | Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service. 455 | Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service. 456 | Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service. 457 | 458 | ``` 459 | 460 | 启动docker 服务 461 | 462 | ```shell 463 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl start docker " ; done 464 | 465 | ``` 466 | 467 | #### 预导入容器镜像 468 | 469 | 从Kubernetes 1.4开始,Kubenetes实现了容器化运行其依赖服务,比如etcd、kube-proxy、kube-controller-manager等。因此,在Kubernetes应用启动的时候首先会通过网络下载所需要的容器镜像,由于众所周知且不可描述的原因,Kubernetes所需的Google容器镜像文件大陆地区无法下载,我们访问不到gcr.io网站。 470 | 471 | 我们通过其他技术手段可以访问gcr.io网站,并下载了这些容器镜像。如果你需要独立下载安装Kubernetes 1.4,请自备梯子。 472 | 473 | Master节点需要如下容器镜像: 474 | 475 | gcr.io/google_containers/etcd-amd64:2.2.5 476 | gcr.io/google_containers/exechealthz-amd64:1.1 477 | gcr.io/google_containers/kube-apiserver-amd64:v1.4.0 478 | gcr.io/google_containers/kube-controller-manager-amd64:v1.4.0 479 | gcr.io/google_containers/kube-discovery-amd64:1.0 480 | gcr.io/google_containers/kubedns-amd64:1.7 481 | gcr.io/google_containers/kube-dnsmasq-amd64:1.3 482 | gcr.io/google_containers/kube-proxy-amd64:v1.4.0 483 | gcr.io/google_containers/kube-scheduler-amd64:v1.4.0 484 | gcr.io/google_containers/pause-amd64:3.0 485 | weaveworks/weave-kube:1.7.2 486 | weaveworks/weave-npc:1.7.2 487 | 488 | Node节点需要如下容器镜像: 489 | 490 | gcr.io/google_containers/kube-proxy-amd64:v1.4.0 491 | gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0 492 | gcr.io/google_containers/pause-amd64:3.0 493 | weaveworks/weave-kube:1.7.2 494 | weaveworks/weave-npc:1.7.2 495 | 496 | 在后面的章节中会具体介绍这些容器镜像的具体作用。 497 | 498 | 接下来在实验环境中下载并导入容器镜像: 499 | 500 | 1. 下载k8s-1.4-master-img.tbz到Master节点 501 | 502 | ```shell 503 | [kiosk@foundation0 Desktop]$ ssh root@master0 "wget http://classroom.example.com/materials/k8s-imgs/k8s-1.4-master-img.tbz " 504 | ``` 505 | 506 | 2. 下载k8s-1.4-node-img.tbz 到Node节点 507 | 508 | ```shell 509 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "wget http://classroom.example.com/materials/k8s-imgs/k8s-1.4-node-img.tbz " ; done 510 | ``` 511 | 512 | 3. 在Master节点上将k8s-1.4-master-img.tbz文件解包 513 | 514 | ```shell 515 | [kiosk@foundation0 Desktop]$ ssh root@master0 "tar -jxf k8s-1.4-master-img.tbz" 516 | ``` 517 | 518 | 4. 在Master节点上导入容器镜像 519 | 520 | ```shell 521 | [kiosk@foundation0 Desktop]$ ssh root@master0 'for i in ./k8s-1.4-master-img/*.img ; do docker load -i $i ; done' 522 | ``` 523 | 524 | 5. 在Node节点上将k8s-1.4-node-img.tbz文件解包 525 | 526 | ```shell 527 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf k8s-1.4-node-img.tbz " ; done 528 | ``` 529 | 530 | 6. 在Node节点上导入容器镜像 531 | 532 | ```shell 533 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./k8s-1.4-node-img/*.img ; do docker load -i $i ; done' ; done 534 | ``` 535 | 536 | ### 启动 Kubernetes集群 537 | 538 | #### 确认kubelet服务启动 539 | 540 | 保证SELinux关闭的前提下在所有节点上启动kubelet服务 541 | 542 | ```shell 543 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "hostname ; getenforce " ; done 544 | master0.example.com 545 | Permissive 546 | nodea0.example.com 547 | Permissive 548 | nodeb0.example.com 549 | Permissive 550 | 551 | [kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl start kubelet " ; done 552 | 553 | ``` 554 | 555 | 此时如果查看节点上的日志文件/var/log/messages,此时会报错出来,原因是没有修改配置文件。 556 | 557 | ```shell 558 | Oct 28 01:03:50 localhost systemd: Unit kubelet.service entered failed state. 559 | Oct 28 01:03:50 localhost systemd: kubelet.service failed. 560 | Oct 28 01:04:01 localhost systemd: kubelet.service holdoff time over, scheduling restart. 561 | Oct 28 01:04:01 localhost systemd: Started Kubernetes Kubelet Server. 562 | Oct 28 01:04:01 localhost systemd: Starting Kubernetes Kubelet Server... 563 | Oct 28 01:04:01 localhost kubelet: error: failed to run Kubelet: invalid kubeconfig: stat /etc/kubernetes/kubelet.conf: no such file or directory 564 | Oct 28 01:04:01 localhost systemd: kubelet.service: main process exited, code=exited, status=1/FAILURE 565 | ``` 566 | 567 | #### 初始化Kubernetes 1.4 集群 568 | 569 | Kubernetes 1.4 受Docker 1.12的影响提供了kubeadm工具,使Kubernetes 1.4的集群搭建非常简单,仅仅两个命令就可以完成集群的初始化和节点的加入。 570 | 571 | * 初始化集群环境 572 | 573 | ```shell 574 | [kiosk@foundation0 Desktop]$ ssh root@master0 "kubeadm init" 575 | generated token: "279c9d.eb291600fc5d4f6f" 576 | created keys and certificates in "/etc/kubernetes/pki" 577 | created "/etc/kubernetes/kubelet.conf" 578 | created "/etc/kubernetes/admin.conf" 579 | created API client configuration 580 | created API client, waiting for the control plane to become ready 581 | all control plane components are healthy after 15.801121 seconds 582 | waiting for at least one node to register and become ready 583 | first node is ready after 3.007568 seconds 584 | created essential addon: kube-discovery, waiting for it to become ready 585 | kube-discovery is ready after 2.504817 seconds 586 | created essential addon: kube-proxy 587 | created essential addon: kube-dns 588 | 589 | Kubernetes master initialised successfully! 590 | 591 | You can now join any number of machines by running the following on each node: 592 | 593 | kubeadm join --token 279c9d.eb291600fc5d4f6f 172.25.0.10 594 | 595 | ``` 596 | 597 | 请记下最后一行“kubeadm join --token 279c9d.eb291600fc5d4f6f 172.25.0.10”,后面我们节点加入时会用到。 598 | 599 | * 查看初始化状态 600 | 601 | ```shell 602 | [kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl get pod --all-namespaces" 603 | NAMESPACE NAME READY STATUS RESTARTS AGE 604 | kube-system etcd-master0.example.com 1/1 Running 0 6m 605 | kube-system kube-apiserver-master0.example.com 1/1 Running 1 6m 606 | kube-system kube-controller-manager-master0.example.com 1/1 Running 0 6m 607 | kube-system kube-discovery-982812725-l5uow 1/1 Running 0 6m 608 | kube-system kube-dns-2247936740-uxzfc 0/3 ContainerCreating 0 6m 609 | kube-system kube-proxy-amd64-joq3k 1/1 Running 0 6m 610 | kube-system kube-scheduler-master0.example.com 1/1 Running 0 6m 611 | ``` 612 | 613 | * 加入新节点 614 | 615 | ```shell 616 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i " kubeadm join --token 279c9d.eb291600fc5d4f6f 172.25.0.10 " ; done 617 | validating provided token 618 | created cluster info discovery client, requesting info from "http://172.25.0.10:9898/cluster-info/v1/?token-id=279c9d" 619 | cluster info object received, verifying signature using given token 620 | cluster info signature and contents are valid, will use API endpoints [https://172.25.0.10:443] 621 | created API client to obtain unique certificate for this node, generating keys and certificate signing request 622 | received signed certificate from the API server, generating kubelet configuration 623 | created "/etc/kubernetes/kubelet.conf" 624 | 625 | Node join complete: 626 | * Certificate signing request sent to master and response 627 | received. 628 | * Kubelet informed of new secure connection details. 629 | 630 | Run 'kubectl get nodes' on the master to see this machine join. 631 | 632 | validating provided token 633 | created cluster info discovery client, requesting info from "http://172.25.0.10:9898/cluster-info/v1/?token-id=279c9d" 634 | cluster info object received, verifying signature using given token 635 | cluster info signature and contents are valid, will use API endpoints [https://172.25.0.10:443] 636 | created API client to obtain unique certificate for this node, generating keys and certificate signing request 637 | received signed certificate from the API server, generating kubelet configuration 638 | created "/etc/kubernetes/kubelet.conf" 639 | 640 | Node join complete: 641 | * Certificate signing request sent to master and response 642 | received. 643 | * Kubelet informed of new secure connection details. 644 | 645 | Run 'kubectl get nodes' on the master to see this machine join. 646 | 647 | ``` 648 | 649 | * 测试节点状态 650 | 651 | ```shell 652 | [kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl get nodes" 653 | NAME STATUS AGE 654 | master0.example.com Ready 10m 655 | nodea0.example.com Ready 1m 656 | nodeb0.example.com Ready 1m 657 | 658 | [kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl get pod --all-namespaces" 659 | NAMESPACE NAME READY STATUS RESTARTS AGE 660 | kube-system etcd-master0.example.com 1/1 Running 0 13m 661 | kube-system kube-apiserver-master0.example.com 1/1 Running 1 13m 662 | kube-system kube-controller-manager-master0.example.com 1/1 Running 0 13m 663 | kube-system kube-discovery-982812725-l5uow 1/1 Running 0 13m 664 | kube-system kube-dns-2247936740-uxzfc 0/3 ContainerCreating 0 13m 665 | kube-system kube-proxy-amd64-7oy3h 1/1 Running 0 3m 666 | kube-system kube-proxy-amd64-joq3k 1/1 Running 0 13m 667 | kube-system kube-proxy-amd64-wbyyx 1/1 Running 0 3m 668 | kube-system kube-scheduler-master0.example.com 1/1 Running 0 13m 669 | 670 | ``` 671 | 672 | 大家会发现kube-dns一直处在ContainerCreating状态,这是因为我们还未配置Kubernetes网络。 673 | 674 | * 配置kubernetes网络 675 | 676 | 我们使用weave-kube配置Kubernetes网络,公网地址为 https://git.io/weave-kube 677 | 678 | ```shell 679 | [kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl apply -f http://classroom.example.com/materials/k8s-conf/weave-kube" 680 | daemonset "weave-net" created 681 | ``` 682 | 683 | * 测试节点状态 684 | 685 | ```shell 686 | [kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl get pod --all-namespaces" 687 | NAMESPACE NAME READY STATUS RESTARTS AGE 688 | kube-system etcd-master0.example.com 1/1 Running 0 22m 689 | kube-system kube-apiserver-master0.example.com 1/1 Running 1 22m 690 | kube-system kube-controller-manager-master0.example.com 1/1 Running 0 22m 691 | kube-system kube-discovery-982812725-l5uow 1/1 Running 0 22m 692 | kube-system kube-dns-2247936740-uxzfc 3/3 Running 0 22m 693 | kube-system kube-proxy-amd64-7oy3h 1/1 Running 0 12m 694 | kube-system kube-proxy-amd64-joq3k 1/1 Running 0 22m 695 | kube-system kube-proxy-amd64-wbyyx 1/1 Running 0 12m 696 | kube-system kube-scheduler-master0.example.com 1/1 Running 0 22m 697 | kube-system weave-net-15j6s 2/2 Running 0 3m 698 | kube-system weave-net-3ybbh 2/2 Running 0 3m 699 | kube-system weave-net-8ilap 2/2 Running 0 3m 700 | ``` 701 | 702 | * 安装kubectl扩展工具 703 | 704 | 默认情况下我们使用kubernetes控制工具kubectl时是没有自动补全功能的,这样在日常工作中要记忆大量繁琐的命令参数和当前状态值。我们需要生成系统支持的补全(completion)文件,并放置到系统相关目录中。 705 | 706 | ```shell 707 | [root@master0 ~]# kubectl completion bash >kubectl 708 | [root@master0 ~]# cp kubectl /etc/bash_completion.d/ 709 | ``` 710 | 711 | 需要重新登录系统此配置才能生效。重新登录master0系统后使用kubectl工具就可以使用TAB键来补全命令了。 712 | 713 | ### 安装kubernetes-dashboard 714 | 715 | Kubernetes 1.2中引入了一个Web UI以方便用户配置管理Kubernetes 集群,这个Web UI就是kubernetes-dashboard。它是Kubernetes官方提供的可视化工具,在Kubernetes 1.4系统中可以完成大多数的管理操作。 716 | 717 | 在Kubernetes 1.4中安装kubernetes-dashboard是非常简单的,直接使用Kubernetes提供的yaml文件就可以安装,公网yaml文件位置在https://rawgit.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml。我们在实验环境下为大家提供了本地文件。 718 | 719 | ```shell 720 | [kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl apply -f http://classroom.example.com/materials/k8s-conf/kubernetes-dashboard.yaml " 721 | deployment "kubernetes-dashboard" created 722 | service "kubernetes-dashboard" created 723 | ``` 724 | 725 | kubernetes-dashboard.yaml文件的内容如下,具体的关键字说明会在后面的课程中具体描述: 726 | 727 | ```yaml 728 | # Copyright 2015 Google Inc. All Rights Reserved. 729 | # 730 | # Licensed under the Apache License, Version 2.0 (the "License"); 731 | # you may not use this file except in compliance with the License. 732 | # You may obtain a copy of the License at 733 | # 734 | # http://www.apache.org/licenses/LICENSE-2.0 735 | # 736 | # Unless required by applicable law or agreed to in writing, software 737 | # distributed under the License is distributed on an "AS IS" BASIS, 738 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 739 | # See the License for the specific language governing permissions and 740 | # limitations under the License. 741 | 742 | # Configuration to deploy release version of the Dashboard UI. 743 | # 744 | # Example usage: kubectl create -f 745 | 746 | kind: Deployment 747 | apiVersion: extensions/v1beta1 748 | metadata: 749 | labels: 750 | app: kubernetes-dashboard 751 | name: kubernetes-dashboard 752 | namespace: kube-system 753 | spec: 754 | replicas: 1 755 | selector: 756 | matchLabels: 757 | app: kubernetes-dashboard 758 | template: 759 | metadata: 760 | labels: 761 | app: kubernetes-dashboard 762 | spec: 763 | containers: 764 | - name: kubernetes-dashboard 765 | image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0 766 | imagePullPolicy: IfNotPresent 767 | # 请注意这里,imagePullPolicy关键字设置了获取容器镜像的策略,网络文件中默认设置为 768 | # Always 表示每次都下载镜像,我将这里设置为IfNotPresent,这样我们就可以使用之前预 769 | # 导入的镜像 770 | ports: 771 | - containerPort: 9090 772 | protocol: TCP 773 | args: 774 | # Uncomment the following line to manually specify Kubernetes API server Host 775 | # If not specified, Dashboard will attempt to auto discover the API server and connect 776 | # to it. Uncomment only if the default does not work. 777 | # - --apiserver-host=http://my-address:port 778 | livenessProbe: 779 | httpGet: 780 | path: / 781 | port: 9090 782 | initialDelaySeconds: 30 783 | timeoutSeconds: 30 784 | --- 785 | kind: Service 786 | apiVersion: v1 787 | metadata: 788 | labels: 789 | app: kubernetes-dashboard 790 | name: kubernetes-dashboard 791 | namespace: kube-system 792 | spec: 793 | type: NodePort 794 | ports: 795 | - port: 80 796 | targetPort: 9090 797 | selector: 798 | app: kubernetes-dashboard 799 | ``` 800 | 801 | 正常创建kubernetes-dashboard服务后,我们需要查看其抛出的随机端口,这个端口号30000~32767之间,Kubernetes 通过kube-proxy服务代理用户请求,具体链接过程我们后面的课程具体介绍。 802 | 803 | ```shell 804 | [root@master0 ~]# kubectl describe service --namespace=kube-system 805 | Name: kube-dns 806 | Namespace: kube-system 807 | Labels: component=kube-dns 808 | k8s-app=kube-dns 809 | kubernetes.io/cluster-service=true 810 | name=kube-dns 811 | tier=node 812 | Selector: name=kube-dns 813 | Type: ClusterIP 814 | IP: 100.64.0.10 815 | Port: dns 53/UDP 816 | Endpoints: 10.46.0.1:53 817 | Port: dns-tcp 53/TCP 818 | Endpoints: 10.46.0.1:53 819 | Session Affinity: None 820 | No events. 821 | 822 | Name: kubernetes-dashboard 823 | Namespace: kube-system 824 | Labels: app=kubernetes-dashboard 825 | Selector: app=kubernetes-dashboard 826 | Type: NodePort 827 | IP: 100.70.61.228 828 | Port: 80/TCP 829 | NodePort: 30054/TCP 830 | Endpoints: 10.40.0.1:9090 831 | Session Affinity: None 832 | ``` 833 | 834 | 我做实验的时候kubernetes-dashboard的NodePort是30054/TCP,你看到的NodePort应该和我这里不同。通过浏览器firefox访问http://master0.example.com:30054就可以看到Kubernetes UI的界面了。生产环境下需要考虑安全性,应该在前端加认证代理。 835 | 836 | ![001-kubernetes-dashboard](pic/001-kubernetes-dashboard.png) 837 | 838 | 在这里Kubernetes 1.4中有个Bug,本来我们是可以通过https://master0.example.com/ui访问Kubernetes UI的,但是由于代码的兼容问题,导致访问的时候会回显”Unauthorized“信息。 839 | 840 | ## 管理 Kubernetes 集群服务 841 | 842 | ### 从简单的WEB服务入手学习 843 | 844 | #### kubectl命令介绍 845 | 846 | 我们后续的操作都是使用kubectl命令来完成的,并使用kubernetes-dashboard WEB UI验证。我们看看如何使用kubectl命令。 847 | 848 | 首先kubectl命令默认只能在Master节点上运行 849 | 850 | ```shell 851 | [root@master0 ~]# kubectl cluster-info 852 | Kubernetes master is running at http://localhost:8080 853 | kube-dns is running at http://localhost:8080/api/v1/proxy/namespaces/kube-system/services/kube-dns 854 | 855 | To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. 856 | ``` 857 | 858 | 如果要在Node节点上运行,默认会由于无法连接Kubernetes管理服务而报错 859 | 860 | ```shell 861 | [root@nodea0 ~]# kubectl cluster-info 862 | The connection to the server localhost:8080 was refused - did you specify the right host or port? 863 | ``` 864 | 865 | 如果你希望在Node节点上正确的执行kubectl,需要将Master节点上的admin.conf文件拷贝到Node节点上,并明确指定admin.conf的路径。这种操作由于安全性问题不推荐。 866 | 867 | ```shell 868 | [root@master0 ~]# scp /etc/kubernetes/admin.conf root@nodea0:~/ 869 | root@nodea0's password: 870 | admin.conf 100% 9190 9.0KB/s 00:00 871 | 872 | [root@nodea0 ~]# kubectl --kubeconfig=./admin.conf cluster-info 873 | Kubernetes master is running at https://172.25.0.10:443 874 | kube-dns is running at https://172.25.0.10:443/api/v1/proxy/namespaces/kube-system/services/kube-dns 875 | 876 | To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. 877 | ``` 878 | 879 | 请注意上例中的主机名,请替换为你自己的设备号。 880 | 881 | kubectl命令的子命令和操作参数比较多,在使用kubectl操作Kubernetes环境之前,我们先罗列出其常用的子命令和操作参数。 882 | 883 | kubectl 常用子命令列表: 884 | 885 | | 命令 | 说明 | 886 | | -------------- | -------------------------------------- | 887 | | get | 显示资源信息 | 888 | | describe | 详细的描述资源信息 | 889 | | create | 通过指定的文件或URL创建资源 | 890 | | update | 通过指定的文件或URL修改更新资源 | 891 | | delete | 通过指定的文件、URL、资源ID或标签删除资源 | 892 | | namespace | 设置或显示指定的命名空间(已经弃用) | 893 | | logs | 打印在某Pod中的容器日志信息 | 894 | | rolling-update | 对给定的副本控制器(ReplicationController)执行滚动更新 | 895 | | scale | 调整副本管理器(ReplicationController)副本数 | 896 | | exec | 在某容器中执行命令,类似Docker的exec命令 | 897 | | port-forward | 为某Pod设置一个或多个端口转发 | 898 | | proxy | 运行Kubernetes API Server代理 | 899 | | run | 在机器中运行一个独立的镜像 | 900 | | stop | 通过ID或资源名删除一个资源(已经弃用) | 901 | | expose | 将资源对象暴露为Kubernetes Server | 902 | | label | 修改某个资源上的标签 | 903 | | config | 修改集群的配置信息 | 904 | | cluster-info | 显示集群信息 | 905 | | api-versions | 显示API版本信息 | 906 | | version | 打印kubectl和API Server版本信息 | 907 | | help | 帮助命令 | 908 | 909 | kubectl 常用可操作资源对象列表: 910 | 911 | | 资源对象名称 | 缩写 | 912 | | :----------------------- | :----- | 913 | | deployments | de | 914 | | endpoints | ep | 915 | | horizontalpodautoscalers | hpa | 916 | | ingresses | ing | 917 | | limitranges | limits | 918 | | nodes | no | 919 | | namespaces | ns | 920 | | pods | po | 921 | | persistentvolumes | pv | 922 | | persistentvolumecails | pvc | 923 | | resourcequotas | quota | 924 | | replicationcontrollers | rc | 925 | | services | svc | 926 | | deployments | null | 927 | | jobs | null | 928 | | secrets | null | 929 | | serviceaccounts | null | 930 | 931 | kubectl 常用命令行公共参数: 932 | 933 | | 参数 | 说明 | 934 | | ----------------------------------- | ---------------------------------------- | 935 | | --alsologtostderr[=false] | 同时输出日志到标准错误控制台和文件 | 936 | | --certificate-authority="" | 用以进行认证授权的.cert文件路径 | 937 | | --client-certificate="" | TLS使用的客户端证书路径 | 938 | | --client-key="" | TLS使用的客户端密钥路径 | 939 | | --cluster="" | 指定使用的kubeconfig配置文件中的集群名 | 940 | | --context="" | 指定使用的kubeconfig配置文件中的环境名 | 941 | | --insecure-skip-tls-verify[=false]: | 如果为true,将不会检查服务器凭证的有效性,这会导致你的HTTPS链接变得不安全 | 942 | | --kubeconfig="" | 命令行请求使用的配置文件路径 | 943 | | --log-backtrace-at=:0 | 当日志长度超过定义的行数时,忽略堆栈信息 | 944 | | --log-dir="" | 如果不为空,将日志文件写入此目录 | 945 | | --log-flush-frequency=5s | 刷新日志的最大时间间隔 | 946 | | --logtostderr[=true] | 输出日志到标准错误控制台,不输出到文件 | 947 | | --match-server-version[=false] | 要求服务端和客户端版本匹配 | 948 | | --namespace="" | 如果不为空,命令将使用指定namespace | 949 | | --password="" | API Server进行简单认证使用的密码 | 950 | | -s, --server="" | Kubernetes API Server的地址和端口号 | 951 | | --stderrthreshold=2 | 高于此级别的日志将被输出到错误控制台 | 952 | | --token="" | 认证到API Server使用的令牌 | 953 | | --user="" | 指定使用的kubeconfig配置文件中的用户名 | 954 | | --username="" | API Server进行简单认证使用的用户名 | 955 | | --v=0 | 指定输出日志的级别 | 956 | | --vmodule= | 指定输出日志的模块,格式如下:pattern=N,使用逗号分隔 | 957 | 958 | #### 创建简单Pod 959 | 960 | Kubernetes创建对象的时候一般是使用预定义文件编写对象信息说明,然后使用kubectl工具或kubernetes-dashboard UI来创建。由于我们在内网完成此操作,所以需要预先将镜像导入所有node节点,如果你是在公网环境下,则不需要预先导入镜像。 961 | 962 | 1. 下载镜像文件压缩包 963 | 964 | ```shell 965 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "wget http://classroom.example.com/materials/k8s-imgs/nginx-img.tbz " ; done 966 | ``` 967 | 968 | 2. 在节点上解开压缩包 969 | 970 | ```shell 971 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf nginx-img.tbz " ; done 972 | ``` 973 | 974 | 3. 将镜像导入节点系统 975 | 976 | 977 | ```shell 978 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./nginx/*.img ; do docker load -i $i ; done' ; done 979 | ``` 980 | 981 | 我们以nginx web应用为例创建我们第一个Pod。以下是创建Pod的yaml文件内容: 982 | 983 | ```yaml 984 | apiVersion: v1 985 | kind: Pod 986 | metadata: 987 | name: nginx 988 | spec: 989 | containers: 990 | - name: nginx 991 | image: nginx:latest 992 | imagePullPolicy: IfNotPresent 993 | ports: 994 | - containerPort: 80 995 | ``` 996 | 997 | 将文件保存为nginx-pod.yaml,后面我们将使用”kubectl create“命令来创建nginx Pod。文件中的标签我们后面具体介绍。 998 | 999 | ```shell 1000 | # 查看配置文件内容 1001 | [root@master0 ~]# cat nginx-pod.yaml 1002 | apiVersion: v1 1003 | kind: Pod 1004 | metadata: 1005 | name: nginx 1006 | spec: 1007 | containers: 1008 | - name: nginx 1009 | image: nginx:latest 1010 | imagePullPolicy: IfNotPresent 1011 | ports: 1012 | - containerPort: 80 1013 | 1014 | # 创建Pod nginx 1015 | [root@master0 ~]# kubectl create -f nginx-pod.yaml 1016 | pod "nginx" created 1017 | 1018 | # 查看Pod 状态和信息 1019 | [root@master0 ~]# kubectl get pod 1020 | NAME READY STATUS RESTARTS AGE 1021 | nginx 1/1 Running 0 31s 1022 | 1023 | # 查看Pod nginx的详细信息 1024 | [root@master0 ~]# kubectl describe pod nginx 1025 | Name: nginx 1026 | Namespace: default 1027 | Node: nodea0.example.com/172.25.0.11 1028 | Start Time: Fri, 28 Oct 2016 08:39:58 +0800 1029 | Labels: 1030 | Status: Running 1031 | IP: 10.32.0.2 1032 | Controllers: 1033 | Containers: 1034 | nginx: 1035 | Container ID: docker://76ff103f17a4024c94e3d28d2a98814bc943be8626216965e9be43726179d37c 1036 | Image: nginx:latest 1037 | Image ID: docker://sha256:e43d811ce2f4986aa69bc8ba6c92f0789537f604d1601e0b6ec024e1c38062b4 1038 | Port: 80/TCP 1039 | State: Running 1040 | Started: Fri, 28 Oct 2016 08:40:28 +0800 1041 | Ready: True 1042 | Restart Count: 0 1043 | Volume Mounts: 1044 | /var/run/secrets/kubernetes.io/serviceaccount from default-token-d6l71 (ro) 1045 | Environment Variables: 1046 | Conditions: 1047 | Type Status 1048 | Initialized True 1049 | Ready True 1050 | PodScheduled True 1051 | Volumes: 1052 | default-token-d6l71: 1053 | Type: Secret (a volume populated by a Secret) 1054 | SecretName: default-token-d6l71 1055 | QoS Class: BestEffort 1056 | Tolerations: 1057 | Events: 1058 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 1059 | --------- -------- ----- ---- ------------- -------- ------ ------- 1060 | 1m 1m 1 {default-scheduler } Normal Scheduled Successfully assigned nginx to nodea0.example.com 1061 | 55s 55s 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Pulling pulling image "nginx:latest" 1062 | 35s 35s 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Pulled Successfully pulled image "nginx:latest" 1063 | 32s 32s 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Created Created container with docker id 76ff103f17a4; Security:[seccomp=unconfined] 1064 | 31s 31s 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Started Started container with docker id 76ff103f17a4 1065 | ``` 1066 | 1067 | 目前创建的Pod是无法在外部访问的,在配置文件中我们指定的80/tcp端口只能Kubernetes内指定运行的其他Pod访问。为能够访问这个nginx,我们还要创建一个基本容器(busybox),使用这个基本容器来测试Pod的访问状态。 1068 | 1069 | ```shell 1070 | [root@master0 ~]# kubectl run busybox --image=busybox:latest --restart=Never --tty -i --generator=run-pod/v1 --env="POD_IP=$(kubectl get pod nginx -o go-template='{{.status.podIP}}')" 1071 | Waiting for pod default/busybox to be running, status is Pending, pod ready: false 1072 | Waiting for pod default/busybox to be running, status is Pending, pod ready: false 1073 | Waiting for pod default/busybox to be running, status is Pending, pod ready: false 1074 | Waiting for pod default/busybox to be running, status is Pending, pod ready: false 1075 | Waiting for pod default/busybox to be running, status is Pending, pod ready: false 1076 | Waiting for pod default/busybox to be running, status is Pending, pod ready: false 1077 | Waiting for pod default/busybox to be running, status is Pending, pod ready: false 1078 | If you don't see a command prompt, try pressing enter. 1079 | / # ifconfig 1080 | eth0 Link encap:Ethernet HWaddr 9E:D6:D2:44:B5:9A 1081 | inet addr:10.40.0.3 Bcast:0.0.0.0 Mask:255.240.0.0 1082 | inet6 addr: fe80::9cd6:d2ff:fe44:b59a/64 Scope:Link 1083 | UP BROADCAST RUNNING MULTICAST MTU:1410 Metric:1 1084 | RX packets:10 errors:0 dropped:0 overruns:0 frame:0 1085 | TX packets:4 errors:0 dropped:0 overruns:0 carrier:0 1086 | collisions:0 txqueuelen:0 1087 | RX bytes:816 (816.0 B) TX bytes:300 (300.0 B) 1088 | 1089 | lo Link encap:Local Loopback 1090 | inet addr:127.0.0.1 Mask:255.0.0.0 1091 | inet6 addr: ::1/128 Scope:Host 1092 | UP LOOPBACK RUNNING MTU:65536 Metric:1 1093 | RX packets:0 errors:0 dropped:0 overruns:0 frame:0 1094 | TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 1095 | collisions:0 txqueuelen:0 1096 | RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) 1097 | 1098 | / # echo $POD_IP 1099 | 10.32.0.3 1100 | / # wget -qO- http://$POD_IP 1101 | 1102 | 1103 | 1104 | Welcome to nginx! 1105 | 1112 | 1113 | 1114 |

Welcome to nginx!

1115 |

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

1117 | 1118 |

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

1122 | 1123 |

Thank you for using nginx.

1124 | 1125 | 1126 | / # exit 1127 | Waiting for pod default/busybox to terminate, status is Running 1128 | ``` 1129 | 1130 | 以上操作中,我们通过kubectl的run命令创建了一个Pod busybox,并创建busybox容器的虚拟控制台,通过虚拟控制台查看busybox容器的IP,并连接nginx容器查看其状态。具体的参数我们后面会逐一介绍。 1131 | 1132 | 操作完毕后删除临时创建的Pod busybox。 1133 | 1134 | ```shell 1135 | [root@master0 ~]# kubectl delete pod busybox 1136 | pod "busybox" deleted 1137 | ``` 1138 | 1139 | Pod的状态包括以下几种: 1140 | 1141 | | 状态 | 说明 | 1142 | | --------- | ------------------------------------- | 1143 | | Pending | 已经创建该Pod,但Pod中还有若干容器没有完成运行,可能正在下载容器镜像 | 1144 | | Running | Pod中所有容器已经运行,包括容器启动状态或重启状态 | 1145 | | Succeeded | Pod中所有容器成功退出,且不会继续重启 | 1146 | | Failed | Pod中所有容器已经退出,但容器为失败状态,可能由于无法下载容器镜像 | 1147 | | Unknown | 由于某种原因无法获取Pod状态,可能由于Master和Node网络通信不畅 | 1148 | 1149 | #### 设置Pod Label 1150 | 1151 | 同样的步骤我们可以创建更多的Pod,但如何去区分Pod呢? 我们使用之前的讲过的概念—Label。 1152 | 1153 | 将原有的nginx-pod.yaml文件在本地拷贝为web-pod.yaml,并添加label标签项目。 1154 | 1155 | ```yaml 1156 | apiVersion: v1 1157 | kind: Pod 1158 | metadata: 1159 | name: web 1160 | labels: 1161 | app: nginx-web 1162 | spec: 1163 | containers: 1164 | - name: nginx 1165 | image: nginx:latest 1166 | imagePullPolicy: IfNotPresent 1167 | ports: 1168 | - containerPort: 80 1169 | ``` 1170 | 1171 | 接下来和前面创建Pod nginx一样,我们可以创建Pod web ,和Pod nginx不同,初始化创建的时候,我们就设置了其Label,为“app:nginx-web”。 1172 | 1173 | ```shell 1174 | 创建Pod web 1175 | [root@master0 ~]# kubectl create -f web-pod.yaml 1176 | pod "web" created 1177 | 1178 | 查看Pod 的状态和信息 1179 | [root@master0 ~]# kubectl get pod 1180 | NAME READY STATUS RESTARTS AGE 1181 | nginx 1/1 Running 1 7d 1182 | web 1/1 Running 0 13s 1183 | 1184 | 查看Pod web 的详细信息 1185 | [root@master0 ~]# kubectl describe pod web 1186 | Name: web 1187 | Namespace: default 1188 | Node: nodea0.example.com/172.25.0.11 1189 | Start Time: Fri, 04 Nov 2016 14:03:12 +0800 1190 | Labels: app=nginx-web 1191 | Status: Running 1192 | IP: 10.32.0.4 1193 | Controllers: 1194 | Containers: 1195 | nginx: 1196 | Container ID: docker://06c006624e46532ae8daee0683b533225a4c69f26302a7387d062b0d3627522e 1197 | Image: nginx:latest 1198 | Image ID: docker://sha256:e43d811ce2f4986aa69bc8ba6c92f0789537f604d1601e0b6ec024e1c38062b4 1199 | Port: 80/TCP 1200 | State: Running 1201 | Started: Fri, 04 Nov 2016 14:03:23 +0800 1202 | Ready: True 1203 | Restart Count: 0 1204 | Volume Mounts: 1205 | /var/run/secrets/kubernetes.io/serviceaccount from default-token-d6l71 (ro) 1206 | Environment Variables: 1207 | Conditions: 1208 | Type Status 1209 | Initialized True 1210 | Ready True 1211 | PodScheduled True 1212 | Volumes: 1213 | default-token-d6l71: 1214 | Type: Secret (a volume populated by a Secret) 1215 | SecretName: default-token-d6l71 1216 | QoS Class: BestEffort 1217 | Tolerations: 1218 | Events: 1219 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 1220 | --------- -------- ----- ---- ------------- -------- ------ ------- 1221 | 7m 7m 1 {default-scheduler } Normal Scheduled Successfully assigned web to nodea0.example.com 1222 | 7m 7m 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Pulling pulling image "nginx:latest" 1223 | 7m 7m 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Pulled Successfully pulled image "nginx:latest" 1224 | 7m 7m 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Created Created container with docker id 06c006624e46; Security:[seccomp=unconfined] 1225 | 7m 7m 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Started Started container with docker id 06c006624e46 1226 | 1227 | ``` 1228 | 1229 | 可以发现,相对Pod nginx,在Pod web详细信息中Lables栏目中有“app=nginx-web”,而Pod nginx详细信息中Lables栏目为""。 1230 | 1231 | 我们可以通过kubectl -l 参数通过指定Label 信息指定Pod。 1232 | 1233 | ```shell 1234 | [root@master0 ~]# kubectl get pod 1235 | NAME READY STATUS RESTARTS AGE 1236 | nginx 1/1 Running 1 7d 1237 | web 1/1 Running 0 15m 1238 | 1239 | [root@master0 ~]# kubectl get pod -l app=nginx-web 1240 | NAME READY STATUS RESTARTS AGE 1241 | web 1/1 Running 0 15m 1242 | ``` 1243 | 1244 | 我们要给已经创建的Pod nginx设置Label也很容易,使用kubectl lable 命令就可以了。 1245 | 1246 | ```shell 1247 | [root@master0 ~]# kubectl label pod nginx app=nginx-test 1248 | pod "nginx" labeled 1249 | ``` 1250 | 1251 | 如果要覆盖一个已经设置的Label 需要在kubectl label 命令后加上—overwrite参数。 1252 | 1253 | ```shell 1254 | [root@master0 ~]# kubectl label --overwrite pod nginx app=nginx-foo 1255 | pod "nginx" labeled 1256 | ``` 1257 | 1258 | 如果要删除一个Pod 的Label,需要的仅仅是在label的名字后加上”-“号。 1259 | 1260 | ```shell 1261 | [root@master0 ~]# kubectl label pod nginx app- 1262 | pod "nginx" labeled 1263 | ``` 1264 | 1265 | #### 创建多容器Pod 1266 | 1267 | 在同一个Pod中,我们可以创建多个相互关联的容器,在容器之间可以通过Volume共享数据。 1268 | 1269 | 接下来的例子中,我们将使用到nginx:latest和busybox:latest两个镜像,并建立一个emptyDir类型的Volume,用以共享存储。 1270 | 1271 | 首先我们要创建一个多容器Pod配置文件: 1272 | 1273 | ```yaml 1274 | apiVersion: v1 1275 | kind: Pod 1276 | metadata: 1277 | name: multicontainer-pod 1278 | spec: 1279 | containers: 1280 | - name: nginx 1281 | image: nginx:latest 1282 | imagePullPolicy: IfNotPresent 1283 | ports: 1284 | - containerPort: 80 1285 | volumeMounts: # spec.volumeMounts[] 设置挂接卷 1286 | - name: htmlroot # spec.volumeMounts[].name 挂接卷名称,由spec.volumes[].name定义 1287 | mountPath: /usr/share/nginx/html # 挂接到容器路径 1288 | - name: busybox 1289 | image: busybox:latest 1290 | imagePullPolicy: IfNotPresent 1291 | command: ["sh","-c","while : ; do sleep 10 ; done"] # 容器执行命令 1292 | volumeMounts: 1293 | - name: htmlroot 1294 | mountPath: /mnt 1295 | volumes: # spec.volumes[] 定义卷类型 1296 | - name: htmlroot # spec.volumes[].name 定义卷名称,容器挂接卷时引用 1297 | emptyDir: {} # 卷类型 1298 | ``` 1299 | 1300 | 将多容器Pod配置文件保存为multicontainer_pod.yaml ,在Master上使用kubectl创建: 1301 | 1302 | ```shell 1303 | [root@master0 ~]# kubectl create -f multicontainer_pod.yaml 1304 | pod "multicontainer-pod" created 1305 | ``` 1306 | 1307 | 等待pod被创建成功后,我们可以查看当前多容器Pod的状态: 1308 | 1309 | ```shell 1310 | [root@master0 ~]# kubectl get pod multicontainer-pod 1311 | NAME READY STATUS RESTARTS AGE 1312 | multicontainer-pod 2/2 Running 0 1m 1313 | [root@master0 ~]# kubectl describe pod multicontainer-pod 1314 | Name: multicontainer-pod 1315 | Namespace: default 1316 | Node: nodea0.example.com/172.25.0.11 1317 | Start Time: Tue, 22 Nov 2016 16:26:16 +0800 1318 | Labels: 1319 | Status: Running 1320 | IP: 10.40.0.1 1321 | Controllers: 1322 | Containers: 1323 | nginx: 1324 | Container ID: docker://4ee4004da7e92b15da43af86947a6a4d649207c5e9d95f0a1ffe88e9a0655306 1325 | Image: nginx:latest 1326 | Image ID: docker://sha256:05a60462f8bafb215ddc5c20a364b5fb637670200a74a5bb13a1b23f64515561 1327 | Port: 80/TCP 1328 | State: Running 1329 | Started: Tue, 22 Nov 2016 16:26:27 +0800 1330 | Ready: True 1331 | Restart Count: 0 1332 | Volume Mounts: 1333 | /usr/share/nginx/html from htmlroot (rw) 1334 | /var/run/secrets/kubernetes.io/serviceaccount from default-token-k5azo (ro) 1335 | Environment Variables: 1336 | busybox: 1337 | Container ID: docker://a3831df2aa83f55b90f0e54dbc75026d2d28721803e3e754f3c6f6076db53645 1338 | Image: busybox:latest 1339 | Image ID: docker://sha256:e02e811dd08fd49e7f6032625495118e63f597eb150403d02e3238af1df240ba 1340 | Port: 1341 | Command: 1342 | sh 1343 | -c 1344 | while : ; do sleep 10 ; done 1345 | State: Running 1346 | Started: Tue, 22 Nov 2016 16:26:33 +0800 1347 | Ready: True 1348 | Restart Count: 0 1349 | Volume Mounts: 1350 | /mnt from htmlroot (rw) 1351 | /var/run/secrets/kubernetes.io/serviceaccount from default-token-k5azo (ro) 1352 | Environment Variables: 1353 | Conditions: 1354 | Type Status 1355 | Initialized True 1356 | Ready True 1357 | PodScheduled True 1358 | Volumes: 1359 | htmlroot: 1360 | Type: EmptyDir (a temporary directory that shares a pod's lifetime) 1361 | Medium: 1362 | default-token-k5azo: 1363 | Type: Secret (a volume populated by a Secret) 1364 | SecretName: default-token-k5azo 1365 | QoS Class: BestEffort 1366 | Tolerations: 1367 | Events: 1368 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 1369 | --------- -------- ----- ---- ------------- -------- ------ ------- 1370 | 1m 1m 1 {default-scheduler } Normal Scheduled Successfully assigned multicontainer-pod to nodea0.example.com 1371 | 1m 1m 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Pulled Container image "nginx:latest" already present on machine 1372 | 1m 1m 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Created Created container with docker id 4ee4004da7e9; Security:[seccomp=unconfined] 1373 | 1m 1m 1 {kubelet nodea0.example.com} spec.containers{nginx} Normal Started Started container with docker id 4ee4004da7e9 1374 | 1m 1m 1 {kubelet nodea0.example.com} spec.containers{busybox} Normal Pulled Container image "busybox:latest" already present on machine 1375 | 1m 1m 1 {kubelet nodea0.example.com} spec.containers{busybox} Normal Created Created container with docker id a3831df2aa83; Security:[seccomp=unconfined] 1376 | 1m 1m 1 {kubelet nodea0.example.com} spec.containers{busybox} Normal Started Started container with docker id a3831df2aa83 1377 | ``` 1378 | 1379 | 我们可以通过kubectl exec 命令连接Pod中的容器,查看并测试共享卷。 1380 | 1381 | ```shell 1382 | # 连接multicontainer-pod中的busybox容器 1383 | [root@master0 ~]# kubectl exec multicontainer-pod -c busybox -it /bin/sh 1384 | 1385 | # 使用df 查看磁盘挂接情况,请注意/mnt目录挂接情况 1386 | / # df 1387 | Filesystem 1K-blocks Used Available Use% Mounted on 1388 | /dev/mapper/docker-253:0-669894-c8bc33b61e5399bfb777d2d306ce707322234fa8217c188bacc3389b0cc3dbef 1389 | 10474496 34672 10439824 0% / 1390 | tmpfs 508396 0 508396 0% /dev 1391 | tmpfs 508396 0 508396 0% /sys/fs/cgroup 1392 | /dev/mapper/rhel-root 1393 | 9226240 4034764 5191476 44% /mnt 1394 | /dev/mapper/rhel-root 1395 | 9226240 4034764 5191476 44% /dev/termination-log 1396 | /dev/mapper/rhel-root 1397 | 9226240 4034764 5191476 44% /etc/resolv.conf 1398 | /dev/mapper/rhel-root 1399 | 9226240 4034764 5191476 44% /etc/hostname 1400 | /dev/mapper/rhel-root 1401 | 9226240 4034764 5191476 44% /etc/hosts 1402 | shm 65536 0 65536 0% /dev/shm 1403 | tmpfs 508396 12 508384 0% /var/run/secrets/kubernetes.io/serviceaccount 1404 | tmpfs 508396 0 508396 0% /proc/kcore 1405 | tmpfs 508396 0 508396 0% /proc/timer_list 1406 | tmpfs 508396 0 508396 0% /proc/timer_stats 1407 | tmpfs 508396 0 508396 0% /proc/sched_debug 1408 | 1409 | # 我们进入 /mnt 目录后创建 index.html文件,并写入测试字符串 1410 | / # cd /mnt 1411 | /mnt # ls 1412 | /mnt # echo "

busybox" > index.html 1413 | /mnt # exit 1414 | ``` 1415 | 1416 | 退出busybox容器,再连接nginx容器: 1417 | 1418 | ```shell 1419 | [root@master0 ~]# kubectl exec multicontainer-pod -c nginx -it /bin/sh 1420 | # df 1421 | Filesystem 1K-blocks Used Available Use% Mounted on 1422 | /dev/mapper/docker-253:0-669894-985f5295e7838bcf7b60c163493cd010b1a5665a9ab46955ffdc6f7cadbf8f66 10474496 232540 10241956 3% / 1423 | tmpfs 508396 0 508396 0% /dev 1424 | tmpfs 508396 0 508396 0% /sys/fs/cgroup 1425 | /dev/mapper/rhel-root 9226240 4035728 5190512 44% /etc/hosts 1426 | shm 65536 0 65536 0% /dev/shm 1427 | tmpfs 508396 12 508384 1% /run/secrets/kubernetes.io/serviceaccount 1428 | # cd /usr/share/nginx/html 1429 | # ls 1430 | index.html 1431 | # cat index.html 1432 |

busybox 1433 | # exit 1434 | ``` 1435 | 1436 | 可以看出多容器Pod中的nginx和busybox容器共享了一个存储卷。 1437 | 1438 | 测试成功后,不要忘记做清除操作: 1439 | 1440 | ```shell 1441 | [root@master0 ~]# kubectl delete -f multicontainer_pod.yaml 1442 | pod "multicontainer-pod" deleted 1443 | ``` 1444 | 1445 | #### 创建简单Deployment 1446 | 1447 | 我们可以通过一次配置启动多个Pod ,并且保证当Pod损坏或销毁时可以自动重建。这就需要Depolyment来完成。 1448 | 1449 | 首先创建nginx-deployment.yaml 文件,文件内容如下: 1450 | 1451 | ```yaml 1452 | apiVersion: extensions/v1beta1 1453 | kind: Deployment 1454 | metadata: 1455 | name: nginx-deployment 1456 | spec: 1457 | replicas: 2 1458 | template: 1459 | metadata: 1460 | labels: 1461 | app: nginx 1462 | spec: 1463 | containers: 1464 | - name: nginx 1465 | image: nginx:latest 1466 | imagePullPolicy: IfNotPresent 1467 | ports: 1468 | - containerPort: 80 1469 | ``` 1470 | 1471 | 此文件指定了Deployment 的名字为nginx-deployment,将创建两个replicas,也就是将有两个新的Pod。 1472 | 1473 | 运行kubectl create命令,创建Deloyment 。 1474 | 1475 | ```shell 1476 | [root@master0 ~]# kubectl create -f nginx-deployment.yaml 1477 | deployment "nginx-deployment" created 1478 | ``` 1479 | 1480 | 创建成功后可以看到: 1481 | 1482 | ```shell 1483 | [root@master0 ~]# kubectl get deployment 1484 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 1485 | nginx-deployment 2 2 2 0 13s 1486 | ``` 1487 | 1488 | 同时有两个新的Pod被创建出来: 1489 | 1490 | ```shell 1491 | root@master0 ~]# kubectl get pod 1492 | NAME READY STATUS RESTARTS AGE 1493 | nginx-deployment-2947857529-3dpz6 1/1 Running 0 25s 1494 | nginx-deployment-2947857529-xt8gd 1/1 Running 0 25s 1495 | ``` 1496 | 1497 | 如果你没有清除之前创建的Pod ,可以通过Label来分拣这两个新的Pod: 1498 | 1499 | ```shell 1500 | [root@master0 ~]# kubectl get pod -l app=nginx 1501 | NAME READY STATUS RESTARTS AGE 1502 | nginx-deployment-2947857529-3dpz6 1/1 Running 0 55s 1503 | nginx-deployment-2947857529-xt8gd 1/1 Running 0 55s 1504 | ``` 1505 | 1506 | 当然,我们可以是直接使用kubectl run 命令来创建Deployment。 1507 | 1508 | ```shell 1509 | [root@master0 ~]# kubectl run run-nginx --image=nginx:latest --replicas=2 --labels=app=nginx-run --port=80 1510 | deployment "run-nginx" created 1511 | ``` 1512 | 1513 | 查看Deployment信息: 1514 | 1515 | ```shell 1516 | [root@master0 ~]# kubectl get deployment run-nginx 1517 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 1518 | run-nginx 2 2 2 2 55s 1519 | ``` 1520 | 1521 | 查看创建的Pod信息: 1522 | 1523 | ```shell 1524 | [root@master0 ~]# kubectl get pod 1525 | NAME READY STATUS RESTARTS AGE 1526 | nginx-deployment-2947857529-3dpz6 1/1 Running 0 5m 1527 | nginx-deployment-2947857529-xt8gd 1/1 Running 0 5m 1528 | run-nginx-1357102973-02c4z 1/1 Running 0 1m 1529 | run-nginx-1357102973-c7pkm 1/1 Running 0 1m 1530 | ``` 1531 | 1532 | 也可以根据Label分拣Pod: 1533 | 1534 | ```shell 1535 | [root@master0 ~]# kubectl get pod -l app=nginx-run 1536 | NAME READY STATUS RESTARTS AGE 1537 | run-nginx-1357102973-02c4z 1/1 Running 0 2m 1538 | run-nginx-1357102973-c7pkm 1/1 Running 0 2m 1539 | ``` 1540 | 1541 | 为了下次创建Deployment更加方便,我们可以将现在运行的Deployment的创建信息导出为yaml格式文件: 1542 | 1543 | ```shell 1544 | [root@master0 ~]# kubectl --export -o yaml get deployment run-nginx | tee nginx-run.yaml 1545 | apiVersion: extensions/v1beta1 1546 | kind: Deployment 1547 | metadata: 1548 | annotations: 1549 | deployment.kubernetes.io/revision: "1" 1550 | creationTimestamp: null 1551 | generation: 1 1552 | labels: 1553 | app: nginx-run 1554 | name: run-nginx 1555 | selfLink: /apis/extensions/v1beta1/namespaces//deployments/run-nginx 1556 | spec: 1557 | replicas: 2 1558 | selector: 1559 | matchLabels: 1560 | app: nginx-run 1561 | strategy: 1562 | rollingUpdate: 1563 | maxSurge: 1 1564 | maxUnavailable: 1 1565 | type: RollingUpdate 1566 | template: 1567 | metadata: 1568 | creationTimestamp: null 1569 | labels: 1570 | app: nginx-run 1571 | spec: 1572 | containers: 1573 | - image: nginx:latest 1574 | imagePullPolicy: IfNotPresent 1575 | name: run-nginx 1576 | ports: 1577 | - containerPort: 80 1578 | protocol: TCP 1579 | resources: {} 1580 | terminationMessagePath: /dev/termination-log 1581 | dnsPolicy: ClusterFirst 1582 | restartPolicy: Always 1583 | securityContext: {} 1584 | terminationGracePeriodSeconds: 30 1585 | status: {} 1586 | ``` 1587 | 1588 | 之后我们需要再次创建或在其他的集群环境中创建此Deployment,就可以使用kubectl create命令来完成了。 1589 | 1590 | 我们可以完成以下步骤测试删除Deployment run-nginx然后通过导出的yaml文件重新创建run-nginx。 1591 | 1592 | ```shell 1593 | [root@master0 ~]# kubectl delete deployment run-nginx 1594 | deployment "run-nginx" deleted 1595 | 1596 | [root@master0 ~]# kubectl create -f nginx-run.yaml 1597 | deployment "run-nginx" created 1598 | 1599 | [root@master0 ~]# kubectl get deployment run-ngix 1600 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 1601 | run-nginx 2 2 2 2 24s 1602 | ``` 1603 | 1604 | #### 创建简单Service 1605 | 1606 | 在我们已经创建Pod和Deployment的基础上,我们可以创建Service以使外部用户可以访问到Kubernetes内的应用服务。 1607 | 1608 | 首先创建nginx-server.yaml 文件,文件内容如下: 1609 | 1610 | ```yaml 1611 | apiVersion: v1 1612 | kind: Service 1613 | metadata: 1614 | name: nginx-service 1615 | spec: 1616 | ports: 1617 | - port: 8000 1618 | targetPort: 80 1619 | protocol: TCP 1620 | selector: 1621 | app: nginx 1622 | type: LoadBalancer 1623 | ``` 1624 | 1625 | 指定Service名为nginx-service,设置集群内部端口为8000,对应到Pod中的80端口,协议类型为TCP,选择器指定Lable为app=nginx的Deployment,使用负载均衡的方式访问Pod。 1626 | 1627 | 运行kubectl create命令,创建Service: 1628 | 1629 | ```shell 1630 | [root@master0 ~]# kubectl create -f nginx-service.yaml 1631 | ``` 1632 | 1633 | 查看当前的Service状态: 1634 | 1635 | ```shell 1636 | [root@master0 ~]# kubectl get service 1637 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 1638 | kubernetes 100.64.0.1 443/TCP 12d 1639 | nginx-service 100.68.22.181 8000/TCP 1m 1640 | ``` 1641 | 1642 | 查看nginx-service服务的具体情况: 1643 | 1644 | ```shell 1645 | [root@master0 ~]# kubectl describe service nginx-service 1646 | Name: nginx-service 1647 | Namespace: default 1648 | Labels: 1649 | Selector: app=nginx 1650 | Type: LoadBalancer 1651 | IP: 100.68.22.181 1652 | Port: 8000/TCP 1653 | NodePort: 32690/TCP 1654 | Endpoints: 10.32.0.13:80,10.40.0.9:80 1655 | Session Affinity: None 1656 | ``` 1657 | 1658 | NodePort就是我们可以通过外部访问到的服务端口,可以通过curl或firefox访问确认。Session Affinity是指定是否保持Session,后面的课程中我们会深入的介绍。 1659 | 1660 | ```shell 1661 | [root@foundation0 ~]# curl http://master0.example.com:32690 1662 | 1663 | 1664 | 1665 | Welcome to nginx! 1666 | 1673 | 1674 | 1675 |

Welcome to nginx!

1676 |

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

1678 | 1679 |

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

1683 | 1684 |

Thank you for using nginx.

1685 | 1686 | 1687 | ``` 1688 | 1689 | 目前nginx-service是由nginx-deployment提供资源,而nginx-deployment是由两个Pod组成的。 1690 | 1691 | ```shell 1692 | [root@master0 ~]# kubectl get deployment -l app=nginx 1693 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 1694 | nginx-deployment 2 2 2 2 4d 1695 | [root@master0 ~]# kubectl get pods -l app=nginx 1696 | NAME READY STATUS RESTARTS AGE 1697 | nginx-deployment-2947857529-3dpz6 1/1 Running 2 4d 1698 | nginx-deployment-2947857529-xt8gd 1/1 Running 2 4d 1699 | ``` 1700 | 1701 | 我们可以通过修改两个Pod上nginx的主页来查看Service轮训负载均衡的状态: 1702 | 1703 | ```shell 1704 | [root@master0 ~]# kubectl exec -it nginx-deployment-2947857529-3dpz6 /bin/bash 1705 | root@nginx-deployment-2947857529-3dpz6:/# echo "

Pod A

" > /usr/share/nginx/html/index.html 1706 | root@nginx-deployment-2947857529-3dpz6:/# exit 1707 | exit 1708 | [root@master0 ~]# kubectl exec -it nginx-deployment-2947857529-xt8gd /bin/bash 1709 | root@nginx-deployment-2947857529-xt8gd:/# echo "

Pod B

" > /usr/share/nginx/html/index.html 1710 | root@nginx-deployment-2947857529-xt8gd:/# exit 1711 | exit 1712 | ``` 1713 | 1714 | 在外部机器上使用curl或firefox你可以查看到访问是轮训的: 1715 | 1716 | ```shell 1717 | [root@foundation0 ~]# curl http://master0.example.com:32690 1718 |

Pod B

1719 | [root@foundation0 ~]# curl http://master0.example.com:32690 1720 |

Pod A

1721 | [root@foundation0 ~]# curl http://master0.example.com:32690 1722 |

Pod A

1723 | [root@foundation0 ~]# curl http://master0.example.com:32690 1724 |

Pod A

1725 | [root@foundation0 ~]# curl http://master0.example.com:32690 1726 |

Pod B

1727 | ``` 1728 | 1729 | 为方便后续内容的介绍和实验,我们可以将创建的Service、Deployment和Pod尽数删除。 1730 | 1731 | ```shell 1732 | [root@master0 ~]# kubectl delete service nginx-service 1733 | service "nginx-service" deleted 1734 | 1735 | [root@master0 ~]# kubectl delete deployment nginx-deployment 1736 | deployment "nginx-deployment" deleted 1737 | 1738 | [root@master0 ~]# kubectl delete deployment run-nginx 1739 | deployment "run-nginx" deleted 1740 | 1741 | [root@master0 ~]# kubectl delete pod nginx 1742 | pod "nginx" deleted 1743 | 1744 | [root@master0 ~]# kubectl delete pod web 1745 | pod "web" deleted 1746 | ``` 1747 | 1748 | #### 同时创建Deployment和Service 1749 | 1750 | 分别创建Pod、Deployment和Service既麻烦又不利于关联性,所以在生产环境中,我们会将所以得对象创建整合在一个配置文件中定义,并通过统一的配置文件来创建Service。 1751 | 1752 | 在上一节中,我们按部就班的创建了nginx-service服务,并且在测试成功后删除了所有自定对象。目前kubernetes环境下没有任何自定义对象了: 1753 | 1754 | ```shell 1755 | [root@master0 ~]# kubectl get pod 1756 | [root@master0 ~]# kubectl get deployment 1757 | [root@master0 ~]# kubectl get service 1758 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 1759 | kubernetes 100.64.0.1 443/TCP 12d 1760 | ``` 1761 | 1762 | 我们可以将前面创建Deployment和Service的配置文件修改并合并起来成为新的配置文件my-nginx.yaml: 1763 | 1764 | ```yaml 1765 | apiVersion: extensions/v1beta1 1766 | kind: Deployment 1767 | metadata: 1768 | name: nginx-deployment 1769 | spec: 1770 | replicas: 2 1771 | template: 1772 | metadata: 1773 | labels: 1774 | app: nginx 1775 | spec: 1776 | containers: 1777 | - name: nginx 1778 | image: nginx:latest 1779 | imagePullPolicy: IfNotPresent 1780 | ports: 1781 | - containerPort: 80 1782 | --- 1783 | apiVersion: v1 1784 | kind: Service 1785 | metadata: 1786 | name: nginx-service 1787 | spec: 1788 | ports: 1789 | - port: 8000 1790 | targetPort: 80 1791 | protocol: TCP 1792 | selector: 1793 | app: nginx 1794 | type: LoadBalancer 1795 | ``` 1796 | 1797 | 注意:在两个不同部分之间需要使用三个“-”号组成的间隔符号用以区分不同的设置区域。 1798 | 1799 | 使用my-nginx.yaml配置文件创建运行环境: 1800 | 1801 | ```shell 1802 | [root@master0 ~]# kubectl create -f my-nginx.yaml 1803 | deployment "nginx-deployment" created 1804 | service "nginx-service" created 1805 | ``` 1806 | 1807 | 可以看出kubectl还是根据配置文件分别创建Deployment和Service的。 1808 | 1809 | 我们仍然可以通过kubectl查看当前的Pod、Deployment和Service状态,并获得访问Service的外部端口。其操作和之前单独建立Service时没有区别。 1810 | 1811 | ### 创建复杂的多Service微服务环境 1812 | 1813 | #### 部署镜像 1814 | 1815 | 复杂的微服务环境需要多个镜像来完成环境搭建,他们分为前端web应用和后端数据库应用。我们在此例子中我们将完成一个留言板应用微服务,它是由前端php应用和后端redis数据库组成,并且为了保证业务的正常运行,我们可以设置前端和后端运行多副本的方式。 1816 | 1817 | 前端服务镜像为kissingwolf/guestbook-frontend,后端服务镜像为kissingwolf/redis-mater和kissingwolf/redis-slave。这个微服务是建立在前端php应用链接redis数据库的基础上的。 1818 | 1819 | 1. 下载镜像文件压缩包 1820 | 1821 | ```shell 1822 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "wget http://classroom.example.com/materials/k8s-imgs/guestbook-img.tbz " ; done 1823 | ``` 1824 | 1825 | 2. 在节点上解开压缩包 1826 | 1827 | ```shell 1828 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf guestbook-img.tbz " ; done 1829 | ``` 1830 | 1831 | 3. 将镜像导入节点系统 1832 | 1833 | ```shell 1834 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./guestbook/*.img ; do docker load -i $i ; done' ; done 1835 | ``` 1836 | 1837 | 如果在公网上实验,可以跳过部署镜像的步骤,所有镜像均可以在hub.docker.com上找到并通过kubernetes自动下载。 1838 | 1839 | #### 编写微服务部署配置文件 1840 | 1841 | 我们将要建立的是一个线上留言板微服务,此微服务包括三个kubernetes Service:redis-master、redis-slave和frontend。 1842 | 1843 | ```yaml 1844 | apiVersion: v1 1845 | kind: Service 1846 | metadata: 1847 | name: redis-master 1848 | labels: 1849 | app: redis 1850 | tier: backend 1851 | role: master 1852 | spec: 1853 | ports: 1854 | - port: 6379 1855 | targetPort: 6379 1856 | selector: 1857 | app: redis 1858 | tier: backend 1859 | role: master 1860 | --- 1861 | apiVersion: extensions/v1beta1 1862 | kind: Deployment 1863 | metadata: 1864 | name: redis-master 1865 | spec: 1866 | template: 1867 | metadata: 1868 | labels: 1869 | app: redis 1870 | role: master 1871 | tier: backend 1872 | spec: 1873 | containers: 1874 | - name: master 1875 | image: kissingwolf/redis-master:latest 1876 | imagePullPolicy: IfNotPresent 1877 | ports: 1878 | - containerPort: 6379 1879 | --- 1880 | apiVersion: v1 1881 | kind: Service 1882 | metadata: 1883 | name: redis-slave 1884 | labels: 1885 | app: redis 1886 | tier: backend 1887 | role: slave 1888 | spec: 1889 | ports: 1890 | - port: 6379 1891 | selector: 1892 | app: redis 1893 | tier: backend 1894 | role: slave 1895 | --- 1896 | apiVersion: extensions/v1beta1 1897 | kind: Deployment 1898 | metadata: 1899 | name: redis-slave 1900 | spec: 1901 | replicas: 2 1902 | template: 1903 | metadata: 1904 | labels: 1905 | app: redis 1906 | role: slave 1907 | tier: backend 1908 | spec: 1909 | containers: 1910 | - name: slave 1911 | image: kissingwolf/redis-slave:latest 1912 | imagePullPolicy: IfNotPresent 1913 | env: 1914 | - name: GET_HOSTS_FROM 1915 | value: dns 1916 | ports: 1917 | - containerPort: 6379 1918 | --- 1919 | apiVersion: v1 1920 | kind: Service 1921 | metadata: 1922 | name: frontend 1923 | labels: 1924 | app: guestbook 1925 | tier: frontend 1926 | spec: 1927 | type: LoadBalancer 1928 | ports: 1929 | - port: 80 1930 | selector: 1931 | app: guestbook 1932 | tier: frontend 1933 | --- 1934 | apiVersion: extensions/v1beta1 1935 | kind: Deployment 1936 | metadata: 1937 | name: frontend 1938 | spec: 1939 | replicas: 3 1940 | template: 1941 | metadata: 1942 | labels: 1943 | app: guestbook 1944 | tier: frontend 1945 | spec: 1946 | containers: 1947 | - name: php-redis 1948 | image: kissingwolf/guestbook-frontend:latest 1949 | imagePullPolicy: IfNotPresent 1950 | env: 1951 | - name: GET_HOSTS_FROM 1952 | value: dns 1953 | ports: 1954 | - containerPort: 80 1955 | ``` 1956 | 1957 | 将以上内容保存为guestbook.yaml文件,放置到master0主机上,请使用自己的foundation号替换0。 1958 | 1959 | 此文件由三个连续“-”符号分为了六个部分:三个Service块和三个Deployment块,我们分别介绍其作用。 1960 | 1961 | redis-mater Service块: 1962 | 1963 | ```yaml 1964 | apiVersion: v1 # api版本 1965 | kind: Service # 配置类型 1966 | metadata: # 元信息配置 1967 | name: redis-master # metadata.name Service名称,需符合命名规范 1968 | labels: # metadata.labels[] 自定义标签属性列表 1969 | app: redis # 自定义标签属性 1970 | tier: backend # 自定义标签属性 1971 | role: master # 自定义标签属性 1972 | spec: # 详细描述,用以配置Service 1973 | ports: # spec.ports[] Service 需要暴露的端口号列表 1974 | - port: 6379 # spec.ports[].port Service 监听的端口号,默认为TCP 1975 | targetPort: 6379 # spec.ports[].targetPort 需要转发到后端的端口号,默认为TCP,如果与port一致可以省略不写。 1976 | selector: # spec.selector[] 标签选择器,将符合标签的Deployment关联Service 1977 | app: redis # 查找的目标标签属性 1978 | tier: backend # 查找的目标标签属性 1979 | role: master # 查找的目标标签属性 1980 | ``` 1981 | 1982 | redis-master Deployment块: 1983 | 1984 | ```yaml 1985 | apiVersion: extensions/v1beta1 # api版本 1986 | kind: Deployment # 配置类型 1987 | metadata: # 元信息配置 1988 | name: redis-master # metadata.name Deployment名称,需符合命名规范 1989 | spec: # 详细描述,用以配置Deployment 1990 | template: # spec.template 容器的定义,此部分和Pod定义的内容基本一致 1991 | metadata: # spec.template.metadata 元数据配置 1992 | labels: # spec.template.metadata.labels[] 自定义标签属性列表 1993 | app: redis # 自定义标签属性 1994 | role: master # 自定义标签属性 1995 | tier: backend # 自定义标签属性 1996 | spec: # spec.template.spce 容器的详细描述 1997 | containers: # spec.template.spce.containers[] Pod中运行容器的详细列表 1998 | - name: master # spec.template.spce.containers[].name 容器名称 1999 | image: kissingwolf/redis-master:latest # .image 容器镜像名称 2000 | imagePullPolicy: IfNotPresent # .imagePullPolicy 获得镜像的策略 2001 | ports: # spec.template.spce.containers[].ports[] 容器需要暴露的端口号列表 2002 | - containerPort: 6379 # .containerPort 容器需要监听的端口号,默认为TCP 2003 | ``` 2004 | 2005 | redis-slave Service块,与redis-master Service基本一致: 2006 | 2007 | ```yaml 2008 | apiVersion: v1 2009 | kind: Service 2010 | metadata: 2011 | name: redis-slave 2012 | labels: 2013 | app: redis 2014 | tier: backend 2015 | role: slave 2016 | spec: 2017 | ports: 2018 | - port: 6379 2019 | selector: 2020 | app: redis 2021 | tier: backend 2022 | role: slave 2023 | ``` 2024 | 2025 | redis-slave Deployment块,与redis-master Depolyment 定义相仿的地方我们就不再啰嗦了: 2026 | 2027 | ```yaml 2028 | apiVersion: extensions/v1beta1 2029 | kind: Deployment 2030 | metadata: 2031 | name: redis-slave 2032 | spec: 2033 | replicas: 2 # spec.replicas 设置Pod的副本数,如果设置为0,则不创建Pod 2034 | template: 2035 | metadata: 2036 | labels: 2037 | app: redis 2038 | role: slave 2039 | tier: backend 2040 | spec: 2041 | containers: 2042 | - name: slave 2043 | image: kissingwolf/redis-slave:latest 2044 | imagePullPolicy: IfNotPresent 2045 | env: # spec.template.spec.containers[].env[] 容器运行前需要设置的环境变量列表 2046 | - name: GET_HOSTS_FROM # 容器内环境变量名 2047 | value: dns # 设置变量对应的值 2048 | ports: 2049 | - containerPort: 6379 2050 | ``` 2051 | 2052 | 容器化管理的理念是容器内运行的程序和结构与容器环境管理者无关!我们不用在乎容器内的配置和运行程序安装、运行和处理的问题,容器对应容器环境的管理者应该是个黑盒子,容器管理者应该像执行一条命令一样运行一个容器。在这个例子中,我们不用关心redis-master和redis-slave是如何通讯的,就好像我们不需要关心两个程序是如何由管道符号“|”连接起输入输出的。 2053 | 2054 | frontend Service 块: 2055 | 2056 | ```yaml 2057 | apiVersion: v1 2058 | kind: Service 2059 | metadata: 2060 | name: frontend 2061 | labels: 2062 | app: guestbook 2063 | tier: frontend 2064 | spec: 2065 | type: LoadBalancer # spec.type 指定Service的访问方式,LoadBalancer指定kube-proxy完成负载分发,默认会在每个node上指定一个相同的30000以上端口监听。 2066 | ports: 2067 | - port: 80 2068 | selector: 2069 | app: guestbook 2070 | tier: frontend 2071 | ``` 2072 | 2073 | frontend Deployment 块: 2074 | 2075 | ```yaml 2076 | apiVersion: extensions/v1beta1 2077 | kind: Deployment 2078 | metadata: 2079 | name: frontend 2080 | spec: 2081 | replicas: 3 2082 | template: 2083 | metadata: 2084 | labels: 2085 | app: guestbook 2086 | tier: frontend 2087 | spec: 2088 | containers: 2089 | - name: php-redis 2090 | image: kissingwolf/guestbook-frontend:latest 2091 | imagePullPolicy: IfNotPresent 2092 | env: 2093 | - name: GET_HOSTS_FROM 2094 | value: dns 2095 | ports: 2096 | - containerPort: 80 2097 | ``` 2098 | 2099 | 综上,我们在配置中指定将创建三个服务,redis-master服务创建1个pod,redis-slave服务创建2个pod,而前端web服务frontend创建3个pod。可以充分做到冗余和负载均衡。 2100 | 2101 | #### 启动微服务 2102 | 2103 | 编写好guestbook.yaml配置文件后,可以使用kubectl create命令创建这个由三个自服务组成的微服务。 2104 | 2105 | ```shell 2106 | [root@master0 ~]# kubectl create -f guestbook.yaml 2107 | service "redis-master" created 2108 | deployment "redis-master" created 2109 | service "redis-slave" created 2110 | deployment "redis-slave" created 2111 | service "frontend" created 2112 | deployment "frontend" created 2113 | ``` 2114 | 2115 | 我们可以同过kubectl get 命令查看Pod、Deployment和Service的状态: 2116 | 2117 | ```shell 2118 | [root@master0 ~]# kubectl get pods 2119 | NAME READY STATUS RESTARTS AGE 2120 | frontend-2027482420-7viu0 1/1 Running 0 2m 2121 | frontend-2027482420-ai4zb 1/1 Running 0 2m 2122 | frontend-2027482420-g8mfr 1/1 Running 0 2m 2123 | redis-master-2712522894-8t10c 1/1 Running 0 2m 2124 | redis-slave-2928339718-kkuio 1/1 Running 0 2m 2125 | redis-slave-2928339718-ysakf 1/1 Running 0 2m 2126 | ``` 2127 | 2128 | ```shell 2129 | [root@master0 ~]# kubectl get deployment 2130 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 2131 | frontend 3 3 3 3 3m 2132 | redis-master 1 1 1 1 3m 2133 | redis-slave 2 2 2 2 3m 2134 | ``` 2135 | 2136 | ```shell 2137 | [root@master0 ~]# kubectl get service 2138 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 2139 | frontend 100.65.209.142 80/TCP 4m 2140 | kubernetes 100.64.0.1 443/TCP 13d 2141 | redis-master 100.74.117.129 6379/TCP 4m 2142 | redis-slave 100.71.128.170 6379/TCP 4m 2143 | ``` 2144 | 2145 | #### 访问微服务 2146 | 2147 | 当前我们Guestbook微服务中,负责处理用户请求的前端服务frontend注册到kubernetes后,kube-proxy将代理其监听请求,并开启一个30000以上端口的监听。 2148 | 2149 | ```shell 2150 | [root@master0 ~]# kubectl describe service frontend 2151 | Name: frontend 2152 | Namespace: default 2153 | Labels: app=guestbook 2154 | tier=frontend 2155 | Selector: app=guestbook,tier=frontend 2156 | Type: LoadBalancer 2157 | IP: 100.65.209.142 2158 | Port: 80/TCP 2159 | NodePort: 31876/TCP 2160 | Endpoints: 10.32.0.18:80,10.32.0.19:80,10.40.0.15:80 2161 | Session Affinity: None 2162 | ``` 2163 | 2164 | NodePort 指定的就是kube-proxy监听的端口,frontend服务目前的端口是31876/TCP,你机器上会有不同。在master和每个node上都有。 2165 | 2166 | ```shell 2167 | [root@foundation0 ~]# for i in master0 nodea0 nodeb0 ; do ssh root@$i "hostname ; netstat -natp |grep ':31876' " ; done 2168 | master0.example.com 2169 | tcp6 0 0 :::31876 :::* LISTEN 3679/kube-proxy 2170 | nodea0.example.com 2171 | tcp6 0 0 :::31876 :::* LISTEN 2886/kube-proxy 2172 | nodeb0.example.com 2173 | tcp6 0 0 :::31876 :::* LISTEN 3060/kube-proxy 2174 | ``` 2175 | 2176 | 我们可以通过浏览器访问master或任意节点,其访问均通过kube-proxy均衡的分发到各个pod上。并且在Node或Pod损坏的情况下自动迁移和恢复服务。 2177 | 2178 | ### 创建带共享存储卷的Service环境 2179 | 2180 | #### 创建NFS共享存储卷 2181 | 2182 | 我们可以使用很多种方法和软件导出并共享NFS卷,在我们当前的环境中,比较方便的做法是使用sharestorage虚拟机,利用已经安装好的openfiler系统导出NFS卷。 2183 | 2184 | Openfiler是一种开源的共享存储系统,可以支持NFS、CIFS和iSCSI等共享方式。在我们的现有环境中它充当共享存储设备。 2185 | 2186 | 首先我们启动本地的sharestorage虚拟机: 2187 | 2188 | ```shell 2189 | [kiosk@foundation0 ~]$ rht-vmctl start sharestorage 2190 | ``` 2191 | 2192 | 启动正常以后我们就可以访问共享存储设备了,虚拟机为sharestorage,主机名为sharestorageN,域名为sharestorageN.example.com。 N为你的Foundation 号,我演示机的Foundation 号为0。 2193 | 2194 | 打开浏览器访问sharestorage的配置界面,登录地址为http://sharestorageN.example.com:446 , 用户名为openfiler,密码为uplooking。 2195 | 2196 | ![001-openfiler-signin](pic/001-openfiler-signin.png) 2197 | 2198 | 登录后看到如下界面 2199 | 2200 | ![002-openfiler-index](pic/002-openfiler-index.png) 2201 | 2202 | 点击”Services",然后点击“NFSv3 server"后的”Enable“,打开NFS服务![003-openfiler-nfs-service-enable](pic/003-openfiler-nfs-service-enable.png) 2203 | 2204 | 打开NFS服务后,点击”Volumes“创建一个新的卷组,“create new physical volumes"![004-openfiler-create-pv](pic/004-openfiler-create-pv.png) 2205 | 2206 | 选择”/dev/hdb"![005-openfiler-create-pv](pic/005-openfiler-create-pv.png) 2207 | 2208 | 点击“create”,创建物理卷![006-openfiler-create-pv](pic/006-openfiler-create-pv.png) 2209 | 2210 | 创建好物理卷后,点击“shares”,再点击“create a new filesystem volume",创建文件系统卷![007-openfiler-create-share](pic/007-openfiler-create-share.png) 2211 | 2212 | 在”Volume group name“中填入”share“,并且勾选”/dev/hdb1"![008-openfiler-create-share](pic/008-openfiler-create-share.png) 2213 | 2214 | ![009-openfiler-create-share](pic/009-openfiler-create-share.png) 2215 | 2216 | 点击“Add volume group”,后在“Volumes”中可以看到![010-openfiler-create-lv](pic/010-openfiler-create-lv.png) 2217 | 2218 | 在“Shares”中可以看到我们建好的“Network Shares”![011-openfiler-create-share-dir](pic/011-openfiler-create-share-dir.png) 2219 | 2220 | 点击“Volumes”,可以在“Volumes Section”中选择“Add Volume",创建卷”nfs“,设置”Required Space (MB)“为5000,点击”Create“创建 2221 | 2222 | ![012-openfiler-create-share-dir](pic/012-openfiler-create-share-dir.png) 2223 | 2224 | 可以看到,”nfs“卷已经建立好![013-openfiler-create-share-vg](pic/013-openfiler-create-share-vg.png) 2225 | 2226 | 由于安全方面的考虑,我们需要设置能够访问此共享设备的网络,请点击“System”,拉到页面下方,在“Network Access Configuration”,填入name:"mynet",Netework:"172.25.0.0",Netmask:"255.255.0.0"。点击”Update“![014-openfiler-create-network-access](pic/014-openfiler-create-network-access.png) 2227 | 2228 | 然后点击”Shares“,点选Network Shares 中的”nfs“,创建导出 ![015-openfiler-create-share-dir-default](pic/015-openfiler-create-share-dir-default.png) 2229 | 2230 | 在”Folder name“中填入”k8s“,点击”Create Sub-folder“![016-openfiler-create-share-dir-vg](pic/016-openfiler-create-share-dir-vg.png) 2231 | 2232 | 创建好后,点击”k8s“,点击”Make Share“![017-openfiler-create-share-dir](pic/017-openfiler-create-share-dir.png) 2233 | 2234 | 进入共享设置中,在“Share Access Control Mode”中选“Plublic guest access”,点“Update”![018-openfiler-create-share](pic/018-openfiler-create-share.png) 2235 | 2236 | 然后下拉页面,看到“Host access configuration",在NFS项目中选”RW“,然后“Update” ![019-openfiler-create-share-nfs](pic/019-openfiler-create-share-nfs.png) 2237 | 2238 | 至此,共享存储设备端就配置了NFS导出设备及文件系统。 2239 | 2240 | NFS导出的路径为:sharestorageN.example.com:/mnt/share/nfs/k8s ,你可以在其他系统中测试挂接。N为你的机器号。 2241 | 2242 | Master和Node设备上要挂接此共享设备需要安装nfs客户端相关软件包,并且启动相应服务: 2243 | 2244 | ```shell 2245 | [root@master0 ~]# yum install nfs-utils -y 2246 | [root@master0 ~]# systemctl start rpcbind 2247 | [root@nodea0 ~]# yum install nfs-utils -y 2248 | [root@nodea0 ~]# systemctl start rpcbind 2249 | [root@nodeb0 ~]# yum install nfs-utils -y 2250 | [root@nodeb0 ~]# systemctl start rpcbind 2251 | ``` 2252 | 2253 | 将sharestorageN.example.com:/mnt/share/nfs/k8s挂接到Master主机的/mnt目录下,方便后期测试使用,其他Node无需挂接 2254 | 2255 | ```shell 2256 | [root@master0 ~]# mount sharestorage0.example.com:/mnt/share/nfs/k8s /mnt 2257 | ``` 2258 | 2259 | #### 编写挂接共享存储服务配置文件 2260 | 2261 | 我们创建一个配置文件nfs-nginx-service-pv.yaml,其中包括创建PV、PVC、Deployment和Service的配置项目 2262 | 2263 | ```yaml 2264 | apiVersion: v1 2265 | kind: PersistentVolume 2266 | metadata: 2267 | name: nfspv001 2268 | spec: 2269 | capacity: 2270 | storage: 5Gi 2271 | accessModes: 2272 | - ReadWriteMany 2273 | nfs: 2274 | path: /mnt/share/nfs/k8s 2275 | server: 172.25.0.8 2276 | 2277 | --- 2278 | 2279 | apiVersion: v1 2280 | kind: PersistentVolumeClaim 2281 | metadata: 2282 | name: myclaim001 2283 | spec: 2284 | accessModes: 2285 | - ReadWriteMany 2286 | resources: 2287 | requests: 2288 | storage: 3Gi 2289 | 2290 | --- 2291 | 2292 | apiVersion: extensions/v1beta1 2293 | kind: Deployment 2294 | metadata: 2295 | name: nginx-deployment-pv 2296 | spec: 2297 | replicas: 2 2298 | template: 2299 | metadata: 2300 | labels: 2301 | app: nginx-pv 2302 | spec: 2303 | containers: 2304 | - name: nginx-pv 2305 | image: nginx:latest 2306 | imagePullPolicy: IfNotPresent 2307 | ports: 2308 | - containerPort: 80 2309 | volumeMounts: 2310 | - mountPath: "/usr/share/nginx/html" 2311 | name: mypvc 2312 | volumes: 2313 | - name: mypvc 2314 | persistentVolumeClaim: 2315 | claimName: myclaim001 2316 | 2317 | --- 2318 | 2319 | apiVersion: v1 2320 | kind: Service 2321 | metadata: 2322 | name: nginx-service-pv 2323 | spec: 2324 | ports: 2325 | - port: 8000 2326 | targetPort: 80 2327 | protocol: TCP 2328 | selector: 2329 | app: nginx-pv 2330 | type: LoadBalancer 2331 | ``` 2332 | 2333 | 此文件由三个连续“-”符号分为了四个部分:Persistent Volume、Persistent Volume Claim、Deployment 和Service。 2334 | 2335 | Persistent Volume 部分用以定义网络存储卷: 2336 | 2337 | ```yaml 2338 | apiVersion: v1 # Api 版本 2339 | kind: PersistentVolume # 配置类型 2340 | metadata: # 元数据 2341 | name: nfspv001 # metadata.name 网络存储卷名称,需符合命名规范 2342 | spec: # 详细信息 2343 | capacity: # 容量定义 2344 | storage: 5Gi # 存储容量 2345 | accessModes: # 访问方式 2346 | - ReadWriteMany # ReadWriteMany 可读写,可以被多个Node挂接 2347 | nfs: # 卷类型为nfs 2348 | path: /mnt/share/storage1/k8s # 挂接目录 2349 | server: 172.25.0.8 # nfs 服务器IP 2350 | ``` 2351 | 2352 | Persistent Volume Claim 部分用以定义网络存储卷区域,分配给Pod Volume的对象 2353 | 2354 | ```yaml 2355 | apiVersion: v1 # Api 版本 2356 | kind: PersistentVolumeClaim # 配置类型 2357 | metadata: # 元数据 2358 | name: myclaim001 # metadata.name 网络存储卷对象名称,需符合命名规范 2359 | spec: # 详细信息 2360 | accessModes: # 访问模式 2361 | - ReadWriteMany # ReadWriteMany 可读写,可以被多个Node挂接 2362 | resources: # 资源定义 2363 | requests: # 具体配置 2364 | storage: 3Gi # 存储容量 2365 | ``` 2366 | 2367 | Deployment 部分用以定义Pod副本具体对象信息,包括挂接Volume对象信息 2368 | 2369 | ```yaml 2370 | apiVersion: extensions/v1beta1 # Api 版本 2371 | kind: Deployment # 配置类型 2372 | metadata: # 元数据 2373 | name: nginx-deployment-pv # metadata.name Deployment 对象名称,需符合命名规范 2374 | spec: # 详细信息 2375 | replicas: 2 # 副本数 2376 | template: # Pod 对象模板信息 2377 | metadata: # 元数据 2378 | labels: # 标签 2379 | app: nginx-pv # 自定义标签信息 2380 | spec: # spec.template.spce 容器的详细描述 2381 | containers: # 容器对象信息 2382 | - name: nginx # 容器名 2383 | image: nginx:latest # 镜像信息 2384 | imagePullPolicy: IfNotPresent # 镜像下载方式 2385 | ports: # 端口信息 2386 | - containerPort: 80 # 容器暴露端口 2387 | volumeMounts: # 容器挂接卷信息 2388 | - mountPath: "/usr/share/nginx/html" # 容器挂接卷到本地路径 2389 | name: mypvc # 卷名称 2390 | volumes: # 卷定义 2391 | - name: mypvc # 卷名定义 2392 | persistentVolumeClaim: # 定义使用PVC设置卷 2393 | claimName: myclaim001 # PVC卷名 2394 | 2395 | ``` 2396 | 2397 | Service 部分用以定义服务具体对象信息 2398 | 2399 | ```yaml 2400 | apiVersion: v1 2401 | kind: Service 2402 | metadata: 2403 | name: nginx-service 2404 | spec: 2405 | ports: 2406 | - port: 8000 2407 | targetPort: 80 2408 | protocol: TCP 2409 | selector: 2410 | app: nginx-pv 2411 | type: LoadBalancer 2412 | ``` 2413 | 2414 | #### 启动带共享存储卷的Service环境 2415 | 2416 | 编写好nfs-nginx-service-pv.yaml配置文件后,可以通过kubectl create 命令创建Service及其相关对象。 2417 | 2418 | ```shell 2419 | [root@master0 ~]# kubectl create -f nfs-nginx-service-pv.yaml 2420 | persistentvolume "nfspv001" created 2421 | persistentvolumeclaim "myclaim001" created 2422 | deployment "nginx-deployment" created 2423 | service "nginx-service-pv" created 2424 | ``` 2425 | 2426 | 我们可以通过kubectl相关命令查看创建好的对象: 2427 | 2428 | ```shell 2429 | # 查看PV 对象 2430 | [root@master0 ~]# kubectl get pv 2431 | NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE 2432 | nfspv001 5Gi RWX Retain Bound default/myclaim001 1m 2433 | [root@master0 ~]# kubectl describe pv 2434 | Name: nfspv001 2435 | Labels: 2436 | Status: Bound 2437 | Claim: default/myclaim001 2438 | Reclaim Policy: Retain 2439 | Access Modes: RWX 2440 | Capacity: 5Gi 2441 | Message: 2442 | Source: 2443 | Type: NFS (an NFS mount that lasts the lifetime of a pod) 2444 | Server: 172.25.0.8 2445 | Path: /mnt/share/nfs/k8s 2446 | ReadOnly: false 2447 | 2448 | # 查看PVC 对象 2449 | [root@master0 ~]# kubectl get pvc 2450 | NAME STATUS VOLUME CAPACITY ACCESSMODES AGE 2451 | myclaim001 Bound nfspv001 5Gi RWX 2m 2452 | [root@master0 ~]# kubectl describe pvc 2453 | Name: myclaim001 2454 | Namespace: default 2455 | Status: Bound 2456 | Volume: nfspv001 2457 | Labels: 2458 | Capacity: 5Gi 2459 | Access Modes: RWX 2460 | 2461 | # 查看Deployment 对象 2462 | [root@master0 ~]# kubectl get deployment nginx-deployment-pv 2463 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 2464 | nginx-deployment-pv 2 2 2 2 21s 2465 | [root@master0 ~]# kubectl describe deployment nginx-deployment-pv 2466 | Name: nginx-deployment-pv 2467 | Namespace: default 2468 | CreationTimestamp: Mon, 21 Nov 2016 17:08:06 +0800 2469 | Labels: app=nginx-pv 2470 | Selector: app=nginx-pv 2471 | Replicas: 2 updated | 2 total | 2 available | 0 unavailable 2472 | StrategyType: RollingUpdate 2473 | MinReadySeconds: 0 2474 | RollingUpdateStrategy: 1 max unavailable, 1 max surge 2475 | OldReplicaSets: 2476 | NewReplicaSet: nginx-deployment-pv-2162189116 (2/2 replicas created) 2477 | Events: 2478 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 2479 | --------- -------- ----- ---- ------------- -------- ------ ------- 2480 | 32s 32s 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-pv-2162189116 to 2 2481 | 2482 | # 查看Service 对象信息 2483 | [root@master0 ~]# kubectl get service nginx-service-pv 2484 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 2485 | nginx-service-pv 100.68.143.130 8000/TCP 3m 2486 | [root@master0 ~]# kubectl describe service nginx-service-pv 2487 | Name: nginx-service-pv 2488 | Namespace: default 2489 | Labels: 2490 | Selector: app=nginx-pv 2491 | Type: LoadBalancer 2492 | IP: 100.68.143.130 2493 | Port: 8000/TCP 2494 | NodePort: 31905/TCP 2495 | Endpoints: 10.32.0.31:80,10.40.0.26:80 2496 | Session Affinity: None 2497 | ``` 2498 | 2499 | 我们还可以连接相应Pod 查看其Volume挂接情况,需要注意你的pod信息和这里显示的会不同: 2500 | 2501 | ```shell 2502 | # 首先查询Pod信息 2503 | [root@master0 ~]# kubectl get pod |grep nginx-deployment-pv 2504 | nginx-deployment-pv-2162189116-bg1bw 1/1 Running 0 5m 2505 | nginx-deployment-pv-2162189116-yuozz 1/1 Running 0 5m 2506 | 2507 | # 分别连接Pod,查看其内部挂接信息 2508 | [root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-bg1bw /bin/bash 2509 | root@nginx-deployment-pv-2162189116-bg1bw:/# df 2510 | Filesystem 1K-blocks Used Available Use% Mounted on 2511 | /dev/mapper/docker-253:0-669928-284631bea446cc1ea5b0dfd7678a85b7875767bb43bac60762b3a2a73ca6bd3d 10474496 232560 10241936 3% / 2512 | tmpfs 508396 0 508396 0% /dev 2513 | tmpfs 508396 0 508396 0% /sys/fs/cgroup 2514 | /dev/mapper/rhel-root 9226240 6569852 2656388 72% /etc/hosts 2515 | shm 65536 0 65536 0% /dev/shm 2516 | 172.25.0.8:/mnt/share/nfs/k8s 5134336 4416 5129920 1% /usr/share/nginx/html 2517 | tmpfs 508396 12 508384 1% /run/secrets/kubernetes.io/serviceaccount 2518 | root@nginx-deployment-pv-2162189116-bg1bw:/# exit 2519 | exit 2520 | 2521 | [root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116- /bin/bash 2522 | nginx-deployment-pv-2162189116-bg1bw nginx-deployment-pv-2162189116-yuozz 2523 | [root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-yuozz /bin/bash 2524 | root@nginx-deployment-pv-2162189116-yuozz:/# mount |grep k8s 2525 | 172.25.0.8:/mnt/share/nfs/k8s on /usr/share/nginx/html type nfs (rw,relatime,vers=3,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=172.25.0.8,mountvers=3,mountport=677,mountproto=udp,local_lock=none,addr=172.25.0.8) 2526 | root@nginx-deployment-pv-2162189116-yuozz:/# exit 2527 | exit 2528 | ``` 2529 | 2530 | 可以看到,每个Pod都自动挂接了NFS共享目录,并且挂接在本地的/usr/share/nginx/html目录上。 2531 | 2532 | 我们可以进入Pod设置nginx的index.html,通过curl访问Service的NodePort来测试。 2533 | 2534 | ```shell 2535 | [root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-yuozz /bin/bash 2536 | root@nginx-deployment-pv-2162189116-yuozz:/# cd /usr/share/nginx/html/ 2537 | <62189116-yuozz:/usr/share/nginx/html# echo "

PV Test

" > index.html 2538 | root@nginx-deployment-pv-2162189116-yuozz:/usr/share/nginx/html# exit 2539 | exit 2540 | # 另外一个Pod 上应该也可以看到相同的文件 2541 | [root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-bg1bw /bin/bash 2542 | <9116-bg1bw:/# cat /usr/share/nginx/html/index.html 2543 |

PV Test

2544 | root@nginx-deployment-pv-2162189116-bg1bw exit 2545 | exit 2546 | # 通过curl工具访问Service的主页, 请用你自己的NodePort替换31905 2547 | [root@master0 ~]# curl http://master0.example.com:31905 2548 |

PV Test

2549 | ``` 2550 | 2551 | 我们之前在Master上挂接了相同的NFS目录,本地挂节点是/mnt,我们现在应该可以看到此目录下也存在文件index.html 2552 | 2553 | ```shell 2554 | [root@master0 ~]# cd /mnt 2555 | [root@master0 mnt]# ls 2556 | index.html 2557 | [root@master0 mnt]# cat index.html 2558 |

PV Test

2559 | ``` 2560 | 2561 | 综上就是我们创建带共享存储卷的Service环境的方法。此方法解决了微服务中多副本存储共享问题。 2562 | 2563 | ### 管理Pod的调度 2564 | 2565 | #### RC 和 Deployment 设置自动调度 2566 | 2567 | RC和Deployment设计的目的之一就是设置和管理Pod的多副本化,以及维持并监控Pod副本的数量。Kubernetes 集群通过RC和Deployment在集群内部始终维护用户指定的Pod副本数量。 2568 | 2569 | 之前我们使用my-nginx.yaml创建过一个Pod副本数为2的基于nginx web 的服务,其配置文件内容如下: 2570 | 2571 | ```yaml 2572 | apiVersion: extensions/v1beta1 2573 | kind: Deployment 2574 | metadata: 2575 | name: nginx-deployment 2576 | spec: 2577 | replicas: 2 # 指定副本数为 2 2578 | template: 2579 | metadata: 2580 | labels: 2581 | app: nginx 2582 | spec: 2583 | containers: 2584 | - name: nginx 2585 | image: nginx:latest 2586 | imagePullPolicy: IfNotPresent 2587 | ports: 2588 | - containerPort: 80 2589 | --- 2590 | apiVersion: v1 2591 | kind: Service 2592 | metadata: 2593 | name: nginx-service 2594 | spec: 2595 | ports: 2596 | - port: 8000 2597 | targetPort: 80 2598 | protocol: TCP 2599 | selector: 2600 | app: nginx 2601 | type: LoadBalancer 2602 | ``` 2603 | 2604 | 我们在此重新创建这个服务: 2605 | 2606 | ```shell 2607 | [root@master0 ~]# kubectl create -f my-nginx.yaml 2608 | deployment "nginx-deployment" created 2609 | service "nginx-service" created 2610 | ``` 2611 | 2612 | Pod 运行在Kubernetes 现有的Node上,我们可以通过kubectl get nodes 命令查看当前Node 的信息: 2613 | 2614 | ```shell 2615 | [root@master0 ~]# kubectl get nodes 2616 | NAME STATUS AGE 2617 | master0.example.com Ready 23h 2618 | nodea0.example.com Ready 23h 2619 | nodeb0.example.com Ready 23h 2620 | ``` 2621 | 2622 | 其中,masterN为Kubernetes集群中的Master节点,nodeaN和nodebN为运行Pod资源的Node节点。 2623 | 2624 | 我们可以通过命令kubectl get pod -o wide 来查看Pod分别运行在那些Node上: 2625 | 2626 | ```shell 2627 | [root@master0 ~]# kubectl get pod -o wide 2628 | NAME READY STATUS RESTARTS AGE IP NODE 2629 | nginx-deployment-2273492681-74lpo 1/1 Running 0 41m 10.46.0.4 nodeb0.example.com 2630 | nginx-deployment-2273492681-zgnzx 1/1 Running 0 41m 10.40.0.1 nodea0.example.com 2631 | ``` 2632 | 2633 | 在我们不做任何额外设置时,Pod在RC和Deployment 管理下会自动分布到现有资源Node节点上,并且在Node节点损坏或离线状态下自动迁移到正常的节点上。我们可以模拟节点nodeb损坏,验证Pod迁移。 2634 | 2635 | ```shell 2636 | # 首先迫使nodebN 节点离线 2637 | [kiosk@foundation0 ~]$ ssh root@nodeb0 "reboot" 2638 | 2639 | # 这时再查看Pod 信息 2640 | # 发现 nodebN 失效后,在正常的节点上创建新的 Pod 2641 | [root@master0 ~]# kubectl get pod -o wide 2642 | NAME READY STATUS RESTARTS AGE IP NODE 2643 | nginx-deployment-2273492681-0bpic 0/1 ContainerCreating 0 16s nodea0.example.com 2644 | nginx-deployment-2273492681-d9md3 1/1 Running 0 7m 10.40.0.1 nodea0.example.com 2645 | nginx-deployment-2273492681-g397i 1/1 Terminating 0 7m 10.46.0.4 nodeb0.example.com 2646 | 2647 | # 新Pod 运行正常后,删除失效的Pod 2648 | [root@master0 ~]# kubectl get pod -o wide 2649 | NAME READY STATUS RESTARTS AGE IP NODE 2650 | nginx-deployment-2273492681-0bpic 1/1 Running 0 22s 10.40.0.2 nodea0.example.com 2651 | nginx-deployment-2273492681-d9md3 1/1 Running 0 7m 10.40.0.1 nodea0.example.com 2652 | nginx-deployment-2273492681-g397i 1/1 Terminating 0 7m 10.46.0.4 nodeb0.example.com 2653 | [root@master0 ~]# kubectl get pod -o wide 2654 | NAME READY STATUS RESTARTS AGE IP NODE 2655 | nginx-deployment-2273492681-0bpic 1/1 Running 0 29s 10.40.0.2 nodea0.example.com 2656 | nginx-deployment-2273492681-d9md3 1/1 Running 0 8m 10.40.0.1 nodea0.example.com 2657 | 2658 | # Node 正常恢复后并不将Pod资源切回 2659 | [root@master0 ~]# kubectl get node 2660 | NAME STATUS AGE 2661 | master0.example.com Ready 1d 2662 | nodea0.example.com Ready 1d 2663 | nodeb0.example.com Ready 1d 2664 | [root@master0 ~]# kubectl get pod -o wide 2665 | NAME READY STATUS RESTARTS AGE IP NODE 2666 | nginx-deployment-2273492681-0bpic 1/1 Running 0 5m 10.40.0.2 nodea0.example.com 2667 | nginx-deployment-2273492681-d9md3 1/1 Running 0 13m 10.40.0.1 nodea0.example.com 2668 | ``` 2669 | 2670 | #### Pod 副本数扩容和缩减 2671 | 2672 | 在生产环境中,我们经常会遇到由于负载的增大需要对某个服务进行扩容的场景,可以经常会遇到由于资源紧张需要将不太重要的或实际负载不高的服务进行缩减的场景。在Kubernetes 集群环境中,我们可以很方便的利用Pod的副本数来控制服务容量的增减。 2673 | 2674 | ##### 手动增减Pod副本数 2675 | 2676 | 我们在Kubernetes 集群运行状态下,可以使用kubectl scale 命令手动增减RC和Deployment中Pod的副本数。 2677 | 2678 | 增加Deployment 中的副本数,演示如下: 2679 | 2680 | ```shell 2681 | # 当前 nginx-deployment 状态如下 2682 | [root@master0 ~]# kubectl get deployment nginx-deployment 2683 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 2684 | nginx-deployment 2 2 2 2 25m 2685 | 2686 | # 当前 pod 状态如下 2687 | [root@master0 ~]# kubectl get pod 2688 | NAME READY STATUS RESTARTS AGE 2689 | nginx-deployment-2273492681-0bpic 1/1 Running 0 18m 2690 | nginx-deployment-2273492681-d9md3 1/1 Running 0 26m 2691 | 2692 | # 我们将nginx-deployment 中的pod 数由2设置为3 2693 | [root@master0 ~]# kubectl scale --replicas=3 deployment nginx-deployment 2694 | deployment "nginx-deployment" scaled 2695 | 2696 | # 增加副本数后的nginx-deployment 状态如下 2697 | [root@master0 ~]# kubectl get deployment nginx-deployment 2698 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 2699 | nginx-deployment 3 3 3 3 28m 2700 | 2701 | # 增加副本数后的 pod 状态如下 2702 | [root@master0 ~]# kubectl get pod 2703 | NAME READY STATUS RESTARTS AGE 2704 | nginx-deployment-2273492681-0bpic 1/1 Running 0 21m 2705 | nginx-deployment-2273492681-d9md3 1/1 Running 0 28m 2706 | nginx-deployment-2273492681-m3qcj 1/1 Running 0 58s 2707 | 2708 | # 为后续实验清理环境 2709 | [root@master0 ~]# kubectl delete -f my-nginx.yaml 2710 | deployment "nginx-deployment" deleted 2711 | service "nginx-service" deleted 2712 | ``` 2713 | 2714 | ##### 自动增减Pod副本数 2715 | 2716 | 除了手动通过kubectl scale 命令来增减Pod副本数之外,我们还可以使用在前面介绍过的概念Horizontal Pod Autoscaler(HPA)来根据容器占用的CPU使用率来自动进行增减Pod副本数。 2717 | 2718 | Horizontal Pod Autoscaler (HPA) 基于Master节点上的kube-controller-manager服务定义的监测时长(默认为30秒),周期性的检测Pod的CPU使用率,当满足预设条件时对RC或Deployment中的副本数进行调整。 2719 | 2720 | 要使用HPA就需要预设Pod的CPU使用条件,同时还需要Kubernetes Heapster组件的支持,需要安装Heapster组件,否则无法获取Pod的CPU使用情况。 2721 | 2722 | 首先我们需要装载Heapster运行容器的镜像到每个Node节点上: 2723 | 2724 | ```shell 2725 | # 在所有Node节点上下载heapster-img.tbz文件 2726 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i " wget http://classroom.example.com/materials/k8s-imgs/heapster-img.tbz" ; done 2727 | 2728 | # 然后将其解包 2729 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf heapster-img.tbz " ; done 2730 | 2731 | # 然后将其导入 2732 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./heapster/*.img ; do docker load -i $i ; done' ; done 2733 | ``` 2734 | 2735 | 在所有Node节点导入所需的heapster容器镜像后,需要在Master上运行Heapster脚本将其启动起来: 2736 | 2737 | ```shell 2738 | # 首先下载Heapster运行环境包到Master节点上 2739 | [kiosk@foundation0 Desktop]$ ssh root@master0 "wget http://classroom.example.com/materials/k8s-conf/heapster.tar " 2740 | 2741 | # 然后将其解包 2742 | [kiosk@foundation0 Desktop]$ ssh root@master0 "tar -xf heapster.tar " 2743 | 2744 | # 在Master节点上执行heapster目录下的kube.sh脚本 2745 | [root@master0 heapster]# ./kube.sh 2746 | 2747 | # 耐心等待3~5分钟,可以看到相应的Pod运行起来 2748 | [root@master0 heapster]# kubectl get pod --all-namespaces 2749 | NAMESPACE NAME READY STATUS RESTARTS AGE 2750 | kube-system etcd-master0.example.com 1/1 Running 5 3d 2751 | kube-system heapster-3901806196-8c2rj 1/1 Running 3 1d 2752 | kube-system kube-apiserver-master0.example.com 1/1 Running 10 3d 2753 | kube-system kube-controller-manager-master0.example.com 1/1 Running 5 3d 2754 | kube-system kube-discovery-982812725-nghjq 1/1 Running 5 3d 2755 | kube-system kube-dns-2247936740-f32d9 3/3 Running 15 3d 2756 | kube-system kube-proxy-amd64-gzmv5 1/1 Running 6 3d 2757 | kube-system kube-proxy-amd64-px2ms 1/1 Running 3 1d 2758 | kube-system kube-proxy-amd64-tve1y 1/1 Running 4 3d 2759 | kube-system kube-scheduler-master0.example.com 1/1 Running 5 3d 2760 | kube-system kubernetes-dashboard-1171352413-yuqpa 1/1 Running 3 2d 2761 | kube-system monitoring-grafana-927606581-45lpl 1/1 Running 3 1d 2762 | kube-system monitoring-influxdb-3276295126-ec2nf 1/1 Running 3 1d 2763 | kube-system weave-net-cfenz 2/2 Running 15 3d 2764 | kube-system weave-net-kpvob 2/2 Running 6 1d 2765 | kube-system weave-net-xblek 2/2 Running 10 3d 2766 | 2767 | # 有三个服务正常运行 2768 | [root@master0 ~]# kubectl get service heapster --namespace=kube-system 2769 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 2770 | heapster 100.77.26.79 80/TCP 1d 2771 | [root@master0 ~]# kubectl get service monitoring-grafana --namespace=kube-system 2772 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 2773 | monitoring-grafana 100.70.101.80 80/TCP 1d 2774 | [root@master0 ~]# kubectl get service monitoring-influxdb --namespace=kube-system 2775 | NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 2776 | monitoring-influxdb 100.64.163.255 8086/TCP 1d 2777 | ``` 2778 | 2779 | Kubernetes 的Heapster模块正常部署后,我们就可以做自动增减Pod副本数的实验了。 2780 | 2781 | 首先在所有Node节点上部署hpa-example容器镜像,它是一个安装了apache和php的测试环境,我们在后面的试验中将访问其服务使其产生工作负载。 2782 | 2783 | ```shell 2784 | # 在所有Node节点上下载hpa-example-img.tbz包 2785 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i " wget http://classroom.example.com/materials/k8s-imgs/hpa-example-img.tbz" ; done 2786 | 2787 | # 然后将其解开 2788 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf hpa-example-img.tbz " ; done 2789 | 2790 | # 然后将hpa-example容器镜像导入 2791 | [kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./hpa-example/*.img ; do docker load -i $i ; done' ; done 2792 | Loaded image: kissingwolf/hpa-example:latest 2793 | Loaded image: kissingwolf/hpa-example:latest 2794 | 2795 | # 如果你之前没有注意清除Node节点环境下载的文件,由于磁盘空间限制可能会导致无法导入,你可以执行以下命令,清除下载文件并提出磁盘空间 2796 | [kiosk@foundation0 ~]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "rm -rf ~/*.tbz " ; done 2797 | 2798 | ``` 2799 | 2800 | 接下来我们创建hpa-example.yaml配置文件,设置自动增减Pod副本数的Service环境: 2801 | 2802 | ```yaml 2803 | apiVersion: extensions/v1beta1 2804 | kind: Deployment 2805 | metadata: 2806 | name: hpa-example-deployment 2807 | spec: 2808 | replicas: 2 2809 | template: 2810 | metadata: 2811 | labels: 2812 | app: hpa-example 2813 | spec: 2814 | containers: 2815 | - name: php-apache 2816 | image: kissingwolf/hpa-example:latest 2817 | imagePullPolicy: IfNotPresent 2818 | ports: 2819 | - containerPort: 80 2820 | resources: 2821 | requests: 2822 | cpu: 200m 2823 | --- 2824 | apiVersion: v1 2825 | kind: Service 2826 | metadata: 2827 | name: hpa-example-service 2828 | spec: 2829 | ports: 2830 | - port: 8000 2831 | targetPort: 80 2832 | protocol: TCP 2833 | selector: 2834 | app: hpa-example 2835 | type: LoadBalancer 2836 | --- 2837 | apiVersion: autoscaling/v1 2838 | kind: HorizontalPodAutoscaler 2839 | metadata: 2840 | name: hpa-example-deployment 2841 | spec: 2842 | maxReplicas: 10 2843 | minReplicas: 2 2844 | scaleTargetRef: 2845 | apiVersion: extensions/v1beta1 2846 | kind: Deployment 2847 | name: hpa-example-deployment 2848 | targetCPUUtilizationPercentage: 50 2849 | ``` 2850 | 2851 | 配置文件分为三个部分:Deployment、Service和HorizontalPodAutoscaler 2852 | 2853 | Depolyment部分说明: 2854 | 2855 | ```yaml 2856 | apiVersion: extensions/v1beta1 2857 | kind: Deployment 2858 | metadata: 2859 | name: hpa-example-deployment 2860 | spec: 2861 | replicas: 2 # 初始化副本数为2 2862 | template: 2863 | metadata: 2864 | labels: 2865 | app: hpa-example 2866 | spec: 2867 | containers: 2868 | - name: php-apache 2869 | image: kissingwolf/hpa-example:latest 2870 | imagePullPolicy: IfNotPresent 2871 | ports: 2872 | - containerPort: 80 2873 | resources: # spec.template.spce.containers[].resources 设置资源监控 2874 | requests: # 资源监控项目 2875 | cpu: 200m # CPU资源初始化限制,1 cpu core = 1000m ,200m = 0.2 cpu core 2876 | ``` 2877 | 2878 | HorizontalPodAutoscaler部分说明: 2879 | 2880 | ```yaml 2881 | apiVersion: autoscaling/v1 # 配置版本 2882 | kind: HorizontalPodAutoscaler # 配置类型 2883 | metadata: 2884 | name: hpa-example-deployment 2885 | spec: 2886 | maxReplicas: 10 # 设置最大副本数 2887 | minReplicas: 2 # 设置最小副本数 2888 | scaleTargetRef: 2889 | apiVersion: extensions/v1beta1 2890 | kind: Deployment 2891 | name: hpa-example-deployment # 配置的Deployment名称 2892 | targetCPUUtilizationPercentage: 50 # 设置Pod Cpu使用率维持在50% 2893 | ``` 2894 | 2895 | 通过kubectl create 命令创建此服务: 2896 | 2897 | ```shell 2898 | [root@master0 ~]# kubectl create -f hpa-example.yaml 2899 | deployment "hpa-example-deployment" created 2900 | service "hpa-example-service" created 2901 | horizontalpodautoscaler "hpa-example-deployment" created 2902 | ``` 2903 | 2904 | 初始化状态可以通过 kubectl get hpa 命令查看: 2905 | 2906 | ```shell 2907 | [root@master0 ~]# kubectl get hpa 2908 | NAME REFERENCE TARGET CURRENT MINPODS MAXPODS AGE 2909 | hpa-example-deployment Deployment/hpa-example-deployment 50% 2 10 47s 2910 | # 需要等待1分钟左右才能收集好资源信息 2911 | [root@master0 ~]# kubectl get hpa 2912 | NAME REFERENCE TARGET CURRENT MINPODS MAXPODS AGE 2913 | hpa-example-deployment Deployment/hpa-example-deployment 50% 0% 2 10 2m 2914 | ``` 2915 | 2916 | 我们可以通过使用物理机foundationN发起请求,以增加负载的方式使Depolyment中的副本数自动增加: 2917 | 2918 | ```shell 2919 | # 首先确定hpa-example-service 对外暴露的端口(NodePort) 2920 | [root@master0 ~]# kubectl describe service hpa-example-service 2921 | Name: hpa-example-service 2922 | Namespace: default 2923 | Labels: 2924 | Selector: app=hpa-example 2925 | Type: LoadBalancer 2926 | IP: 100.69.106.140 2927 | Port: 8000/TCP 2928 | NodePort: 32445/TCP 2929 | Endpoints: 10.40.0.1:80,10.46.0.20:80 2930 | Session Affinity: None 2931 | No events. 2932 | 2933 | # 在foundationN上执行如下命令产生负载,请注意MasterN为你自己的Master主机,访问端口为你即时查看到的端口 2934 | [kiosk@foundation0 ~]$ while : ; do curl http://master0.example.com:32445 >/dev/null 2>&1 ; done 2935 | 2936 | # 初始化的Deployment状态如下: 2937 | [root@master0 ~]# kubectl get deployment 2938 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 2939 | hpa-example-deployment 2 2 2 2 15m 2940 | 2941 | # 经过2分钟左右后Deployment状态如下 2942 | [root@master0 ~]# kubectl get deployment 2943 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 2944 | hpa-example-deployment 6 6 6 6 18m 2945 | 2946 | # hpa 状态如下 2947 | [root@master0 ~]# kubectl get hpa 2948 | NAME REFERENCE TARGET CURRENT MINPODS MAXPODS AGE 2949 | hpa-example-deployment Deployment/hpa-example-deployment 50% 55% 2 10 19m 2950 | 2951 | # pod 状态如下 2952 | [root@master0 ~]# kubectl get pod 2953 | NAME READY STATUS RESTARTS AGE 2954 | hpa-example-deployment-882509061-0vrl2 1/1 Running 0 36s 2955 | hpa-example-deployment-882509061-5qrzz 1/1 Running 0 18m 2956 | hpa-example-deployment-882509061-99a4s 1/1 Running 0 36s 2957 | hpa-example-deployment-882509061-iw037 1/1 Running 0 36s 2958 | hpa-example-deployment-882509061-vus9q 1/1 Running 0 36s 2959 | hpa-example-deployment-882509061-zejct 1/1 Running 0 18m 2960 | ``` 2961 | 2962 | 如果我们停下foundationN上运行的运行的负载,则Deployment中的Pod数会自动减少。 2963 | 2964 | 最后不要忘记清除环境: 2965 | 2966 | ```shell 2967 | [root@master0 ~]# kubectl delete -f hpa-example.yaml 2968 | deployment "hpa-example-deployment" deleted 2969 | service "hpa-example-service" deleted 2970 | horizontalpodautoscaler "hpa-example-deployment" deleted 2971 | ``` 2972 | 2973 | #### Pod 中容器的滚动升级 2974 | 2975 | 当Kubernetes集群中的某个Service由于某种原因需要升级相关Pod中的容器镜像,我们会想当然的认为正确的操作步骤是先停止Service上的所有相关Pod,然后从镜像注册中心拉取新的镜像然后启动。在Kubernetes集群中Pod数量不多的情况下这样的操作没有问题,但是如果集群规模比较大,Pod的数量比较多,并且用户访问又是持续化的,这样的操作会带来灾难性的后果。你可以想象以下在大草原上,数以百万计的野牛狂奔向你的时候,你和你的同伴所有的武器都突然哑火的感觉吗?我们称这种情况叫做“惊群效应”。 2976 | 2977 | Kubernetes 是通过滚动升级(rolling-update)功能来解决这个问题的,RC方式中操作命令为 kubectl rolling-update,Deployment方式中操作命令为 kubectl set image 。 2978 | 2979 | ##### RC 滚动升级 2980 | 2981 | RC 滚动升级功能通过将原有RC和升级RC置于同一Service和Namespace下,然后自动控制原有RC中的Pod副本数量逐渐减少直至为零,同时自动控制升级RC中的Pod副本数量从零逐渐增长至指定值。新旧RC中的Pod副本资源共享且均衡负载,有效的降低了惊群效应的发生。 2982 | 2983 | 在前面的试验中,我们在Node上导入了nginx三个版本的镜像,之前使用的都是latest最新版本,在接下来的试验中,我们会首先创建一个使用低版本nginx镜像的Service ,然后使用配置文件更新这个Service的nginx镜像到高版本。 2984 | 2985 | 创建旧有RC的配置文件命名为test-rollingupdate-v1.yaml,内容如下: 2986 | 2987 | ```yaml 2988 | apiVersion: v1 2989 | kind: ReplicationController 2990 | metadata: 2991 | name: test-rollingupdate-v1 2992 | labels: 2993 | name: nginx 2994 | version: v1 2995 | spec: 2996 | replicas: 4 # 我们设置Pod副本数为4 2997 | selector: 2998 | name: nginx 2999 | version: v1 3000 | template: 3001 | metadata: 3002 | labels: 3003 | name: nginx 3004 | version: v1 3005 | spec: 3006 | containers: 3007 | - name: nginx 3008 | image: nginx:1.10.2 # nginx容器镜像版本为1.10.2 3009 | imagePullPolicy: IfNotPresent 3010 | ports: 3011 | - containerPort: 80 3012 | ``` 3013 | 3014 | 使用配置文件生成RC: 3015 | 3016 | ```shell 3017 | [root@master0 ~]# kubectl create -f test-rollingupdate-v1.yaml 3018 | replicationcontroller "test-rollingupdate-v1" create 3019 | ``` 3020 | 3021 | 查看当前RC信息: 3022 | 3023 | ```shell 3024 | [root@master0 ~]# kubectl get rc 3025 | NAME DESIRED CURRENT READY AGE 3026 | test-rollingupdate-v1 4 4 4 56s 3027 | 3028 | [root@master0 ~]# kubectl get pod 3029 | NAME READY STATUS RESTARTS AGE 3030 | test-rollingupdate-v1-8nl1f 1/1 Running 0 1m 3031 | test-rollingupdate-v1-8y5fx 1/1 Running 0 1m 3032 | test-rollingupdate-v1-l6l03 1/1 Running 0 1m 3033 | test-rollingupdate-v1-mk1cr 1/1 Running 0 1m 3034 | 3035 | [root@master0 ~]# kubectl describe replicationcontroller 3036 | Name: test-rollingupdate-v1 3037 | Namespace: default 3038 | Image(s): nginx:1.10.2 3039 | Selector: name=nginx,version=v1 3040 | Labels: name=nginx 3041 | version=v1 3042 | Replicas: 4 current / 4 desired 3043 | Pods Status: 4 Running / 0 Waiting / 0 Succeeded / 0 Failed 3044 | No volumes. 3045 | Events: 3046 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 3047 | --------- -------- ----- ---- ------------- -------- ------ ------- 3048 | 1m 1m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-mk1cr 3049 | 1m 1m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-8y5fx 3050 | 1m 1m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-l6l03 3051 | 1m 1m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-8nl1f 3052 | ``` 3053 | 3054 | 创建新有RC的配置文件命名为test-rollingupdate-v2.yaml,内容如下: 3055 | 3056 | ```yaml 3057 | apiVersion: v1 3058 | kind: ReplicationController 3059 | metadata: 3060 | name: test-rollingupdate-v2 # RC的名字不能与旧RC同名 3061 | labels: 3062 | name: nginx-rc 3063 | version: v2 # 用于与旧版本区分 3064 | spec: 3065 | replicas: 2 # v2 版本的副本数可以与v1 不同 3066 | selector: 3067 | name: nginx 3068 | version: v2 # spec.selector中至少有一个Label不能与旧版本不同 3069 | template: 3070 | metadata: 3071 | labels: 3072 | name: nginx 3073 | version: v2 # 用于与旧版本区分 3074 | spec: 3075 | containers: 3076 | - name: nginx 3077 | image: nginx:1.11.5 # nginx容器镜像版本升级为1.11.5 3078 | imagePullPolicy: IfNotPresent 3079 | ports: 3080 | - containerPort: 80 3081 | ``` 3082 | 3083 | 使用kubectl rolling-update命令滚动升级test-rollingupdate-v1 RC: 3084 | 3085 | ```shell 3086 | [rootmaster0 ~]# kubectl rolling-update test-rollingupdate-v1 -f test-rollingupdate-v2.yaml 3087 | Created test-rollingupdate-v2 3088 | Scaling up test-rollingupdate-v2 from 0 to 2, scaling down test-rollingupdate-v1 from 4 to 0 (keep 2 pods available, don't exceed 3 pods) 3089 | .... 3090 | 此处根据副本个数决定切换时间和显示 3091 | ``` 3092 | 3093 | 同时打开另外一个终端,查看RC信息,可以看到v1和v2版本开始切换: 3094 | 3095 | ```shell 3096 | [root@master0 ~]# kubectl get rc 3097 | NAME DESIRED CURRENT READY AGE 3098 | test-rollingupdate-v1 2 2 2 12m 3099 | test-rollingupdate-v2 1 1 1 1m 3100 | ``` 3101 | 3102 | 在另一个终端中,查看replicationcontroller的详细信息,可以看到v1和v2版本具体操作信息: 3103 | 3104 | ```shell 3105 | [root@master0 ~]# kubectl describe replicationcontroller 3106 | Name: test-rollingupdate-v1 3107 | Namespace: default 3108 | Image(s): nginx:1.10.2 3109 | Selector: name=nginx,version=v1 3110 | Labels: name=nginx 3111 | version=v1 3112 | Replicas: 1 current / 1 desired 3113 | Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed 3114 | No volumes. 3115 | Events: 3116 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 3117 | --------- -------- ----- ---- ------------- -------- ------ ------- 3118 | 13m 13m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-mk1cr 3119 | 13m 13m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-8y5fx 3120 | 13m 13m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-l6l03 3121 | 13m 13m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v1-8nl1f 3122 | 1m 1m 1 {replication-controller } Normal SuccessfulDelete Deleted pod: test-rollingupdate-v1-8y5fx 3123 | 1m 1m 1 {replication-controller } Normal SuccessfulDelete Deleted pod: test-rollingupdate-v1-8nl1f 3124 | 7s 7s 1 {replication-controller } Normal SuccessfulDelete Deleted pod: test-rollingupdate-v1-mk1cr 3125 | 3126 | 3127 | Name: test-rollingupdate-v2 3128 | Namespace: default 3129 | Image(s): nginx:1.11.5 3130 | Selector: name=nginx,version=v2 3131 | Labels: name=nginx-rc 3132 | version=v2 3133 | Replicas: 2 current / 2 desired 3134 | Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed 3135 | No volumes. 3136 | Events: 3137 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 3138 | --------- -------- ----- ---- ------------- -------- ------ ------- 3139 | 1m 1m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v2-6i9hm 3140 | 7s 7s 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v2-tf47l 3141 | ``` 3142 | 3143 | 等待一段时间后,v1中的Pod均切换为v2的Pod: 3144 | 3145 | ```shell 3146 | [root@master0 ~]# kubectl rolling-update test-rollingupdate-v1 -f test-rollingupdate-v2.yaml 3147 | Created test-rollingupdate-v2 3148 | Scaling up test-rollingupdate-v2 from 0 to 2, scaling down test-rollingupdate-v1 from 4 to 0 (keep 2 pods available, don't exceed 3 pods) 3149 | Scaling test-rollingupdate-v1 down to 2 3150 | Scaling test-rollingupdate-v2 up to 1 3151 | Scaling test-rollingupdate-v1 down to 1 3152 | Scaling test-rollingupdate-v2 up to 2 3153 | Scaling test-rollingupdate-v1 down to 0 3154 | Update succeeded. Deleting test-rollingupdate-v1 3155 | replicationcontroller "test-rollingupdate-v1" rolling updated to "test-rollingupdate-v2" 3156 | ``` 3157 | 3158 | 滚动升级后,仅保留v2 版本的RC: 3159 | 3160 | ```shell 3161 | [root@master0 ~]# kubectl get rc 3162 | NAME DESIRED CURRENT READY AGE 3163 | test-rollingupdate-v2 2 2 2 8m 3164 | ``` 3165 | 3166 | 查看replicationcontroller 具体信息也是一样: 3167 | 3168 | ```shell 3169 | [root@master0 ~]# kubectl describe replicationcontroller 3170 | Name: test-rollingupdate-v2 3171 | Namespace: default 3172 | Image(s): nginx:1.11.5 3173 | Selector: name=nginx,version=v2 3174 | Labels: name=nginx-rc 3175 | version=v2 3176 | Replicas: 2 current / 2 desired 3177 | Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed 3178 | No volumes. 3179 | Events: 3180 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 3181 | --------- -------- ----- ---- ------------- -------- ------ ------- 3182 | 9m 9m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v2-6i9hm 3183 | 8m 8m 1 {replication-controller } Normal SuccessfulCreate Created pod: test-rollingupdate-v2-tf47l 3184 | ``` 3185 | 3186 | 我们也可以不更新配置文件直接使用命令指定要滚动升级的nginx镜像,命令如下: 3187 | 3188 | ```shell 3189 | [root@master0 ~]# kubectl rolling-update test-rollingupdate-v1 --image=nginx:1.11.5 3190 | ``` 3191 | 3192 | 我们如果发现滚动升级后新版本的镜像有问题,还可以指定原有镜像回滚。 3193 | 3194 | ```shell 3195 | [root@master0 ~]# kubectl rolling-update test-rollingupdate-v1 --image=nginx:1.10.2 --rollback 3196 | ``` 3197 | 3198 | ##### Deployment 滚动升级 3199 | 3200 | Deployment 滚动升级比较简单,首先我们创建一个测试用的Deployment,配置文件命名为test-deployment-rollup.yaml ,内容如下: 3201 | 3202 | ```yaml 3203 | apiVersion: extensions/v1beta1 3204 | kind: Deployment 3205 | metadata: 3206 | name: nginx-deployment 3207 | spec: 3208 | replicas: 4 3209 | template: 3210 | metadata: 3211 | labels: 3212 | app: nginx 3213 | spec: 3214 | containers: 3215 | - name: nginx 3216 | image: nginx:1.10.2 3217 | imagePullPolicy: IfNotPresent 3218 | ports: 3219 | - containerPort: 80 3220 | ``` 3221 | 3222 | 然后使用kubectl create 创建Deployment: 3223 | 3224 | ```shell 3225 | [root@master0 ~]# kubectl create -f test-deployment-rollup.yaml 3226 | deployment "nginx-deployment" created 3227 | ``` 3228 | 3229 | 通过查看Deployment的详细信息,可以获悉其默认使用RollingUpdate方式: 3230 | 3231 | ```shell 3232 | [root@master0 ~]# kubectl describe deployment 3233 | Name: nginx-deployment 3234 | Namespace: default 3235 | CreationTimestamp: Tue, 06 Dec 2016 16:29:01 +0800 3236 | Labels: app=nginx 3237 | Selector: app=nginx 3238 | Replicas: 4 updated | 4 total | 4 available | 0 unavailable 3239 | StrategyType: RollingUpdate 3240 | MinReadySeconds: 0 3241 | RollingUpdateStrategy: 1 max unavailable, 1 max surge 3242 | OldReplicaSets: 3243 | NewReplicaSet: nginx-deployment-2612444508 (4/4 replicas created) 3244 | Events: 3245 | FirstSeen LastSeen Count From SubobjectPath Type Reason Message 3246 | --------- -------- ----- ---- ------------- -------- ------ ------- 3247 | 3m 3m 1 {deployment-controller } Normal ScalingReplicaSet Scaled up replica set nginx-deployment-2612444508 to 4 3248 | ``` 3249 | 3250 | 我们只需要使用kubectl set image 命令就可以滚动升级其Pod容器镜像: 3251 | 3252 | ```shell 3253 | [root@master0 ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.11.5 3254 | deployment "nginx-deployment" image updated 3255 | ``` 3256 | 3257 | 其中deployment/nginx-deployment 是deployment的名字,nginx=nginx:1.11.5 是容器名=容器镜像名及版本。 3258 | 3259 | 滚动的方式也是将旧版本容器逐步停止,然后逐一生成新版本容器,但要比RC方式更快。 3260 | 3261 | ```shell 3262 | [root@master0 ~]# kubectl get pods 3263 | NAME READY STATUS RESTARTS AGE 3264 | nginx-deployment-2612444508-xicfw 1/1 Running 0 6m 3265 | nginx-deployment-2937634144-bnyuz 0/1 ContainerCreating 0 7s 3266 | nginx-deployment-2937634144-eo6qb 0/1 ContainerCreating 0 9s 3267 | nginx-deployment-2937634144-mvyjb 1/1 Running 0 17s 3268 | nginx-deployment-2937634144-rzgcl 1/1 Running 0 17s 3269 | ``` 3270 | 3271 | 3272 | 3273 | 3274 | 3275 | 3276 | 3277 | 3278 | 3279 | 3280 | 3281 | 3282 | 3283 | 3284 | --------------------------------------------------------------------------------