我们通过kubernetns部署wordpress熟悉一下kubernetes的deployment,configmap,secret,service,configmap,扮演了什么样的角色
使用deployment部署nginx
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
ports:
- port: 80
selector:
k8s-app: nginx
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
k8s-app: nginx
spec:
replicas: 2
selector:
matchLabels:
k8s-app: nginx
template:
metadata:
labels:
k8s-app: nginx
spec:
volumes:
- name: nginxconfig
configMap:
name: nginx-config
# - name: wordpress
# hostPath:
# path: /root/wordpress/wordpress
- name: wp-config
configMap:
name: wp-config
containers:
- name: nginx
image: wordpress-nginx:v1.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
volumeMounts:
- name: nginxconfig
mountPath: /etc/nginx/conf.d/
# - name: wordpress
# mountPath: /code/wordpress
- name: wp-config
mountPath: /code/wordpress/wp-config.php
subPath: wp-config.php
通过configmap作为配置文件
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
wordpress.conf: |
server {
listen 80;
server_name localhost;
root /code/wordpress;
location / {
index index.html index.php;
}
location ~ \.php$ {
fastcgi_pass php-svc:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
apiVersion: v1
data:
wp-config.php: "<?php\n/**\n * The base configuration for WordPress\n *\n * The
wp-config.php creation script uses this file during the installation.\n * You
don't have to use the web site, you can copy this file to \"wp-config.php\"\n
* and fill in the values.\n *\n * This file contains the following configurations:\n
*\n * * Database settings\n * * Secret keys\n * * Database table prefix\n * *
ABSPATH\n *\n * @link https://wordpress.org/documentation/article/editing-wp-config-php/\n
*\n * @package WordPress\n */\n\n// ** Database settings - You can get this info
from your web host ** //\n/** The name of the database for WordPress */\ndefine(
'DB_NAME', 'wordpress' );\n\n/** Database username */\ndefine( 'DB_USER', 'root'
);\n\n/** Database password */\ndefine( 'DB_PASSWORD', '1223' );\n\n/** Database
hostname */\ndefine( 'DB_HOST', 'localhost:3306' );\n\n/** Database charset to
use in creating database tables. */\ndefine( 'DB_CHARSET', 'utf8mb4' );\n\n/**
The database collate type. Don't change this if in doubt. */\ndefine( 'DB_COLLATE',
'' );\n\n/**#@+\n * Authentication unique keys and salts.\n *\n * Change these
to different unique phrases! You can generate these using\n * the {@link https://api.wordpress.org/secret-key/1.1/salt/
WordPress.org secret-key service}.\n *\n * You can change these at any point in
time to invalidate all existing cookies.\n * This will force all users to have
to log in again.\n *\n * @since 2.6.0\n */\ndefine( 'AUTH_KEY', '6nj!+=B9#Ow1|b3fM)
u2G@x#_/<([br@3#[_k^EKmdNkc4KJ1RcTsVI3&n*>}I.' );\ndefine( 'SECURE_AUTH_KEY',
\ '#dUIlW{v-](Y;_L2{Z8#Ob|p`~<i2uWATA]0hZIb/*/XU8(GH_,&y-q8y0/}6Xy*' );\ndefine(
'LOGGED_IN_KEY', 'zyM- YHh$4<lE#&Sd#/j;%4cqg7FaDYq!W>la!zbF|MEan`QioyDg21C]+h_ML9('
);\ndefine( 'NONCE_KEY', 'fG@_h3eFW> QnVVoz/xw(TZfpr!Qq#^EndSu~MR8-db8Cii}[h7_=j}6?<=
%9o>' );\ndefine( 'AUTH_SALT', 'o8vjsjY!~fb/8lg!~4 k (R{{XCF#Jk~q,4w7]mG<C=_-E3[87fDE`1bcXq`Wt6V'
);\ndefine( 'SECURE_AUTH_SALT', '9LfxTWx9jY}6Hif3y7y&eM(?K3aO8j1[9%J@#Kx5d0yIpiOKsJm8WL;?;TgRE#u['
);\ndefine( 'LOGGED_IN_SALT', '[;uf+ZwH?krM6<n<PsM]<FtXq8o8Y#T:kRR=}Cs;_gFpg<Tt+H])I9_7VUGZ{hi9'
);\ndefine( 'NONCE_SALT', '1dvR-AGqe+npYseuJ0S$2;(?CR6+IS=)L6*0y$u`zJ7.]H#`f<{/UC=-2uu5s+k>'
);\n\n/**#@-*/\n\n/**\n * WordPress database table prefix.\n *\n * You can have
multiple installations in one database if you give each\n * a unique prefix. Only
numbers, letters, and underscores please!\n */\n$table_prefix = 'wp_';\n\n/**\n
* For developers: WordPress debugging mode.\n *\n * Change this to true to enable
the display of notices during development.\n * It is strongly recommended that
plugin and theme developers use WP_DEBUG\n * in their development environments.\n
*\n * For information on other constants that can be used for debugging,\n * visit
the documentation.\n *\n * @link https://wordpress.org/documentation/article/debugging-in-wordpress/\n
*/\ndefine( 'WP_DEBUG', false );\n\n/* Add any custom values between this line
and the \"stop editing\" line. */\n\n\n\n/* That's all, stop editing! Happy publishing.
*/\n\n/** Absolute path to the WordPress directory. */\nif ( ! defined( 'ABSPATH'
) ) {\n\tdefine( 'ABSPATH', __DIR__ . '/' );\n}\n\n/** Sets up WordPress vars
and included files. */\nrequire_once ABSPATH . 'wp-settings.php';\n\n"
kind: ConfigMap
metadata:
creationTimestamp: "2023-05-27T07:38:52Z"
managedFields:
- apiVersion: v1
fieldsType: FieldsV1
fieldsV1:
f:data:
.: {}
f:wp-config.php: {}
manager: kubectl-create
operation: Update
time: "2023-05-27T07:38:52Z"
name: wp-config
namespace: default
resourceVersion: "4713"
uid: 5c2ebe67-85c9-4328-b118-b5158d1b4995
使用deployment部署php
apiVersion: v1
kind: Service
metadata:
name: php-svc
spec:
ports:
- port: 9000
selector:
k8s-app: php
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: php
labels:
k8s-app: php
spec:
replicas: 2
selector:
matchLabels:
k8s-app: php
template:
metadata:
labels:
k8s-app: php
spec:
volumes:
# - name: wordpress
# hostPath:
# path: /root/wordpress/wordpress
- name: wp-config
configMap:
name: wp-config
containers:
- name: php
image: wordpress-php:v4.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9000
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 256Mi
env:
- name: PATH
value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
volumeMounts:
# - name: wordpress
# mountPath: /code/wordpress
- name: wp-config
mountPath: /code/wordpress/wp-config.php
subPath: wp-config.php
使用deployment部署mysql
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- mountPath: "/var/lib/mysql"
name: mysql-data
volumes:
- name: mysql-data
emptyDir: {}
pod的常见生命状态
1、Unschedulable: #Pod不能被调度,kube-scheduler没有匹配到合适的node节点
2、PodScheduled: #Pod正处于调度中,在kube-scheduler刚开始调度时候,还没有将pod分配到指定的node,在筛选出合适的节点后就会更新etcd数据,将pod分配到指定的node
3、Pending: #正在创建Pod但是Pod中的容器还没有全部被创建完成=[处于此状态的Pod应该检查Pod依赖的存储是否有权限挂载等。]
4、Failed: #Pod中有容器启动失败而导致pod工作异常。
5、Unknown: #由于某种原因无法获得Pod的当前状态,通常是由于与pod所在的node节点通信错误。
6、Initialized: #所有pod中的初始化容器已经完成了
7、ImagePullBackOff: #Pod所在的node节点下载镜像失败
8、Running: #Pod内部的容器已经被创建并且启动。
9、Ready: #表示pod中的容器已经可以提供访问服务
10、Error: # pod启动过程中发生错误
11、NodeLost: #Pod所在节点失联
12、Waiting: #Pod等待启动
13、Terminal: #Pod正在被销毁
14、CrashLoopGBackOff: #pod,但是kubelet正在将它重启
15、InvalidImageName: #node节点无法解析镜像名称导致的镜像无法下载
16、ImageInspectError: #无法校验镜像,镜像不完整导致
17、ErrImageNeverPull: #策略禁止拉取镜像,镜像中心权限是私有等
18、RegistryUnavailable: #镜像服务器不可用,网络原因或harbor宕机
19、ErrImagePull:#镜像拉取出错,超时或下载被强制终止
20、CreateContainerConfigError: #不能创建kubelet使用的容器配置
21、CreateContainerError: #创建容器失败
22、RunContainerError:#pod运行失败,容器中没有初始化PID为1的守护进程等
23、ContainersNotInitialized: #pod没有初始化完毕
24、ContainerNotReady: #pod没有准备完毕
25、ContainerCreating:#pod正在创建中
26、PodInitializing: #pod正在初始化中、
27、DockerDaemonNotReady:#node节点docker服务没有启动
28、NetworkPluginNotReady: #网络插件没有启动
Kubernetes 的所有管理能力构建在对象抽象的基础上,核心对象包括:
• Node:计算节点的抽象,用来描述计算节点的资源抽象、健康状态等。
• Namespace:资源隔离的基本单位,可以简单理解为文件系统中的目录结构。
• Pod:用来描述应用实例,包括镜像地址、资源需求等。 Kubernetes 中最核心
的对象,也是打通应用和基础架构的秘密武器。
• Service:服务如何将应用发布成服务,本质上是负载均衡和域名服务的声明
kubertnetes主节点
APIserver: 这是 Kubernetes 控制面板中唯一带有用户可访问 API 以及用户可交互的组件。API 服
务器会暴露一个 RESTful 的 Kubernetes API 并使用 JSON 格式的清单文件(manifest
files)。
etcd: Kubernetes 使 用“etcd”。这是一个强大的、稳定的、高可用的键值存储,被
Kubernetes 用于长久储存所有的 API 对象。
controller manager: 被称为“kube-controller manager”,它运行着所有处理集群日常任务的控制器。包括了节点控制器、副本控制器、端点(endpoint)控制器以及服务账户等。
scheduler: 调度器会监控新建的 pods(一组或一个容器)并将其分配给节点
工作节点
kubelet: 负责调度到对应节点的 Pod 的生命周期管理,执行任务并将 Pod 状态报告给主节点的渠道,通过容器运行时(拉取镜像、启动和停止容器等)来运行这些容器。它还会定期执行被请求的容器的健康探测程序。
kube-proxy: 它负责节点的网络,在主机上维护网络规则并执行连接转发。它还负责对正在服务的 pods 进行负载平衡
我们用下面的方式访问etcd里的数据
• 进入容器查询数据
export ETCDCTL_API=3
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key
/etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt get --keys-only --prefix /
• 监听对象变化
etcdctl --endpoints https://localhost:2379 --cert /etc/kubernetes/pki/etcd/server.crt --key
/etc/kubernetes/pki/etcd/server.key --cacert /etc/kubernetes/pki/etcd/ca.crt watch --prefix /registry/services/specs/default/mynginx
Kube-APIServer 是 Kubernetes 最重要的核心组件之一,主要提供以下功能:
• 提供集群管理的 REST API 接口,包括:
• 认证 Authentication;
• 授权 Authorization;
• 准入 Admission(Mutating & Valiating)。
• 提供其他模块之间的数据交互和通信的枢纽(其他模块通过 APIServer 查询或
修改数据,只有 APIServer 才直接操作 etcd)。
• APIServer 提供 etcd 数据缓存以减少集群对 etcd 的访问。
像很多httpserver的开发一样我们需要注册路由,APIHandler用来注册路由,对于对apiserver的访问都需要认证,它时用AuthN来做的,然后它会做速率限制,用Rate Limit来做,他还有日志记录模块,是Auditing,然后就是授权AuthZ,kubernetes被广泛使用的是RBAC,apiServer还支持Aggregater,就是你可以定义自己校验准则,那么准入控制怎么理解?在你的请求之上加入一些东西,添加和更改一些属性,在存入etcd之前,也就是在原始请求上面动一些东西(Mutating)同时还得知道这个请求是否有效(Valiating),
kubernetes提供了好几种project volume 其中包括secret, configmap,DownwardApi,ServiceAccountToken,
Secret 最典型的使用场景,莫过于存放数据库的 Credential 信息,比如下面这个例子
apiVersion: v1
kind: Pod
metadata:
name: volume-secret
spec:
containers:
- name: volume-secret
image: wordpress-nginx:v2.0
imagePullPolicy: Never
volumeMounts:
- name: mysql-cred
mountPath: /project-volume
readOnly: true
volumes:
- name: mysql-cred
projected:
sources:
- secret:
name: user
- secret:
name: pass
我们可以通过下面的命令创建secret
kubectl create secret generic pass --from-file=./password.txt
kubectl create secret generic user --from-file=./user.txt
也可以使用yaml的方式,但是内容必须是base64加密的
apiVersion: v1
kind: Secret
metadata:
name: volume-secret
type: Opaque
data:
user: YWRtaW4=
pass: YWRtGEGW
更重要的是,像这样通过挂载方式进入到容器里的 Secret,一旦其对应的 Etcd 里的数据被更新,这些 Volume 里的文件内容,同样也会被更新。其实,这是 kubelet 组件在定时维护这些 Volume
downwardAPi支持我们把pod的信息通过volume挂载到容器内,如下面例子
apiVersion: v1
kind: Pod
metadata:
name: downwardapi-volume
labels:
zone: us-tset-coast
cluster: test-cluster1
rack: rack-23
spec:
containers:
- name: tset
image: nginx:latest
imagePullPolicy: IfNotPresent
command: ["sh","-c"]
args:
- while true; do
if [[ -e /etc/pods/labels ]]; then
echo -en '\n\n'; cat /etc/labels; fi;
sleep 5;
done;
volumeMounts:
- name: podinfo
mountPath: /etc/pods
readOnly: false
volumes:
- name: podinfo
projected:
sources:
- downwardAPI:
items:
- path: labels
fieldRef:
fieldPath: metadata.labels
downwardAPI还支持下面的类型
1. 使用fieldRef可以声明使用:
spec.nodeName - 宿主机名字
status.hostIP - 宿主机IP
metadata.name - Pod的名字
metadata.namespace - Pod的Namespace
status.podIP - Pod的IP
spec.serviceAccountName - Pod的Service Account的名字
metadata.uid - Pod的UID
metadata.labels['<KEY>'] - 指定<KEY>的Label值
metadata.annotations['<KEY>'] - 指定<KEY>的Annotation值
metadata.labels - Pod的所有Label
metadata.annotations - Pod的所有Annotation
2. 使用resourceFieldRef可以声明使用:
容器的CPU limit
容器的CPU request
容器的memory limit
容器的memory request
当我们使用downwardAPI的时候我们能挂载的信息必须是容器未启动前就确定的东西,我们也可以通过env环境变量的方式注入信息,只不过当env的内容改变的时候容器里的内容不会变,通过volume挂载会实时更新,kubelet在维护,我们知道要访问到apiserver我们必须要经过授权,而我们创建的pod并未对它进行授权操作,它是怎末访问到apiserver的呢,那是因为我们创建的每个pod,kubernetes都为它默认挂载了一个service account,pod只用携带了里面的serviceaccount Token,就能访问到 apiserver,出于安全考虑,我们也可以声明不挂载这个service account,
kubernetes 支持三种探针方式对容器进行检查,它们分别是,livesnessprobe,readnessprobe,startupprobe,我们通过下面的一个简单的例子说明一下
apiVersion: v1
kind: Pod
metadata:
labels:
tset: liveness
name: test-liveness
spec:
containers:
- name: liveness
image: nginx:latest
imagePullPolicy: IfNotPresent
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
现在是正常的,过了30秒我们看看pod events
我们发现pod的liveness发现了异常,但是这个时候为什么pod状态还是running呢,这是因为这个pod的PodRestartPolicy是always,当livess不通过的时候会根据pod重启策略对它操作,有下面三种策略
ALways: 总是重启
OnFailure: 只有在容器异常的时候才重启
Never:不重启
当我们运行一个job pod的时候为它设成always就没有意义,如果我们我们在容器退出之后收集他的日志信息,我们就要设置成never,因为重启之后这些信息可能会消失
只要pod的restartPolicy是always,那pod的状态就一直是running。进行重启,否则就进入Faied
对于包含多个容器的pod,只有里面的容器都是异常,pod状态才是failed,在此之前pod的状态都是running,ready 显示的是正常pod的数量
所以,假如一个 Pod 里只有一个容器,然后这个容器异常退出了。那么,只有当 restartPolicy=Never 时,这个 Pod 才会进入 Failed 状态。而其他情况下,由于 Kubernetes 都可以重启这个容器,所以 Pod 的状态保持 Running 不变。而如果这个 Pod 有多个容器,仅有一个容器异常退出,它就始终保持 Running 状态,哪怕即使 restartPolicy=Never。只有当所有容器也异常退出之后,这个 Pod 才会进入 Failed 状态。当我们使用readynessprobe的时候,容器异常会从service后端剔除,如果使用了startupprobe,会禁用其他探针,直到它成功,主要解决在慢启动程序或复杂程序中readinessProbe、livenessProbe探针无法较好的判断程序是否启动、是否存活,
当我们使用kind类型为pod的时候去创建pod的时候,这个pod就会跟node绑定,当我们使用控制器创建pod的时候,当node出现问题的时候,可以把pod调度到其他节点
先简单了解一下controller
Controller Manager 是集群的大脑,是确保整个集群动起来的关键;
• 作用是确保 Kubernetes 遵循声明式系统规范,确保系统的真实状态(Actual
State)与用户定义的期望状态(Desired State)一致;
• Controller Manager 是多个控制器的组合,每个 Controller 事实上都是一个
control loop,负责侦听其管控的对象,当对象发生变更时完成配置;
• Controller 配置失败通常会触发自动重试,整个集群会在控制器不断重试的机
制下确保最终一致性( Eventual Consistency)。
他的逻辑如下图
controller是一个典型的生产者消费者模型,我们可以通过kubebuilder等代码生成工具生成的,会生成informer我们观察的对象的代码框架,list全量的代码框架,然后通过路由注册,决定对该对象执行神什么操作,又很多event,比如 delete update等,然后把他的key也就是namespace, 放入到一个队列中,然后启动goroutin 多个worker 去从这个队列去取数据,
控制器就是一直在执行一个controller loop 把实际状态变成期望状态,期望状态就是replicas所定义的数量,它会被写到etcd,controller 从etcd中统计labels对应的pod数量,根据deployment定义的template来创建或删除对象,deployment 对实际上是通过replicasset控制pod的数量的,每一个replicasset都对应一个版本的一组pod,我们通过一组例子来看看
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 4
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.12
imagePullPolicy: IfNotPresent
创建的时候加上 --record
使用 rollout status 查看状态。然后我们通过kebectl edit 改一下镜像,查看一下deployment的信息
deployment的字段中还有一个滚动更新的策略,RollingUpdateStrategy 的配置中,maxSurge 指定的是除了 DESIRED 数量之外,在一次“滚动”中,Deployment 控制器还可以创建多少个新 Pod;而 maxUnavailable 指的是,在一次“滚动”中,Deployment 控制器可以删除多少个旧 Pod。
用下面图片描述关联信息
我们可以使用 rollout undo 回到上一个版本
kubectl rollout undo deploy/nginx-deployment
使用rollout 查看历史版本
kubectl rollout history deploy/nginx-deployment
看版本的详细信息
kubectl rollout history deploy/nginx-deployment --revision=2
回到某个版本
kubectl rollout undo deploy/nginx-deployment --to-revision=2
通过pause参数控制replicasset 的数量
kubectl rollout pause deploy/nginx-deployment
中间进行任何操作,最后在resume,只生成一个replicasset
kubectl rollout resume deploy/nginx-deployment
Deployment 对象有一个字段,叫作 spec.revisionHistoryLimit,就是 Kubernetes 为 Deployment 保留的“历史版本”个数。所以,如果把它设置为 0,你就再也不能做回滚操作了。
金丝雀部署:优先发布一台或少量机器升级,等验证无误后再更新其他机器。优点是用户影响范围小,不足之处是要额外控制如何做自动更新。
蓝绿部署:2组机器,蓝代表当前的V1版本,绿代表已经升级完成的V2版本。通过LB将流量全部导入V2完成升级部署。优点是切换快速,缺点是影响全部用户。
deployment创建pod的流程图
我们一般可以通过两种方式创建pod,一种是命令行的方式,一种是编写yaml文件的方式,我们一般使用控制器去创建pod,这样的话就算pod当前的node坏掉了也可以调度到其他健康的节点,这些常见的控制器包括deployment statefulset daemonset,加入我们创建了一个deployment,首先APIserver会鉴别我们有没有权限,通过之后把具体的api信息写入到etcd中,这个时候集群中的controller manager开始工作了,它包含很多控制器,deployment controller发现一个新的deployment被创建了,就会创建一个replicasset,把这个信息写入到etcd中去,集群中的replicasset controller发现一个新的replicasset被创建了 它会实现控制器的基本模式,就是把实际状态变成期望状态,期望状态就是replicas所对应的值,它会被写到etcd中,实际状态就是查看labels匹配的pod数量是否大于replicas对应的值,在这里我们需要创建pod,把这个信息给到apiserver,apiserver与控制器之间的连接是watch实时连接,pod有一个nodename的字段,这时候它为空,调度器就开始工作了,它通过一系列的算法挑选到一个合适的node,把节点的名字写入到pod中去,把这个信息给到apiserver,kubelet会实时监控当前节点的信息,当它发现apiserver中有pod中的nodename字段和自己当前的节点的一样他们还没有被创建,然后它就开始创创建工作了,看你集群中用的是什么cri,docker 还是containerd,外挂存储的话,调用csi工作,cni中的网络插件未pod设置网络,最后在把创建的pod的信息给到apiserver,再把它写入到etcd中。