Node,Pod,Replication Controller,Service等都是k8s中的一种“资源对象”,都可以通过工具kubectl执行增、删、改的管理操作。其配置结果是保存在etcd中。k8s就像是一个自动化的资源控制系统,通过对etcd库中保存的资源期望状态和实际资源状态进行差异对比,来发现、控制和进行“纠错”。

1、k8s集群管理角色——Master

每个k8s集群里需要一个Master节点来负责整个集群的管理和控制,通常要占据一个独立的服务器,从高可用角度考虑则建议使用3台服务器。
Master节点上运行着以下关键进程或服务:
  • k8s API Server,提供了HTTP Rest接口的关键服务进程,是集群中所有资源增、删、改操作的唯一入口,也是控制集群的唯一入口。
  • k8s Controller Manager,是集群中所有资源对象的运行指挥中心。
  • k8s Scheduler,是负责调度Pod资源的进程。
  • etcd服务,提供集群中所有资源对象配置的持久化。

2、k8s集群管理角色——Node

Node是k8s集群中的工作负载节点,当某个Node当机时,其上的工作负载会被Master自动转移到其他节点上去。
Node节点上运行着以下关键进程或服务:
  • Kublete,负责Pod对应的容器的创建、启停管理,与Master节点协作,实现集群管理的基本功能。
  • kube-proxy,是提供k8s的通信与负载均衡功能的重要组件。
  • Docker Engine,docker引擎。
Node节点可以动态地加入到k8s集群中,在这个过程中kublete找到Master完成信息注册。在加入集群后,kublete进程就定时向Master节点发送操作系统、Docker版本、CPU、内存的统计信息,以及当前有哪些Pods在运行。当某个Node超时未发送以上信息时,会被Master判定为“失联”(Not Ready),随即触发工作负载转移的流程。
查看集群中有哪些Nodes:
[root@gqtest ~]# kubectl get nodes
NAME        STATUS    AGE
127.0.0.1   Ready     3d

查看某个Node的详细信息:
[root@gqtest ~]# kubectl describe node "127.0.0.1"
Name:            127.0.0.1
Role:            
Labels:            beta.kubernetes.io/arch=amd64
beta.kubernetes.io/os=linux
kubernetes.io/hostname=127.0.0.1
Taints:            <none>
CreationTimestamp:    Sat, 17 Feb 2018 22:14:43 +0800
Phase:            
Conditions:
Type            Status    LastHeartbeatTime            LastTransitionTime            Reason                Message
----            ------    -----------------            ------------------            ------                -------
OutOfDisk         False     Wed, 21 Feb 2018 10:40:50 +0800     Sat, 17 Feb 2018 22:14:43 +0800     KubeletHasSufficientDisk     kubelet has sufficient disk space available
MemoryPressure     False     Wed, 21 Feb 2018 10:40:50 +0800     Sat, 17 Feb 2018 22:14:43 +0800     KubeletHasSufficientMemory     kubelet has sufficient memory available
DiskPressure         False     Wed, 21 Feb 2018 10:40:50 +0800     Sat, 17 Feb 2018 22:14:43 +0800     KubeletHasNoDiskPressure     kubelet has no disk pressure
Ready         True     Wed, 21 Feb 2018 10:40:50 +0800     Wed, 21 Feb 2018 10:40:00 +0800     KubeletReady             kubelet is posting ready status
Addresses:        127.0.0.1,127.0.0.1,127.0.0.1
Capacity:
alpha.kubernetes.io/nvidia-gpu:    0
cpu:                    2
memory:                1532144Ki
pods:                    110
Allocatable:
alpha.kubernetes.io/nvidia-gpu:    0
cpu:                    2
memory:                1532144Ki
pods:                    110
System Info:
Machine ID:            3be0a8ad023f4dd0b530ddcaeecf83cd
System UUID:            E351A3F3-7D82-4C97-A174-297F8526DDBD
Boot ID:            d40f85db-90fe-4904-92cc-847b6f136058
Kernel Version:        3.10.0-693.17.1.el7.x86_64
OS Image:            CentOS Linux 7 (Core)
Operating System:        linux
Architecture:            amd64
Container Runtime Version:    docker://1.12.6
Kubelet Version:        v1.5.2
Kube-Proxy Version:        v1.5.2
ExternalID:            127.0.0.1
Non-terminated Pods:        (3 in total)
Namespace            Name            CPU Requests    CPU Limits    Memory Requests    Memory Limits
---------            ----            ------------    ----------    ---------------    -------------
default            mysql-mn49n        0 (0%)        0 (0%)        0 (0%)        0 (0%)
default            myweb-k6fp0        0 (0%)        0 (0%)        0 (0%)        0 (0%)
default            myweb-m9nv9        0 (0%)        0 (0%)        0 (0%)        0 (0%)
Allocated resources:
(Total limits may be over 100 percent, i.e., overcommitted.
CPU Requests    CPU Limits    Memory Requests    Memory Limits
------------    ----------    ---------------    -------------
0 (0%)    0 (0%)        0 (0%)        0 (0%)
Events:
FirstSeen    LastSeen    Count    From            SubObjectPath    Type        Reason            Message
---------    --------    -----    ----            -------------    --------    ------            -------
1m        1m        1    {kubelet 127.0.0.1}            Normal        Starting        Starting kubelet.
1m        1m        1    {kubelet 127.0.0.1}            Warning        ImageGCFailed        unable to find data for container /
1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeHasSufficientDisk    Node 127.0.0.1 status is now: NodeHasSufficientDisk
1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeHasSufficientMemory    Node 127.0.0.1 status is now: NodeHasSufficientMemory
1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeHasNoDiskPressure    Node 127.0.0.1 status is now: NodeHasNoDiskPressure
1m        1m        1    {kubelet 127.0.0.1}            Warning        Rebooted        Node 127.0.0.1 has been rebooted, boot id: d40f85db-90fe-4904-92cc-847b6f136058
1m        1m        1    {kubelet 127.0.0.1}            Normal        NodeNotReady        Node 127.0.0.1 status is now: NodeNotReady
58s        58s        1    {kubelet 127.0.0.1}            Normal        NodeReady        Node 127.0.0.1 status is now: NodeReady
1m        55s        2    {kubelet 127.0.0.1}            Warning        MissingClusterDNS    kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-k6fp0_default(3ac7a156-1414-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.
1m        53s        2    {kubelet 127.0.0.1}            Warning        MissingClusterDNS    kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "myweb-m9nv9_default(3ac7b4c7-1414-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.
1m        48s        2    {kubelet 127.0.0.1}            Warning        MissingClusterDNS    kubelet does not have ClusterDNS IP configured and cannot create Pod using "ClusterFirst" policy. pod: "mysql-mn49n_default(cee70dc4-140e-11e8-9692-0800275f8277)". Falling back to DNSDefault policy.

3、k8s集群最小资源调度管理单位——Pod

一般情况下,Pod是Kubernetes创建或部署的最小/最简单的基本单位,一个Pod代表集群上正在运行的一个进程。
注:在单个Pod中共同管理多个容器是一个相对高级的用法,应该只有在容器紧密耦合的特殊实例中使用此模式。
Kubernetes中的Pod使用可分两种主要方式:
  • Pod中运行一个容器。“one-container-per-Pod”模式是Kubernetes最常见的用法。在这种情况下,你可以将Pod视为单个封装的容器,但是Kubernetes是直接管理Pod而不是容器。
  • Pods中运行多个需要一起工作的容器。Pod可以封装紧密耦合的应用,它们需要由多个容器组成,它们之间能够共享资源,这些容器可以形成一个单一的内部service单位。

每个Pod都是运行应用的单个实例,如果需要水平扩展应用(例如,运行多个实例),则应该使用多个Pods,每个实例一个Pod。在Kubernetes中,这样通常称为Replication。Replication的Pod通常由Controller创建和管理。

Pods提供两种共享资源:网络和存储。
  • 网络,每个Pod被分配一个独立的IP地址,Pod中的每个容器共享网络命名空间,包括IP地址和网络端口。Pod内的容器可以使用localhost相互通信。当Pod中的容器与Pod 外部通信时,他们必须协调如何使用共享网络资源(如端口)。k8s要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,这一般是采用虚拟二层网络技术来实现的,如Open vSwitch。
  • 存储,Pod可以指定一组共享存储volumes。Pod中的所有容器都可以访问共享volumes,允许这些容器共享数据。volumes 还用于Pod中的数据持久化,以防其中一个容器需要重新启动而丢失数据。
Pod的几个关键知识点:
  • 很少会直接在kubernetes中创建单个Pod。因为Pod的生命周期是短暂的,用后即焚的实体。当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kuberentes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。
  • Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod。
  • Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。通常,Controller会用你提供的Pod Template来创建相应的Pod。
  • Pod都会包含一个特殊的“根容器”,一个或多个业务容器。当根容器“死亡”了时,代表该Pod整个容器组的不可用。
  • Endpoint,Pod的IP加上Pod里的容器端口(containerPort)称为一个Endpoint,代表着此Pod里的一个服务进程的对外通信地址。
  • Pod Event事件,可以使用kubectl describe pod xxxx查看一个Pod的详细信息,其中包括了该Pod所发生的每个事件的信息。Event事件信息对于故障排查很有帮助。
Pod的类型:
  • 普通Pod
  • 静态Pod,不受etcd的配置信息管控,直接存放在某个Node上且只在该Node上运行。
Pod的资源限额:
  • CPU限额,资源限制的单位为cpu Core的数量,该资源是以绝对值计算的。k8s将1/1000的CPU Core的资源定义为可供分配的最小CPU资源单位,称为m。通常一个容器的CPU配额被定义为100~300m,即占用0.1~0.3个CPU Core。因为该配额是一个资源的绝对值,所以无论是在一个只有2 CPU Cores的机器上,还是在一个有48CPU Cores的机器上,100m所代表的CPU使用量都是一样的。
  • 内存限额,内存的资源限制是以节节为计量单位,内存的配额也是按资源的绝对值进行分配的。
k8s中配置资源限额的方法:
  • Requests设置,一个资源的最小申请量,是系统必须满足的一个用量;
  • Limits设置,该资源的最大允许使用量,不能被突破,当容器试图突破该参数限制时,可能会被k8s Kii并重启。
一般是结合使用以上两个参数去定义一个资源的配额,如下所示。
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"

4、Label和Label Selector

  • Label是一个key/value的键值对,可以附加到各种资源对象上。
  • 一个资源对象可以定义任意数量的Label,同一个Label也可以被添加到任意数量的资源对象上去。
  • 通常是在资源对象定义时确定Label,也支持在对象创建后动态添加或删除。
  • Label和Lable Selector构成了k8s系统中最核心的一个应用模型,可以对被管理对象进行精细分组,以实现整个集群的高可用。
通过给指定的资源对象捆绑一个或多个不同的Label来实现多维度的资源分组管理。一些示例标签如下:
  • "release" : "stable", "release" : "canary"
  • "environment" : "dev","environment" : "qa","environment" : "production"
  • "tier" : "frontend","tier" : "backend","tier" : "cache"
  • "partition" : "customerA", "partition" : "customerB"
  • "track" : "daily", "track" : "weekly"
Label Selector可以查询和筛选拥有某些Label的资源对象。
  • 目前支持两种选择器:equality-based(基于平等)和set-based(基于集合)的。前者采用等式的表达式,后者采用集合操作的表达式匹配标签。
  • 标签选择器可以由逗号分隔的多个requirements 组成。在多重需求的情况下,必须满足所有要求,因此逗号分隔符作为AND逻辑运算符。
equality-based示例:
  • name = redis-slave
  • env != production
set-based示例:
  • name in (redis-master, redis-slave)
  • name not in (frontend)
Label Selector set-based筛选功能仅在以下新出现的管理对象中得到了支持:
  • Deployment
  • ReplicaSet
  • DaemonSet
  • Job
Label Selector的几个重要使用场景:
  • kube-controller进程,通过资源对象RC上定义的Label Selector来筛选要监控和管理的Pod副本的数量。
  • kube-proxy进程,通过Service的Lable Selector来选择对应的Pod,建立起每个Service到对应Pod的请求转发路由表,进而实现Service的智能负载均衡机制。
  • kube-scheduler进程,通过为某些Node定义特定的Label,然后在Pod定义文件中使用NodeSelector进行筛选,进而实现Pod的定向调度功能。

5、Replication Controller与Replica Set

  • ReplicationController(简称RC),定义了一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值。通过RC,k8s实现了用户应用集群的高可用性,同时大减少了系统管理员在传统IT环境中需要完成的许多手工运维工作(如主机监控、应用监控和故障恢复等)。
  • ReplicationController是早期k8s版本中主要使用的一项技术,在较新版本中,RC的功能已经逐渐被功能更强大的ReplicaSet和Deployment的组合所替代。
  • 因为ReplicationController、ReplicaSet和Deployment之间存在着巨大的相似性,所以仍然有了解和掌握RC知识和使用方法的必要。
RC会包含以下几个部分:
  • Pod期待的副本数;
  • 用于筛选目标Pod的Label Selector;
  • 当Pod的副本数量小于预期时,用于创建新Pod的Pod模板。
修改RC的副本数量可以实现Pod的动态缩放:
$ kubectl scale rc redis-slave --replicas=3
关于删除RC:
  • 删除RC并不会影响通过该RC已经创建好的Pod;
  • 为了删除使用该RC创建的Pods,可以设置replicas为0,然后更新该RC;
  • 使用kubectl delete命令删除RC及其所有pod。
关于ReplicaSet的知识:
  • 由于Replication Controller与k8s代码中的模块Replication Controller同名,所以在k8s v1.2时就升级成了另外一个新的工具Replica Set。二者之间唯一的区别是,Replica Set支持了基于集合的Label Selector功能。
  • ReplicaSet是支持新的set-based选择器要求的下一代ReplicationController 。它主要用作Deployment协调pod创建、删除和更新。虽然ReplicaSets可以独立使用,但它主要被 Deployments用作pod 机制的创建、删除和更新,建议使用Deployment而不是直接使用ReplicaSets。
ReplicaSet的使用示例(部分):
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: frontend
spec:
replicas: 3
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: guestbook
tier: frontend
spec:

RC(Replica Set)的一些特性:
  • 在大多数情况下,我们通过定义一个RC实现Pod的创建过程及副本数量的自动控制。
  • RC里包括完整的Pod定义模板。
  • RC通过Label Selector机制实现对Pod副本的自动控制。
  • 通过改变RC里的Pod副本数量,可以实现Pod的扩容或缩容功能。
  • 通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级功能。

6、Deployment

Deployment为Pod和Replica Set(升级版的 Replication Controller)提供声明式更新。
注意:您不该手动管理由 Deployment 创建的 Replica Set,否则您就篡越了 Deployment controller 的职责!
Deployment的典型的用例如下:
  • 创建一个Deployment对象来生成对应的ReplicaSet,并完成Pod副本的创建过程。
  • 检查Deployment的状态来查看部署动作是否完成,Pod副本的数量是否达到预期的值。
  • 更新Deployment以创建新的Pod,通过修改Pod-Template-Spec字段来声明Pod的新状态。这会创建一个新的ReplicaSet,Deployment会按照控制的速率将pod从旧的ReplicaSet移动到新的ReplicaSet中。
  • 如果当前状态不稳定,则回滚到一个早先的Deployment版本。
  • 暂停Deployment,以便于一次性修改多个PodTemplateSpec的配置项,然后再恢复Deployment,进行新的发布。
  • 扩容Deployment以满足更高的负载。
  • 查看Deployment 的状态,以此作为发布是否成功的指标。
  • 清除旧的不必要的 ReplicaSets。
(1)创建 Deployment
下面是一个 Deployment 示例,它创建了一个 ReplicaSet 来启动3个 nginx pod。
下载示例文件nginx-deployment.yaml并执行命令:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

执行创建命令:
[root@gqtest ~]# kubectl create -f nginx-deployment.yaml --record
deployment "nginx-deployment" created

注:--record选项可以记录当前命令执行记录,便于以后查看一个deployment revision中执行了哪些命令。
[root@gqtest ~]# kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            0           2m

看到available数量仍为0,继续查看一下该deployment的详细情况:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 0 available | 1 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
Type        Status    Reason
----        ------    ------
Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-4087004473 (1/1 replicas created)
Events:
FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
---------    --------    -----    ----                -------------    --------    ------            -------
3m        3m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1

上面输出信息没有异常,再查看一次该deployment的状态,如下所示,available的实例数量已经是1,说明经过一个创建过程(往往会因为要在线下载docker image镜像而需要等待一段时间),已经成功创建:
[root@gqtest ~]# kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            1           4m

查看Replica Set信息,可以到到ReplicaSet名称是在Deployment后添加一串数字:
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-4087004473   1         1         1         4m

继续查看pod的状态,如下所示:
[root@gqtest ~]# kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
mysql-mn49n                         1/1       Running   1          3d
myweb-k6fp0                         1/1       Running   1          3d
myweb-m9nv9                         1/1       Running   1          3d
nginx-deployment-4087004473-xdxhn   1/1       Running   0          7m

(2)更新Deployment
Deployment的更新(rollout)当且仅当Deployment的pod template中的label发生更新或者镜像发生更改时,才会被触发。像Deployment扩容,不会触发rollout事件。
假如我们现在想要让 nginx pod 使用nginx:1.9.1的镜像来代替原来的nginx:1.7.9的镜像。
[root@gqtest ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
deployment "nginx-deployment" image updated

或者可以使用edit命令来编辑 Deployment,修改 .spec.template.spec.containers[0].image ,将nginx:1.7.9 改写成 nginx:1.9.1。
[root@gqtest ~]# kubectl edit deployment/nginx-deployment
查看更新结果:
[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
[root@gqtest ~]# kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            0           18m
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   1         1         0         4m
nginx-deployment-4087004473   0         0         0         19m

从上面的输出信息中看到,正在创建一个新的ReplicaSet的过程中,同时已经将旧的缩容到了0个replica。
查看pod状态时,已经看不到旧的pod了:
[root@gqtest ~]# kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
mysql-mn49n                         1/1       Running   1          3d
myweb-k6fp0                         1/1       Running   1          3d
myweb-m9nv9                         1/1       Running   1          3d
nginx-deployment-3599678771-chzwr   1/1       Running   0          6m

再次执行查看Deployment更新结果的命令,显示已经成功完成:
[root@gqtest ~]# kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out

[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   1         1         1         8m
nginx-deployment-4087004473   0         0         0         23m

最后查看一下该Deployment任务的详细信息:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 1 available | 0 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
Type        Status    Reason
----        ------    ------
Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-3599678771 (1/1 replicas created)
Events:
FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
---------    --------    -----    ----                -------------    --------    ------            -------
25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
10m        10m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
10m        10m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0

Rollover(多个rollout并行)的说明:
假如您创建了一个有5个niginx:1.7.9 replica的 Deployment,但是当还只有3个nginx:1.7.9的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment。在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个nginx:1.7.9的 Pod 都创建完成后才开始改变航道。
Label selector 更新:
上面的演示是基于image文件发生了更改的条件下的一个Deployment更新的样例。通常不鼓励更新 label selector,虽然技术上可行,但关联影响较多。
(3)回退Deployment
可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。
只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。
我们先故意执行一个有错误的Deployment任务:
[root@gqtest ~]# kubectl set image deployment/nginx-deployment nginx=nginx: 1.99
deployment "nginx-deployment" image updated

我们故障写了一个不存在的镜像文件版本,Deployment rollout任务会补卡住,如下所示:
[root@gqtest ~]# kubectl rollout status deployments nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...

可以看到pod的状态处于"ImagePullBackOff"的错误状态:
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   0         0         0         23m
nginx-deployment-4087004473   0         0         0         38m
nginx-deployment-538426637    1         1         0         3m
[root@gqtest ~]# kubectl rollout status deployments nginx-deployment
Waiting for rollout to finish: 0 of 1 updated replicas are available...
^C[root@gqtest ~]# kubectl get pods
NAME                               READY     STATUS             RESTARTS   AGE
mysql-mn49n                        1/1       Running            1          3d
myweb-k6fp0                        1/1       Running            1          3d
myweb-m9nv9                        1/1       Running            1          3d
nginx-deployment-538426637-g55n4   0/1        ImagePullBackOff   0          3m

注:Deployment controller会自动停止坏的 rollout,并停止扩容新的 ReplicaSet。
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 0 available | 1 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
Type        Status    Reason
----        ------    ------
Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-538426637 (1/1 replicas created)
Events:
FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
---------    --------    -----    ----                -------------    --------    ------            -------
40m        40m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
25m        25m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0
5m        5m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1
5m        5m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0

我们需要回退到稳定版本的Deployment revision。
先查看一下Deployment的revision记录:
[root@gqtest ~]# kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION    CHANGE-CAUSE
1        kubectl create -f nginx-deployment.yaml --record
2        kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
3        kubectl set image deployment/nginx-deployment nginx=nginx:1.99

注:因为我们创建 Deployment 的时候使用了--recored参数可以记录命令,我们可以很方便的查看每次 revision 的变化。
查看单个revision 的详细信息:
[root@gqtest ~]# kubectl rollout history deployment/nginx-deployment --revision=2
deployments "nginx-deployment" with revision #2
Labels:    app=nginx
pod-template-hash=3599678771
Annotations:    kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.9.1
Containers:
nginx:
Image:    nginx:1.9.1
Port:    80/TCP
Volume Mounts:    <none>
Environment Variables:    <none>
No volumes.

回退到历史版本
回退到上一个版本:
[root@gqtest ~]# kubectl rollout undo deployment/nginx-deployment
deployment "nginx-deployment" rolled back

回退到一个指定的版本:
# kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment "nginx-deployment" rolled back

查看下回退结果:
[root@gqtest ~]# kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   1         1         1            1           46m

查看下回退版本所产生的事件记录:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        1 updated | 1 total | 1 available | 0 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
Type        Status    Reason
----        ------    ------
Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-3599678771 (1/1 replicas created)
Events:
FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
---------    --------    -----    ----                -------------    --------    ------            -------
46m        46m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
32m        32m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0
11m        11m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1
11m        11m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0
32m        2m        2    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
2m        2m        1    {deployment-controller }            Normal        DeploymentRollback    Rolled back deployment "nginx-deployment" to revision 2
2m        2m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-538426637 to 0

可以通过设置.spec.revisonHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment就不允许回退了。
(4)Deployment 扩容
使用以下命令将nginx-deployment扩容为3副本:
[root@gqtest ~]# kubectl scale deployment nginx-deployment --replicas 3
deployment "nginx-deployment" scaled

[root@gqtest ~]# kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           51m

[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3599678771   3         3         3         36m
nginx-deployment-4087004473   0         0         0         51m
nginx-deployment-538426637    0         0         0         16m

[root@gqtest ~]# kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
mysql-mn49n                         1/1       Running   1          3d
myweb-k6fp0                         1/1       Running   1          3d
myweb-m9nv9                         1/1       Running   1          3d
nginx-deployment-3599678771-hz7l6   1/1       Running   0          7m
nginx-deployment-3599678771-j136m   1/1       Running   0          1m
nginx-deployment-3599678771-kqwpf   1/1       Running   0          1m

查看该Deployment的事件记录:
[root@gqtest ~]# kubectl describe deployment nginx-deployment
Name:            nginx-deployment
Namespace:        default
CreationTimestamp:    Wed, 21 Feb 2018 17:02:45 +0800
Labels:            app=nginx
Selector:        app=nginx
Replicas:        3 updated | 3 total | 3 available | 0 unavailable
StrategyType:        RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:    1 max unavailable, 1 max surge
Conditions:
Type        Status    Reason
----        ------    ------
Available     True    MinimumReplicasAvailable
OldReplicaSets:    <none>
NewReplicaSet:    nginx-deployment-3599678771 (3/3 replicas created)
Events:
FirstSeen    LastSeen    Count    From                SubObjectPath    Type        Reason            Message
---------    --------    -----    ----                -------------    --------    ------            -------
52m        52m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-4087004473 to 1
37m        37m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-4087004473 to 0
17m        17m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-538426637 to 1
17m        17m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-3599678771 to 0
37m        7m        2    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 1
7m        7m        1    {deployment-controller }            Normal        DeploymentRollback    Rolled back deployment "nginx-deployment" to revision 2
7m        7m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled down replica set nginx-deployment-538426637 to 0
1m        1m        1    {deployment-controller }            Normal        ScalingReplicaSet    Scaled up replica set nginx-deployment-3599678771 to 3

注:假如集群中启用了horizontal pod autoscaling,您可以给 Deployment 设置一个 autoscaler,基于当前 Pod的 CPU 利用率选择最少和最多的 Pod 数。
关于Deployment的比例扩容:
RollingUpdate Deployment 支持同时运行一个应用的多个版本。或者 autoscaler 扩 容 RollingUpdate Deployment 的时候,正在中途的 rollout(进行中或者已经暂停的),为了降低风险,Deployment controller 将会平衡已存在的活动中的 ReplicaSet(有 Pod 的 ReplicaSet)和新加入的 replica。这被称为比例扩容。
(5)暂停和恢复Deployment
可以在发出一次或多次更新前暂停一个 Deployment,然后再恢复它。这样您就能多次暂停和恢复 Deployment,在此期间进行一些修复工作,而不会发出不必要的 rollout。
[root@gqtest ~]# kubectl rollout pause deployment/nginx-deployment
deployment "nginx-deployment" paused

[root@gqtest ~]# kubectl set image deploy/nginx-deployment nginx=nginx:1.9.2
deployment "nginx-deployment" image updated

即使执行了上面的镜像更改命令,也没有触发任何Deployment rollout的事件。而且可以继续做更多的修改,例如:
[root@gqtest ~]# kubectl set resources deployment nginx-deployment -c=nginx --limits=cpu=200m,memory=256Mi
deployment "nginx-deployment" resource requirements updated

注:-c选项的作用是指定容器唯一标识名
当各种变更都准备妥当后,我们恢复该deployment:
[root@gqtest ~]# kubectl rollout resume deploy nginx-deployment
deployment "nginx-deployment" resumed

查看一下deployment处理结果,使用-w选项(watch)跟踪输出如下所示:
[root@gqtest ~]# kubectl get rs -w
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-27997383     2         2         0         2m
nginx-deployment-3599678771   2         2         2         1h
nginx-deployment-4087004473   0         0         0         1h
nginx-deployment-538426637    0         0         0         59m
NAME                        DESIRED   CURRENT   READY     AGE
nginx-deployment-27997383   2         2         1         4m
nginx-deployment-3599678771   1         2         2         1h
nginx-deployment-3599678771   1         2         2         1h
nginx-deployment-27997383   3         2         1         4m
nginx-deployment-3599678771   1         1         1         1h
nginx-deployment-27997383   3         2         1         4m
nginx-deployment-27997383   3         3         1         4m
nginx-deployment-27997383   3         3         2         5m
nginx-deployment-3599678771   0         1         1         1h
nginx-deployment-3599678771   0         1         1         1h
nginx-deployment-3599678771   0         0         0         1h

可以看到已经成功完成了合并了多次更新内容后的deployment任务:
[root@gqtest ~]# kubectl get rs
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-27997383     3         3         3         13m
nginx-deployment-3599678771   0         0         0         1h
nginx-deployment-4087004473   0         0         0         1h
nginx-deployment-538426637    0         0         0         1h

(6)Deployment 状态
Deployment 在生命周期中有多种状态。在创建一个新的 ReplicaSet 的时候它可以是 progressing 状态, complete 状态,或者 fail to progress 状态。
进行中的 Deployment
Kubernetes 将执行过下列任务之一的 Deployment 标记为 progressing 状态:
  • Deployment 正在创建新的ReplicaSet过程中。
  • Deployment 正在扩容一个已有的 ReplicaSet。
  • Deployment 正在缩容一个已有的 ReplicaSet。
  • 有新的可用的 pod 出现。
可以使用kubectl rollout status命令监控 Deployment 的进度。
完成的 Deployment
Kubernetes 将包括以下特性的 Deployment 标记为 complete 状态:
  • Deployment 最小可用。最小可用意味着 Deployment 的可用 replica 个数等于或者超过 Deployment 策略中的期望个数。
  • 所有与该 Deployment 相关的replica都被更新到了指定版本,也就说更新完成。
  • 该 Deployment 中没有旧的 Pod 存在。
可以用kubectl rollout status命令查看 Deployment 是否完成。如果 rollout 成功完成,kubectl rollout status将返回一个0值的 Exit Code。
[root@gqtest ~]# kubectl rollout status deploy/nginx-deployment
deployment "nginx-deployment" successfully rolled out
[root@gqtest ~]# echo $?
0

版本记录的清理策略
可以设置 Deployment 中的 .spec.revisionHistoryLimit 项来指定保留多少旧的 ReplicaSet。 余下的将在后台被当作垃圾收集。默认的,所有的 revision 历史就都会被保留。在未来的版本中,将会更改为2。
注意: 将该值设置为0,将导致所有的 Deployment 历史记录都会被清除,该 Deployment 就无法再回退了。
(7)金丝雀 Deployment
如果想要使用 Deployment 对部分用户或服务器发布 release,可以创建多个 Deployment,每个 Deployment 对应一个 release。
(8)编写 Deployment Spec
在所有的 Kubernetes 配置中,Deployment 也需要apiVersion,kind和metadata这些配置项。
Deployment也需要 .spec section.
Pod Template
  • .spec.template 是 .spec中唯一要求的字段。
  • .spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段。
  • 为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他controller重复了)和适当的重启策略。
  • .spec.template.spec.restartPolicy 可以设置为 Always , 如果不指定的话这就是默认配置。
Replicas
  • .spec.replicas 是可以选字段,指定期望的pod数量,默认是1。
Selector
  • .spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。
  • 如果被指定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝。如果 .spec.selector 没有被指定, .spec.selector.matchLabels 默认是 .spec.template.metadata.labels。
  • 在Pod的template跟.spec.template不同或者数量超过了.spec.replicas规定的数量的情况下,Deployment会杀掉label跟selector不同的Pod。
注意: 不应该再创建其他label跟这个selector匹配的pod,或者通过其他Deployment,或者通过其他Controller,例如ReplicaSet和ReplicationController。否则该Deployment会被把它们当成都是自己创建的。Kubernetes不会阻止这么做。如果有多个controller使用了重复的selector,controller们就会互相打架并导致不正确的行为。
策略
  • .spec.strategy 指定新的Pod替换旧的Pod的策略。 .spec.strategy.type 可以是"Recreate"或者是 "RollingUpdate"(按比例更新)。"RollingUpdate"是默认值。
  • Recreate Deployment,.spec.strategy.type==Recreate时,在创建出新的Pod之前会先杀掉所有已存在的Pod。
  • Rolling Update Deployment,.spec.strategy.type==RollingUpdate时,Deployment使用rolling update 的方式更新Pod 。可以指定maxUnavailable 和 maxSurge 来控制 rolling update 进程。
  • Max Unavailable,.spec.strategy.rollingUpdate.maxUnavailable 是可选配置项,用来指定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百分比(例如10%)。通过计算百分比的绝对值向下取整。例如,该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻可以用的Pod数量至少是期望Pod数量的70%。
  • Max Surge,.spec.strategy.rollingUpdate.maxSurge 是可选配置项,用来指定可以超过期望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如10%)。当MaxUnavailable为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。例如,该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。
Progress Deadline Seconds
  • .spec.progressDeadlineSeconds 是可选配置项,用来指定在系统报告Deployment的failed progressing——表现为resource的状态中type=Progressing、Status=False、 Reason=ProgressDeadlineExceeded前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到这种状态时就会自动回滚。
  • 如果设置该参数,该值必须大于 .spec.minReadySeconds。
Min Ready Seconds
  • .spec.minReadySeconds是一个可选配置项,用来指定没有任何容器crash的Pod并被认为是可用状态的最小秒数。默认是0(Pod在ready后就会被认为是可用状态)。
Rollback To
  • .spec.rollbackTo 是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。
Revision
  • .spec.rollbackTo.revision是一个可选配置项,用来指定回退到的revision。默认是0,意味着回退到历史中最老的revision。
Revision History Limit
  • Deployment revision history存储在它控制的ReplicaSets中。
  • .spec.revisionHistoryLimit 是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset或会被保留,将资源存储在etcd中,是用kubectl get rs查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦删除了旧的RepelicaSet,该Deployment就无法再回退到那个revison了。
  • 如果将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。
Paused
  • .spec.paused是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout。
  • Deployment被创建之后默认是非paused。

7、Horizontal Pod Autoscaler

横向自动扩容功能,简称HPA,也是k8s系统中的一种资源对象。在v1.1版本中首次发布,在v1.2版本中升级为稳定版。在v1.6版本之前,仅支持使用CPU负载作为是否扩容的判定条件;自v1.6版本开始提供了根据应用自定义指标进行自动扩容和缩容的功能,不过目前仍为实验性质。
可以通过yaml文件定义一个HPA对象,或者直接使用命令创建一个HPA对象。
yaml文件定义的样例:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
kink: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90

注:当名为php-apache的deployment的Pods副本的CPU使用率超过90%时,会触发自动扩容行为。但扩容或缩容都必须满足的约束条件是Pod的副本数量要在1~10之间。
以上为命令行方式创建一个HPA:
[root@gqtest ~]# kubectl autoscale deployment nginx-deployment --min=1 --max=5 --cpu-percent=80
deployment "nginx-deployment" autoscaled

8、StatefulSet

StatefulSet(有状态系统服务设计)在k8s v1.5中引入,在Kubernetes 1.7中还是beta特性。
现实中很多服务是有状态的,如MySQL集群、kafka集群、ZooKeeper集群等,这些应用集群有以下特点:
  • 每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现和通信;
  • 集群的规模是相对固定的,且不能随意变动;
  • 集群里每个节点都是有状态的,通常会持久化数据到永久存储中;
  • 如果磁盘损坏导致集群里某个节点无法正常运行,则集群功能会部分受损;
StatefulSet是Deployment/RC的一个特殊变种,有如下特性:
  • StatefulSet里每个Pod都有稳定、唯一的网络标识,可以用来发现集群内其他成员。假设StatefulSet名字叫kafka,那么第1个Pod会命名为kafka-0,第2个Pod叫kafka-1,以此类推。
  • StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
  • StatefulSet里的Pod采用稳定的持久化存储卷,通过PV/PVC实现,删除Pod时默认不会删除与StatefulSet相关的存储卷。
  • StatefulSet需要与Headless Service配合使用,需要在每个StatefulSet的定义中声明它属于哪个Headless Service。

Headless Service没有Cluster IP,当解析Headless Service的DNS域名时,得到的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上,又为受Headless Service控制的每个Pod实例创建了一个DNS域名,格式为:$(podname).$(headless service name)。

样例:一个3节点的kafka的StatefulSet集群
  • 该应用集群的headless service名称定义为kafka
  • 该应用集群的StatefulSet名字为kafka,则StatefulSet里的3个Pod的名称分别为:kafka-0,kafka-1,kafka-2
  • 该应用集群的StatefulSet中3个Pod的DNS名称应该是:kafka-0.kafka,kafka-1.kafka,kafka-2.kafka
可以在该应用集群的配置文件中直接使用上述DNS名称来代表相应的Pod。

9、Service

(1)Service的概念
k8s的Service定义了一个服务的访问入口地址,前端的应用通过这个入口地址访问其背后的一组由Pod副本组成的集群实例。Service与其后端Pod副本集群之间则是通过Label Selector来实现对接的。RC的作用相当于是保证Service的服务能力和服务质量始终处于预期的标准。一个 Service 在 Kubernetes 中是一个 REST 对象。
Service 定义可以基于 POST 方式,请求 apiserver 创建新的实例。 例如,假定有一组 Pod,它们对外暴露了 9376 端口,同时还被打上 "app=MyApp" 标签。
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
上述配置将创建一个名称为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 端口 9376,并且具有标签 "app=MyApp" 的 Pod 上。 这个 Service 将被指派一个 IP 地址(通常称为 “Cluster IP”)。 Service 能够将一个接收端口映射到任意的 targetPort。 默认情况下,targetPort 将被设置为与 port 字段相同的值。Kubernetes Service 能够支持 TCP 和 UDP 协议,默认 TCP 协议。
使用以下命令查看Service的yaml定义文件:
# kubectl get svc my-service -o yaml
(2)没有 selector 的 Service
Servcie 抽象了该如何访问 Kubernetes Pod,但也能够抽象其它类型的 backend,例如:
  • 希望在生产环境中使用外部的数据库集群。
  • 希望服务指向另一个 Namespace 中或其它集群中的服务。
  • 正在将工作负载同时转移到 Kubernetes 集群和运行在 Kubernetes 集群之外的 backend。
定义没有 selector 的 Service :
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376

由于这个 Service 没有 selector,就不会创建相关的 Endpoints 对象。可以手动将 Service 映射到指定的 Endpoints:
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 9376

注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。
访问没有 selector 的 Service,与有 selector 的 Service 的原理相同。请求将被路由到用户定义的 Endpoint(该示例中为 1.2.3.4:9376)。
ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。 相反地,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务。
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com

当查询主机 my-service.prod.svc.CLUSTER时,集群的 DNS 服务将返回一个值为 my.database.example.com 的 CNAME 记录。 访问这个服务的工作方式与其它的相同,唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。 如果后续决定要将数据库迁移到 Kubernetes 集群中,可以启动对应的 Pod,增加合适的 Selector 或 Endpoint,修改 Service 的 type。
(3)VIP 和 Service 代理
运行在每个Node上的kube-proxy进程其实就是一个智能的软件负载均衡器,它会负责把对Service的请求转发到后端的某个Pod实例上并在内部实现服务的负载均衡与会话保持机制。Service不是共用一个负载均衡器的IP,而是被分配了一个全局唯一的虚拟IP地址,称为Cluster IP。在Service的整个生命周期内,它的Cluster IP不会改变。 kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。在k8s v1.2版本之前默认使用userspace提供vip代理服务,从 Kubernetes v1.2 起,默认是使用 iptables 代理。
iptables 代理模式
这种模式,kube-proxy 会监视 Kubernetes master 对 Service 对象和 Endpoints 对象的添加和移除。 对每个 Service,它会创建相关 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求,进而将请求重定向到 Service 的一组 backend 中的某个上面。 对于每个 Endpoints 对象,它也会创建 iptables 规则,这个规则会选择一个 backend Pod。默认的策略是,随机选择一个 backend。 实现基于客户端 IP 的会话亲和性,可以将 service.spec.sessionAffinity 的值设置为 "ClientIP" (默认值为 "None")。
和 userspace 代理类似,网络返回的结果是,任何到达 Service 的 IP:Port 的请求,都会被代理到一个合适的 backend,不需要客户端知道关于 Kubernetes、Service、或 Pod 的任何信息。 这应该比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始选择的 Pod 没有响应,iptables 代理能够自动地重试另一个 Pod,所以它需要依赖  readiness probes。
(4)多端口 Service
很多 Service 需要暴露多个端口。对于这种情况,Kubernetes 支持在 Service 对象中定义多个端口。 当使用多个端口时,必须给出所有的端口的名称,这样 Endpoint 就不会产生歧义,例如:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
(5)选择自己的 IP 地址
在 Service 创建的请求中,可以通过设置 spec.clusterIP 字段来指定自己的集群 IP 地址。 比如,希望替换一个已经已存在的 DNS 条目,或者遗留系统已经配置了一个固定的 IP 且很难重新配置。 用户选择的 IP 地址必须合法,并且这个 IP 地址在 service-cluster-ip-range CIDR 范围内,这对 API Server 来说是通过一个标识来指定的。 如果 IP 地址不合法,API Server 会返回 HTTP 状态码 422,表示值不合法。
(6)服务发现
Kubernetes 支持2种基本的服务发现模式 —— 环境变量和 DNS。
环境变量
当 Pod 运行在 Node 上,kubelet 会为每个活跃的 Service 添加一组环境变量。 它同时支持 Docker links兼容 变量、简单的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 变量,这里 Service 的名称需大写,横线被转换成下划线。
举个例子,一个名称为 "redis-master" 的 Service 暴露了 TCP 端口 6379,同时给它分配了 Cluster IP 地址 10.0.0.11,这个 Service 生成了如下环境变量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
这意味着需要有顺序的要求 —— Pod 想要访问的任何 Service 必须在 Pod 自己之前被创建,否则这些环境变量就不会被赋值。DNS 并没有这个限制。
DNS
一个强烈推荐的集群插件 是 DNS 服务器。 DNS 服务器监视着创建新 Service 的 Kubernetes API,从而为每一个 Service 创建一组 DNS 记录。 如果整个集群的 DNS 一直被启用,那么所有的 Pod 应该能够自动对 Service 进行名称解析。
例如,有一个名称为 "my-service" 的 Service,它在 Kubernetes 集群中名为 "my-ns" 的 Namespace 中,为 "my-service.my-ns" 创建了一条 DNS 记录。 在名称为 "my-ns" 的 Namespace 中的 Pod 应该能够简单地通过名称查询找到 "my-service"。 在另一个 Namespace 中的 Pod 必须限定名称为 "my-service.my-ns"。 这些名称查询的结果是 Cluster IP。
Kubernetes 也支持对端口名称的 DNS SRV(Service)记录。 如果名称为 "my-service.my-ns" 的 Service 有一个名为 "http" 的 TCP 端口,可以对 "_http._tcp.my-service.my-ns" 执行 DNS SRV 查询,得到 "http" 的端口号。
Kubernetes DNS 服务器是唯一的一种能够访问 ExternalName 类型的 Service 的方式。 更多信息可以查看 DNS Pod 和 Service。
Kubernetes 从 1.3 版本起, DNS 是内置的服务,通过插件管理器 集群插件 自动被启动。Kubernetes DNS 在集群中调度 DNS Pod 和 Service ,配置 kubelet 以通知个别容器使用 DNS Service 的 IP 解析 DNS 名字。
(7)Headless Service
有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。
这个选项允许开发人员自由寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。
对这类 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了 selector。
配置 Selector
对定义了 selector 的 Headless Service,Endpoint 控制器在 API 中创建了 Endpoints 记录,并且修改 DNS 配置返回 A 记录(地址),通过这个地址直接到达 Service 的后端 Pod上。
不配置 Selector
对没有定义 selector 的 Headless Service,Endpoint 控制器不会创建 Endpoints 记录。 
(8)发布服务 —— type类型
对一些应用希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。
Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。
Type 的取值以及行为如下:
  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。
  • NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。
  • LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
  • ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。
k8s中有3种IP地址:
  • Node IP: Node节点的IP地址,这是集群中每个节点的物理网卡的IP地址;
  • Pod IP: Pod的IP地址,这是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络;
  • Cluster IP:Service 的IP地址,这也是一个虚拟的IP,但它更像是一个“伪造”的IP地址,因为它没有一个实体网络对象,所以无法响应ping命令。它只能结合Service Port组成一个具体的通信服务端口,单独的Cluster IP不具备TCP/IP通信的基础。在k8s集群之内,Node IP网、Pod IP网与Cluster IP网之间的通信采用的是k8s自己设计的一种编程实现的特殊的路由规则,不同于常见的IP路由实现。

10、Volume

(1)Volume概要知识
  • 默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题,第一:当容器挂掉kubelet将重启启动它时,文件将会丢失;第二:当Pod中同时运行多个容器,容器之间需要共享文件时。Kubernetes的Volume解决了这两个问题。
  • Kubernetes Volume具有明确的生命周期,与pod相同。因此,Volume的生命周期比Pod中运行的任何容器要持久,在容器重新启动时能可以保留数据,当然,当Pod被删除不存在时,Volume也将消失。
  • Kubernetes支持许多类型的Volume,Pod可以同时使用任意类型/数量的Volume。
  • 要使用Volume,pod需要指定Volume的类型和内容(spec.volumes字段),和映射到容器的位置(spec.containers.volumeMounts字段)。
Volume的配置样例:
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
volumes:
- name: datavol
emptyDir: {}
containers:
- name: tomcat-demo
image: tomcat
volumeMounts:
- mountPath: /mydata-data
name: datavol
imagePullPolicy: IfNotPresent      
更多的Volume类型配置样例可以参见: https://github.com/kubernetes/kubernetes/tree/master/examples/volumes
(2)Volume 类型
Kubernetes支持Volume类型有:
  • emptyDir
  • hostPath
  • gcePersistentDisk
  • awsElasticBlockStore
  • nfs
  • iscsi
  • fc (fibre channel)
  • flocker
  • glusterfs
  • rbd
  • cephfs
  • gitRepo
  • secret
  • persistentVolumeClaim
  • downwardAPI
  • projected
  • azureFileVolume
  • azureDisk
  • vsphereVolume
  • Quobyte
  • PortworxVolume
  • ScaleIO
  • StorageOS
  • local
(3)emptyDir
使用emptyDir时,当Pod分配到Node上时,将会创建一个emptyDir Volume,它的内容为空,并且只要Node上的Pod一直运行,Volume就会一直存在。当Pod(不管任何原因)从Node上被删除时,emptyDir也同时会删除,存储的数据也将永久删除。emptyDir Volume的存储介质(Disk,SSD等)取决于kubelet根目录(如/var/lib/kubelet)所处文件系统的存储介质。不限制emptyDir或hostPath Volume使用的空间大小,不对容器或Pod的资源隔离。
注:删除容器不影响emptyDir。
emptyDir的一些用途:
  • 临时空间
  • 一个容器需要从另一个容器中获取数据的目录
示例:
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: gcr.io/google_containers/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
(4)hostPath
hostPath允许挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。
通常有以下两种使用场景:
  • 容器应用程序生成的日志文件需要永久保存时,可以使用宿主机的文件系统进行存储。
  • 需要访问宿主机上Docker引擎内部数据结构的容器应用时,可以通过定义hostPath为宿主机/var/lib/docker目录,使容器内部应用可以直接访问Docker的文件系统。
使用hostPath的注意事项:
  • 在不同的Node上具有相同配置的Pod,可能会因为宿主机上的目录和文件不同而导致对Volume上目录和文件的访问结果不一致。
  • 如果使用了资源配额管理,则k8s无法将hostPath在宿主机上使用的资源纳入管理。
hostPath使用示例:
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
(5)NFS
Kubernetes中通过简单地配置就可以挂载NFS到Pod中,而NFS中的数据是可以永久保存的,同时NFS支持同时写操作。Pod被删除时,Volume被卸载,内容被保留。这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递。
NFS使用示例(PV):nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs
spec:
  capacity:
    storage: 1Mi
  accessModes:
    - ReadWriteMany
  nfs:
    # FIXME: use the right IP
    server: 10.244.1.4
path: "/exports"

11、Persistent Volume(PV)

  • PersistentVolume子系统为用户和管理员提供了一个API,它抽象了如何提供存储以及如何使用它的细节。 为此,我们引入两个新的API资源:PersistentVolume和PersistentVolumeClaim。
  • PersistentVolume(PV)是管理员设置的群集中的一部分存储。 它就像集群中的一个资源,就像一个节点是集群资源一样。 PV是类似Volumes的插件,但具有独立于Pod的生命周期。 此API对象捕获存储实现的细节,即NFS,iSCSI或特定于云提供程序的存储系统。
  • PersistentVolumeClaim(PVC)是用户存储的请求。 它与Pod相似。 Pod消耗节点资源,PVC消耗PV资源。 Pod可以请求特定级别的资源(CPU和内存)。 PVC声明可以请求特定的存储空间大小和访问模式(例如,可以一次读/写或多次只读)。
PV可以理解成是k8s集群中的某个网络存储中对应的一块存储资源,它与Volume很类似,但有以下区别:
  • PV只能是网络存储,不属于任何Node,但可以在每个Node上访问;
  • PV不是定义是Pod上的,而是独立定义的;
  • PV目前支持的存储类型包括:gcePersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC、Flocker、NFS、iSCSI、RBD、CephFS、Cinder、GlusterFS、Vsphere Volume、Quobyte Volumes、VMware Photon、Portworx Volumes、ScaleIO Volumes和HostPath(仅支持单机测试使用)。
比较重要的PV的accessModes属性:
  • ReadWriteOnce: 读写权限,只能被单个Node挂载;
  • ReadOnlyMany: 只读权限,允许被多个Node挂载;
  • ReadWriteMany: 读写权限,允许被多个Node挂载;
PV的几种状态:
  • Available
  • Bound
  • Released: 对应的PVC已经删除,但资源还没有被集群回收。
  • Failed:PV自动回收失败。
PV/PVC使用示例:nfs-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: my-nfs
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi

注:PV的示例参见上一段落的NFS使用示例。
然后,在Pod的Volume定义中引用上述PVC即可:
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: my-nfs

12、Namespace

Kubernetes可以使用Namespaces(命名空间)创建多个虚拟集群。当团队或项目中具有许多用户时,可以考虑使用Namespace来实现多租户的资源隔离。
  • Namespace为名称提供了一个范围。资源的Names在Namespace中具有唯一性。
  • Namespace是一种将集群资源划分为多个用途(通过 resource quota)的方法。
  • 在未来的Kubernetes版本中,默认情况下,相同Namespace中的对象将具有相同的访问控制策略。
  • k8s集群启动后,会创建一个名为"default"的Namespace,如果不特别指明一个资源对象的Namespace属性,则用户创建的资源对象都会使用默认的"default"。
大多数Kubernetes资源(例如pod、services、replication controllers或其他)都在某些Namespace中,但Namespace资源本身并不在Namespace中。而低级别资源(如Node和persistentVolumes)不在任何Namespace中。Events是一个例外:它们可能有也可能没有Namespace,具体取决于Events的对象。
创建Namespace
(1) 命令行直接创建
$ kubectl create namespace new-namespace
(2) 通过文件创建
$ cat my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: new-namespace

$ kubectl create -f ./my-namespace.yaml
注意:命名空间名称满足正则表达式[a-z0-9]([-a-z0-9]*[a-z0-9])?,最大长度为63位
删除Namespace
$ kubectl delete namespaces new-namespace
注意:
  • 删除一个namespace会自动删除所有属于该namespace的资源。
  • default和kube-system命名空间不可删除。
  • PersistentVolumes是不属于任何namespace的,但PersistentVolumeClaim是属于某个特定namespace的。
  • Events是否属于namespace取决于产生events的对象。
查看 Namespace
使用以下命令列出群集中的当前的Namespace:
$ kubectl get namespaces
NAME STATUS AGE
default Active 1d
kube-system Active 1d

Kubernetes从两个初始的Namespace开始:
  • default
  • kube-system 由Kubernetes系统创建的对象的Namespace
将一个资源对象放入名为"development"的命名空间里:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox

设置请求的名称空间
要临时设置Request的Namespace,请使用--namespace 标志。
例如:
$ kubectl --namespace=<insert-namespace-name-here> run nginx --image=nginx
$ kubectl --namespace=<insert-namespace-name-here> get pods

注:如果不加--namespace参数,kubectl get将仅显示属于"default"命名空间的资源对象。
可以使用kubectl命令将创建的Namespace可以永久保存在context中。
$ kubectl config set-context $(kubectl config current-context) --namespace=<insert-namespace-name-here>
# Validate it
$ kubectl config view | grep namespace:

13、Annotation(注解)

Annotation与Label类似,也使用key/value键值对的形式定义。可以使用Kubernetes Annotations将任何非标识metadata附加到对象。客户端(如工具和库)可以检索此metadata。
可以使用Labels或Annotations将元数据附加到Kubernetes对象。标签可用于选择对象并查找满足某些条件的对象集合。相比之下,Annotations不用于标识和选择对象。Annotations中的元数据可以是small 或large,structured 或unstructured,并且可以包括标签不允许使用的字符。
Annotations就如标签一样,也是由key/value组成:
"annotations": {
"key1" : "value1",
"key2" : "value2"
}

以下是在Annotations中记录信息的一些例子:
  • 构建、发布的镜像信息,如时间戳,发行ID,git分支,PR编号,镜像hashes和注Registry地址。
  • 一些日志记录、监视、分析或audit repositories。
  • 一些工具信息:例如,名称、版本和构建信息。
  • 用户或工具/系统来源信息,例如来自其他生态系统组件对象的URL。
  • 负责人电话/座机,或一些信息目录。
注意:Annotations不会被Kubernetes直接使用,其主要目的是方便用户阅读查找。

参考1:《Kubernetes权威指南——从Docker到Kubernetes实践全接触》第1章。

参考2:Kubernetes中文社区 | 中文文档

k8s技术预研2--Kubernetes中的13项重要概念或术语相关推荐

  1. k8s技术预研7--深入掌握Kubernetes Pod

    目录 1.Yaml格式的Pod定义文件完整模板详解 2.Pod的基本用法     2.1 由1个容器组成的Pod示例     2.2 由两个为紧耦合关系的容器打包组成的Pod示例 3.静态Pod    ...

  2. k8s技术预研8--深入掌握Kubernetes Service

    本文内容已经基于k8s v1.8.8进行了验证测试. k8s的Service定义了一个服务的访问入口地址,前端的应用通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,来自外部的访问请求被负载 ...

  3. k8s技术预研13--kubernetes共享存储原理与动态存储供应用使用示例

    1.共享存储机制概述 Kubernetes对于有状态的容器应用或者对于数据需要持久化的应用,不仅需要将容器内的目录挂载到宿主机的目录或者emptyDir临时存储卷,而且需要更加可靠的存储来保存应用产生 ...

  4. k8s技术预研3--使用kubeadm安装、配置Kubernetes集群以及进行故障排查的方法

    一.软硬件环境 采用CentOS7.4 minimual,docker 1.12,kubeadm 1.7.5,etcd 3.0, k8s 1.7.6 本章节以下配置内容需要在全部节点上都做配置.我们这 ...

  5. 基于CKEditor网页富文本编辑工具转PDF文件的技术预研分析报告

    目  录 基于CKEditor网页富文本编辑工具转PDF文件的技术预研分析报告 1 1. 预研背景 4 2. 预研目的和意义 4 3. 预研目标 4 4. 预研技术概述 4 5. 预研技术实现 7 5 ...

  6. 技术人文|声音捐赠,一场PBL式技术预研实验

    你有没有想过,在不能说话的情况下该如何向不识字的摊主点不加香菜的二两毛细(拉面)? "用手比划?"  "画在纸上?"  "找别人帮忙?" 这 ...

  7. k8s与CICD--将drone部署到kubernetes中,实现agent动态收缩

    前言 本文主要讲如何把drone部署到k8s集群当中,本身drone这种基于容 器的pipeline方式,和k8s是相当契合的.这样的好处有: k8s集群守护drone-server 和drone-a ...

  8. 容器编排技术 -- 使用Minikube在Kubernetes中运行应用

    容器编排技术 -- 使用Minikube在Kubernetes中运行应用 1 目标 2 准备工作 3 创建Minikube集群 4 创建Node.js应用程序 5 创建Docker容器镜像 6 创建D ...

  9. 附录H-2 技术预研报告

    目 录 0. 文档介绍 4 摘要 4 读者对象 4 参考文档 4 术语与缩写解释 4 背景介绍 5 技术预研目标 5 技术预研取得的工作成果 5 技术A的研究报告 5 技术B的研究报告 5 文档介绍 ...

最新文章

  1. 查询远程或本地计算机的登录账户
  2. CentOS 7 用yum安装 MySQL
  3. 如何实现控制台清屏?(借鉴)
  4. [翻译]QUIC 与 HTTP/3:太过庞大而致失败?-- 论导致 QUIC 失败的因素
  5. virtual box虚拟机分区后下一步看不见解决
  6. eclipse 不能切换输入法
  7. anaconda3 安装tensorflow 报Cannot remove entries fro
  8. c#namespace
  9. Qt 周立功USBCAN总线上位机
  10. win10禁用驱动程序强制签名_Win10系统永久禁用驱动数字签名的方法是什么?
  11. STM8S003F3 内部时钟初始化以及定时器做延时的使用
  12. Field of view xxxx underlying table doesn't have a default value 的一种解决方法
  13. nmap命令man详解与脚本目录
  14. Visual studio2022“无法生成.exe文件,系统找不到指定文件
  15. 用verilog编写按键消抖代码
  16. 基于52840 S340协议栈USB flash U盘实现
  17. 编程和乐高机器人的区别
  18. 广丰计算机技术学院,广丰区五都镇中学祝晓旺——信息技术教育的拓荒者
  19. 10岁娃获“信息学奥赛”省一等奖
  20. 汇编语言中常用进制数据输出的程序实现

热门文章

  1. linux访问端口没有主机路由,ftp: connect: 没有到主机的路由
  2. 杰理之蓝牙播歌和蓝牙通话部分及蓝牙发射器【篇】
  3. SHA1算法的编程实现
  4. JAVA的那个蜥蜴是什么_你知道爬行脑是什么脑吗?
  5. 最小二乘法实现曲线拟合
  6. 一款在线制图工具介绍:如何在线免费绘制UML,云架构,ER模型,平面图,流程图等-...
  7. 群晖3617可以有几个网卡_NAS搭建记录(群晖DS218+):锂电池UPS方案/功耗测试
  8. linux的 /home ,home/ ,/home/ 的区别
  9. 做自媒体视频剪辑可以当成副业来赚钱吗?
  10. ubuntu12.04 set python2.7 opencv2.4.9