写在前面

接下来我们开始在Kubernetes简易环境中,来练习和使用一些常用的kubectl的命令,了解这些对于后续熟练和使用kubernetes有非常大的帮助。笔者将kubectl命令安装在了主节点上,因此接下来就在主节点上执行对应的kubectl命令。

(1)使用kubectl version命令查看k8s的版本信息:

1
2
3
[root@server02 target]# ·
Client Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.0", GitCommit:"925c127ec6b946659ad0fd596fa959be43f0cc05", GitTreeState:"clean", BuildDate:"2017-12-15T21:07:38Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.0", GitCommit:"925c127ec6b946659ad0fd596fa959be43f0cc05", GitTreeState:"clean", BuildDate:"2017-12-15T20:55:30Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}

(2)使用kubectl get nodes命令查看k8s集群中所有的节点信息:

1
2
3
4
[root@server02 target]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
192.168.51.121 Ready <none> 24m v1.9.0
192.168.51.123 Ready <none> 24m v1.9.0

(3)使用kubectl get pods命令查看k8s集群中所有的pods信息:

1
2
[root@server02 target]# kubectl get pods
No resources found.

(4)使用kubectl --help来查看所有kubectl所支持的命令及帮助信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Basic Commands (Beginner):
create Create a resource from a file or from stdin.
expose 使用 replication controller, service, deployment 或者 pod 并暴露它作为一个 新的Kubernetes Service
run 在集群中运行一个指定的镜像
set 为 objects 设置一个指定的特征
run-container 在集群中运行一个指定的镜像. This command is deprecated, use "run" instead

Basic Commands (Intermediate):
get 显示一个或更多 resources
explain 查看资源的文档
edit 在服务器上编辑一个资源
delete Delete resources by filenames, stdin, resources and names, or by resources and label selector

Deploy Commands:
rollout Manage the rollout of a resource
rolling-update 完成指定的 ReplicationController 的滚动升级
scale 为 Deployment, ReplicaSet, Replication Controller 或者 Job 设置一个新的副本数量
autoscale 自动调整一个 Deployment, ReplicaSet, 或者 ReplicationController 的副本数量

Cluster Management Commands:
certificate 修改 certificate 资源.
cluster-info 显示集群信息
top Display Resource (CPU/Memory/Storage) usage.
cordon 标记 node 为 unschedulable
uncordon 标记 node 为 schedulable
drain Drain node in preparation for maintenance
taint 更新一个或者多个 node 上的 taints

Troubleshooting and Debugging Commands:
describe 显示一个指定 resource 或者 group 的 resources 详情
logs 输出容器在 pod 中的日志
attach Attach 到一个运行中的 container
exec 在一个 container 中执行一个命令
port-forward Forward one or more local ports to a pod
proxy 运行一个 proxy 到 Kubernetes API server
cp 复制 files 和 directories 到 containers 和从容器中复制 files 和 directories.
auth Inspect authorization

Advanced Commands:
apply 通过文件名或标准输入流(stdin)对资源进行配置
patch 使用 strategic merge patch 更新一个资源的 field(s)
replace 通过 filename 或者 stdin替换一个资源
convert 在不同的 API versions 转换配置文件

Settings Commands:
label 更新在这个资源上的 labels
annotate 更新一个资源的注解
completion Output shell completion code for the specified shell (bash or zsh)

Other Commands:
api-versions Print the supported API versions on the server, in the form of "group/version"
config 修改 kubeconfig 文件
help Help about any command
plugin Runs a command-line plugin
version 输出 client 和 server 的版本信息

接下来我们尝试通过kubectl run命令在k8s集群中部署一个命名为kubernetes-bootcamp的应用,Docker镜像通过--image指定,--port设置应用对外服务的端口,使用的命令如下:

1
$ kubectl run kubernetes-bootcamp --image=jocatalin/kubernetes-bootcamp:v1 --port=8080

然后我们查看一下所有应用的信息:

1
2
3
[root@server02 target]# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1 1 1 0 4m

再来查看一下所有Pod的信息:

1
2
3
[root@server02 target]# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-5d7f968ccb-z59rw 0/1 ContainerCreating 0 5m

也可以使用如下命令查看所有pod的更多信息:

1
2
3
[root@server02 target]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kubernetes-bootcamp-5d7f968ccb-z59rw 0/1 ContainerCreating 0 6m <none> 192.168.51.123

如果开发者希望删除某个deployment,此时可以使用如下命令,后面的kubernetes-bootcamp则是应用的名称:

1
2
[root@server02 target]# kubectl delete deployments kubernetes-bootcamp
deployment "kubernetes-bootcamp" deleted

其实kubernetes对一些命令也提供了一些别名,举个例子,假设当前使用的是kubectl get命令,它可以显示一个或更多的resources,此时就可以使用kubectl get --help查看后面所跟一些命令的别名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Valid resource types include: 

* all
* certificatesigningrequests (aka 'csr')
* clusterrolebindings
* clusterroles
* componentstatuses (aka 'cs')
* configmaps (aka 'cm')
* controllerrevisions
* cronjobs
* customresourcedefinition (aka 'crd')
* daemonsets (aka 'ds')
* deployments (aka 'deploy')
* endpoints (aka 'ep')
* events (aka 'ev')
* horizontalpodautoscalers (aka 'hpa')
* ingresses (aka 'ing')
* jobs
* limitranges (aka 'limits')
* namespaces (aka 'ns')
* networkpolicies (aka 'netpol')
* nodes (aka 'no')
* persistentvolumeclaims (aka 'pvc')
* persistentvolumes (aka 'pv')
* poddisruptionbudgets (aka 'pdb')
* podpreset
* pods (aka 'po')
* podsecuritypolicies (aka 'psp')
* podtemplates
* replicasets (aka 'rs')
* replicationcontrollers (aka 'rc')
* resourcequotas (aka 'quota')
* rolebindings
* roles
* secrets
* serviceaccounts (aka 'sa')
* services (aka 'svc')
* statefulsets (aka 'sts')
* storageclasses (aka 'sc')

Examples:
# List all pods in ps output format.
kubectl get pods

# List all pods in ps output format with more information (such as node name).
kubectl get pods -o wide

# List a single replication controller with specified NAME in ps output format.
kubectl get replicationcontroller web

# List a single pod in JSON output format.
kubectl get -o json pod web-pod-13je7

# List a pod identified by type and name specified in "pod.yaml" in JSON output format.
kubectl get -f pod.yaml -o json

# Return only the phase value of the specified pod.
kubectl get -o template pod/web-pod-13je7 --template={{.status.phase}}

# List all replication controllers and services together in ps output format.
kubectl get rc,services

# List one or more resources by their type and names.
kubectl get rc/web service/frontend pods/web-pod-13je7

# List all resources with different types.
kubectl get all

例如,查看所有应用的信息所使用的命令,可由

1
$ kubectl get deployments

简化为如下的:

1
$ kubectl get deploy

我们再来看一下查看所有pod的更多信息:

1
2
3
[root@server02 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kubernetes-bootcamp-6b7849c495-mgzpp 1/1 Running 0 22h 172.20.40.194 192.168.51.123

如果想要查看k8s的日志运行信息,可以使用journalctl -f命令。可以使用kubectl describe deploy +应用名称命令来查看某个应用的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@server02 ~]# kubectl describe deploy kubernetes-bootcamp
Name: kubernetes-bootcamp
Namespace: default
CreationTimestamp: Mon, 29 Aug 2022 22:30:58 +0800
Labels: run=kubernetes-bootcamp
Annotations: deployment.kubernetes.io/revision=1
Selector: run=kubernetes-bootcamp
Replicas: 1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: run=kubernetes-bootcamp
Containers:
kubernetes-bootcamp:
Image: jocatalin/kubernetes-bootcamp:v1
Port: 8080/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: kubernetes-bootcamp-6b7849c495 (1/1 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 22h deployment-controller Scaled up replica set kubernetes-bootcamp-6b7849c495 to 1

我们再来看一下查看所有pod信息:

1
2
3
[root@server02 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kubernetes-bootcamp-6b7849c495-mgzpp 1/1 Running 0 22h 172.20.40.194 192.168.51.123

再来看一下这个Pod的信息,可使用kubectl describe pods +pod名称命令:

基础命令的学习就到此结束了,接下来我们继续学习其他的知识。deployment已经创建起来了,我们该如何去访问它呢?第一种方式是使用kubectl proxy命令 ,它会在本地监听8001端口,这个主要用于测试:

1
2
[root@server02 ~]# kubectl proxy
Starting to serve on 127.0.0.1:8001

然后我们就可以通过这个REST API来访问这个集群中运行的每一个服务。可以新开一个终端,在里面先查看一下当前所有的pods信息:

1
2
3
[root@server02 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-6b7849c495-mgzpp 1/1 Running 1 1d

然后我们再来通过REST API访问这个pod,使用的命令如下:

1
2
[root@server02 ~]# curl http://localhost:8001/api/v1/proxy/namespaces/default/pods/kubernetes-bootcamp-6b7849c495-mgzpp/
Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6b7849c495-mgzpp | v=1

这样我们就成功的访问了这个服务。接着我们停掉这个kubectl proxy,开始学习扩缩容相关内容。首先我们查看一下当前所有的deployments信息:

1
2
3
[root@server02 ~]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1 1 1 1 1d

接着我们来对这个deployments进行扩缩容,设置4个副本数,使用的命令如下:

1
2
[root@server02 ~]# kubectl scale deploy kubernetes-bootcamp --replicas=4
deployment "kubernetes-bootcamp" scaled

然后我们来看一下当前所有的pods信息:

1
2
3
4
5
6
[root@server02 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kubernetes-bootcamp-6b7849c495-85bvf 1/1 Running 0 3m 172.20.188.1 192.168.51.121
kubernetes-bootcamp-6b7849c495-ddcnq 1/1 Running 0 3m 172.20.188.2 192.168.51.121
kubernetes-bootcamp-6b7849c495-mgzpp 1/1 Running 1 1d 172.20.40.195 192.168.51.123
kubernetes-bootcamp-6b7849c495-nqwdv 1/1 Running 0 3m 172.20.40.196 192.168.51.123

现在4个实例都已经启动了,可以看到在121上运行了2个实例,在123上也运行了2个实例,也就是说Kubernetes会尽可能的分散资源,不会在一台机器上运行很多,而在另一台机器上运行很少。接下来我们使用同样的命令,将其缩容为2个:

1
2
[root@server02 ~]# kubectl scale deploy kubernetes-bootcamp --replicas=2
deployment "kubernetes-bootcamp" scaled

之后再来查看一下当前所有的pods信息:

1
2
3
4
[root@server02 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kubernetes-bootcamp-6b7849c495-mgzpp 1/1 Running 1 1d 172.20.40.195 192.168.51.123
kubernetes-bootcamp-6b7849c495-nqwdv 1/1 Running 0 10m 172.20.40.196 192.168.51.123

接下来学习如何更新镜像,首先查看一下当前列表中所有的deployments信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@server02 ~]# kubectl describe deployments
Name: kubernetes-bootcamp
Namespace: default
CreationTimestamp: Mon, 29 Aug 2022 22:30:58 +0800
Labels: run=kubernetes-bootcamp
Annotations: deployment.kubernetes.io/revision=1
Selector: run=kubernetes-bootcamp
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: run=kubernetes-bootcamp
Containers:
kubernetes-bootcamp:
Image: jocatalin/kubernetes-bootcamp:v1
Port: 8080/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
OldReplicaSets: <none>
NewReplicaSet: kubernetes-bootcamp-6b7849c495 (2/2 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 12m deployment-controller Scaled up replica set kubernetes-bootcamp-6b7849c495 to 4
Normal ScalingReplicaSet 4m deployment-controller Scaled down replica set kubernetes-bootcamp-6b7849c495 to 2

可以看到这个名为kubernetes-bootcamp的deployments所使用的的镜像为jocatalin/kubernetes-bootcamp:v1,接下来我们尝试更新这个镜像:

1
2
[root@server02 ~]# kubectl set image deploy kubernetes-bootcamp kubernetes-bootcamp=jocatalin/kubernetes-bootcamp:v2
deployment "kubernetes-bootcamp" image updated

注意使用这个命令的时候,默认deployment的名称和容器的名称是一样的。然后使用如下命令来查看命令的更新结果:

1
2
[root@server02 ~]# kubectl rollout status deploy kubernetes-bootcamp
deployment "kubernetes-bootcamp" successfully rolled out

从执行结果中可以看出它更新成功了,然后我们继续使用kubectl describe deployments命令来确认已经更新成功:

如果开发者对某一deployment执行了错误的命令,现在想回滚到上一步的操作状态,说白了就是撤销当前的操作,可以使用kubectl rollout undo deploy +deployment的名称这一命令。之后可以使用kubectl rollout status deploy +deployment的名称这一命令来查看当前执行状态。

上面学习的都是使用命令来管理服务,但是当服务较多时,还使用命令就显得捉襟见肘了,此时就可以通过配置文件来进行管理。

回到主节点所在机器上,在/home/envy目录下新建一个名为services的目录,然后在这个services目录中新建一个名为nginx-pod.yaml文件。一般来说,我们都会使用yaml格式的文件来创建k8s服务的配置文件。nginx-pod.yaml文件里面的内容如下所示:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

以下是对上述配置文件中一些配置项的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 版本号
apiVersion: v1
# 类型
kind: Pod
# 元数据
metadata:
name: nginx
# 比较特别的配置
spec:
containers: # 容器
- name: nginx # 容器名称
image: nginx:1.7.9 # 容器所使用的镜像版本信息
ports:
- containerPort: 80 # 容器端口

然后我们尝试使用kubectl create命令从指定文件或者输入中创建一个资源,当然这里就是从nginx-pod.yaml配置文件中来创建一个名为nginx的Pod:

1
2
[root@server02 services]# kubectl create -f nginx-pod.yaml 
pod "nginx" created

然后我们来看一下这个Pod的信息:

1
2
3
4
5
[root@server02 services]# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-7689dc585d-rlz44 1/1 Running 0 1h
kubernetes-bootcamp-7689dc585d-z2678 1/1 Running 0 1h
nginx 0/1 ImagePullBackOff 0 1m

我们再来看一下deployment,可以发现不存在新的deployment,这是因为我们创建的是一个Pod,一个Pod就是一个Pod,这个和deployment没有任何关系:

1
2
3
[root@server02 services]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 2 2 2 2 1d

但是当我们使用kubectl run命令时,它会创建一个deployment,而不只是仅仅创建一个Pod而已。

再来看一下当前所有的Pods,可以看到我们前面创建的Pods已经创建完成并启动了:

1
2
3
4
NAME                                   READY     STATUS             RESTARTS   AGE
kubernetes-bootcamp-7689dc585d-rlz44 1/1 Running 0 1h
kubernetes-bootcamp-7689dc585d-z2678 1/1 Running 0 1h
nginx 1/1 Running 0 1m

现在问题来了,我们该如何去访问这个nginx呢?还是采用之前的方式,在当前窗口执行kubectl proxy命令,然后新开一个终端执行如下命令:

1
[root@server02 ~]# curl http://localhost:8001/api/v1/proxy/namespaces/default/pods/nginx/

接下来我们尝试创建一个nginx的deployment,在/home/envy/services目录中新建一个名为nginx-deployment.yaml的配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

以下是对上述配置文件中一些配置项的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 版本号,注意固定格式
apiVersion: apps/v1beta1
# 类型
kind: Deployment
# 元数据
metadata:
name: nginx-deployment
# 比较特别的配置
spec:
replicas: 2 # 两个副本
template: # 模板信息
metadata: #模板元数据
labels: # 标签
app: nginx
spec: # 比较特别的配置
containers: # 容器
- name: nginx # 容器名称
image: nginx:1.7.9 # 容器所使用的镜像版本信息
ports:
- containerPort: 80 # 容器端口

之后我们继续使用之前的命令来创建一个deployment:

1
2
[root@server02 services]# kubectl create -f nginx-deployment.yaml 
deployment "nginx-deployment" created

然后我们查看一下当前所有的deployments,使用的命令如下:

1
2
3
4
[root@server02 services]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 2 2 2 2 1d
nginx-deployment 2 2 2 1 1m

可以看到这个nginx-deployment目前只有一个,因为它正在去另一个服务器上下载,目前只有一个节点下载了这个镜像。然后我们还可以查看一下所有的pods:

1
2
3
4
5
6
7
[root@server02 services]# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-7689dc585d-rlz44 1/1 Running 0 1h
kubernetes-bootcamp-7689dc585d-z2678 1/1 Running 0 1h
nginx 1/1 Running 0 31m
nginx-deployment-6c54bd5869-q5z6c 0/1 ImagePullBackOff 0 3m
nginx-deployment-6c54bd5869-rt4zh 1/1 Running 0 3m

这里主要关心以nginx-deployment开头的两个deployment,可以看到一个正在运行,另一个正在拉取镜像。我们还可以使用如下命令来查看指定标签的Pods:

1
2
3
4
[root@server02 services]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deployment-6c54bd5869-q5z6c 0/1 ImagePullBackOff 0 5m
nginx-deployment-6c54bd5869-rt4zh 1/1 Running 0 5m

可以看到这里就只是查看以app=nginx为标签的pods的信息,service的原理其实也是根据这个label(标签)来筛选出它所想要服务的Pod,然后实现一个负载均衡。

最后可以看到这两个以app=nginx为标签的pods已经成功启动了:

1
2
3
4
[root@server02 services]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deployment-6c54bd5869-q5z6c 1/1 Running 0 10m
nginx-deployment-6c54bd5869-rt4zh 1/1 Running 0 10m

接下来我们尝试在这基础的集群之上,再给它添加两个功能,也就是kube-proxy和kube-dns。当开发者需要给集群增加service功能,此时就需要增加kube-proxy组件;当需要dns,通过名称解析服务这样的功能,此时就可以增加kube-dns组件。k8s会把一些不必要的功能做成一个组件,这样有效的降低了各个组件之间的耦合程度。

给集群增加service功能–kube-proxy(工作节点执行)

简介

每台工作节点上都应该运行一个kube-proxy服务,它负责监听APIServer中service和endpoint的变化,并通过iptables等来为服务配置负载均衡,是让服务在集群外可以被访问到的重要方式。

部署

kube-proxy的部署方式也是通过系统服务,部署流程跟etcd完全一样,操作如下:

1
2
3
4
5
6
7
8
9
10
11
12
# 确保工作目录存在
$ mkdir -p /var/lib/kube-proxy
#复制kube-proxy服务配置文件到系统服务目录
$ cp /home/envy/kubernetes-starter/target/worker-node/kube-proxy.service /lib/systemd/system/
#复制kube-proxy依赖的配置文件
$ cp /home/envy/kubernetes-starter/target/worker-node/kube-proxy.kubeconfig /etc/kubernetes/
#enable服务
$ systemctl enable kube-proxy.service
# 启动服务
$ service kube-proxy start
# 查看服务日志,看是否有错误信息,确保服务正常
$ journalctl -f -u kube-proxy.service

然后我们查看一下监听的端口号,看到10249和10256端口都是正常启动的,说明配置是成功的。

1
$ netstat -ntlp

接下来我们看一下这个kube-proxy.service文件的内容,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target
[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/home/envy/bin/kube-proxy \
--bind-address=192.168.51.121 \
--hostname-override=192.168.51.121 \
--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig \
--logtostderr=true \
--v=2
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

这里主要关注Service部分,其中WorkingDirectory是kube-proxy的工作目录,ExecStart是启动的命令,/home/envy/bin/kube-proxy则是需要启动的程序名称,通过--bind-address参数来设置监听的地址,kubeconfig则是它所依赖的配置文件,描述了kube-proxy如何访问api-server。

下面是kube-proxy.kubeconfig配置文件的内容,它配置了kube-proxy如何访问api-server,内容与kubelet雷同,不再赘述。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
clusters:
- cluster:
server: http://192.168.51.122:8080
name: kubernetes
contexts:
- context:
cluster: kubernetes
name: default
current-context: default
kind: Config
preferences: {}
users: []

这样我们就给集群添加了kube-proxy,那么问题来了,我们该如何使用它呢?往下看,回到主节点,首先使用如下命令来查看所有的服务:

1
2
3
[root@server02 services]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 3d

注意这个服务是APIServer在启动的时候就默认创建的服务,它的作用是什么呢?我们先看一下它有什么东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@server02 services]# kubectl describe service kubernetes
Name: kubernetes
Namespace: default
Labels: component=apiserver
provider=kubernetes
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP: 10.68.0.1
Port: https 443/TCP
TargetPort: 6443/TCP
Endpoints: 192.168.51.122:6443
Session Affinity: ClientIP
Events: <none>

可以看到它的Type是ClusterIP,而且IP地址为10.68.0.1,相当于给APIServer做成了一个服务,它有什么作用呢?第一,集群内其他的组件可通过这个IP来访问到APIServer,这样它就不需要依赖于具体的IP地址;第二,负载均衡,它可以用来负责APIServer这一组件的高可用,当然前提是我们有多个APIServer实例的时候,然后他就可以通过这个Server IP来实现负载均衡,并且会自动过滤掉不可用的节点,进而实现APIServer的高可用。

其实我们自己也可以创建service,首先查看一下当前所有的deployments:

1
2
3
4
[root@server02 ~]# kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 2 2 2 2 2d
nginx-deployment 2 2 2 2 23h

可以看到这里有两个deployments,之前我们访问这两个deployments的时候,使用的都是代理的方式,即kubectl proxy,开一个HTTP的EndPoint,然后通过这个EndPoint的地址去访问这个Pod。接下来我们看看kube-proxy如何给我们带来方便,让我们如何访问这个Pod。可使用如下命令:

1
2
[root@server02 ~]# kubectl expose deploy kubernetes-bootcamp --type="NodePort" --target-port=8080 --port=80
service "kubernetes-bootcamp" exposed

该命令说明为kubectl expose deploy deployment名称 --type="NodePort" --target-port=容器端口 --port=Service端口,接着我们再次使用之前的命令来获取当前所有的service:

1
2
3
4
[root@server02 ~]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 3d
kubernetes-bootcamp NodePort 10.68.37.19 <none> 80:30735/TCP 1m

可以看到这里多了一个service,它的类型为NodePort,所分配的IP地址为10.68.37.19,注意这里的端口映射,将80端口映射到30735,这个30735是随机的端口号,查看一下这个30735端口是否为监听端口:

1
2
[root@server02 ~]# netstat -ntlp|grep 30735
[root@server02 ~]#

可以看到这个30735不是监听端口,但是这个端口应该是监听端口才是,为什么这里没有监听呢?是因为当前节点是master节点,而不是工作节点,它并没有kube-proxy。我们去另一台机器,如123机器,反正只要是工作节点就可以,之后查看 一下这台机器上的30735端口是否为监听端口,可以看到它确实是监听者。

这个30735是随机的端口号,也就是kube-proxy实际在节点上启动的一个端口,可以通过这个端口来访问我们的服务。上述命令中的target-port则是当前容器,也就是Pod它真正提供服务的端口,也就是说这个容器实际上监听的端口是8080。port则是我们在这个CLUSTER-IP虚拟IP上去访问这个服务的时候需要提供的端口,这里提供的则是80端口。也就是说此时我们可以通过Service的CLUSTER-IP加上80服务端口,或者是通过NodeIP+NodePort去访问这个服务,这是两种方式。

接下来我们看一下当前所有的Pod信息:

1
2
3
4
5
6
7
[root@server02 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kubernetes-bootcamp-7689dc585d-x7bft 1/1 Running 0 55m 172.20.188.12 192.168.51.121
kubernetes-bootcamp-7689dc585d-z2678 1/1 Running 2 2d 172.20.188.10 192.168.51.121
nginx 1/1 Running 2 1d 172.20.188.9 192.168.51.121
nginx-deployment-6c54bd5869-chjwn 1/1 Running 0 55m 172.20.188.13 192.168.51.121
nginx-deployment-6c54bd5869-rt4zh 1/1 Running 2 1d 172.20.188.11 192.168.51.121

从原理上来说,上述pod列表中的任意一个pod所包含的容器,都是可以访问到CLUSTER-IP,并且它们之间也都是可以互相连通的,这是Kubernetes在设计上所做的要求,前提是我们的环境是正常的。此处以192.168.51.121节点上容器为例,我们查看其中包含bootcamp的容器,可以看到它有多个,选择一个id为0b4380c9fb67的容器,并进入到该容器,直接访问CLUSTER-IP加上80端口,可以看到它是可以访问这个服务的:

1
2
[root@server01 ~]# docker exec -it 0b4380c9fb67 bash
root@kubernetes-bootcamp-7689dc585d-x7bft:/# curl 10.68.37.19:80

同样的我们可以在这个容器中尝试看一下是否可以连通其他的Pod,可以选择之前的nginx,它的IP地址为172.20.188.9,可以看到访问结果如下:

回到主节点,在前面我们创建的服务,它的NodePort是随机的,也就是前面多次提到的30735,我们希望将这个端口给固定下来,即我们给它指定一个NodePort,这也是可以的,但是需要我们通过配置文件的方式来进行设置。

截止到目前,/home/envy/services目录下已经有两个配置文件了:

1
2
[root@server02 services]# ls
nginx-deployment.yaml nginx-pod.yaml

接下来我们创建一个新的配置文件,名为nginx-service.yaml,里面的内容如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
ports:
- port: 8080
targetPort: 80
nodePort: 20000
selector:
app: nginx
type: NodePort

以下是对上述配置文件中一些配置项的说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 版本号,注意固定格式
apiVersion: v1
# 类型
kind: Service
# 元数据
metadata:
name: nginx-service
# 比较特别的配置
spec:
ports: # 端口
- port: 8080 # Service端口(即CLUSTRER_IP所对应的端口)
targetPort: 80 # 容器端口(即具体的Nginx服务所对应的端口)
nodePort: 20000 # 节点端口(即能对节点外部提供服务的端口)
selector: # 给谁提供这样的服务,需要指定具体的Pod
app: nginx # 通过app=nginx进行指定
type: NodePort # 类型,默认的类型是CLUSTRER_IP,此处使用NodePort

之后我们继续使用之前的命令来创建一个service:

1
2
[root@server02 services]# kubectl create -f nginx-service.yaml 
service "nginx-service" created

然后我们再来查看当前所有的service:

1
2
3
4
5
[root@server02 services]# kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 5d
kubernetes-bootcamp NodePort 10.68.37.19 <none> 80:30735/TCP 1d
nginx-service NodePort 10.68.235.19 <none> 8080:20000/TCP 1m

可以看到这里多了一个名为nginx-service的service,而且类型为NodePort,它也有一个CLUSTER-IP,重要的是它的端口映射为8080:20000,即8080为服务的端口,而20000则是节点的端口。然后我们尝试在主节点,使用192.168.51.121:2000来访问121工作节点的nginx服务,可以看到可以访问成功了。同理也可以使用192.168.51.123:2000来访问123工作节点的nginx服务,这也是可以的。通过命令和配置文件方式创建出来的service都是一样的,没有任何区别,只是Kubernetes给我们提供了不同的操作方式而已。

给集群增加dns功能–kube-dns(以app方式启动)

简介

kube-dns为Kubernetes集群提供命名服务,主要用于解析集群服务名和Pod的hostname,目的是让pod可以通过名字访问到集群内的服务。它通过添加A记录的方式实现名字和service的解析,其中普通的service会解析到service-ip,而headless的service则会解析到pod列表,并且实现负载均衡。

部署

kube-dns则是通过Kubernetes应用的方式进行部署,其中我们之前生成的target文件里面kube-dns.yaml的配置文件与官方基本上一致,除了镜像名称不同而已。

kube-dns这个服务比较特殊的一点在于它的namespace为kube-system,而不是main这个空间中。这个服务所涉及到的东西比较多,如果我们手动创建它的配置文件,这其实是很难的,一般做法是直接使用官方提供的文件,然后在里面根据实际情况进行配置,多个组件之间使用---进行区分。

查看一下这个/home/envy/kubernetes-starter/target/services/kube-dns.yaml配置文件中的信息,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
---
#ConfigMap是我们见到的一个新类型,顾名思义是做配置管理的,这里用作kube-dns配置存储
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: EnsureExists

---
#认证授权使用,这里未用到
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-dns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: Reconcile

---
#dns服务
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "KubeDNS"
spec:
selector:
#选择器,一个服务包含了哪些pods
k8s-app: kube-dns
#服务的clusterip,需要跟kubelet保持一致
clusterIP: 10.68.0.2
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP

---
#具体的pod定义,包含了三个容器
apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
addonmanager.kubernetes.io/mode: Reconcile
spec:
strategy:
rollingUpdate:
maxSurge: 10%
maxUnavailable: 0
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ''
spec:
tolerations:
- key: "CriticalAddonsOnly"
operator: "Exists"
volumes:
- name: kube-dns-config
configMap:
name: kube-dns
optional: true
containers:
#实现dns解析功能
- name: kubedns
image: registry.cn-hangzhou.aliyuncs.com/imooc/k8s-dns-kube-dns-amd64:1.14.5
resources:
# TODO: Set memory limits when we've profiled the container for large
# clusters, then set request = limit to keep this container in
# guaranteed class. Currently, this container falls into the
# "burstable" category so the kubelet doesn't backoff from restarting it.
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
livenessProbe:
httpGet:
path: /healthcheck/kubedns
port: 10054
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /readiness
port: 8081
scheme: HTTP
# we poll on pod startup for the Kubernetes master service and
# only setup the /readiness HTTP server once that's available.
initialDelaySeconds: 3
timeoutSeconds: 5
args:
- --domain=cluster.local.
- --dns-port=10053
- --config-dir=/kube-dns-config
#访问kube-apiserver的地址
- --kube-master-url=http://192.168.51.122:8080
- --v=2
env:
- name: PROMETHEUS_PORT
value: "10055"
ports:
- containerPort: 10053
name: dns-local
protocol: UDP
- containerPort: 10053
name: dns-tcp-local
protocol: TCP
- containerPort: 10055
name: metrics
protocol: TCP
volumeMounts:
- name: kube-dns-config
mountPath: /kube-dns-config
#dnsmasq类似一个dns缓存,用于提高访问效率
- name: dnsmasq
image: registry.cn-hangzhou.aliyuncs.com/imooc/k8s-dns-dnsmasq-nanny-amd64:1.14.5
livenessProbe:
httpGet:
path: /healthcheck/dnsmasq
port: 10054
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
args:
- -v=2
- -logtostderr
- -configDir=/etc/k8s/dns/dnsmasq-nanny
- -restartDnsmasq=true
- --
- -k
- --cache-size=1000
- --log-facility=-
- --server=/cluster.local./127.0.0.1#10053
- --server=/in-addr.arpa/127.0.0.1#10053
- --server=/ip6.arpa/127.0.0.1#10053
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
# see: https://github.com/kubernetes/kubernetes/issues/29055 for details
resources:
requests:
cpu: 150m
memory: 20Mi
volumeMounts:
- name: kube-dns-config
mountPath: /etc/k8s/dns/dnsmasq-nanny
#sidecar是一个监控功能,负责监控另外两个容器的运行
- name: sidecar
image: registry.cn-hangzhou.aliyuncs.com/imooc/k8s-dns-sidecar-amd64:1.14.5
livenessProbe:
httpGet:
path: /metrics
port: 10054
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
args:
- --v=2
- --logtostderr
- --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.cluster.local.,5,A
- --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.cluster.local.,5,A
ports:
- containerPort: 10054
name: metrics
protocol: TCP
resources:
requests:
memory: 20Mi
cpu: 10m
dnsPolicy: Default # Don't use cluster DNS.
serviceAccountName: kube-dns

之后执行如下命令即可启动kube-dns服务:

1
$ kubectl create -f /home/envy/kubernetes-starter/target/services/kube-dns.yaml

可以看到输出信息如下:

1
2
3
4
5
[root@server02 services]# kubectl create -f /home/envy/kubernetes-starter/target/services/kube-dns.yaml
configmap "kube-dns" created
serviceaccount "kube-dns" created
service "kube-dns" created
deployment "kube-dns" created

configMap用于配置管理,serviceaccount也是一种类型,主要用于Kubernetes的认证与授权,这里未使用到。

然后我们就可以使用如下命令来查看这个名为kube-system的namespace中的服务信息:

1
2
3
[root@server02 services]# kubectl -n kube-system get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.68.0.2 <none> 53/UDP,53/TCP 3m

可以看到这里面有一个CLUSTER-IP,这个就是之前我们在创建kubelet的时候所配置的地址,这样在使用kube-dns的时候它就会自动生效。注意我们在kube-dns的yaml配置文件中也需要配置一个与它完全一样的地址。

再来看一下这个名为kube-system的namespace中的deployment信息:

1
2
3
[root@server02 ~]# kubectl -n kube-system get deploy 
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kube-dns 1 1 1 1 9h

然后看一下这个名为kube-system的namespace中的pod信息:

1
2
3
[root@server02 ~]# kubectl -n kube-system get pods
NAME READY STATUS RESTARTS AGE
kube-dns-7d7b4f767d-6xnnm 3/3 Running 3 9h

可以看到这个Pod中运行了三个容器,它们都是Running状态,接下来我们看一下这些容器运行在哪些机器上:

1
2
3
[root@server02 ~]# kubectl -n kube-system get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
kube-dns-7d7b4f767d-6xnnm 3/3 Running 3 9h 172.20.40.202 192.168.51.123

可以看到它们都运行在192.168.51.123这台机器上,接下来我们我们先查看这个192.168.51.123机器上运行的容器,然后进入到某一容器中,使用curl nginx-service:8080来访问之前创建的名为nginx-service的service。可以看到它是可以访问的,这就说明dns确实给我们提供了解析的功能,通过名字解析到了对应的CLUSTER-IP。也就是说其实它最终访问的还是CLUSTER-IP:SERVICED_PORT。然后我们还可以查看当前容器的dns配置文件:

1
2
3
4
root@kubernetes-bootcamp-7689dc585d-x7bft:/# cat /etc/resolv.conf 
nameserver 10.68.0.2
search default.svc.cluster.local. svc.cluster.local. cluster.local.
options ndots:5

可以看到它第一个nameserver的值就是10.68.0.2,实际上就是通过这个值来解析的,这也可以证实为什么我们通过名字就可以映射到CLUSTER-IP。