k8s学习

Kubernetes(K8S) 入门进阶实战完整教程,黑马程序员K8S全套教程
更多看 Kubernetes详细教程.md

常用命令

k8s常用命令(需要可查ctrl+f)

#查看pod
kubectl get pod -n <name space> #查看pod有哪些label
kubectl get pod --show-labels -n <namespace>#查看service(查看其它资源也类似)
kubectl get svc -n <namespace>#查看已运行资源的yml
kubectl get pod -o yaml#应用yaml文件(配置修改,版本修改后执行)
kubectl  apply -f xxx.yml (如果要滚动更新,必须使用该命令)#如果k8s的配置文件没有变动(注意不是业务的配置文件),可以用这个方法快速重启服务
kubectl delete pod ( -n {namespace} ) {pod-name}#重启多个pod
kubectl delete pod <pod1>  <pod2>#重启一组pod
kubectl delete pod -l app=biz-sense-metro #停止服务
kubectl delete -f 配置文件or配置文件目录#启动服务
kubectl apply -f 配置文件or配置文件目录#重新部署
kubectl delete -f .  &&  kubectl apply -f .#查看pod详细信息
kubectl describe pod <pod_name>#查看node详细信息
kubectl describe nodes#从pod拷贝文件
kubectl cp mysql-478535978-1dnm2:/tmp/message.log message.log#查看资源类型及简写
kubectl api-resources #进入容器的交互式shell
kubectl exec -it -n <namespace>  <pod>  bash (或sh)#查看pod当前资源使用情况
kubectl top pod_name#查看某个资源的字段意义
kubectl explain deployment.spec.template.spec.dnsPolicy#清理docker镜像
docker system prune -af (注意:这个命令会把你暂时关闭的容器,以及暂时没有用到的 Docker 镜像都删掉)#查看pod中各组件的版本号
kubectl describe pod -n namespace podname | grep "Image:" | sort -u       sort -u  表示去重

重点说明

一个pod可以包含多个container,共享网络、ip、端口
ip连通性说明:pod ip,service ip在pod和node层面都可以连通,在集群外部无法连通
端口说明:node port用例暴露服务,以便集群外部可以访问到集群内部的服务;pod port为监听的port;service port为k8s内部service使用
域名说明:k8s支持短域名和完整长域名,短域名如:mysql-default.compnent,长域名如:mysql-default.test.svc.cluster.local,域名只在k8s的pod内可以连通,在集群node节点无法访问
deployments与pod的关系:deployments→ rs→pod,deployments通过k8s控制器,确保pod副本数在期望的状态
ingress说明:通过nodeport端口暴露服务内部访问链:pod→service name→service ip(可选)→ pod ip
外部访问链:外部服务→ ingress port+path→ pod ip修改configmap配置后,必须像pod一样
kubectl delete -f configmap
kubectl delete -f configmap
必须重启对应的pod才能生效
kubectl delete -f configmap
kubectl delete -f configmap
对于由deployments、statefulsets、daemonsets派生出来的pod,删除操作等于重启
pod重启说明:对于同一个名字的pod可以自动重启(由于错误导致的重启),没有手动重启,只有手动删除,删除后,日志消失
pod重启后资源状态说明(deployments、statefulsets、daemonsets)service ip在不重新部署的情况下不会变化

常见故障判断

查看日志:kubectl logs <pod> -n <namespace>
查看后十行日志:kubectl logs <pod> --tail=10
查看上次启动的日志(restarts次数不为0):kubectl logs <pod> -p
查看详细描述:kubectl describe pod <pod> -n <namespace>  关注Reason和Events相关内容

其他命令

开机自启动应用

# 启动chronyd服务
[root@master ~]# systemctl start chronyd
# 设置开机自启动
[root@master ~]# systemctl enable chronyd

重新加载配置

sysctl -p

设置变量,运行脚本

命令行直接输入
images=(kube-apiserver:v1.17.4etcd:3.4.3-0coredns:1.6.5
)for imageName in ${images[@]};dodocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageNamedocker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageNamedocker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
done

日志过滤

kubectl logs -n namespace podname | sed -n '/2020-01-15 12:16*/ ,/2020-01-15 22:34*/p'
kubectl logs -n namespace podname | grep "output" > /tmp/output.log

进入node

查看k8s node
k get node进入node
ssh node名称

显示运行中的Pod、Service、Deployment以及ReplicaSet的关键信息

kubectl get all

Kubernetes级别的日志

kubectl get events

kubectl命令

kubectl是kubernetes集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。kubectl命令的语法如下:

kubectl [command] [type] [name] [flags]comand:指定要对资源执行的操作,例如create、get、delete
type:指定资源类型,比如deployment、pod、service
name:指定资源的名称,名称大小写敏感
flags:指定额外的可选参数
# 查看所有pod
kubectl get pod
# 查看某个pod
kubectl get pod pod_name
# 查看某个pod,以yaml格式展示结果
kubectl get pod pod_name -o yaml
kubectl get pod pod_name -o json     以json形式显示

命令式对象管理

kubernetes允许对资源进行多种操作,可以通过–help查看详细的操作命令

kubectl --help


以一个namespace / pod的创建和删除简单演示下命令的使用

# 创建一个namespace
[root@master ~]# kubectl create namespace dev
namespace/dev created# 获取namespace
[root@master ~]# kubectl get ns
NAME              STATUS   AGE
default           Active   21h
dev               Active   21s
kube-node-lease   Active   21h
kube-public       Active   21h
kube-system       Active   21h# 在此namespace下创建并运行一个nginx的Pod
[root@master ~]# kubectl run pod --image=nginx:latest -n dev
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/pod created# 查看新创建的pod
[root@master ~]# kubectl get pod -n dev
NAME  READY   STATUS    RESTARTS   AGE
pod   1/1     Running   0          21s# 删除指定的pod
[root@master ~]# kubectl delete pod pod-864f9875b9-pcw7x
pod "pod" deleted# 删除指定的namespace
[root@master ~]# kubectl delete ns dev
namespace "dev" deleted

命令式对象配置

命令式对象配置就是使用命令配合配置文件一起来操作kubernetes资源。

1) 创建一个nginxpod.yaml,内容如下:

apiVersion: v1
kind: Namespace
metadata:name: dev---apiVersion: v1
kind: Pod
metadata:name: nginxpodnamespace: dev
spec:containers:- name: nginx-containersimage: nginx:latest

2)执行create命令,创建资源:

[root@master ~]# kubectl create -f nginxpod.yaml
namespace/dev created
pod/nginxpod created

此时发现创建了两个资源对象,分别是namespace和pod

3)执行get命令,查看资源:

[root@master ~]#  kubectl get -f nginxpod.yaml
NAME            STATUS   AGE
namespace/dev   Active   18sNAME            READY   STATUS    RESTARTS   AGE
pod/nginxpod    1/1     Running   0          17s

这样就显示了两个资源对象的信息

4)执行delete命令,删除资源:

[root@master ~]# kubectl delete -f nginxpod.yaml
namespace "dev" deleted
pod "nginxpod" deleted

此时发现两个资源对象被删除了

总结:命令式对象配置的方式操作资源,可以简单的认为:命令  +  yaml配置文件(里面是命令需要的各种参数)

声明式对象配置

声明式对象配置跟命令式对象配置很相似,但是它只有一个命令apply。

# 首先执行一次kubectl apply -f yaml文件,发现创建了资源
[root@master ~]#  kubectl apply -f nginxpod.yaml
namespace/dev created
pod/nginxpod created# 再次执行一次kubectl apply -f yaml文件,发现说资源没有变动
[root@master ~]#  kubectl apply -f nginxpod.yaml
namespace/dev unchanged
pod/nginxpod unchanged
总结:其实声明式对象配置就是使用apply描述一个资源最终的状态(在yaml中定义状态)使用apply操作资源:如果资源不存在,就创建,相当于 kubectl create如果资源已存在,就更新,相当于 kubectl patch

扩展:kubectl可以在node节点上运行吗 ?

kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点运行此命令,需要将master上的.kube文件复制到node节点上,即在master节点上执行下面操作:

scp  -r  HOME/.kube   node1: HOME/

使用推荐: 三种方式应该怎么用 ?

创建/更新资源   使用声明式对象配置 kubectl apply -f XXX.yaml
删除资源        使用命令式对象配置 kubectl delete -f XXX.yaml
查询资源        使用命令式对象管理 kubectl get(describe) 资源名称-f, --filename=[]: Filename, directory, or URL to files to use to create the resource

实战入门

Namespace

Namespace是kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。

默认情况下,kubernetes集群中的所有的Pod都是可以相互访问的。但是在实际中,可能不想让两个Pod之间进行互相的访问,那此时就可以将两个Pod划分到不同的namespace下。kubernetes通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上的"组",以方便不同的组的资源进行隔离使用和管理。

可以通过kubernetes的授权机制,将不同的namespace交给不同租户进行管理,这样就实现了多租户的资源隔离。此时还能结合kubernetes的资源配额机制,限定不同租户能占用的资源,例如CPU使用量、内存使用量等等,来实现租户可用资源的管理。

kubernetes在集群启动之后,会默认创建几个namespace

[root@master ~]# kubectl  get namespace
NAME              STATUS   AGE
default           Active   45h     #  所有未指定Namespace的对象都会被分配在default命名空间
kube-node-lease   Active   45h     #  集群节点之间的心跳维护,v1.13开始引入
kube-public       Active   45h     #  此命名空间下的资源可以被所有人访问(包括未认证用户)
kube-system       Active   45h     #  所有由Kubernetes系统创建的资源都处于这个命名空间

查看

1 查看所有的ns 命令:kubectl get ns

[root@master ~]# kubectl get ns
NAME              STATUS   AGE
default           Active   45h
kube-node-lease   Active   45h
kube-public       Active   45h
kube-system       Active   45h

2 查看指定的ns 命令:kubectl get ns ns名称

[root@master ~]# kubectl get ns default
NAME      STATUS   AGE
default   Active   45h

3 指定输出格式

kubectl get ns ns名称  -o 格式参数# kubernetes支持的格式有很多,比较常见的是wide、json、yaml
[root@master ~]# kubectl get ns default -o yaml
apiVersion: v1
kind: Namespace
metadata:creationTimestamp: "2021-05-08T04:44:16Z"name: defaultresourceVersion: "151"selfLink: /api/v1/namespaces/defaultuid: 7405f73a-e486-43d4-9db6-145f1409f090
spec:finalizers:- kubernetes
status:phase: Active

4 查看ns详情 命令:kubectl describe ns ns名称

[root@master ~]# kubectl describe ns default
Name:         default
Labels:       <none>
Annotations:  <none>
Status:       Active  # Active 命名空间正在使用中  Terminating 正在删除命名空间# ResourceQuota 针对namespace做的资源限制
# LimitRange针对namespace中的每个组件做的资源限制
No resource quota.
No LimitRange resource.

创建

创建namespace

[root@master ~]# kubectl create ns dev
namespace/dev created

删除

删除namespace

[root@master ~]# kubectl delete ns dev
namespace "dev" deleted

配置方式

首先准备一个yaml文件:ns-dev.yaml

固定写法apiVersion: v1
kind: Namespace
metadata:              元数据name: dev

然后就可以执行对应的创建和删除命令了:

创建:kubectl create -f ns-dev.yaml删除:kubectl delete -f ns-dev.yaml

Pod

Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。

Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。

kubernetes在集群启动之后,集群中的各个组件也都是以Pod方式运行的。可以通过下面命令查看:

[root@master ~]# kubectl get pod -n kube-system
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE
kube-system   coredns-6955765f44-68g6v         1/1     Running   0          2d1h
kube-system   coredns-6955765f44-cs5r8         1/1     Running   0          2d1h
kube-system   etcd-master                      1/1     Running   0          2d1h
kube-system   kube-apiserver-master            1/1     Running   0          2d1h
kube-system   kube-controller-manager-master   1/1     Running   0          2d1h
kube-system   kube-flannel-ds-amd64-47r25      1/1     Running   0          2d1h
kube-system   kube-flannel-ds-amd64-ls5lh      1/1     Running   0          2d1h
kube-system   kube-proxy-685tk                 1/1     Running   0          2d1h
kube-system   kube-proxy-87spt                 1/1     Running   0          2d1h
kube-system   kube-scheduler-master            1/1     Running   0          2d1h注意!pod name是变动的,最好现查现用

创建并运行

kubernetes没有提供单独运行Pod的命令,都是通过Pod控制器来实现的

# 命令格式: kubectl run pod控制器名称  --image=镜像:版本  --port=暴露端口  --namespace  命名空间
# --image  指定Pod的镜像
# --port   指定端口
# --namespace  指定namespace[root@master ~]# kubectl create ns dev    先创建命名空间才能在命名空间创建pod[root@master ~]# kubectl run nginx --image=nginx:latest --port=80 --namespace dev
deployment.apps/nginx created
NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
nginx-6867cdf567-6jrln   1/1     Running   0          16h   10.244.2.2   node2   <none>           <none>
NAME            pod名字
READY           当前pod中有几个容器在运行/当前pod中有几个容器
RESTARTS        重启次数
AGE             启动了多久
NODE            当前pod调度到了哪个节点运行

查看更详细的信息

[root@master ~]# kubectl describe pod nginx-dd6b5d745-sbfpj -n devName:         nginx-dd6b5d745-sbfpj
Namespace:    dev
Priority:     0
Node:         node1/192.168.182.101            调度到了哪个node
Start Time:   Tue, 14 Dec 2021 18:00:17 +0800          启动时间
Labels:       pod-template-hash=dd6b5d745run=nginx
Annotations:  <none>
Status:       Running
IP:           10.244.1.7                pod的ip
IPs:IP:           10.244.1.7
Controlled By:  ReplicaSet/nginx-dd6b5d745
Containers:                     pod中的容器们nginx:Container ID:   docker://513e75ca4bed0b3bc08c68dbffed8b0069b68049dc802107b87d4b99913c9d08Image:          nginx:latest                             使用镜像Image ID:       docker-pullable://nginx@sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603Port:           80/TCP                  容器端口Host Port:      0/TCP               主机端口State:          RunningStarted:      Tue, 14 Dec 2021 18:00:34 +0800           Ready:          TrueRestart Count:  0Environment:    <none>Mounts:                        挂载点/var/run/secrets/kubernetes.io/serviceaccount from default-token-d28j7 (ro)
Conditions:Type              StatusInitialized       TrueReady             TrueContainersReady   TruePodScheduled      True
Volumes:default-token-d28j7:Type:        Secret (a volume populated by a Secret)SecretName:  default-token-d28j7Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300snode.kubernetes.io/unreachable:NoExecute for 300s
Events:             启动事件,用这里可以排除错误Type    Reason     Age    From               Message----    ------     ----   ----               -------Normal  Scheduled  9m23s  default-scheduler  Successfully assigned dev/nginx-dd6b5d745-sbfpj to node1Normal  Pulling    9m16s  kubelet, node1     Pulling image "nginx:latest"Normal  Pulled     9m     kubelet, node1     Successfully pulled image "nginx:latest"Normal  Created    9m     kubelet, node1     Created container nginxNormal  Started    9m     kubelet, node1     Started container nginx错误run示例
[root@master ~]# kubectl run nginx1 --image=nginx:1111 --port=80 --namespace dev
[root@master ~]# kubectl describe pod nginx1-78f86cf8dd-msszb -n devEvents:Type     Reason     Age                From               Message----     ------     ----               ----               -------Normal   Scheduled  63s                default-scheduler  Successfully assigned dev/nginx1-78f86cf8dd-msszb to node2Normal   BackOff    38s                kubelet, node2     Back-off pulling image "nginx:1111"Warning  Failed     38s                kubelet, node2     Error: ImagePullBackOffNormal   Pulling    25s (x2 over 58s)  kubelet, node2     Pulling image "nginx:1111"    Warning  Failed     5s (x2 over 38s)   kubelet, node2     Failed to pull image "nginx:1111": rpc error: code = Unknown desc = Error response from daemon: manifest for nginx:1111 not found                             Warning  Failed     5s (x2 over 38s)   kubelet, node2     Error: ErrImagePull可以看到是pull image错误

访问Pod

用 -o wide

# 获取podIP
[root@master ~]# kubectl get pods -n dev -o wide
NAME    READY   STATUS    RESTARTS   AGE    IP             NODE    ...
nginx   1/1     Running   0          190s   10.244.1.23   node1   ...#访问POD
[root@master ~]# curl http://10.244.1.23:80
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title>
</head>
<body><p><em>Thank you for using nginx.</em></p>
</body>
</html>
# 动态查看pod
[root@k8s-master01 ~]# kubectl get pods pod-initcontainer -n dev -w
NAME                             READY   STATUS     RESTARTS   AGE
pod-initcontainer                0/1     Init:0/2   0          15s
pod-initcontainer                0/1     Init:1/2   0          52s
pod-initcontainer                0/1     Init:1/2   0          53s
pod-initcontainer                0/1     PodInitializing   0          89s
pod-initcontainer                1/1     Running           0          90s

删除指定Pod

# 删除指定Pod
[root@master ~]# kubectl delete pod nginx-dqewacdaeq-32fw -n dev
pod "ginx-dqewacdaeq-32fw" deleted# 此时,显示删除Pod成功,但是再查询,发现又新产生了一个
[root@master ~]# kubectl get pods -n dev
NAME                READY   STATUS    RESTARTS   AGE
nginx-feew-32few     1/1     Running   0          21s# 这是因为当前Pod(nginx-feew-32few)是由Pod控制器(ngnix)创建的,控制器会监控Pod状况,一旦发现Pod死亡,会立即重建
# 此时要想删除Pod(nginx-feew-32few),必须删除Pod控制器(nginx)# 先来查询一下当前namespace下的Pod控制器
[root@master ~]# kubectl get deploy -n  dev
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           9m7s# 接下来,删除此PodPod控制器
[root@master ~]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted# 稍等片刻,再查询Pod,发现Pod被删除了
[root@master ~]# kubectl get pods -n dev
No resources found in dev namespace.

配置操作

基本配置

创建一个pod-nginx.yaml,内容如下:

apiVersion: v1
kind: Pod           是pod不是pod控制器
metadata:name: nginx        这里是pod的名字,不是pod控制器名字namespace: dev
spec:containers:- image: nginx:latestname: podports:- name: nginx-portcontainerPort: 80protocol: TCP

然后就可以执行对应的创建和删除命令了:

创建:kubectl create -f pod-nginx.yaml删除:kubectl delete -f pod-nginx.yaml

镜像拉取

apiVersion: v1
kind: Pod
metadata:name: pod-imagepullpolicynamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1imagePullPolicy: Never # 用于设置镜像拉取策略- name: busyboximage: busybox:1.30
imagePullPolicy,用于设置镜像拉取策略,kubernetes支持配置三种拉取策略:Always:总是从远程仓库拉取镜像(一直远程下载)
IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就本地 本地没远程下载)
Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错 (一直使用本地)
默认值说明:如果镜像tag为具体版本号, 默认策略是:IfNotPresent
如果镜像tag为:latest(最终版本) ,默认策略是always
# 创建Pod
[root@k8s-master01 pod]# kubectl create -f pod-imagepullpolicy.yaml
pod/pod-imagepullpolicy created# 查看Pod详情
# 此时明显可以看到nginx镜像有一步Pulling image "nginx:1.17.1"的过程
[root@k8s-master01 pod]# kubectl describe pod pod-imagepullpolicy -n dev
......
Events:Type     Reason     Age               From               Message----     ------     ----              ----               -------Normal   Scheduled  <unknown>         default-scheduler  Successfully assigned dev/pod-imagePullPolicy to node1Normal   Pulling    32s               kubelet, node1     Pulling image "nginx:1.17.1"Normal   Pulled     26s               kubelet, node1     Successfully pulled image "nginx:1.17.1"Normal   Created    26s               kubelet, node1     Created container nginxNormal   Started    25s               kubelet, node1     Started container nginxNormal   Pulled     7s (x3 over 25s)  kubelet, node1     Container image "busybox:1.30" already present on machineNormal   Created    7s (x3 over 25s)  kubelet, node1     Created container busyboxNormal   Started    7s (x3 over 25s)  kubelet, node1     Started container busybox

启动命令

在前面的案例中,一直有一个问题没有解决,就是的busybox容器一直没有成功运行,那么到底是什么原因导致这个容器的故障呢?

原来busybox并不是一个程序,而是类似于一个工具类的集合,kubernetes集群启动管理后,它会自动关闭。解决方法就是让其一直在运行,这就用到了command配置。

创建pod-command.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-commandnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","touch /tmp/hello.txt;while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done;"]
command,用于在pod中的容器初始化完毕之后运行一个命令。稍微解释下上面命令的意思:"/bin/sh","-c", 使用sh执行命令touch /tmp/hello.txt; 创建一个/tmp/hello.txt 文件while true;do /bin/echo $(date +%T) >> /tmp/hello.txt; sleep 3; done; 每隔3秒向文件中写入当前时间
# 创建Pod
[root@k8s-master01 pod]# kubectl create  -f pod-command.yaml
pod/pod-command created# 查看Pod状态
# 此时发现两个pod都正常运行了
[root@k8s-master01 pod]# kubectl get pods pod-command -n dev
NAME          READY   STATUS   RESTARTS   AGE
pod-command   2/2     Runing   0          2s# 进入pod中的busybox容器,查看文件内容
# 补充一个命令: kubectl exec  pod名称 -n 命名空间 -it -c 容器名称 /bin/sh  在容器内部执行命令
# 使用这个命令就可以进入某个容器的内部,然后进行相关操作了
# 比如,可以查看txt文件的内容
[root@k8s-master01 pod]# kubectl exec pod-command -n dev -it -c busybox /bin/sh
/ # tail -f /tmp/hello.txt
14:44:19
14:44:22
14:44:25
特别说明:通过上面发现command已经可以完成启动命令和传递参数的功能,为什么这里还要提供一个args选项,用于传递参数呢?这其实跟docker有点关系,kubernetes中的command、args两项其实是实现覆盖Dockerfile中ENTRYPOINT的功能。1 如果command和args均没有写,那么用Dockerfile的配置。2 如果command写了,但args没有写,那么Dockerfile默认的配置会被忽略,执行输入的command3 如果command没写,但args写了,那么Dockerfile中配置的ENTRYPOINT的命令会被执行,使用当前args的参数4 如果command和args都写了,那么Dockerfile的配置被忽略,执行command并追加上args参数

环境变量

创建pod-env.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-envnamespace: dev
spec:containers:- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","while true;do /bin/echo $(date +%T);sleep 60; done;"]env: # 设置环境变量列表- name: "username"value: "admin"- name: "password"value: "123456"

env,环境变量,用于在pod中的容器设置环境变量。

# 进入容器,输出环境变量
[root@k8s-master01 ~]# kubectl exec pod-env -n dev -c busybox -it /bin/sh
/ # echo $username
admin
/ # echo $password
123456

这种方式不是很推荐,推荐将这些配置单独存储在配置文件中,这种方式将在后面介绍。

端口设置

端口设置,也就是containers的ports选项。

访问容器中的程序需要使用的是Podip:containerPort

apiVersion: v1
kind: Pod
metadata:name: pod-portsnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: # 设置容器暴露的端口列表- name: nginx-portcontainerPort: 80protocol: TCP

资源配额

容器中的程序要运行,肯定是要占用一定资源的,比如cpu和内存等,如果不对某个容器的资源做限制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes提供了对内存和cpu的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:

limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启
requests :用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动

可以通过上面两个选项设置资源的上下限。

apiVersion: v1
kind: Pod
metadata:name: pod-resourcesnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1resources: # 资源配额limits:  # 限制资源(上限)cpu: "2" # CPU限制,单位是core数memory: "10Gi" # 内存限制requests: # 请求资源(下限)cpu: "1"  # CPU限制,单位是core数memory: "10Mi"  # 内存限制cpu:core数,可以为整数或小数
memory: 内存大小,可以使用Gi、Mi、G、M等形式

pod生命周期

pod对象从创建至终的这段时间范围称为pod的生命周期,它主要包含下面的过程:

pod创建过程
运行初始化容器(init container)过程
运行主容器(main container)容器启动后钩子(post start)、容器终止前钩子(pre stop)容器的存活性探测(liveness probe)、就绪性探测(readiness probe)
pod终止过程

在整个生命周期中,Pod会出现5种状态(相位),分别如下:挂起(Pending):apiserver已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中
运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成
成功(Succeeded):pod中的所有容器都已经成功终止并且不会被重启
失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态
未知(Unknown):apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所导致

钩子函数

钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码。

kubernetes在主容器的启动之后和停止之前提供了两个钩子函数:

post start:容器创建之后执行,如果失败了会重启容器
pre stop :容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作

钩子处理器支持使用下面三种方式定义动作:

Exec命令:在容器内执行一次命令

……lifecycle:postStart: exec:command:- cat- /tmp/healthy
……

TCPSocket:在当前容器尝试访问指定的socket

……      lifecycle:postStart:tcpSocket:port: 8080
……

HTTPGet:在当前容器中向某url发起http请求

……lifecycle:postStart:httpGet:path: / #URI地址port: 80 #端口号host: 192.168.5.3 #主机地址scheme: HTTP #支持的协议,http或者https
……

接下来,以exec方式为例,演示下钩子函数的使用,创建pod-hook-exec.yaml文件,内容如下:

apiVersion: v1
kind: Pod
metadata:name: pod-hook-execnamespace: dev
spec:containers:- name: main-containerimage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80lifecycle:postStart: exec: # 在容器启动的时候执行一个命令,修改掉nginx的默认首页内容command: ["/bin/sh", "-c", "echo postStart... > /usr/share/nginx/html/index.html"]preStop:exec: # 在容器停止之前停止nginx服务command: ["/usr/sbin/nginx","-s","quit"]

容器探测

容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么kubernetes就会把该问题实例" 摘除 ",不承担业务流量。kubernetes提供了两种探针来实现容器探测,分别是:

liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器
readiness probes:就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,k8s不会转发
流量livenessProbe 决定是否重启容器,readinessProbe 决定是否将请求转发给容器。

上面两种探针目前均支持三种探测方式:

Exec命令:在容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则不正常

……livenessProbe:exec:command:- cat- /tmp/healthy
……

TCPSocket:将会尝试访问一个用户容器的端口,如果能够建立这条连接,则认为程序正常,否则不正常

……      livenessProbe:tcpSocket:port: 8080
……

HTTPGet:调用容器内Web应用的URL,如果返回的状态码在200和399之间,则认为程序正常,否则不正常

……livenessProbe:httpGet:path: / #URI地址port: 80 #端口号host: 127.0.0.1 #主机地址scheme: HTTP #支持的协议,http或者https
……

下面以liveness probes为例,做几个演示:

方式一:Exec

创建pod-liveness-exec.yaml

apiVersion: v1
kind: Pod
metadata:name: pod-liveness-execnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80livenessProbe:exec:command: ["/bin/cat","/tmp/hello.txt"] # 执行一个查看文件的命令
# 创建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-exec.yaml
pod/pod-liveness-exec created# 查看Pod详情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-exec -n dev
......Normal   Created    20s (x2 over 50s)  kubelet, node1     Created container nginxNormal   Started    20s (x2 over 50s)  kubelet, node1     Started container nginxNormal   Killing    20s                kubelet, node1     Container nginx failed liveness probe, will be restartedWarning  Unhealthy  0s (x5 over 40s)   kubelet, node1     Liveness probe failed: cat: can't open '/tmp/hello11.txt': No such file or directory# 观察上面的信息就会发现nginx容器启动之后就进行了健康检查
# 检查失败之后,容器被kill掉,然后尝试进行重启(这是重启策略的作用,后面讲解)
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master01 ~]# kubectl get pods pod-liveness-exec -n dev
NAME                READY   STATUS             RESTARTS   AGE
pod-liveness-exec   0/1     CrashLoopBackOff   2          3m19s# 当然接下来,可以修改成一个存在的文件,比如/tmp/hello.txt,再试,结果就正常了......

方式二:TCPSocket

apiVersion: v1
kind: Pod
metadata:name: pod-liveness-tcpsocketnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports: - name: nginx-portcontainerPort: 80livenessProbe:tcpSocket:port: 8080 # 尝试访问8080端口

创建pod,观察效果

# 创建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-tcpsocket.yaml
pod/pod-liveness-tcpsocket created# 查看Pod详情
[root@k8s-master01 ~]# kubectl describe pods pod-liveness-tcpsocket -n dev
......Normal   Scheduled  31s                            default-scheduler  Successfully assigned dev/pod-liveness-tcpsocket to node2Normal   Pulled     <invalid>                      kubelet, node2     Container image "nginx:1.17.1" already present on machineNormal   Created    <invalid>                      kubelet, node2     Created container nginxNormal   Started    <invalid>                      kubelet, node2     Started container nginxWarning  Unhealthy  <invalid> (x2 over <invalid>)  kubelet, node2     Liveness probe failed: dial tcp 10.244.2.44:8080: connect: connection refused# 观察上面的信息,发现尝试访问8080端口,但是失败了
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master01 ~]# kubectl get pods pod-liveness-tcpsocket  -n dev
NAME                     READY   STATUS             RESTARTS   AGE
pod-liveness-tcpsocket   0/1     CrashLoopBackOff   2          3m19s# 当然接下来,可以修改成一个可以访问的端口,比如80,再试,结果就正常了......

方式三:HTTPGet

apiVersion: v1
kind: Pod
metadata:name: pod-liveness-httpgetnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:  # 其实就是访问http://127.0.0.1:80/hello  scheme: HTTP #支持的协议,http或者httpsport: 80 #端口号path: /hello #URI地址
# 创建Pod
[root@k8s-master01 ~]# kubectl create -f pod-liveness-httpget.yaml
pod/pod-liveness-httpget created# 查看Pod详情
[root@k8s-master01 ~]# kubectl describe pod pod-liveness-httpget -n dev
.......Normal   Pulled     6s (x3 over 64s)  kubelet, node1     Container image "nginx:1.17.1" already present on machineNormal   Created    6s (x3 over 64s)  kubelet, node1     Created container nginxNormal   Started    6s (x3 over 63s)  kubelet, node1     Started container nginxWarning  Unhealthy  6s (x6 over 56s)  kubelet, node1     Liveness probe failed: HTTP probe failed with statuscode: 404Normal   Killing    6s (x2 over 36s)  kubelet, node1     Container nginx failed liveness probe, will be restarted# 观察上面信息,尝试访问路径,但是未找到,出现404错误
# 稍等一会之后,再观察pod信息,就可以看到RESTARTS不再是0,而是一直增长
[root@k8s-master01 ~]# kubectl get pod pod-liveness-httpget -n dev
NAME                   READY   STATUS    RESTARTS   AGE
pod-liveness-httpget   1/1     Running   5          3m17s# 当然接下来,可以修改成一个可以访问的路径path,比如/,再试,结果就正常了......

已经使用liveness Probe演示了三种探测方式,但是查看livenessProbe的子属性,会发现除了这三种方式,还有一些其他的配置,在这里一并解释下:

[root@k8s-master01 ~]# kubectl explain pod.spec.containers.livenessProbe
FIELDS:exec <Object>  tcpSocket    <Object>httpGet      <Object>initialDelaySeconds  <integer>  # 容器启动后等待多少秒执行第一次探测timeoutSeconds       <integer>  # 探测超时时间。默认1秒,最小1秒periodSeconds        <integer>  # 执行探测的频率。默认是10秒,最小1秒failureThreshold     <integer>  # 连续探测失败多少次才被认定为失败。默认是3。最小值是1successThreshold     <integer>  # 连续探测成功多少次才被认定为成功。默认是1initialDelaySeconds: 30 # 容器启动后30s开始探测
timeoutSeconds: 5 # 探测超时时间为5s

重启策略

一旦容器探测出现了问题,kubernetes就会对容器所在的Pod进行重启,其实这是由pod的重启策略决定的,pod的重启策略有 3 种,分别如下:

Always :容器失效时,自动重启该容器,这也是默认值。
OnFailure : 容器终止运行且退出码不为0时重启
Never : 不论状态为何,都不重启该容器

重启策略适用于pod对象中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作的延迟时长以此为10s、20s、40s、80s、160s和300s,300s是最大延迟时长。

apiVersion: v1
kind: Pod
metadata:name: pod-restartpolicynamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- name: nginx-portcontainerPort: 80livenessProbe:httpGet:scheme: HTTPport: 80path: /hellorestartPolicy: Never # 设置重启策略为Never

Pod调度

一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式:

自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration

定向调度

指的是利用在pod上声明nodeName或者nodeSelector,以此将Pod调度到期望的node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过pod运行失败而已。

NodeName

NodeName用于强制约束将Pod调度到指定的Name的Node节点上。这种方式,其实是直接跳过Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。

apiVersion: v1
kind: Pod
metadata:name: pod-nodenamenamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeName: node1 # 指定调度到node1节点上

NodeSelector

NodeSelector用于将pod调度到添加了指定标签的node节点上。它是通过kubernetes的label-selector机制实现的,也就是说,在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束。

apiVersion: v1
kind: Pod
metadata:name: pod-nodeselectornamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1nodeSelector: nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上

亲和性调度

上一节,介绍了两种定向调度的方式,使用起来非常方便,但是也有一定的问题,那就是如果没有满足条件的Node,那么Pod将不会被运行,即使在集群中还有可用Node列表也不行,这就限制了它的使用场景。

基于上面的问题,kubernetes还提供了一种亲和性调度(Affinity)。它在NodeSelector的基础之上的进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。

Affinity主要分为三类:

nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题
podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题

关于亲和性(反亲和性)使用场景的说明:

亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。
反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。

NodeAffinity

首先来看一下NodeAffinity的可配置项

pod.spec.affinity.nodeAffinityrequiredDuringSchedulingIgnoredDuringExecution  Node节点必须满足指定的所有规则才可以,相当于硬限制nodeSelectorTerms  节点选择列表matchFields   按节点字段列出的节点选择器要求列表matchExpressions   按节点标签列出的节点选择器要求列表(推荐)key    键values 值operat or 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, LtpreferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当于软限制 (倾向)preference   一个节点选择器项,与相应的权重相关联matchFields   按节点字段列出的节点选择器要求列表matchExpressions   按节点标签列出的节点选择器要求列表(推荐)key    键values 值operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Ltweight 倾向权重,在范围1-100。

关系符的使用说明:

- matchExpressions:- key: nodeenv              # 匹配存在标签的key为nodeenv的节点operator: Exists- key: nodeenv              # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点operator: Invalues: ["xxx","yyy"]- key: nodeenv              # 匹配标签的key为nodeenv,且value大于"xxx"的节点operator: Gtvalues: "xxx"
apiVersion: v1
kind: Pod
metadata:name: pod-nodeaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:  #亲和性设置nodeAffinity: #设置node亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制nodeSelectorTerms:- matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签- key: nodeenvoperator: Invalues: ["xxx","yyy"]

PodAffinity

PodAffinity主要实现以运行的Pod为参照,实现让新创建的Pod跟参照pod在一个区域的功能。

首先来看一下PodAffinity的可配置项:

pod.spec.affinity.podAffinityrequiredDuringSchedulingIgnoredDuringExecution  硬限制namespaces       指定参照pod的namespacetopologyKey      指定调度作用域labelSelector    标签选择器matchExpressions  按节点标签列出的节点选择器要求列表(推荐)key    键values 值operator 关系符 支持In, NotIn, Exists, DoesNotExist.matchLabels    指多个matchExpressions映射的内容preferredDuringSchedulingIgnoredDuringExecution 软限制podAffinityTerm  选项namespaces      topologyKeylabelSelectormatchExpressions  key    键values 值operatormatchLabels weight 倾向权重,在范围1-100
apiVersion: v1
kind: Pod
metadata:name: pod-podaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:  #亲和性设置podAffinity: #设置pod亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签- key: podenvoperator: Invalues: ["xxx","yyy"]topologyKey: kubernetes.io/hostname上面配置表达的意思是:新Pod必须要与拥有标签nodeenv=xxx或者nodeenv=yyy的pod在同一Node上

PodAntiAffinity

PodAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod跟参照pod不在一个区域中的功能。

它的配置方式和选项跟PodAffinty是一样的,这里不再做详细解释,直接做一个测试案例。

apiVersion: v1
kind: Pod
metadata:name: pod-podantiaffinity-requirednamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1affinity:  #亲和性设置podAntiAffinity: #设置pod亲和性requiredDuringSchedulingIgnoredDuringExecution: # 硬限制- labelSelector:matchExpressions: # 匹配podenv的值在["pro"]中的标签- key: podenvoperator: Invalues: ["pro"]topologyKey: kubernetes.io/hostname

污点和容忍

点这里

Label

Label是kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。

Label的特点:

一个Label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等等
一个资源对象可以定义任意数量的Label ,同一个Label也可以被添加到任意数量的资源对象上去
Label通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除

可以通过Label实现资源的多维度分组,以便灵活、方便地进行资源分配、调度、配置、部署等管理工作。

一些常用的Label 示例如下:版本标签:"version":"release", "version":"stable"......
环境标签:"environment":"dev","environment":"test","environment":"pro"
架构标签:"tier":"frontend","tier":"backend"

标签定义完毕之后,还要考虑到标签的选择,这就要使用到Label Selector,即:

Label用于给某个资源对象定义标识Label Selector用于查询和筛选拥有某些标签的资源对象

当前有两种Label Selector:

  • 基于等式的Label Selector
name = slave: 选择所有包含Label中key="name"且value="slave"的对象env != production: 选择所有包括Label中的key="env"且value不等于"production"的对象
  • 基于集合的Label Selector
name in (master, slave): 选择所有包含Label中的key="name"且value="master"或"slave"的对象name not in (frontend): 选择所有包含Label中的key="name"且value不等于"frontend"的对象
  • 标签的选择条件可以使用多个,此时将多个Label Selector进行组合,使用逗号","进行分隔即可。例如:
name=slave,env!=productionname not in (frontend),env!=production

命令方式

# 为pod资源打标签
[root@master ~]# kubectl label pod nginx-pod version=1.0 -n dev
pod/nginx-pod labeled# 为pod资源更新标签
[root@master ~]# kubectl label pod nginx-pod version=2.0 -n dev --overwrite
pod/nginx-pod labeled# 查看标签
[root@master ~]# kubectl get pod nginx-pod  -n dev --show-labels
NAME        READY   STATUS    RESTARTS   AGE   LABELS
nginx-pod   1/1     Running   0          10m   version=2.0# 筛选标签           -l  表示 --selector=''    可以通过 kubectl label --help 查看
[root@master ~]# kubectl get pod -n dev -l version=2.0  --show-labels
NAME        READY   STATUS    RESTARTS   AGE   LABELS
nginx-pod   1/1     Running   0          17m   version=2.0
[root@master ~]# kubectl get pod -n dev -l version!=2.0 --show-labels
No resources found in dev namespace.#删除标签
[root@master ~]# kubectl label pod nginx-pod version- -n dev
pod/nginx-pod labeled

配置方式

apiVersion: v1
kind: Pod
metadata:name: nginxnamespace: dev labels:                #配置文件加标签version: "3.0"     env: "test"
spec:containers:- image: nginx:latestname: podports:- name: nginx-portcontainerPort: 80protocol: TCP

然后就可以执行对应的更新命令了:kubectl apply -f pod-nginx.yaml

Deployment

在kubernetes中,Pod是最小的控制单元,但是kubernetes很少直接控制Pod,一般都是通过Pod控制器来完成的。Pod控制器用于pod的管理,确保pod资源符合预期的状态,当pod的资源出现故障时,会尝试进行重启或重建pod。

在kubernetes中Pod控制器的种类有很多,本章节只介绍一种:Deployment。

流程

用户通过 kubectl 创建 deployment
deployment 创建 replicaset
repliset 创建 pod

命令操作

deploy   deployment  deployments.apps
是一样的
# 命令格式: kubectl create deployment 名称  [参数]
# --image  指定pod的镜像
# --port   指定端口
# --replicas  指定创建pod数量
# --namespace  指定namespace# 运行一个deployments,镜像为nginx,副本数为3 port 80 namespace dev
[root@master ~]# kubectl run nginx --image=nginx:latest --port=80 --replicas=3 -n dev
deployment.apps/nginx created# 查看创建的Pod
[root@master ~]# kubectl get pods -n dev
NAME                     READY   STATUS    RESTARTS   AGE
nginx-5ff7956ff6-6k8cb   1/1     Running   0          19s
nginx-5ff7956ff6-jxfjt   1/1     Running   0          19s
nginx-5ff7956ff6-v6jqw   1/1     Running   0          19s# 查看deployment的信息
[root@master ~]# kubectl get deploy -n dev
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   3/3     3            3           2m42s#同时查看deploy和pod
kubectl get deploy,pod -n dev# UP-TO-DATE:成功升级的副本数量
# AVAILABLE:可用副本的数量
[root@master ~]# kubectl get deploy -n dev -o wide
NAME    READY UP-TO-DATE  AVAILABLE   AGE     CONTAINERS   IMAGES              SELECTOR
nginx   3/3     3         3           2m51s   nginx        nginx:latest        run=nginx# 查看deployment的详细信息
[root@master ~]# kubectl describe deploy nginx -n dev
Name:                   nginx        pod控制器名称
Namespace:              dev          命名空间
CreationTimestamp:      Wed, 08 May 2021 11:14:14 +0800
Labels:                 run=nginx          标签
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               run=nginx          标签选择器
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:Labels:  run=nginxContainers:nginx:Image:        nginx:latestPort:         80/TCPHost Port:    0/TCPEnvironment:  <none>Mounts:       <none>Volumes:        <none>
Conditions:Type           Status  Reason----           ------  ------Available      True    MinimumReplicasAvailableProgressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-5ff7956ff6 (3/3 replicas created)
Events:Type    Reason             Age    From                   Message----    ------             ----   ----                   -------Normal  ScalingReplicaSet  5m43s  deployment-controller  Scaled up replicaset nginx-5ff7956ff6 to 3这里告诉我们创建了一个 NewReplicaSet: nginx-5ff7956ff6 (3/3 replicas created),证明deoloyments是通过replicaset管理pod # 删除 deployment 同时也会删除相应的 pod ,相当于强制删除了 pod
[root@master ~]# kubectl delete deploy nginx -n dev
deployment.apps "nginx" deleted

通过 deployment 更新镜像

set image

$ kubectl get deployments
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE
deploy-hello-server   2/2     2            2           17m$ kubectl describe deployments deploy-hello-server
Name:                   deploy-hello-server
Namespace:              default
CreationTimestamp:      Thu, 03 Jun 2021 03:19:59 -0400
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=deploy-hello-server
...
Pod Template:Labels:  app=deploy-hello-serverContainers:hello-server:Image:        wssiqi/hello-server:v1     //!!!!可以看到这个deployment的image只有wssiqi/hello-server:v1Port:         <none>
...

我们将这个image升级为 wssiqi/hello-server:v2

$ kubectl set image deployments/deploy-hello-server hello-server=wssiqi/hello-server:v2
deployment.apps/deploy-hello-server image updateddeployments/deploy-hello-server: deployments/ 为固定写法 deploy-hello-server 来自于 Name: deploy-hello-serverhello-server=wssiqi/hello-server:v2: hello-server 是 container 名字,来自于 Containers: hello-serverwssiqi/hello-server:v2 表示将containerhello-server的image设置为v2版本
检查是否已经更新完成
$ kubectl describe deployments deploy-hello-server | grep ImageImage:        wssiqi/hello-server:v2

通过deployment.yaml文件来更新image

deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:name: deploy-hello-server
spec:selector:matchLabels:app: deploy-hello-serverreplicas: 2template:metadata:labels:app: deploy-hello-serverspec:containers:- image: wssiqi/hello-server:v3           //更新这里name: hello-server

应用新的deployment

$ kubectl apply -f deployment.yaml
deployment.apps/deploy-hello-server configured$ kubectl get deployments -o wide
NAME                  READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS     IMAGES                   SELECTOR
deploy-hello-server   2/2     2            2           36m   hello-server   wssiqi/hello-server:v3   app=deploy-hello-server

配置操作

创建一个deploy-nginx.yaml,内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:name: nginxnamespace: dev
spec:replicas: 3selector:matchLabels:run: nginxtemplate:metadata:labels:run: nginxspec:containers:- image: nginx:latestname: nginxports:- containerPort: 80protocol: TCP

然后就可以执行对应的创建和删除命令了:

创建:kubectl create -f deploy-nginx.yaml删除:kubectl delete -f deploy-nginx.yaml

Pod控制器介绍

Pod是kubernetes的最小管理单元,在kubernetes中,按照pod的创建方式可以将其分为两类:

自主式pod:kubernetes直接创建出来的Pod,这种pod删除后就没有了,也不会重建
控制器创建的pod:kubernetes通过控制器创建的pod,这种pod删除了之后还会自动重建

Pod控制器是管理pod的中间层,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod。

ReplicaSet(RS)

ReplicaSet的主要作用是保证一定数量的pod正常运行,它会持续监听这些Pod的运行状态,一旦Pod发生故障,就会重启或重建。同时它还支持对pod数量的扩缩容和镜像版本的升降级。

kubectl get rs
查看replicasetkubectl describe replicasets.apps  nginx-6db489d4b7Name:           nginx-6db489d4b7
Namespace:      default
Selector:       pod-template-hash=6db489d4b7,run=nginx
Labels:         pod-template-hash=6db489d4b7run=nginx
Annotations:    deployment.kubernetes.io/desired-replicas: 2deployment.kubernetes.io/max-replicas: 3deployment.kubernetes.io/revision: 1
Controlled By:  Deployment/nginx      //这里表示是replicasets是由deployment、nginx创建
Replicas:       2 current / 2 desired
Pods Status:    2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:Labels:  pod-template-hash=6db489d4b7run=nginxContainers:nginx:Image:        nginxPort:         <none>Host Port:    <none>Environment:  <none>Mounts:       <none>Volumes:        <none>
Events:Type    Reason            Age    From                   Message----    ------            ----   ----                   -------Normal  SuccessfulCreate  3m56s  replicaset-controller  Created pod: nginx-6db489d4b7-ghzd9Normal  SuccessfulCreate  3m56s  replicaset-controller  Created pod: nginx-6db489d4b7-7f6jb
查看podkubectl describe pod nginx-6db489d4b7-7f6jb
Name:         nginx-6db489d4b7-7f6jb
Namespace:    default
...
Controlled By:  ReplicaSet/nginx-6db489d4b7    //表示是由ReplicaSet/nginx-6db489d4b7创建pod
...

更多看这里

Deployment(Deploy)

为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。值得一提的是,这种控制器并不直接管理pod,而是通过管理ReplicaSet来简介管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大。

Deployment主要功能有下面几个:支持ReplicaSet的所有功能
支持发布的停止、继续
支持滚动升级和回滚版本
# 查看当前升级版本的状态
[root@k8s-master01 ~]# kubectl rollout status deploy pc-deployment -n dev
deployment "pc-deployment" successfully rolled out# 查看升级历史记录
[root@k8s-master01 ~]# kubectl rollout history deploy pc-deployment -n dev
deployment.apps/pc-deployment
REVISION  CHANGE-CAUSE
1         kubectl create --filename=pc-deployment.yaml --record=true
2         kubectl create --filename=pc-deployment.yaml --record=true
3         kubectl create --filename=pc-deployment.yaml --record=true
# 可以发现有三次版本记录,说明完成过两次升级# 版本回滚
# 这里直接使用--to-revision=1回滚到了1版本, 如果省略这个选项,就是回退到上个版本,就是2版本
[root@k8s-master01 ~]# kubectl rollout undo deployment pc-deployment --to-revision=1 -n dev
deployment.apps/pc-deployment rolled back

更多看这里

Horizontal Pod Autoscaler(HPA)

在前面的课程中,我们已经可以实现通过手工执行kubectl scale命令实现Pod扩容或缩容,但是这显然不符合Kubernetes的定位目标--自动化、智能化。 Kubernetes期望可以实现通过监测Pod的使用情况,实现pod数量的自动调整,于是就产生了Horizontal Pod Autoscaler(HPA)这种控制器。

HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过追踪分析RC控制的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数,这是HPA的实现原理。
更多看这里

DaemonSet(DS)

DaemonSet类型的控制器可以保证在集群中的每一台(或指定)节点上都运行一个副本。一般适用于日志收集、节点监控等场景。也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。

DaemonSet控制器的特点:每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
当节点从集群中移除时,Pod 也就被垃圾回收了
# 查看daemonset
[root@k8s-master01 ~]#  kubectl get ds

job

Job,主要用于负责**批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)**任务。Job特点如下:

当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量
当成功结束的pod达到指定的数量时,Job将完成执行
# 查看job
[root@k8s-master01 ~]# kubectl get job

CronJob(CJ)

CronJob控制器以 Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务

# 查看cronjob
[root@k8s-master01 ~]# kubectl get cronjobs

Service

通过上节课的学习,已经能够利用Deployment来创建一组Pod来提供具有高可用性的服务。

虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:

Pod IP 会随着Pod的重建产生变化
Pod IP 仅仅是集群内可见的虚拟IP,外部无法访问

这样对于访问这个服务带来了难度。因此,kubernetes设计了Service来解决这个问题。

Service可以看作是一组同类Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。

创建集群内部可访问的Service

# 暴露Service
[root@master ~]# kubectl expose deploy nginx --name=svc-nginx1 --type=ClusterIP --port=80 --target-port=80 -n dev
service/svc-nginx1 exposedClusterIP 的创建方式只能在集群内部访问# 查看service
[root@master ~]# kubectl get svc svc-nginx1 -n dev -o wide
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE     SELECTOR
svc-nginx1   ClusterIP   10.109.179.231   <none>        80/TCP    3m51s   run=nginx# 这里产生了一个CLUSTER-IP,这就是service的IP,在Service的生命周期中,这个地址是不会变动的
# 可以通过这个IP访问当前service对应的POD[root@master ~]# curl 10.109.179.231:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
</head>
<body>
<h1>Welcome to nginx!</h1>
.......
</body>
</html>

创建集群外部也可访问的Service

# 上面创建的Service的type类型为ClusterIP,这个ip地址只用集群内部可访问
# 如果需要创建外部也可以访问的Service,需要修改type为NodePort[root@master ~]# kubectl expose deploy nginx --name=svc-nginx2 --type=NodePort --port=80 --target-port=80 -n dev
service/svc-nginx2 exposed# 此时查看,会发现出现了NodePort类型的Service,而且有一对Port(80:31928/TC)
[root@master ~]# kubectl get svc  svc-nginx2  -n dev -o wide
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE    SELECTOR
svc-nginx2    NodePort    10.100.94.0      <none>        80:31928/TCP   9s     run=nginx# 接下来就可以通过集群外的主机访问 节点IP:31928访问服务了
# 例如在的电脑主机上通过浏览器访问下面的地址
http://192.168.90.100:31928/

删除Service

[root@master ~]# kubectl delete svc svc-nginx-1 -n dev
service "svc-nginx-1" deleted

配置方式

创建一个svc-nginx.yaml,内容如下:

apiVersion: v1
kind: Service
metadata:name: svc-nginxnamespace: dev
spec:clusterIP: 10.109.179.231    #固定svc的内网ip,不写默认有ports:- port: 80protocol: TCPtargetPort: 80selector:run: nginxtype: ClusterIP

然后就可以执行对应的创建和删除命令了:

创建:kubectl create -f svc-nginx.yaml删除:kubectl delete -f svc-nginx.yaml

Service介绍

在kubernetes中,pod是应用程序的载体,我们可以通过pod的ip来访问应用程序,但是pod的ip地址不是固定的,这也就意味着不方便直接采用pod的ip对服务进行访问

为了解决这个问题,kubernetes提供了Service资源,Service会对提供同一个服务的多个pod进行聚合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的pod服务。

Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对应的访问规则。

# 10.97.97.97:80 是service提供的访问入口
# 当访问这个入口的映射时,可以发现后面有三个pod的服务在等待调用,
# kube-proxy会基于rr(轮询)的策略,将请求分发到其中一个pod上去
# 这个规则会同时在集群内的所有节点上都生成,所以在任何一个节点上访问都可以。
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags-> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.97.97.97:80 rr-> 10.244.1.39:80               Masq    1      0          0-> 10.244.1.40:80               Masq    1      0          0-> 10.244.2.33:80               Masq    1      0          0

Service类型

Service的资源清单文件:

kind: Service  # 资源类型
apiVersion: v1  # 资源版本
metadata: # 元数据name: service # 资源名称namespace: dev # 命名空间
spec: # 描述selector: # 标签选择器,用于确定当前service代理哪些podapp: nginxtype: # Service类型,指定service的访问方式clusterIP:  # 虚拟服务的ip地址sessionAffinity: # session亲和性,支持ClientIP、None两个选项ports: # 端口信息- protocol: TCP port: 3017  # service端口targetPort: 5003 # pod端口nodePort: 31122 # 主机端口ClusterIP:默认值,它是Kubernetes系统自动分配的虚拟IP,只能在集群内部访问
NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
ExternalName: 把集群外部的服务引入集群内部,直接使用

Endpoint

Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,它是根据service配置文件中selector描述产生的。

一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,Endpoints是实现实际服务的端点集合。换句话说,service和pod之间的联系是通过endpoints实现的

kubectl get endpoints -n namspace -o wide

Ingress介绍

Service对集群之外暴露服务的主要方式有两种:NotePort和LoadBalancer,但是这两种方式,都有一定的缺点:

NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显
LB方式的缺点是每个service需要一个LB,浪费、麻烦,并且需要kubernetes之外设备的支持

基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图表示:

更多看这里

数据存储

在前面已经提到,容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。

Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时,Volume中的数据也不会丢失。

kubernetes的Volume支持多种类型,比较常见的有下面几个:简单存储:EmptyDir、HostPath、NFS
高级存储:PV、PVC
配置存储:ConfigMap、Secret

基本存储

EmptyDir

EmptyDir是最基础的Volume类型,一个EmptyDir就是Host上的一个空目录

EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录,当Pod销毁时, EmptyDir中的数据也会被永久删除。 EmptyDir用途如下:

临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
一个容器需要从另一个容器中获取数据的目录(多容器共享目录)

apiVersion: v1
kind: Pod
metadata:name: volume-emptydirnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80volumeMounts:  # 将logs-volume挂在到nginx容器中,对应的目录为 /var/log/nginx- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始命令,动态读取指定文件中内容volumeMounts:  # 将logs-volume 挂在到busybox容器中,对应的目录为 /logs- name: logs-volumemountPath: /logsvolumes: # 声明volume, name为logs-volume,类型为emptyDir- name: logs-volumeemptyDir: {}

HostPath

EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath。

HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计就可以保证Pod销毁了,但是数据依据可以存在于Node主机上

apiVersion: v1
kind: Pod
metadata:name: volume-hostpathnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80volumeMounts:- name: logs-volumemountPath: /var/log/nginx           挂载到的是pod内的 容器中的路径- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","tail -f /logs/access.log"]volumeMounts:- name: logs-volumemountPath: /logs           挂载到的是pod内的 容器中的路径volumes:- name: logs-volumehostPath: path: /root/logs          挂载到的是pod外的,node内的路径type: DirectoryOrCreate  # 目录存在就使用,不存在就先创建后使用

nfs

HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用NFS、CIFS

NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据就可以成功访问

apiVersion: v1
kind: Pod
metadata:name: volume-nfsnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1ports:- containerPort: 80volumeMounts:- name: logs-volumemountPath: /var/log/nginx- name: busyboximage: busybox:1.30command: ["/bin/sh","-c","tail -f /logs/access.log"] volumeMounts:- name: logs-volumemountPath: /logsvolumes:- name: logs-volumenfs:server: 192.168.5.6  #nfs服务器地址path: /root/data/nfs #共享文件路径

高级存储

点这里

为了能够屏蔽底层存储实现的细节,方便用户使用, kubernetes引入PV和PVC两种资源对象

  • PV(Persistent Volume)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下PV由kubernetes管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。

  • PVC(Persistent Volume Claim)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC其实就是用户向kubernetes系统发出的一种资源需求申请

使用了PV和PVC之后,工作可以得到进一步的细分:

存储:存储工程师维护
PV: kubernetes管理员维护
PVC:kubernetes用户维护

PV

PV是存储资源的抽象,下面是资源清单文件:

apiVersion: v1
kind: PersistentVolume
metadata:name: pv2
spec:nfs: # 存储类型,与底层真正存储对应capacity:  # 存储能力,目前只支持存储空间的设置storage: 2GiaccessModes:  # 访问模式storageClassName: # 存储类别persistentVolumeReclaimPolicy: # 回收策略## 一些操作

PV 的关键配置参数说明:

  • 存储类型
    底层实际存储的类型,kubernetes支持多种存储类型,每种存储类型的配置都有所差异

  • 存储能力(capacity)
    目前只支持存储空间的设置( storage=1Gi ),不过未来可能会加入IOPS、吞吐量等指标的配置

  • 访问模式(accessModes)
    用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

 ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载ReadOnlyMany(ROX): 只读权限,可以被多个节点挂载ReadWriteMany(RWX):读写权限,可以被多个节点挂载需要注意的是,底层不同的存储类型可能支持的访问模式不同
  • 回收策略(persistentVolumeReclaimPolicy)
    PV不再被使用了之后,对其的处理方式。目前支持三种策略:
 Retain (保留) 保留数据,需要管理员手工清理数据Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*Delete (删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务需要注意的是,底层不同的存储类型可能支持的回收策略不同
  • 存储类别
    PV可以通过storageClassName参数指定一个存储类别
    具有特定类别的PV只能与请求了该类别的PVC进行绑定
    未设定类别的PV则只能与不请求任何类别的PVC进行绑定
  • 状态(status)
    一个 PV 的生命周期中,可能会处于4中不同的阶段
 Available(可用): 表示可用状态,还未被任何 PVC 绑定Bound(已绑定): 表示 PV 已经被 PVC 绑定Released(已释放): 表示 PVC 被删除,但是资源还未被集群重新声明Failed(失败): 表示该 PV 的自动回收失败
  • 相关命令
# 创建 pv
[root@k8s-master01 ~]# kubectl create -f pv.yaml# 查看pv
[root@k8s-master01 ~]# kubectl get pv -o wide

PVC

PVC是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息。下面是资源清单文件:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvcnamespace: dev
spec:accessModes: # 访问模式selector: # 采用标签对PV选择storageClassName: # 存储类别resources: # 请求空间requests:storage: 5Gi

PVC 的关键配置参数说明:

  • 访问模式(accessModes)
    用于描述用户应用对存储资源的访问权限

  • 选择条件(selector)
    通过Label Selector的设置,可使PVC对于系统中己存在的PV进行筛选

  • 存储类别(storageClassName)
    PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出

  • 资源请求(Resources )
    描述对存储资源的请求

  • 相关命令

# 创建pvc
[root@k8s-master01 ~]# kubectl create -f pvc.yaml# 查看pvc
[root@k8s-master01 ~]# kubectl get pvc  -n dev -o wide
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE   VOLUMEMODE
pvc1   Bound    pv1      1Gi        RWX                           15s   Filesystem# 查看pv
[root@k8s-master01 ~]# kubectl get pv -o wide
NAME  CAPACITY ACCESS MODES  RECLAIM POLICY  STATUS    CLAIM       AGE     VOLUMEMODE
pv1    1Gi        RWx        Retain          Bound    dev/pvc1    3h37m    Filesystem

配置存储

ConfigMap

ConfigMap是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。

创建configmap.yaml,内容如下:

apiVersion: v1
kind: ConfigMap
metadata:name: configmapnamespace: dev
data:info: |username:adminpassword:123456

接下来,使用此配置文件创建configmap

# 创建configmap
[root@k8s-master01 ~]# kubectl create -f configmap.yaml
configmap/configmap created# 查看configmap详情
[root@k8s-master01 ~]# kubectl describe cm configmap -n dev
Name:         configmap
Namespace:    dev
Labels:       <none>
Annotations:  <none>Data
====
info:
----
username:admin
password:123456Events:  <none>

接下来创建一个pod-configmap.yaml,将上面创建的configmap挂载进去

apiVersion: v1
kind: Pod
metadata:name: pod-configmapnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts: # 将configmap挂载到目录- name: configmountPath: /configmap/configvolumes: # 引用configmap- name: configconfigMap:name: configmap
# 创建pod
[root@k8s-master01 ~]# kubectl create -f pod-configmap.yaml
pod/pod-configmap created# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-configmap -n dev
NAME            READY   STATUS    RESTARTS   AGE
pod-configmap   1/1     Running   0          6s#进入容器
[root@k8s-master01 ~]# kubectl exec -it pod-configmap -n dev /bin/sh
# cd /configmap/config/
# ls
info
# more info
username:admin
password:123456# 可以看到映射已经成功,每个configmap都映射成了一个目录
# key--->文件     value---->文件中的内容
# 此时如果更新configmap的内容, 容器中的值也会动态更新

Secret

在kubernetes中,还存在一种和ConfigMap非常类似的对象,称为Secret对象。它主要用于存储敏感信息,例如密码、秘钥、证书等等。

1 首先使用base64对数据进行编码

[root@k8s-master01 ~]# echo -n 'admin' | base64 #准备username
YWRtaW4=
[root@k8s-master01 ~]# echo -n '123456' | base64 #准备password
MTIzNDU2

2 接下来编写secret.yaml,并创建Secret

apiVersion: v1
kind: Secret
metadata:name: secretnamespace: dev
type: Opaque
data:username: YWRtaW4=password: MTIzNDU2
# 创建secret
[root@k8s-master01 ~]# kubectl create -f secret.yaml
secret/secret created# 查看secret详情
[root@k8s-master01 ~]# kubectl describe secret secret -n dev
Name:         secret
Namespace:    dev
Labels:       <none>
Annotations:  <none>
Type:  Opaque
Data
====
password:  6 bytes
username:  5 bytes

3 创建pod-secret.yaml,将上面创建的secret挂载进去:

apiVersion: v1
kind: Pod
metadata:name: pod-secretnamespace: dev
spec:containers:- name: nginximage: nginx:1.17.1volumeMounts: # 将secret挂载到目录- name: configmountPath: /secret/configvolumes:- name: configsecret:secretName: secret
# 创建pod
[root@k8s-master01 ~]# kubectl create -f pod-secret.yaml
pod/pod-secret created# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-secret -n dev
NAME            READY   STATUS    RESTARTS   AGE
pod-secret      1/1     Running   0          2m28s# 进入容器,查看secret信息,发现已经自动解码了
[root@k8s-master01 ~]# kubectl exec -it pod-secret /bin/sh -n dev
/ # ls /secret/config/
password  username
/ # more /secret/config/username
admin
/ # more /secret/config/password
123456

查看当前po中容器中的image的版本

比如想看 namespace 为 front 中的 name 为 front_end 的 pod 中的 image 版本

1 先查看这个pod 的运行状态k get pod -n front front_end 2 查看详细信息k describe pod -n front  front_end Containers:frontend:Container ID:   docker://xxxxxImage:          ip:端口/front_end:2.3.0-502ba535    这里就是image的版本Image ID:       docker-pullable://ip:端口/xxxxx3 docker 查看镜像docker images | grep front_end 4 如果发现有多个image。则docker查看容器中运行的是哪一个imagedocker ps | grep frontCONTAINER ID        IMAGE           COMMAND                  CREATED             STATUS              PORTS               NAMES容器id             502ba535               命令                 26 minutes ago      Up 26 minutes          端口                 名字5 可以看到容器中具体运行的 image 的 镜像id  502ba535 ,对比可以知道 运行的镜像版本6 查看镜像的详细信息docker inspect 镜像id 502ba535

查看 镜像,容器,po,svc 相关信息

docker images
docker images -a
docker images | grep xxx
docker inspect 镜像id     查看镜像的详细信息docker ps
docker ps -a
docker ps | grep xxx
docker inspect 容器id      查看容器的详细信息
docker exec -it 容器id /bin/bash      进入容器
docker logs 容器id --tail=100        查看容器日志k get po
k get po -A
k get po -A | grep xxxx
k describe po -n namespace pod名字      查看pod详细信息
k logs -n namespace pod名字 --tail=10
k logs -n namespace pod名字 -f         实时更新
k exec -it pod名字 /bin/bash      进入podk get svc
k get svc -A
k get svc -A | grep xxxx
k describe svc svc名字           查看svc一些信息
k exec -it pod名字 /bin/bash      进入pod
不必  进入和查看日志

配置相关

查看配置kubectl get cm查看某个配置kubectl get cm <name> -o yaml编辑配置kubectl edit cm <name>应用yaml文件kubectl  apply -f xxx.yaml查看日志kubectl logs -f <pod> -c <container> -n <namespace>
查看pod(获取pod):kubectl get pod --all-namespaces查看pod里的container等信息(获取container):kubectl get pod -n industry industry-platform-service-747b74fdc6-6875x -o yaml查看日志:kubectl logs -f industry-platform-service-7558875577-74qjl -c industry-platform

日志

查看后十行日志
kubectl logs <pod> --tail=10查看上次启动的日志
kubectl logs <pod> -p# --since 10m是只打印10分钟内的日志,日志太多的时候可以使用这个参数
kubectl logs -f ( -n {namespace} ) {pod-name} ( --since 10m )
修改image版本
namespace,deployment,container,镜像路径:tag
kubectl --namespace=<namespace> set image <deployment> <container>=imageurl:tag例:服务端
kubectl --namespace=default set image deployment/industry-platform-service industry-platform=industry-platform:0.0.1-190-g08c0b3f前端
kubectl --namespace=default set image deployment/industry-platform-service frontend=spring-test/industry-frontend:2.0.54
进入容器的交互式shell
kubectl exec <pod> -it /bin/bash例:
kubectl exec industry-platform-service-7558875577-74qjl -it /bin/bash

跨namespace名称空间访问Service服务

Kubernetes的两个Service(ServiceA、ServiceB)和对应的Pod(PodA、PodB)分别属于不同的namespace名称空间,现需要PodA和PodB跨namespace名称空间并通过Service实现互访。应该如何实现?

[root@k8s-master cross_ns]# kubectl get pod -A -o wide | grep -E '(my)|(NAME)'
NAMESPACE              NAME                                         READY   STATUS    RESTARTS   AGE   IP             NODE         NOMINATED NODE   READINESS GATES
myns                   myapp-deploy1-5b9d78576c-wfw4n               1/1     Running   0          41m   10.244.2.136   k8s-node02   <none>           <none>
myns                   myapp-deploy1-5b9d78576c-zsfjl               1/1     Running   0          41m   10.244.3.193   k8s-node01   <none>           <none>
mytest                 myapp-deploy2-dc8f96497-nnkqn                1/1     Running   0          41m   10.244.3.194   k8s-node01   <none>           <none>
mytest                 myapp-deploy2-dc8f96497-w47dt                1/1     Running   0          41m   10.244.2.137   k8s-node02   <none>           <none>
[root@k8s-master cross_ns]#
[root@k8s-master cross_ns]#
[root@k8s-master cross_ns]# kubectl get svc -A -o wide | grep -E '(my)|(NAME)'
NAMESPACE              NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                  AGE   SELECTOR
myns                   myapp-clusterip1            ClusterIP   10.100.61.11     <none>        80/TCP                   41m   app=myapp,release=v1
mytest                 myapp-clusterip2            ClusterIP   10.100.201.103   <none>        80/TCP                   41m   app=myapp,release=v2

pod跨名称空间namespace与Service通信
说明:是通过Service的NAME进行通信,而不是Service的IP【因为每次重启Service,NAME不会改变,而IP是会改变的】。

# 进入ns名称空间下的一个Pod容器
[root@k8s-master cross_ns]# kubectl exec -it -n myns myapp-deploy1-5b9d78576c-wfw4n sh
/ # cd /root/
### 如下说明在同一名称空间下,通信无问题
~ # ping myapp-clusterip1
PING myapp-clusterip1 (10.100.61.11): 56 data bytes
可以ping通### 如下说明在不同的名称空间下,通过Service的NAME进行通信存在问题
~ # ping myapp-clusterip2
ping: bad address 'myapp-clusterip2'
不通

解决

通过Service的ExternalName类型即可实现跨namespace名称空间与Service通信

Service域名格式:$(service name).$(namespace).svc.cluster.local,其中 cluster.local 为指定的集群的域名

相关yaml文件

[root@k8s-master cross_ns]# cat svc_ExternalName_visit.yaml
# 实现 myns 名称空间的pod,访问 mytest 名称空间的Service:myapp-clusterip2
apiVersion: v1
kind: Service
metadata:name: myapp-clusterip1-externalnamenamespace: myns
spec:type: ExternalNameexternalName: myapp-clusterip2.mytest.svc.cluster.local      !!!!!重点看这里!!!!!ports:- name: httpport: 80targetPort: 80
---
# 实现 mytest 名称空间的Pod,访问 myns 名称空间的Service:myapp-clusterip1
apiVersion: v1
kind: Service
metadata:name: myapp-clusterip2-externalnamenamespace: mytest
spec:type: ExternalNameexternalName: myapp-clusterip1.myns.svc.cluster.local        !!!!!重点看这里!!!!!ports:- name: httpport: 80targetPort: 80

运行yaml文件

[root@k8s-master cross_ns]# kubectl apply -f svc_ExternalName_visit.yaml
[root@k8s-master cross_ns]# kubectl get svc -A -o wide | grep -E '(ExternalName)|(NAME)'
NAMESPACE              NAME                            TYPE           CLUSTER-IP       EXTERNAL-IP                                 PORT(S)                  AGE   SELECTOR
myns                   myapp-clusterip1-externalname   ExternalName   <none>           myapp-clusterip2.mytest.svc.cluster.local   80/TCP                   28s   <none>
mytest                 myapp-clusterip2-externalname   ExternalName   <none>           myapp-clusterip1.myns.svc.cluster.local     80/TCP                   28s   <none>

测试

myns 名称空间的pod,访问 mytest 名称空间的Service:myapp-clusterip2

[root@k8s-master cross_ns]# kubectl exec -it -n myns myapp-deploy1-5b9d78576c-wfw4n sh
/ # cd /root/
### 如下说明在同一名称空间下,通信无问题
~ # ping myapp-clusterip1
PING myapp-clusterip1 (10.100.61.11): 56 data bytes
可以ping通### 如下说明通过Service externalname类型,实现了Pod跨namespace名称空间与Service访问
~ # ping myapp-clusterip1-externalname
PING myapp-clusterip1-externalname (10.100.201.103): 56 data bytes
可以ping通

在外部cmd怎么访问service/pod服务

访问service

ping [service-name].[namespace].svc.cluster.local

访问pod

http://pod_name.所在svc_name.pod_namespace.svc.cluster.local:svc_port/

底层更新镜像步骤

1 下载镜像包

在线下载
docker pull 镜像仓库/镜像离线导入1 导入镜像docker load 镜像包name#查看镜像,会看到该镜像的名称为空dokcer images | head2 镜像打tagdocker tag IMAGEid 本地仓库地址3、上传镜像至本地仓库docker push 本地仓库地址

2 修改配置

进入到对应的pod的目录 例如
cd /etc/kubernetes/apps/servicename,找到配置文件(不同服务使用的yml文件名不一致)修改配置文件里边的image版本
....image:10.84.194.43:8081/test/test:0.9.1-amd64
....

3 重新启动

更新完镜像版本后重新pod  就可以生效了  (可以重启这个路径下的所有pod  ,也可以只用其单独修改的配置文件对应的pod )kubectl delete -f /etc/kubernetes/apps/sevicename   (停用该路径下的所有pod)
kubectl apply -f /etc/kubernetes/apps/servicename   (停用后,重启该路径下的所有pod)注意:如果不想对整个路径下的pod都重启,可以单独重启某个配置文件

总结

操作如下第一步:cd   /etc/kubernetes/apps/servicename第二步执行: kubectl delete -f pod.yml  (停用配置文件)第三步执行:kubectl apply -f pod.yml  (停用后,又启用配置文件)  即可生效

查看pod生命,最近是不是被delete,apply了

使用 history,看最近的操作记录,describe pod 查看 pod 的详细信息

Containers:                      pod中的容器们nginx:Container ID:   docker://513e75ca4bed0b3bc08c68dbffed8b0069b68049dc802107b87d4b99913c9d08Image:          nginx:latest                             使用镜像Image ID:       docker-pullable://nginx@sha256:9522864dd661dcadfd9958f9e0de192a1fdda2c162a35668ab6ac42b465f0603Port:           80/TCP                  容器端口Host Port:      0/TCP               主机端口State:          RunningStarted:      Tue, 14 Dec 2021 18:00:34 +0800           !!!!pod的创建时间!!!!..........
Events:             !!!!pod的age!!!!Type    Reason     Age    From               Message----    ------     ----   ----               -------Normal  Scheduled  9m23s  default-scheduler  Successfully assigned dev/nginx-dd6b5d745-sbfpj to node1Normal  Pulling    9m16s  kubelet, node1     Pulling image "nginx:latest"Normal  Pulled     9m     kubelet, node1     Successfully pulled image "nginx:latest"Normal  Created    9m     kubelet, node1     Created container nginxNormal  Started    9m     kubelet, node1     Started container nginx

pod的生命周期

参考Kubernetes Pod的生命周期(Lifecycle)

k8s 健康检查

健康检查(Health Check)可用于服务运行的状态监控,比如腾讯旗下的DNSPOD的D监控,要求配置一个访问路径判断网站是否可以正常访问,实际上就是一个健康检查,当发现健康检查失败时会发送一个邮件通知或者短信来告知网站管理员进行维修

而在现代一些分布式系统中,用户访问不再是单台主机,而是一个由成百上千台实例组成的集群,用户请求通过负载均衡器分发到不同的实例,负载均衡帮助解决单台服务器的访问压力,同时提高了系统的高可用性,而健康检查常常作为当前实例是否“可用”的判断标准。

即:当系统发现某台实例健康检查不通过负载均衡器将不会把流量导向该实例

Kubernetes提供了两种探针来检查容器的状态Liveliness和Readiness,根据官方文档,Liveliness探针是为了查看容器是否正在运行,翻译为存活探针(livenessProbe),Readiness探针是为了查看容器是否准备好接受HTTP请求,翻译为就绪探针(readinessProbe)。

Kubernetes中Pod是Kubernetes创建及管理最小的可部署的计算单元一个Pod由一个或者多个容器(Docker,rocket等等)组成,这些容器共享内存,网络以及运行容器的方式

在Kubernetes上下文中存活探针和就绪探针被称作健康检查。这些容器探针是一些周期性运行的小进程,这些探针返回的结果(成功,失败或者未知)反映了容器在Kubernetes的状态。基于这些结果,Kubernetes会判断如何处理每个容器,以保证弹性,高可用性和更长的正常运行时间

应用

强大的自愈能力是 Kubernetes 这类容器编排引擎的一个重要特性。自愈的默认实现方式是自动重启发生故障的容器。除此之外,用户还可以利用 Liveness 和 Readiness 探测机制设置更精细的健康检查,进而实现如下需求:

零停机部署避免部署无效的镜像更加安全的滚动升级

就绪探针

就绪探针 旨在让Kubernetes知道你的应用是否准备好为请求提供服务。Kubernetes只有在就绪探针通过才会把流量转发到Pod。如果就绪探针检测失败,K8s会把Pod从service endpoints中删除,停止向该容器发送流量,直到它通过`。

存活探针

Liveness探测器是让Kubernetes知道你的应用是否活着。如果你的应用还活着,那么Kubernetes就让它继续存在。如果你的应用程序已经死了,Kubernetes将移除Pod并重新启动一个来替换它

startupProbe

用于容器启动时判断容器是否启动完成,在这个探针探测成功前,即容器启动成功前livenessProbereadinessProbe探针都不会起作用防止某些启动很慢的容器在启动过程中livenessProbe探针探测容器失败触发容器重启导致死锁

如果这个探针探测失败(容器启动时间超过了阈值),kubelet会把这个容器kill掉,再根据restartPolicy决定是否创建容器

探针类型

探针类型是指通过何种方式来进行健康检查,K8S有三种类型的探测:HTTP,Command和TCP

httpGet: 发送HTTP请求,返回200-400范围状态码表示成功
exec: 执行Shell命令返回状态码为0表示成功
tcpSocket: 发起TCP Socket建立成功

HTTP

HTTP探测可能是最常见的探针类型。即使应用不是HTTP服务,也可以创建一个轻量级HTTP服务器来响应探测。比如让Kubernetes通过HTTP访问一个URL,如果返回码在200到300范围内,就将应用程序标记为健康状态否则它被标记为不健康

command

对于command探测,是指Kubernetes在容器内运行命令。如果command以退出代码0返回,则容器将标记为正常。否则,它被标记为不健康

TCP

Kubernetes尝试在指定端口上建立TCP连接。如果它可以建立连接,容器被认为是健康的;如果它不能被认为是不健康的。这常用于对gRPC或FTP服务的探测

重启策略

Always:  当容器终止后,总是重启,默认策略
OnFailure: 当容器异常退出(退出状态码非0)时,才重启容器
Never: 当容器终止退出,从不重启容器

初始探测延迟 initialDelaySeconds

可以配置K8S健康检查运行的频率检查成功或失败的条件,以及响应的超时时间存活探针探测失败会导致pod重新启动,所以配置初始探测延迟 initialDelaySeconds十分重要,要确保在应用准备之后探针才启动。否则,应用将无限重启

示例

apiVersion: apps/v1
kind: Deployment
metadata:labels:app: webname: web
spec:replicas: 1selector:matchLabels:app: webtemplate:metadata:labels:app: webspec:restartPolicy: Alwayscontainers:- image: nginximagePullPolicy: IfNotPresentname: webports:- containerPort: 80livenessProbe:tcpSocket:port: 80initialDelaySeconds: 5failureThreshold: 2periodSeconds: 5readinessProbe:httpGet:path: /index.htmlport: 80initialDelaySeconds: 5periodSeconds: 5startupProbe:  # startupProbehttpGet:path: /healthzport: liveness-portfailureThreshold: 30periodSeconds: 10  # 容器启动30*10=300s内liveness和readiness探针不会执行,300s后startup探针还没成功,容器会被kill掉重启---
apiVersion: v1
kind: Service
metadata:labels:app: webname: web
spec:ports:- port: 80protocol: TCPselector:app: webtype: NodePort
initialDelaySeconds: 第一次启动Pod后等待多久时间开始健康检查
failureThreshold:  探针执行失败几次后容器状态变为失败,默认3次
periodSeconds: 健康检查的周期

问题

spark 使用job的方式部署,导致pod被删除(包括delete/evict,常见为大数据节点服务器断电、网络异常)后无法重启,影响相关功能

1 检测是否 hdfs出问题
查看Namenode的pods是否处于Running状态及是否有过重启的记录

查看pods状态
# kubectl -n component get pods -o wide | grep hdfs查看Namenode是否处于safemode模式
# kubectl -n component exec -it hdfs-namenode-default-0 -- hdfs dfsadmin -safemode get如果是的话,退出安全模式
kubectl -n component exec -it hdfs-namenode-default-0 -- hdfs dfsadmin -safemode leave

kubernetes启动Pod遇到CrashLoopBackOff的解决思路

问题现状
[root@master ~]# kubectl get pods -n kube-system | grep -v Running
NAME                                        READY     STATUS             RESTARTS   AGE
prometheus-tim-3864503240-rwpq5             0/1       CrashLoopBackOff   2516       8d
查找问题
[root@master ~]# kubectl describe pod prometheus-tim-3864503240-rwpq5 -n kube-system
...
spec.containers{prometheus} Warning     BackOff     Back-off restarting failed container8d        4s      59160   kubelet, test-95                    Warning     FailedSync  Error syncing pod
查看异常pod的日志
...
level=info ts=2018-01-29T03:11:57.9034449Z caller=main.go:394 msg="Loading configuration file" filename=/etc/prometheus/prometheus.yaml
level=error ts=2018-01-29T03:11:57.903503914Z caller=main.go:356 msg="Error loading config" err="couldn't load configuration (--config.file=/etc/prometheus/prometheus.yaml): open /etc/prometheus/prometheus.yaml: no such file or directory"
发现是   /etc/prometheus/prometheus.yaml 中的问题

一个pod 对应一个容器,一个镜像

进入容器或者podpod / 容器中的目录pod.yml 中挂载上去的

docker exec -it contatiner_id bash
kubectl exec -it -n namespace pod_name bash

rancher

看下面的文章 知道是干啥的,会用即可
Rancher 使用介绍(可以通过界面管理 K8s 平台)
Rancher1-简单介绍-认识rancher
Rancher的优点及不足

k8s 和 管理工具 rancher相关推荐

  1. k8s可视化管理工具Rancher安装和使用

    k8s系列入门级教程-k8s可视化管理工具Rancher安装和使用 <超低成本的k8s集群搭建教程> 使用rke安装企业级k8s集群-待填坑 prometheus+grafana监控搭建教 ...

  2. 【云原生 • DevOps】一文掌握容器管理工具 Rancher

    本章学习目标: 掌握容器管理工具 Rancher 的安装与使用: 掌握基于 Rancher 的应用部署.扩容缩容操作. 目录 一.容器管理工具 Rancher 介绍 二.Rancher 的安装 三.R ...

  3. Kubernetes:开源 K8s 管理工具 Rancher 认知

    写在前面 博文内容涉及 Rancher 的介绍,集群内安装 查看 Rancher 的基本功能 理解不足小伙伴帮忙指正 我所渴求的,無非是將心中脫穎語出的本性付諸生活,為何竟如此艱難呢 ------赫尔 ...

  4. k8s可视化管理工具

    Kubernetes已经成为大规模部署容器化应用程序一种标准方式.但是,Kubernetes本身却是非常复杂,作为新手是很难全面了解一个集群的全貌,因此一个优秀的可视化界面管理工具是非常必要的,毕竟人 ...

  5. 【云原生 • Kubernetes】一文掌握 k8s 包管理工具 Helm

    本文导读 一.为什么要引入 Helm 1. Helm 的应用场景 2. 使用 Helm 可以解决哪些问题 二.Helm 概述 三.Helm 安装与配置(v3) 1. 安装 Helm v3 2. 配置 ...

  6. docker 可视化管理工具 rancher 简介

    目录 1.Rancher介绍 Rancher组成 Rancher展示 Web管理界面-简单易用 环境管理 主机管理 应用商店 负载均衡 部署NFS连接外部存储 2.Rancher安装 (1)镜像下载 ...

  7. Docker可视化管理工具Rancher

    1.Rancher介绍 Rancher是一个开源的企业级容器管理平台.通过Rancher,企业再也不必自己使用一系列的开源软件去从头搭建容器服务平台.Rancher提供了在生产环境中使用的管理Dock ...

  8. docker可视化管理工具对比 DockerUI Shipyard Rancher Portainer

    目录 1.前言 2.优劣对比 DockerUI Shipyard Rancher Portainer 1.前言 谈及docker,避免不了需要熟练的记住好多命令及其用法,对于熟悉shell.技术开发人 ...

  9. docker、K8S、k3s、rancher

    第一:docker 基础使用 docker 竞品 最新调查:OpenStack.Docker.KVM被评为最火的云开源项目. Docker替代品,Containerd Docker与KVM之间的区别( ...

最新文章

  1. React+TypeScript练手小项目
  2. linux系统安装yum环境
  3. my sql Group_concat函数
  4. Spark-大规模数据处理计算引擎
  5. 利用Eigen进行矩阵计算
  6. java连接mongo_java 连接mongodb
  7. Midletinfo-探索手机javaME系统信息的实用工具
  8. c++派生类构造顺序
  9. 海洋科学导论知识点总结
  10. 【解决方案 六】---在VS2015里使用ILDasm
  11. 解答腾讯会议的常见新手问题
  12. Python 爬虫框架Scrapy Spiders学习
  13. 1296. 聪明的燕姿
  14. SWT定位– setBounds()或setLocation()
  15. 打造高铁WiFi新体验,中兴新支点ICG与高铁强强联手
  16. 页面置换模拟程序设计
  17. INSERT INTO SET
  18. 计算机网络(10)奈氏准则和香农定理
  19. npm安装electron报RequestError: socket hang up错误解决方法
  20. python中的main函数可以被其他文件调用么_在Python中,如何在另一个py文件的[if\u name\uuuu='\uu main\uu']中调用子例程?...

热门文章

  1. JAVA中的三大集合框架
  2. matlab揭秘 david 豆瓣,《Matlab揭秘》(David McMahon)[PDF]
  3. 安装Anaconda3-5.1.0-Linux-x86_64.sh报错
  4. 法国音乐之神 -- 保罗·莫里哀精选
  5. Rotary pumps 旋转泵
  6. 优秀!博士毕业两年后任副教授,34岁就成为中国最年轻女博导之一!
  7. Java程序员面试时记住这些谈薪技巧,让你薪资提升30%
  8. php云系统清除版权,PHP云人才系统 PHPYun任意文件删除
  9. 阿里云——手把手教你搭建个人网站
  10. 我看亨德利和奥沙利文(比较客观)