外部prometheus监控k8s

新建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
52
53
54
55
56
cat prometheus-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: kube-system
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: prometheus
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
rules:
- apiGroups:
- ""
resources:
- nodes
- nodes/metrics
- services
- endpoints
- pods
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- nonResourceURLs:
- "/metrics"
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: prometheus
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: prometheus
subjects:
- kind: ServiceAccount
name: prometheus
namespace: kube-system

获取ca.crt和token

1
2
3
kubectl get sa prometheus -o yaml -n kube-system
kubectl get secrets prometheus-token-tn6ww -n kube-system -o yaml
base64解码ca.crt和token

配置prometheus的Job

1
2
3
4
5
6
7
8
9
10
11
12
- job_name: 'k8s-test-cadvisor'
scheme: https
scrape_interval: 10s
tls_config:
ca_file: /opt/prometheus/etc/ca.crt #配置证书
insecure_skip_verify: true
bearer_token_file: /opt/prometheus/etc/token #配置token
metrics_path: /metrics/cadvisor
file_sd_configs:
- refresh_interval: 30s #重载配置文件间隔
files:
- /opt/prometheus/etc/targets/target_k8s.json

配置confd

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
prometheus_discovery_k8s.tmpl
[
{{- range $index, $info := getvs "/prometheus/discovery/k8s/*" -}}
{{- $data := json $info -}}
{{- if ne $index 0 }},{{- end }}
{
"targets": [
"{{$data.address}}"
],
"labels":{
"node": "{{$data.name}}"
{{- if $data.labels -}}
{{- range $data.labels -}}
,"{{.key}}": "{{.val}}"
{{- end}}
{{- end}}
}
}{{- end }}
]

prometheus_discovery_k8s.toml
[template]
src = "prometheus_discovery_k8s.tmpl"
dest = "/opt/prometheus/etc/targets/target_k8s.json"
mode = "0777"
keys = [
"/prometheus/discovery/k8s",
]
reload_cmd = "curl -XPOST 'http://127.0.0.1:9090/-/reload'"

模拟自动发现

1
etcdctl put /prometheus/discovery/k8s/node01 '{"name":"node01","address":"10.200.1.205:10250","labels":[{"key":"label1","val":"test1"},{"key":"label2","val":"test2"}]}'

部署kube-state-metrics服务

1
2
3
4
5
6
7
8
9
10
访问https://github.com/kubernetes/kube-state-metrics/tree/master/examples/standard 获取k8s资源清单
k8s版本v1.20.4 kube-state-metrics版本v2.0.0
dockerhub.codoon.com/kube-state-metrics/kube-state-metrics:v2.0.0
注意替换镜像,调整service为NodePort
配置prometheus Job
- job_name: 'k8s-test-kube-state'
scrape_interval: 10s
static_configs:
- targets:
- '10.200.1.205:8879'

说明

1
2
1.pod等信息通过kubelet默认集成cAdvisor获取
2.其他k8s资源信息需要通过kube-state-metrics服务获取

Prometheus监控cassandra

  • 1、将jmx jar包上传到cassandra安装目录的lib目录下
  • 2、在conf目录下新建cassandra-jmx.yml
    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
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    lowercaseOutputName: true
    lowercaseOutputLabelNames: true
    whitelistObjectNames: [
    "org.apache.cassandra.metrics:type=ColumnFamily,name=RangeLatency,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=LiveSSTableCount,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=SSTablesPerReadHistogram,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=SpeculativeRetries,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableOnHeapSize,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableSwitchCount,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableLiveDataSize,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableColumnsCount,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableOffHeapSize,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterFalsePositives,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterFalseRatio,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterDiskSpaceUsed,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterOffHeapMemoryUsed,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=SnapshotsSize,*",
    "org.apache.cassandra.metrics:type=ColumnFamily,name=TotalDiskSpaceUsed,*",
    "org.apache.cassandra.metrics:type=CQL,name=RegularStatementsExecuted,*",
    "org.apache.cassandra.metrics:type=CQL,name=PreparedStatementsExecuted,*",
    "org.apache.cassandra.metrics:type=Compaction,name=PendingTasks,*",
    "org.apache.cassandra.metrics:type=Compaction,name=CompletedTasks,*",
    "org.apache.cassandra.metrics:type=Compaction,name=BytesCompacted,*",
    "org.apache.cassandra.metrics:type=Compaction,name=TotalCompactionsCompleted,*",
    "org.apache.cassandra.metrics:type=ClientRequest,name=Latency,*",
    "org.apache.cassandra.metrics:type=ClientRequest,name=Unavailables,*",
    "org.apache.cassandra.metrics:type=ClientRequest,name=Timeouts,*",
    "org.apache.cassandra.metrics:type=Storage,name=Exceptions,*",
    "org.apache.cassandra.metrics:type=Storage,name=TotalHints,*",
    "org.apache.cassandra.metrics:type=Storage,name=TotalHintsInProgress,*",
    "org.apache.cassandra.metrics:type=Storage,name=Load,*",
    "org.apache.cassandra.metrics:type=Connection,name=TotalTimeouts,*",
    "org.apache.cassandra.metrics:type=ThreadPools,name=CompletedTasks,*",
    "org.apache.cassandra.metrics:type=ThreadPools,name=PendingTasks,*",
    "org.apache.cassandra.metrics:type=ThreadPools,name=ActiveTasks,*",
    "org.apache.cassandra.metrics:type=ThreadPools,name=TotalBlockedTasks,*",
    "org.apache.cassandra.metrics:type=ThreadPools,name=CurrentlyBlockedTasks,*",
    "org.apache.cassandra.metrics:type=DroppedMessage,name=Dropped,*",
    "org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=HitRate,*",
    "org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Hits,*",
    "org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Requests,*",
    "org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Entries,*",
    "org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Size,*",
    #"org.apache.cassandra.metrics:type=Streaming,name=TotalIncomingBytes,*",
    #"org.apache.cassandra.metrics:type=Streaming,name=TotalOutgoingBytes,*",
    "org.apache.cassandra.metrics:type=Client,name=connectedNativeClients,*",
    "org.apache.cassandra.metrics:type=Client,name=connectedThriftClients,*",
    "org.apache.cassandra.metrics:type=Table,name=WriteLatency,*",
    "org.apache.cassandra.metrics:type=Table,name=ReadLatency,*",
    "org.apache.cassandra.net:type=FailureDetector,*",
    ]
    #blacklistObjectNames: ["org.apache.cassandra.metrics:type=ColumnFamily,*"]
    rules:
    - pattern: org.apache.cassandra.metrics<type=(Connection|Streaming), scope=(\S*), name=(\S*)><>(Count|Value)
    name: cassandra_$1_$3
    labels:
    address: "$2"
    - pattern: org.apache.cassandra.metrics<type=(ColumnFamily), name=(RangeLatency)><>(Mean)
    name: cassandra_$1_$2_$3
    - pattern: org.apache.cassandra.net<type=(FailureDetector)><>(DownEndpointCount)
    name: cassandra_$1_$2
    - pattern: org.apache.cassandra.metrics<type=(Keyspace), keyspace=(\S*), name=(\S*)><>(Count|Mean|95thPercentile)
    name: cassandra_$1_$3_$4
    labels:
    "$1": "$2"
    - pattern: org.apache.cassandra.metrics<type=(Table), keyspace=(\S*), scope=(\S*), name=(\S*)><>(Count|Mean|95thPercentile)
    name: cassandra_$1_$4_$5
    labels:
    "keyspace": "$2"
    "table": "$3"
    - pattern: org.apache.cassandra.metrics<type=(ClientRequest), scope=(\S*), name=(\S*)><>(Count|Mean|95thPercentile)
    name: cassandra_$1_$3_$4
    labels:
    "type": "$2"
    - pattern: org.apache.cassandra.metrics<type=(\S*)(?:, ((?!scope)\S*)=(\S*))?(?:, scope=(\S*))?,
    name=(\S*)><>(Count|Value)
    name: cassandra_$1_$5
    labels:
    "$1": "$4"
    "$2": "$3"
  • 3、在conf目录下cassandra-env.sh增加javaagent
    1
    JVM_OPTS="$JVM_OPTS -javaagent:$CASSANDRA_HOME/lib/jamm-0.3.0.jar -javaagent:$CASSANDRA_HOME/lib/jmx_prometheus_javaagent-0.16.0.jar=7070:${CASSANDRA_HOME}/conf/cassandra-jmx.yml"
  • 4、etcd注册
    1
    etcdctl put /prometheus/discovery/cassandra/cassandra-v2-2 '{"name":"cassandra-v2-2","address":"10.10.21.177:7070","labels": [{"key":"c_type","val":"cassandra"},{"key":"ip","val":"10.10.21.177"}]}'

k8s常见问题总结

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
    1、iptables 规则丢包

    2、listen了源port_range范围内的端口
    比如 net.ipv4.ip_local_port_range="1024 65535",但又listen了 9100 端口,当作为client发请求时,选择一个port_range范围内的端口作为源端口,就可能选到9100,但这个端口已经被listen了,就会选取失败,导致丢包。

    3、高并发 NAT 导致 conntrack 插入冲突
    如果高并发并且做了NAT,比如使用了ip-masq-agent,对集群外的网段或公网进行SNAT,又或者集群内访问Service被做了DNAT,再加上高并发的话,内核就会高并发进行NAT和conntrack插入,当并发NAT后五元组冲突,最终插入的时候只有先插入的那个成功,另外冲突的就会插入失败,然后就丢包了。
    可以通过conntrack -S确认,如果insert_failed计数在增加,说明有conntrack插入冲突。

    4、socket buffer满导致丢包
    netstat -s | grep "buffer errors"的计数统计在增加,说明流量较大,socket buffer不够用,需要调大下buffer容量
    net.ipv4.tcp_wmem = 4096 16384 4194304
    net.ipv4.tcp_rmem = 4096 87380 6291456
    net.ipv4.tcp_mem = 381462 508616 762924
    net.core.rmem_default = 8388608
    net.core.rmem_max = 26214400
    net.core.wmem_max = 26214400

    5、MTU 不一致导致丢包
    如果容器内网卡 MTU 比另一端宿主机内的网卡 MTU 不一致(通常是 CNI 插件问题),数据包就可能被截断导致一些数据丢失
    如果容器内的MTU更大,发出去的包如果超过MTU可能就被丢弃了(通常节点内核不会像交换机那样严谨会分片发送)。
    同样的,如果容器内的MTU更小,进来的包如果超过MTU可能就被丢弃。
    MTU 大小可以通过 ip address show 或 ifconfig 来确认。

    6、连接队列满导致丢包
    对于TCP连接,三次握手建立连接,没建立成功前存储在半连接队列,建立成功但还没被应用层accept之前,存储在全连接队列。队列大小是有上限的,如果满了就会丢包
    如果并发太高或机器负载过高,半连接队列可能会满,新来的SYN建连包会被丢包
    如果应用层accept连接过慢,会导致全连接队列堆积,满了就会丢包,通常是并发高、机器负载高或应用夯死等原因
    可通过netstat -s | grep -E 'drop|overflow'确认
    全连接队列可通过ss -lnt 观察Rec-Q
    net.ipv4.tcp_max_syn_backlog = 8096 #半连接队列长度
    net.core.somaxconn = 32768 #全连接队列长度

    7、源端口耗尽
    当client发起请求或外部流量通过NodePort进来时会进行SNAT,会从当前netns中选择一个端口作为源端口,端口范围由net.ipv4.ip_local_port_range参数决定,如果并发大就可能导致端口耗尽从而丢包
  • ipvs连接复用引起系列问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    当client对service发起大量新建TCP连接时,新的连接被转发到Terminating已销毁的Pod上,导致持续丢包(报错no route to host),其根本原因是内核ipvs连接复用引发

    当client_ip:client_port复用发生时,对于TIME_WAIT状态下的ip_vs_conn进行重新调度使得connection在rs上分布更均衡以提高性能
    如果conn_reuse_mode=0则会复用旧ip_vs_conn里的rs,使得连接更不均衡

    如果conn_reuse_mode为1表示不复用,每次新建的连接都会重新调度rs并新建ip_vs_conn,但在新建连接时(SYN包),如果client_ip:client_port匹配到了ipvs旧连接(TIMEWAIT状态)且使用了conntrack,就会丢掉第一SYN包,等待重传后(1s)才能连接成功,从而导致连接性能急剧下降。

    如果conn_reuse_mode=0表示复用
    只要client_ip:client_port匹配上ip_vs_conn(发生复用),就会直接发给对应rs,不管rs当前是什么状态,即使rs的weight为0(通常是TIME_WAIT状态也会转发,而TIME_WAIT的rs通常是Terminating状态已销毁的Pod,故转发连接出现异常。
    高并发下大量复用,没有为新连接重新调度rs,直接转发到复用连接对应rs上,从而导致新连接被固化到部分rs上。

    实际业务场景:
    1、滚动更新连接异常,被访问服务滚动更新时,Pod有新建也有销毁,ipvs发生连接复用时转发到已销毁的Pod导致连接异常(no route to host)
    2、滚动更新负载不均,由于复用时不会重新调度连接,导致新连接被固化到部分Pod上。
    3、新扩容的Pod接收流量少,同样也是由于复用时不会重新调度连接,导致新连接被固化到扩容之前这些Pod上

    如何规避:
    集群外调用
    1、使用SLB直通Pod,通常通过NodePort暴露端口,前面SLB将流量转发到NodePort上,在通过ipvs转发到后端Pod上,云厂商都支持SLB直通Pod,直接将请求转发到Pod,不经过NodePort,从而没有ipvs转发,从而在流量接入层规避次问题。
    2、使用ingress转发,在集群内部署ingress controller(如nginx ingress),流量到达ingress再向后转发时(直接转发到集群内的Pod),不会经过service转发而且直接转发到service对应的Pod IP:Port,从而没有ipvs转发,ingress controller本身结合方案一SLB直通Pod方式部署。
    集群内调用
    集群内的服务间调用,默认会走ipvs转发,对于高并发场景的业务,可考虑Service Mesh(如istio)来治理流量,服务间转发由sidecar代理,从而没有ipvs转发。

    终结方案:内核修复
  • 优雅重启

    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
    容器终止流程:
    1、Pod被删除,状态标记为Terminating
    2、kube-proxy更新转发规则,将Pod从service的endpoint列表中删除,新的流量不再转发到次Pod
    3、如果Pod配置了preStop钩子,将执行此钩子
    4、kubelet对Pod中各个container发送SIGTERM信号以通知容器进程开始优雅停止
    5、等待容器进程完全停止,如果在TerminationGracePeriodSeconds内(默认30s)还没完全停止,就发送SIGKILL信号强制杀死进程
    6、所有容器进程终止,清理Pod资源

    业务代码处理SIGTERM信号,主要逻辑不接受新增连接,继续处理留存连接,所有连接处理完后才退出

    注意:如果容器启动入口使用脚本,容器主进程是shell,业务进程是通过shell启动,成了shell进程的子进程;
    shell进程默认不会处理SIGTERM信号,也不会将信号传递给子进程。

    解决方案:
    1、shell使用exec启动,exec让该程序进程替代当前shell进程,即让新启动进程成为主进程
    #!/bin/bash
    exec /opt/cmdb
    2、多进程场景,使用trap传递信号
    #!/bin/bash
    /opt/app1 & pid1="$!"
    /opt/app1 & pid2="$!"

    handle_sigterm() {
    kill -SIGTERM $pid1 $pid2
    wait $pid1 $pid2
    }
    trap handle_sigterm SIGTERM

    wait

    最优方案:使用init系统
    install -y dumb-init
    ENTRYPOINT ["dumb-init", "--"]
    CMD ["/start.sh"]

    使用preStop钩子
    某些情况下,Pod被删除的一小段时间内,仍然可能有新连接转发过来,因为kubelet与kube-proxy同时watch到Pod被删除,kubelet有可能在kube-proxy同步完规则前已经停止容器,这就导致一些新的连接被转到正在删除的Pod,而通常情况下,当程序收到SIGTERM信号后不再接收新连接,只保持存量连接继续处理,故导致Pod删除瞬间部分请求失败。此类情况,可利用preStop钩子先sleep会儿,等待kube-proxy同步完规则再开始停止容器内进程
    lifecycle:
    preStop:
    exec:
    command:
    - sleep
    - 5

    调整优雅时间
    如果需要优雅终止时间较长(preStop+业务进程停止超过默认30s),可自定义terminationGracePeriodSeconds,避免过早被SIGKILL杀死

k8s集群升级

  • 通过kubeadm安装k8s集群
    • 升级目标:原版本1.16.9,升级到1.20.4
    • 升级流程:
      • 备份kubeadm config kubeadm config view > kubeadm.config
      • master节点安装kubelet-1.17.17 kubeadm-1.17.17 kubectl-1.17.17
      • 查看升级计划kubeadm upgrade plan
      • 开始升级kubeadm upgrade apply v1.17.17
      • 节点安装kubelet-1.17.17 kubeadm-1.17.17 kubectl-1.17.17
      • 重启所有节点kubelet systemctl daemon-reload systemctl restart kubelet
      • 查看版本kubectl get nodes
    • 升级1.18.8重复以上流程
    • 升级1.20.4重复以上流程

docker registry

  • 1、方法一 安装httpd-tools生成auth
    1
    2
    yum install httpd-tools
    htpasswd -Bbn devops codoon.com > /codoon/registry/auth/htpasswd
  • 2、方法二 registry:2.7.0 生成auth
    1
    docker run --entrypoint htpasswd registry:2.7.0 -Bbn devops codoon.com > /codoon/registry/auth/htpasswd
  • 3、启动docker-registry2
    1
    docker run -d -p 5000:5000 -v /codoon/registry/auth/:/auth/ -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=basic-realm" -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" -v /codoon/registry/:/var/lib/registry/ registry:2.7.0
  • 4、还可以配置镜像存储到阿里OSS
    1
    2
    3
    4
    5
    #通过环境变量配置 -e
    "REGISTRY_STORAGE_OSS_ACCESSKEYID=xxxx" "REGISTRY_STORAGE_OSS_ACCESSKEYSECRET=xxxxx"
    "REGISTRY_STORAGE_OSS_REGION=oss-cn-xxx"
    "REGISTRY_STORAGE_OSS_BUCKET=xxxxx"
    "REGISTRY_STORAGE_OSS_ENDPOINT=xxxxxx-internal.aliyuncs.com"
  • 5、常用api
    1
    2
    3
    4
    #查看镜像列表 n获取多少条
    /v2/_catalog?n=1000
    #获取某个镜像的tag列表
    /v2/image_name/tags/list

Lua基本用法

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/local/bin/lua

print("hello world!")
--单行注释
--[[
多行注释
多行注释
]]--

-- 默认变量被认为是全局的,全局变量不需要声明
print(A)
A = 10
print(A)
-- 删除一个全局变量,只需将变量赋值为nil

--[[
数据类型
nil
boolean
number 双精度类型的实浮点数
string 单双引号的字符串
function c或lua编写的函数
userdata 任意存储在变量中c数据结构
thread 执行的独立线路,用于执行协同程序
table 关联数组,数组的索引可以是数字、字符串或表类型。table的创建是通过构造表达式来完成,最简单构造表达式{} 空表
]]--
print(type("hello world!"))
print(type(3.14*3))
print(type(print))
print(type(type))
print(type(true))
print(type(nil))

-- .. 字符串链接符
Tab1 = {key1 = "val1", key2 = "val2", "val3"}
for k,v in pairs(Tab1) do
print(k .. " - " .. v)
end

Tab1.key1 = nil
for k,v in pairs(Tab1) do
print(k .. " -- " .. v)
end

-- 字符串块
local gocode = [[
package main

import "fmt"

func main() {
fmt.Println("hi ropon!")
}
]]
print(gocode)

print(3.14 .. 15926)

-- #来计算字符串的长度
Str1 = "www.ropon.top"
print(#Str1)

-- 创建一个空的table
local tab1 = {}
-- 直接初始化表
local tab2 = {"go", "python", "php", "lua"}

print(...)
local a = {}
a["key"] = "val"
local key = 10
a[key] = 18
a[key] = a[key]+1
for k,v in pairs(a) do
print(k .. " : " .. v)
end
-- 注意表的索引默认是从1开始
print(...)
for k, v in pairs(tab2) do
print("key", k)
end
-- 函数
function Func1(n)
if n == 0 then
return 1
else
return n* Func1(n -1)
end
end

print(Func1(5))
local func2 = Func1
print(func2(5))
-- 匿名函数
function TestF1(tab, fun)
for k ,v in pairs(tab) do
print(fun(k,v))
end
end
local tab={key1="val1", key2="val2"};
TestF1(tab,
--匿名函数
function(k,v)
return k.."="..v;
end)
-- 循环
-- while true do

-- end

-- for index, value in ipairs(t) do

-- end

-- for key, value in pairs(t) do

-- end

-- 字符串操作方法
print(string.upper("ropon"))
print(string.lower("Ropon"))
-- 要操作的字符串/被替换的字符/要替换的字符/替换几次
print(string.gsub("www.ropon.top","ropon","pengge",1))
print(string.find("www.ropon.top","ropon",1))
print(string.reverse("ropon.top"))
print(string.format("my name is %s,age is %d","ropon",18))
print(string.len("ropon.top"))
print(string.sub("www.ropon.top",1,3))

-- 数组 索引默认从1开始,也可以从0开始
local arr1 = {"golang","lua"}
for i=0,2 do
print(arr1[i])
end

local arr2 = {}
for i = -2, 2 do
arr2[i] = i*2
end

for i = -2, 2 do
print(arr2[i])
end
-- 多维数组
local arr3 = {}
for i = 1, 3 do
arr3[i] = {}
for j = 1, 3 do
arr3[i][j] = i*j
end
end

for i=1,3 do
for j=1,3 do
print(arr3[i][j])
end
end

require("module")

print(module.constant)
module.func3()

local m = require("module")
m.func1()

local req = require("socket.http")
body,ret = req.request("https://ip.ropon.top")
print(body)

分享一套运维面试题

  • 1、都说linux下一切皆文件,你是怎么理解的?
  • 2、都说socket在linux实际是一个文件,是通过什么技术将socket变成一个文件的?
  • 3、linux下软/硬连接是什么,他们有什么区别?
  • 4、linux 下一个用户登录以下文件加载顺序是?~/.bash_profile, ~/.bash_login, ~/.profile, ~/.bashrc, /etc/profile
  • 5、都说systemd他可以并行启动,他的实现原理是? 僵尸进程/孤儿进程他们是?区别是啥?为什么说ssh很安全?具体原理是?如何保证的
  • 6、shell相关 [[]] [] (()) () 他们区别是?
  • 7、shell fuction如何返回字符串
  • 8、shell $# $* 是什么意思
  • 9、如何使用shell实现一个爬虫,你描述下整个过程,会用到哪些命令?
  • 10、redis哨兵机制主从是如何切换的?
  • 11、网络 mtu是什么?他发生在那一层,如果2个设备mtu值不同,以谁为准,那么2个设备是如何协商的?详细协商流程是?
  • 12、0.0.0.0 255.255.255.255 这2个ip地址是什么意思?他们的适用场景是? VIP如何漂移的?他的底层原理是?
  • 13、tunnel VPN 翻墙他实现的原理是,他是怎么绕过防火墙的,数据包传输流程,他在那一层欺骗了防火墙等
  • 14、nginx相关 正向/反向代理是什么意思?他们区别是?
  • 15、一般架构 nginx集群 –反向代理–> tomcat,为什么不能反过来部署呢?高并发/动静分离等,这些我程序也可以做
  • 16、都说nginx轻量级,比如对比tomcat,epoll是操作系统机制,与nginx无关,我自己开发也可以调用,内存占用小/启动快这不是特点,模块化这也不是特点,我自己开发程序也可以实现。。。
  • 17、都说docker轻量级,他轻量在哪里,从技术角度分析,咱们都是搞技术,不需要从产品角度分析他为什么轻量?
  • 18、docker volume bind mount他们区别是什么? 如何构建最小的镜像,说说你的思路?
  • 19、docker -p/P 他的实现原理是什么?
  • 20、Infrastructure as code思想,你是怎么理解的?
  • 21、资产管理/配置管理他们分别是?区别是?
  • 22、持续部署/持续交付他们是?区别是?
  • 23、ci/cd带来的好处是?蓝绿发布是?他的优缺点?
  • 24、jenkins 如何通过原生方式备份配置/job等(不通过jenkins提供导入导出/及拷贝整个目录)
  • 25、pipeline是什么,他有什么优势?
  • 26、jenkins 我要在其他环境重放job如何实现?
  • 27、shift-left你是怎么理解的?
  • 28、git相关 svn/git分别是?他们有什么区别?
  • 29、git中index/local/remote/workspace 他们是?比如git add xxx 他发生了什么?
  • 30、都说git管理/切分支等轻量,他们轻量在哪里,具体原理是?
  • 31、git rebase xxx 发送冲突时,他的根本原因是?不要说具体场景;git fetch/git pull 他们区别是?
  • 32、你平时开发使用一套分支流程是?为什么要这么做?他的优缺点是?
  • 33、kafka相关 broker partition segment 他们是?他们关系是?
  • 34、kafka如何保证数据可靠?ISR是什么?他的工作机制是?
  • 35、python相关 GIL是什么,为什么需要它?
  • 36、python decoration(装饰器) 他的场景是?他的原理是?
  • 37、如果要你实现一个sidecar,说说你的思路?
  • 38、线上一个服务响应很慢,你如何排查,排查流程是?

vue3之reactive底层Proxy原理

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
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<script>
let person = {
name: 'ropon',
age: 18
}
const p = new Proxy(person, {
get (target, propsName) {
console.log(target, propsName)
console.log(`有人读取了p身上${propsName}的属性`)
return target[propsName]
},
set (target, propsName, value) {
console.log(`有人设置或新增了p身上${propsName}属性,值是${value}`)
target[propsName] = value
},
deleteProperty (target, propsName) {
console.log(`有人删除了p身上${propsName}属性`)
return delete target[propsName]
}
})
</script>
</body>

</html>

golang panic堆栈分析


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
52
package main

import (
"fmt"
"runtime/debug"
)

type trace struct{}

type user struct {
name string
}

func main() {
slice := make([]string, 2, 4)
//TestStack(slice, "hello", 10)
var t trace
t.TestStack(slice, "hello", 10)
//Example(true, false, true, 25)
}

func TestStack(slice []string, str string, i int) {
panic("Want stack trace")
}

func (t *trace) TestStack(slice []string, str string, i int) (u *user, err error) {
fmt.Printf("Receiver Address: %p\n", t)
panic("Want stack trace")
}

func Example(b1, b2, b3 bool, i uint8) {
defer func() {
err := recover()
if err != nil {
stackStr := string(debug.Stack())
fmt.Println(stackStr)
}
}()
panic("Want stack trace")
}

/*
goroutine 1 [running]:
main.(*trace).TestStack(0x60, {0x11a95b8, 0x60, 0x113d6c0}, {0xc00008c000, 0x0}, 0xc0000001a0)
0x60 *t的地址
{0x11a95b8, 0x60, 0x113d6c0} slice []string 3字节 底层数组指针/长度/容量
{0xc00008c000, 0x0} str string 底层字节数组指针/长度
0xc0000001a0 i int

main.Example(0xa0, 0x1, 0x0, 0x0)
*/

测试写博客

  • 关于Lomorage介绍,请点击此处了解

  • 下面介绍如何安装:

小米R3G路由器安装Lomorage

  • 1、安装挂载opt分区

    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
    #1.替换源为阿里源
    cat /etc/opkg/distfeeds.conf
    src/gz openwrt_core https://mirrors.aliyun.com/openwrt/releases/21.02.3/targets/ramips/mt7621/packages
    src/gz openwrt_base https://mirrors.aliyun.com/openwrt/releases/21.02.3/packages/mipsel_24kc/base
    src/gz openwrt_luci https://mirrors.aliyun.com/openwrt/releases/21.02.3/packages/mipsel_24kc/luci
    src/gz openwrt_packages https://downloads.openwrt.org/releases/21.02.3/packages/mipsel_24kc/packages
    src/gz openwrt_routing https://mirrors.aliyun.com/openwrt/releases/21.02.3/packages/mipsel_24kc/routing
    src/gz openwrt_telephony https://mirrors.aliyun.com/openwrt/releases/21.02.3/packages/mipsel_24kc/telephony
    #2.USB和存储器的内核模块
    opkg install kmod-usb-storage kmod-scsi-generic
    #文件系统
    opkg install kmod-fs-ext4
    #USB辅助工具、分区、格式化等 非必要
    opkg install usbutils fdisk e2fsprogs
    #分区
    fdisk /dev/sdb
    #格式化
    mkfs.ext4 /dev/sdb1
    #挂载
    mkdir /opt
    mount /dev/sdb1 /opt
    #开机自动挂载
    opkg install block-mount blkid blkid非必要
    #方式一:查看生成配置并配置自动挂载
    block detect
    uci set fstab.@mount[-1].enabled=1
    uci commit fstab
    #方式二:vim
    vim /etc/config/fstab
    /etc/init.d/fstab enable
  • 2、安装Entware

    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
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    #!/bin/sh

    TYPE='generic'
    #TYPE='alternative'

    #|---------|-----------------------|---------------|---------------|---------------------|-------------------|-------------------|----------------------|-------------------|
    #| ARCH | aarch64-k3.10 | armv5sf-k3.2 | armv7sf-k2.6 | armv7sf-k3.2 | mipselsf-k3.4 | mipssf-k3.4 | x64-k3.2 | x86-k2.6 |
    #| LOADER | ld-linux-aarch64.so.1 | ld-linux.so.3 | ld-linux.so.3 | ld-linux.so.3 | ld.so.1 | ld.so.1 | ld-linux-x86-64.so.2 | ld-linux.so.2 |
    #| GLIBC | 2.27 | 2.27 | 2.23 | 2.27 | 2.27 | 2.27 | 2.27 | 2.23 |
    #|---------|-----------------------|---------------|---------------|---------------------|-------------------|-------------------|----------------------|-------------------|

    unset LD_LIBRARY_PATH
    unset LD_PRELOAD

    ARCH=mipselsf-k3.4
    LOADER=ld.so.1
    GLIBC=2.27

    echo 'Info: Checking for prerequisites and creating folders...'
    if [ -d /opt ]; then
    echo 'Warning: Folder /opt exists!'
    else
    mkdir /opt
    fi
    # no need to create many folders. entware-opt package creates most
    for folder in bin etc lib/opkg tmp var/lock
    do
    if [ -d "/opt/$folder" ]; then
    echo "Warning: Folder /opt/$folder exists!"
    echo 'Warning: If something goes wrong please clean /opt folder and try again.'
    else
    mkdir -p /opt/$folder
    fi
    done

    echo 'Info: Opkg package manager deployment...'
    URL=http://bin.entware.net/${ARCH}/installer
    wget $URL/opkg -O /opt/bin/opkg
    chmod 755 /opt/bin/opkg
    wget $URL/opkg.conf -O /opt/etc/opkg.conf

    echo 'Info: Basic packages installation...'
    /opt/bin/opkg update
    if [ $TYPE = 'alternative' ]; then
    /opt/bin/opkg install busybox
    fi
    /opt/bin/opkg install entware-opt

    # Fix for multiuser environment
    chmod 777 /opt/tmp

    for file in passwd group shells shadow gshadow; do
    if [ $TYPE = 'generic' ]; then
    if [ -f /etc/$file ]; then
    ln -sf /etc/$file /opt/etc/$file
    else
    [ -f /opt/etc/$file.1 ] && cp /opt/etc/$file.1 /opt/etc/$file
    fi
    else
    if [ -f /opt/etc/$file.1 ]; then
    cp /opt/etc/$file.1 /opt/etc/$file
    fi
    fi
    done

    [ -f /etc/localtime ] && ln -sf /etc/localtime /opt/etc/localtime

    echo 'Info: Congratulations!'
    echo 'Info: If there are no errors above then Entware was successfully initialized.'
    echo 'Info: Add /opt/bin & /opt/sbin to $PATH variable'
    echo 'Info: Add "/opt/etc/init.d/rc.unslung start" to startup script for Entware services to start'
    if [ $TYPE = 'alternative' ]; then
    echo 'Info: Use ssh server from Entware for better compatibility.'
    fi
    echo 'Info: Found a Bug? Please report at https://github.com/Entware/Entware/issues'
  • 安装lomorage

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    #1.查看cpu架构
    cat /proc/cpuinfo
    opkg install lscpu
    lscpu | grep "Byte Order"
    uname -a
    #2.调整/etc/profile
    PATH添加/opt/bin/go/bin:/opt/bin:/opt/sbin
    LD_LIBRARY_PATH添加/opt/lib
    #3.安装依赖及工具
    opkg install coreutils-stat perl-image-exiftool ffmpeg ffprobe lsblk
    #4.安装lomorage
    cat /opt/etc/opkg.conf
    src/gz entware http://bin.entware.net/mipselsf-k3.4
    src/gz lomorage https://lomoware.lomorage.com/opkg/mipsel-3.4
    dest root /
    lists_dir ext /opt/var/opkg-lists
    arch all 100
    arch mipsel-3x 150
    arch mipsel-3.4 160

    opkg update --no-check-certificate
    opkg install lomo-backend --no-check-certificate
    #安装完成后会自动启动,默认挂载目录/mnt,端口默认8000,可自行修改/etc/init.d/lomod