k8s 学习笔记

经常使用的kubectl命令
 
kubectl run kubia --image=luksa/kubia --port=8080 --generator=run/v1
 
--image 指定镜像
--port 是告诉kubernetes 应用监听8080端口
--generator 一般不会用到,它让kubernetes建立一个replicationController . 通常不带这个参数 ,建立的是depoly资源,deploy 在调用replicaset资源,replicaset 和replicationController很像,好像是它的升级版本有更强的selector表达能力,并且这里的--generator=run/v1 并非建立出的rc名称是run/v1,建立出的rc就是kubia, run/v1多是标示rc的版本吧,具体不知道,反正后面不用这个,知道有这么个东西便可
后续学习中,得知--generator=run/v1 就是告诉kubernetes须要建立一个rc来管理pod
若是是:kubectcl run dnsutils --image=tutum/dnsutils --generator=run-pod/v1 --command -- sleep infinity
这里的--generator=run-pod/v1选项就是让kubectl直接建立pod,而不须要经过replicationController之类的资源来建立。
 
kubectl get pods
kubectl expose rc kubia --type=loadBalancer --name kubia-http
暴露 名称为kubia的rc 为名称kubia-http 的service,而且使用loadbalnacer,会将建立kubia时 port端口8080 映射出来
 
kubectl get service
 
kubectl get repliactioncontrollers
 
kubectl scale rc kubia --replicas=3 扩容为3个pod
 
kubectl get pods -o wide 查看pod在哪一个node节点上
 
kubectl describe pod pod-id
 
 
查看进群状态
kubectl cluster-info
 
使用kubectl explain 来发现可能的API对象字段,如:
kubectl explain pods
想查看某个对象下某个具体字段的使用方法
kubectl explain pod.spec
 
使用kubectl create 来建立pod
kubectl create -f kubia-manual.yaml
 
获得运行中pod的完整定义
kubectl get pod kubia-manual -o yaml
kubectl get pod kubia-manual -o json
 
查看应用程序日志
kubectl logs kubia-manual
当一个pod中有多个容器时
kubectl logs kubia-manual -c kubia
 
在不经过service外界相与pod通讯,可使用port-forward命令将短裤转发到指定pod
如下命令会将机器的本地端口8888转发到咱们的kubia-manual pod的端口8080:
kubectl port-forward kubia-manual 8888:8080
 
 
在kubernetes中 标签时能够组织kubernetes全部资源。
kubernetes 中建立出来的具体的对象都是资源。某一个资源 属于某一个资源类如 pod资源类,或者对象。
 
查看全部pod上有什么标签
kubectl get po --show-labels
 
若是你想将标签做为显示列的列头可使用-L
kubectl get po -L env,app
 
添加pod标签
kubectl label po kubia-manual env=test
 
修改pod的现有标签
kubectl label po kubia-manual env=debug --overwrite
 
经过标签选择器列出pod
kubectl get po -l env=debug
 
列出包含evn标签的pod,无论env是什么值
kubectl get po -l env
 
列出不含evn标签的pod
kubectl get po -l '!env' (确保是单引号)
 
env !=
env in (test,debug)
env not in (prod,devel)
 
kubernetes中调用pod到哪一个节点上是可有可无的,但因为实际状况,每台node的硬件环境不一致,因此某些状况要求将不一样pod调到指定节点上运行。也能够经过label实现。
kubectl label node node-id gpu=true
将pod调用到此节点只须要在yaml中描述到
apiVersion: v1
kind: pod
metadata:
kubia-gpu
spec:
nodeSelector:
gpu: "true"
containers:
- image: luksa/kubia
name: kubia
 
 
探针:对于pod中容器要进行监控,能够用探针。
3种探针方式:
http get 返回2xx,3xx 也就是没错误4xx,5xx
tcp 套接字 能创建链接正常,反之不正常
exec 执行某个命令,成功即0,不然失败
 
http get的描述方法:
apiVersion: v1
kind: Pod
metadata:
name: kubia-liveneess
spec:
containers:
- image: luksa/kubia-unhealthy
name: kubia
livenessProbe:
httpGet:
path: /
port: 8080
 
luksa/kubia-unhealthy 这个镜像为node.js,提供web服务,其中应用中添加了让此web服务只在前5次正常返回,5次后就会返回错误。
 
通常错误3次后,就会重启容器,那么你想看容器错误日志就要看上次的日志所以要用 --previous参数
kuectl logs mypod --previous
 
使用kubectl describe po pod-id能看到具体的错误代码,以及在底部显示了容器为何终止,--kubernetes发现容器不健康,因此终止并从新建立
 
默认在描述了探针,pod在启动的时候就会在刚刚启动时进行一次检测,所以最好的作法是给检测加一个初始延迟。
apiVersion: v1
kind: Pod
metadata:
name: kubia-liveneess
spec:
containers:
- image: luksa/kubia-unhealthy
name: kubia
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15
初始延迟为15秒
 
 
replicationcontroller
选择器
模版
副本数
 
若是更改选择器,则会建立新的pod
若是更改pod的标签,那么也会建立新的pod进行替换,可是老的pod不会被删除
若是更改模版,好比给加入新标签,那么将在下次替换时用到新模版,这个能够用于升级pod使用
 
kubectl edit rc kubia 这将在你的默认编辑器中打开Replicationcontroller 的YAML配置。
使用export KUBE_EDITOR="/usr/bin/nano"设置默认编辑器
 
kubectl delete rc kubia 这是删除replicationcontroller命令,删除rc的同时,pod也会被删除。咱们知道rc只是pod的管理器,rc建立的pod不是rc的组成部分,因此咱们也能够只删除rc而不删除pod,使用--cascade=false 参数、
kubectl delete rc kubia --cascade=false
 
最初replicationcontroller 是用于复制和在异常时从新调度节点的惟一kubernetes组建,后来被replicaSet代替了。如今基本上见不到replicationcontroller,可是有的环境仍是有可能会有,因此咱们仍是知道的好。
replicaset咱们一般也不会直接建立,而是在建立最高层级的deployment资源时自动建立。
replicaset 与replicationcontroller的区别
replicaset 标签选择器的能力更强。
replicationcontroller只能指定标签名:标签值
replicaset 能够指定env=pro,env=devel ,也能够指定只要包含env标签就行,理解为env=*
虽然说不用直接建立,为了学习咱们手动建立,
定义replicaset
apiVersion: apps/v1beta2
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLables:
app: kubia
template:
metadata:
lables:
app:kubia
spec:
containers:
- name:kubia
image: luska/kubia
 
template部分和replicationController 同样
selector处不同。replicationController 用selector ,replicaSet用 selector.matchLables选择器 更简单,更具备表达力
replicaSet 的简写 rs
kubectl get rs
kubectl describe rs
 
rs 的matchlables 是精确匹配,说rs支持更强表达能力的选择器,是由于rs还有matchExpressions选择器
selector:
matchExpressions:
- key: app
operator: In
values:
- kubia
operator(运算符)有四个
In
NotIn
Exists: 必须包含某个key,值不重要,values不该指定
DoesNotExists: 必须不包含某个key, values不该指定
 
当你的replicaSet 的选择器同时定义了matchLables和 matchExpressions ,必须两个同时知足才能是pod和选择器匹配
 
kubectl delete rs kubia 删除rs的同时pod也被删除,删除前建议列出pod进行确认
 
rc,rs 都用于在kubernetes集群上运行指定数量的pod,但他们不关心在哪一个节点上运行。有种状况是让每一个节点都运行某一个pod好比日志收集,和监控应用。这时候要用到另一个控制器了DaemonSet.
DaemonSet也使用Pod模版,默认是将模版中的pod,在每一个节点中建立pod,但也有特殊状况,只在某特定节点上建立pod,好比不一样的硬盘类型。这能够用pod模版的nodeSelector属性指定
apiVersion: apps/v1beta2
kind: DaemonSet
metadata:
name: ssd-monitor
spec:
selector:
matchLables:
app: ssd-monitor
template:
metadata:
lables:
app: ssd-monitor
spec:
nodeSelector:
disk: ssd
containers:
- name: main
image: luksa/ssd-monitor
 
DaemonSet 的简写ds
kubectl get ds
一样删除ds,同时也会删除pod
 
rc,rs,ds都是持续运行pod也就是pod执行结束或者手动删除pod,这些控制器都会重启pod,有些时候须要让pod运行完成退出后不在重启,而且保证pod若是在运行时异常退出了能够重启的状况。这就须要用到job了。
job管理的pod,正常完成退出后不重启,当节点故障时,会按照replicaSet的pod方式,从新安排到一个新节点。也能够配置job,若是进程异常退出返回错误码时,重启容器。
 
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
template:
metadata:
lables:
app: batch-job
spec:
restartPolicy: onFailure Job不能使用Always为默认的重启策略
containers:
- name: main
image: luksa/batch-job
这里么有定义标签选择器,它将根据pod模版中的标签自动建立标签选择器
onFailure 当容器出错时重启容器,若是时Never将永远不重启
 
kubectl get jobs
kubectl get po -a 查看被删除的pod
 
能够配置job 按顺序运行几回
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
completions: 5
template:
... ...
也能够声明能够并行的数量
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
completions: 5 共计运行5次
parallelism: 2 能够并行2个
template:
... ...
parallelism也能够像replicaSet同样扩缩容
kubectl scale job batch-job --replicas 3
 
job是一次性任务,万一运行卡住了,你永远不知道它的状态,因此你能够限制它运行的时长。超过期长标记为失败,这就须要用到pod中activeDeadlineSeconds 属性来定义运行时长。
同时能够定义job 中spec.backoffLimit配置job被标记为失败以前能够重试的次数。默认为6.
 
job建立后,当即会运行,但有些时候咱们但愿定时运行job,这就须要用到kubernetes的CronJob资源
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *" 每15分钟执行一次而且在0,15,30,45
JobTemplate:
spec:
template:
matedata:
lables:
app: periodic-batch-job
spec:
restartPolicy: OnFailure
containers:
- name: main
image: luksa/batch-job
假如咱们的任务运行时间要求很是准确,不但愿本来定在10:30:00运行的任务拖到10:30:16运行,可能超过15秒运行结果就有误差了。这时能够设置startingDeadlineSeconds。
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: batch-job-every-fifteen-minutes
spec:
schedule: "0,15,30,45 * * * *" 每15分钟执行一次而且在0,15,30,45
startingDeadlineSeconds: 15
JobTemplate:
 
replicationController,replicaSet,DaemonSet, Job, CronJob 这几种管理pod的控制器的基本内容就这些了。高级用法碰到在了解。
 
kubernetes service资源
 
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: kubia
 
kubectl get svc
 
kubectl exec kubia-id -- curl -s http://service_ip
双横缸表明着kubectl 命令项的结束,下面的是容器内部执行的命令
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
sessionAffinity: ClientIP
sessionAffinity属性默认为None,ClientIP 是保证特定客户端产生的请求每次都指向同一个pod
 
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
ports:
- name: http
port: 80
targetPort: 8080
- name: https
port: 443
target: 8443
selector:
app: kubia
标签选择器应用于整个服务,不能对每一个端口作单独的配置
 
上面是采用端口号进行映射,还有一种方式给端口命名,这样在作映射的时候直接指向名称。好处是pod的端口随便改,而不用改service的配置以下:
kind: Pod
metadata:
name: kubia
spec:
containers:
- name: kubia
ports:
- name: http
containerPort: 8080
- name: https
containerPort: 8443
 
apiVersion: v1
kind: Service
spec:
ports:
- name: http
port: 80
targetPort: http
- name: https
port: 443
targetPort: https
selector:
app: kubia
kubectl delete po --all 删除全部pod ,而无论pod的id
kubectl delete all --all all表明全部资源,--all表明全部资源对象
 
backend-database.default.svc.cluster.local
backend-database 服务名称
default 命名空间
svc.cluaster.local是在全部集群本地服务名称中使用的可配置集群域后缀
 
 
kubectl exec -ti kubia-3inly bash 运行bash很像 docker exec -ti id bash
不要ping kubernetes中建立的服务名称,这是由于服务的ip是一个虚拟的IP,只有在与服务端口结合时才有意义
 
 
endpoint资源
kubernetes service不只能够暴露pod给外部,一样也能够把外部服务建立为服务让内部pod进行访问。服务并非和pod直接相连的,有一种资源-endpoint介于二者之间。
endpoint资源就是暴露一个服务的IP地址和端口的列表,endpoint资源和其余kubernetes资源同样,因此可使用kubectl info 来获取它的基本信息
kubectl describe svc kubia 执行此命令能看到endpoint资源
kubectl get endpoints kubia
我知道在建立service时定义了selector pod选择器,但在重定向传入链接时不会直接使用它。
选择器用于构建IP和端口列表,而后存储在EndPoint资源中。当客户端链接到服务时,服务代理选择这些IP和端口对中的一个,并将传入链接重定向到改位置监听的服务器。
 
EndPoint是一个单独的资源,而不是service的属性,因此咱们能够单独的建立endpoint资源对象。
咱们在建立service时不声明pod选择器就不会建立endpoint
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
ports:
- port: 80
这里并无定义selector
下面咱们手动建立endpoint
apiVersion: v1
kind: Endpoints
metadata:
name: external-service 这里的名称必定和service的一致
subsets:
- addresses:
- ip: 1.1.1.1
- ip: 2.2.2.2
ports:
- port: 80 这里的port是endpoint的目标端口,是service中的targetPort
以上就作了一个将外部服务经过service让内部pod能够访问。
 
还有一种简单的方式,给外部服务建立一个别名服务。
apiVersion: v1
kind: Service
matedata:
name: external-service
spec:
type: ExternalName 代码的type被设置成ExternalName
externalName: someapi.somecompany.com 实际服务的彻底限定名
ports:
- port: 80
 
内部就可使用external-service来访问服务了
 
 
kubectl get po --all-namespaces 很是好用
 
kubernetes关于pod挂载卷的知识
首先要知道卷是pod资源的属性,pv,pvc是单独的资源。pod 资源的volumes属性有多种type,其中就包含有挂载pvc的类型。这也帮我理清了之间的关系。
pv通常是系统管理员作的
pvc 是通常k8s用户声明的,大概意思就是说我须要一个 什么权限的,多少存储空间的卷,而后k8s api收到这个请求就去找pv资源对象,若是二者相匹配,那么pv就和pvc绑定了。
从这里咱们也想到了,pv若是是手动建立的话,那就麻烦大了。几个,几十个还好说,上万个,管理员该如何建立这么多。因此才有了动态建立pv的需求。这就引出另一个资源 storageClass ,这个资源声明后端挂载什么样的存储服务,好比nfs,chef等,有了这个通常用户在定义pvc的时候,在提出存储空间和读写权限的同时声明用那个storageClass了,以下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata: mysql-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: managed-nfs-storage (注意这里)
resources:
requests:
storage: 5Gi
 
以上即是pvc使用storageClass资源对象建立pv,pvc的绑定了
api收到对storageClass声明后,就会调用这个storageClass的生产者进行生产。
咱们就会想到,在建立storageClass资源对象的时候确定会指定生产者。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: example-nfs
provisioner: example.com/nfs 这里指定生产者,其中example.com/nfs只是一个标签,你在deploymend里定义什么这里就写什么。
mountOptions:
- vers=4.1
 
那么问题又来了。在k8s中生产者组件不支持nfs,因此才会安装第三方组件。第三方组件的安装就须要建立相关的rbac的角色和帐户。第三方组件是用deploymend的资源托管相关组件pod的。
那么经过deploy部署的pod怎么就是provisioner了?这个我不清楚,后面学习后在总结吧。
 
 
回到正题上。
volumes 的4种
1. emptyDir
2. gitRepo
3. hostpath
4.PersistentVolumeClaim
5.configMap,secret
6. 各类云平台的存储磁盘卷如google的gce,aws的ebs,azure的azureDisk
其实4只是一个归纳,nfs,chef 这些网络存储统统能够单独来使用。但我以为实际使用中仍是讲这些网络存储转化成pv,pvc
 
从简单开始学习
emptyDir 两种应用场景: 1. 同一个pod中,各个容器间共享文件。 2. 当程序对大数据处理时内存空间不够时临时写入文件(固然也可使用宿主主机的内存)
例子:
apiVersion: v1
kind: Pod
metadata:
name: fortune
spec:
containers:
- image: luksa/fortune
name: html-generator
volumeMounts:
- name: html
mountPath: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPaht: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
emptyDir: {} (为{}表示使用节点服务器的文件系统)
- name: html-2
emptyDir:
medium: Memory (使用节点服务器的内存)
 
以上就是emptyDir的用法。gitRepo实际上是依赖于emptyDir的,你能够把它理解成,声明了一个emptyDir而后在把gitrepo下载填充到emptyDir
例子:
apiVersion: v1
kind: Pod
metadata:
name: gitrepo-volume-pod
spec:
containers:
- image: nginx: alpine
name: web-nginx
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes:
- name: html
gitRepo:
revision: master
directory: . (这个.很重要,表示在当前emptyDir目录下否则就会建立一个kubia-website-example目录)
 
以上就是gitRepo的用法,其中有个问题就是pod中的gitrepo并不会及时更新。若是想要及时更新须要用到sidecar ,加入到pod中,在Docker Hub搜索 “git ryc”获取相关镜像。
还有一个状况不得不使用sidecar container 。gitrepo 不支持链接私有库,也就是不能是ssh密钥链接,也不能够有用户名和密码。这时候就须要使用支持验证链接的sidecar container来实现了。具体怎么使用,用到的时候在研究吧.
至此gitRepo卷类型算是简单介绍了,下面学习hostpath
 
大多数时候pod应该忽略他们的主机节点,所以他们也不须要访问节点文件系统上的文件。可是某些系统级别的pod(一般由DaemonSet管理)确实须要读取节点的文件或使用节点文件系统来访问节设备,这时候就须要用到hostPath
hostPath 算是第一个持久化存储卷了,可是这个不多用到,由于这个卷只能在某一单一节点上,pod重启后极可能在另一个节点上。固然可使用NodeSelector但这样看起来也不高明。因此建议使用网络存储。hostPath过
 
接下来是网络存储和云平台提供的存储磁盘卷。这两种在用到的时候找相关的属性进行配置便可。也没什么要注意的,实际应用场景用到最多的持久存储是pv,pvc,storageClass
 
 
configmap
kubectl create configmap fortune-config --from-literal=sleep-interval=25
 
通常configmap包含多个映射条目因此建立时能够屡次使用--from-literal参数
kubectl create conigmap fortune-config --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two
 
kubectl get configmap fortune-config -o yaml
 
configmap一样能够存储粗力度的配置数据,好比完整的配置文件。kubectl create configmap 命令支持从硬盘上读取文件,并将文件内容单独存储为ConfigMap中的条目:
kubectl create configmap my-config --from-file=config-file.conf
运行此命令,kubectl 会在当前目录下找config-file.conf文件,并将文件内容存储在configmap中以config-file.conf 为键名的条目下。固然也能够手动指定键名
kubectl create configmap my-config --from-file=customkey=config-file.conf
 
kubectl create configmap my-config --from-file=/path/to/dir
这时候,kubectl会为文件中每一个文件单首创建条目,仅限文件名可做为合法ConfigMap键名的文件。
 
当让也能够将上面的参数混合使用
 
configmap设置完成了,如何将映射的值传递给pod的容器?三种方法
一,设置环境变量,例子以下:
apiVersion: v1
kind: Pod
metadata:
name: fortune-env-from-configmap
spec:
containers:
- image: luksa/fortune:env
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
很好奇当pod 容器中引用不存在的configmap会发生什么?
pod会尝试运行全部容器,只有这个容器启动失败,其余正常(假若有多个容器),当你过后建立处这个configmap,无需重启pod,容器就会成功。固然也能够引用为可选的,设置configMapKeyRef.optional: true便可,这样即便ConfigMap不存在,容器也能正常启动。
 
若是configmap中条目不少,用env属性列出麻烦且容易出错。那么有没有办法一次导入呢。用envFrom, 例如configmap中有三个条目FOO、BAR和FOO-BAR
 
spec:
containers:
- image: some-image
envFrom:
- prefix: CONFIG_ 全部环境变量均包含前缀CONFIG_ ,不设置就将引用configmap中的键名
configMapRef:
name: my-config-map
如此,容器中多出两个变量 CONFIG_FOO 、CONFIG_BAR
为何是两个,由于另一个FOO-BAR包含破折号,不是一个合法的环境变量名称。被忽略了,因此咱们应该注意建立configmap时 不要使用-
 
上面咱们学习了如何将configmap中的条目以变量的形式传入到容器中
那么如何将configmap中的条目做为容器运行的参数args呢?例子:
apiVersion: v1
kind: Pod
metadata:
name: fortune-env-from-configmap
spec:
containers:
- image: luksa/fortune:env
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
args: ["$(INTERVAL)"]
 
环境变量和参数都适合于变量值较短的场景。configmap是能够包含完整配置文件内容的,当你想要将其暴露给容器时,可使用上一章中提到的卷的类型。configmap卷会将ConfigMap中的每一个条目均暴露成一个文件。运行在容器的进程经过读取文件内容得到对应的条目值。
configmap-files为一个目录
kubectl create configmap fortune-config --from-file=configmap-files
例子
apiVersion: v1
kind: Pod
metadata:
name: fortune-configmap-volume
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
...
- name: config
mountPaht: /etc/nginx/config.d
readOnly: true
....
volumes:
...
- name: config
configMap:
name: fortune-config
...
 
一种特殊状况,当你只想暴露configmap-files目录下的某一个配置文件,该如何作:
volumes:
- name: config
configmap:
name: fortune-config
items:
- key: my-nginx-config.conf
path: gzip.conf
这样配置后,挂载fortune-config 卷后,就只有my-nginx-config.conf 而且挂载后的名称为gzip.conf
 
另外一种特殊状况,咱们经过上述挂载configmap卷后会发现,被挂载的目录以前的文件都被隐藏掉了。那么若是你需求不想隐藏以前的文件,该如何作:
spec:
containers:
- image: some/image
volumeMounts:
- name: myvolume
mountPath: /etc/someconfig.conf 挂载到指定的某一个文件,而不是某个文件夹
subPath: myconfig.conf 挂载指定的条目,而不是完整的卷
 
为configMap卷中的文件设置权限
volumes:
- name: config
configmap:
name: fortune-config
defaultMode: "6600"
 
更新应用配置且不重启应用程序
使用环境变量或命令行参数做为配置源的弊端在于没法在进程运行时更新配置。将ConfigMap暴露为卷能够达到配置热更新的效果,无需从新建立pod或重启容器。
ConfigMap被更新后,卷中引用它的文件也会相应更新,进程发现文件被改变后进行重载。kubernetes一样支持文件更新后手动通知容器。但要注意的是,更新configmap以后对应文件的更新会至关耗时。
咱们使用kubectl edit configmap fortune-config 来更改某一个值。
而后执行
kubectl exec fortune-configmap-volume -c web-server -- cat /etc/nginx/config.d/my-nginx-config.conf
查看文件是否是被修改了,若是没有稍等一会再查看。在确认更改后,咱们来手动通知容器进行重载。
kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload
 
你可能会疑惑 Kubernetes更新完configmap卷中的全部文件以前(全部的文件被更新而不是一部分),应用是否会监听到文件编号并主动进行重载。答案是不会,会在全部的文件更新后,一次性更新到pod容器中。kubernetes经过富豪连接作到这一点的。
kubectl exec -ti fortune-configmap-volume -c web-server -- ls -lA /etc/nginx/config.d
..4989_09_04_15_06.028485
..data -> ..4989_09_04_15_06.028485
my-nginx-config.conf -> ..data/my-nginx-config.conf
sleep-interval -> ..data/sleep-interval
能够看到,被挂载configMap卷中的文件是..data文件夹中文件的符号连接,而..data又是链接到 ..4989的符号连接,每当ConfigMap被更新后,Kubernetes 会建立一个这样的文件夹,卸任全部文件并从新将符号..data连接至新文件夹,经过这种方式能够一次性修改全部文件。
 
若是挂载configmap中的某一个文件,而不是文件夹,configmap更新以后对应的文件不会被更新。若是如今你须要挂载单个文件且在修改Configmap后想自动更新,能够将该卷挂载到其余文件夹,而后作一个软连接。
至于如何实现应用重载配置 ,须要应用本身实现了。
 
configmap都是以名文存储的,有些信息比较敏感,就须要用秘文存储了。
这就须要使用kubernetes 的secret资源了。
首先有一个状况咱们须要了解
使用kubectl describe pod pod-id 查看任何一个pod你都会发现默认有挂载一个secret卷。
Volumes:
default-token-ps7ff:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-ps7ff
Optional: false
这个卷里的内容咱们可使用kubectl describe secrets查看
# kubectl describe secrets default-token-ps7ff
Name: default-token-ps7ff
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: 6efa7f7c-6a61-11e9-bfdb-0a382f97318e
 
Type: kubernetes.io/service-account-token
 
Data
====
ca.crt: 1359 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtp...
能够看出这个Secret包含是哪一个条目ca.crt 、namespace与token
包含了从pod内部安全访问Kubernetes API服务器所需的所有信息。尽管咱们但愿作到应用对kubernetes无感知,可是直连Kubernetes没有其余方法,你只能使用secret卷提供的文件来访问kubernetes API。
使用kubectl describe pod命令显示secret卷北挂载的位置:
mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-ps7ff (ro)
 
注意: default-token Secret默认会被挂载至每一个容器。能够经过设置pod定义中的automountServiceAccountToken字段为false,或者设置pod使用的服务帐户中的相同字段为false来关闭这种默认行为。
 
查看容器中挂载了哪些条目
# kubectl exec nginx-dnm9n -- ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token
 
建立Secret
openssl genrsa -out https.key 2048
openssl req -new -x509 -key https.key -out https.cert -days 3650 -subj /CN=www.kubia-example.com
# kubectl create secret generic fortune-https --from-file=https.key --from-file=https.cert --from-file=foo
secret/fortune-https created
也可使用 --from-file=fortune-https 囊括这个歌文件夹中的文件
# kubectl get secret fortune-https -o yaml
# kubectl describe secret fortune-https
 
对比configmap与secret
secret与configMap有比较大的区别,这也是为什么kubernetes开发者们在支持了Secret一段时间以后会选择建立ConfigMap。穿件的Secret的YAML格式定义显示
# kubectl get secret fortune-https -o yaml
apiVersion: v1
data:
foo: YmFyCg==
https.cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURFekNDQWZ1Z0F3SUJBZ0lKQU96Y00rNzI3RWJHTUEwR0NTcUdTSWIzRFFFQkN3VUFNQ0F4SGpBY0JnTlYKQkF
https.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBeFYvUVJiazJiRm8zRmdZdWpaTWxPVGg3MUxpY3AyUS9pL2pib2E1SExlUlpSTDBi
kind: Secret
. . .
将其与CoinfigMap的Yaml格式定义作对比
apiVersion: v1
data:
bar: baz
foo: bar
one: two
kind: ConfigMap
 
注意到Secret条目的内容会被以Base64个市编译,而ConfigMap直接以纯文本展现。这种区别致使在处理YAML和JSON格式的Secret时会稍许有些麻烦,须要在设置和读取相关条目时对内容进行编解码。
这个具体使用中是这样的,
好比你如今想把一个配置文件加入到Secret中,那么你首先将配置文件中的内容经过BASE64进行编码后才能做为条目。
固然你会问难道kubernetes不提供base64编码?提供,只能对字符串,不能接受文件。以下
kund: Secret
apiVersion: v1
stringData:
foo: plain text
data:
https.cert: lksjkaldjldasjladgjsjl...
https.key: lsiodjsdlfjahcdo...
建立后使用kubectl get secret -o yaml会看到stringData字段中的全部条目会被Base64编码后展现在data字段下。因此stringData字段是只写不可读的。
 
如何在pod中使用Secret
apiVersion: v1
kind: Pod
metadata:
name: fortune-https
spec:
containers:
- image: luksa/fortune:env
name: html-generator
env:
- name: INTERVAL
valueFrom:
configMapKeyRef:
name: fortune-config
key: sleep-interval
volumeMounts:
- name: html
mountPaht: /var/htdocs
- image: nginx:alpine
name: web-server
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
- name: config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: certs
mountPath: /etc/nginx/certs/
readOnly: true
ports:
- containerPort: 80
- containerPort: 443
volumes:
- name: html
emptyDir: {}
- name: config
configmap:
name: fortune-config
items:
- key: my-nginx-config.conf
path: https.conf
- name: certs
secret:
secretname: fortune-https
简单点的实例:
apiVersion: v1
kind: Pod
metadata:
name: fortune-https
spec:
containers:
- image: nginx:alpine
name: web-server
volumeMounts:
- name: certs
mountPath: /etc/nginx/certs/
readOnly: true
ports:
- containerPort: 80
- containerPort: 443
volumes:
- name: certs
secret:
secretName: fortune-https
 
固然也能够将secret条目暴露为环境变量。但不建议这么作,应用一般会在错误报告时转储环境变量,或者是启动时打印在应用日志中,无心中就暴露Secret信息。另外,子进程会继承父进程的全部环境变量,若是是经过第三方二进制程序启动应用,你并不知道它使用敏感数据作了什么。因此不建议用环境变量,建议使用secret卷的形式挂载到pod.
env:
- name: FOO_SECRET
valueFrom:
secretKeyRef:
name: fortune-https
key: foo
 
学会了secret,接下来就有一个比较经常使用的secret实际应用,dockerhub
kubectl create secret docker-registry mydockerhubsecret --docker-username=myusername --docker-password=mypassword --docker-email= my.email@provider.com
 
使用:
apiVersion: v1
kind: Pod
metadata:
name: private-pod
spec:
imagePullSecrets:
- name: mydockerhubsecret
containers:
- image: username/private:tag
name: main
假设系统一般运行大量Pod,你可能会好奇是否须要为每一个Pod都添加相同的镜像拉取Secret.并非,能够经过添加Secret至ServiceAccount 使全部pod都能自动添加上镜像拉取的Secret.
 
 
Deployment 声明式地升级应用
如今你已经知道如何将应用程序组件打包进容器,将他们分组到pod中,并为它们提供临时或者持久存储,将密钥或配置文件注入,并可使pod之间互相通讯。这就是微服务化:如何将一个大规模系统拆分红哥哥独立运行的组件。以后,你将会须要升级你的应用程序。如何升级再kubernetes集群中运行的程序,以及kubernetes如何帮助你实现真正的零停机升级过程。升级操做能够经过使用replicationController 或者 replicaSet实现,但Kubernetes提供了另外一种基于ReplicaSet的资源Deployment,并支持声明式地更新应用程序。
 
看一个简单的例子:
现有一个应用 ,版本为V1 ,运行在Kubernetes的pod中,如今应用的镜像有更新,标记为v2,那么如何将新版本运行的pod替换掉V1版本的pod.
有如下两种方法更新pod:
1. 直接删除全部现有的pod,而后建立新pod
2. 新建立一组pod,等待运行后删除旧pod. 能够一次性建立,一次性删除,也能够一部分一部分操做。
这两种各有优缺点:第一种方法将会致使应用程序在必定的时间内不可用。第二种方法会致使新旧版本同时在线,若是对统一个数据的处理方式不同,将会给系统带来数据损坏。
暂且无论优缺点,先来看看如何实现。
 
咱们用replicationController来托管pod v1,能够直接经过将pod模版修改成v2,而后再删除旧pod,这时候rc就会按照新模版建立pod.
若是你能够接受短暂的服务不可用,那这是最简单更新pod的方法。
 
接下来咱们用第二种方法。首先要保持两个版本同时在线,因此要双倍的硬件资源。
前提咱们是用service来暴露pod的。保持rc v1不变,而后建立 rc v2 ,rc v2的pod模版选择新镜像标签V2. 建立rc v2 ,rc v2将运行pod v2 。等待pod v2运行正常后,咱们使用 kubectl set selector 命令来修改service的pod选择器。
 
这种一次性从v1切到v2的方法即为蓝绿部署,主要问题就是硬件资源要两倍。若是咱们不是一次切换,而是一点点切,资源将不须要两倍。好比你建立rc v2,成功后,缩容 rc v1 ,更改service的pod选择器。而后再扩容rc v2,在缩容 v1 这就是滚动更新。那么这个就要求你的应用容许存在两个不一样版本,具体要根据实际需求来操做了。
 
以上第二种方法的两种状况,咱们大概知道是怎么回事了。但咱们在实际操做中,要建立两个rc,而且要修改service的pod selector. 这要是用滚动更新,pod副本越多,咱们手动操做的次数就越多,手动操做越多越容易出现问题。那么问题来了,难道kubernetes没有提供自动完成这些操做的方法吗?答案是提供了。k8s中使用kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
 
咱们来经过一个例子了解下:
用rc建立一个应用v1 ,并使用service的 loadbalancer将服务对外暴露。
apiVersion: v1
kind: ReplicationController
metadata:
name: kubia-v1
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
---
apiVersion: v1
kind: Service
metadata:
name: kubia
spec:
type: LoadBalancer
selector:
app: kubia
ports:
- port: 80
targetPort: 8080
 
查看loaderbalancer的IP
#kubectl get svc kubia
访问测试
# while true; do curl http://IP; done
this is v1 running in pod 主机名
接下来用v2版本升级,this is v2 running in pod 主机名
kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2
使用 kubia v2版本应用替换运行着kubia-v1 的replicationController,将新的复制控制器命名为kubia-v2,并使用luksa/kubia:v2做为容器镜像。
kubectl 经过复制kubia-v1 的replicationController并在其pod模版中改变镜像版本。若是仔细观察控制器的标签选择器,会阿贤它也被作了修改。它不只包含一个简单的app=kubia标签,并且还包含一个额外的deployment标签,为了由这个ReplicationController管理,pod必须具有这个标签。这也是为了不使用新的和旧的RC来管理同一组Pod.可是即便新建立的pod添加了deployment标签,pod中仍是有app=kubia标签,因此不只新的RC要加deployment标签,旧的RC一样也要加上deployment标签,标签的值为 一个镜像hash(先这样理解)。要保证旧的RC添加deployment标签后依然能够管理以前建立的pod,所以也要修改旧pod,进行添加标签,而实际上正是在修改旧RC以前,kubectl修改了旧pod的标签:
kubectl get po --show-labels 进行查看标签
设置完成后,就开始执行更新操做了,过程就是咱们上面描述的滚动更新过程。
 
为何 kubectl rolling-update已通过时
咱们能够在使用rolling-update命令的时候添加 --v 6 来提供日志级别,使得全部kubectl 发起的到API服务器的请求都会被输出。
你会看到一个put请求:
/api/v1/namespace/default/replicationcontrollers/kubia-v1
它是表示kubia-v1 ReplicationController资源的RESTful URL.这些请求减小了RC的副本数,这代表伸缩的请求是由kubectl 客户端执行的,而不是kubernetes master执行的。那么当kubectl执行升级时失去了网络链接,升级过程就会中断。对于中断后的结果处理起来将很麻烦。因此咱们想尽可能把这个过程让master负责。这就引出了DeployMent资源。
 
Deployment是一个高阶资源,replicationController和replicaSet都被认为是底层的概念。当建立一个Deployment时,ReplicaSet资源就会被建立,实际的pod是由Deployment的Replicaset建立和管理的,而不是由Deployment直接建立和管理的。
 
接下来咱们建立一个简单的deployment,将上面的replicationController稍做修改:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas: 3
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v1
name: nodejs
由于以前RC只维护和管理了一个特定的版本的pod,并须要命名为kubia-v1,而一个deployment资源高于版本自己。能够同时管理多个版本的pod,因此在命名时不须要指定应用的版本号。
 
kubectl create -f kubia-deployment-v1.yaml --record
使用 --record选项会记录历史版本号,在以后操做中很是有用。
kubectl get deployment kubia
kubectl describe deployment kubia
还有一个命令,专门用于查看部署状态:
kubectl rollout status deployment kubia
 
查看pod,会发现使用deployment资源部署的pod名称有必定的规律,
deployment的名字 + replicaset pod模版的hash + 随机数, 如:
kubia-15098375-otnsd
kubia-15098375-djc6s
而rc建立出来的名称是 : rc名称 + 随机数
kubia-v1-kgysg
能够经过查看replicaSet来确认pod模版hash
kubectl get replicasets
kubia-15098375 ...
deployment正是根据pod模版的hash值,对给定版本的pod模版建立相同的(或使用已有的)ReplicaSet.
 
Deployment的高明之处
有不一样的更新策略
Recreate 策略在删除旧的Pod以后才开始建立新的Pod。若是不容许多个版本共存,使用这个,但会有短暂的不可用。
RollingUpdate 策略会渐进地删除旧的pod,于此同时建立新的pod.这是deployment默认使用的策略。若是支持多个版本共存,推荐使用这个。
 
咱们来演示下deployment滚动升级的过程。
在演示以前咱们先减慢滚动升级的速度,以方便咱们观察
kubectl path deployment kubia -p '{"spec": {"minReadySeconds": 10} }'
使用path对于修改单个或少许资源属性很是有用,不须要在经过编辑器编辑,使用minReadySeconds 配置正常检查后多少时间才属于正常。
注:使用patch命令更改Deployment的自有属性,并不会致使pod的任何更新,由于pod模版并无被修改。更改其余Deployment的属性,好比所须要的副本数或部署策略,也不会触发滚动升级,现有运行中的pod也不会受影响。
 
触发滚动升级
kubectl set image deployment kubia nodejs=luksa/kubia:v2
执行完成后pod模版的镜像会被更改成luksa/kubia:v2
 
deployment的优势
使用控制器接管整个升级过程,不用担忧网络中断。
仅仅更改pod模版便可。
注:若是Deployment中的pod模版引用了一个ConfigMap(或secret),那么更改ConfigMap字眼自己将不会触发升级操做。若是真的须要修改应用程序的配置并想触发更新的话,能够经过建立一个新的ConfigMap并修改pod模版引用新的ConfigMap.
Deployment背后完成的升级过程和kubectl rolling-update命令类似。
一个新的ReplicaSet会被建立而后慢慢扩容,同时以前版本的Replicaset会慢慢缩容至0
 
deployment的另一个好处是能够回滚
kubectl rollout undo deployment kubia
deployment会被回滚到上一个版本
undo命令也能够在滚动升级过程当中运行,并直接中止滚动升级。在升级过程当中一建立的pod会被删除并被老版本的pod替代。
显示deployment的滚动升级历史
kubectl rollout history deployment kubia
reversion change-cause
2 kubectl set image deployment kubia nodejs=luksa/kubia:v2
3 kubectl set image deployment kubia nodejs=luksa/kubia:v3
还记得建立Deployment的时候--record 参数吗?若是不给这个参数,版本历史中的Change-cause这一栏会空。这也会使用户很难辨别每次的版本作了哪些修改。
回滚到一个特定的Deployment版本
kubectl rollout undo deployment kubia --to-reversion=1
这些历史版本的replicaset都用特定的版本号保存Deployment的完整的信息,因此不该该手动删除ReplicaSet。若是删除会丢失Deployment的历史版本记录而致使没法回滚。
ReplicaSet默认保留最近的两个版本,能够经过制定Deployment的reversionHistoryLimit属性来限制历史版本数量。apps/v1beta2 版本的Deployment默认值为10.
 
控制滚动更新的速率
maxSurge 最多超过时望副本数多少个pod,默认为25%,能够设置为整数
maxUanavailable 最多容许指望副本数内多少个pod为不可用状态。
看图比较容易理解
 
暂停滚动更新:
kubectl rollout pause deployment kubia
 
恢复滚动更新:
kubectl rollout resume deployment kubia
 
阻止出错版本的滚动升级
使用minReadySeconds属性指定至少要成功运行多久以后,才能将其视为可用。在pod可用以前,滚动升级的过程不会继续。是由maxSurge属性和maxUanavailable属性决定。
例子:
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: kubia
spec:
replicas:3
minReadySeconds: 10
strategy:
rollingUpdate:
maxSurge: 1
maxUnavilable: 0
type: RollingUpdate
template:
metadata:
name: kubia
labels:
app: kubia
spec:
containers:
- image: luksa/kubia:v3
name: nodejs
readinessProbe:
periodSeconds: 1
httpGet:
path: /
port: 8080
kubectl apply -f kubia-deployment-v3-with-readinesscheck.yaml
kubectl rollout status deployment kubia
就绪探针是如何阻止出错版本的滚动升级的
首先luksa/kubia:v3镜像应用前5个应用是正常反馈,后面会500状态返回。所以pod会从service的endpoint中移除。当执行curl时,pod已经被标记为未就绪。这就解释了为何请求不会到新的pod上了。
使用kubectl rollout status deployment kubia查看状体,发现只显示一个新副本启动,以后滚动升级便没有进行下去,由于新的pod一直处于不可用状态。即便变为就绪状态后,也至少须要保持10秒,才是真正可用。在这以前滚动升级过程将再也不建立任何新的pod,由于当前maxUnavailable属性设置为0,因此不会删除任何原始的pod。若是没有定义minReadySeconds,一旦有一次就绪探针调用成功,便会认为新的pod已经处于可用状态。所以最好适当的设置minReadySeconds.另外就绪探针默认间隔为1秒。
deployment资源介绍完结。
 
复制有状态的Pod
replicaSet经过一个pod模版建立多个pod副本。这些副本除了它们的名字和IP地址不一样外,没有被的差别。若是pod模版里描述了一个关联到特定持久卷声明的数据卷,那么ReplicaSet的全部副本都将共享这个持久卷声明,也就是绑定到同一个持久卷声明。
由于是在pod模版里关联持久卷声明的,又会依据pod模版建立多个副本,则不能对每一个副本都指定独立的持久卷声明。因此也不能经过一个ReplicaSet来运行一个每一个实例都须要独立存储的分布式数据存储服务,至少经过单个ReplicaSet是作不到的。老实说,以前你学习到的全部API对象都不能提供这样的数据存储服务,还须要一个其余的对象--StatefulSet
 
咱们先看不使用StatefulSet的状况下有没有方法实现多个副本有本身的持久卷声明。
三种取巧的方法。
第一种方法,不使用ReplicaSet,使用Pod建立多个pod,每一个pod都有独立的持久卷声明。须要手动建立它们,当有的pod消失后(节点故障),须要手动建立它们。所以不是一个好方法。
第二种方法,多个replicaSet ,每一个rs只有一个pod副本。但这看起来很笨重,并且没办法扩缩容。
第三种方法,使用同一个ReplicaSet,你们也都挂载同一个持久卷声明,应用内部作好互斥,建立多个data数据目录,每个pod用一个标记为在用,后面应用不能选被标记为在用的目录。这样作很难保证协调的一点没问题,同时你们用同一个持久卷,读写io将成为整个应用的瓶颈。
 
除了上面的存储需求,集群应用也会要求每个实例拥有生命周期内惟一标识。pod能够随时被删掉,而后被新的pod替代。当一个ReplicaSet中的pod被替换时,尽管新的pod也可能使用被删除pod数据卷中的数据,但它倒是拥有全新主机名和IP的崭新pod.在一些应用中,当启动的实例拥有彻底新的网络标识,但还使用旧实例的数据时,极可能引发问题,好比etcd存储服务。
固然也能够建立多个service ,每个replicaset对应一个service,那么同样很笨重,且显得很低级。辛运的是,Kubernetes为咱们提供了这类需求的完美解决方案--StatefulSet.
 
了解StatefulSet
能够建立一个StatefulSet资源代替ReplicaSet来运行这类pod.它们是专门定制的一类应用,这类应用中每个实例都是不可替代的。
相关文章
相关标签/搜索