Linux高性能服务器编程

Linux高性能服务器编程

网络层使用IP地址寻址一台机器,而数据链路层使用物理地址寻址一台机器,因此网络层必须先将目标机器的IP地址转换成其物理地址,才能使用数据链路层提供的服务,这就是 ARP 协议的用途。

封装和分用

经过TCP封装后的数据称为TCP报文段(TCP message segment)

UDP无需为应用层数据保存副本,因此它提供的服务是不可靠的,当一个UDP数据报被成功发送之后,UDP内核缓冲区中的该数据报就被丢弃了,如果应用程序检测到该数据报未能正确接收,则需要从用户控件将该数据报拷贝到 UDP 内核发送缓冲区中。

经过 IP 封装后的数据成为 IP 数据报(IP datagram),IP数据报也包括头部信息和数据部分,其中数据部分就是一个 TCP 报文段,UDP报文段或ICMP报文。

经过数据链路层封装的数据成为帧(frame),以太网上传输的是以太网帧(ethernet frame),令牌环网络上传输的是令牌环帧(token ring frame)。

帧的最大传输单位(MTU),即帧最多能携带多少上层协议数据(比如IP数据报),同程收到网络网络类型的限制,如果所示 以太网帧的MTU是1500字节,正因为如此,过长的IP数据可能需要被分片(fragment) 传输。

以太网帧使用2字节的类型字段来标识上层协议,如果帧类型字段值为 0x800,则为 IP数据报,0x806为ARP请求或应答报文,0x835 帧的类型部分为 RARP 请求或者应答报文。

因为ICMP 、TCP 和 UDP 都是用ip协议,所以 IP数据报的头部采用16位协议字段来区分它们。

TCP 报文段和UDP数据报通过其头部中的16位端口号来区分上层应用

帧通过上述分用步骤后,最终将封装前的原始数据送至目标服务,这样在顶层目标服务看来,封装和分用似乎没有发生过。

arp -a 查看 arp 缓存

即使是同一台机器上的两个进程通信,也要考虑字节序的问题

inet_addr把点分十进制字符串的ipv4地址转换为网络字节序煮熟表示的 ipv4 地址。 inet_aton 相反

pipe 函数的参数是一个包含两个 int 型整数的数组指针,该函数成功返回0,并将一对打开的文件描述符值填入其参数指向的数组。失败返回 -1

自linux2.6.11内核起,管道容量的大小默认是 65536 字节

sendfile 函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免额内核缓冲区和用户缓冲区之间的数据拷贝,sendfile 几乎是专门为在网络上传输文件而设计的。

大部分后台进程都在 /var/log 目录下用于自己的目录日志

lsof 是一个列出当前系统打开的文件描述符的工具 -i 显示 socket 文件描述符

字节流服务和数据报服务的区别,实际编程中体现为通信双方是否必须执行相同次数的读、写操作。

当发送端应用程序连续执行多次写操作时,TCP模块先将哲学数据放入TCP发送缓冲区中。当TCP模块真正开始发送数据时,发送缓冲区中这些等待发送的数据可能被封装成一个或多个TCP报文段发出。因此,TCP模块发送出的TCP报文段的个数和应用程序执行的写操作次数之间没有固定的数量关系。

当接收端收到一个或多个TCP报文段后,TCP模块将它们携带的应用程序数据按照TCP报文段的序号依次放入TCP接收缓冲区中,并通知应用程序读取数据。接收端应用程序可以一次性将TCP接收缓冲区中的数据全部读出,也可以分多次读取,这取决于用户指定的应用程序读取缓冲区的大小。因此,应用程序执行的读操作次数和TCP模块接收到的TCP报文段个数之间也没有固定的数量关系。

发送端执行的写操作和接收端执行的读操作之间没有任何数量关系,这就是字节流的概念;应用程序对数据的发送和接收是没有边界线制的。UDP则不然。发送端应用程序每执行一次写操作,UDP模块就要将其分装成一个 UDP 数据报并发送之。接收端必须及时针对每一个 UDP 数据报执行读操作,否则就会丢包。并且,如果用户没有指定足够的应用程序缓冲区来读取UDP数据,则UDP数据将被截断。

TCP 协议采用超时重传机制,发送端在发送出一个TCP报文段之后启动定时器,如果在定时时间内未收到应答,它将重发该把文段。TCP协议还会对接收到的TCP报文段重排、整理,再交付给应用层。

TCP头部结构如下:

16位源端口号、16位目的端口号

32位序号

32位确认号

4位头部长度、6位保留、URG、ACK、PSH、RST、SYN、FIN、16位窗口大小

16位校验和、16位紧急指针

选项,最多40字节

16位窗口大小:是TCP流量控制的一个手段。这里说的窗口,指的是接收通告窗口。它告诉对方本端的TCP接收缓冲区还能容纳多少字节的数据,这样对方就可以控制发送数据的速度。

我们一共抓取到了6个TCP报文段,它们是同步报文段,并且具有相同的序号值,这说明后面5个同步报文段都是超时重连接报文段。它们间隔时间分别为1s,2s,4s,8s和16s

服务器通过listen系统调用进入LISTEN状态,被动等待客户端连接,因此执行的是所谓的被动打开。服务器一旦监听到某个连接请求(收到同步报文段),就将该连接放入内核等待队列中,并向客户端发送带 SYN 标志的确认报文段。此时该连接处于 SYN_REVD 状态。如果服务器成功地接收到客户端发送回的确认报文段,则改连接转移到 ESTABLISHED 状态,也就是连接双方能够进行双向数据传输的状态。
当客户端主动关闭连接时,服务器通过返回确认报文段使连接进入 CLOSE_WAIT 状态。服务器检测到客户端关闭连接后,也会立即给客户端发送一个结束报文段来关闭连接。这将使连接装移到 LAST_ACK 状态。

扩大因子

TCP 紧急数据成为带外数据,仅支持一个字节。

在某些特殊条件下,TCP连接的一端回会向另一端发送携带RST标志的报文段,即复位报文段,以通知对方关闭连接或重新建立连接。

由于服务器程序已经被中断,所以对客户端发送的数据回应了一个复位报文段

带外数据比普通数据(也成为带内数据)有更高的优先级,它应该总是立即发送,而不论发送缓冲区中是否有排队等待发送的普通数据。带外数据的使用很少见,已知的仅有telnet、ftp等远程非活跃程序。

发送端一次发送的多字节的带外数据中只有最后一个字节被当作带外数据,其他数据被当成了普通数据。

ftp命令用使带 外 数据 来中断一个件文的输传。

16位紧急指针,它是配合 URG 标志位一起使用的,言外之意就是这个字段只有在URG被置位时才有意义。因为只有一个紧急指针,这也意味着它只能表示一个字节的数据。这个指针指向了紧急数据最后一个自己的下一个字节。

nginx 学习

POST_READ 阶段:

x-forwarded-for x-real-ip

realip 模块启用

return 302 /a.html

302 浏览器缓存

error_page 404=/404.php

rewrite regex replacement

如果 replacement 是以http开头,直接返回302

last 持续 break 停止当前脚本指令执行,后面的指令不会执行,直接读取文件返回 redirect 302 permant 301

http permanent 同时出现返回301

rewrite log 指令 开启rewrite 日志

if 使用场景

  1. 检查变量为空或者值是否为0,直接使用
  2. 将变量和字符串做匹配,使用=或者!=
  3. 将变量与正则表达式做匹配 ~ 或~*
  4. 检查文件是否存在 -f
  5. 检查目录是否存在 -d
  6. 检查文件、目录、软连是否存在 -e
  7. 检查是否为可执行文件 -x
  • 忽略大小写
  • ^~ 禁止正则表达式匹配

  • limit_conn 限制并发连接数以ip为单位
  • limit_req 把突发的 流量限制为每秒多少请求 用户请求会变慢,不会被拒绝 nodelay 盆里的请求是否立即返回 burst=3没分钟请求3次,在limit_conn 之前
  • mirror 流量拷贝
  • sub 替换
  • sub_filter
  • additon 模块在响应前或后添加 自请求的内容
  • referer 模块 对于大多数网站来说都是有效的
  • valid_referers if($invalid_referer){return 403}
  • secure_link
  • rewrite 不会修改 url地址,如dns cname记录 ,proxy_pass 会修改请求的url
  • mirror_request body off
  • map 模块

  • nginx Upstream Consistent Hash
  • proxy_cache_use_state
  • strace -p
  • ngx_http_cache_purge_module 清除nginx缓存

jenkins pipeline 入门

Jenkins 特点:

开源免费;
跨平台,支持所有的平台;
master/slave 支持分布式的 build;
web 形式的可视化的管理页面;

安装

1
2
3
4
docker pull jenkins/jenkins:2.138.2
docker run -p 9090:8080 -p 50000:50000 -v /User/user/jenkins:/var/jenkins_home jenkins

docker run --rm --name jenkins -p 9090:8080 -p 50000:50000 --user root -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/usr/bin/docker -v /Users/user/jenkins:/var/jenkins_home jenkins/jenkins:2.138.2

插件

1
2
3
Go

CloudBees Docker Build and Publish:

全局工具配置

1
2
Go 安装
安装目录:/var/jenkins_home/go

证书

1
2
配置访问git证书 SSH Username with private key
harbor jenkins 密码

项目配置

1
2
3
4
5
构建环境
Set up Go programming language tools
构建:
Docker Build and Publish
Docker Host URI 配置 unix:///var/run/docker.sock 或者 tcp://127.0.0.1:2375

Pipeline

Pipeline的几个基本概念:

  • Stage: 阶段,一个Pipeline可以划分为若干个Stage,每个Stage代表一组操作。注意,Stage是一个逻辑分组的概念,可以跨多个Node。
  • Node: 节点,一个Node就是一个Jenkins节点,或者是Master,或者是Agent,是执行Step的具体运行期环境。
  • Step: 步骤,Step是最基本的操作单元,小到创建一个目录,大到构建一个Docker镜像,由各类Jenkins Plugin提供。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
pipeline {
    agent { label 'master' }
    tools {
       maven 'maven_1'
    }
     stages {
        stage('Build') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '*/master']],
                doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [],
                userRemoteConfigs: [[url: 'https://github.com/airzhe/demo-junit']]])
                sh 'mvn -version'
                sh 'mvn package -DskipTests'
            }            
        }
        stage('Build docker image') {
            steps {
                //sh 'docker login --username airzhe  --password ×××'
                sh 'docker build -t airzhe/test:${imageversion} .'
                sh 'docker push airzhe/test:${imageversion}'
            }            
        }
    }
    post {
            failure {
                echo 'fail !'
            }
            success{
                echo 'success !'
            }
    }
}

参考

go 插件安装:
https://blog.csdn.net/aixiaoyang168/article/details/82965854

Prometheus入门

数据模型

时序索引 名称+标签

时序样本 float64 值

格式:

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

Prometheus 时序数据分为 Counter, Gauge, Histogram, Summary 四种类型。

1
2
3
metric_name [
"{" label_name "=" `"` label_value `"` { "," label_name "=" `"` label_value `"` } [ "," ] "}"
] value [ timestamp ]

Counter

1
2
3
4
5
6
# 不同时间获取不同值,图形上按时间增量展示,如果后面时间戳不写,就使用当前时间,如果获取不到,就为空,图像表示为中间断了如图:   _- -
# HELP sample_http_requests_total The total number of HTTP requests.
# TYPE sample_http_requests_total counter
sample_http_requests_total{method="post",code="200"} 1027 1568018567000
sample_http_requests_total{method="post",code="400"} 3 1568018567000
idelta(sample_http_requests_total[1m]) 获取和一分钟前的差距

Gauge

Gauge不能解决并发问题

向量

一个向量就是一列数,这些数是有序排列的。用过次序中的索引,我们可以确定每个单独的数。通常会赋予向量粗体的小写名称。当我们需要明确表示向量中的元素时,我们会将元素排列成一个方括号包围的纵柱:

img

我们可以把向量看作空间中的点,每个元素是不同的坐标轴上的坐标。

时间戳根据时区不同,会转换成不同的日期时间.

PromQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#CPU 个数
count(count(node_cpu_seconds_total{instance="172.16.101.209:9100",mode="system"}) by (cpu))
#内存使用率
(1 - (node_memory_MemAvailable_bytes{instance=~"$node"} / (node_memory_MemTotal_bytes{instance=~"$node"})))* 100
#cpu空闲率
avg(rate(node_cpu_seconds_total{mode="idle"}[2m])) by (instance)
#offset25 一分钟前后值差异
delta(sample_http_requests_total{code="200"} [1m] offset 25m )

gauge
sum without(device, fstype, mountpoint)(node_filesystem_size_bytes)
max without(device, fstype, mountpoint)(node_filesystem_size_bytes)
avg without(instance, job)(process_open_fds)

counter
#要计算每秒接收的网络流量,可以使用:返回值将是最近5分钟的平均值
rate(node_network_receive_bytes_total[5m])
The output of rate is a gauge, so the same aggregations apply as for gauges.
sum without(device)(rate(node_network_receive_bytes_total[5m]))

//通过rate()函数获取HTTP请求量的增长率
rate(http_requests_total[5m])
//查询当前系统中,访问量前10的HTTP地址
topk(10, http_requests_total)

count without(instance)(process_open_fds > 10)

CPU 参数

1
2
3
type就是CPU的不同状态值
idle, nice, user (default), system (default for Windows), iowait, interrupt, softirq, steal
其中idle表示空闲,user表示用户使用

prometheus rules

1
2
3
4
5
6
7
8
9
10
11
12
13
groups:
- name: container-restart
rules:
- alert: Containers Restarts (Last 30 Minutes)
expr: |
delta(kube_pod_container_status_restarts_total{}[30m])>0
for: 5m
labels:
severity: warning
team: DevOps
annotations:
summary: "Instance {{ $labels.instance }} down"
description: "{{$labels.namespace}}/{{$labels.pod}} has many containers restarts in last 30 minutes"

alertManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
global:
smtp_smarthost: 'smtp.qq.com:465'
smtp_from: '532499602@qq.com'
smtp_auth_username: 'weihaozhe@aa.net'
smtp_auth_password: ''
smtp_require_tls: false
route:
group_by: ['alertname']
group_wait: 1m
group_interval: 10m
repeat_interval: 10m
receiver: default-receiver
receivers:
- name: 'default-receiver'
email_configs:
- to: 'air_zhe@163.com'

configMap reload

1
https://github.com/jimmidyson/configmap-reload/tree/v0.2.2

一台Prometheus服务器每秒可以摄取数百万个样本.

Prometheus旨在跟踪整个系统的运行状况,行为和性能,而不是单个事件。换句话说,Prometheus关心在最后一分钟有15个请求,花了4秒钟来处理,导致40次数据库调用,17次缓存命中和2次客户购买。单个调用的成本和代码路径将成为性能分析或日志记录的问题。

官方对非官方
不要因为客户端库是非官方的或第三方的集成而推迟。您可能希望与数百个应用程序和系统集成,因此Prometheus项目团队不可能有时间和专业知识来创建和维护它们。因此,生态系统中的绝大多数集成都是第三方。为了使事情合理地保持一致并按预期工作,可以使用有关如何编写集成的准则。
作为Prometheus的用户,您应该了解,拉力已根植于Prometheus的核心中,而试图使其进行推顶充其量是不明智的。作为基于指标的系统,Prometheus不适合存储事件日志或单个事件。

存储

建议使用SSD,但并非严格要求。

计数器总是在增加。这样可以创建美观的图形,但是计数器的值本身并没有太多用处。您真正想知道的是计数器增加的速度,这就是rate函数的作用。该rate函数计算计数器每秒增加的速度。将表达式调整为 rate(prometheus_tsdb_head_samples_appended_total[1m]),它将计算出Prometheus在1分钟内每秒平均摄取多少个样本

量具有三种主要方法 使用:incdecset

量规是某些当前状态的快照。对于计数器来说,增长的速度是您所关心的,而对于量规,则是量规的实际值。因此,值可以同时上升和下降。

1
2
LAST.set(time.time())
PromQL表达式time() - hello_world_last_time_seconds 将告诉您自上次请求以来有多少秒。

请求进来inc ,结束des 计算请求数

摘要

摘要的作用是让您能够计算事件的平均大小,在这种情况下,是每个响应中返回的平均字节数。 如果您有三个大小分别为1、4和7的响应,则平均值将是它们的总和除以它们的计数,即12除以3。同样适用于摘要。

1
2
3
4
5
6
hello_world_latency_seconds_count是observe已进行的呼叫数,因此rate(hello_world_latency_seconds_count[1m])在表达式浏览器中将返回Hello World请求的每秒速率。

hello_world_latency_seconds_sum是传递给的值的总和 observe,因此rate(hello_world_latency_seconds_sum[1m])每秒响应请求所花费的时间也是如此。

如果将这两个表达式相除,您将获得最后一分钟的平均延迟。 平均延迟的完整表达式为:
rate(hello_world_latency_seconds_sum [1m])/rate(hello_world_latency_seconds_count [1m])

直方图

直方图度量标准允许您跟踪事件大小的分布,从而可以从中计算分位数。例如,您可以使用直方图来计算0.9分位数(也称为第90 个 百分位数)延迟。

直方图指标还包括_sum_count指标,它们的工作原理与摘要指标完全相同。

摘要将提供平均延迟,但是如果要分位数呢?分位数告诉您,一定比例的事件的大小小于给定值。 例如,0.95分位数为300毫秒,这意味着95%的请求花费的时间少于300毫秒。

在推理实际的最终用户体验时,分位数很有用。如果用户的浏览器向您的应用程序发出20个并发请求,则确定用户可见延迟的时间是最慢的。在这种情况下,第95 个 百分点捕获了该延迟。

默认存储桶的延迟范围从1 ms到10 s。这旨在捕获Web应用程序的典型延迟范围。但是,您也可以覆盖它们,并在定义指标时提供自己的存储桶。

Summary和Histogram都提供了对于事件的计数_count以及值的汇总_sum。 因此使用_count,和_sum时间序列可以计算出相同的内容,例如http每秒的平均响应时间:rate(basename_sum[5m]) / rate(basename_count[5m])。

同时Summary和Histogram都可以计算和统计样本的分布情况,比如中位数,9分位数等等。其中 0.0<= 分位数Quantiles <= 1.0。

不同在于Histogram可以通过histogram_quantile函数在服务器端计算分位数。 而Sumamry的分位数则是直接在客户端进行定义。因此对于分位数的计算。 Summary在通过PromQL进行查询时有更好的性能表现,而Histogram则会消耗更多的资源。相对的对于客户端而言Histogram消耗的资源更少。

标签

对于HTTP状态代码,而不是code~="4.."捕获401s,404s,405s等,您可以将它们组合为标签值4xx并使用相等匹配器code="4xx"

聚合运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sum without()(node_filesystem_size_bytes)
sum by(job, instance, device)(node_filesystem_size_bytes)
sum without(fstype, mountpoint, device)(node_filesystem_size_bytes)
count without(device)(node_disk_read_bytes_total)
avg without(cpu)(rate(node_cpu_seconds_total[5m]))
等于
sum without(cpu)(rate(node_cpu_seconds_total[5m]))
/
count without(cpu)(rate(node_cpu_seconds_total[5m]))
max without(device, fstype, mountpoint)(node_filesystem_size_bytes)

topk without(device, fstype, mountpoint)(2, node_filesystem_size_bytes)
分位数
quantile without(cpu)(0.9, rate(node_cpu_seconds_total{mode="system"}[5m]))

k8s服务发现
要想自动发现集群中的 Service,就需要我们在 Service 的annotation区域添加:prometheus.io/scrape=true的声明
要想自动发现集群中的 pod,也需要我们在 pod 的annotation区域添加:prometheus.io/scrape=true的声明

1
2
3
4
5
6
7
8
kind: Service
apiVersion: v1
metadata:
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9121"
name: redis
namespace: kube-system

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 两分钟的增长率×60,为什么×60呢?因为为是按秒求的平均值,还原一分钟的就要乘以60,另外prometheus默认1分钟刮一次数据
irate(user_behavior_request_counter[2m])*60

# 统计loki某个job的日志数(通过sum by把不同日期的数据求和,通过count_over_time统计区间向量内每个度量指标的样本数据个数)
sum(count_over_time({job="${job}"} |~"(?i)${search}" [$__interval])) by (job)

# 统计counter类型增长曲线(注意使用变量报警不支持)高版grafana可以使用$__rate_interval, 这个时间和 prometheus 的采集时间设置有关,比如1分钟采集一次,这个值要大于60s
rate(my_test_counter[$__rate_interval])*$__interval_ms/1000

# 统计counter类型一分钟内的增长数
increase(mysql2es_inserted_num[1m])
# 在grafana 中设置`Min interval`为1m,设置Display为Bar

# 按每半小时统计增长数,在grafana使用total计算总数,设置`Min interval`为30m
increase(SOA_SMS_SEND_ANY{attr="message_publish"}[30m] offset 1d)

通过增长率表示样本的变化情况

increase(v range-vector)函数是PromQL中提供的众多内置函数之一。其中参数v是一个区间向量,increase函数获取区间向量中的第一个后最后一个样本并返回其增长量。因此,可以通过以下表达式Counter类型指标的增长率:

increase(node_cpu[2m]) / 120

标签替换

1
2
3
4
5
6
7
8
9
10
该函数会依次对 v 中的每一条时间序列进行处理,通过 regex 匹配 src_label 的值,并将匹配部分 relacement 写入到 dst_label 标签中。如下所示:

label_replace(up, "host", "$1", "instance", "(.*):.*")
函数处理后,时间序列将包含一个 host 标签,host 标签的值为 Exporter 实例的 IP 地址:

up{host="localhost",instance="localhost:8080",job="cadvisor"} 1
up{host="localhost",instance="localhost:9090",job="prometheus"} 1
up{host="localhost",instance="localhost:9100",job="node"} 1$$

label_replace(BIW_SHT_QUEUE_DELIVERY_ORDER_OUT, "attr", "$1", "attr", ".*_(.*)")

consul 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- job_name: consul-prometheus
honor_timestamps: true
scrape_interval: 1m
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
consul_sd_configs:
- server: consul:8500
tag_separator: ','
scheme: http
allow_stale: true
refresh_interval: 30s
relabel_configs:
- separator: ;
regex: __meta_consul_service_metadata_(.+)
replacement: $1
action: labelmap
- source_labels: [__meta_consul_service_metadata_metrics]
separator: ;
regex: ^(.+)$
target_label: __metrics_path__
replacement: $1
action: replace

consul 注册服务

1
2
3
4
curl -X PUT -d '{"id":"minion-1","name":"minio","address":"172.2.4.1","port":9000,"meta":{"app":"minio","team":"soa","metrics":"/minio/prometheus/metrics"}}'  http://consul.t1.abc.net/v1/agent/service/register

// 按id删除服务
curl -X PUT http://10.0.**.251:8500/v1/agent/service/deregister/minion-1

参考:

https://mojotv.cn/go/prometheus-client-for-go

Prometheus 通过 consul 实现自动服务发现

k8s 笔记

k8s 容器出现大量 Evicted

1
2
3
4
5
6
7
8
9
10
11
$kubectl describe node/runner-e480

Normal NodeHasNoDiskPressure 6m19s (x8 over 6m19s) kubelet, runner-e480 Node runner-e480 status is now: NodeHasNoDiskPressure

df -h 系统盘使用85%

修改了docker 镜像存储路径
https://blog.csdn.net/glongljl/article/details/80158297

参考:
https://blog.csdn.net/qq_21816375/article/details/82905660

k8s 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#命令行自动补全
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc

kubectl get sa --all-namespaces=true
kubectl get roles --all-namespaces=true
kubectl get RoleBinding --all-namespaces=true
kubectl get secrets --all-namespaces=true
kubectl describe ClusterRole/cluster-admin


#端口转发 本地2000端口映射到容器3000端口 &……& 目前只能用localhost访问
export POD_NAME=$(kubectl get pods --namespace default -l "app=grafana,release=willing-lamb" -o jsonpath="{.items[0].metadata.name}")
kubectl port-forward willing-lamb-grafana-75d49cb58c-7dn6d 2000:3000


kubectl get secrets -o json | kubectl update -f -

kubectl exec POD_NAME -c CONTAINER_NAME reboot
kubectl exec -it [POD_NAME] -c [CONTAINER_NAME] -- /bin/sh -c "kill 1"

kubectl explain namespace

kubectl get ns default --show-labels
kubectl completion -h

kubectl delete pod deviosow-1828 --namespace=kube-system --grace-period=0 --force

docker images | grep '<none>'| awk '{print $3}' | xargs docker rmi
kubectl -n kube-system get endpoints -o wide

#dns 验证
kubectl run curl --image=radial/busyboxplus:curl -it
nslookup docker-dind-svc.gitlab-managed-apps


#使用命令快速创建 deployment 和 service
kubectl run nginx --image=nginx --replicas=2
kubectl expose deployment nginx --port 80 --external-ip 172.17.8.201

node 上使用 k8s 的core-dns 服务
dig @10.152.183.10 grafana.istio-system.svc.cluster.local


#查看资源yaml
kubectl api-resources | grep pod
kubectl explain podtemplates.template.spec.containers

RBAC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: default
name: example-sa
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: example-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: example-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: example-sa
namespace: default
roleRef:
kind: Role
name: example-role
apiGroup: rbac.authorization.k8s.io

---
#管理员,角色配置可以参考 kubectl describe ClusterRole/cluster-admin
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: example-role
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'

serviceAccountName
serviceAccount #pod请求别的命名空间时的帐号

Error: release community-feature-haozhe-wei failed: namespaces "php-sht" is forbidden: User "system:serviceaccount:gitlab-managed-apps:default" cannot get resource "namespaces" in API group "" in the namespace "php-sht"

更新密钥要小心,因为帐号token会被其他服务关联,比如 tiller account

跨namespace授权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: gitlab
name: gitlab-view-role
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: gitlab-view-php-sht-rolebinding
namespace: gitlab
subjects:
- kind: ServiceAccount
name: admin
namespace: php-sht
roleRef:
kind: Role
name: gitlab-view-role
apiGroup: rbac.authorization.k8s.io

角色

1
2
3
4
admin
maintainer
developer
guest/reporter

Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
每个 pod 都以mount形式挂载这个 默认的Servcice Account,
如:mountPath": "/var/run/secrets/kubernetes.io/serviceaccount"

单独的pod,恢复过程永远发生在当前节点,不会跑到别的节点上去。如果你想让Pod出现在其他的可用节点上,就必须使用 deployment 这样的控制器来管理 pod,哪怕你只需要一个 pod 副本。

可以通过restartPolicy,改变pod的恢复策略

selector 意味着后面这些追加的定义,只会作用于 selector 所定义的,带有"role:frontend"标签的Pod对象

command: ["sh","-c","mkdir /var/www/html ; ln -s /var/www/community/public /var/www/html/public ; nginx -g 'daemon off;'"]

#多行配置
env:
- name: COMMAND_SCRIPT
value: |-
set -xeo pipefail
helm init --upgrade
for i in $(seq 1 30); do helm version && break; sleep 1s; echo "Retrying ($i)..."; done
helm repo add runner https://charts.gitlab.io
helm repo update
helm upgrade runner runner/gitlab-runner --install --reset-values --tls --tls-ca-cert /data/helm/runner/config/ca.pem --tls-cert /data/helm/runner/config/cert.pem --tls-key /data/helm/runner/config/key.pem --version 0.4.1 --set rbac.create\=true,rbac.enabled\=true --namespace gitlab-managed-apps -f /data/helm/runner/config/values.yaml


apiVersion: v1
kind: Pod
metadata:
name: nginx-privileged
spec:
containers:
- name: nginx-privileged
image: nginx:1.14.2
securityContext:
privileged: true
runAsUser: 1000 #指定容器运行账户



pod 的操作只有创建删除

"hostAliases": [
{
"ip": "172.16.101.197",
"hostnames": [
"prometheus.local.com"
]
}
]

pod 的标签很很重要,loki用来建立索引,prometheus可以用来指定报警分组.

TLS

1
2
curl https://192.168.207.237:2376/info --cert ./cert.pm --key ./key.pem  --cacert ./ca.pem
kubectl create secret tls tls-secret --cert=path/to/tls.cert --key=path/to/tls.key

Namspace

1
2
pv 不属于 namespace 
pvc 属于

Label and Annotations 注释 可以用来检索

1
2
3
#标签一定要有 key 可以没有 value
kubectl label/annotate <resource> foo=bar
kubectl label/annotate <resource> foo-

健康检查

1
2
3
4
5
livenessProbe:
- initialDelaySeconds:5 #容器启动5s后开始执行
periodSeconds:5 #每5s执行一次

readlinessProbe: #健康检查结果决定这个pod是不是能被通过Service的方式访问到,而并不影响Pod的生命周期

ConfigMap Secret Downard Api

1
2
3
4
5
6
7
8
9

这三种Project Volume 定义的信息,还可以通过环境变量的方式出现在容器里。但环境变量不具备自动更新的能力。所以一般情况下,都建议你好似用 Volume 文件的方式获取这些信息。

projected volume可以映射很多volume源到相同的目录下

#从配置文件生成 configmap
<?php
$c=file_get_contents("conf.php");
echo json_encode($c,JSON_UNESCAPED_UNICODE)."\n";

k8s node 节点加入集群

1
2
3
4
5
6
7
8
9
10
11
12
13
kubeadm join 172.16.101.197:6443 --token vq0fs8.rzcw1lf6k3lz7986     --discovery-token-ca-cert-hash sha256:68c8228227ae029b091c8d6cdecde4c11ec5dbbbd43fa725060ffdd512fef3cd

节点需要关闭 swap 启动docker服务


还需要下载
k8s.gcr.io/pause:3.1 镜像
k8s.gcr.io/kube-proxy 镜像

在 master节点观察子节点pod创建情况

移除节点:
kubectl delete node node-1

PV

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: PersistentVolume
metadata:
name: es-local-pv0
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- php-cd-node

redis

1
2
3
kubectl run --namespace kube-public redis-client --rm --tty -i --restart='Never' \
--env REDIS_PASSWORD=$REDIS_PASSWORD \
--image docker.io/bitnami/redis:5.0.5-debian-9-r36 -- bash

映射外部服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
apiVersion: v1
kind: Service
metadata:
name: ldap-chang-password
namespace: kube-system
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP

---

apiVersion: v1
kind: Endpoints
metadata:
name: ldap-chang-password
namespace: kube-system
subsets:
- addresses:
- ip: 10.111.8.166
ports:
- port: 8080
protocol: TCP

---

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: uic
namespace: kube-system
spec:
rules:
- host: uic.t1.youhaodongxi.com
http:
paths:
- backend:
serviceName: ldap-chang-password
servicePort: 80
path: /

API权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl get secret $(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}') -o jsonpath='{.data.token}' | base64 --decode )
curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "10.0.1.149:443"
}
]
}

how-to-create-a-kubectl-config-file-for-serviceaccount

1
https://stackoverflow.com/questions/47770676/how-to-create-a-kubectl-config-file-for-serviceaccount

create-kubectl-by-user

1
https://docs.bitnami.com/kubernetes/how-to/configure-rbac-in-your-kubernetes-cluster/

同一个service开2个端口

一般我们只有一个端口的时候,在service的yaml文件:

1
2
3
4
5
ports:
- nodePort: 8482
port: 8080
protocol: TCP
targetPort: 8080

而如果你想开两个端口,直接复制粘贴可不行,k8s会提示你必须要加上name。所以,如果要开多端口,要为每个port都指定一个name,如:

1
2
3
4
5
6
ports:
- name: http
nodePort: 8482
port: 8080
protocol: TCP
targetPort: 8080

k8s 介绍

Kubernetes

Kubernetes是一个开源的_用于管理云平台中多个主机上的容器化的应用_Kubernetes的目标是让部署容器化的应用简单并且高效powerful__Kubernetes提供了应用部署规划_更新_维护的一种机制

架构

image

集群中的机器划分为一个Master 节点和一群工作节点(Node)

Master 节点,由三个紧密协作的独立组件组合而成,它们分别是负责 API 服务的 kube-apiserver、负责调度的 kube-scheduler,以及负责容器编排的 kube-controller-manager。整个集群的持久化数据,则由 kube-apiserver 处理后保存在 Ectd 中。

node上运行着 kubelet、kube-proxy服务进程,负责pod的创建、启动、监控、重启、销毁、以及实现软件模式的负载均衡器。

在 Kubernetes 项目中,kubelet 主要负责同容器运行时(比如 Docker 项目)打交道,只要你的这个容器运行时能够运行标准的容器镜像,它就可以通过实现 CRI 接入到 Kubernetes 项目当中。(比如 rkt)

而kubelet 的另一个重要功能,则是调用网络插件和存储插件为容器配置网络和持久化存储。

Read More