分类 其它 下的文章

这篇文章是 C 语言系列第三篇,之前两篇见

哦!这该死的 C 语言!

C 语言基础,来喽!

下面我们来介绍一下 C 语言中一个非常重要的概念 - 函数 (function)。首先就要先给函数下一个定义,函数就是完成特定任务的独立代码单元,这也就是说,一个函数肯定是要为了完成某种功能的,比如一个函数它能够执行加法运算,比如一个函数能交换两个数的值,还有一些函数可能只是为了打印某些东西等等。

函数也可以把很多大的任务拆分成一个个小的任务,通过设计每个小的任务来完成一个大的功能。一个设计优良的函数能够把程序中不需要了解的细节隐藏起来,从而使整个程序结构更加清晰,降低程序的修改难度。

C 语言程序由许多小的函数组成,一个程序会被保存在多个源文件中,每个文件可以单独编译,并可以与库中已编译过的函数一起加载。

下面我们通过一个例子来讨论一下函数是如何创建并使用的。

函数创建以及使用

函数的创建和使用会分为三个步骤:

  • 函数原型 ( function type ):这个是创建函数定义,也叫函数声明,能够表明一个文件中有哪些函数。
  • 函数调用 ( function call ):调用函数的位置,函数被定义出来肯定是要使用它的,在哪里使用的这个函数就被称为函数调用。
  • 函数定义 ( function definition ):这个就是函数的具体要干的什么事儿,也就是函数的具体逻辑是什么。

这么一看,函数和变量简直一模一样了,函数需要原型、调用和定义,而变量也需要这些,只不过变量还可以把原型和定义一起表示。

#include <stdio.h>int num; // 变量原型 int sum(int,int); // 函数原型 int main(void){ num = 12; // 变量定义  int num2 = 11; // 函数原型 + 函数定义   int all = sum(num,num2); // 变量使用 ,函数使用  printf("all = %d",all);  return 0;} // 函数定义 int sum(int a,int b){  return a + b;}

上面这段代码很好的列举了变量的定义以及函数的定义。

我们首先定义了一个 num 变量,这个就是变量的原型,然后在 main 函数中使用这个变量,就是变量的定义和使用,当然变量也可以直接使用原型 + 定义的方式( 上面的 num2 ),sum 函数演示了函数的原型、定义和使用。这里注意一点,main 函数比较特殊,它是所有方法的入口,而且 main 函数无需定义原型就能直接使用。

上面这段代码被一起保存在一个文件中,当然你也可以把它们保存在不同的文件中,只不过把它们放在同一个文件中我们在演示的时候比较方便,还有一点就是能够一起进行编译,这两个函数也可以定义在不同的文件中,分别进行编译,这样的好处是使程序更加易于维护,代码读起来更加顺畅,事实上项目中也是采用的单独编译的方式。当然你也可以把所有的功能都写在 main 函数中,只不过这样不易于维护,也不符合项目开发标准。

一个完整的函数定义形同如下:

返回值类型 函数名(参数列表){    函数体(函数的具体功能)}

注意我们上面说的只是一个完整的函数定义,而不是每个函数必须都要有返回值类型、参数列表、函数体,只有函数名是必须的(这个肯定好理解)。

当然也有函数定义出来什么都没有做,这就相当于是一个空函数,C 语言默认是允许空函数出现的,比如下面函数就是一个空函数。

sort(){}

sort 函数不执行任何操作也不返回任何值,这种函数可以在程序开发期间用于保留位置,留待以后再填充代码

程序其实就是一些变量和函数的集合,函数之间的通信可以通过函数参数、返回值来进行,函数通过传递参数,进行一系列的逻辑计算后,把返回值返回回去,以此达到函数交流、通信的目的。

对于函数来说,我们需要了解的两个关键点是参数列表返回值

函数参数

对于上面的 sum 函数来说,它的函数参数有两个,分别是 int 类型的 a 和 b,像这种在函数定义的括号中的变量被称为函数参数,这两个变量 a 和 b 也叫做形式参数,简称形参。

和定义在函数中的变量一样,形式参数也是局部变量,这些都属于函数私有的,作用域范围都是从进入函数开始起作用到函数执行完成后作用结束。

当函数接受参数时,函数原型用逗号分隔的列表指明参数的数量和类型,函数原型中你可以使用下面方式定义。

int sum(int a,int b); // 函数原型

也可以省略具体的变量名称,使用下面这种方式进行定义。

int sum(int,int); // 函数原型

在函数原型中没有定义变量,只是声明了两个 int 类型的参数。

除了形参之外,还有一个叫做实际参数 ( 实参 ) 的概念,就对应于上面代码中的 sum(num,num2),因为在调用 sum 的时候是知道 num 和 num2 的具体值的,像这种在调用函数中对参数进行传值的参数被称为实参。

简单点来说就是 形式参数是被调用函数中的变量,实际参数是调用函数赋给被调函数的具体值。实际参数可以是常量、变量,或甚至是更复杂的表达式。

被调函数不知道也不关心传入的数值是来自常量、变量还是一般表达式。实参在把值传递给函数的时候,其实是把值拷贝给被调函数的形式参数,所以无论被调函数对拷贝数据进行什么操作,都不会影响主调函数中的原始数据。

如下代码所示

#include <stdio.h>int num; // 变量原型 void sum(int,int); // 函数原型 int main(void){ num = 12; // 变量定义  int num2 = 11; // 函数原型 + 函数定义   sum(num,num2); // 变量使用 ,函数使用  printf("num = %d, num2 = %d",num,num2);  return 0;} // 函数定义 void sum(int a,int b){  int sumAll =  a + b; printf("sumAll = %d\n",sumAll); }

从输出结果可以看出,只要把值传递给 sum 后,不论 sum 函数内部进行何种处理,都不会影响 main 函数中 num 和 num2 的值。

函数返回值

我们上面说过函数之间的通信可以通过函数参数、返回值来进行。函数参数的传递方向是由函数调用者 -> 被调函数,而函数返回值的方向是和参数传递的方向相反,也就是被调函数 -> 函数调用者。

图片

当然并不是所有的函数都需要返回值,而且 return 语句后面也不一定需要表达式,当 return 语句后面没有表达式时,函数不会向调用者返回值。返回值会通过

return 表达式

进行返回,这个返回值的表达式类型和函数定义的返回值类型是一致的。

我们还用上面的 sum 函数来举例子

int sum(int a,int b){  return a + b;}

可以看到,sum 函数的表达式返回了 a + b,这其实就是一个表达式。而我们可以看到上面的 int main 方法,它的返回值是 0 ,这就是返回了一个常量。

return 后面可以不返回任何值,只是单独写一个 return 也是允许的,不过这种方式相当于没有返回任何值,所以它的函数类型可以定义为 void ,如下代码所示:

// 函数定义 void sum(int a,int b){  int sumAll =  a + b;  printf("sumAll = %d\n",sumAll);  return ; }

使用 return 语句的另外一个作用是终止函数的执行,强制把控制返回给调用函数,如下代码所示:

// 函数定义 int sum(int a,int b){   int sumAll =  a + b;  printf("sumAll = %d\n",sumAll);    if(a + b > 0){   return sumAll;  }else{    return 0;  }   }

如果 a + b 的值大于 0 的话,会直接返回 a + b 的和,否则为 0 。这个 if 的控制流程就是强制把结果返回给函数调用者。如果在 if 控制流程后面添加代码的话,那么这段代码不会执行,但是编译却没有给出警告。

图片

在 Java 编辑器中,如果最后一行代码出现在 return 强制返回后面的话,编译器会给出警告或者错误提示这行代码不会被执行。

函数类型

这里需要再强调一下函数类型,定义函数的时候需要声明函数的类型,带返回值的函数类型与返回值类型相同,没有返回值的函数应该将其定义为 void 类型。在老版本的 C 编译器中,如果你没有声明函数类型,编译器会默认把函数当做 int 类型来处理,不过这都是早期的事儿了,现在 C 标准不再支持默认函数为 int 类型这种情况。

在编写函数的时候,你就需要考虑好函数的具体功能是什么,也就是这个函数做了哪些事情,需不需要返回值,如果需要返回值的话,它的返回类型是什么。

函数声明

如果大家学过 Java ,可能对 C 这种先声明再定义的方式很不习惯,为什么函数在定义前需要再单独声明一下呢?我直接定义函数不声明行吗?答案肯定是不行的。

这个先声明再使用一直是 C 语言的标准,标准没有为什么,这就是一个标准,但是这个标准却是一个历史遗留问题。

上世纪 70 年代,大部分计算机内存很小,处理速度也比较差,所以导致代码的运行>时间很长,效率很差,这时候进行我们就需要考虑内存占用和编译时间的问题。因为 C 语言开发的比较早,而且 C 又是和硬件直接打交道的,所以提前声明一下函数能够提前分配内存空间,提升效率。说白了还是效率问题。

还有为什么 C 语言不选择采用预编译一下呢?

参考自 https://www.zhihu.com/question/20567689

首先,C语言出现的很早,那时候编译器也是一个很复杂的东西,当时计算机的内存、外存都很小,编译器做的太大也是一个麻烦的事情,所以事先声明就成为一种规范,保留下来,目的是为了让编译器更简单,虽然这一切已经很过时了。

其次,预编译的成本很高,与脚本语言、解释语言不同,C语言项目的规模可以很大,比如操作系统一级的C语言工程,其源文件有几万个,涉及全局符号几十万个,这样规模的项目预编译一次的负担是很高的,如果是整个项目完全扫描一遍,遍历所有全局符号,再进行真正的编译,估计很多码农都会疯了,等待时间会特别长。

再次,C语言是一种静态链接的语言,如果一个项目被设计成只编译,不链接的方式,比如有些库就会被设计成这样,有些合作开发的项目里,组员之间有时候也只提供obj文件,那么某些全局符号可能就不包含在现有的代码里,那么预搜索就一定找不到某些符号,那么该怎么办?如果不提供声明,这个代码就没办法编译了。

基于以上几点考虑,所以C语言才设计成这样,对于开发者而言,不算友好,但也不算很糟糕,甚至在某些方面是有好处的。

对于一个函数来说,它的最终目的就是通过一系列的逻辑处理获得我们想要的结果,逻辑处理离不开各种程序控制语句,比如说 While 、for、do while 等,下面我们就要来讨论一下这些程序控制语句。

程序控制语句

在有些时候的某些程序可能会重复做一件事情,就应该让计算机做这些重复性的工作,这才是我们需要计算机的意义。毕竟,需要重复计算是使用计算机的主要原因。

C 语言中有很多用于重复计算的方法,我们下面先来介绍其中的一种 --- while 循环

while 循环

下面我们通过一段代码来看一下 while 循环的使用。

#include <stdio.h>int main(){ int i = 1; while (i <= 10) {  printf("%d\n", i);  i++; } return 0;}

这段代码首先声明了一个 i 变量,然后使用了 while 循环来判断 i 的值,当 i 的值 <= 10 的时候,就会执行 while 中的循环逻辑,否则即 i > 10 就会直接跳过循环,不会输出任何结果就直接返回 0 。

如果 i 的值在 10 以内,就会循环打印出来 i 的值。这就是 while 循环的作用。

用通俗易懂的语句来描述 while 循环:当某个判断条件为 true 的时候,循环执行 while 中的代码块。

流程图如下:

图片

在 while 循环中的一个关键点就是进入 while 循环的判断,上面代码就是判断 i <= 10 ,这个表达式是关系运算符的一种。

while循环经常依赖测试表达式作比较,这样的表达式被称为关系表达式,出现在关系表达式中间的运算符叫做关系运算符,下表是我们经常使用到的关系运算符。


运算符说明
<小于
<=小于或等于
==等于
>=大于或等于
>大于
!=不等于


这些运算符会不单单会出现在 while 循环中,实际上任何逻辑控制语句都会使用到这几种运算符。

这里需要说明一点,不能用关系运算符来比较字符串,比如 ch != '@' 。

虽然关系运算符可以用来比较浮点数,但是要注意:比较浮点数时,尽量只使用 < 和 > 。因为浮点数的舍入误差会导致在逻辑上应该相等的两数却不相等。例如,3乘以1/3的积是1.0。如果用把1/3表示成小数点后面6位数字,乘积则是 .999999,不等于 1。

for 循环

for 循环一个非常明显的特征就是把三个行为组合在一处,也就是初始化、判断、更新,如下代码所示。

#include <stdio.h>int main(){ for(int i = 1;i <= 10;i++){  printf("%d\n", i); } return 0;}

可以看到,上面代码中 for 循环分别做了三件事情,每个表达式用 ; 进行分隔。

  • int i = 0 相当于是对 i 进行初始化操作;
  • i <= 10 相当于对 i 进行一个逻辑判断,逻辑判断是判断是否进行下一次循环的关键。
  • i++ 相当于是更新 i 的值。

for 循环的一般形式定义如下:

for(表达式1;表达式2;表达式3){    语句;}

这里要注意的是,表达式 1 只在循环开始时执行一次,而表达式 3 是循环结束后再执行。表达式 2 可以省略,省略后默认值为 1,则判断为真,for 循环就会成为一个死循环。

for 循环的流程图如下

图片

do while 循环

一般来说,循环的方式可以分为两种:入口循环和出口循环,什么意思呢?入口循环是先进行循环,再执行每次循环要做的事情,比如上面的 while 循环、for 循环,他们都是先进行判断是否需要进行下一次循环,如果需要的话,才会打印出 i 的值,这就是入口循环。

而出口循环则是要先执行代码,再判断是否要进行下一次循环,即在循环的每次迭代之后检查测试条件,这保证了至少执行循环体中的内容一次,典型的出口循环就是 do ... while。

我们把上面的代码进行修改:

#include <stdio.h>int main(){ int i = 1; do{  printf("%d\n", i);  i++; }while(i <= 10);  return 0;}

从输出结果可以看到,do while 循环在执行完循环体后才执行测试条件,所以 do ... while 循环至少执行循环体一次,而 for 循环和 while 循环在执行循环体之前先执行测试条件,do ... while 的一般形式如下

do 代码while ( 表达式 );

do ... while 循环的流程图如下

图片

到现在为止, C 语言中的程序控制语句我们都了解了,那么该如何进行选择呢?

实际上上面我们已经稍微讨论了一下如何选择的问题了。

while 循环和 for 循环很类似,这两类循环都是先进行一次循环条件的判断,然后再执行具体的循环体操作,只要一次循环条件不满足则一次都不会执行;而 do ... while 循环会至少先进行一次循环,然后才会执行循环判断。

一般来说,使用 for 循环的场景比较多,因为 for 循环形式更加简洁,而且在 for 循环中,变量和判断以及更新的作用域都在循环体内,不会有其他外部代码来修改这些变量,更可控,在 while 和 do ... while 循环中,变量的更新不可控,而且代码也没有 for 循环可读性强。

break 和 continue

break 和 continue 相当于是循环体内领导者的这样一个角色,有了这两个角色存在,循环体内的代码会根据这两个关键字来判断是中断循环还是执行下一次循环。

C 语言中的 break 有两种用法:

  • 一种用法是用在循环体中,当 break 出现在循环体中时,会中断这个循环。
  • 一种用法是用在 switch 语句中,用作中断这个 switch 语句的 case 条件。

break 用于中断循环:如下代码所示

#include <stdio.h>int main(void){ for(int i = 1;i <= 10;i++){  if(i == 5){   break;  }  printf("i 的值 = %d\n",i); }  return 0;}

输出的结果是 i 的值 = 1 - 4, 当 i == 5 时,会进入到 if 判断中,if 判断会直接触发 break,break 用于跳出当前循环,当前是 for 循环,所以 break 会直接跳到 for 循环外面,也就是直接 return ,不会再打印 i 的值。

图片

continue 关键字用于跳过当前循环,执行下一次循环,它和 break 很相似但是有着本质的区别,break 是跳出循环,continue 是执行下一次循环,我们同样拿这个代码来说明,只需要把上面的 break 改成 continue 即可。

#include <stdio.h>int main(void){ for(int i = 1;i <= 10;i++){  if(i == 5){   continue;  }  printf("i 的值 = %d\n",i); }  return 0;}

(这段代码的输出结果会输出出 i = 5 以外的值)

从输出结果可以看出,只有 i = 5 的值没有输出,这也就是说,当代码执行到 i == 5 的时候,会进行 continue 继续执行当前循环,从而跳过这次循环后面的代码,如下图所示。

图片

总结

这篇文章我主要和你聊了聊 C 语言中的函数,函数定义、函数返回值、参数以及程序控制流程中的三类循环的特点以及选型,最后又介绍了一下 break 和 continue 的作用。


Minio是一个开源的分布式文件存储系统,它基于 Golang 编写,虽然轻量,却拥有着不错的高性能,可以将图片、视频、音乐、pdf这些文件存储到多个主机,可以存储到多个Linux,或者多个Windows,或者多个Mac,Minio中存储最大文件可以达到5TB任何类型的文件都是支持的,主要应用在微服务系统中.


图片

一、准备机器


获取最新更新以及文章用到的软件包,请移步点击:查看更新

1、准备四台机器,(minio集群最少四台)。





192.168.223.131 minio-1192.168.223.128 minio-2192.168.223.129 minio-3192.168.223.130 minio-4

2、编辑hosts文件,将以上内容添加到hosts中


vim /etc/hosts

图片

部署(所有机器均执行)

以下的操作都需要在四台机器上执行

3、创建挂载磁盘路径


mkdir -p /data/minio_data/

4、挂载磁盘路径到文件系统

注意:需要将新建的目录挂在到对应的磁盘下,磁盘不挂载好,集群启动会报错,还需要注意的是挂载的文件系统至少要1G不然无法初始化导致集群报错

文件系统 容量 已用 可用 已用% 挂载点












[root@minio-1 minio]# df -h文件系统                                容量  已用  可用 已用% 挂载点devtmpfs                                470M     0  470M    0% /devtmpfs                                   487M     0  487M    0% /dev/shmtmpfs                                   487M  8.4M  478M    2% /runtmpfs                                   487M     0  487M    0% /sys/fs/cgroup/dev/mapper/centos_hadoop--master-root   47G   12G   36G   25% //dev/sda1                              1014M  240M  775M   24% /boottmpfs                                    98M     0   98M    0% /run/user/0tmpfs                                    98M   12K   98M    1% /run/user/42————————————————

5、将上面挂载磁盘路径挂载到相应的文件系统上


mount /dev/sda1 /data/minio_data/

6、查看挂载信息

图片

 7、创建minio目录


cd   /data/minio_data/

  8、下载安装包



wget http://dl.minio.org.cn/server/minio/release/linux-amd64/miniowget https://dl.min.io/client/mc/release/linux-amd64/mc

  9、赋执行权限(根据情况,这里赋全部权限)


chmod +x minio mc

  10、创建启动脚本,编辑run.sh文件


mkdir /data/minio_data && cd /data/minio_data

内容如下:







cat > run.sh <<EOF#!/bin/bashexport MINIO_ACCESS_KEY=minioexport MINIO_SECRET_KEY=Leo825#20210423/usr/local/bin/minio server --address=192.168.81.235:9000 http://192.168.81.235/data/minio_data/data1  http://192.168.81.236/data/minio_data/data1  http://192.168.81.237/data/minio_data/data1 http://192.168.81.234/data/minio_data/data1EOF

11、赋执行权限(根据情况,这里赋全部权限)


chmod 777 /data/minio_data/run.sh

12、创建启动服务,创建minio.service启动脚本


vim /usr/lib/systemd/system/minio.service


内容如下:



















[Unit]Description=Minio serviceDocumentation=https://docs.minio.io/

[Service]#安装包路径WorkingDirectory=/data/minio_data#启动命令路径ExecStart=/data/minio_data/run.sh

Restart=on-failureRestartSec=5

[Install]WantedBy=multi-user.target

复制代码

13、启动测试(所有机器执行)


复制代码

重新加载服务的配置文件


systemctl daemon-reload


启动minio服务


systemctl start minio


查看minio状态












systemctl status minio[root@minio-2 ~]# systemctl status minio● minio.service - Minio serviceLoaded: loaded (/usr/lib/systemd/system/minio.service; disabled; vendor preset: disabled)Active: active (running) since 日 2021-01-31 17:22:54 CST; 17s agoDocs: https://docs.minio.io/Main PID: 2036 (run.sh)Tasks: 8CGroup: /system.slice/minio.service├─2036 /bin/bash /data/minio_data/run.sh└─2039 /data/minio_data server http://192.168.223.232/data/minio_data/data1 http://192.168.223.233/data/minio_data/data1

关闭minio服务


systemctl stop minio

复制代码

14、访问地址

集群中的任何一台机器都可以访问:





http://192.168.223.132:9000/http://192.168.223.133:9000/http://192.168.223.134:9000/http://192.168.223.135:9000/

15、创建测试桶

图片

16、上传测试

图片

17、主机上可以查看到上传的文件


图片


二、nginx配置文件服务器访问

1、执行命令




mc  alias set minio http://192.168.223.132:9000/ minio Leo825#20210423 --api S3v4开启匿名访问mc policy set public minio/sy01

2、web页面开启匿名访问


图片

图片


 3、http访问,sy01是桶名称,方便浏览器访问。




























































upstream minio-server{  server 192.168.6.124:9000 weight=25 max_fails=2 fail_timeout=30s;  server 192.168.6.125:9000 weight=25 max_fails=2 fail_timeout=30s;  server 192.168.6.126:9000 weight=25 max_fails=2 fail_timeout=30s;  server 192.168.6.128:9000 weight=25 max_fails=2 fail_timeout=30s;}

server {  listen 8888;  server_name 192.168.6.120;

  #To allow special characters in headers  ignore_invalid_headers off;  # Allow any size file to be uploaded.  # Set to a value such as 1000m; to restrict file size to a specific value  client_max_body_size 0;  # To disable buffering  proxy_buffering off;

  location /sy01/ {     proxy_set_header X-Real-IP $remote_addr;     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     proxy_set_header X-Forwarded-Proto $scheme;     proxy_set_header Host $http_host;

     proxy_connect_timeout 300;     # Default is HTTP/1, keepalive is only enabled in HTTP/1.1     proxy_http_version 1.1;     proxy_set_header Connection "";     chunked_transfer_encoding off;

     proxy_pass http://minio-server;   }

   location / {     proxy_set_header X-Real-IP $remote_addr;     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     proxy_set_header X-Forwarded-Proto $scheme;     proxy_set_header Host $http_host;

     proxy_connect_timeout 300;     # Default is HTTP/1, keepalive is only enabled in HTTP/1.1     proxy_http_version 1.1;     proxy_set_header Connection "";     chunked_transfer_encoding off;

     proxy_pass http://minio-server; # If you are using docker-compose this would be the hostname i.e. minio     # Health Check endpoint might go here. See https://www.nginx.com/resources/wiki/modules/healthcheck/     # /minio/health/live;   }}

  4、https访问,minio是负载minio服务,sy01是桶名称,方便浏览器访问。
















































upstream minio-server{       server 192.168.6.124:9000 weight=25 max_fails=2 fail_timeout=30s;       server 192.168.6.125:9000 weight=25 max_fails=2 fail_timeout=30s;       server 192.168.6.126:9000 weight=25 max_fails=2 fail_timeout=30s;       server 192.168.6.128:9000 weight=25 max_fails=2 fail_timeout=30s;}

server {    listen 443 ssl;    server_name  192.168.6.120;

    ssl_certificate /etc/nginx/ssl/192.168.6.120.crt;    ssl_certificate_key /etc/nginx/ssl/192.168.6.120.key;

    location /sy01/ {         proxy_set_header X-Real-IP $remote_addr;         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;         proxy_set_header X-Forwarded-Proto $scheme;         proxy_set_header Host $http_host;

         proxy_connect_timeout 300;         proxy_http_version 1.1;         proxy_set_header Connection "";         chunked_transfer_encoding off;         proxy_pass http://minio-server;  }

  location /minio {     proxy_set_header X-Real-IP $remote_addr;     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;     proxy_set_header X-Forwarded-Proto $scheme;     proxy_set_header Host $http_host;

     proxy_connect_timeout 300;     proxy_http_version 1.1;     proxy_set_header Connection "";     chunked_transfer_encoding off;     proxy_pass http://minio-server;  }  }

或者换成tcp代理也可以实现,放在http层外,如果服务器在内网,访问在外网,中间经过了代理,或者隧道之内的,必须要要tcp,否则会http头部hred验证会失败。













upstream minio-server {        server 192.168.178.40:9000     max_fails=3 fail_timeout=30s;        server 192.168.178.41:9000     max_fails=3 fail_timeout=30s;        server 192.168.178.42:9000     max_fails=3 fail_timeout=30s;        server 192.168.178.43:9000     max_fails=3 fail_timeout=30s;}server {        listen 9000;        proxy_connect_timeout 2s;        proxy_timeout 900s;        proxy_pass minio-server;}


企业级架构ELKF集群


图片

服务器

A:10.10.1.3

B:10.10.1.4

C:10.10.1.5

客户端

D:10.10.1.6

下面将用ABC代表服务器了

ABC修改计算机名称




A服务器:hostnamectl set-hostname node1B服务器:hostnamectl set-hostname node2C服务器:hostnamectl set-hostname node3


ADCD更新源并安装java




curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repocurl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repoyum install lrzsz vim java-1.8.0-openjdk java-1.8.0-openjdk-devel-y   java –version -y


ABC都切换到 


cd /usr/local/src/


吧文件下面文件都拷贝到上面的这个目录







apache-zookeeper-3.6.0-bin.tar.gzelasticsearch-7.13.1-x86_64.rpmfilebeat-7.13.1-x86_64.rpmkafka_2.12-2.5.0.tgzkibana-7.13.1-x86_64.rpmlogstash-7.13.1-x86_64.rpm


图片


ABC都要进行更新源,和安装java




curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repocurl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repoyum install lrzsz vim java-1.8.0-openjdk java-1.8.0-openjdk-devel-y   java –version -y


ABC安装ES集群服务,进行连接



cd /usr/local/src/yum localinstall elasticsearch-7.13.1-x86_64.rpm -y


 JVM的内存限制更改,根据实际环境调整,更具服务器内存修改,如果服务器8G,这里可以改为6G,给服务器预留1-2G即可



vi /etc/elasticsearch/jvm.options -Xms4g 改为 -Xmx2g





ES集群实战注意

  集群交互是使用证书交互

  搭建集群前需要先创建证书文件

ES集群交互CA证书创建  A服务器上


/usr/share/elasticsearch/bin/elasticsearch-certutil ca           #一直回车


查看证书位置


ll -rht /usr/share/elasticsearch/elastic-stack-ca.p12


创建证书


/usr/share/elasticsearch/bin/elasticsearch-certutil cert --ca /usr/share/elasticsearch/elastic-stack-ca.p12           #一直回车


查看证书



ll -rht /usr/share/elasticsearch/elastic-certificates.p12cp /usr/share/elasticsearch/elastic-certificates.p12 /etc/elasticsearch/elastic-certificates.p12


证书要求权限修改,不然集群搭建失败



chmod 600 /etc/elasticsearch/elastic-certificates.p12chown elasticsearch:elasticsearch /etc/elasticsearch/elastic-certificates.p12


吧加密ca证书拷贝到BC服务器



scp /etc/elasticsearch/elastic-certificates.p12 10.10.1.4:/etc/elasticsearch/scp /etc/elasticsearch/elastic-certificates.p12 10.10.1.5:/etc/elasticsearch/


图片


ABC服务器对比MD5值是否一致


md5sum /etc/elasticsearch/elastic-certificates.p12


图片


B服务器和C服务器都要执行



chmod 600 /etc/elasticsearch/elastic-certificates.p12chown elasticsearch:elasticsearch /etc/elasticsearch/elastic-certificates.p12


ADC服务器都要进行修改


















vi /etc/elasticsearch/elasticsearch.ymlcluster.name: maixiaolunnode.name: node1   #服务器A就改为nede1,服务器B改为node2,服务器C改为node3node.master: truenode.data: truepath.data: /var/lib/elasticsearchpath.logs: /var/log/elasticsearchnetwork.host: 0.0.0.0http.port: 9200discovery.seed_hosts: ["10.10.1.3", "10.10.1.4", "10.10.1.5"]cluster.initial_master_nodes: ["10.10.1.3", "10.10.1.4", "10.10.1.5"]xpack.security.enabled: truexpack.monitoring.enabled: truexpack.security.transport.ssl.enabled: truexpack.security.transport.ssl.verification_mode: certificatexpack.security.transport.ssl.keystore.path: /etc/elasticsearch/elastic-certificates.p12xpack.security.transport.ssl.truststore.path: /etc/elasticsearch/elastic-certificates.p12


ABC防火墙要放通端口









firewall-cmd --zone=public --add-port=9300/tcp --permanentfirewall-cmd --zone=public --add-port=9300/udp --permanentfirewall-cmd --zone=public --add-port=9200/tcp --permanentfirewall-cmd --zone=public --add-port=9200/udp --permanentfirewall-cmd --zone=public --add-port=5601/tcp --permanentfirewall-cmd --zone=public --add-port=5601/udp --permanentfirewall-cmd --zone=public --add-port=80/tcp --permanentfirewall-cmd --zone=public --add-port=3000/tcp --permanent

重新载入


firewall-cmd --reload


图片


图片


图片


ABC服务器顺序启动ES服务



systemctl enable elasticsearchsystemctl restart elasticsearch


继续看第一台服务器日志


图片


出现valid,表示成功了

接下来是查看3台集群中间的互动



yum -y install net-tools  安装 netstatnetstat -anp |grep 10.0.0.20:9300   查看集群之间的交互


检查9300端口连接情况


图片


确认集群中所有es的日志正常再设置密码maixiaolun..123

在主服务器(服务器A,想把那个当主服务器就在那个服务器上运行)上运行

ES设置密码/usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive


图片


ES设置随机密码/usr/share/elasticsearch/bin/elasticsearch-setup-passwords auto

验证集群是否成功,标记为*的为master节点。网页访问或者curl访问

http://10.10.1.3:9200    账号:elastic   密码:刚刚设置的

http://10.10.1.3:9200/_cat/nodes?v    //查看节点信息  带*的就是主的


图片


http://xxx:9200/_cat/indices?v

命令方式查看 输入账号密码

curl -u elastic:maixiaolun..123 http://10.10.1.3:9200/_cat/nodes?v


图片


B服务器安装Kibana (因为是前端,蹦了就蹦了,不要紧,所有不需要每个地方都搭建来)

B服务器安装



cd /usr/local/src/yum localinstall kibana-7.13.1-x86_64.rpm -y


图片


Kibana配置连接ES集群








vi /etc/kibana/kibana.ymlserver.port: 5601server.host: "0.0.0.0"elasticsearch.hosts: ["http://10.10.1.3:9200", "http://10.10.1.4:9200", "http://10.10.1.5:9200"]elasticsearch.username: "elastic"elasticsearch.password: "maixiaolun..123"logging.dest: /tmp/kibana.log



B服务器Kibana的启动和访问



systemctl enable kibanasystemctl restart kibana



Kibana监控开启


netstat -tulnp


图片


http://10.10.1.3:5601

账号:elastic

密码:maixiaolun..123


BC服务器安装logstash日志分析 (就是蹦了一台也不要紧,此做法为高可用做法,安装一台的话也是可以的)



cd /usr/local/src/yum localinstall logstash-7.13.1-x86_64.rpm -y


图片


内存配置jvm.options 1g,根据自己实际情况改


vi /etc/logstash/jvm.options

配置logstash配置文件



































vi /etc/logstash/conf.d/logstash.conf#监听5044传来的日志input {   beats {   host => '0.0.0.0'   port => 5044 }}#这里是正则表达式,分析日志filter {grok {match => {"message" => '%{IP:remote_addr} - (%{WORD:remote_user}|-) [%{HTTPDATE:time_local}] "%{WORD:method} %{NOTSPACE:request} HTTP/%{NUMBER}" %{NUMBER:status} %{NUMBER:body_bytes_sent} %{QS} %{QS:http_user_agent}'}remove_field => ["message"]}date {match => ["time_local", "dd/MMM/yyyy:HH:mm:ss Z"]target => "@timestamp"}}

#发送给所有ES服务器output {elasticsearch {hosts => ["http://10.10.1.3:9200", "http://10.10.1.4:9200", "http://10.10.1.5:9200"]user => "elastic"password => "maixiaolun..123"index => "mxlnginx-%{+YYYY.MM.dd}"}}


启动Logstash,配置重载:kill -1 pid



systemctl restart logstashsystemctl enable logstash


等待启动,查看日志

/var/log/logstash   日志生成的地方


tail -f logstash-plain.log


图片


ZK集群部署

所有服务器都要进行安装







cd /usr/local/src/tar -zxvf apache-zookeeper-3.6.0-bin.tar.gzmv apache-zookeeper-3.6.0-bin /usr/local/zookeepermkdir -pv /usr/local/zookeeper/datacd /usr/local/zookeeper/confcp zoo_sample.cfg zoo.cfg

  zk集群至少需要三台机器

集群配置zoo.cfg 












vi zoo.cfgtickTime=2000initLimit=10syncLimit=5dataDir=/usr/local/zookeeper/dataclientPort=2181autopurge.snapRetainCount=3autopurge.purgeInterval=1server.1=10.10.1.3:2888:3888server.2=10.10.1.4:2888:3888server.3=10.10.1.5:2888:3888


更改zk集群的id


vi /usr/local/zookeeper/data/myid

第一台就填写1

第二台就填写2

第三台就填写3


图片


图片

分别为1 2 3

systemctl管理











vi /usr/lib/systemd/system/zookeeper.service[Unit]Description=zookeeperAfter=network.target[Service]Type=forkingExecStart=/usr/local/zookeeper/bin/zkServer.sh startUser=root[Install]WantedBy=multi-user.target


启动zk



systemctl enable zookeepersystemctl restart zookeeper


启动zk集群查看状态




cd /usr/local/zookeeper/bin/./zkServer.sh start./zkServer.sh status


图片

报错的话,检查防火墙。

正确显示为下


图片

Kafka集群部署

下载地址:http://kafka.apache.org/downloads

安装



cd /usr/local/src/tar -zxvf kafka_2.12-2.5.0.tgz -C /usr/local/

  Jvm内存修改/usr/local/kafka_2.12-2.5.0/bin/kafka-server-start.sh,根据实际情况修

改,每台服务器都要改


图片


修改Kafka配置server.properties



cd /usr/local/kafka_2.12-2.5.0/config/vi server.properties


图片













broker.id=0listeners=PLAINTEXT://xxx:9092 log.retention.hours=1  #根据实际情况修改  日志保留时间zookeeper.connect=xxx:2181,xxx:2181,xxx:2181zookeeper.connection.timeout.ms=18000 改为 zookeeper.connection.timeout.ms=60000拷贝到其他服务器上scp server.properties 10.0.0.21:/usr/local/kafka_2.12-2.5.0/config/然后在新的服务器上vi /usr/local/kafka_2.12-2.5.0/config/server.properties改listeners=PLAINTEXT://xxx:9092 对应当前服务器的ipbroker.id=0 改为broker.id=1

如果启动的时候报如下错误,是因为链接超时,吧60000改为 1200000也就是2分钟


图片


Kafka使用systemctl管理











vi /usr/lib/systemd/system/kafka.service[Unit]Description=kafkaAfter=network.target[Service]Type=simpleExecStart=/usr/local/kafka_2.12-2.5.0/bin/kafka-server-start.sh /usr/local/kafka_2.12-2.5.0/config/server.propertiesUser=root[Install]WantedBy=multi-user.target

Kafka启动



systemctl enable kafka systemctl restart kafka


查看kafka日志



cd /usr/local/kafka_2.12-2.5.0/logstail -f server.log


图片


创建topic,创建成功说明kafka集群搭建成功

--replication-factor 3 代表3台服务器



cd /usr/local/kafka_2.12-2.5.0/bin/./kafka-topics.sh --create --zookeeper 10.10.1.4:2181 --replication-factor 3 --partitions 1 --to


图片


logstash获取kafka数据 


vi /etc/logstash/conf.d/logstash.conf


#获取kafka的日志






















































input { kafka {   bootstrap_servers => "10.10.1.3:9092,10.10.1.4:9092,10.10.1.5:9092"   topics => ["mxlkafka"]   group_id => "mxlgroup"   codec => "json" }}





filter { mutate {   remove_field => ["agent","ecs","log","input","[host][os]"] }}



#正则表达式分析日志filter {grok {match => {"message" => '%{IP:remote_addr} - (%{WORD:remote_user}|-) [%{HTTPDATE:time_local}] "%{WORD:method} %{NOTSPACE:request} HTTP/%{NUMBER}" %{NUMBER:status} %{NUMBER:body_bytes_sent} %{QS} %{QS:http_user_agent}'}remove_field => ["message"]}date {match => ["time_local", "dd/MMM/yyyy:HH:mm:ss Z"]target => "@timestamp"}}



#吧分析过后的日志发送给ESoutput {elasticsearch {hosts => ["http://10.10.1.3:9200", "http://10.10.1.4:9200", "http://10.10.1.5:9200"]user => "elastic"password => "maixiaolun..123"index => "mxlnginx-%{+YYYY.MM.dd}"     #这里代表日志的名字,不通服务器可以写不通的名字}}

重载



ps auxfww | grep logstashkill -1 804


图片


看日志

cd /var/log/logstash/

tail -f logstash-plain.log


图片


测试环境是否正常

效果同ELFK,但架构做了优化

Kafka查看队列信息,验证新架构是否生效




cd /usr/local/kafka_2.12-2.5.0/bin/./kafka-consumer-groups.sh --bootstrap-server 10.10.1.4:9092 --list./kafka-consumer-groups.sh --bootstrap-server 10.10.1.4:9092 --group mxlgroup -describe


  LOG-END-OFFSET不断增大,LAG不堆积说明架构生效


图片


LOG-END-OFFSET 一直增加就代表kafka在消费

LAG 一直在堆积

Logstash扩展

  配置保持一致,启动即可




客户端D进行安装nginx进行测试数据是否传入服务器集群

安装nginx做测试,如果有其他服务可以直接测试



yum install nginx -ysystemctl start nginx


安装filebeat,用于发送日志到服务器



cd /usr/local/src/yum localinstall filebeat-7.13.1-x86_64.rpm -y




配置filebeat进行监控nginx



















vi /etc/filebeat/filebeat.ymlfilebeat.inputs:- type: log  tail_files: true  backoff: "1s"  paths:     - /var/log/nginx/access.log

processors:- drop_fields:   fields: ["agent","ecs","log","input"]

output:  kafka:    hosts: ["10.10.1.3:9092", "10.10.1.4:9092", "10.10.1.5:9092"]    topic: mxlkafka   #这里代表日志的名字,不通服务器可以写不通的名字


启动filebeat



systemctl start filebeatsystemctl enable filebeat


登入B服务器网页

http://10.10.1.4:5601

账号:elastic

密码:maixiaolun..123


打开Stack Management


图片



添加日志数据 Index Patterns


图片


添加


图片



输入刚刚设置的名字 mxlnginx* 点击下一步

图片


选择@timestamp  下一步


图片


这时候就能看到添加的内容了



图片



访问客户端地址nginx页面 10.10.1.6  多刷新几次


图片

就可以去看数据了


图片



看到日志已经上来了



图片


读者可在公众号后台回复关键字 ELKF集群7搭建 获取二进制安装包


1、日志

在Kubernetes或者说Docker中都可以查看容器的日志,但如果直接通过命令行的方式去看的话会很麻烦,要不断的去敲命令,去找容器名称,很不方便操作!

在没有使用容器的项目中,我们也知道有ELK这一套组合拳是专门用来操作日志的,那K8S肯定也是可以用它们来进行日志收集、查看的。

1.1 日志查看

1.1.1 命令方式

1.1.1.1 docker

  • docker ps 查看 container id

    image.png

  • docker logs container_id 查看日志

    image.png

1.1.1.2 Kubernetes命令

  • kubectl 命令查看

    image.png

    • kubectl logs -f <pod-name> [-c <container-name>]

  • Pod日志

    image.png

    • kubectl describe pod <pod-name>

      kubectl describe除了能够查看pod的日志信息,还能查看比如Node、RC、Service、Namespace等信息。

      注意 :要是想查看指定命名空间之下的,可以-n=namespace

  • 组件服务级别

    比如kube-apiserver、kube-schedule、kubelet、kube-proxy、kube-controller-manager等都可以使用journalctl进行查看

    如果K8S中出现问题了,一般就可以通过这些命令来查看是不是组件出异常了

    image.png

1.1.2 ELK

Elasticsearch搜索引擎与名为Logstash的数据收集和日志解析引擎以及名为Kibana的分析和可视化平台一起开发。这三个产品被设计成一个集成解决方案,称为 Elastic Stack(ELK Stack)

日志采集可以有多种方式选择,ELK中的Logstash可以由其他具有相同功能的多个组件替换,如Logpilot、fluentd等,本章节我们采用 Logpilot 进行演示。

1.1.2.1 结构图

  • Pod中的日志通过mount挂载到宿主机中的某个目录

  • Logpilot采集这个目录下日志之后交给Elasticsearch去做搜索引擎的规则

  • 最后通过Kibana做可视化的展示

image.png

1.1.2.2 部署ELK

1.1.2.2.1 部署LogPilot
  • 准备YAML文件

    由于环境被我弄坏了,我这边重新安装了K8S集群,并且采用了最新版的1.18.2

    下面的yaml文件可能会不兼容旧版本,如果不兼容,大家网上搜一份修改下

    apiVersion: apps/v1kind: DaemonSet                   # 日志采集需要在所有节点都运行,所以采用DaemonSet资源metadata:
      name: log-pilot  namespace: kube-system  labels:
        k8s-app: log-pilot    kubernetes.io/cluster-service: "true"spec:
      selector: 
        matchLabels: 
          k8s-app: log-es      kubernetes.io/cluster-service: "true"
          version: v1.22  template:
        metadata:
          labels:
            k8s-app: log-es        kubernetes.io/cluster-service: "true"
            version: v1.22    spec:
          tolerations:                # 这里的配置表示允许将资源部署到Master节点中
          - key: node-role.kubernetes.io/master        effect: NoSchedule      containers:
          - name: log-pilot        image: registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.5-filebeat        resources:                # 资源限制
              limits: 
                memory: 200Mi          requests:
                cpu: 100m            memory: 200Mi        env:                  # 定义与Elasticsearch通信的一些环境变量
              - name: "FILEBEAT_OUTPUT"
                value: "elasticsearch"
              - name: "ELASTICSEARCH_HOST"
                value: "elasticsearch-api"
              - name: "ELASTICSEARCH_PORT"
                value: "9200"
              - name: "ELASTICSEARCH_USER"
                value: "elastic"
              - name: "ELASTICSEARCH_PASSWORD"
                value: "elastic"
            volumeMounts:         # 挂载日志目录
            - name: sock          mountPath: /var/run/docker.sock        - name: root          mountPath: /host          readOnly: true
            - name: varlib          mountPath: /var/lib/filebeat        - name: varlog          mountPath: /var/log/filebeat        securityContext:
              capabilities:
                add:
                - SYS_ADMIN      terminationGracePeriodSeconds: 30
          volumes:
          - name: sock        hostPath:
              path: /var/run/docker.sock      - name: root        hostPath:
              path: /      - name: varlib        hostPath:
              path: /var/lib/filebeat          type: DirectoryOrCreate      - name: varlog        hostPath:
              path: /var/log/filebeat          type: DirectoryOrCreate
    • log-pilot.yaml

  • 创建资源

    [root@master-kubeadm-k8s log]# kubectl apply -f log-pilot.yamldaemonset.extensions/log-pilot created
  • 查看资源

    [root@master-kubeadm-k8s log]# kubectl get pods -n kube-system -o wide | grep loglog-pilot-8f4nv                              1/1     Running            0          2m4s   192.168.221.88    worker02-kubeadm-k8s   <none>           <none>log-pilot-h25fc                              1/1     Running            0          2m4s   192.168.16.250    master-kubeadm-k8s     <none>           <none>
        [root@master-kubeadm-k8s log]# kubectl get daemonset -n kube-systemNAME          DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGEcalico-node   3         3         3       3            3           beta.kubernetes.io/os=linux   41d
    kube-proxy    3         3         3       3            3           <none>                        41d
    log-pilot     2         2         2       2            2           <none>                        26s
1.1.2.2.2 部署Elasticsearch
  • 准备YAML文件

    注意这里的资源要求还是比较高的,自己注意下系统资源是否够用

    apiVersion: v1kind: Service             # 这里的service是为外部访问ElasticSearch提供metadata:
      name: elasticsearch-api # 这里的名称要与logPilot中的ElasticHost环境变量一致
      namespace: kube-system  
      labels:
        name: elasticsearchspec:
      selector:
        app: es  ports:
      - name: transport    port: 9200
        protocol: TCP---apiVersion: v1kind: Service             # # 这里的service是为ElasticSearch集群之间通信提供的metadata:
      name: elasticsearch-discovery  namespace: kube-system  labels:
        name: elasticsearchspec:
      selector:
        app: es  ports:
      - name: transport    port: 9300
        protocol: TCP---apiVersion: apps/v1kind: StatefulSet         # 希望Elasticsearch是有序启动的,所以使用StatefulSetmetadata:
      name: elasticsearch  namespace: kube-system  labels:
        kubernetes.io/cluster-service: "true"spec:
      replicas: 3
      serviceName: "elasticsearch-service"
      selector:
        matchLabels:
          app: es  template:
        metadata:
          labels:
            app: es    spec:
          tolerations:            # 同样elasticsearch也部署到Master节点中
          - effect: NoSchedule        key: node-role.kubernetes.io/master      initContainers:     # 这里是指在创建容器之前要先进行初始化操作
          - name: init-sysctl        image: busybox:1.27
            command:
            - sysctl        - -w        - vm.max_map_count=262144        securityContext:
              privileged: true
          containers:
          - name: elasticsearch        image: registry.cn-hangzhou.aliyuncs.com/log-monitor/elasticsearch:v5.5.1        ports:
            - containerPort: 9200
              protocol: TCP        - containerPort: 9300
              protocol: TCP        securityContext:
              capabilities:
                add:
                  - IPC_LOCK              - SYS_RESOURCE        resources:
              limits:
                memory: 4000Mi          requests:
                cpu: 100m            memory: 2000Mi        env:
              - name: "http.host"
                value: "0.0.0.0"
              - name: "network.host"
                value: "_eth0_"
              - name: "cluster.name"
                value: "docker-cluster"
              - name: "bootstrap.memory_lock"
                value: "false"
              - name: "discovery.zen.ping.unicast.hosts"
                value: "elasticsearch-discovery"
              - name: "discovery.zen.ping.unicast.hosts.resolve_timeout"
                value: "10s"
              - name: "discovery.zen.ping_timeout"
                value: "6s"
              - name: "discovery.zen.minimum_master_nodes"
                value: "2"
              - name: "discovery.zen.fd.ping_interval"
                value: "2s"
              - name: "discovery.zen.no_master_block"
                value: "write"
              - name: "gateway.expected_nodes"
                value: "2"
              - name: "gateway.expected_master_nodes"
                value: "1"
              - name: "transport.tcp.connect_timeout"
                value: "60s"
              - name: "ES_JAVA_OPTS"
                value: "-Xms2g -Xmx2g"
            livenessProbe:                    # 健康检查
              tcpSocket:
                port: transport          initialDelaySeconds: 20
              periodSeconds: 10
            volumeMounts:
            - name: es-data          mountPath: /data      terminationGracePeriodSeconds: 30
          volumes:
          - name: es-data        hostPath:
              path: /es-data
    • elasticsearch.yaml

  • 创建资源

    [root@master-kubeadm-k8s log]# kubectl apply -f elasticsearch.yamlservice/elasticsearch-api created
    service/elasticsearch-discovery created
    statefulset.apps/elasticsearch created
  • 查看资源

    # Pod会进行有序的创建[root@master-kubeadm-k8s log]# kubectl get pods -n kube-system -o wide | grep elasticelasticsearch-0                              1/1     Running           0          4m36s   10.244.221.69    worker02- kubeadm-k8s   <none>           <none>elasticsearch-1                              1/1     Running           0          4m33s   10.244.14.4      worker01-kubeadm-k8s   <none>           <none>elasticsearch-2                              0/1     PodInitializing   0          101s    10.244.16.194    master-kubeadm-k8s     <none>           <none>[root@master-kubeadm-k8s log]# kubectl get svc -n kube-system -o wide | grep elasticelasticsearch-api         ClusterIP   10.104.144.183   <none>        9200/TCP                 5m2s   app=es
    elasticsearch-discovery   ClusterIP   10.109.137.36    <none>        9300/TCP                 5m2s   app=es[root@master-kubeadm-k8s log]# kubectl get statefulset -n kube-systemNAME            READY   AGEelasticsearch   3/3     6m14s
1.1.2.2.3 部署Kibana

kibana主要是对外提供访问的,所以这边需要配置Service和Ingress
前提:要有Ingress Controller的支持,比如Nginx Controller

  • 准备YAML文件

    • kibana.yaml

      # DeploymentapiVersion: apps/v1kind: Deploymentmetadata:
        name: kibana  namespace: kube-system  labels:
          component: kibanaspec:
        replicas: 1
        selector:
          matchLabels:
           component: kibana  template:
          metadata:
            labels:
              component: kibana    spec:
            containers:
            - name: kibana        image: registry.cn-hangzhou.aliyuncs.com/log-monitor/kibana:v5.5.1        env:
              - name: CLUSTER_NAME          value: docker-cluster        - name: ELASTICSEARCH_URL   # elasticsearch 地址
                value: http://elasticsearch-api:9200/        resources:
                limits:
                  cpu: 1000m          requests:
                  cpu: 100m        ports:
              - containerPort: 5601
                name: http---# ServiceapiVersion: v1kind: Servicemetadata:
        name: kibana  namespace: kube-system  labels:
          component: kibanaspec:
        selector:
          component: kibana  ports:
        - name: http    port: 80
          targetPort: http---# IngressapiVersion: extensions/v1beta1kind: Ingressmetadata:
        name: kibana  namespace: kube-systemspec:
        rules:
        - host: log.k8s.sunny.com     # 本地hosts配置域名
          http:
            paths:
            - path: /        backend:
                serviceName: kibana          servicePort: 80
  • 创建资源

    [root@master-kubeadm-k8s log]# kubectl apply -f kibana.yamldeployment.apps/kibana created
    service/kibana created
    ingress.extensions/kibana created
  • 查看资源

    [root@master-kubeadm-k8s log]# kubectl get pods -n kube-system | grep kibanakibana-8747dff7d-l627g                       1/1     Running   0          2m2s[root@master-kubeadm-k8s log]# kubectl get svc  -n kube-system | grep kibanakibana                    ClusterIP   10.109.177.214   <none>        80/TCP                   2m40s[root@master-kubeadm-k8s log]# kubectl get ingress  -n kube-system | grep kibanakibana   <none>   log.k8s.sunny.com             80      2m43s
  • 测试

image.png

2、监控

2.1 Prometheus简介

这里的监控是指监控K8S集群的健康状态,包括节点、K8S组件以及Pod都会监控。

Prometheus 是一个开源的监控和警报系统,它直接从目标主机上运行的代理程序中抓取指标,并将收集的样本集中存储在其服务器上。

2016 年 Prometheus 成为继 Kubernetes 之后,成为 CNCF (Cloud Native Computing Foundation)中的第二个项目成员。

2.1.2 主要功能

  • 多维 数据模型(时序由 metric 名字和 k/v 的 labels 构成)。

  • 灵活的查询语句(PromQL)。

  • 无依赖存储,支持 local 和 remote 不同模型。

  • 采用 http 协议,使用 pull 模式,拉取数据,简单易懂。

  • 监控目标,可以采用服务发现或静态配置的方式。

  • 支持多种统计数据模型,图形化友好。

2.1.3 Prometheus架构

image.png

从这个架构图,也可以看出 Prometheus 的主要模块包含:Server、Exporters、Pushgateway、PromQL、Alertmanager、WebUI 等。

它大致使用逻辑是这样:

  • Prometheus server 定期从静态配置的 target 或者服务发现的 target 拉取数据。

  • 当新拉取的数据大于配置内存缓存区的时候,Prometheus 会将数据持久化到磁盘(如果使用 remote storage 将持久化到云端)。

  • Prometheus 可以配置 rule,然后定时查询数据,当条件触发的时候,会将 alert 推送到配置的 Alertmanager。

  • Alertmanager 收到警告的时候,可以根据配置,聚合、去重、降噪,最后发送警告。

  • 可以使用 API、Prometheus Console 或者 Grafana 查询和聚合数据。

2.1.4 Prometheus知识普及

  • 支持pull、push数据添加方式

  • 支持k8s服务发现

  • 提供查询语言PromQL

  • 时序(time series)是由名字(Metric)以及一组key/value标签定义的数据类型

2.2 数据采集

  • 服务器数据

    • 在每一个节点中部署工具:Node-Exporter

  • K8S组件数据

    • IP:2379/metrics 拿到ETCD数据

    • IP:6443/metrics 拿到apiServer数据

    • IP:10252/metrics 拿到controller manager数据

    • IP:10251/metrics 拿到scheduler数据

    • 访问集群中不同端口下的metrics接口即可拿到

    • 比如

  • 容器数据

    • 在kubelet中有个cAdvisor组件,默认就可以拿到容器的数据

2.2.1 服务器数据采集

Prometheus可以从Kubernetes集群的各个组件中采集数据,比如kubelet中自带的cAdvisor,api-server等,而node-export就是其中一种来源。

Exporter是Prometheus的一类数据采集组件的总称。它负责从目标处搜集数据,并将其转化为Prometheus支持的格式。

服务器的指标采集是通过 Node-Exporter 进行采集,比如服务器CPU、内存、磁盘、I/O等信息

image.png

2.2.1.1 部署NodeExporter

  • 准备YAML文件

    • namespace.yaml

      我们将监控的资源都放到这个命名空间下,方便管理

      apiVersion: v1kind: Namespacemetadata: 
        name: ns-monitor  labels:
          name: ns-monitor
    • node-exporter.yaml

      kind: DaemonSetapiVersion: apps/v1metadata: 
        labels:
          app: node-exporter  name: node-exporter  namespace: ns-monitorspec:
        revisionHistoryLimit: 10
        selector:
          matchLabels:
            app: node-exporter  template:
          metadata:
            labels:
              app: node-exporter    spec:
            containers:
              - name: node-exporter          image: prom/node-exporter:v0.16.0          ports:
                  - containerPort: 9100
                    protocol: TCP              name: http      hostNetwork: true
            hostPID: true
            tolerations:
              - effect: NoSchedule          operator: Exists---# 本来NodeExporter是不需要对外提供访问的,但我们这里一步一步来,先保证每一步都正确再往后进行kind: ServiceapiVersion: v1metadata:
        labels:
          app: node-exporter  name: node-exporter-service  namespace: ns-monitorspec:
        ports:
          - name: http      port: 9100
            nodePort: 31672
            protocol: TCP  type: NodePort  selector:
          app: node-exporter
  • 创建资源

    [root@master-kubeadm-k8s prometheus]# kubectl apply -f node-exporter.yamldaemonset.apps/node-exporter created
    service/node-exporter-service created
  • 查看资源

    [root@master-kubeadm-k8s prometheus]# kubectl get pods -n ns-monitorNAME                  READY   STATUS    RESTARTS   AGEnode-exporter-dsjbq   1/1     Running   0          2m32s
    node-exporter-mdnrj   1/1     Running   0          2m32s
    node-exporter-sxwxx   1/1     Running   0          2m32s[root@master-kubeadm-k8s prometheus]# kubectl get svc -n ns-monitorNAME                    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGEnode-exporter-service   NodePort   10.109.226.6   <none>        9100:31672/TCP   2m46s
  • 测试

    image.png

2.2.1 部署Prometheus

  • 准备YAML文件

    • prometheus.yaml

      prometheus的yaml文件很值得学习,很多常用的资源类型这里都用到了

      apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:
        name: prometheusrules:
        - apiGroups: [""] # "" indicates the core API group
          resources:
            - nodes      - nodes/proxy      - services      - endpoints      - pods    verbs:
            - get      - watch      - list  - apiGroups:
            - extensions    resources:
            - ingresses    verbs:
            - get      - watch      - list  - nonResourceURLs: ["/metrics"]
          verbs:
            - get---apiVersion: v1kind: ServiceAccountmetadata:
        name: prometheus  namespace: ns-monitor  labels:
          app: prometheus---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:
        name: prometheussubjects:
        - kind: ServiceAccount    name: prometheus    namespace: ns-monitorroleRef:
        kind: ClusterRole  name: prometheus  apiGroup: rbac.authorization.k8s.io---apiVersion: v1kind: ConfigMapmetadata:
        name: prometheus-conf  namespace: ns-monitor  labels:
          app: prometheusdata:
        prometheus.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']
            - job_name: 'grafana'
              static_configs:
                - targets:
                    - 'grafana-service.ns-monitor:3000'
      
            - job_name: 'kubernetes-apiservers'
      
              kubernetes_sd_configs:
              - role: endpoints        # Default to scraping over https. If required, just disable this or change to
              # `http`.
              scheme: https        # This TLS & bearer token file config is used to connect to the actual scrape
              # endpoints for cluster components. This is separate to discovery auth
              # configuration because discovery & scraping are two separate concerns in
              # Prometheus. The discovery auth config is automatic if Prometheus runs inside
              # the cluster. Otherwise, more config options have to be provided within the
              # <kubernetes_sd_config>.
              tls_config:
                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt          # If your node certificates are self-signed or use a different CA to the
                # master CA, then disable certificate verification below. Note that
                # certificate verification is an integral part of a secure infrastructure
                # so this should only be disabled in a controlled environment. You can
                # disable certificate verification by uncommenting the line below.
                #
                # insecure_skip_verify: true
              bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token        # Keep only the default/kubernetes service endpoints for the https port. This
              # will add targets for each API server which Kubernetes adds an endpoint to
              # the default/kubernetes service.
              relabel_configs:
              - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
                action: keep          regex: default;kubernetes;https      # Scrape config for nodes (kubelet).
            #
            # Rather than connecting directly to the node, the scrape is proxied though the
            # Kubernetes apiserver.  This means it will work if Prometheus is running out of
            # cluster, or can't connect to nodes for some other reason (e.g. because of
            # firewalling).
            - job_name: 'kubernetes-nodes'
      
              # Default to scraping over https. If required, just disable this or change to
              # `http`.
              scheme: https        # This TLS & bearer token file config is used to connect to the actual scrape
              # endpoints for cluster components. This is separate to discovery auth
              # configuration because discovery & scraping are two separate concerns in
              # Prometheus. The discovery auth config is automatic if Prometheus runs inside
              # the cluster. Otherwise, more config options have to be provided within the
              # <kubernetes_sd_config>.
              tls_config:
                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token        kubernetes_sd_configs:
              - role: node        relabel_configs:
              - action: labelmap          regex: __meta_kubernetes_node_label_(.+)        - target_label: __address__          replacement: kubernetes.default.svc:443
              - source_labels: [__meta_kubernetes_node_name]
                regex: (.+)          target_label: __metrics_path__          replacement: /api/v1/nodes/${1}/proxy/metrics      # Scrape config for Kubelet cAdvisor.
            #
            # This is required for Kubernetes 1.7.3 and later, where cAdvisor metrics
            # (those whose names begin with 'container_') have been removed from the
            # Kubelet metrics endpoint.  This job scrapes the cAdvisor endpoint to
            # retrieve those metrics.
            #
            # In Kubernetes 1.7.0-1.7.2, these metrics are only exposed on the cAdvisor
            # HTTP endpoint; use "replacement: /api/v1/nodes/${1}:4194/proxy/metrics"
            # in that case (and ensure cAdvisor's HTTP server hasn't been disabled with
            # the --cadvisor-port=0 Kubelet flag).
            #
            # This job is not necessary and should be removed in Kubernetes 1.6 and
            # earlier versions, or it will cause the metrics to be scraped twice.
            - job_name: 'kubernetes-cadvisor'
      
              # Default to scraping over https. If required, just disable this or change to
              # `http`.
              scheme: https        # This TLS & bearer token file config is used to connect to the actual scrape
              # endpoints for cluster components. This is separate to discovery auth
              # configuration because discovery & scraping are two separate concerns in
              # Prometheus. The discovery auth config is automatic if Prometheus runs inside
              # the cluster. Otherwise, more config options have to be provided within the
              # <kubernetes_sd_config>.
              tls_config:
                ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token        kubernetes_sd_configs:
              - role: node        relabel_configs:
              - action: labelmap          regex: __meta_kubernetes_node_label_(.+)        - target_label: __address__          replacement: kubernetes.default.svc:443
              - source_labels: [__meta_kubernetes_node_name]
                regex: (.+)          target_label: __metrics_path__          replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor      # Scrape config for service endpoints.
            #
            # The relabeling allows the actual service scrape endpoint to be configured
            # via the following annotations:
            #
            # * `prometheus.io/scrape`: Only scrape services that have a value of `true`
            # * `prometheus.io/scheme`: If the metrics endpoint is secured then you will need
            # to set this to `https` & most likely set the `tls_config` of the scrape config.
            # * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
            # * `prometheus.io/port`: If the metrics are exposed on a different port to the
            # service then set this appropriately.
            - job_name: 'kubernetes-service-endpoints'
      
              kubernetes_sd_configs:
              - role: endpoints        relabel_configs:
              - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
                action: keep          regex: true
              - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
                action: replace          target_label: __scheme__          regex: (https?)        - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
                action: replace          target_label: __metrics_path__          regex: (.+)        - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
                action: replace          target_label: __address__          regex: ([^:]+)(?::\d+)?;(\d+)          replacement: $1:$2        - action: labelmap          regex: __meta_kubernetes_service_label_(.+)        - source_labels: [__meta_kubernetes_namespace]
                action: replace          target_label: kubernetes_namespace        - source_labels: [__meta_kubernetes_service_name]
                action: replace          target_label: kubernetes_name      # Example scrape config for probing services via the Blackbox Exporter.
            #
            # The relabeling allows the actual service scrape endpoint to be configured
            # via the following annotations:
            #
            # * `prometheus.io/probe`: Only probe services that have a value of `true`
            - job_name: 'kubernetes-services'
      
              metrics_path: /probe        params:
                module: [http_2xx]
      
              kubernetes_sd_configs:
              - role: service        relabel_configs:
              - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_probe]
                action: keep          regex: true
              - source_labels: [__address__]
                target_label: __param_target        - target_label: __address__          replacement: blackbox-exporter.example.com:9115
              - source_labels: [__param_target]
                target_label: instance        - action: labelmap          regex: __meta_kubernetes_service_label_(.+)        - source_labels: [__meta_kubernetes_namespace]
                target_label: kubernetes_namespace        - source_labels: [__meta_kubernetes_service_name]
                target_label: kubernetes_name      # Example scrape config for probing ingresses via the Blackbox Exporter.
            #
            # The relabeling allows the actual ingress scrape endpoint to be configured
            # via the following annotations:
            #
            # * `prometheus.io/probe`: Only probe services that have a value of `true`
            - job_name: 'kubernetes-ingresses'
      
              metrics_path: /probe        params:
                module: [http_2xx]
      
              kubernetes_sd_configs:
                - role: ingress        relabel_configs:
                - source_labels: [__meta_kubernetes_ingress_annotation_prometheus_io_probe]
                  action: keep            regex: true
                - source_labels: [__meta_kubernetes_ingress_scheme,__address__,__meta_kubernetes_ingress_path]
                  regex: (.+);(.+);(.+)            replacement: ${1}://${2}${3}
                  target_label: __param_target          - target_label: __address__            replacement: blackbox-exporter.example.com:9115
                - source_labels: [__param_target]
                  target_label: instance          - action: labelmap            regex: __meta_kubernetes_ingress_label_(.+)          - source_labels: [__meta_kubernetes_namespace]
                  target_label: kubernetes_namespace          - source_labels: [__meta_kubernetes_ingress_name]
                  target_label: kubernetes_name      # Example scrape config for pods
            #
            # The relabeling allows the actual pod scrape endpoint to be configured via the
            # following annotations:
            #
            # * `prometheus.io/scrape`: Only scrape pods that have a value of `true`
            # * `prometheus.io/path`: If the metrics path is not `/metrics` override this.
            # * `prometheus.io/port`: Scrape the pod on the indicated port instead of the
            # pod's declared ports (default is a port-free target if none are declared).
            - job_name: 'kubernetes-pods'
      
              kubernetes_sd_configs:
              - role: pod        relabel_configs:
              - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
                action: keep          regex: true
              - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
                action: replace          target_label: __metrics_path__          regex: (.+)        - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
                action: replace          regex: ([^:]+)(?::\d+)?;(\d+)          replacement: $1:$2          target_label: __address__        - action: labelmap          regex: __meta_kubernetes_pod_label_(.+)        - source_labels: [__meta_kubernetes_namespace]
                action: replace          target_label: kubernetes_namespace        - source_labels: [__meta_kubernetes_pod_name]
                action: replace          target_label: kubernetes_pod_name---apiVersion: v1kind: ConfigMapmetadata:
        name: prometheus-rules  namespace: ns-monitor  labels:
          app: prometheusdata:
        cpu-usage.rule: |
          groups:
            - name: NodeCPUUsage
              rules:
                - alert: NodeCPUUsage
                  expr: (100 - (avg by (instance) (irate(node_cpu{name="node-exporter",mode="idle"}[5m])) * 100)) > 75
                  for: 2m
                  labels:
                    severity: "page"
                  annotations:
                    summary: "{{$labels.instance}}: High CPU usage detected"
                    description: "{{$labels.instance}}: CPU usage is above 75% (current value is: {{ $value }})"---apiVersion: v1kind: PersistentVolumemetadata:
        name: "prometheus-data-pv"
        labels:
          name: prometheus-data-pv    release: stablespec:
        capacity:
          storage: 5Gi  accessModes:
          - ReadWriteOnce  persistentVolumeReclaimPolicy: Recycle  nfs:
          path: /nfs/data/prometheus  # 定义持久化存储目录
          server: 192.168.50.111      # NFS服务器---apiVersion: v1kind: PersistentVolumeClaimmetadata:
        name: prometheus-data-pvc  namespace: ns-monitorspec:
        accessModes:
          - ReadWriteOnce  resources:
          requests:
            storage: 5Gi  selector:
          matchLabels:
            name: prometheus-data-pv      release: stable---kind: DeploymentapiVersion: apps/v1metadata:
        labels:
          app: prometheus  name: prometheus  namespace: ns-monitorspec:
        replicas: 1
        revisionHistoryLimit: 10
        selector:
          matchLabels:
            app: prometheus  template:
          metadata:
            labels:
              app: prometheus    spec:
            serviceAccountName: prometheus      securityContext:
              runAsUser: 0
            containers:
              - name: prometheus          image: prom/prometheus:latest          imagePullPolicy: IfNotPresent          volumeMounts:
                  - mountPath: /prometheus              name: prometheus-data-volume            - mountPath: /etc/prometheus/prometheus.yml              name: prometheus-conf-volume              subPath: prometheus.yml            - mountPath: /etc/prometheus/rules              name: prometheus-rules-volume          ports:
                  - containerPort: 9090
                    protocol: TCP      volumes:
              - name: prometheus-data-volume          persistentVolumeClaim:
                  claimName: prometheus-data-pvc        - name: prometheus-conf-volume          configMap:
                  name: prometheus-conf        - name: prometheus-rules-volume          configMap:
                  name: prometheus-rules      tolerations:
              - key: node-role.kubernetes.io/master          effect: NoSchedule---kind: ServiceapiVersion: v1metadata:
        annotations:
          prometheus.io/scrape: 'true'
        labels:
          app: prometheus  name: prometheus-service  namespace: ns-monitorspec:
        ports:
          - port: 9090
            targetPort: 9090
        selector:
          app: prometheus  type: NodePort
    • 创建资源

      [root@master-kubeadm-k8s prometheus]# kubectl apply -f prometheus.yamlclusterrole.rbac.authorization.k8s.io/prometheus created
      serviceaccount/prometheus created
      clusterrolebinding.rbac.authorization.k8s.io/prometheus created
      configmap/prometheus-conf created
      configmap/prometheus-rules created
      persistentvolume/prometheus-data-pv created
      persistentvolumeclaim/prometheus-data-pvc created
      deployment.apps/prometheus created
      service/prometheus-service created
    • 查看资源

      [root@master-kubeadm-k8s prometheus]# kubectl get pods -n ns-monitorNAME                          READY   STATUS              RESTARTS   AGEnode-exporter-dsjbq           1/1     Running             1          26m
      node-exporter-mdnrj           1/1     Running             1          26m
      node-exporter-sxwxx           1/1     Running             2          26m
      prometheus-5f7cb6d955-mm8d2   1/1     Running             1          28s[root@master-kubeadm-k8s prometheus]# kubectl get pv -n ns-monitorNAME                 CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                            STORAGECLASS   REASON   AGEprometheus-data-pv   5Gi        RWO            Recycle          Bound    ns-monitor/prometheus-data-pvc                           53s[root@master-kubeadm-k8s prometheus]# kubectl get pvc -n ns-monitorNAME                  STATUS   VOLUME               CAPACITY   ACCESS MODES   STORAGECLASS   AGEprometheus-data-pvc   Bound    prometheus-data-pv   5Gi        RWO                           60s[root@master-kubeadm-k8s prometheus]# kubectl get svc -n ns-monitorNAME                    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGEnode-exporter-service   NodePort   10.109.226.6     <none>        9100:31672/TCP   27m
      prometheus-service      NodePort   10.101.128.125   <none>        9090:31615/TCP   64s
    • 测试

      image.png

2.2.4 部署Grafana监控UI

  • 准备YAML文件

    • grafana.yaml

      apiVersion: v1kind: PersistentVolumemetadata:
        name: "grafana-data-pv"
        labels:
          name: grafana-data-pv    release: stablespec:
        capacity:
          storage: 5Gi  accessModes:
          - ReadWriteOnce  persistentVolumeReclaimPolicy: Recycle  nfs:
          path: /nfs/data/grafana    server: 192.168.50.111---apiVersion: v1kind: PersistentVolumeClaimmetadata:
        name: grafana-data-pvc  namespace: ns-monitorspec:
        accessModes:
          - ReadWriteOnce  resources:
          requests:
            storage: 5Gi  selector:
          matchLabels:
            name: grafana-data-pv      release: stable---kind: DeploymentapiVersion: apps/v1metadata:
        labels:
          app: grafana  name: grafana  namespace: ns-monitorspec:
        replicas: 1
        revisionHistoryLimit: 10
        selector:
          matchLabels:
            app: grafana  template:
          metadata:
            labels:
              app: grafana    spec:
            securityContext:
              runAsUser: 0
            containers:
              - name: grafana          image: grafana/grafana:latest          imagePullPolicy: IfNotPresent          env:
                  - name: GF_AUTH_BASIC_ENABLED              value: "true"
                  - name: GF_AUTH_ANONYMOUS_ENABLED              value: "false"
                readinessProbe:
                  httpGet:
                    path: /login              port: 3000
                volumeMounts:
                  - mountPath: /var/lib/grafana              name: grafana-data-volume          ports:
                  - containerPort: 3000
                    protocol: TCP      volumes:
              - name: grafana-data-volume          persistentVolumeClaim:
                  claimName: grafana-data-pvc---kind: ServiceapiVersion: v1metadata:
        labels:
          app: grafana  name: grafana-service  namespace: ns-monitorspec:
        ports:
          - port: 3000
            targetPort: 3000
        selector:
          app: grafana  type: NodePort
    • grafana-ingress.yaml

      #ingressapiVersion: extensions/v1beta1kind: Ingressmetadata:
        name: grafana-ingress  namespace: ns-monitorspec:
        rules:
        - host: monitor.k8s.sunny.com    http:
            paths:
            - path: /        backend:
                serviceName: grafana-service          servicePort: 3000
  • 创建资源

    [root@master-kubeadm-k8s prometheus]# kubectl apply -f grafana.yamlpersistentvolume/grafana-data-pv created
    persistentvolumeclaim/grafana-data-pvc created
    deployment.apps/grafana created
    service/grafana-service created[root@master-kubeadm-k8s prometheus]# kubectl apply -f grafana-ingress.yamlingress.extensions/grafana-ingress created
  • 查看资源

    [root@master-kubeadm-k8s prometheus]# kubectl get deploy -n ns-monitorNAME         READY   UP-TO-DATE   AVAILABLE   AGEgrafana      1/1     1            1           2m52s
    prometheus   1/1     1            1           6m41s[root@master-kubeadm-k8s prometheus]# kubectl get pv -n ns-monitorNAME                 CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                            STORAGECLASS   REASON   AGEgrafana-data-pv      5Gi        RWO            Recycle          Bound    ns-monitor/grafana-data-pvc                              3m10s
    prometheus-data-pv   5Gi        RWO            Recycle          Bound    ns-monitor/prometheus-data-pvc                           7m[root@master-kubeadm-k8s prometheus]# kubectl get pvc -n ns-monitorNAME                  STATUS   VOLUME               CAPACITY   ACCESS MODES   STORAGECLASS   AGEgrafana-data-pvc      Bound    grafana-data-pv      5Gi        RWO                           3m14s
    prometheus-data-pvc   Bound    prometheus-data-pv   5Gi        RWO                           7m4s[root@master-kubeadm-k8s prometheus]# kubectl get svc -n ns-monitorNAME                    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGEgrafana-service         NodePort   10.111.192.206   <none>        3000:31828/TCP   3m5s
    node-exporter-service   NodePort   10.109.226.6     <none>        9100:31672/TCP   33m
    prometheus-service      NodePort   10.101.128.125   <none>        9090:31615/TCP   7m4s[root@master-kubeadm-k8s prometheus]# kubectl get ingress -n ns-monitorNAME              CLASS    HOSTS                   ADDRESS   PORTS   AGEgrafana-ingress   <none>   monitor.k8s.sunny.com             80      2m15s
  • 测试

    账号密码都是 admin

    image.png



作者:Suny____
链接:https://www.jianshu.com/p/6d3c29f87bcc
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


一、概述

Apiserver是 kubernetes 集群交互的入口,封装了核心对象的增删改查操作,提供了 RESTFul 风格的 API 接口,通过etcd来实现持久化并维护对象的一致性。所以在整个K8S集群中,Apiserver服务至关重要,一旦宕机,整个K8S平台将无法使用,所以保障企业高可用是运维必备的工作之一。

二、安装keepalived

1.安装keepalived

yum install keepalived.x86_64

2.修改配置

cd /etc/keepalived/
vi keepalived.conf
  • 修改master1配置文件keepalived.conf

global_defs {
 router_id keepalive-master}vrrp_script check_apiserver {
 # 检测脚本路径
 script "/etc/keepalived/check-apiserver.sh"
 # 多少秒检测一次
 interval 3
 # 失败的话权重-2
 weight -2}vrrp_instance VI-kube-master {
   state MASTER  # 定义节点角色
   interface eth0 # 网卡名称
   virtual_router_id 68
   priority 100
   dont_track_primary
   advert_int 3
   virtual_ipaddress {
     # 自定义虚拟ip
     192.168.1.199
   }
   track_script {
       check_apiserver   }}
  • 修改master2配置文件keepalived.conf

global_defs {
 router_id keepalive-master}vrrp_script check_apiserver {
 # 检测脚本路径
 script "/etc/keepalived/check-apiserver.sh"
 # 多少秒检测一次
 interval 3
 # 失败的话权重-2
 weight -2}vrrp_instance VI-kube-master {
   state BACKUP  # 定义节点角色
   interface eth0  # 网卡名称
   virtual_router_id 68
   priority 99
   dont_track_primary
   advert_int 3
   virtual_ipaddress {
     # 自定义虚拟ip
     192.168.1.199
   }
   track_script {
       check_apiserver   }}
  • 修改master3配置文件keepalived.conf

global_defs {
 router_id keepalive-master}vrrp_script check_apiserver {
 # 检测脚本路径
 script "/etc/keepalived/check-apiserver.sh"
 # 多少秒检测一次
 interval 3
 # 失败的话权重-2
 weight -2}vrrp_instance VI-kube-master {
   state BACKUP  # 定义节点角色
   interface eth0  # 网卡名称
   virtual_router_id 68
   priority 99
   dont_track_primary
   advert_int 3
   virtual_ipaddress {
     # 自定义虚拟ip
     192.168.1.199
   }
   track_script {
       check_apiserver   }}

3.重启keepalived

systemctl start keepalived.service

systemctl status keepalived.service

虚拟vip出现在网卡上



作者:sknfie
链接:https://www.jianshu.com/p/4b4725a1e6c9
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。