用了几个月普罗米修斯了,回头再来补一篇博客(笑
参考:https://blog.csdn.net/cp3_zyh/article/details/124019043

简介

谷歌出品,基于Go编写,容器时代版本的答案!

特点:

  • 由指标名+键值对的标签标识的时序数据和转为监控而生的时序数据库TSDB
  • promQL
  • 自动发现
  • 成熟的容器化支持
  • 独立部署,无需agent,但是想要获得一些针对性的监控需要对应的exporter
  • 基于HTTP采集数据
  • 秒级采集精度
  • 基于数学模型计算,能够实现复杂的监控逻辑(如计算QPS相关的指标等)
  • 数据恢复机制


prometheus server可以独立部署,采集目标的数据并存储在tsdb中,自身对外提供promQL,计算告警规则并把触发告警的推送给altermanager
exporter可以帮助普罗米修斯采集数据,目前市面上常见的软件均已有现成的exporter,如mysql\nginx等
pushgateway主要用于针对一些短期存在的job,这些job可以直接向其push metrics,prometheus server周期性过去取数据就行
altermanager从server获取到alter之后,对其进行去重分组后路由至正确的告警方式,支持多种告警格式
serverdiscovery负责动态发现监控对象

普罗米修斯的TS数据库以每两小时为间隔来分block存储,每一个块又分chunk存储,chunk用于存储采集过来的ts数据、index、metadata,index是对metrics和labels进行索引后对存储。后台还会把小block合并成大block以减少内存里block的数量

普罗米修斯使用WAL机制来保护未落盘的数据不会因为突然宕机而丢失,WAL被分割成默认大小为128M的文件段(segment)文件段以数字命名,长度为8位的整形。WAL的写入单位是页(page),每页的大小为32KB,所以每个段大小必须是页的大小的整数倍。如果WAL一次性写入的页数超过一个段的空闲页数,就会创建一个新的文件段来保存这些页,从而确保一次性写入的页不会跨段存储。当出现宕机,等到机器恢复之后,普罗米修斯会把这些数据恢复进内存

Prometheus的数据由指标名称metric、一组键值对、时间戳、指标值组成,键值对可以在配置文件里面自定义,方便后面根据其来用promQL进行数据的分组、筛选等工作。
指标表达方式

<metric name>{<label name>=<label value>, ...} 

四种数据类型

Counter
Counter是计数器类型

  • Counter 用于累计值,例如记录请求次数、任务完成数、错误发生次数。
  • 一直增加,不会减少。
  • 重启进程后,会被重置。

Gague
Gauge是测量器类型

  • Gauge是常规数值,例如温度变化、内存使用变化。
  • 可变大,可变小。
  • 重启进程后,会被重置

Histogram
histogram是直方图,在Prometheus系统的查询语言中,有三种作用:
1)在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中. 后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。
2)对每个采样点值累计和(sum)
3)对采样点的次数累计和(count)
度量指标名称: [basename]_上面三类的作用度量指标名称_
举例:
对一批访问,分别列出响应时间在0~0.5,0.5~1,1+的数量

[basename]bucket{le="上边界"}, 这个值为小于等于上边界的所有采样点数量
[basename]_sum_
[basename]_count

Summary
Summary即概率图,类似于Histogram,常用于跟踪与时间相关的数据。典型的应用包括请求持续时
间、响应大小等。Summary同样提供样本的count和sum功能;还提供quantiles功能,可以按百分比划
分跟踪结果,例如,quantile取值0.95,表示取样本里的95%数据。Histogram需要计算,summary直接存储

安装与入门示例

https://prometheus.io/download/ 挑版本下载
解压
cp -rf prometheus-2.38.0.linux-amd64 /usr/local/prometheus
./prometheus &运行,默认运行在9090端口
但是注意这种方式启动会使得你丢失普罗米修斯的日志!!你可以配置个systemd或者用nohup来指定输出日志

[root@master prometheus]# ss -tnlp | grep 9090
LISTEN     0      128       [::]:9090                  [::]:*                   users:(("prometheus",pid=373,fd=8))

nohup ./prometheus > /var/log/prom.log 2>&1  & #nohup方式,把输出重定向到了我指定的日志文件里面

普罗米修斯本身还带有一些启动的参数

--config.file=”prometheus.yml” 指定配置文件
-–storage.tsdb.path=”data/”    指标存储的基本路径。
--storage.tsdb.retention=15d 指定保存的数据的时间

当然,因为这里只是演示,我就不加参数了,实际生产环境按需设置

访问端口就可以访问了,阿里云的服务器如果发现访问不了的话,先去安全组里面放行9090端口

关于普罗米修斯的访问控制可以看

https://blog.csdn.net/qq_31725371/article/details/114978760


image.png

prometheus的配置文件是prometheus.yml,当然你也可以用--yml来指定配置文件

# my global config
global:
  scrape_interval: 15s # 采集间隔Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # 每隔多久执行一次监控规则Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs: #这里开始就是监控项的配置了
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"] #监控对象

这里我们把targets改成targets: ["localhost:9090", "node1:9100"]试试看(node1是另外一台服务器,已经配置了hosts,9100一般是node_exporter的端口
当然,这里得提前在机器上装好node_exporter并启动,安装方式同上,官网下载运行就行,当然,记得放行9100端口
image.png
以cpu使用率为例子

image.png

查询node_cpu_seconds_total,我们得到了各项cpu使用时间数据,counter类型的
image.png
通过cpu占用率的原理可以得知,cpu的使用率=cpu除空闲时间以外的总时间/总时间
所以这里应该这么写
(1-((sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by (instance)) / (sum(increase(node_cpu_seconds_total[1m])) by (instance)))) * 100
image.png
image.png
这个指标是个counter类型,累计的,{}取了带标签为mode="idle"的数据,increase()可以取一段时间内的增长量,因为一台云服务器一般不止1核,会查出多条cpu数据,单核单核看没多少意义,所以用sum进行总和。by instance是按实例拆分数据,如果是集群多机器的时候可以根据主机来区分数据分开展示。[1m]是就取一分钟内的数据,对于cpu这种实时性很强的指标取太长意义不大
((sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by (instance)) 分解⬇️
node_cpu_seconds_total{mode="idle"}[1m]
increase(node_cpu_seconds_total{mode="idle"}[1m])
sum(increase(node_cpu_seconds_total{mode="idle"}[1m]))
(sum(increase(node_cpu_seconds_total{mode="idle"}[1m])) by (instance)

promQL

https://blog.csdn.net/fu_huo_1993/article/details/114482586
这篇比较全,以下是部分

匹配器

使用{}可以通过标签来筛选数据

=: 精确的匹配给定的标签值
!=: 不匹配给定的标签值
=~: 正则表达式匹配给定的标签值
!~: 不匹配正则表格式给定的标签值
=~和!= 必须要指定一个标签名字或者匹配的标签必须要有值

例子:

查询出指标名称up中标签job等于prometheus的时间序列数据
up{job=“prometheus”}
查询出指标名称up中标签job以pro开头的的时间序列数据
up{job=~“pro.*”}
查询出指标名称up中不存在标签env的时间序列数据

时间区间

up[5m] 表示查询5分钟时间内各实例的状态
ms(毫秒)、s(秒)、m(分钟)、h(小时)、d(天)、w(周)、y(年)

d 表示一天总是24小时
w 表示一周总是7天
y 表示一年总是365天
可以将多个不同的时间单位串联起来使用,但是大的时间单位必须在前面,小的时间单位在后面,且不同的时间单位只能出现一次。且不能是小数。
eg: 1h30m可以,但是1.5h则不才可以。

偏移量

表示获取指标名称prometheus_http_requests_total的所有时间序列在距离此刻1分钟之前的5分钟之内的样本。

prometheus_http_requests_total[5m] offset 1m

运算符

+(加)、-(减)、*(乘)、/(除)、%(取模)、^(幂等)
== (等于)、!=(不等于)、>(大于)、<(小于)、>=(大于或等于)、<=(小于或等于)
and(并且-交集)、or(或者-并集)、unless(除非-补集)

如果存在bool修饰符,则结果只返回0,1.如果不使用bool修饰符,那么返回满足结果的个数
image.png
image.png

匹配

两个即时向量之间进行计算,需要拥有一样的标签才能计算,使用匹配关键字可以帮助我们达成计算目的
向量之间的运算尝试为左侧的每个条目在右侧向量中找到匹配的元素

ignoring:允许在匹配时忽略某些标签。

on:允许在匹配时只匹配指定的标签。

例:
method_code:http_errors:rate5m{method="get", code="500"}  24   和
method:http_requests:rate5m{method="get"}  600

这两个拥有共同的标签method,但是前者多一个code,正常来讲是无法计算的

method_code:http_errors:rate5m{code="500"} / ignoring(code) method:http_requests:rate5m
这种写法让我们忽视前者的code标签,这样两者拥有的标签就一样了
一对多和多对一:“一”的一侧的每个向量元素可以与“多”侧的多个元素进行匹配。必须使用group_left或group_right修饰符显式请求,其中left/right确定哪个向量具有更高的基数

例
method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m

内置函数

by 从计算结果从保留该标签、移除其他标签来分组,具体见上面的入门例子
without:与by类似,移除该标签保留其他标签
abs():绝对值
floor():粘贴并匹配样式
increase():返回区间范围内值的增长量,counter
rate():平均增长速率,区间内counter增长量/时间
irate():取最后两个值计算瞬间增长率
sum():合计
topk(n,metric):选出最高的几个值,counter,gague
cout():统计结果的条数

常用组件

https://prometheus.io/download/ 官方组件的下载地址

node_exporter

主机监控

push_gateway

因为pushgateway是被动获取数据,运行起来后取普罗米修斯里面单独给他加一个

  - job_name: "pushgateway"
    static_configs:
      - targets: ["localhost:9091"]

pushgateway可以自己写一些小脚本跑在机器上,定期给普罗米修斯推送

blackbox

端口监测,只能检活

grafana

https://grafana.com/grafana/download/9.1.2
service grafana-server start
以这种方式启动的grafana会使用默认的配置文件和日志文件
/etc/grafana/grafana.ini
/var/log/grafana/grafana.log

访问3000端口
image.png
配置数据源,然后建立dashboard或者先建立folder再建立dashboard,建议先建文件夹,grafana有完善的权限控制,后续可以把一个应用的监控面板放在一个文件夹里面,然后把相关的人员放进一个team里面,赋权的时候直接给team赋予权利就行了

容器化方案+自动发现+dashboard

prometheus有docker镜像,可以直接拉取
除此以外还有另外一套通过yaml来部署的项目,或者自己自定义文件来安装(如通过helm等
https://github.com/prometheus-operator/kube-prometheus

这里介绍通过helm编排容器,以及基于文件的自动发现,以下三篇为参考教程
https://yunlzheng.gitbook.io/prometheus-book/part-ii-prometheus-jin-jie/sd/service-discovery-with-file
https://cloud.tencent.com/developer/article/1885193
https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247495887&idx=1&sn=beddcaebaf6738a2d9f69bf5c35a348c&chksm=fdbaffd2cacd76c41e796065bc11ed608b404587c371281c3f3e3df4b65b81fc26178cbcf310&scene=21#wechat_redirect
docker pull prom/prometheus
docker pull grafana/grafana
在node机器上准备好prometheus.yaml,挂载目录建议提前创建好然后赋权赋高点,不然会出现权限问题导致应用创建失败

global:
  scrape_interval: 15s
  scrape_timeout: 10s
  evaluation_interval: 15s
scrape_configs:
- job_name: file_ds
  honor_timestamps: true
  scrape_interval: 15s
  scrape_timeout: 10s
  metrics_path: /metrics
  scheme: http
  follow_redirects: true
  file_sd_configs:
  - files:
    - /var/file-sd.json
    refresh_interval: 1m

helm编写,具体请见http://shangxizhuan.site/archives/411/例子部分

配置完后,尝试访问服务,成功,如果是阿里云之类的记得先放行端口
image.png

现在我们来测试一下自动发现功能正不正常,在一台机子上运行期node_exporter,然后去cm里面修改

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-file-sd
  namespace: {{ .Values.namespace }}
  labels:
    app: promtest
data:
  file-sd.json: |
    [
      {
      "targets": [ "localhost:9090" ],
      "labels": {
        "env": "prod",
        "job": "local"
      }
    },
    {
      "targets": [ "server2:9100"],
      "labels": {
        "env": "ser2",
        "job": "node"
      }
    }
    ]

等个一分钟左右就看到新的节点已经被自动加载上来了
image.png
这里节点挂了的原因并不是node_exporter,而是这个exporter是单点的,不是集群里的pod,没做路由连接不上

dashboard部分,先拉取官方的recommand.yaml,按需修改
https://github.com/kubernetes/dashboard

http://www.live-in.org/archives/3320.html
如果发现pod起不来,试试安装在master节点,修改下面两处

    spec:
      nodeName: master节点的名字
      containers:
        - name: kubernetes-dashboard
          image: kubernetesui/dashboard:v2.0.4
          imagePullPolicy: Always


    spec:
      nodeName: master节点的名字
      containers:
        - name: dashboard-metrics-scraper
          image: kubernetesui/metrics-scraper:v1.0.4

443端口相关报错,删除yaml里面检活部分

如果进去发现列不出pod,试试添加角色

https://stackoverflow.com/questions/58719006/kubernetes-dashboard-error-using-service-account-token
https://stackoverflow.com/questions/46664104/how-to-sign-in-kubernetes-dashboard
kubectl delete clusterrolebinding kubernetes-dashboard kubectl create clusterrolebinding kubernetes-dashboard --clusterrole=cluster-admin --serviceaccount=kube-system:kubernetes-dashboard
kubectl create clusterrolebinding deployment-controller --clusterrole=cluster-admin --serviceaccount=kube-system:deployment-controller
获取token
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | awk '/^deployment-controller-token-/{print $1}') | awk '$1=="token:"{print $2}'

访问url,如果发现打不开,使用https再试试看,出现报错按照上面的方案处理

image.png

其他及缺陷

上面给的只是基础的容器化方案,大集群肯定会涉及到高可用的问题,对于这个,比起官方的联邦方案,我更推荐thanos

对存储机制和联邦集群方案的分析在这

普罗米修斯毋庸置疑是容器化环境下监控方案的旗帜,falcon虽好,但是不太支持容器,至于zabbix......什么年代了,还在用传统监控(笑

prometheus本身也是有点点缺陷的,当数据量很大的时候,启动会比较慢,因为其WAL机制,虽然保障了数据恢复机制,但是也使得其在启动的时候必须把还没落盘的数据都加载到内存里面,而且还是一块一块加载(虽然这边给官方提过意见,但是官方表示改不了,就这样)

关于数据采集,除了官方的和社区的那些exporter,一般公司肯定有自己开发的业务,这种一般得自己开发exporter,或者,java应用可以用micrometer打点,毕竟普罗米修斯他跟别的监控不太一样,采集哪些指标、指标怎么进行计算,除了通用的一些,这些只有开发者自己最清楚

https://github.com/micrometer-metrics/micrometer

deployment

以之前写的一个go微服务镜像为例子

https://k8s.easydoc.net/docs/dRiQjyTY/28366845/6GiNOzyZ/3iQiyInr

https://blog.csdn.net/m0_54024707/article/details/122225607 可作为参考,或者运行kubectl explain pod

image.png

  • 这里不知为啥直接拉镜像拉不下来,于是我把镜像在每个node上都拉了一份

    apiVersion: apps/v1 #kubectl api-versions
    kind: Deployment
    metadata: 
    name: go-microserver #部署的服务的名字
    spec: #详细定义
    replicas: 2 #副本数
    #使用标签来查找关联的pod
    selector: 
      matchLabels: 
        app: go-server-test
    #创建pod使用的模版
    template: 
      metadata: 
        labels: 
          app: go-server-test #这里必须和上面标签选择器相同,不然会找不到
      # 定义容器
      spec:
        containers: 
        - name: "go-service"
          image: registry-vpc.cn-hangzhou.aliyuncs.com/tokugawa/micro-go
          ports: 
          - name: httpd  #名称
            containerPort: 8080 #容器内部服务真正的端口,targetport映射在这个端口上
            protocol: TCP        
            # port: 8081 # 集群内部访问pod
            # nodePort: 8082 #集群外部客户端访问pod
            # targetPort: 8080 #pod的端口,从port和nodePort来的流量经过kube-proxy流入到后端pod的targetPort上,最后进入容器
     
            
    [root@master ~]# kubectl get pods -o wide
    NAME                             READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
    go-microserver-8b87c8767-59t8t   1/1     Running   0          43s   10.244.0.16   node1   <none>           <none>
    go-microserver-8b87c8767-9pxkf   1/1     Running   0          43s   10.244.0.16   node2   <none>           <none>
    
    [root@master ~]# kubectl describe pod go-microserver-8b87c8767-kbk9n
    Name:         go-microserver-8b87c8767-kbk9n
    Namespace:    default
    Priority:     0
    Node:         node1/172.29.36.123
    Start Time:   Tue, 30 Aug 2022 15:00:55 +0800
    Labels:       app=go-server-test
                pod-template-hash=8b87c8767
    Annotations:  <none>
    Status:       Running
    IP:           10.244.0.14
    IPs:
    IP:           10.244.0.14
    Controlled By:  ReplicaSet/go-microserver-8b87c8767
    Containers:
    go-service:
      Container ID:   docker://7dc5da32e70e0e2453b9cbe36a13ed6947d35884a093df554ad4ddb0b617a607
      Image:          registry.cn-hangzhou.aliyuncs.com/tokugawa/micro-go:v1
      Image ID:       docker-pullable://registry.cn-hangzhou.aliyuncs.com/tokugawa/micro-go@sha256:04fbd8859168bf7e3321e1fcb574bc5b6d41f8c2915094e268cf45abb77f2515
      Port:           8080/TCP
      Host Port:      0/TCP
      State:          Running
        Started:      Tue, 30 Aug 2022 15:00:57 +0800
      Ready:          True
      Restart Count:  0
      Environment:    <none>
      Mounts:
        /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-qnk7x (ro)
    Conditions:
    Type              Status
    Initialized       True 
    Ready             True 
    ContainersReady   True 
    PodScheduled      True 
    Volumes:
    kube-api-access-qnk7x:
      Type:                    Projected (a volume that contains injected data from multiple sources)
      TokenExpirationSeconds:  3607
      ConfigMapName:           kube-root-ca.crt
      ConfigMapOptional:       <nil>
      DownwardAPI:             true
    QoS Class:                   BestEffort
    Node-Selectors:              <none>
    Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                               node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
    Events:
    Type    Reason     Age   From               Message
    ----    ------     ----  ----               -------
    Normal  Scheduled  100s  default-scheduler  Successfully assigned default/go-microserver-8b87c8767-kbk9n to node1
    Normal  Pulled     98s   kubelet            Container image "registry.cn-hangzhou.aliyuncs.com/tokugawa/micro-go:v1" already present on machine
    Normal  Created    98s   kubelet            Created container go-service
    Normal  Started    98s   kubelet            Started container go-service

    进入pod内部

    kubectl  exec -ti podname -- /bin/sh
    
    # pwd
    /go/src/mircoservice-1
    # ps -ef | grep example
    root         1     0  0 07:13 ?        00:00:00 /bin/sh -c ${SOURCES}example
    root         7     1  0 07:13 ?        00:00:00 /go/src/mircoservice-1/example
    root        18    11  0 07:16 pts/0    00:00:00 grep example
    # curl 127.0.0.1:8080
    <h1>hello!</h1></br>
    <h2>here is the main website!</h2>
    <b>enjoy your day!</b></br>

    扩容

    kubectl scale deployment go-microserver  --replicas=3
    deployment.apps/go-microserver scaled
    
    [root@master ~]# kubectl get pods
    NAME                              READY   STATUS    RESTARTS   AGE
    go-microserver-7d47df97fd-d25wg   1/1     Running   0          27m
    go-microserver-7d47df97fd-gmzj2   0/1     Pending   0          40s
    go-microserver-7d47df97fd-s849v   1/1     Running   0          27m

    使用指令端口映射,但是只对本地localhost网卡有效

    kubectl port-forward go-microserver-8b87c8767-6j77l 8080:8080
    
    [root@master ~]# curl localhost:8080
    <h1>hello!</h1></br>
    <h2>here is the main website!</h2>
    <b>enjoy your day!</b></br>

    查看日志

    [root@master ~]# kubectl logs pod/go-microserver-8b87c8767-6j77l
    [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
    
    [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
     - using env:   export GIN_MODE=release
     - using code:  gin.SetMode(gin.ReleaseMode)
    
    [GIN-debug] GET    /ping                     --> main.main.func1 (3 handlers)
    [GIN-debug] GET    /hello                    --> main.main.func2 (3 handlers)
    [GIN-debug] Loaded HTML Templates (2): 
          - 
          - index.html
    
    [GIN-debug] GET    /ico                      --> github.com/gin-gonic/gin.(*RouterGroup).StaticFile.func1 (3 handlers)
    [GIN-debug] HEAD   /ico                      --> github.com/gin-gonic/gin.(*RouterGroup).StaticFile.func1 (3 handlers)
    [GIN-debug] GET    /                         --> main.main.func3 (3 handlers)
    [GIN-debug] GET    /api/books                --> main.main.func4 (3 handlers)
    [GIN-debug] GET    /api/books/:isbn          --> main.main.func5 (3 handlers)
    [GIN-debug] PUT    /api/books/:isbn          --> main.main.func6 (3 handlers)
    [GIN-debug] POST   /api/books                --> main.main.func7 (3 handlers)
    [GIN-debug] DELETE /api/books/:isbn          --> main.main.func8 (3 handlers)
    [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
    Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
    [GIN-debug] Listening and serving HTTP on :8080
    [GIN] 2022/08/30 - 08:04:14 | 200 |     267.116µs |       127.0.0.1 | GET      "/"

    查看历史版本

    kubectl rollout history deployment go-microserver

service

上面起的实例如果想要访问里面的服务还得单独做端口映射,并且只在本机有效,也没办法做到负载均衡,很麻烦,如果我们想要在外部也能够访问到pod里面的服务,并且实现流量转发负载均衡等功能的话,我们可以使用service
service通过标签对应pod,生命周期不与pod绑定,提供负载均衡作用,对外提供访问的端口,对内使用名字可以直接访问,其他具体看前一篇博客

NodePort

apiVersion: v1
kind: Service
metadata:
  name: go-service
spec: #详细定义
  selector: 
    app: go-server-test
  type: NodePort
  ports: 
    - port: 8080  #可以配置多端口
      targetPort: 8080 # 容器端口
      nodePort: 31111 #节点端口  

部署后查看

[root@master ~]# kubectl get svc 
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
go-service   NodePort    10.101.79.120   <none>        8080:31111/TCP   5m34s
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP          17h

从另外一个节点用vip 8080访问,如果是外部机器访问则走31111,如果是云服务器,这里要记得放行端口

[root@node1 ~]# curl 10.101.79.120:8080
<h1>hello!</h1></br>
<h2>here is the main website!</h2>

statefulset

statefulset用于管理有状态的pod,例如数据库一类。pod的创建是顺序的,销毁是逆序的,如果被销毁重建,pod的名字不会改变,但是ip会变,所以使用名字而不要使用ip去连接。如果使用service名字去连接会随机连接上一个,要指定pod的话得用pod-name.service-name

这里以mysql(mariadb)官网镜像为例子

apiVersion: apps/v1
kind: StatefulSet
metadata: 
  name: mariadb
spec: 
  serviceName: mariadb
  replicas: 3
  selector: 
    matchLabels: 
      app: mariadb
  template: 
    metadata: 
      labels: 
        app: mariadb
    spec: 
      containers: 
      - name: mariadb
        image: mariadb:latest
        imagePullPolicy: IfNotPresent #只有当本地不存在镜像才从远处拉取
        env: 
        - name: MARIADB_ROOT_PASSWORD
          value: "123456"  #设置环境变量,这里必须至少得从三个里面选一个初始化:MARIADB_ROOT_PASSWORD, MARIADB_ALLOW_EMPTY_ROOT_PASSWORD and MARIADB_RANDOM_ROOT_PASSWORD

---

apiVersion: v1
kind: Service
metadata: 
  name: mariadb
spec: 
  selector: 
    app: mariadb
  type: ClusterIP #默认类型,自动分配一个仅 cluster (集群)内部可以访问的虚拟 IP
  clusterIP: None #headless
  ports: 
  - port: 3306
    targetPort: 3306

statefulset名字不再是随机的,这里因为做了个headless,所以clusterip没有ip

kubectl get pods -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
go-microserver-74c85dd7f-8kzlh   1/1     Running   0          28h   10.244.0.21   node2   <none>           <none>
go-microserver-74c85dd7f-jlt4h   1/1     Running   0          28h   10.244.0.20   node1   <none>           <none>
go-microserver-74c85dd7f-m6wzl   1/1     Running   0          28h   10.244.0.20   node2   <none>           <none>
go-microserver-74c85dd7f-zntnr   1/1     Running   0          28h   10.244.0.21   node1   <none>           <none>
mariadb-0                        1/1     Running   0          17m   10.244.0.23   node1   <none>           <none>
mariadb-1                        1/1     Running   0          17m   10.244.0.23   node2   <none>           <none>
mariadb-2                        1/1     Running   0          17m   10.244.0.24   node1   <none>           <none>

[root@master ~]# kubectl get statefulset
NAME      READY   AGE
mariadb   3/3     7m23s

[root@master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
go-service   NodePort    10.101.79.120   <none>        8080:31111/TCP   28h
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP          45h
mariadb      ClusterIP   None            <none>        3306/TCP         8m15s

[root@master ~]# kubectl get endpoints
NAME         ENDPOINTS                                                        AGE
go-service   10.244.0.20:8080,10.244.0.20:8080,10.244.0.21:8080 + 1 more...   28h
kubernetes   172.20.245.132:6443                                              45h
mariadb      10.244.0.23:3306,10.244.0.23:3306,10.244.0.24:3306               21m

这里登进去功能正常,但是连不通,换了nodeport用另外一台机器试一下连接发现被拒绝了,突然想起mariadb可能是初始化设置默认拒绝远程连接.....这里就不做演示了,多个副本可以配个主从,弄了端口映射后访问其中一个就行

数据持久化

statefulset的pod重建后里面的内容会丢失,如果想要数据持久化,我们可以挂载个盘上去,可以是本地磁盘,也可以是云厂商提供的相关服务,或者NFS之类的
如果是挂载在本地的目录,得要指定pod在那台机子上运行才行,这里以本地挂载的一种方式为例子

hostpath:使用本地目录
mariadb默认存储路径/var/lib/mysql/

apiVersion: apps/v1
kind: StatefulSet
metadata: 
  name: mariadb
spec: 
  serviceName: mariadb
  replicas: 1
  selector: 
    matchLabels: 
      app: mariadb
  template: 
    metadata: 
      labels: 
        app: mariadb
    spec: 
      containers: 
      - name: mariadb
        image: mariadb:latest
        imagePullPolicy: IfNotPresent #只有当本地不存在镜像才从远处拉取
        env: 
        - name: MARIADB_ROOT_PASSWORD
          value: "123456"
        volumeMounts: 
          - mountPath: /var/lib/mysql/ #容器里面的挂载路径
            name: mysqldata #卷的名字,必须和下面一样 
      volumes: 
      - name: mysqldata
        hostPath: 
          path: /data01/mysql #node上的挂载点
          type: DirectoryOrCreate     # 指向一个目录,不存在时自动创建


创建完去node1上查看,发现已经有文件了

[root@node1 ~]# ll /data01/mysql/
total 123328
-rw-rw---- 1 polkitd input    417792 Aug 31 22:55 aria_log.00000001
-rw-rw---- 1 polkitd input        52 Aug 31 22:55 aria_log_control
-rw-rw---- 1 polkitd input         9 Aug 31 22:55 ddl_recovery.log
-rw-rw---- 1 polkitd input       946 Aug 31 22:55 ib_buffer_pool
-rw-rw---- 1 polkitd input  12582912 Aug 31 22:55 ibdata1
-rw-rw---- 1 polkitd input 100663296 Aug 31 22:55 ib_logfile0
-rw-rw---- 1 polkitd input  12582912 Aug 31 22:55 ibtmp1
-rw-rw---- 1 polkitd input         0 Aug 31 22:55 multi-master.info
drwx------ 2 polkitd input      4096 Aug 31 22:55 mysql
drwx------ 2 polkitd input      4096 Aug 31 22:55 performance_schema
drwx------ 2 polkitd input     12288 Aug 31 22:55 sys

进pod创建个库表,塞点数据,发现node出现了对应的新文件

[root@master ~]# kubectl exec -it mariadb-0 -- /bin/bash
root@mariadb-0:/# mysql -uroot -p123456
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.6.5-MariaDB-1:10.6.5+maria~focal mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> create database db1 charset utf8;
Query OK, 1 row affected (0.000 sec)

MariaDB [(none)]> use db1
Database changed
MariaDB [db1]> create table tb1(name char(10),age int(3));
Query OK, 0 rows affected (0.007 sec)

MariaDB [db1]> insert into tb1 values("zhangsan",14),("lisi",15);
Query OK, 2 rows affected (0.001 sec)
Records: 2  Duplicates: 0  Warnings: 0

MariaDB [db1]> select * from tb1;
+----------+------+
| name     | age  |
+----------+------+
| zhangsan |   14 |
| lisi     |   15 |
+----------+------+
2 rows in set (0.000 sec)





[root@node1 ~]# ll /data01/mysql/
total 123340
-rw-rw---- 1 polkitd input    417792 Aug 31 22:55 aria_log.00000001
-rw-rw---- 1 polkitd input        52 Aug 31 22:55 aria_log_control
drwx------ 2 polkitd input      4096 Aug 31 23:11 db1
-rw-rw---- 1 polkitd input     12288 Aug 31 23:11 ddl_recovery.log
-rw-rw---- 1 polkitd input       946 Aug 31 22:55 ib_buffer_pool
-rw-rw---- 1 polkitd input  12582912 Aug 31 22:55 ibdata1
-rw-rw---- 1 polkitd input 100663296 Aug 31 23:12 ib_logfile0
-rw-rw---- 1 polkitd input  12582912 Aug 31 22:55 ibtmp1
-rw-rw---- 1 polkitd input         0 Aug 31 22:55 multi-master.info
drwx------ 2 polkitd input      4096 Aug 31 22:55 mysql
drwx------ 2 polkitd input      4096 Aug 31 22:55 performance_schema
drwx------ 2 polkitd input     12288 Aug 31 22:55 sys

现在我们把这个pod删掉,然后再次创建,发现数据还在

[root@master ~]# kubectl delete -f mysql.yaml 
statefulset.apps "mariadb" deleted
[root@master ~]# kubectl get pod
NAME                             READY   STATUS    RESTARTS   AGE
go-microserver-74c85dd7f-8kzlh   1/1     Running   0          30h
go-microserver-74c85dd7f-jlt4h   1/1     Running   0          30h
go-microserver-74c85dd7f-m6wzl   1/1     Running   0          30h
go-microserver-74c85dd7f-zntnr   1/1     Running   0          30h
[root@master ~]# kubectl apply -f mysql.yaml 
statefulset.apps/mariadb created
[root@master ~]# kubectl get pod
NAME                             READY   STATUS    RESTARTS   AGE
go-microserver-74c85dd7f-8kzlh   1/1     Running   0          30h
go-microserver-74c85dd7f-jlt4h   1/1     Running   0          30h
go-microserver-74c85dd7f-m6wzl   1/1     Running   0          30h
go-microserver-74c85dd7f-zntnr   1/1     Running   0          30h
mariadb-0                        1/1     Running   0          3s


[root@master ~]# kubectl exec -it mariadb-0 -- /bin/bash
root@mariadb-0:/# mysql -uroot -p123456
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.6.5-MariaDB-1:10.6.5+maria~focal mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| db1                |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.000 sec)

MariaDB [(none)]> use db1;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [db1]> show tables;
+---------------+
| Tables_in_db1 |
+---------------+
| tb1           |
+---------------+
1 row in set (0.000 sec)

MariaDB [db1]> select * from tb1;
+----------+------+
| name     | age  |
+----------+------+
| zhangsan |   14 |
| lisi     |   15 |
+----------+------+
2 rows in set (0.000 sec)

pvc

Persistent Volume,也就是持久化存储
image.png
以一个本地pvc为例子,注意,这里需要提前创建挂载点目录!

apiVersion: apps/v1
kind: StatefulSet
metadata: 
  name: mariadb
spec: 
  serviceName: mariadb
  replicas: 1
  selector: 
    matchLabels: 
      app: mariadb
  template: 
    metadata: 
      labels: 
        app: mariadb
    spec: 
      containers: 
      - name: mariadb
        image: mariadb:latest
        imagePullPolicy: IfNotPresent #只有当本地不存在镜像才从远处拉取
        env: 
        - name: MARIADB_ROOT_PASSWORD
          value: "123456"
        volumeMounts: 
          - mountPath: /var/lib/mysql/ #容器里面的挂载路径
            name: mysqldata #卷的名字,必须和下面一样 
      volumes: 
      - name: mysqldata
        persistentVolumeClaim:
         claimName: mariadbdata
---
apiVersion: storage.k8s.io/v1 # SC,划分存储盘的类型
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolume ## 卷的具体描述信息
metadata:
  name: mariadbdata
spec:
  capacity:
    storage: 2Gi
  volumeMode: Filesystem  # Filesystem(文件系统) Block(块)
  accessModes:
    - ReadWriteOnce       # 卷可以被一个节点以读写方式挂载
  persistentVolumeReclaimPolicy: Delete #pv回收策略,怕误删可以选择Retain
  storageClassName: local-storage
  local:
    path: /data/sql
  nodeAffinity:
    required:
      # 通过 hostname 限定在某个节点创建存储卷
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - node2
---
apiVersion: v1
kind: PersistentVolumeClaim #对存储需求的申请,系统根据这个申请单去寻找合适的pv
metadata:
  name: mariadbdata
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: "local-storage"
  resources:
    requests:
      storage: 2Gi

apply看一下,成了

[root@master ~]# kubectl get pods -o wide
NAME                             READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
go-microserver-74c85dd7f-8kzlh   1/1     Running   0          31h   10.244.0.21   node2   <none>           <none>
go-microserver-74c85dd7f-jlt4h   1/1     Running   0          31h   10.244.0.20   node1   <none>           <none>
go-microserver-74c85dd7f-m6wzl   1/1     Running   0          31h   10.244.0.20   node2   <none>           <none>
go-microserver-74c85dd7f-zntnr   1/1     Running   0          31h   10.244.0.21   node1   <none>           <none>
mariadb-0                        1/1     Running   0          12s   10.244.0.25   node2   <none>           <none>
[root@master ~]# kubectl get sc
NAME            PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-storage   kubernetes.io/no-provisioner   Delete          WaitForFirstConsumer   false                  20s
[root@master ~]# kubectl get pv
NAME          CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                 STORAGECLASS    REASON   AGE
mariadbdata   2Gi        RWO            Delete           Bound    default/mariadbdata   local-storage            23s
[root@master ~]# kubectl get pvc
NAME          STATUS   VOLUME        CAPACITY   ACCESS MODES   STORAGECLASS    AGE
mariadbdata   Bound    mariadbdata   2Gi        RWO            local-storage   25s




[root@node2 ~]# ll /data/sql
total 123328
-rw-rw---- 1 polkitd input    417792 Sep  1 00:17 aria_log.00000001
-rw-rw---- 1 polkitd input        52 Sep  1 00:17 aria_log_control
-rw-rw---- 1 polkitd input         9 Sep  1 00:17 ddl_recovery.log
-rw-rw---- 1 polkitd input       946 Sep  1 00:17 ib_buffer_pool
-rw-rw---- 1 polkitd input  12582912 Sep  1 00:17 ibdata1
-rw-rw---- 1 polkitd input 100663296 Sep  1 00:17 ib_logfile0
-rw-rw---- 1 polkitd input  12582912 Sep  1 00:17 ibtmp1
-rw-rw---- 1 polkitd input         0 Sep  1 00:17 multi-master.info
drwx------ 2 polkitd input      4096 Sep  1 00:17 mysql
drwx------ 2 polkitd input      4096 Sep  1 00:17 performance_schema
drwx------ 2 polkitd input     12288 Sep  1 00:17 sys

configuremap

保存配置的键值对,实现配置与应用分离

我们把yaml里面的env部分删除,把密码写进cm.yaml里面,部署看看

apiVersion: v1
kind: ConfigMap
metadata:
  name: mariadb-config
data:
  MARIADB_ROOT_PASSWORD: "123456"
apiVersion: apps/v1
kind: StatefulSet
metadata: 
  name: mariadb
spec: 
  serviceName: mariadb
  replicas: 1
  selector: 
    matchLabels: 
      app: mariadb
  template: 
    metadata: 
      labels: 
        app: mariadb
    spec: 
      containers: 
      - name: mariadb
        image: mariadb:latest
        imagePullPolicy: IfNotPresent #只有当本地不存在镜像才从远处拉取
        env: 
        - name: MARIADB_ROOT_PASSWORD
          valueFrom:
            configMapKeyRef: 
              name: mariadb-config
              key: MARIADB_ROOT_PASSWORD
        volumeMounts: 
          - mountPath: /var/lib/mysql/ #容器里面的挂载路径
            name: mysqldata #卷的名字,必须和下面一样 
      volumes: 
      - name: mysqldata
        persistentVolumeClaim:
         claimName: mariadbdata


可以看到容器成功起来了,如果这里没有指定密码的话,mariadb是起不来的

[root@master ~]# kubectl  apply -f  cm.yaml 
configmap/mariadb-config created
[root@master ~]# kubectl apply -f mysql.yaml 
statefulset.apps/mariadb created
storageclass.storage.k8s.io/local-storage created
persistentvolume/mariadbdata created
persistentvolumeclaim/mariadbdata created
[root@master ~]# kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
go-microserver-74c85dd7f-8kzlh   1/1     Running   0          40h
go-microserver-74c85dd7f-jlt4h   1/1     Running   0          40h
go-microserver-74c85dd7f-m6wzl   1/1     Running   0          40h
go-microserver-74c85dd7f-zntnr   1/1     Running   0          40h
mariadb-0                        1/1     Running   0          5s
[root@master ~]# kubectl get cm
NAME               DATA   AGE
kube-root-ca.crt   1      2d10h
mariadb-config     1      3m14s
[root@master ~]# kubectl get cm mariadb-config -o yaml 
apiVersion: v1
data:
  MARIADB_ROOT_PASSWORD: "123456"
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"MARIADB_ROOT_PASSWORD":"123456"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"mariadb-config","namespace":"default"}}
  creationTimestamp: "2022-09-01T01:47:05Z"
  name: mariadb-config
  namespace: default
  resourceVersion: "305952"
  uid: 16c011c0-7fce-4c3b-81de-1d131994eed3

secret

一些重要的数据,如密码之类的,可以用base64编码之后放在secret里面

apiVersion: v1
kind: Secret
metadata:
  name: mariadb-secret
# Opaque 用户定义的任意数据,更多类型介绍 https://kubernetes.io/zh/docs/concepts/configuration/secret/#secret-types
type: Opaque
data:
  MARIADB_ROOT_PASSWORD: MTIzNDU2

apiVersion: apps/v1
kind: StatefulSet
metadata: 
  name: mariadb
spec: 
  serviceName: mariadb
  replicas: 1
  selector: 
    matchLabels: 
      app: mariadb
  template: 
    metadata: 
      labels: 
        app: mariadb
    spec: 
      containers: 
      - name: mariadb
        image: mariadb:latest
        imagePullPolicy: IfNotPresent #只有当本地不存在镜像才从远处拉取
        env: 
        - name: MARIADB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mariadb-secret
              key: MARIADB_ROOT_PASSWORD
        volumeMounts: 
          - mountPath: /var/lib/mysql/ #容器里面的挂载路径
            name: mysqldata #卷的名字,必须和下面一样 
      volumes: 
      - name: mysqldata
        persistentVolumeClaim:
         claimName: mariadbdata

          # Secret 的所有数据定义为容器的环境变量,Secret 中的键名称为 Pod 中的环境变量名称
          # envFrom:
          # - secretRef:
          #     name: secretname

把configuremap下了换secret,statefulset配置也改一下,pod正常启动了

[root@master ~]# kubectl apply -f secret.yaml 
secret/mariadb-secret created
[root@master ~]# kubectl apply -f mysql.yaml 
statefulset.apps/mariadb created
storageclass.storage.k8s.io/local-storage created
persistentvolume/mariadbdata created
persistentvolumeclaim/mariadbdata created
[root@master ~]# kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
go-microserver-74c85dd7f-8kzlh   1/1     Running   0          40h
go-microserver-74c85dd7f-jlt4h   1/1     Running   0          40h
go-microserver-74c85dd7f-m6wzl   1/1     Running   0          40h
go-microserver-74c85dd7f-zntnr   1/1     Running   0          40h
mariadb-0                        1/1     Running   0          7s

挂载为文件,这里就复制个例子
挂载后,会在容器中对应路径生成文件,一个 key 一个文件,内容就是 value,文档

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

namespace

类似于编程里面的namespace概念,我们可以把应用划分进不同的ns空间来进行分隔和管理

# 创建命名空间
kubectl create namespace testapp
# 部署应用到指定的命名空间
kubectl apply -f app.yml --namespace testapp
# 查询
kubectl get pod --namespace kube-system

示例

[root@master ~]# kubectl create namespace testapp
namespace/testapp created
[root@master ~]# kubectl apply -f goservice.yaml -n testapp
deployment.apps/go-microserver created
[root@master ~]# kubectl -n testapp get pods
NAME                             READY   STATUS    RESTARTS   AGE
go-microserver-74c85dd7f-46zwk   1/1     Running   0          21s
go-microserver-74c85dd7f-5qlxp   1/1     Running   0          21s
go-microserver-74c85dd7f-6dmqp   1/1     Running   0          21s
go-microserver-74c85dd7f-dhh9g   1/1     Running   0          21s
[root@master ~]# kubectl get ns
NAME              STATUS   AGE
default           Active   2d10h
kube-flannel      Active   47h
kube-node-lease   Active   2d10h
kube-public       Active   2d10h
kube-system       Active   2d10h
testapp           Active   53s

https://feisky.gitbooks.io/kubernetes/content/introduction/

k8s是一个能够自动部署容器的集群管理系统,相较于传统的容器技术,k8s能够实现pod的自动调度,回滚,负载均衡等技术
k8s由master节点和node节点组成,master节点负责控制集群,node就是容器所真实存在的平台,他可能是物理机,也可能是一台虚拟机,一个node上面可能有一个或者多个pod,在新pod生成的时候,master会根据算法把他调度到合适的某台node上面去完成生成。
k8s架构.jpg

基础概念

k8s的核心组件

  • etcd 保存了整个集群的状态;
  • apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;
  • controller manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
  • scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;
  • kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理;
  • Container runtime 负责镜像管理以及 Pod 和容器的真正运行(CRI);
  • kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡

安装

单机测试-minikube

感觉云相关的组建配置要求都比较高......至少单核的学生服务器是跑不起来了,这里我在阿里云上白嫖了一台2核4g的服务器,或者也可以使用自己本地的虚拟机来安装,配置给高点就行

一般来讲k8s的master是独立部署的,而minikube则把master和node合并在一起了
image.png

https://minikube.sigs.k8s.io/docs/start/

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
minikube start

这里可能会报Exiting due to DRV_AS_ROOT: The "docker" driver should not be used with root privileges.,要么另外创建一个用户跑dockerhttps://blog.csdn.net/fly_leopard/article/details/108790217

要么minikube start --force --driver=docker --image-mirror-country='cn' --image-repository='registry.cn-hangzhou.aliyuncs.com/google_containers' --base-image='registry.cn-hangzhou.aliyuncs.com/google_containers/kicbase:v0.0.28''__

alias kubectl="minikube kubectl --"

不过这里因为墙的原因,即使加了代理设置试了几次也没能把镜像拉下来,心累......有兴趣玩这个模拟器可以自查这本官方手册
https://minikube.sigs.k8s.io/docs/start/

集群

https://k8s.easydoc.net/docs/dRiQjyTY/28366845/6GiNOzyZ/nd7yOvdY#nav_3

主节点需要:
docker
kubectl
kubeadm

worker节点需要:
docker
kubelet
kube-proxy

我这里以三台2c4g的云服务器为例来进行部署,服务器可以按流量计费去租,云服务厂商的镜像一般都是已经调整过的,如果是自己的镜像可能会有很多坑,先列举几个
1.关闭swap

swapoff -a
vi /etc/fstab 
#/dev/mapper/centos_hhdcloudrd6-swap swap                    swap    defaults        0 0
reboot

2.ip转发

 echo 1 > /proc/sys/net/ipv4/ip_forward

一台master两台node,全部修改/etc/hosts互相认识一下,改完记得ping一下名字看看通不通

关防火墙和SELinux,不过默认一般都是都关了的

配置k8s和docker的安装源

cat <<EOF > kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
mv kubernetes.repo /etc/yum.repos.d/


yum -y install yum-utils
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装组件

yum install -y kubelet-1.22.4 kubectl-1.22.4 kubeadm-1.22.4 docker-ce

设置开机自启

systemctl enable kubelet
systemctl start kubelet
systemctl enable docker
systemctl start docker

修改docker配置

# kubernetes 官方推荐 docker 等使用 systemd 作为 cgroupdriver,否则 kubelet 启动不了
#kubelet会默认--cgroup-driver=systemd,以免systemd和cgroupfs发生冲突导致kubelet无法使用
cat <<EOF > daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "registry-mirrors": ["https://ud6340vz.mirror.aliyuncs.com"]
}
EOF
mkdir /etc/docker
mv daemon.json /etc/docker/

systemctl daemon-reload
systemctl restart docker

在master使用kubeadm初始化集群,搭建一个控制平面
https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/kubeadm-init/

echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables #这个每个节点都要
kubeadm init --image-repository=registry.aliyuncs.com/google_containers --pod-network-cidr=10.244.0.0/16 
#此处使用flannel,需要与文件中的net-conf.json.Network保持一致,不然会报错
#查看cidr的值的快速办法cat net.yaml |grep -E "^\s*\"Network"
# 这里可以用pod-network-cidr来指定cidr,不过后面的cni插件得要做出相应的修改
#如果你想要使用coredns作为你的dns而非kube-dns,那么你可以使用--feature-gates=CoreDNS=true


记得保存kubeamd join这条指令,后面让工作节点加入集群用的
忘记了重新获取:kubeadm token create --print-join-command
kubeadm join 172.20.245.132:6443 --token 32tne8.90kj2le30sgrxy1u \
        --discovery-token-ca-cert-hash sha256:aa08f6d92df2507ad0c9739df7ffd25861576fde4453dc47aec9a08984b51a43 

请注意这里是采用了单节点来部署master节点,如果你需要多节点部署高可用的k8s,那你需要在初始化的时候加入--control-plane-endpoint + 负载均衡,如果没有加后续无法调整!
https://www.cnblogs.com/cmt/p/12061089.html
https://www.cnblogs.com/dudu/p/12168433.html
https://www.jianshu.com/p/35b2c6deb573

复制授权文件,以便 kubectl 可以有权限访问集群
如果你其他节点需要访问集群,或者你需要从你外部的工作站访问集群,则需要从主节点复制admin.conf过去其他节点(linux和mac可以使用scp指令去复制)

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

如果不行就
vi /etc/profile.d/kube.sh
export KUBECONFIG=$HOME/.kube/config
source /etc/profile.d/kube.sh

当你从外部访问
kubectl --kubeconfig ./admin.conf get nodes

当你希望从外部访问kuber api
scp root@<control-plane-host>:/etc/kubernetes/admin.conf .
kubectl --kubeconfig ./admin.conf proxy

在工作节点运行保存的kubeadm join
如果你后续想要让机器加入集群,可以使用上面保存的指令

kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>
如果你丢失了令牌,可以使用这个来列出
#kubeadm token list
#token一般有效期为24h,如果过期了, 你可以重新生成一个
kubeadm token create

跑完后就可以使用kubectl 命令了,但是我这里不知道为什么环境变量上不去,于是额外执行

vi /etc/profile.d/kube.sh
export KUBECONFIG=/etc/kubernetes/kubelet.conf

创建kube-flannel.yml,写入这个文件的内容(因为直接拉拉不下来),这是一个cni插件,处理网络相关的,https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml,然后kubectl apply -f kube-flannel.yml,不做这一步会发现node一直都是not ready
要注意的是,这里默认的文件有个坑,他默认的type为vxlan,而我们是云服务器,如果只是像这样做个小练习还好,实际环境一定要改或者按需选择!
(直接部署不做其他配套改动其实大概率是行不通的,就当是仪式感了)

#如果你对你的网络有信心,你可以直接运行
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.20.2/Documentation/kube-flannel.yml

#如果你发现你部署完flannel就寄了,可以查看pod的日志以观察是什么原因,这里提供一种情形,具体看我另外的帖子
#导致这个问题的原因是kubeadm的时候没加cidr的参数
#编辑 master 机器上的 /etc/kubernetes/manifests/kube-controller-manager.yaml
#启动文件加上下面两句话,下面这个cluster-cidr要和kube-flannel.yml里面的地址一致,要和kube-proxy.config.yaml里面的clusterCIDR一致

- --allocate-node-cidrs=true
- --cluster-cidr=10.244.0.0/16#

你为你集群安装的pod网络插件不能与你集群网络本身产生任何重叠,否则会导致一些问题,如果你想要修改网络,你可以在init的时候使用--pod-network-cidr 参数默认情况下,k8s使用RBAC来作为权限校正机制,你需要确保你选择的cni支持这种机制
cni本身并不由kubeadm提供,所以在选择那些第三方的时候最好足够小心谨慎,每个集群只能安装一个cni

https://jimmysong.io/kubernetes-handbook/practice/flannel-installation.html

[root@master ~]# kubectl get nodes
NAME     STATUS   ROLES                  AGE   VERSION
master   Ready    control-plane,master   10h   v1.22.4
node1    Ready    <none>                 10h   v1.22.4
node2    Ready    <none>                 10h   v1.22.4

在每个节点上写入这个文件/run/flannel/subnet.env

FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

使用命令行运行和删除一个pod

[root@master ~]# kubectl get pods
No resources found in default namespace.
[root@master ~]# kubectl run test-server --image=go-service:v3
pod/test-server created
[root@master ~]# kubectl get pods
NAME          READY   STATUS              RESTARTS   AGE
test-server   0/1     ContainerCreating   0          8s
[root@master ~]# kubectl get pods
No resources found in default namespace.

基础操作

kubectl

kubectl常用指令
https://blog.csdn.net/weixin_36755535/article/details/127615449

kubectl apply/create -f yaml #根据yaml文件来创建pod,create只能创建一次,apply如果pod不存在则创建,如果存在则更新
kubectl get pod -n nodename  -o wide/yaml --show-labels -l label #-n指定node,获取其中的pod列表,-o wide显示详细信息,yaml是以yaml的格式显示,show-labels显示标签,-l显示含有某标签的pod
kubectl get namespaces/ns #获取namespace
kubectl create namespace new-namespace  #创建namespace
kubectl create cm xxx --from-file xxxx  #从文件创建cm
kubectl describe #查看某对象的详细信息
kubectl get service servername #查看server
kubectl -n node logs pod/podname -c 具体的容器名 #查看日志,如果pod里只有一个容器,那-c可以不用
kubectl delete pod  xxx #删除pod
kubectl exec -it <pod-name> -c <container-name> -- bash #登陆容器
#如果想直接执行命令,可以把-it去掉,然后把--后面的东西换成你的命令,--意味着kubectl语句的结束,在这之后的-不会被解析成kubectl的参数
kubectl delete 类型 名称 #    删除
kubectl logs pod/podname #    查看日志
kubectl -n xxx get pods xxxx -o jsonpath={.spec.containers[*].name} #查看pod里面的容器
kubectl rolling-update test-pod -f test-pod2.json #滚动更新
kubectl rolling-update test-pod test-pod2 --image=image:v2 #更新资源名和镜像
kubectl edit #更新资源,立即生效,可以通过配置环境变量KUBE_EDITOR来指定编辑器
kubectl scale --replicas=   #手动放缩
kubectl label --overwrite #打标签,如果原来有标签,需要overwrite,不然只会警告不会实际更改
kubectl explain ingress.spec #查询某种资源类型的定义规范格式等

yaml编写

k8s一般采用yaml来生成想要的pod

yaml大小写敏感,使用缩进来表示层级,相同层级要作对齐,用空格不要用tab,末尾要打个空格,如果把多个yaml放在一个文件,需要使用------来分割,#表示注释

apiVersion: group/apiversion  # 如果没有给定group名称,那么默认为croe,可以使用kubectl api-versions 获取当前k8s版本上所有的apiVersion版本信息(每个版本可能不同)
kind:       #资源类别
metadata:  #资源元数据
  name   
  namespace  #k8s自身的namespace   
  lables   
  annotations   #主要目的是方便用户阅读查找
spec: #期望的状态(disired state)
    containers:  
      - image:    
        name:   
        ports:    
          - name:      
            containerPort:    
            protocol: 

status: #当前状态,本字段有kubernetes自身维护,用户不能去定义

变量格式

k8s支持三种类型的变量:常量、对象(键值对),数组(一组按顺序排列的值),记得要打空格
常量:

数字、布尔、时间、字符串等

s1: hello
s2: 123
s3: True

对象

adb:
  a1: 1
  b1: 2

数组

addr:
  - Beijing
  - tokyo

常见kind

kind类型
资源.jpg

ReplicationController、ReplicaSet、deployment

rc/rs作用相似,后者是前者的升级版,rs的作用是使的pod的副本数与用户设置的数量保持一致,当有容器异常退出,填会去调度新的pod来进行替代,deployment比rc的功能更多,除了能够管理rc以外,deploy还能够提供回滚等操作,所以我们一般会去创建deployment而非直接创建rc/rs

示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec: #描述详情
  replicas: 3 #副本数量
  template: #模版,如果pod的数量不足,会按照下列模版去生成
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

daemonset

daemonset作用是使的这个容器在每个node上都有一个运行副本,多用于部署一些日志、监控等功能

statefulset

StatefulSet 是为了解决有状态服务的问题(对应 Deployments 和 ReplicaSets 是为无状态服务而设计),其应用场景包括

  • 稳定的持久化存储,即 Pod 重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现
  • 稳定的网络标志,即 Pod 重新调度后其 PodName 和 HostName 不变,基于 Headless Service(即没有 Cluster IP 的 Service)来实现
  • 有序部署,有序扩展,即 Pod 是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依序进行(即从 0 到 N-1,在下一个 Pod 运行之前所有之前的 Pod 必须都是 Running 和 Ready 状态),基于 init containers 来实现
  • 有序收缩,有序删除(即从 N-1 到 0)

用人话来说,就是那些会产生数据的,比如普罗米修斯,就得用这种

要注意的是,pod的数据并不能持久化保存,比如重启下可能数据就没了,如果想要让数据长期存储,得另外挂载卷volume

示例

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
          name: web
        volumeMounts:   #挂载
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi

service

service.jpg

service的功能是把一些pod抽象成一组,为其提供一个统一的网络入口,通过service可以方便的实现负载均衡等功能,service是通过标签来选择服务后端等,这些匹配标签的 Pod IP 和端口列表组成 endpoints,由 kube-proxy 负责将服务 IP 负载均衡到这些 endpoints 上。不然pod的ip会在每次重启发生变化,并且只能在内部访问

Service 有四种类型:

  • ClusterIP:默认类型,自动分配一个仅 cluster (集群)内部可以访问的虚拟 IP
  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 :NodePort 来访问该服务。如果 kube-proxy 设置了 --nodeport-addresses=10.240.0.0/16(v1.10 支持),那么仅该 NodePort 仅对设置在范围内的 IP 有效。
  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 :NodePort
  • ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。需要 kube-dns 版本在 1.7 以上。

另外,也可以将已有的服务以 Service 的形式加入到 Kubernetes 集群中来,只需要在创建 Service 的时候不指定 Label selector,而是在 Service 创建好后手动为其添加 endpoint。

示例一:定义了一个叫做nginx的service,他会把这个service策略下的pod的80端口的数据转发到default namespace连标签为name: nginx的pod的80端口

apiVersion: v1
kind: Service
metadata:
  labels:
    run: nginx
  name: nginx
  namespace: default
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:  # 标签选择器
    run: nginx
  sessionAffinity: None
  type: ClusterIP
 

示例二:当涉及到多个端口,需要每个端口都起一个名

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  - name: https
    protocol: TCP
    port: 443
    targetPort: 9377 

示例三:转发到集群外的service,有两种写法

3.1:自定义 endpoint,即创建同名的 service 和 endpoint,在 endpoint 中设置外部服务的 IP 和端口

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
---
kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 1.2.3.4
    ports:
      - port: 9376

3.2:通过 DNS 转发,在 service 定义中指定 externalName。此时 DNS 服务会给 ..svc.cluster.local 创建一个 CNAME 记录,其值为 my.database.example.com。并且,该服务不会自动分配 Cluster IP,需要通过 service 的 DNS 来访问。

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: default
spec:
  type: ExternalName
  externalName: my.database.example.com

如果一个service没有指定ip,那就被叫做headless service,一共有两种,一种是上那种通过dns转发的,还一种是指定selectors,通过dns a 去找后端endpoint列表。

apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  clusterIP: None
  ports:
  - name: tcp-80-80-3b6tl
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  sessionAffinity: None #一种分发策略,基于客户端IP地址进行会话保持/关联的模式,即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上
  type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 2
  revisionHistoryLimit: 5  # 历史记录版本
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:latest
        imagePullPolicy: Always
        name: nginx
        resources:
          limits:
            memory: 128Mi
          requests:
            cpu: 200m
            memory: 128Mi
      dnsPolicy: ClusterFirst
      restartPolicy: Always
headless

https://cloud.tencent.com/developer/article/1638722
用上面的方法布的pod都会有个cluster ip,由kube-proxy负责,如果不想要集群ip,而是想给pod弄个单独的serviceip,那么可以使用headless,clusterIP: None
headless有lable secector和service(iptables代理)两种模式
对于有selector的headless,endpiont会记录并修改dns返回的地址,使得通过地址能够直接到达pod,pod的backend里面包含了跟他满足同样条件的其他pod的ip。如果没有selector,endpoints不会做记录
image.png

apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  clusterIP: None
  ports:
  - name: tcp-80-80-3b6tl
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  sessionAffinity: None #一种分发策略,基于客户端IP地址进行会话保持/关联的模式,即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上
  type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 2
  revisionHistoryLimit: 5  # 历史记录版本
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:latest
        imagePullPolicy: Always
        name: nginx
        resources:
          limits:
            memory: 128Mi
          requests:
            cpu: 200m
            memory: 128Mi
      dnsPolicy: ClusterFirst
      restartPolicy: Always

命名端口,使用别名可以在pod修改端口后service无需修改

kind: pod
spec: 
    containers:
      - name: test
        ports: 
          - name: http #给这个端口起名为http
            containerPort: 8080
          -    name: https
            containerPort: 8443

-------
apiVersion: v1
kind: Service
spec:
    ports:
    - name: http        #将80端口映射到叫http的端口上
      port: 80
      targetPort: http
    

service并不是与服务直连的,而是通过endpoint,使用describe来查看service可以看见相关的信息
endpoint就是暴露服务的ip和端口列表
选择器⽤于构建IP和端⼜列表,然后存储在Endpoint资源中。当客户端连接到服务时,服务代理选择这些IP和端⼜对中的⼀个,并将传⼊连接重定向到在该位置监听的服务器。
这也就意味着,service其实是和endpoint解耦的,你完全可以独立维护一个endpoint的yaml,将其对接在一个不含pod选择器的service上,两者需要相同的名称

[root@master ~]# kubectl describe svc jenkins-service -n devops-tools
Name:                     jenkins-service
Namespace:                devops-tools
Labels:                   <none>
Annotations:              prometheus.io/path: /
                          prometheus.io/port: 8080
                          prometheus.io/scrape: true
Selector:                 app=jenkins-server
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.111.233.39
IPs:                      10.111.233.39
Port:                     <unset>  8080/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  32000/TCP
Endpoints:                10.244.1.3:8080   #服务endpoint的pod的列表
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

[root@master ~]# kubectl get endpoints -n devops-tools
NAME              ENDPOINTS         AGE
jenkins-service   10.244.1.3:8080   16d
loadbalance

负载均衡器loadbalance拥有自己的公开可访问的ip地址,并且将所有的连接重定向到服务,可以通过负载均衡器的ip地址访问服务
lb是nodeport的扩展,如果k8s所在的环境不支持lb,则lb会表现得如同nodeport
lb是基于连接的,类似四层转发
image.png
image.png
image.png


需要注意的是,流量进入到节点之后,可能不止只转到该节点上的容器,还有可能会被转到别的节点上,以造成额外的开销,如果你不希望这种情况的发生,可以配置externalTrafficPolicy为Local,但是这会对负载均衡的性能造成影响
https://www.cnblogs.com/zisefeizhu/p/13262239.html
默认可能会发生的情况,这种情况下因为被节点转过一次(SNAT),最终后端将无法记录原始的客户端ip
image.png
local
image.png

ingress

lb的缺点在上述中已经体现的很明显了,每个lb都需要自己的负载均衡器和ip地址,为了解决这个问题,我们选择使用ingress来代替
ingress只需要一个ip就可以为多个服务提供访问,当一个请求到达时,他可以根据主机名和路径来决定转发
ingress是基于应用层的,类似七层转发
ingress需要ingress控制器才能实现,不同的k8s环境在这点上所需不同,请注意这点

ingress控制器就相当于一个监听器,通过apiserver不断去监听后端service\pod的变化,根据ingress的配置去更新代理
https://help.aliyun.com/document_detail/204886.html
image.png
image.png

backend除了支持service之外,还支持连接资源,他和service是互斥的,resource常用用法是将静态资源导向对象存储后端

[root@master ~]# kubectl explain ingress.spec.rules.http.paths.backend
KIND:     Ingress
VERSION:  networking.k8s.io/v1

RESOURCE: backend <Object>

DESCRIPTION:
     Backend defines the referenced service endpoint to which the traffic will
     be forwarded to.

     IngressBackend describes all endpoints for a given service and port.

FIELDS:
   resource     <Object>
     Resource is an ObjectRef to another Kubernetes resource in the namespace of
     the Ingress object. If resource is specified, a service.Name and
     service.Port must not be specified. This is a mutually exclusive setting
     with "Service".

   service      <Object>
     Service references a Service as a Backend. This is a mutually exclusive
     setting with "Resource".

namespace

Namespace 是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或用户组。常见的 pod, service, replication controller 和 deployment 等都是属于某一个 namespace 的(默认是 default),而 node, persistent volume,namespace 等资源则不属于任何 namespace。
Namespace 常用来隔离不同的用户,比如 Kubernetes 自带的服务一般运行在 kube-system namespace 中。

用人话说,默认情况下k8s里的pod是可以互相访问的,如果想要实现分组隔离,可以使用namespace,另外可以把namespace交给不同的用户进行管理

kubectl 可以通过 --namespace 或者 -n 选项指定 namespace。如果不指定,默认为 default。查看操作下, 也可以通过设置 --all-namespace=true 来查看所有 namespace 下的资源。可以通过kubectl get namespaces/ns来查看

创建

(1) 命令行直接创建
$ kubectl create namespace new-namespace

(2) 通过文件创建
$ cat my-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: new-namespace

$ kubectl create -f ./my-namespace.yaml


删除

kubectl delete namespaces new-namespace
删除一个 namespace 会自动删除所有属于该 namespace 的资源。
default 和 kube-system 命名空间不可删除。
PersistentVolume 是不属于任何 namespace 的,但 PersistentVolumeClaim 是属于某个特定 namespace 的。
Event 是否属于 namespace 取决于产生 event 的对象。
v1.7 版本增加了 kube-public 命名空间,该命名空间用来存放公共的信息,一般以 ConfigMap 的形式存放

configmap

configap可以帮我实现配置和应用分离,其用于保存配置数据的键值对,可以用来保存单个属性,也可以用来保存配置文件。

要注意:

  • ConfigMap 必须在 Pod 引用它之前创建
  • 使用 envFrom 时,将会自动忽略无效的键
  • Pod 只能使用同一个命名空间内的 ConfigMap

通过yaml来编写

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm
  
  
  
  
$ kubectl create  -f  config.yaml
configmap "special-config" created

使用

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: ["/bin/sh", "-c", "env"]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config  #所使用的configmap
              key: special.how        #所使用的变量
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.type
      envFrom:
        - configMapRef:
            name: env-config
  restartPolicy: Never
  
  
  
  #输出
SPECIAL_LEVEL_KEY=very
SPECIAL_TYPE_KEY=charm
log_level=INFO

作为命令行参数

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: ["/bin/sh", "-c", "echo $(SPECIAL_LEVEL_KEY) $(SPECIAL_TYPE_KEY)" ]
      env:
        - name: SPECIAL_LEVEL_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.how
        - name: SPECIAL_TYPE_KEY
          valueFrom:
            configMapKeyRef:
              name: special-config
              key: special.type
  restartPolicy: Never

挂载

apiVersion: v1
kind: ConfigMap
metadata:  
  name: configmap  
  namespace: dev
  data:  
    info: |    
      username:admin    
      password:123456
      
kubectl create -f configmap.yaml
configmap/configmap created


apiVersion: v1
kind: Pod
metadata:  
  name: pod-configmap  
  namespace: dev
  spec:  
    containers:  
      - name: nginx    
        image: nginx:1.17.1    
    volumeMounts: # 将configmap挂载到目录    
      - name: config      
        mountPath: /configmap/config  
    volumes: # 引用configmap  
      - name: config    
        configMap:      
        name: configmap

volume

容器会在挂掉之后丢失所有数据,为了使数据持久化,可以采用volume,volume的生命周期与pod绑定,其中部分(emptyDir、secret、gitRepo等)不会因为容器挂掉而失去数据,但是如果pod被删除数据也会被清空

emptyDir

如果 Pod 设置了 emptyDir 类型 Volume, Pod 被分配到 Node 上时候,会创建 emptyDir,只要 Pod 运行在 Node 上,emptyDir 都会存在(容器挂掉不会导致 emptyDir 丢失数据),但是如果 Pod 从 Node 上被删除(Pod 被删除,或者 Pod 发生迁移),emptyDir 也会被删除,并且永久丢失。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts: #将cache-volume挂载进容器里/cache
    - mountPath: /cache  # 容器内的挂载点
      name: cache-volume  #名字
  volumes:  #声明一个卷叫做cache-volume,类型为emptyDir
  - name: cache-volume  #与上面名字对应
    emptyDir: {}

hostpath

hostPath 允许挂载 Node 上的文件系统到 Pod 里面去。如果 Pod 需要使用 Node 上的文件,可以使用 hostPath。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /data
      type: DirectoryOrCreate  # 目录存在就使用,不存在就先创建后使用,可选
      
#关于type的值的一点说明:    
DirectoryOrCreate 目录存在就使用,不存在就先创建后使用    
Directory   目录必须存在    
FileOrCreate  文件存在就使用,不存在就先创建后使用    
File 文件必须存在     
Socket  unix套接字必须存在    
CharDevice  字符设备必须存在    
BlockDevice 块设备必须存在

NFS
可以将文件挂载到一台NFS服务器上,那台服务器必须已经开启了NFS,并将共享目录暴露给pod所在网段

persistent volume

pv是持久化卷的意思,是对底层存储的一种抽象,一般通过管理员创建,通过插件实现共享化存储。

PV 提供网络存储资源,而 PVC (PersistentVolumeClaim)请求存储资源,这样,设置持久化的工作流包括配置底层文件系统或者云数据卷、创建持久性数据卷、最后创建 PVC 来将 Pod 跟数据卷关联起来。PV 和 PVC 可以将 pod 和数据卷解耦,pod 不需要知道确切的文件系统或者支持它的持久化引擎。

说白点就是pv负责把实际的存储抽象打包起来,而PVC负责去调用这些pv,上面用pvc的人不需要知道底层存储到底是什么样子的

Volume 的生命周期包括 5 个阶段

  1. Provisioning,即 PV 的创建,可以直接创建 PV(静态方式),也可以使用 StorageClass 动态创建
  2. Binding,用户创建pvc,如果有满足条件的会绑定,如果没有会一直等着
  3. Using,Pod 通过 PVC 使用该 Volume,并可以通过准入控制 StorageObjectInUseProtection(1.9 及以前版本为 PVCProtection)阻止删除正在使用的 PVC
  4. Releasing,Pod 释放 Volume 并删除 PVC
  5. Reclaiming,回收 PV,可以保留 PV 以便下次使用,也可以直接从云存储中删除
  6. Deleting,删除 PV 并从云存储中删除后段存储

根据这 5 个阶段,Volume 的状态有以下 4 种

  • Available:可用
  • Bound:已经分配给 PVC
  • Released:PVC 解绑但还未执行回收策略
  • Failed:发生错误
apiVersion: v1  
kind: PersistentVolume
metadata:  
  name: pv2
spec:  
  nfs: # 存储类型 
  capacity:  # 存储能力   
    storage: 2Gi  
  accessModes:  # 访问模式  
  storageClassName: # 存储类别  
  persistentVolumeReclaimPolicy: # 回收策略

访问模式分为三种,不同的底层存储类型可能支持的访问方式不同

  1. ReadWriteOnce:读写,只能被单个节点挂载
  2. ReadOnlyMany:只读,多个节点
  3. ReadWriteMany:读写,多个节点

回收策略分为三种:

  1. retain:保留数据,需要手动清理
  2. recycle:清除数据
  3. delete:删除卷

存储类别:pv可以设置一个存储类别,该pv只能被申请对应类别的pvc绑定,没有设置存储类别的pv只能与不请求任何类别的pvc绑定,一经绑定该pv无法与其他pvc绑定

pvc

apiVersion: v1
kind: PersistentVolumeClaim
metadata: 
  name: pvc  
  namespace: dev
  spec:  
    accessModes: # 访问模式  
    selector: # 采用标签对PV选择  
    storageClassName: # 存储类别,只有设置了类别的pv会被匹配  
    resources: # 请求空间    
      requests:      
        storage: 5Gi

ClusterRole/Role

apiserver支持RBAC的方式来进行授权管理,即根据角色的不同而授予不同的权限类型,角色即为针对某些资源对象的一组权限的集合

  • thanos

last\w确定重启时间

/var/log/messages

dmesg -d -T

journalctl -k --list

journalctl -k -l 重启前的日志号

如果以上都看不出任何问题的话,可能是别的一些棘手的情况,比如这次遇到的就是内核上的bug,messages日志无异常且journalctl丢失重启前的所有日志

某应用的两台机器8088不通,8080通,检查情况

登上这两台机器,互telnet,不通,这两台机器在同一网段,说明不是外部防火墙权限问题。lsof -i查看,8088有应用在监听,iptables --list -n | grep 8088未找到规则,8080的有,vi /etc/sysconfig/iptables ,把8080的规则复制一份给8088加上,重启iptables,问题解决