k8s 部署应用
看完ubuntu 搭建 k8s 集群后我们搭建起了一个 k8s 集群,继续学习 k8s,本文讲述在k8s中部署应用的相关操作.
kubectl 是 k8s 集群管理控制器,使用kubectl api-resources
可以显示服务器支持的 API 资源
接下来进行实战
一. 部署一个应用
deployment 是 Pod 之上的抽象,他可以定义一组 Pod 的副本数目,以及这个 Pod 的版本.一般大家用 Deployment 来做应用真正的管理,而 Pod 是组成 Deployment 最小的单元
kubectl create deployment web --image=nginx:1.14 --dry-run -o yaml > web.yaml
create deployment web
创建一个名为 web 的 deployment 资源--dry-run
表示检查语法,但不实际执行-o yaml
表示以 yaml 格式输出> web.yaml
表示输出重定向到 web.yaml
# web.yaml
----------------------------------------------------------------------------------------------------
apiVersion: apps/v1 # 版本号
kind: Deployment # 种类,还有 Pod 之类的
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
replicas: 1 # 副本数量
selector:
matchLabels:
app: web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: web
spec:
containers:
- image: nginx:1.14 # 指定的镜像文件
name: nginx
resources: {}
status: {}
用下面的命令将配置 web.yaml 应用到 resource,
kubectl apply -f web.yaml
执行完毕后可以通过以下命令查看 Deployment 和 Pod
kubectl get deplyment,pods -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment.extensions/web 1/1 1 1 34m nginx nginx:1.14 app=web
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/web-7c5c4cb946-zxf97 1/1 Running 0 8s 10.24.1.11 k8s-node1 <none> <none>
可以到当前资源正在正常运行,
curl 10.24.1.11
如果有 nginx 的标准返回说明应用已经部署完毕
二. 扩容或缩容
k8s 扩容十分简单,只需要修改之前的 web.yaml 文件,将 replicas的值改为 10,然后再更新配置
kubectl replace -f web.yaml
也可以直接使用 scale 命令,一键指定副本数
kubectl scale deploy web --replicas=10
三. 节点污点
节点的属性:
- NoSchedult: 一定不被调度,master 默认为此
- PreferNodSchedult: 尽量不被调度
- NoExecute: 不会调度,并且还会驱逐 Node 已有 Pod
- none: 正常调度
查看当前节点污点值
kubectl describe node k8s-master | grep Taints
节点删除污点
kubectl taint node k8s-master node-role.kubernetes.io/master:NoSchedule-
节点设置污点
kubectl taint node k8s-master node-role.kubernetes.io/master=:NoSchedule
四. Pod 调度策略
Pod 的调度策略会影响 Pod 最终被调度到哪些节点上:
- 预选策略 必须全部满足,比如 node 是否正常,对 pod 依赖的存储卷是否能满足需求
- 优选策略 会根据启用的函数的得分相加得到评估
- 高级调度
- Toleration
- Taint
- label
- Affinity 分为软亲和性和硬亲和性
1. request limits
继续在之前的环境上测试
先删除之前部署的资源
kubectl delete deployment web
然后修改web.yaml
的containers
字段
containers:
- image: nginx:1.14
name: nginx
resources:
requests:
memory: "3Gi"
limits:
memory: "4Gi"
最后应用
kubectl apply -f web.yaml
2. 节点标签选择器
删除 deployment
kubectl delete deployment web
给节点打标签
kubectl label node k8s-master test23_env=prod
修改web.yaml
的containers
字段
spec:
hostNetwork: true
containers:
- image: nginx:1.14
name: nginx
resources: {}
nodeSelector:
test123_env: prod
可以发现 Pod 都被调度到了 master 节点,并且节点的污点值的优先级高于标签
删除标签
kubectl label node k8s-master test123_env-
3. 节点亲和性
亲和性和节点选择器类似, 多了操作符:In NotIn Eists Gt Lt DoesNotExists
,
- 软亲和性
preferreDuringSchedulingIgnoredDuringExecution
非必须满足 - 硬亲和性
requiredDuringSchedulingIgnoredDuringExecution
必须满足
spec:
containers:
- image: nginx:1.14
name: nginx
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: test123_env
operator: In
values:
- dev
- test
preferreDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
- matchExpressions:
- key: group
operator: In
values:
- ttttest
五. secret
secret 是包含少量敏感数据(例如密码,令牌或密钥)的对象.不然此类细心就可能会放入 Pod 或容器镜像中.使用 secret 意外着不需要将应用程序代码中包含机密数据.
secret 可以独立于使用它们的 Pod 创建,因此在创建,查看和编辑 Pod 的工作流程中暴露 secret 的风险较小.
Secret 可以通过三种方式于 Pod 一起使用:
- 作为挂载在一个或多个容器上的卷中的文件
- 作为容器的环境变量
- 在为 Pod拉取镜像时有 kubelet 执行
1. secret 创建方式
a. 命令行创建
创建名为secret1
的 secret 资源
echo -n 'admin' > ./username
echo -n '123456789' > ./password
kubectl create secret generic secret1 --from-file=./username --from-file=./password
b.编写 yaml
首先获取 user 和 password 的 base64 编码
echo -n 'admin' | base64
echo -n '123456789' | base64
然后编写 yaml 文件
vim secret.yaml
-------------------------------------
apiVersion: v1
kind: Secret
metadata:
name: secret2
type: Opaque
data:
username: YWRtaW4=
password: MTIzNDU2Nzg5
最后使用secret.yaml
文件创建
kubectl apply -f secret.yaml
查看指定的 secret 资源信息
kubectl describe secret secret1
查看所有 secret 资源信息
kubectl get secret
查看指定 secret 资源的内容
kubectl get secret secret2 -o jsonpath='{.data}'
# 输出 map[password:MTIzNDU2Nzg5 username:YWRtaW4=]%
echo -n "MTIzNDU2Nzg5" | base64 --decode # 解码
删除创建的 secret 资源
kubectl delete secret secret1
2. secret资源的使用方式
a. 添加到 Pod 的环境变量
b. 以 volume 的形式挂载到 pod
六. ConfigMap
ConfigMap 和 secret 类似但是不需要加密
vim redis.map
-------------------------------
redis.ip=127.0.0.1
redis.port=6379
redis.password=123456
执行下面的命令创建一个名为redis-config
的 ConfigMap
kubectl create configmap redis-config --from-file=redis.map
七. 存储编排
自动挂载所选存储系统,包括本地存储,网络存储系统.
- PV: PersistentVolume, 持久化卷, PV 是存储的抽象,为了使本地磁盘与网络磁盘提供一致的服务.
- PVC: PersistentVolumnClaim, 持久化卷声明, PVC 是 PV 的使用者,当底层存储变化了,只需要修改 PV,而不用修改 Pod
1. 本地存储
a. 创建 PV
创建一个 PV.yaml 声明的本地存储路径为/data/hostpath
vim PV.yaml
------------------------------------
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
hostPath:
path: /data/hostpath
创建 PV 资源
kubectl apply -f PV.yaml
查看已有的 PV 资源
kubectl get pv
这个 pv 的状态为Available
,说明还没有被 PVC 绑定,
⚠️注意这里声明的 hostPath 所有节点都有
b.创建 PVC
vim PVC.yaml
----------------------------
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
创建 PVC 资源
kubectl apply -f PVC.yaml
查看
kubectl get pv,pvc
可以发现 pv 的状态变为了 Bound
,表示这个 pv已经被绑定,PVC 是由 k8s 查找满足我们声明要求的 PV,然后由它来绑定的.
c. Pod 绑定 PVC
修改之前的 web.yaml
的Containers
字段
containers:
- image: nginx:1.14
name: nginx
volumeMounts:
- name: myresource
mountPath: /usr/share/nginx/html
volumes:
- name: myresource
persistentVolumeClaim:
claimName: my-pvc
⚠️注意在Pod 所在的 node 的/data/hostpath目录下创建 index.html,不然本地路径替换了原有的路径,里面内容为空.
将 web.yaml
中的replica设置为多个后, PV 虽然只有一个.但是与 Pod 的概念类似,与它绑定的 Pod 有 N 个副本,PV就有 N 个副本,且PV 的副本之间的内容可以不一致
我尝试使用 hostNetwork: true
,发现这样使得一台 node 只能启动一个 Pod.所以使用 StatefulSet+PV+hostNetwork 就可以部署类似挖矿的程序,
# root @ k8s-master in ~ [10:07:19]
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-84fc5964b5-27hfv 1/1 Running 1 9m35s 192.168.1.21 k8s-master <none> <none>
web-84fc5964b5-j8w24 0/1 CrashLoopBackOff 5 4m51s 192.168.1.21 k8s-master <none> <none>
web-84fc5964b5-n8lzh 1/1 Running 0 9m34s 192.168.1.8 k8s-node1 <none> <none>
# root @ k8s-master in ~ [10:07:27]
$ curl 192.168.1.21
master hello world
# root @ k8s-master in ~ [10:07:34]
$ curl 192.168.1.8
node1 hello world
2. NFS 存储
- 安装 NFS 服务器
# 安装nfs
yum install -y nfs-utils
# 创建共享目录
mkdir -p /data/nfs
# 配置共享目录
cat > /etc/exports <<EOF
/data/nfs *(rw,no_root_squash)
EOF
# 启动nfs服务
systemctl start nfs
# 查看服务是否启动成功
ps aux | grep nfs
- 修改 PV.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
labels:
type: remote
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
nfs: # 声明nfs存储
path: /data/nfs
server: 192.168.108.100
- apply 所有
八. 服务发现与负载均衡
扩容后有很多 Pod,访问每个 Pod 的方式都是不同的,如何均匀的访问每一个 Pod(负载均衡),并且当扩容或者缩容的时候能够动态的将Pod添加或者剔除出负载均衡的范围(服务发现).
service 类型介绍(http://dockone.io/article/4884)
k8s 中的 Service用来提供这个服务,service 有 3 种类型
- ClusterIp, 集群内部访问(默认)
- NodePort: 集群外部访问(包含 ClusterIp)
- LoadBalancer: 暴露服务到 internet 的标准方式
- Ingress: 它处于多个服务的前端,扮演着“智能路由”或者集群入口的角色。
kubectl expose deployment web --port=8080 --target-port=80 --type=NodePort --dry-run -o yaml > service.yaml
expose deployment web
将类型为 deployment 的 web公开为新的k8s服务,--port
: 指定集群内部访问的端口--target-port
: 指定容器本身的端口--type
表明这个 service 的类型
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 80
selector:
app: web
type: NodePort
status:
loadBalancer: {}