1.前言
kubernetes 项目发展至今,社区出现了很多非常优秀的特性,这些特性极大地扩展了Kubernetes的能力。目前很多公司基于Google Kubernetes 和 Docker 打造各自的私有化PaaS平台,这些高级特性往往是PaaS平台需要的,深入了解这些特性能够帮助架构设计人员快速确定技术方案,达到事半功倍的效果。目前很多PaaS平台都通过开放Kubernetes 特性,获得了很好的效果。
2.特性介绍
1)集群联邦
用户在生产环境部署微服务时有一个非常明确的需求,那就是希望服务部署能够跨zone、跨区域、跨集群甚至跨云边界。相对于单个集群的服务部署模式,跨集群服务能够提供按地域分布的服务,支持混合云、多云场景,全面提升服务的高可用等级。服务使用者希望服务无论在集群内部还是外部,都有稳定、一致的连接。
从Kubernetes的角度来说,以前只针对一个集群进行管理即可,加入集群联邦特性之后,就需要一个”集群控制器”,通过这个控制器控制各个集群。各个集群在集群控制器中有相应的标识,发送的命令通过集群控制器进行转发。
从上图可以看出,用户通过UI、CLI、API发起的请求,最终汇集到集群联邦控制面板,集群控制面板负责与各个集群进行交互。更为详细信息请查看:
http://blog.kubernetes.io/2016/07/cross-cluster-services.html
2) 有状态应用
众所周知,Pod在Kubernetes中是会发生漂移的。比如节点出现故障,应用会迁移到正常节点。对于无状态应用,这是没有问题的,但是针对有状态应用,这种方式则不可取,因为迁移之后,应用的状态(比如写在节点上的临时文件)不会随着应用迁移,会引起状态不一致的问题。比如Mysql,Mongodb,Zookeeper等引用,必须挂载数据卷并且支持稳定的访问。
无状态的概念,就是只负责运算,不负责任何数据的存储,这样就能很轻松地做到水平扩展。对于无状态应用,如何做到水平扩展呢?谷歌提出了解决方案,Pod负责运行无状态应用,PetSet负责有状态应用。
PetSet 作为运行有状态应用的基础,需要具体以下条件:
* 有唯一的编号
* 在网络上有一个不会改变的标识,k8s是通过域名实现的。pod则是名字后面还有随机数,所以需要有service来做转发
* 每个有状态服务,都需要有自己的卷,这样就能保证数据可靠存储
PetSet.yaml 如下:
# A headless service to create DNS records
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
# *.nginx.default.svc.cluster.local
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1alpha1
kind: PetSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
app: nginx
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
terminationGracePeriodSeconds: 0
containers:
- name: nginx
image: gcr.io/google_containers/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
annotations:
volume.alpha.kubernetes.io/storage-class: anything
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
从yaml中可以看出,Service以PetSet中的Pet作为后端Pod,并且PetSet挂载了持久化存储。同时,PetSet支持以下功能:
* Peer discovery
* PetSet Update
* PetSet 扩容
* 镜像升级
* PetSet删除(仅仅删除PetSet不会删除挂载的持久化存储,如果要在删除PetSet同时删除PVC,需要使用–cascade=false参数)
Kubernetes Petset 官方文档:
http://kubernetes.io/docs/user-guide/petset/
3)Init-Container
在Pod真正运行起来之前,通常有一些初始化的动作需要做,比如初始化本地数据(可能是一些配置)。Pod里面的多个Container(至少一个),在挂载volume之后,并行地进行初始化。
K8S 为了解决Container在运行之前的初始化工作,引入了init-container的概念。一个Init container的定义如下:
apiVersion: v1
kind: Pod
metadata:
name: kubectl
annotations:
pod.alpha.kubernetes.io/init-containers: '[
{
"name": "download",
"image": "busybox",
"command": [
"wget",
"-O",
"/work-dir/kubectl",
"http://storage.googleapis.com/kubernetes-release/release/v1.2.4/bin/linux/amd64/kubectl"
],
"volumeMounts": [
{
"name": "workdir",
"mountPath": "/work-dir"
}
]
},
{
"name": "chmod",
"image": "busybox",
"command": ["chmod", "755", "/work-dir/kubectl"],
"volumeMounts": [
{
"name": "workdir",
"mountPath": "/work-dir"
}
]
}
]'
spec:
containers:
- name: kubectl
image: busybox
command:
- /bin/sleep
- "36000"
volumeMounts:
- name: workdir
mountPath: /usr/local/bin
dnsPolicy: Default
volumes:
- name: workdir
emptyDir: {}
这里的init container包含两个容器,一个负责下载,另一个负责修改权限。在busybox加载之前完成所有操作,这非常符合init container的设计意图。
这个应用场景是将kubectl容器化处理,首先现在kubectl二进制,然后切换执行权限,一旦Kubectl二进制有版本变更,仅需要升级二进制,重启pod即可。使用方式如下:
kubectl exec -t -i kubectl kubectl version
Client Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.4", GitCommit:"3eed1e3be6848b877ff80a93da3785d9034d0a4f", GitTreeState:"clean"}
Server Version: version.Info{Major:"1", Minor:"3+", GitVersion:"v1.3.0-beta.2", GitCommit:"caf9a4d87700ba034a7b39cced19bd5628ca6aa3", GitTreeState:"clean"}
官方设计文档在此:
https://github.com/kubernetes/kubernetes/blob/master/docs/proposals/container-init.md
4)configmap
应用通常需要很多配置参数,这些配置参数通常都以Container环境变量的形式传入容器内部,能够有一个k8s对象,保存通用的配置参数呢?
k8s提供了configmap对象,这个对象存储了map(key、value)形式的参数。一个configmap定义如下:
kind: ConfigMap
apiVersion: v1
metadata:
creationTimestamp: 2016-02-18T19:14:38Z
name: example-config
namespace: default
data:
example.property.1: hello
example.property.2: world
example.property.file: |-
property.1=value-1
property.2=value-2
property.3=value-3
configmap使用场景如下:
* ConfigMap 作为环境变量使用
configmap:
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
special.type: charm
Pod:
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/busybox
command: [ "/bin/sh", "-c", "env" ]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
- name: SPECIAL_TYPE_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.type
restartPolicy: Never
* 作为命令行参数
configmap:
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
special.type: charm
pod:
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/busybox
command: [ "/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.how
- name: SPECIAL_TYPE_KEY
valueFrom:
configMapKeyRef:
name: special-config
key: special.type
restartPolicy: Never
* 挂载卷
configmap:
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
special.type: charm
pod:
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: gcr.io/google_containers/busybox
command: [ "/bin/sh", "-c", "cat /etc/config/special.how" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: special-config
restartPolicy: Never
官方资料在此:http://kubernetes.io/docs/user-guide/configmap/
3. Template
在kubernetes中,创建应用涉及到的对象比较多,通常需要很多参数。用户如果没有模版,每次都需要配置很多参数。在反复创建过程中,效率很低。
K8S 提供了模版能力,开放该能力能够帮助用户快速创建应用。一个应用模版实例如下:
MySql:
https://github.com/openshift/origin/blob/master/examples/db-templates/mysql-persistent-template.json
Jekins:
https://github.com/openshift/origin/blob/master/examples/jenkins/jenkins-persistent-template.json
目前主要使用PodTemplate,里面主要涉及Pod的定义(镜像、挂卷等等)
// PodSpec is a description of a pod
type PodSpec struct {
Volumes []Volume `json:"volumes"`
// List of initialization containers belonging to the pod.
InitContainers []Container `json:"-"`
// List of containers belonging to the pod.
Containers []Container `json:"containers"`
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty"`
// Optional duration in seconds the pod needs to terminate gracefully. May be decreased in delete request.
// Value must be non-negative integer. The value zero indicates delete immediately.
// If this value is nil, the default grace period will be used instead.
// The grace period is the duration in seconds after the processes running in the pod are sent
// a termination signal and the time when the processes are forcibly halted with a kill signal.
// Set this value longer than the expected cleanup time for your process.
TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty"`
// Optional duration in seconds relative to the StartTime that the pod may be active on a node
// before the system actively tries to terminate the pod; value must be positive integer
ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,omitempty"`
// Required: Set DNS policy.
DNSPolicy DNSPolicy `json:"dnsPolicy,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
// ServiceAccountName is the name of the ServiceAccount to use to run this pod
// The pod will be allowed to use secrets referenced by the ServiceAccount
ServiceAccountName string `json:"serviceAccountName"`
// NodeName is a request to schedule this pod onto a specific node. If it is non-empty,
// the scheduler simply schedules this pod onto that node, assuming that it fits resource
// requirements.
NodeName string `json:"nodeName,omitempty"`
// SecurityContext holds pod-level security attributes and common container settings.
// Optional: Defaults to empty. See type description for default values of each field.
SecurityContext *PodSecurityContext `json:"securityContext,omitempty"`
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
// If specified, these secrets will be passed to individual puller implementations for them to use. For example,
// in the case of docker, only DockerConfig type secrets are honored.
ImagePullSecrets []LocalObjectReference `json:"imagePullSecrets,omitempty"`
// Specifies the hostname of the Pod.
// If not specified, the pod's hostname will be set to a system-defined value.
Hostname string `json:"hostname,omitempty"`
// If specified, the fully qualified Pod hostname will be "<hostname>.<subdomain>.<pod namespace>.svc.<cluster domain>".
// If not specified, the pod will not have a domainname at all.
Subdomain string `json:"subdomain,omitempty"`
}
3.总结
kubernetes 发展迅速,增加了很多新特性,本篇的总结仅作为开端,后续会介绍其他更多特性。