K8s基本概念
Node
一个物理节点或虚拟机
Pod
k8s的基本组件和最小单元,是container的抽象。用来运行容器,一个pod中可以运行多个容器,但通常只运行一个
Serivce
可以被附加在Pod上的静态(永久)IP,Pod和Service的生命周期是不同的,Pod停止后,Service依然存在。Service可以绑定多个Pod,起到负载均衡的作用。
Ingress
用于实现用域名的方式访问k8s内部应用
ConfigMap
来保存key-value pair配置数据
Secret
存储加密的配置数据
Volume
挂载一个存储到Pod中,可以是Pod运行的服务器(即Node)的存储,或是远程存储(不是K8s的一部分)。K8s不管理存储。
Deployment
Pod的抽象,创建Pod的蓝图,可以指定由多少个Pod副本
StatefulSet
数据库等有状态的Pod,通过StatefulSet来启动,而不是Deployment
K8s架构
K8s集群的Node分为master Node和worker Node,Wokrder Node中可以存在多个Pod。
每个Worker Node要安装三样东西:
- Contaier Runtime,如Dokcer
- Kublete,接受配置,并在Node中自动Pod,分配Node的资源(CPU,RAM)给Pod
- Kube Proxy,在请求转发到Pod的组件,起到负载均衡的作用
Master Node
- Api Server,与K8s集群交互的唯一通道
- Scheduler,调度器,寻找一个用来部署Pod的Node,然后由Kublete启动Pod
- Controller Manager,用来检测Pod的状态,如果Pod crash后,Controller会通知Scheduler重新调度该Pod
- etdc,用来表示集群的KV对,集群所有的状态改变都存储在其中。Controller的行为依据etdc来运行 Master Node一般部署多个,所以Api Server是负载均衡的,etcd是分布式存储。
Kubectl
K8s集群的命令行工具
Kubectl基本命令
- kebuectl get node.获取所有Node
- kubectl get pod.获取所有pod
$ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-depl-5ddc44dd46-ss96b 1/1 Running 0 3h34m[-o] output, -o wide 获取更多信息, -o yaml获得详细信息,并以yaml的形式展示
- kubectl get service
- kubectl edit [deploy-name],获得deployment的配置文件。修改配置文件后,pod在列表中替换直到消失,但是replicaset中还存在,只是数量都为0
- kubectl logs [pod-name],获取Pod中应用的日志
- kubectl exec -it [pod-name] – /bin/bash,在容器中打开交互式命令行
- kubectl delete [deployment],删除deployment,会连带删除deplicaset和pod
- kubectl describe pod [pod-name],获取pod的信息,用于调试
创建Deployment
kubectl create depolyment [name] –image=[image-name],创建一个最基本的deployment,只有名称和镜像名
Replicaset
Replicaset用于管理Pod的副本,实践中,不能对这个对象作修改,
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
nginx-depl-5ddc44dd46 1 1 1 3h34m
K8s配置文件
Deployment Configuration
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec: # for deployment
replicas: 2 # 部署多少个副本
selector:
matchLabels:
app: nginx
template: # 在一个配置文件中再套一层
metadata:
labels: # label用于识别一组资源,是一组键值对
app: nginx
spec: # for a pod,Pod的blueprint,
containers:
- name: nginx # 在Pod部署一个container
image: nginx:1.16
ports:
- containerPort: 8080
Service Configuration
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector: # 在deployment和pod之间建立连接
app: nginx
ports:
- protocol: TCP
port: 80 # 请求被发往80端口
targetPort: 8080 # pod在监听那个端口,service将请求转发到对应的pod
Secret Configuration
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
data:
password: YWRtaW4xMjM= # base64编码
ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-configmap
data:
database_url: mysql-service
配置文件可以定义Department、Service等。配置文件由三部分组成:
- metadata。name
- specification
- status。自动生成,K8s用来比较期望的状态和当前状态(存储在etcd中)来做出改变
kubectl apply -f [file-name]。部署一个配置文件,一个配置文件可以被反复部署,多次部署后K8s会识别到是一次更新,如下:
$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment created
$ kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment configured
外网可访问的Service
默认创建的service分配的IP是k8s集群内部IP,所以叫internal service,通过指定type为LoadBalancer和nodePort,将其变为external service。默认的service type是ClusterIP。
apiVersion: v1
kind: Service
metadata:
name: mysql-express-service
spec:
selector:
app: mysql-express # 转发到哪个Pod
type: LoadBalancer
ports:
- protocol: TCP
port: 3333
targetPort: 3333
nodePort: 30000 # 对外暴露的端口
通过describe命令查看service的部署
$ kubectl describe service nginx-service
...
Port: <unset> 80/TCP
TargetPort: 8080/TCP
Endpoints: 172.17.0.3:8080,172.17.0.4:8080 // 请求转发的目的
Session Affinity: None
Events: <none>
Namespace
可以认为是K8s集群中的虚拟集群。多个团队如果再同一个命名空间部署同一个name的组件,会覆盖掉另一个。不同的namespace有隔离作用。通过kubectl获取的组件都是在默认default namespace。部署component时,通过选项-n(–namespace)指定要部署到哪个namespace。或者在配置文件的metadata中指定。 Servcie可以跨命名空间共享,指定service name时,在后面加上所在的namespace即可。
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-configmap
namespace: other-namespace ##
data:
database_url: mysql-service.some-namespace # 通过后缀指定
Ingress
通过暴露同一的外部访问方式,将请求转发给service。每个external service会占用一个端口,ingress可以通过域名的形式访问。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name
spec:
rules:
- host: app.com # 访问的host
http:
paths: # 通过url定义转发规则
- backend:
serviceName: my-service # 转发给对应的internal service
servicePort: 8080 # service的port
Controller
Ingress只是K8s的一层抽象,如果要让IIngress工作,必须选取一个实现,这个实现被称为Controller。Controller也是运行在K8s商上的Pod。Controller是集群中域名的entry point。
安装controller
$ kubectl addons enable ingress
这个命令会安装默认的ingress实现,即nginx-ingress-controller,并运行一个pod。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
spec:
rules:
- host: dashboard.com
http:
paths:
- backend:
serviceName: kubernetes-dashboard
servicePort: 80
Helm
K8s的包管理器,打包yaml文件,并将其分发到仓库上,能被其他用户共享。yaml文件的集合也被称为Helm Charts。
Helm可以用于构建蓝图;或者是模板,动态将文件中的placeholder进行替换。Template file存放这yaml模板,placeholder的值在value.yml中寻找。
结构
Helm的顶层是个文件夹,文件夹的名称是charts的名称。Chart.yaml包含chart的元数据,如名称、依赖、版本。Values.yaml包含template中的 value,可以有默认值并重载它。charts文件夹有chart的依赖,有其他chart文件。template文件夹包含模板文件。
mychart/
Chart.yaml
cahrts/
templates/
当执行 helm install
替换默认值
helm install --values=my-value.yml <cahrtname>
这个命令会将my-value.yml中的值合并到Value.yml中。
或:
helm install --set version=xxx
Release Managerment
Volumes
K8s有三种持久化数据的组件,并相互配合
- Persistent Volume, pv
- Persistent Volume Claim, pvc
- Storage Class, sc
K8s没有开箱即用的持久化方案,需要自己配置在pod重启时保存数据。因为不知道pod何时重启,所以storage要能被所有pod访问到
pv
pv是集群中的资源,像cpu和ram一样。用来存储数据。pv由k8s配置文件创建。
pv是一个抽象概念,它必须由实际的物理存储提供实现,可以选择node的物理存储,或者aws之类的云存储。pv没有命名空间。
storageClass:
- hostpath 类似docker,将node的路径挂载到pod中。但是在集群中pod可能会被调度到其他node上
```yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: example-volume
labels:
type: local
spec:
#we use local node storage here!
#kubectl get storageclass
通过kubectl get storageclass获取。k8s存储类型接口,k8s需要知道使用的何种类型的存储
storageClassName: hostpath # docker提供的 capacity: # 容量 storage: 1Gi accessModes: # 权限
- ReadWriteOnce hostPath: path: “/mnt/data” # 在host上的哪个路径 ```
pvc
pv由管理创建,开发者在应用声明他们想用到的pv,这在pvc中配置。配置会会匹配到一个具体pv,然后应用。向pv申请存储。有namespace
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: example-claim
spec:
storageClassName: hostpath
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Mi # 从pv中申请50M
在pod应用:
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config
labels:
app: postgres
data:
POSTGRES_DB: postgresdb
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin123
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
selector:
matchLabels:
app: postgres
replicas: 1
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:10.4
imagePullPolicy: "IfNotPresent"
ports:
- containerPort: 5432
envFrom:
- configMapRef:
name: postgres-config
volumeMounts: # 挂载到pod哪个目录下
- name: data # 指向volume
mountPath: /var/lib/postgresql/data
volumes:
- name: data
persistentVolumeClaim: # 指向一个具体的pvc
claimName: example-claim
---
apiVersion: v1
kind: Service
metadata:
name: postgres
labels:
app: postgres
spec:
selector:
app: postgres
ports:
- protocol: TCP
name: http
port: 5432
targetPort: 5432
应用发起存储请求时,被发送给pvc,然后pvc将请求发送给匹配的pv。找到匹配的pv后,volume会被挂载到pod上
sc
当pvc想要pv时,sc可以用来动态创建pv。
StatefulSet
StatefulSet和Deployment相似,可以有replica和storage。Deployment创建时会有个随机的名称,创建和删除pod是随机的。StatefulSet创建时要在yml指定名称,因为要有一种机制来决定那个节点是主节点,其他节点同步主节点数据,所以存储(pv)也是分开的。pod会共享volume,statefulset有自己的pv。pod没有DNS。
statefulset在启动是不会同时启动,而是会一个个启动。stateful命名规则为,[podname]-[ordinal],ordinal会从0累加。
---
apiVersion: v1
kind: ConfigMap
metadata:
name: redis-cluster
data:
update-node.sh: |
#!/bin/sh
REDIS_NODES="/data/nodes.conf"
sed -i -e "/myself/ s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${POD_IP}/" ${REDIS_NODES}
exec "$@"
redis.conf: |+
cluster-enabled yes
cluster-require-full-coverage no
cluster-node-timeout 15000
cluster-config-file /data/nodes.conf
cluster-migration-barrier 1
appendonly yes
protected-mode no
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
spec:
serviceName: redis-cluster # 暴露pod
replicas: 6
selector:
matchLabels:
app: redis-cluster
template:
metadata:
labels:
app: redis-cluster
spec:
containers:
- name: redis
image: redis:5.0.1-alpine
ports:
- containerPort: 6379
name: client
- containerPort: 16379
name: gossip
command: ["/conf/update-node.sh", "redis-server", "/conf/redis.conf"]
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: conf
mountPath: /conf
readOnly: false
- name: data # redis写入数据的路径
mountPath: /data
readOnly: false
volumes:
- name: conf
configMap:
name: redis-cluster
defaultMode: 0755
volumeClaimTemplates: # 通过pvc来持久化数据,为每个pod动态提供volume
- metadata:
name: data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "hostpath"
resources:
requests:
storage: 50Mi # 在data这个volume里面,声明50M的存储
---
apiVersion: v1
kind: Service
metadata:
name: redis-cluster
spec:
clusterIP: None
ports:
- port: 6379
targetPort: 6379
name: client
- port: 16379
targetPort: 16379
name: gossip
selector:
app: redis-cluster