Pod只是一个逻辑概念,实质是一组共享了某些资源的容器,是Kubernetes的最小调度单位
一般做法:容器A共享容器B$ docker run --net=B --volumes-from=B --name=A image-A ...
,这种做法会存在一个问题,容器AB的关系从对等变成了拓扑。
骚操作:那是否可以让Pod内所有的容器都有一个顶级拓扑【中间容器Infra】,在Pod中这个容器永远第一个被创建,其它容器Join进来就完事了。Infra占用很少的资源,只有100~200K,且永远处于Pause状态。
你可以把Pod理解为“虚拟机”,把容器理解为“用户进程”,因此,以“虚拟机”角度来看,凡是调度、网络,存储,以及安全相关的属性,都是Pod维度的。
这个状态意味着,Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中。但是,这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。
这个状态下,Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。
这个状态意味s着,Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。
这个状态下,Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。
这是一个异常状态,意味着 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver,这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题。
绑定Node和Pod
apiVersion: v1
kind: Pod
...
spec:
nodeSelector:
disktype: ssd //标签
HostAlias
定义了 Pod 的 hosts 文件
apiVersion: v1
kind: Pod
...
spec:
hostAliases:
- ip: "10.1.2.3"
hostnames:
- "foo.remote"
- "bar.remote"
...
启动容器,在Pod /etc/hosts里可以查看到如下:
cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
...
10.244.135.10 hostaliases-pod
10.1.2.3 foo.remote
10.1.2.3 bar.remote
spec:
shareProcessNamespace: true
这样,我们就可以在 shell 容器里执行 ps 指令,查看所有正在运行的进程:
$ kubectl attach -it nginx -c shell
/ # ps ax
PID USER TIME COMMAND
1 root 0:00 /pause
8 root 0:00 nginx: master process nginx -g daemon off;
14 101 0:00 nginx: worker process
15 root 0:00 sh
21 root 0:00 ps ax
spec:
hostNetwork: true
hostIPC: true
hostPID: true
docker相关属性,Image(镜像)、Command(启动命令)、workingDir(容器的工作目录)、Ports(容器要开发的端口),以及 volumeMounts(容器要挂载的 Volume)都是构成 Kubernetes 项目中 Container 的主要字段
钩子
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: test-liveness-exec
spec:
containers:
- name: liveness
image: busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5 #容器启动后5秒执行
periodSeconds: 5 #每隔5秒执行一次
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
test-liveness-exec 1/1 Running 0 10s
$ kubectl describe pod test-liveness-exec
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
--------- -------- ----- ---- ------------- -------- ------ -------
2s 2s 1 {kubelet worker0} spec.containers{liveness} Warning Unhealthy Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
这个时候可以发现,文件不存在,检测报告容器不正常
$ kubectl get pod test-liveness-exec
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
发现容器的状态还是Running,为啥?这就是容器的恢复机制restartPolicy
返璞归真
HTTP方式
...
livenessProbe:
httpGet:
path: /healthz #Pod暴露健康检查URL,在 Web 服务类的应用中非常常用
port: 8080
httpHeaders:
- name: X-Custom-Header
value: Awesome
initialDelaySeconds: 3
periodSeconds: 3
TCP方式
...
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
创建
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque #不透明
data:
user: YWRtaW4=
pass: MWYyZDFlMmU2N2Rm
使用
spec:
volumes:
- name: mysql-cred
projected:
sources:
- secret:
name: user
- secret:
name: pass
与Secret 的区别在于,ConfigMap 保存的是不需要加密的、应用所需的配置信息
创建
apiVersion: v1
kind: ConfigMap
metadata:
name: mysecret
data:
mysql.user: zhangsan
mysql.pass: 123456
使用
spec:
template:
spec:
containers:
env:
- name: MYSQL_SERVICE_USER
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.user
- name: MYSQL_SERVICE_PASSWORD
valueFrom:
configMapKeyRef:
name: nacos-cm
key: mysql.password
让 Pod 里的容器能够直接获取到这个 Pod API 对象本身的信息
apiVersion: v1
kind: Pod
metadata:
name: test-downwardapi-volume
labels:
zone: us-est-coast
cluster: test-cluster1
rack: rack-22
spec:
containers:
- name: client-container
image: k8s.gcr.io/busybox
command: ["sh", "-c"]
args:
- while true; do
if [[ -e /etc/podinfo/labels ]]; then
echo -en '\n\n'; cat /etc/podinfo/labels; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
readOnly: false
volumes:
- name: podinfo
projected:
sources:
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels #暴露 Pod 的 metadata.labels 信息给容器
通过这样的声明方式,当前 Pod 的 Labels 字段的值,就会被 Kubernetes 自动挂载成为容器里的 /etc/podinfo/labels 文件
一种特殊的 Secret
Kubernetes 其实在每个 Pod 创建的时候,自动在它的 spec.volumes 部分添加上了默认 ServiceAccountToken 的定义,然后自动给每个容器加上了对应的 volumeMounts 字段。如下:
$ kubectl describe pod nginx-deployment-5c678cfb6d-lg9lw
Containers:
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-s8rbq (ro)
Volumes:
default-token-s8rbq:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-s8rbq
Optional: false
这种把 Kubernetes 客户端以容器的方式运行在集群里,然后使用 default Service Account 自动授权的方式,被称作“InClusterConfig”,也是最推荐的进行 Kubernetes API 编程的授权方式。
Pod 的字段这么多,我又不可能全记住,Kubernetes 能不能自动给 Pod 填充某些字段呢?
想在开发人员编写的 Pod 里追加的字段,都可以预先定义好
Pod 这种“超亲密关系”容器的设计思想,实际上就是希望,当用户想在一个容器里跑多个功能并不相关的应用时,应该优先考虑它们是不是更应该被描述成一个 Pod 里的多个容器
apiVersion: v1
kind: Pod
metadata:
name: javaweb-2
spec:
initContainers:
- image: geektime/sample:v2
name: war
command: ["cp", "/sample.war", "/app"]
volumeMounts:
- mountPath: /app
name: app-volume
containers:
- image: geektime/tomcat:7.0
name: tomcat
command: ["sh","-c","/root/apache-tomcat-7.0.42-v2/bin/start.sh"]
volumeMounts:
- mountPath: /root/apache-tomcat-7.0.42-v2/webapps
name: app-volume
ports:
- containerPort: 8080
hostPort: 8001
volumes:
- name: app-volume
emptyDir: {}
思路:利用InitContainer,先把war包copy到war-volume,然后让webapps挂载到war-volume。
思路:同样,sidecar容器的主要工作也是使用共享volume,完成日志文件的操作,原理同案例一。
控制器模式和“驱动模式有什么区别”
kubernetes对于容器资源编排的核心原理就是:控制循环(control loop)
for {
实际状态 := 获取集群中对象X的实际状态(Actual State)
期望状态 := 获取集群中对象X的期望状态(Desired State)
if 实际状态 == 期望状态{
什么都不做
} else {
执行编排动作,将实际状态调整为期望状态
}
}
Pod的水平扩展/收缩,滚动更新都依赖于ReplicaSet(Api对象)
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-set
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
跟Deployment的定义没啥区别蛮(实质就是Deployment定义的一个子集),而Deployment实际操作的正是ReplicaSet而不是Pod(层层控制)。Deployment,ReplicaSet,Pod之间的关系如下图:
其中,ReplicaSet 负责通过“控制器模式”,保证系统中 Pod 的个数永远等于指定的个数(比如,3 个)。这也正是 Deployment 只允许容器的 restartPolicy=Always 的主要原因:只有在容器能保证自己始终是 Running 状态的前提下,ReplicaSet 调整 Pod 的个数才有意义。而在此基础上,Deployment 同样通过“控制器模式”,来操作 ReplicaSet 的个数和属性,进而实现“水平扩展 / 收缩”和“滚动更新”这两个编排动作。其中,“水平扩展 / 收缩”非常容易实现,Deployment Controller 只需要修改它所控制的 ReplicaSet 的 Pod 副本个数就可以了。
$ kubectl scale deployment nginx-deployment --replicas=4
deployment.apps/nginx-deployment scaled
$ kubectl get deployments
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 3 0 0 0 1s
$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out
$ kubectl edit deployment/nginx-deployment
...
spec:
containers:
- name: nginx
image: nginx:1.91 # 1.7.9 -> 1.91
ports:
- containerPort: 80
...
deployment.extensions/nginx-deployment edited
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1764197365 2 2 2 24s
nginx-deployment-3167673210 0 0 0 35s #为啥这里多了一条
nginx-deployment-2156724341 2 2 0 7s
由于这个 nginx:1.91 镜像在 Docker Hub 中并不存在,所以这个 Deployment 的“滚动更新”被触发后,会立刻报错并停止。
$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 kubectl create -f nginx-deployment.yaml --record
2 kubectl edit deployment/nginx-deployment
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.91
$ kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.extensions/nginx-deployment
我们对 Deployment 进行的每一次更新操作,都会生成一个新的 ReplicaSet 对象,是不是有些多余,甚至浪费资源呢?
所以,Kubernetes 项目还提供了一个指令,使得我们对 Deployment 的多次更新操作,最后 只生成一个 ReplicaSet。具体的做法是,在更新 Deployment 前,你要先执行一条 kubectl rollout pause 指令。它的用法如下所示:
$ kubectl rollout pause deployment/nginx-deployment
deployment.extensions/nginx-deployment paused
修改之后执行
$ kubectl rollout resume deployment/nginx-deployment
deployment.extensions/nginx-deployment resumed
查看rs
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-1764197365 0 0 0 2m
nginx-3196763511 3 3 3 28s
只有一个 hash=3196763511 的 ReplicaSet 被创建了出来
无状态应用管理器
携带标签app=nginx的Pod数,被控制为2
控制器管理的Pod对象通常由template来定义(实际就是Pod定义)。在上图中被Deployment控制的Pod就是根据template创建出来的。
有状态应用,Statefulset概念中的状态究竟是指?从真实应用中可以抽象出俩种状态:1.拓扑;2.存储
先后顺序
访问Service的IP地址(VIP),由service代理到Pod
Headless Service 的区别在于,不需要分配VIP,可以直接以DNS的方式解析出Pod的IP地址
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx" #使用ServiceName告诉StatefulSet控制器,通过Headless的方式解析Pod地址
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.9.1
ports:
- containerPort: 80
name: web
$ kubectl exec web-0 -- sh -c 'hostname'
web-0 #pod名-编号
$ kubectl exec web-1 -- sh -c 'hostname'
web-1
$ kubectl run -i --tty --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
$ nslookup web-0.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local #pod名.service名.svc.cluster.local
Name: web-0.nginx
Address 1: 10.244.1.7
$ nslookup web-1.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-1.nginx
Address 1: 10.244.2.7
$ kubectl run -i --tty --image busybox dns-test --restart=Never --rm /bin/sh
$ nslookup web-0.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local #dns不会改变
Name: web-0.nginx
Address 1: 10.244.1.8 #ip会变
$ nslookup web-1.nginx
Server: 10.0.0.10
Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-1.nginx
Address 1: 10.244.2.8
存储数据
PVC、PV 的设计,也使得 StatefulSet 对存储状态的管理成为了可能
PV(Persistence Volume),按字面可以理解为持久化卷,那PVC又是什么鬼?PVC(Persistence Volume Claim),持久化卷定义,按我的理解,它其实就是一个声明,类似于“接口与实现”的关系。这样一来,开发人员只需要简单的配置即可(复杂的卷的配置,让运维去做吧,反正不是我的锅)
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pv-claim #此处配置PV一样
spec:
accessModes:
- ReadWriteOnce #读写权限配置一下
resources:
requests:
storage: 1Gi #配置下大小,so easy,妈妈再也不用担心我不会配置卷了
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.9.1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates: #看这里
- metadata:
name: www
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
不是说好的PVC么,这又是个什么玩意?从名字上看,好像是个template,好像和PodTemplate一个作用。也就是说,凡是被这个Statefulset管理的Pod都会声明一个PVC(说了半天,它就是个PVC的模板工厂)。
$ kubectl create -f statefulset.yaml
$ kubectl get pvc -l app=nginx
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
www-web-0 Bound pvc-15c268c7-b507-11e6-932f-42010a800002 1Gi RWO 48s
www-web-1 Bound pvc-15c79307-b507-11e6-932f-42010a800002 1Gi RWO 48s
上边可以看出来命名规则:<PVC 名字 >-<StatefulSet 名字 >-< 编号 >
apiVersion: extensions/v1beta1
kind: ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
name: my-ingress
namespace: default
spec:
rules:
- host: www.hccqn.com
http:
paths:
- backend:
serviceName: nginx
servicePort: 80
path: /
容器化守护进程,运行在Kubernetes集群中的每一个节点,并且每个节点只有一个实例。
那这个Pod到底有什么用呢?比如:网络插件,存储插件的Agent组件,各种监控组件和日志组件,必须运行在每一个节点上。
DaemonSet 只管理 Pod 对象,然后通过 nodeAffinity 和 Toleration 这两个调度器的小功能,保证了每个节点上有且只有一个 Pod。
Job Controller实际控制的作业的并行度,总共需完成的任务数俩个重要参数。Cron Job顾名思义就是定时Job
命令式命令行操作
$ docker service create --name nginx --replicas 2 nginx
$ docker service update --image nginx:1.7.9 nginx
命令式配置文件操作
$ kubectl create -f nginx.yaml
$ kubectl replace -f nginx.yaml
声明式 API
$ kubectl apply -f nginx.yaml
声明式API跟命令式操作有什么区别?
是否一次能处理多个写操作,并且具备 Merge 能力
如何使用控制器模式,同 Kubernetes 里 API 对象的“增、删、改、查”进行协作,进而完成用户业务逻辑的编写过程
Group/Version/Resource
编写CRD的yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: networks.samplecrd.k8s.io
spec:
group: samplecrd.k8s.io
version: v1
names:
kind: Network
plural: networks
scope: Namespaced
GOPATH 下,创建一个结构如下的项目
$ tree $GOPATH/src/github.com/<your-name>/k8s-controller-custom-resource
.
├── controller.go
├── crd
│ └── network.yaml
├── example
│ └── example-network.yaml
├── main.go
└── pkg
└── apis
└── samplecrd
├── register.go
└── v1
├── doc.go
├── register.go
└── types.go
我在 pkg/apis/samplecrd 目录下创建了一个 register.go 文件,用来放置后面要用到的全局变量
package samplecrd
const (
GroupName = "samplecrd.k8s.io"
Version = "v1"
)
我需要在 pkg/apis/samplecrd 目录下添加一个 doc.go 文件
// +k8s:deepcopy-gen=package
// +groupName=samplecrd.k8s.io
package v1
添加 types.go 文件
package v1
...
// +genclient
// +genclient:noStatus
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Network describes a Network resource
type Network struct {
// TypeMeta is the metadata for the resource, like kind and apiversion
metav1.TypeMeta `json:",inline"`
// ObjectMeta contains the metadata for the particular object, including
// things like...
// - name
// - namespace
// - self link
// - labels
// - ... etc ...
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec networkspec `json:"spec"`
}
// networkspec is the spec for a Network resource
type networkspec struct {
Cidr string `json:"cidr"`
Gateway string `json:"gateway"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// NetworkList is a list of Network resources
type NetworkList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []Network `json:"items"`
}
编写一个 pkg/apis/samplecrd/v1/register.go
package v1
...
// addKnownTypes adds our types to the API scheme by registering
// Network and NetworkList
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(
SchemeGroupVersion,
&Network{},
&NetworkList{},
)
// register the type in the scheme
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil
}
我就要使用 Kubernetes 提供的代码生成工具,为上面定义的 Network 资源类型自动生成 clientset、informer 和 lister
# 代码生成的工作目录,也就是我们的项目路径
$ ROOT_PACKAGE="github.com/resouer/k8s-controller-custom-resource"
# API Group
$ CUSTOM_RESOURCE_NAME="samplecrd"
# API Version
$ CUSTOM_RESOURCE_VERSION="v1"
# 安装k8s.io/code-generator
$ go get -u k8s.io/code-generator/...
$ cd $GOPATH/src/k8s.io/code-generator
# 执行代码自动生成,其中pkg/client是生成目标目录,pkg/apis是类型定义目录
$ ./generate-groups.sh all "$ROOT_PACKAGE/pkg/client" "$ROOT_PACKAGE/pkg/apis" "$CUSTOM_RESOURCE_NAME:$CUSTOM_RESOURCE_VERSION"
代码生成工作完成之后,我们再查看一下这个项目的目录结构:
$ tree
.
├── controller.go
├── crd
│ └── network.yaml
├── example
│ └── example-network.yaml
├── main.go
└── pkg
├── apis
│ └── samplecrd
│ ├── constants.go
│ └── v1
│ ├── doc.go
│ ├── register.go
│ ├── types.go
│ └── zz_generated.deepcopy.go
└── client
├── clientset
├── informers
└── listers
基于声明式API的业务功能实现,往往通过控制器监视API对象的变化,然后决定具体的工作
func main() {
...
cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)
...
kubeClient, err := kubernetes.NewForConfig(cfg)
...
networkClient, err := clientset.NewForConfig(cfg)
...
networkInformerFactory := informers.NewSharedInformerFactory(networkClient, ...)
controller := NewController(kubeClient, networkClient,
networkInformerFactory.Samplecrd().V1().Networks())
go networkInformerFactory.Start(stopCh)
if err = controller.Run(2, stopCh); err != nil {
glog.Fatalf("Error running controller: %s", err.Error())
}
}
func NewController(
kubeclientset kubernetes.Interface,
networkclientset clientset.Interface,
networkInformer informers.NetworkInformer) *Controller {
...
controller := &Controller{
kubeclientset: kubeclientset,
networkclientset: networkclientset,
networksLister: networkInformer.Lister(),
networksSynced: networkInformer.Informer().HasSynced,
workqueue: workqueue.NewNamedRateLimitingQueue(..., "Networks"),
...
}
networkInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: controller.enqueueNetwork,
UpdateFunc: func(old, new interface{}) {
oldNetwork := old.(*samplecrdv1.Network)
newNetwork := new.(*samplecrdv1.Network)
if oldNetwork.ResourceVersion == newNetwork.ResourceVersion {
return
}
controller.enqueueNetwork(new)
},
DeleteFunc: controller.enqueueNetworkForDelete,
return controller
}
func (c *Controller) runWorker() {
for c.processNextWorkItem() {
}
}
func (c *Controller) processNextWorkItem() bool {
obj, shutdown := c.workqueue.Get()
...
err := func(obj interface{}) error {
...
if err := c.syncHandler(key); err != nil {
return fmt.Errorf("error syncing '%s': %s", key, err.Error())
}
c.workqueue.Forget(obj)
...
return nil
}(obj)
...
return true
}
func (c *Controller) syncHandler(key string) error {
namespace, name, err := cache.SplitMetaNamespaceKey(key)
...
network, err := c.networksLister.Networks(namespace).Get(name)
if err != nil {
if errors.IsNotFound(err) {
glog.Warningf("Network does not exist in local cache: %s/%s, will delete it from Neutron ...",
namespace, name)
glog.Warningf("Network: %s/%s does not exist in local cache, will delete it from Neutron ...",
namespace, name)
// FIX ME: call Neutron API to delete this network by name.
//
// neutron.Delete(namespace, name)
return nil
}
...
return err
}
glog.Infof("[Neutron] Try to process network: %#v ...", network)
// FIX ME: Do diff().
//
// actualNetwork, exists := neutron.Get(namespace, name)
//
// if !exists {
// neutron.Create(namespace, name)
// } else if !reflect.DeepEqual(actualNetwork, network) {
// neutron.Update(namespace, name)
// }
return nil
}
被隔离的容器进程,如何跟其它Network Namespace 网络栈里的容器进程进行交互?
现实当中,俩台主机通信,需要通过网线连接到交换机,而在linux中,能够起到交换机作用的网络设备,是网桥(Bridge)。他是一个工作在数据链路层的设备,主要功能是根据MAC地址学习来将数据包转发到网桥的不同端口上。
Docker项目会默认在宿主机上创建一个名叫docker0的网桥,凡是连接到docker0上的容器可以通过它来通信。
如何把容器“连到”到docker0上?
这时候,需要使用一个名叫Veth Pair的虚拟设备。它被创建出来之后,总是以俩张虚拟网卡(Veth Peer)的形式成对出现。并且其中一个“网卡“发出的数据包,可以直接出现在它对应的另一张”网卡“上,哪怕是在不同的Network Namespace里。
这就使得Veth pair常常被用作连接不同Network Namespace的“网线”。
apiVersion: v1
kind: Service
metadata:
annotations:
service.kubernetes.io/loadbalance-id: lb-f926asqz
creationTimestamp: "2020-10-06T12:44:28Z"
labels:
app: nginx-ingress
chart: nginx-ingress-1.36.3
component: controller
heritage: Helm
release: nginx-ingress
name: nginx-ingress-controller
namespace: default
resourceVersion: "9367815813"
selfLink: /api/v1/namespaces/default/services/nginx-ingress-controller
uid: ac3c4f2e-07d1-11eb-89ed-06b6185c18a1
spec:
clusterIP: 172.16.255.152
externalTrafficPolicy: Cluster
ports:
- name: http
nodePort: 31156
port: 80
protocol: TCP
targetPort: http
- name: https
nodePort: 32234
port: 443
protocol: TCP
targetPort: https
selector:
app: nginx-ingress
app.kubernetes.io/component: controller
release: nginx-ingress
sessionAffinity: None
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 49.233.242.198
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2020-10-06T12:44:28Z"
labels:
app: nginx-ingress
chart: nginx-ingress-1.36.3
component: default-backend
heritage: Helm
release: nginx-ingress
name: nginx-ingress-default-backend
namespace: default
resourceVersion: "9367811830"
selfLink: /api/v1/namespaces/default/services/nginx-ingress-default-backend
uid: ac3b3633-07d1-11eb-89ed-06b6185c18a1
spec:
clusterIP: 172.16.255.144
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: nginx-ingress
app.kubernetes.io/component: default-backend
release: nginx-ingress
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}
跨主机网络
Kubernetes网络模型与CNI网络
丝雀发布/蓝绿发布
Borg在作业提交时,会主动减小资源限额配置,以容纳更多作业,提高资源利用率,而资源使用量到达一定阈值,Borg会通过快速恢复过程,快速还原原始资源限额,防止出现异常。
用户提交Pod时,声明较小的request供调度器使用,而Kubernetes真正设置给容器Cgroups的,则是相对较大的limits
Kubernetes进行Eviction的具体策略
Pod按配置分类:
同时设置了request和limits并且俩者相同
至少有一个container设置了requests
都没设置
Eviction发生时,kubectl会挑选Pod进行删除操作
Evivtion阈值
memory.available<100Mi #内存
nodefs.available<10% #可用磁盘
nodefs.inodesFree<5% #节点磁盘
imagefs.available<15% #镜像存储空间
kubelet --eviction-hard=imagefs.available<10%,memory.available<500Mi,nodefs.available<5%,nodefs.inodesFree<5% --eviction-soft=imagefs.available<30%,nodefs.available<10% --eviction-soft-grace-period=imagefs.available=2m,nodefs.available=2m --eviction-max-pod-grace-period=600
可以看到eviction其实分为Soft和Hard俩种模式,Soft Eviction 允许你为 Eviction 过程设置一段“优雅时间”,比如上面例子里的 imagefs.available=2m,就意味着当 imagefs 不足的阈值达到 2 分钟之后,kubelet 才会开始 Eviction 的过程。
kubernetes调度器的主要职责就是为新创建出来的Pod,寻找一个最合适的节点(Node)。最适合包括俩层含义:根据调度算法1.寻找可运行该Pod的节点;2.从上述节点中寻找最符合条件的;
Kubernetes调度器的核心其实就是俩个相互独立的循环控制
它的主要目的,是启动一系列的Informer,用来监听Etcd中的Pod、Node、Service等与调度有关的API对象的变化。这种“变化”会被放入调度队列。
主要目的就是选一个最适合的Node
Pod调度成功之后,需要在宿主机上创建出来,并把定义的容器启动起来,这部分功能正是Kubelet这个核心组件的功能
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有