UUBlog

UUBlog

在单台 Nginx 负载能力冗余的情况,没必要申请更多资源做负载均衡,只需要保障高可用即可。 此配置为了应对 nginx单台机器 崩溃,或者重启 导致服务不可用的时候,能自动切换到备用的 nginx。两台 nginx 理应部署在不同的物理机,避免物理机重启的时候彻底不可用。

根据目前医院情况,Nginx 有两台,分别是 100.100.100.245 和 100.100.100.247,那寻找一个还未被使用的同网段 ip,这里假设是 100.100.100.246 还未被使用。 未禁止 ping 的时候 ping 100.100.100.246 是没有回应的。

那目前我们掌握的IP信息大概如下:

- Nginx master: 100.100.100.245

- Nginx slave: 100.100.100.247

- Virtual IP(VIP): 100.100.100.246

配置Keepalived

在Nginx master: 100.100.100.245 配置

执行以下命令安装 keepalived

1
yum install -y keepalived

配置 nginx 健康检查脚本。该脚本主要实现以下功能

  1. 如果不存在 nginx 进程,则尝试启动 nginx 进程。这里我们是用 systemd 管理的 nginx,所以用 systemctl start nginx ,如果是别的方式启动,请写具体的启动命令。
  2. 如果依然启动失败 则杀死 keepalived 进程 使得 VIP 立马飘走。不用等待权重慢慢减少再飘走。
1
2
3
4
5
6
7
8
9
10
11
cat <<EOF> /usr/local/src/nginx_check.sh
#! /bin/bash
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
systemctl start nginx
sleep 2
if [ `ps -C nginx --no-header| wc -l` -eq 0 ];then
killall keepalived
fi
fi
EOF

配置 keepalived

以下脚本注意修改以下几个点

  • 网卡设备名 我这里是 eth0 ,可以使用 ip a 这个命令查看,如果不是 eth0,则修改 interface 后面的 eth0 为你正确的设备名。
  • VIP 我们前面选定的 VIP 是 100.100.100.246
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
cat <<EOF> /etc/keepalived/keepalived.conf 
global_defs {
router_id LVS_Main
}

vrrp_script chk_nginx {
script "/usr/local/src/nginx_check.sh"
interval 2
weight 2
}

vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 160
priority 100
advert_int 1
authentication {
auth type PASS
auth pass 1111
}
track_script {
chk_nginx
}
virtual_ipaddress {
100.100.100.246/24
}
}
EOF

启动 keepalived 并设置开机启动

1
systemctl enable --now keepalived

在 Nginx slave: 100.100.100.247 配置

执行以下命令安装 keepalived

1
yum install -y keepalived

配置 nginx 健康检查脚本

1
2
3
4
5
6
7
8
9
10
11
cat <<EOF> /usr/local/src/nginx_check.sh
#! /bin/bash
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
systemctl start nginx
sleep 2
if [ `ps -C nginx --no-header| wc -l` -eq 0 ];then
killall keepalived
fi
fi
EOF

配置 keepalived

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
cat <<EOF> /etc/keepalived/keepalived.conf 
global_defs {
router_id LVS_Slave
}

vrrp_script chk_nginx {
script "/usr/local/src/nginx_check.sh"
interval 2
weight 2
}

vrrp_instance VI_1 {
state SLAVE
interface eth0
virtual_router_id 160
priority 10
advert_int 1
authentication {
auth type PASS
auth pass 1111
}
track_script {
chk_nginx
}
virtual_ipaddress {
100.100.100.246/24
}
}
EOF

启动 keepalived 并设置开机启动

1
systemctl enable --now keepalived

检查配置效果

确认两台机的 nginx 和 keepalived 都启动

1
2
systemctl status nginx
systemctl status keepalived

检查当前 VIP 是否绑定

目前 master (100.100.100.245)的priority 比较高,所以用命令 ip a 查看的时候,能看到VIP(100.100.100.246) 被绑在对应的网卡

1
ip a

nginx 停止的时候能被 keepalived 再度拉起

1
2
3
systemctl stop nginx
# 过几秒再查看 nginx 是否有启动
systemctl status nginx

测试 VIP 是否能飘走

1
2
3
4
5
6
7
# 在master
systemctl stop keepalived
# 停止后 看VIP是否没有了
ip a

# 在slave上查看 VIP 是否绑定
ip a

注意事项

  • 修改配置后一定要 ·nginx -t· 测试没问题后再 reload
  • 理应注意两台机器的配置保持同步。

简单点的话,在配置了 ssh master 上免密登录 slave 后。可以采用 rsync 命令同步配置

1
2
3
rsync -avz --delete /etc/nginx/ user@100.100.100.247:/etc/nginx/
# 重载 slave nginx 配置
ssh root@100.100.100.247 'systemctl reload nginx'

关注公众号 尹安灿

兄弟们,我又来亏钱了

今天介绍亏钱工具 freqtrade macos 下的安装

下载 freqtrade

1
git clone https://github.com/freqtrade/freqtrade.git

安装并激活python 环境

1
2
conda create --name freqtrade python=3.11
conda activate freqtrade

安装依赖

1
2
3
4
./build_helpers/install_ta-lib.sh
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt
python3 -m pip install -e .

测试

1
2
3
4
5
> freqtrade create-userdir --userdir user_data
2024-02-28 18:28:45,361 - freqtrade - INFO - freqtrade 2024.3-dev-d1028b8ca
2024-02-28 18:28:45,362 - freqtrade.configuration.directory_operations - INFO - Created user-data directory: user_data

# 能看到这个输出 基本问题不大 可以准备亏钱之旅了

安装文档

https://www.freqtrade.io/en/stable/installation/#freqtrade-install-conda-environment

关注公众号 尹安灿

近日有需求对 es 进行基准测试,来和华为云的产品来做个横向对比。简单记录下过程,以便未来能复用。

本次测试主要横向对比本地的机器、华为云 CSS、华为云上自建 ES 集群的性能。

系统均基于 centos7.x

Esrally初始化

安装 python3.9

1
2
3
4
5
6
7
yum install conda
conda create -n py39 python=3.9
conda init bash
## conda 初始化 bash 后 要 relogin 一次
conda activate py39
yum install pbzip
python3 -m pip install --user esrally

安装高版本git

esrally 有些操作依赖 git 来 fetch 它自己的一些资源

1
2
3
yum remove git-*
yum install https://packages.endpointdev.com/rhel/7/os/x86_64/endpoint-repo.x86_64.rpm
yum install git

离线下载好要准备的 track

为了不在测试开始的时候 在网络上耗费太多时间在等待下载测试数据,所以推荐先离线下载好相关数据。

1
2
3
4
5
6
7
8
9
10
esrally list tracks
# 列出可用的 tracks
这里选华为云给出的 benchmark 数据所用的数据集 geonames
curl -O https://raw.githubusercontent.com/elastic/rally-tracks/master/download.sh
chmod a+x download.sh
./download.sh geonames
tar -xf rally-track-data-geonames.tar
# 这就离线好了
ll ~/.rally/benchmarks/tracks/default/geonames/
# 可以查看track 的内容

esrally一些实用的用法

1
2
3
4
5
6
7
8
9
10
# 对已存在的集群进行 benchmark 并调整测试的索引分片数为 6 保存结果到 race.md --track-params 可以修改 track 参数 覆盖默认值
nohup esrally race --track=geonames --pipeline=benchmark-only --target-hosts=10.8.58.101:9200,10.8.58.102:9200,10.8.58.103:9200 --track-params="number_of_shards:6" --challenge=append-no-conflicts --on-error=abort --report-file=~/race.md > race.log &

# 对比两次 race 的结果
esrally list races
esrally compare --baseline a64a2e46-9cf9-4961-9cee-d793608c9de8 --contender ee79d74e-83ad-4124-9a2b-e902d742b387

# 如果在不同的机器进行的基准测试,想在一台机上对比
ll .rally/benchmarks/races/
把这个目录下对应的 race 的文件夹 拷贝到同个机器这个目录就可以了。

常见问题

有时候结果中并没有出现 ·index-append· 的数据,可以更改 ~.rallly/benchmarks/tracks/default/<对应的 track>/challenges/default.json index-append warmup-time-period 的值,从120 调整为 20

如 geonames track 的

1
2
3
4
5
6
7
8
vim ~/.rally/benchmarks/tracks/default/geonames/challenges/default.json
...
{
"operation": "index-append",
"warmup-time-period": 20,
"clients": {{bulk_indexing_clients | default(8)}},
"ignore-response-error-level": "{{error_level | default('non-fatal')}}"
},
Read more »

关注公众号 尹安灿

日志收集,考虑到我们没有主动上报日志,格式也不太统一,容器化的时候,对于我们而言采集容器标准输出的日志是更快捷达到目的的方式。

大致对比了filebeat、fluentd、fluentbit、vector、logstash后,锁定了flientbit。

虽然它功能不是最强大的,但是基本功能能满足我们的业务需求,采集输入端多样化,也能做简单的filter,支持output的端也很丰富。其次,它吃资源比较少,性能也不错。

我们只需要它收集标准输出的日志,输出到es即可。

而在k8s中,有kubesphere共献给fluent的fluent-operator。该opeator提供了fluentbit only、fluentd only、fluentbit+fluentd 这几种采集方式。对于我们而言,就fluentbit 就够了。

我选择了helm方式安装,下载了它chart (https://github.com/fluent/fluent-operator/releases/download/v2.2.0/fluent-operator.tgz)包,解压到`/app/fluent-operator/ `,直接修改里面values.yaml,这样好方便未来管理自己的部署配置。

简单按照文档,修改了fluentbit的input和output后,直接安装即可

1
2
helm upgrade --install fluent-operator --create-namespace -n fluent /app/fluent-operator/ --set containerRuntime=containerd

按照服务名区分索引名

日志是能收集了,但是所有的服务的日志,当天都放到一个索引,类似 “k8s-logs-<yyyy.mm.dd>” 这样的索引中。本地还好,如果生产环境的话,那一天单个索引就十分庞大了。而且对于不同服务日志保留天数做差异化保留的时候也不好处理,也不好直观展示各个服务日志大小。

所以能按照k8s-<service-name>-<yyyy.mm.dd>格式的话,对我们会更为理想点。截止目前的版本,没有现成的配置可以实现这个。经过研究,可以利用fluentbit的 logstash_prefix_key 这个来实现。logstash_prefix是固定的前缀,logstash_prefix_key则可以动态读取 key来作为索引名。

1.修改fluent-operator,让其支持logstash_prefix_key

经过测试,fluent-operator当前版本的模板并没有对该字段进行处理,所以要进行修改让它支持这个字段。

我需要output到es,所以我就修改了es相关的output,其它输出源的可以自己检查下。

修改 ./templates/fluentbit-output-elasticsearch.yaml

在Values.fluentbit.output.es.logstashPrefix之后增加 .Values.fluentbit.output.es.logstashPrefixKey 这段的配置,如下:

1
2
3
4
5
6
{{- if .Values.fluentbit.output.es.logstashPrefix }}
logstashPrefix: {{ .Values.fluentbit.output.es.logstashPrefix | default "ks-logstash-log" | quote }}
{{- end }}
{{- if .Values.fluentbit.output.es.logstashPrefixKey }}
logstashPrefixKey: {{ .Values.fluentbit.output.es.logstashPrefixKey | default "ks-logstash-log-key" | quote }}
{{- end }}

2.让它能获取app name作为服务名

./values.yaml 修改fluentbit部分,开启kubernetes labels,因为labels的app标签就是我们的服务名。

1
2
3
4
5
6
filter:
kubernetes:
enable: true
labels: true


3. 拼接索引名

由于logstash_prefix_key只能接受 key,且不支持嵌套的对象的key。

举个直观的例子,假设你的对象是这样的:

1
2
3
4
5
6
7
8
{
"file": "systemd.log",
"kubernetes": {
"labels": {
"app": "demo-service"
}
}
}

k8s-$kubernets["labels"]["app"] 和 像 $kubernets["labels"]["app"] 这样的值,是取不到任何内容的。像上面的例子,只接受 $file 这个值。

所以这里要实现我们的目标,我们得自己拼接出一个新的key,作为索引名。类似下面:

1
2
3
4
5
6
7
8
9
{
"file": "systemd.log",
"app_name": "k8s-demo-service",
"kubernetes": {
"labels": {
"app": "demo-service"
}
}
}

我是这样实现的,利用lua的filter,通过取出kubernetes的labels,拼接新的值。

我修改的是 cat ./templates/fluentbit-containerd-config.yaml 增加了一个 add_k8s_app_name_field 函数

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
{{- if .Values.Kubernetes -}}
{{- if .Values.fluentbit.enable -}}
{{- if .Values.fluentbit.filter.containerd.enable -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-containerd-config
data:
containerd.lua: |
function containerd( tag, timestamp, record)
if(record["logtag"]~=nil)
then
timeStr = os.date("!*t", timestamp["sec"])
t = string.format("%4d-%02d-%02dT%02d:%02d:%02d.%sZ",
timeStr["year"], timeStr["month"], timeStr["day"],
timeStr["hour"], timeStr["min"], timeStr["sec"],
timestamp["nsec"]);
record["time"] = t;
record["log"] = record["message"];
record["message"] = nil;
return 1, timestamp, record
else
return 0,timestamp,record
end
end

function add_k8s_app_name_field(tag, timestamp, record)
retcode = 0
prefix = 'k8s'
app_name = record['kubernetes']['labels']['app']
if app_name ~= nil then
app_name = prefix .. '-' .. app_name

if app_name ~= nil then
record['app_name'] = app_name
retcode = 2
end
end

return retcode, timestamp, record
end
{{- end }}
{{- end }}
{{- end }}

修改 templates/fluentbit-clusterfilter-kubernetes.yaml 增加新增的lua filter函数 对kubernetes标签进行处理

cat templates/fluentbit-clusterfilter-kubernetes.yaml

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
{{- if .Values.Kubernetes -}}
{{- if .Values.fluentbit.enable -}}
{{- if .Values.fluentbit.filter.kubernetes.enable -}}
apiVersion: fluentbit.fluent.io/v1alpha2
kind: ClusterFilter
metadata:
name: kubernetes
labels:
fluentbit.fluent.io/enabled: "true"
fluentbit.fluent.io/component: logging
spec:
match: kube.*
filters:
- kubernetes:
kubeURL: https://kubernetes.default.svc:443
kubeCAFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
kubeTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
{{- $params := omit .Values.fluentbit.filter.kubernetes "enable" }}
{{- if .Values.fluentbit.output.stdout.enable }}
{{- $_ := set $params "k8sLoggingExclude" true -}}
{{- end }}
{{- with $params }}
{{- . | toYaml | nindent 6 }}
{{- end }}
- lua:
script:
key: containerd.lua
name: fluent-bit-containerd-config
call: add_k8s_app_name_field
timeAsTable: true
- nest:
operation: lift
nestedUnder: kubernetes
addPrefix: kubernetes_
- modify:
rules:
- remove: stream
- remove: kubernetes_pod_id
- remove: kubernetes_docker_id
- remove: kubernetes_container_hash
- remove: kubernetes_labels
- nest:
operation: nest
wildcard:
- kubernetes_*
nestUnder: kubernetes
removePrefix: kubernetes_
{{- end }}
{{- end }}
{{- end }}

主要增加了

1
2
3
4
5
6
- lua:
script:
key: containerd.lua
name: fluent-bit-containerd-config
call: add_k8s_app_name_field
timeAsTable: true

到这里基本就满足所有的条件了,为了不影响systemd的日志收集和归类,也给它的lua filter增加app_name字段.

./templates/fluentbit-lua-config.yaml

1
new_record["app_name"] = "systemd"

经过前面的修改后,只要在values.yaml 设置 fluentbit.output.es.logstashPrefixKey=”$app_name” 即可

4. 应用变更

我修改过的values.yaml,去除了无关部分后主要如下:

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
Kubernetes: true
fluentbit:
crdsEnable: true
enable: true
image:
repository: "hub.xxxx.com/library/fluent-bit"
tag: "v2.0.11"
input:
tail:
enable: true
refreshIntervalSeconds: 10
memBufLimit: 50MB
path: "/var/log/containers/*.log"
skipLongLines: false
systemd:
enable: true
path: "/var/log/journal"
includeKubelet: true
output:
es:
enable: true
# 如果多个host的话,用hosts
host: "es.xxx.local"
port: 9200
logstashFormat: true
logstashPrefixKey: "$app_name"
filter:
kubernetes:
enable: true
labels: true
annotations: false
k8sLoggingExclude: true
containerd:
enable: true
systemd:
enable: true

再来helm更新一下,搞定

1
helm upgrade --install fluent-operator --create-namespace -n fluent /app/fluent-operator/ --set containerRuntime=containerd

效果如下:

1
2
3
4
yellow open k8s-kiali-2023.05.10                iWEHK0gKR6GoPKjO3gH6Eg 1 1      30      0 105.9kb 105.9kb
yellow open k8s-nginx-deploy-2023.05.10 OCzPIKWgRneSn27J-o4k9g 1 1 16 0 76.3kb 76.3kb
yellow open k8s-istiod-2023.05.10 GlA1dI7aQDqPFD-5ZMX6iw 1 1 797 0 329.5kb 329.5kb
yellow open k8s-reviews-2023.05.10 41Ifiq3cTQGBKdJ4Fn2MJQ 1 1 18 0 86.6kb 86.6kb

关注公众号 尹安灿

背景是我们希望能在k8s中通过DNS方式,访问服务的FQDN来调用虚拟机注册到nacos的服务。

我们vm和k8s的网段配置了相关路由能相互访问

之前nacos有维护了一个同步去coredns的项目,但是年久失修,支持的nacos版本和coredns版本都不高。后面在官方文档找资料的时候,发现nacos是支持istio MCP协议的 Pilot MCP协议介绍,于是采取这个方案来完成目标。《Nacos 1.1.4发布,业界率先支持Istio MCP协议

环境:

istio: 1.10

nacos: 2.1.0

配置nacos开启MCP Server

进入nacos配置目录,执行以下命令,把 nacos.istio.mcp.server.enabled 值设置为 true。重启nacos server 让它运行MCP Server。

1
[root@dev_10.1.10.209 nacos]#sed -i 's/nacos.istio.mcp.server.enabled=false/nacos.istio.mcp.server.enabled=true/g' conf/application.properties

重启后nacos MCP server会监听 18848 端口

1
2
3
[root@dev_10.1.10.209 nacos]#lsof -i:18848
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
java 98108 root 171u IPv4 834348182 0t0 TCP *:18848 (LISTEN)

配置istio添加MCP server sources

我测试的nacos server所在的服务器IP10.1.10.209

nacos MCP server监听的端口 18848

编辑istio的configmap,添加以下配置

1
kubectl edit -n istio-system cm istio
1
2
configSources:
- address: xds://10.1.10.209:18848

添加后的配置大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
data:
mesh: |-
accessLogFile: /dev/stdout
configSources:
- address: xds://10.1.10.209:18848
defaultConfig:
discoveryAddress: istiod.istio-system.svc:15012
proxyMetadata: {}
tracing:
zipkin:
address: zipkin.istio-system:9411
enablePrometheusMerge: true
rootNamespace: istio-system
trustDomain: cluster.local
meshNetworks: 'networks: {}'

重启下istiod 连接MCP server同步信息

1
kubectl rollout restart -n istio-system deployment istiod
Read more »

关注公众号 尹安灿

Istio EnvoyFilter+Lua 简单实现动态路由转发

因为种种原因,我们需要实现一个将服务名和subset携带在http请求的header里面,根据这个来实现将服务转发去特定的istio的服务的subset。

比如以下example:

  • 携带 service: msg-group tag: gray 的话,将其路由去msg-group 的gray subset。

    该版本返回数据: Call Read from msg-group,By New version!!!!

  • 携带 service: msg-group tag: default 的话,将其路由去msg-group 的default subset。

    改版本返回数据: Call Read from msg-group

用istio的Virtual Service来实现

大概如下:

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
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: onroute-com
namespace: develop
spec:
gateways:
- msggroup-gateway # 这个gateway - '*'
hosts:
- onroute.com
http:
- match:
- headers:
service:
exact: msg-group
tag:
exact: gray
route:
- destination:
host: msg-group.develop.svc.cluster.local
subset: gray
headers:
response:
set:
test: This value added By static route to gray
- match:
- headers:
service:
exact: msg-group
route:
- destination:
host: msg-group.develop.svc.cluster.local
subset: default
headers:
response:
set:
test: This value added By static route default

请求构造及结果如下:

注:10.99.20.74 是istio-ingressgateway的IP

请求 msg-group 的 gray 版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# curl -v -H "Host: onroute.com" -H "service: msg-group" -H "tag: gray" 10.99.20.74/read
* Trying 10.99.20.74:80...
* Connected to 10.99.20.74 (10.99.20.74) port 80 (#0)
> GET /read HTTP/1.1
> Host: onroute.com
> User-Agent: curl/7.77.0
> Accept: */*
> service: msg-group
> tag: gray
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/plain;charset=UTF-8
< content-length: 43
< date: Fri, 16 Sep 2022 07:36:35 GMT
< x-envoy-upstream-service-time: 7
< server: istio-envoy
< test: This value added By static route to gray
<
* Connection #0 to host 10.99.20.74 left intact
Call Read from msg-group,By New version!!!!%

请求 msg-group 的 default 版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
curl -v -H "Host: onroute.com" -H "service: msg-group" -H "tag: default" 10.99.20.74/read
* Trying 10.99.20.74:80...
* Connected to 10.99.20.74 (10.99.20.74) port 80 (#0)
> GET /read HTTP/1.1
> Host: onroute.com
> User-Agent: curl/7.77.0
> Accept: */*
> service: msg-group
> tag: default
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/plain;charset=UTF-8
< content-length: 24
< date: Fri, 16 Sep 2022 07:38:41 GMT
< x-envoy-upstream-service-time: 12
< server: istio-envoy
< test: This value added By static route default
<
* Connection #0 to host 10.99.20.74 left intact
Call Read from msg-group%

能实现问题,但是有几个小问题

  1. 配置不够灵活,如果面对几百个服务,配置是个巨大的工作量。
  2. 不同的namespace要配置不同的规则。

用Envoy Filter + Lua来实现

针对上面提到的问题,如果用EnvoyFiltter来实现动态的路由的话,就可以解决这个问题。

解决思路都是围绕config.route.v3.RouteAction cluster_header 这个字段来的

(string) Envoy will determine the cluster to route to by reading the value of the HTTP header named by cluster_header from the request headers. If the header is not found or the referenced cluster does not exist, Envoy will return a 404 response.

  1. 设置route的cluster_header字段为 x-service
  2. 从header读取service和tag的值组合出upstream cluster格式,并将x-service头替换为它
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
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: onroute-routing
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
patch:
operation: ADD
value:
name: dynamic_routing
domains:
- 'onroute.com'
- 'onroute.com:*'
routes:
- name: "path-matching"
match:
prefix: "/"
route:
cluster_header: "x-service"
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.http_connection_manager"
subFilter:
name: "envoy.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.service-helper
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua"
inlineCode: |
function envoy_on_request(request_handle)
local headers = request_handle:headers()
local mesh_service = headers:get("service")
local tag = headers:get("tag")
if mesh_service ~= nil then
if tag ~= nil then
request_handle:headers():replace("x-service", "outbound|80|" .. tag .. "|" .. mesh_service .. ".svc.cluster.local")
end
end
end

function envoy_on_response(response_handle)
response_handle:headers():add("test", "from envoy filter response")
end

下面是测试结果:

请求 msg-group的 gray 版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
curl -v -H "Host: onroute.com" -H "service: msg-group.develop" -H "tag: gray" 10.99.20.74/read
* Trying 10.99.20.74:80...
* Connected to 10.99.20.74 (10.99.20.74) port 80 (#0)
> GET /read HTTP/1.1
> Host: onroute.com
> User-Agent: curl/7.77.0
> Accept: */*
> service: msg-group.develop
> tag: gray
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/plain;charset=UTF-8
< content-length: 43
< date: Fri, 16 Sep 2022 08:07:29 GMT
< x-envoy-upstream-service-time: 16
< server: istio-envoy
< test: from envoy filter response
<
* Connection #0 to host 10.99.20.74 left intact
Call Read from msg-group,By New version!!!!%

请求 msg-group的 default 版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
curl -v -H "Host: onroute.com" -H "service: msg-group.develop" -H "tag: default" 10.99.20.74/read
* Trying 10.99.20.74:80...
* Connected to 10.99.20.74 (10.99.20.74) port 80 (#0)
> GET /read HTTP/1.1
> Host: onroute.com
> User-Agent: curl/7.77.0
> Accept: */*
> service: msg-group.develop
> tag: default
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: text/plain;charset=UTF-8
< content-length: 24
< date: Fri, 16 Sep 2022 08:12:40 GMT
< x-envoy-upstream-service-time: 14
< server: istio-envoy
< test: from envoy filter response
<
* Connection #0 to host 10.99.20.74 left intact
Call Read from msg-group%

参考资料

关注公众号 尹安灿

优秀的容量治理 不但能解决当前问题,还能防患于未然

扩容

  • 扩容避免盲目,可以作为应急手段,避免滥用和无脑用
  • 考虑对底层资源的影响 (中间件、数据库、缓存这类、连接数、长连接数之类的)
  • 建立谨慎扩容风气
  • 优化程序为主(异步、读写分离、增加缓存、代码和SQL调优等等)

限流

常见限流策略

  • 流量整形: 指不管流量到达的速率多么不稳定,在接收流量后,都将其匀速输出的过程,即“乱进齐出”。
  • 容忍突发流量: 指的是限流策略允许流量在短时间内突增,且在突增结束后不会影响后续流量的正常限流。
  • 平滑限流: 指的是在限流周期内流量分布均匀,比如限制 10 秒内请求次数不超过 1000,平滑限流应做到分摊到每秒不超过 100 次请求。反之,不平滑限流有可能在第 1 秒就请求了 1000 次,后面 9 秒无法再发出任何请求。
限流策略 流量整形 容忍突发流量 平滑限流 实现复杂度
固定窗口 不支持 不支持 不支持
滑动窗口 不支持 不支持 不支持
漏桶算法 支持 不支持 支持
令牌桶算法 支持 支持 支持

限流应当采用分治的思想

按位置划分的话,可以分为:

  1. 网关层限流|入口限流 静态资源走CDN,仅放过合法的请求,针对IP或者域名粗放式进行限流。
  2. 接入层限流 | 粒度比网关细,主要手段有负载均衡和限流措施,分摊服务压力。
  3. 应用层限流 | 有自己或者单机的限流, 常用的微服务相关的容量治理主要是在这个环节,针对自己,集群,第三方调用的治理。
  4. 数据库层限流 | 主要采取集群、读写分离等方法分摊压力。

和搞安全类似的想法,“一切输入都是有害的”,不信任上层,要有自己的一套治理方法。

Read more »

关注公众号 尹安灿

流量暴涨

接到告警,业务的Kafka出口流量异常,这个kafka一直是订单类在用,平时插入的数据不会达到这个规模。其次,入口带宽很少,出口带宽这么多,掐指一算,大概率是消费的服务出现问题了。一般入口带宽和出口带宽 不是消费组特别多的情况下,两个的带宽相差不会太悬殊。

查找凶手

iftop 看了一下大致流量情况

image-20220128145022454

发现主要流量流出到3.843.85这两台机

tcpdump -i eth0 host <kafka ip> and host <xxx.xxx.3.84> -w test.cap 抓取了几秒这两个主机相关的包

基本确定了流量使用多的端口,主要是 3.141:9093(Kafka) 发送给 3.84:15630 的流量比较多。

tcp流

看了下(3.84:15630 -> 3.141:9093 )的内容 基本可以确定是哪个topic和消费组。

不抓包的话 用netstat来简单过滤下,如果两个主机间建立的连接比较少的话,基本也能定位出是哪个端口,如下

1
2
3
[root@xxx-xxx-3.141 ~]#netstat -ano|grep xxx.xxx.3.84
tcp 0 0 xxx.xxx.3.141:9093 xxx.xxx.3.84:15556 ESTABLISHED keepalive (4.24/0/0)
tcp 0 4128896 xxx.xxx.3.141:9093 xxx.xxx.3.84:15630 ESTABLISHED on (0.20/0/0)

接下来就需要找出3.84:15630是哪个服务监听的端口。

到3.84这台机执行 lsof -i:端口 得到进程ID

然后用 ps aux | grep 或者 lsof -p 找出具体进程信息。

Read more »

关注公众号 尹安灿

我们在持续构建中,经常遇到编译或者运行需要安装大量依赖。像php,java,go,nodejs,python等等。似乎现在没个包管理的语言都不敢出来推广。

但是在使用多阶段构建后,经常遇到依赖反复安装导致构建时间变成的事情。

后面发现了BuildKit可以完成这个事情。

比如下面一个基于nodejs前端工程化的例子:

npm安装依赖后,执行npm run build,将build生成的dist目录下的文件复制到nginx容器提供服务。

而–mount 可以挂载一个具名的缓存来缓存依赖目录,build的时候同样挂载进去即可。下次构建的时候挂载同样的缓存,就不用重复安装了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# syntax = docker/dockerfile:experimental
FROM node:10-alipine3.9 as builder
WORKDIR /code
COPY . .
RUN --mount=type=cache,target=/code/node_modules,id=my_app_npm_module,sharing=locked --mount=type=cache,target=/root/.npm,id=npm_cache npm install --registry=https://registry.npm.taobao.org
RUN --mount=type=cache,target=/code/node_modules,id=my_app_npm_module,sharing=locked npm run build
FROM nginx:1.19.2-alpine
RUN echo $'server {\n\
listen 80;\n\
listen [::]:80;\n\
server_name localhost;\n\
location / {\n\
root /usr/share/nginx/html;\n\
try_files $uri $uri /index.html;\n\
}\n\
}' > /etc/nginx/conf.d/default.conf
COPY --from=builder /code/dist /usr/share/nginx/html
Read more »

关注公众号 尹安灿

基础数据类型

  • 整数类型 int[8/16/32/64]
  • 浮点类型 float[32/64]
  • 字符型 byte
  • 布尔类型 bool
  • 字符串类型 string

复杂数据类型

  • 指针类型 Pointer
  • 数组 Array
  • 结构体 Struct
  • 管道 Channel
  • 函数 Func
  • 切片 Slice
  • 接口 Interface
  • map

类型相关

import(
“fmt”
“unsafe”
)

1
2
3
4
5
6
7
8
9
10
var a byte = 'a'
var b int = '哈'
// 打印字符
fmt.Printf('%c %c', a, b)
// 输出类型
fmt.Printf('%t %t', a, b)
// 打印占用空间
fmt.Printf('%d %d', unsafe.Sizeof(a), unsafe.Sizeof(b))
// 打印出对应的码值
fmt.Printf('%d %d', a, b)

关注公众号 尹安灿

0%