kubernetes 核心实践

docker   k8s  

资源创建方式

  • 命令行
  • yaml

NameSpace

名称空间用来隔离资源

kubectl create ns hello  
kubectl delete ns hello  
apiVersion: v1  
kind: Namespace  
metadata:  
  name: hello
kubectl apply -f hello.yaml  
kubectl delete -f hello.yaml  

pod

运行中的一组容器,Pod是kubernetes中应用的最小单位.

# 创建一个mytest命名空间
kubectl create ns mytest  
# 在mytest里运行一个镜像
kubectl run nginx --image=nginx -n mytest  
# 查看pod
kubectl get pod -n mytest  
# 描述,排错时使用
kubectl describe pod mynginx -n mytest  
# 删除
kubectl delete pod mynginx -n mytest  
# 查看Pod的运行日志
kubectl logs mynginx -n mytest  
# 每个Pod - k8s都会分配一个ip
kubectl get pod -owide -n mytest  
NAME      READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES  
mynginx   1/1     Running   0          60s   192.168.51.197   k8s-node-001   <none>           <none>  
# 集群中的任意机器可以通过pod分配的IP进行访问 
curl 192.168.51.197

# 进入pod里面执行命令
kubectl exec -it mynginx -n mytest  -- /bin/bash  
  • 使用yaml创建(也可在k8s dashboard创建)
apiVersion: v1  
kind: Pod  
metadata:  
  labels:
    run: mynginx
  name: mynginx
#  namespace: default
spec:  
  containers:
  - image: nginx
    name: mynginx
# 创建
kubectl apply -f nginx.yaml  
# 删除
kubectl delete -f nginx.yaml  
  • 一个pod部署多个容器(相同的应用不能放在一个pod上)
apiVersion: v1  
kind: Pod  
metadata:  
  labels:
    run: myapp
  name: myapp
  namespace: mytest
spec:  
  containers:
  - image: nginx
    name: nginx
  - image: tomcat:8.5.68
    name: tomcat
[root@k8s-master-001 ~]# kubectl apply -f myapp.yaml 
[root@k8s-master-001 ~]# kubectl get pod -owide -n mytest
NAME      READY   STATUS    RESTARTS   AGE     IP               NODE           NOMINATED NODE   READINESS GATES  
myapp     2/2     Running   0          3m4s    192.168.74.195   k8s-node-002   <none>           <none>

Deployment

控制Pod,使Pod拥有多副本,自愈,扩缩容等能力

# 清除所有Pod,比较下面两个命令有何不同效果?
kubectl delete pod myapp mynginx -n mytest (删除多个pod空格隔开)

kubectl run mynginx --image=nginx

kubectl create deployment mytomcat --image=tomcat:8.5.68  
# 自愈能力,删除这个pod之后自动拉起一个新的pod
kubectl delete pod mytomcat-6f5f895f4f-rcb5m

# 这时删除需要,删除这次deploy
[root@k8s-master-001 ~]# kubectl delete deploy mytomcat
deployment.apps "mytomcat" deleted  
  • 资源限制
kubectl -n mytestset resources deployment my-dep--limits=cpu=200m,memory=512Mi  
  • 弹性伸缩,当cpu使用超过80%,
kubectl autoscale deployment nginx --min=4 --max=5 --cpu-percent=80 -n default  
  • 多副本
kubectl create deployment my-dep --image=nginx --replicas=3 -n mytest

[root@k8s-master-001 ~]# kubectl get deploy -n mytest
NAME     READY   UP-TO-DATE   AVAILABLE   AGE  
my-dep   3/3     3            3           58s
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:
    app: my-dep
  name: my-dep
  namespace: mytest
spec:  
  replicas: 3
  selector:
    matchLabels:
      app: my-dep
  template:
    metadata:
      labels:
        app: my-dep
    spec:
      containers:
      - image: nginx
        name: nginx
  • 扩缩容 通过命令观察变化watch -n 1 kubectl get pod -n mytest
# 副本增加到5个
kubectl scale --replicas=5 deployment/my-dep  
# 副本减少到3个
kubectl scale --replicas=3 deployment/my-dep  
# 编辑yaml配置进行缩容
kubectl edit deployment my-dep

#修改 replicas

自愈&故障转移

  • 停机
关宿主机模拟
  • 删除pod
docker stop xxx  
  • 容器崩溃

  • 容器崩溃

滚动更新

  • 查看deployment使用了什么镜像
kubectl get deploy my-dep -n mytest -oyaml  
kubectl set image deployment/my-dep nginx=nginx:1.16.1 --record  
kubectl rollout status deployment/my-dep  
# 修改 kubectl edit deployment/my-dep

版本回退

#历史记录
[root@k8s-master-001 ~]# kubectl rollout history deployment/my-dep -n mytest
deployment.apps/my-dep  
REVISION  CHANGE-CAUSE  
1         <none>  
2         kubectl set image deployment/my-dep nginx=nginx:1.16.1 --record=true --namespace=mytest

#查看某个历史详情
kubectl rollout history deployment/my-dep --revision=2

#回滚(回到上次)
[root@k8s-master-001 ~]# kubectl rollout undo deployment/my-dep -n mytest
deployment.apps/my-dep rolled back

#回滚(回到指定版本)
[root@k8s-master-001 ~]# kubectl rollout undo deployment/my-dep --to-revision=1 -n mytest
deployment.apps/my-dep rolled back  

其他工作负载

除了Deployment,k8s还有 StatefulSet 、DaemonSet 、Job 等 类型资源。我们都称为 工作负载。 有状态应用使用 StatefulSet 部署,无状态应用使用 Deployment 部署 https://kubernetes.io/zh/docs/concepts/workloads/controllers/

Deployment: 无状态应用部署,比如微服务,提供多副本等功能  
StatefulSet: 有状态应用部署,比如redis,提供稳定的存储、网络等功能  
DaemonSet: 守护型应用部署,比如日志收集组件,在每个机器都运行一份  
Job/Crontab: 定时任务部署,比如垃圾清理组件,可以在指定时间运行  

Service ClusterIP 模式(默认) 集群内部访问

将一组Pods公开为网络服务的抽象方法

# 暴露Deploy
kubectl expose deployment my-dep --port=8000 --target-port=80 --type=ClusterIP  
# 删除service
[root@k8s-master-001 ~]# kubectl delete service my-dep -n mytest
service "my-dep" deleted  
# 查看service暴露的IP和端口
[root@k8s-master-001 ~]# kubectl get service -n mytest
NAME     TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE  
my-dep   ClusterIP   10.96.156.29   <none>        8000/TCP   3m18s  
#使用标签检索Pod
kubectl get pod -l app=my-dep  
  • 创建一个新的pod测试访问svc ip和svc域名
kubectl create deploy my-tomcat --image=tomcat -n mytest  
# 查看pod name
kubectl get pod -n mytest  
# 进入pod测试
kubectl exec -it  my-tomcat-b4c9b6565-xbll7  -n mytest -- /bin/bash  
# 使用IP和域名都可以访问
root@my-tomcat-b4c9b6565-xbll7:/usr/local/tomcat# curl 10.96.156.29:8000  
3333  
root@my-tomcat-b4c9b6565-xbll7:/usr/local/tomcat# curl 10.96.156.29:8000  
2222  
root@my-tomcat-b4c9b6565-xbll7:/usr/local/tomcat# curl 10.96.156.29:8000  
11111  
root@my-tomcat-b4c9b6565-xbll7:/usr/local/tomcat# curl my-dep.mytest.svc:8000  
2222  
root@my-tomcat-b4c9b6565-xbll7:/usr/local/tomcat# curl my-dep.mytest.svc:8000  
11111  
root@my-tomcat-b4c9b6565-xbll7:/usr/local/tomcat# curl my-dep.mytest.svc:8000  
2222

Service NodePort 模式(集群外也可以访问)

# 先删除刚刚创建的svc
[root@k8s-master-001 ~]# kubectl delete svc my-dep -n mytest

# 使用svc的nodeport创建一次部署
kubectl expose deployment my-dep --port=8000 --target-port=80 --type=NodePort -n mytest

# 查看集群IP
[root@k8s-master-001 ~]# kubectl get svc -n mytest
NAME     TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE  
my-dep   NodePort   10.96.118.198   <none>        8000:30451/TCP   110s  
service "my-dep" deleted

# 这时在浏览器中使用节点IP+30451也可以访问到集群

NodePort范围在 30000-32767 之间

Ingress

Service的统一网关入口

  • 安装
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml

#修改镜像
vi deploy.yaml  
#将image的值改为如下值:
registry.cn-guangzhou.aliyuncs.com/leoiceo_k8s_images/ingress-nginx-controller:v0.46.0

# 部署
kubectl apply -f deploy.yaml

# 检查安装的结果
[root@k8s-master-001 ~]# kubectl get pod,svc -n ingress-nginx
NAME                                            READY   STATUS      RESTARTS   AGE  
pod/ingress-nginx-admission-create-vgpnx        0/1     Completed   0          8m14s  
pod/ingress-nginx-admission-patch-frwc8         0/1     Completed   0          8m14s  
pod/ingress-nginx-controller-576cb8ff96-4rlmn   1/1     Running     0          8m14s

NAME                                         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE  
service/ingress-nginx-controller             NodePort    10.96.172.45   <none>        80:30637/TCP,443:31315/TCP   8m14s  
service/ingress-nginx-controller-admission   ClusterIP   10.96.71.227   <none>        443/TCP                      8m14s

# 最后别忘记把svc暴露的端口要放行
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  name: hello-server
spec:  
  replicas: 2
  selector:
    matchLabels:
      app: hello-server
  template:
    metadata:
      labels:
        app: hello-server
    spec:
      containers:
      - name: hello-server
        image: registry.cn-guangzhou.aliyuncs.com/leoiceo_k8s_images/hello-server
        ports:
        - containerPort: 9000
---
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:
    app: nginx-demo
  name: nginx-demo
spec:  
  replicas: 2
  selector:
    matchLabels:
      app: nginx-demo
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      containers:
      - image: nginx
        name: nginx
---
apiVersion: v1  
kind: Service  
metadata:  
  labels:
    app: nginx-demo
  name: nginx-demo
spec:  
  selector:
    app: nginx-demo
  ports:
  - port: 8000
    protocol: TCP
    targetPort: 80
---
apiVersion: v1  
kind: Service  
metadata:  
  labels:
    app: hello-server
  name: hello-server
spec:  
  selector:
    app: hello-server
  ports:
  - port: 8000
    protocol: TCP
    targetPort: 9000
  • 域名访问 编写一个yaml文件创建ingress规则 ingress-rule.yaml
apiVersion: networking.k8s.io/v1  
kind: Ingress  
metadata:  
  name: ingress-host-bar
spec:  
  ingressClassName: nginx
  rules:
  - host: "hello-k8s.imdst.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: hello-server
            port:
              number: 8000
  - host: "demo-k8s.imdst.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"  
        backend:
          service:
            name: nginx-demo  
            port:
              number: 8000
kubectl apply -f ingress-rule.yaml  
# 查看ingress域名
[root@k8s-master-001 ~]# kubectl get ingress
NAME               CLASS   HOSTS                                    ADDRESS       PORTS   AGE  
ingress-host-bar   nginx   hello-k8s.imdst.com,demo-k8s.imdst.com   10.3.100.67   80      2m55s  
# 查看ingress入口IP
[root@k8s-master-001 ~]#  kubectl get svc -n ingress-nginx
NAME                                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE  
ingress-nginx-controller             NodePort    10.96.172.45   <none>        80:30637/TCP,443:31315/TCP   30m  
ingress-nginx-controller-admission   ClusterIP   10.96.71.227   <none>        443/TCP                      30m

# 将域名绑定到hosts进行测试
echo "10.96.172.45 hello-k8s.imdst.com demo-k8s.imdst.com" >> /etc/hosts

# curl进行访问测试,也可以浏览器中访问,在本机绑定节点IP
[root@k8s-master-001 ~]# curl hello-k8s.imdst.com
Hello World!  
[root@k8s-master-001 ~]# curl demo-k8s.imdst.com
welcome to nginx  
  • 路径重写
#  由于上面已经创建了ingress规则,可以直接通过以下命令去编辑yaml即可以生效
kubectl edit ing ingress-host-bar

apiVersion: networking.k8s.io/v1  
kind: Ingress  
metadata:  
  annotations:                     ## 增加如下两行
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: ingress-host-bar
spec:  
  ingressClassName: nginx
  rules:
  - host: "hello-k8s.imdst.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: hello-server
            port:
              number: 8000
  - host: "demo-k8s.imdst.com"
    http:
      paths:
      - pathType: Prefix
        path: "/nginx(/|$)(.*)"  # 把请求会转给下面的服务,下面的服务一定要能处理这个路径,不能处理就是404
        backend:
          service:
            name: nginx-demo  ## java,比如使用路径重写,去掉前缀nginx
            port:
              number: 8000
#测试访问 
curl demo-k8s.imdst.com/nginx/  等同于访问 demo-k8s.imdst.com/  
curl demo-k8s.imdst.com/nginx/nginx/  等同于访问 demo-k8s.imdst.com/nginx/  
  • 流量限制
apiVersion: networking.k8s.io/v1  
kind: Ingress  
metadata:  
  name: ingress-limit-rate
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "1"
spec:  
  ingressClassName: nginx
  rules:
  - host: "haha-k8s.imdst.com"
    http:
      paths:
      - pathType: Exact
        path: "/"
        backend:
          service:
            name: nginx-demo
            port:
              number: 8000

存储抽象

  • 搭建一个nfs环境进行测试,如果有公有云环境,可以直接购买nfs服务挂载到k8s各个节点
    k8s-master : nfs主节点 /nfs/data
    k8s-slave-001 : nfs-client /nfs/data
    k8s-slave-002 : nfs-client /nfs/data
# 所有节点安装nfs-utils
yum install -y nfs-utils

#nfs主节点
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports  
mkdir -p /nfs/data  
systemctl enable rpcbind --now  
systemctl enable nfs-server --now  
#配置生效
exportfs -r


#从节点
showmount -e 10.3.100.66  
#执行以下命令挂载 nfs 服务器上的共享目录到本机路径 /root/nfsmount
mkdir -p /nfs/data  
mount -t nfs 10.3.100.66:/nfs/data /nfs/data  
# 写入一个测试文件
echo "hello nfs server" > /nfs/data/test.txt  
  • 原生方式挂载数据
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:
    app: nginx-pv-demo
  name: nginx-pv-demo
spec:  
  replicas: 2
  selector:
    matchLabels:
      app: nginx-pv-demo
  template:
    metadata:
      labels:
        app: nginx-pv-demo
    spec:
      containers:
      - image: nginx
        name: nginx
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
      volumes:
        - name: html
          nfs:
            server: 10.3.100.66
            path: /nfs/data/nginx-pv
# 提前创建挂载目录
mkdir -p /nfs/data/nginx-pv  
echo "this is test nginx-pv-demo pages" > /nfs/data/nginx-pv/index.html

kubectl apply -f nginx-pv-demo.yaml  
# 查看pod状态
kubectl get pod  
# 查看部署运行中的状态
kubectl describe pod nginx-pv-demo  
# 暴露Deploy
kubectl expose deployment nginx-pv-demo --port=8000 --target-port=80  
# 查看svc ip,进行测试
kubectl get svc  
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE  
nginx-pv-demo   ClusterIP   10.96.192.211   <none>        8000/TCP   3m34s  
# 多次测试发现都是返回相同的index页面
[root@k8s-master-001 ~]# curl 10.96.192.211:8000
this is test nginx-pv-demo pages  

PV&PVC

PV:持久卷(Persistent Volume),将应用需要持久化的数据保存到指定位置
PVC:持久卷申明(Persistent Volume Claim),申明需要使用的持久卷规格 * 创建pv池

#nfs主节点
mkdir -p /nfs/data/01  
mkdir -p /nfs/data/02  
mkdir -p /nfs/data/03  
  • 创建PV
apiVersion: v1  
kind: PersistentVolume  
metadata:  
  name: pv01-10m
spec:  
  capacity:
    storage: 10M
  accessModes:
    - ReadWriteMany
  storageClassName: nfs
  nfs:
    path: /nfs/data/01
    server: 10.3.100.66
---
apiVersion: v1  
kind: PersistentVolume  
metadata:  
  name: pv02-1gi
spec:  
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  storageClassName: nfs
  nfs:
    path: /nfs/data/02
    server: 10.3.100.66
---
apiVersion: v1  
kind: PersistentVolume  
metadata:  
  name: pv03-3gi
spec:  
  capacity:
    storage: 3Gi
  accessModes:
    - ReadWriteMany
  storageClassName: nfs
  nfs:
    path: /nfs/data/03
    server: 10.3.100.66
# 创建PV
[root@k8s-master-001 ~]# kubectl apply -f pv.yaml 
persistentvolume/pv01-10m created  
# 查看已经创建的PV
[root@k8s-master-001 ~]# kubectl get pv
NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE  
pv01-10m   10M        RWX            Retain           Available           nfs                     34s  
pv02-1gi   1Gi        RWX            Retain           Available           nfs                     34s  
pv03-3gi   3Gi        RWX            Retain           Available           nfs                     34s  
  • PVC创建与PV绑定
kind: PersistentVolumeClaim  
apiVersion: v1  
metadata:  
  name: nginx-pvc
spec:  
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 200Mi
  storageClassName: nfs
[root@k8s-master-001 ~]# kubectl apply -f pvc.yaml 
persistentvolumeclaim/nginx-pvc created  
[root@k8s-master-001 ~]# kubectl get pvc
NAME        STATUS   VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS   AGE  
nginx-pvc   Bound    pv02-1gi   1Gi        RWX            nfs            5s  
[root@k8s-master-001 ~]# 

创建Pod绑定PVC

apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:
    app: nginx-deploy-pvc
  name: nginx-deploy-pvc
spec:  
  replicas: 2
  selector:
    matchLabels:
      app: nginx-deploy-pvc
  template:
    metadata:
      labels:
        app: nginx-deploy-pvc
    spec:
      containers:
      - image: nginx
        name: nginx
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
      volumes:
        - name: html
          persistentVolumeClaim:
            claimName: nginx-pvc
[root@k8s-master-001 ~]# kubectl apply -f nginx-pvc.yaml 
deployment.apps/nginx-deploy-pvc created  
[root@k8s-master-001 ~]# kubectl get pvc
NAME        STATUS   VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS   AGE  
nginx-pvc   Bound    pv02-1gi   1Gi        RWX            nfs            2m38s  
# 这时发现 pv02-1gi 已经被绑定了
[root@k8s-master-001 ~]# kubectl get pv
NAME       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM               STORAGECLASS   REASON   AGE  
pv01-10m   10M        RWX            Retain           Available                       nfs                     8m2s  
pv02-1gi   1Gi        RWX            Retain           Bound       default/nginx-pvc   nfs                     8m2s  
pv03-3gi   3Gi        RWX            Retain           Available                       nfs                     8m2s  

ConfigMap

抽取应用配置,并且可以自动更新 * 测试配置redis.conf

appendonly yes  
  • 创建配置集
# 创建配置,redis保存到k8s的etcd;
[root@k8s-master-001 ~]# kubectl create cm redis-conf --from-file=redis.conf
configmap/redis-conf created  
[root@k8s-master-001 ~]# kubectl get cm
NAME               DATA   AGE  
kube-root-ca.crt   1      8d  
redis-conf         1      5s  
# 查看配置集
[root@k8s-master-001 ~]# kubectl get cm redis-conf -oyaml
apiVersion: v1  
data:   # data是真正的数据,key:默认是是文件名 value: 配置文件内容  
  redis.conf: |
    appendonly yes
kind: ConfigMap  
metadata:  
  name: redis-conf
  namespace: default
  • 创建pod
apiVersion: v1  
kind: Pod  
metadata:  
  name: redis
spec:  
  containers:
  - name: redis
    image: redis
    command:
      - redis-server
      - "/redis-master/redis.conf"  #指的是redis容器内部的位置
    ports:
    - containerPort: 6379
    volumeMounts:
    - mountPath: /data
      name: data
    - mountPath: /redis-master
      name: config
  volumes:
    - name: data
      emptyDir: {}
    - name: config
      configMap:
        name: redis-conf
        items:
        - key: redis.conf
          path: redis.conf
[root@k8s-master-001 ~]# kubectl apply -f redis-pod.yaml 
pod/redis created  
# 等待redis运行
[root@k8s-master-001 ~]# kubectl get pod
NAME                                READY   STATUS              RESTARTS   AGE  
redis                               0/1     ContainerCreating   0          1s  
# 进入容器查看配置是否正常加载
[root@k8s-master-001 ~]# kubectl exec -it redis -- /bin/bash
root@redis:/data# cd /redis-master/  
root@redis:/redis-master# ls  
redis.conf  
root@redis:/redis-master# cat redis.conf  
appendonly yes  
# 退出去修改配置
[root@k8s-master-001 ~]# kubectl get cm
NAME               DATA   AGE  
kube-root-ca.crt   1      8d  
redis-conf         1      13m  
[root@k8s-master-001 ~]# kubectl edit cm redis-conf
configmap/redis-conf edited  
给Redis设置一个密码
requirepass 123456

# 再次登陆容器查看配置变化 
kubectl exec -it redis -- redis-cli  
127.0.0.1:6379> CONFIG GET appendonly  
127.0.0.1:6379> CONFIG GET requirepass  # 需要重启容器才会生效  

检查指定文件内容是否已经更新
修改了CM。Pod里面的配置文件会跟着变

配置值未更改,因为需要重新启动 Pod 才能从关联的 ConfigMap 中获取更新的值。
原因:我们的Pod部署的中间件自己本身没有热更新能力

Secret

Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。 将这些信息放在 secret 中比放在 Pod 的定义或者 容器镜像 中来说更加安全和灵活。

  • 命令格式
kubectl create secret docker-registry leoiceo-secret \  
  --docker-server=<你的镜像仓库服务器> \
  --docker-username=<你的用户名> \
  --docker-password=<你的密码> \
  --docker-email=<你的邮箱地址>
apiVersion: v1  
kind: Pod  
metadata:  
  name: private-php-fpm
spec:  
  containers:
  - name: private-php-fpm
    image: registry.cn-guangzhou.aliyuncs.com/leoiceo/php-fpm:7.4
  imagePullSecrets:
  - name: leoiceo-secret