Harbor 的升级记录
这次更新的目的是, 将原来的 LVM 切换成 btrfs (真香!
存储主要将两个部分迁移到 [[Linux_btrfs|btrfs]] 上。
docker daemon 的工作目录.
harbor 的数据 日志和证书.
下载源代码从官方的项目下载这个版本的 offline 安装包.不用 online 的原因是 : online 还需要从 dockerhub 下载镜像, 国内实在一言难尽.
https://github.com/goharbor/harbor/releases/tag/v2.9.4
cd /opt/
wget https://github.com/goharbor/harbor/releases/download/v2.9.4/harbor-offline-installer-v2.9.4.tgz
解压tar zxvf ./harbor-offline-installer-v2.10.2.tgz
复制之前版本的配置文件cp /opt/harbor-2.10.0/harbor.yml .
确认配置文件中的几个参数已经改为正确的路径, 其他位置不变:
https:
# https port for harbor, default is 443
port: 443
# The path of cert and key files for nginx
certificate: /mnt/btrfs/harbor_data/certs/111.pem
private_key: /mnt/btrfs/harbor_data/certs/111.key
data_volume: /mnt/btrfs/harbor_data
log:
local:
location: /mnt/btrfs/harbor_data/harbor_log
Load 新版本的镜像docker load < ./harbor.v2.10.2.tar.gz
btrfs]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
goharbor/harbor-exporter v2.10.2 9befcab0cee2 4 weeks ago 111MB
goharbor/redis-photon v2.10.2 9d1db211d49a 4 weeks ago 170MB
goharbor/trivy-adapter-photon v2.10.2 8f9e0b6b43ce 4 weeks ago 509MB
goharbor/harbor-registryctl v2.10.2 e5a807ba1f59 4 weeks ago 155MB
goharbor/registry-photon v2.10.2 850d2b3f27f3 4 weeks ago 89MB
goharbor/nginx-photon v2.10.2 9282c21c2fee 4 weeks ago 159MB
goharbor/harbor-log v2.10.2 f288fe2baa96 4 weeks ago 168MB
goharbor/harbor-jobservice v2.10.2 a3247b57a920 4 weeks ago 146MB
goharbor/harbor-core v2.10.2 6cd434d62456 4 weeks ago 174MB
goharbor/harbor-portal v2.10.2 7e5a522c7853 4 weeks ago 167MB
goharbor/harbor-db v2.10.2 cd385df354d4 4 weeks ago 274MB
goharbor/prepare v2.10.2 bf4632d26b65 4 weeks ago 214MB
创建存储并迁移数据mkfs.btrfs -L harbor -d raid1 -m raid1 -n 16k /dev/nvme1n1 /dev/nvme2n1 -f
# 创建子卷
btrfs su cr @docker_data
btrfs su cr @harbor_data
# 创建挂载点
mkdir -v /mnt/btrfs/harbor_data
mkdir -v /mnt/btrfs/harbor_data/certs/
mkdir -v /mnt/btrfs/docker_data
# 同步历史数据
rsync -aP ./harbor_data/ /mnt/btrfs/harbor_data/
rsync -aP ./docker_data/ /mnt/btrfs/docker_data/
rsync -aP ./harbor_log/ /mnt/btrfs/harbor_data/harbor_log
cp -prv ./certs /mnt/btrfs/harbor_data/certs
### 将挂载写入fstab
UUID=519abb44-a6a3-4ed1-b99d-506e9443e73f /mnt/btrfs/docker_data btrfs defaults,compress=zstd,autodefrag,ssd,subvol=@docker_data
UUID=519abb44-a6a3-4ed1-b99d-506e9443e73f /mnt/btrfs/harbor_data btrfs defaults,compress=zstd,autodefrag,ssd,subvol=@harbor_data
确认目录结构mnt]$ tree -L 2 /mnt/btrfs/
/mnt/btrfs/
├── docker_data
│ ├── buildkit
│ ├── containers
│ ├── image
│ ├── network
│ ├── overlay2
│ ├── plugins
│ ├── runtimes
│ ├── swarm
│ ├── tmp
│ ├── trust
│ └── volumes
└── harbor_data
├── ca_download
├── certs
├── database
├── harbor_log
├── job_logs
├── redis
├── registry
└── secret
21 directories, 0 files
安装harbor./install.sh
确认结果harbor]$ docker-compose ps
NAME COMMAND SERVICE STATUS PORTS
harbor-core "/harbor/entrypoint.…" core running (healthy)
harbor-db "/docker-entrypoint.…" postgresql running (healthy)
harbor-jobservice "/harbor/entrypoint.…" jobservice running (healthy)
harbor-log "/bin/sh -c /usr/loc…" log running (healthy) 127.0.0.1:1514->10514/tcp
harbor-portal "nginx -g 'daemon of…" portal running (healthy)
nginx "nginx -g 'daemon of…" proxy running (healthy) 0.0.0.0:80->8080/tcp, :::80->8080/tcp, 0.0.0.0:443->8443/tcp, :::443->8443/tcp
registry "/home/harbor/entryp…" registry running (healthy)
registryctl "/home/harbor/start.…" registryctl running (healthy)
重启系统可以正常重启,harbor 可以访问。
GKE 专有集群创建BastionHost连接apiserver的方式
需求一个GKE集群使用了autopilot创建专有集群, 创建完成之后, 怎么在另一个VPC下的实例内使用 kubectl 管理集群.两个实例:
instance-20240430-111240: 和 GKE 在相同的VPC内, 可以直接访问控制平面进行集群管理.
instance-20240430-053004: 在另一个VPC内, 无法直接访问.
使用 Kubectl Proxy 直接暴露 Apiserver 端口
和GKE相同VPC内启动一个新的实例, 在实例中配置 kubectl 可以正常连接到控制平面.
使用 kubectl 命令创建 proxy 监听在本机的所有地址:root@instance-20240430-111240:~ kubectl proxy --address=0.0.0.0 --kubeconfig .kube/config --accept-hosts "^.*" &
在另一个VPC内的节点上, 使用kubectl 命令进行连接: root@instance-20240430-053004:~ kubectl get pods -o wide -A -s instance-20240430-111240:8001
使用 Tiny Proxy 代理Refer tohttps://cloud.google.com/kubernetes-engine/docs/tutorials/private-cluster-bastion?hl=zh-cn&cloudshell=false#connect
按照上面文档的步骤进行, 之前的步骤都是一致的, 将文档中下面的命令的部分进行替换.
root@instance-20240430-053004:~ gcloud compute ssh INSTANCE_NAME \ --tunnel-through-iap \ --project=PROJECT_ID \ --zone=COMPUTE_ZONE \ --ssh-flag="-4 -L8888:localhost:8888 -N -q -f"
换成
root@instance-20240430-053004:~ ssh -i .ssh/google_compute_engine root@instance-20240430-111240 -4 -L8888:localhost:8888 -N -q -f
直接使用 ssh 隧道, 将 111240 实例的 8888 端口转发到当前实例的8888端口上. 然后配置环境变量设置localhost8888端口作为 https 流量的代理.
设置代理的环境变量 以及 测试 kubectl 命令 获取 ns.
root@instance-20240430-053004:~ export HTTPS_PROXY=localhost:8888
root@instance-20240430-053004:~ kubectl get ns
NAME STATUS AGE
default Active 3d
gke-gmp-system Active 3d
gke-managed-cim Active 3d
gke-managed-filestorecsi Active 3d
gke-managed-system Active 3d
gmp-public Active 3d
kube-node-lease Active 3d
kube-public Active 3d
kube-system Active 3d
Kubernetes 官方相关文档:KubeProxy:https://kubernetes.io/docs/reference/kubectl/generated/kubectl_proxy/
使用 SOCKS5 代理访问 Kubernetes APIhttps://kubernetes.io/zh-cn/docs/tasks/extend-kubernetes/socks5-proxy-access-api/
使用 HTTP 代理访问 Kubernetes APIhttps://kubernetes.io/zh-cn/docs/tasks/extend-kubernetes/http-proxy-access-api/
Portainer 使用记录
创建并启动 Portainer在这里直接使用了dockercompose直接运行, 这个dockercompose 是自己配置的, 其他的服务可以托管给 portainer , 但是 portainer 自己貌似不太能托管自己.
创建compose文件: touch /opt/portainer/docker-compose.yaml
写入配置文件: ---
version: "3.8"
services:
portainer:
image: portainer/portainer-ce:latest
restart: always
environment:
- UUID=0
- GUID=0
- TZ=Asia/Shanghai
volumes:
- /run/docker.sock:/var/run/docker.sock
- /etc/localtime:/etc/localtime:ro
- /opt/Portainer/portainer_data:/data
network_mode: host
cap_add:
- ALL
运行 dockercompose updocker-compose down --remove-orphans
&& \
docker-compose up -d
启动 Portainer Agent在需要管理的其他节点上面, 运行下面的命令:
docker run -d \
-p 9001:9001 \
--name portainer_agent \
--restart=always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /var/lib/docker/volumes:/var/lib/docker/volumes \
reg.liarlee.site/docker.io/portainer/agent:2.19.4
MySQL 无法重连问题的分析
复现方法我的测试环境是完全使用容器的, 还是遇到了一点点小差异.
案例来自一次故障的诊断过程–实验重现 2024年必做实验 的过程, 看看自己差在哪儿.
使用下面的命令运行并进行测试:分离了 server 和 client 在不同的实例上, 开始是放在一起的, 后来为了方便确认范围, 就给分开了.
创建 docker 容器, 运行 MySQL.docker run -it -d --net=host -e MYSQL_ROOT_PASSWORD=123 --name=mysql-server regprox.liarlee.site/docker.io/mysql
连接并创建数据库.mysql -h127.1 --ssl-mode=DISABLED -utest -p123 -e "create database test"
sysbenchdocker run --net=host --privileged -it regprox.liarlee.site/docker.io/phantooom/plantegg:sysbench-lab bash
sysbench --mysql-user='root' --mysql-password='123' --mysql-db='test' --mysql-host='127.0.0.1' --mysql-port='3306' --tables='16' --table-size='10000' --range-size='5' --db-ps-mode='disable' --skip-trx='on' --mysql-ignore-errors='all' --time='1180' --report-interval='1' --histogram='on' --threads=1 oltp_read_only prepare
sysbench --mysql-user='root' --mysql-password='123' --mysql-db='test' --mysql-host='127.0.0.1' --mysql-port='3306' --tables='16' --table-size='10000' --range-size='5' --db-ps-mode='disable' --skip-trx='on' --mysql-ignore-errors='all' --time='1180' --report-interval='1' --histogram='on' --threads=1 oltp_read_only run
查看客户端进程.( 这个记录还是在同一个节点上面测试的记录.MySQL [(none)]> show processlist;
+----+-----------------+---------------------+------+---------+------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+---------------------+------+---------+------+------------------------+------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 336 | Waiting on empty queue | NULL |
| 11 | root | 127.0.0.1:40666 | test | Sleep | 0 | | NULL |
| 12 | root | 172.31.47.174:53264 | NULL | Query | 0 | init | show processlist |
+----+-----------------+---------------------+------+---------+------+------------------------+------------------+
3 rows in set, 1 warning (0.000 sec)
kill进程MySQL [(none)]> kill 11;
Query OK, 0 rows affected (0.001 sec)
MySQL [(none)]> show processlist;
+-----+----------------------+---------------------+------+---------+------+------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+----------------------+---------------------+------+---------+------+------------------------+------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 435 | Waiting on empty queue | NULL |
| 14 | root | 172.31.47.174:56052 | NULL | Query | 0 | init | show processlist |
| 15 | unauthenticated user | 127.0.0.1:48256 | NULL | Connect | 3 | Receiving from client | NULL |
| 16 | unauthenticated user | 127.0.0.1:48258 | NULL | Connect | 3 | Receiving from client | NULL |
| 17 | unauthenticated user | 127.0.0.1:48274 | NULL | Connect | 3 | Receiving from client | NULL |
| 18 | unauthenticated user | 127.0.0.1:48284 | NULL | Connect | 3 | Receiving from client | NULL |
| 19 | unauthenticated user | 127.0.0.1:48294 | NULL | Connect | 3 | Receiving from client | NULL |
| 20 | unauthenticated user | 127.0.0.1:48298 | NULL | Connect | 3 | Receiving from client | NULL |
| 21 | unauthenticated user | 127.0.0.1:48308 | NULL | Connect | 3 | Receiving from client | NULL |
| 22 | unauthenticated user | 127.0.0.1:48310 | NULL | Connect | 3 | Receiving from client | NULL |
| 23 | unauthenticated user | 127.0.0.1:48316 | NULL | Connect | 3 | Receiving from client | NULL |
| 24 | unauthenticated user | 127.0.0.1:48332 | NULL | Connect | 3 | Receiving from client | NULL |
| 25 | unauthenticated user | 127.0.0.1:48338 | NULL | Connect | 3 | Receiving from client | NULL |
| 26 | unauthenticated user | 127.0.0.1:48346 | NULL | Connect | 3 | Receiving from client | NULL |
| 27 | unauthenticated user | 127.0.0.1:48360 | NULL | Connect | 3 | Receiving from client | NULL |
| 28 | unauthenticated user | 127.0.0.1:48366 | NULL | Connect | 3 | Receiving from client | NULL |
| 29 | unauthenticated user | 127.0.0.1:48372 | NULL | Connect | 3 | Receiving from client | NULL |
| 30 | unauthenticated user | 127.0.0.1:48386 | NULL | Connect | 3 | Receiving from client | NULL |
| 31 | unauthenticated user | 127.0.0.1:48394 | NULL | Connect | 3 | Receiving from client | NULL |
| 32 | unauthenticated user | 127.0.0.1:48398 | NULL | Connect | 3 | Receiving from client | NULL |
| 33 | unauthenticated user | 127.0.0.1:48404 | NULL | Connect ...
AWS-CNI 集成 Calico 并启用 WireGuard 加密
看了下说明, 这个东西的主要作用是用于节点间 Pod 流量的加密。
启动集群略
Helm 命令安装 Calico
添加一个helm repohelm repo add projectcalico https://docs.tigera.io/calico/charts
创建一个 value.yamlcat > values.yaml <<EOF
installation:
kubernetesProvider: EKS
registry: reg.liarlee.site/docker.io
EOF
helm show values projectcalico/tigera-operator --version v3.27.2
通过命令安装kubectl create namespace tigera-operator
helm install calico projectcalico/tigera-operator --version v3.27.2 -f values.yaml --namespace tigera-operator
watch kubectl get pods -n calico-system
启用 WireGuardkubectl patch felixconfiguration default --type='merge' -p '{"spec":{"wireguardEnabled":true}}'
# 在节点上面可以看到 wireguard 的网卡 和 内核模块, 说明已经成功了。
# EKS 默认的 AMI 里面是带这个模块的, 不需要手动安装, 可以自动按需装载。
~]$ lsmod | grep wireguard
wireguard 98304 0
curve25519_x86_64 36864 1 wireguard
libcurve25519_generic 49152 2 curve25519_x86_64,wireguard
libchacha20poly1305 16384 1 wireguard
ip6_udp_tunnel 16384 1 wireguard
udp_tunnel 28672 1 wireguard
~]$ ip ad
35: wireguard.cali: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 8941 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 192.168.137.134/32 scope global wireguard.cali
valid_lft forever preferred_lft forever
# 或者可以查看
> kubectl get felixconfiguration default -o yaml
apiVersion: projectcalico.org/v3
kind: FelixConfiguration
...
spec:
bpfConnectTimeLoadBalancing: TCP
bpfHostNetworkedNATWithoutCTLB: Enabled
bpfLogLevel: ""
floatingIPs: Disabled
healthPort: 9099
logSeverityScreen: Info
reportingInterval: 0s
routeTableRange:
max: 99
min: 65
vxlanVNI: 4096
wireguardEnabled: true
参考文档Project Calico:https://docs.tigera.io/calico/3.25/network-policy/encrypt-cluster-pod-traffic#enable-wireguard-for-a-cluster
使用 nsenter 从Kubernetes Node 进入容器网络 Namespace
记录一下使用 nsenter 进入容器的 network namespace 中抓包。在 TroubleShooting 的过程中可能是需要这个方法的。
进入容器ns的步骤
选中一个poddefault haydenarch-68865d5b56-cblc6 ● 1/1 Running 0 172.31.48.162 ip-172-31-53-61.cn-north-1.compute.internal
在节点上找到这个pod的容器id : 02182f3e9137[root@ip-172-31-53-61 ~]$ nerdctl ps| grep archlinux
02182f3e9137 1234567.dkr.ecr.cn-north-1.amazonaws.com.cn/archlinux:latest "sleep infinity" 23 hours ago Up k8s://default/arch-68865d5b56-cblc6/arch
查找这个容器id的进程pid。[root@ip-172-31-53-61 ~]$ nerdctl inspect 02182f3e9137 | grep -i pid
"Pid": 10306,
nsenter 命令进入容器的名称空间。 [root@ip-172-31-53-61 ~]$ nsenter -t 10306 -n
[root@ip-172-31-53-61 ~]$ ip ad
3: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 22:fb:14:7b:91:22 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.31.48.162/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::20fb:14ff:fe7b:9122/64 scope link
valid_lft forever preferred_lft forever
对比容器里面执行命令的结果: [root@haydenarch-68865d5b56-cblc6 /]$ ip ad
3: eth0@if25: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 22:fb:14:7b:91:22 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.31.48.162/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::20fb:14ff:fe7b:9122/64 scope link
valid_lft forever preferred_lft forever
这时候就可以使用节点上面的工具来进行抓包了。
测试可以看到在容器内部是没有tcpdump命令的.
[root@haydenarch-68865d5b56-cblc6 /]$ tcpdump
bash: tcpdump: command not found
在容器内发出一个ping包 。
[root@haydenarch-68865d5b56-cblc6 /]$ ping www.bing.com -c 1
PING a-0001.a-msedge.net (13.107.21.200) 56(84) bytes of data.
64 bytes from 13.107.21.200 (13.107.21.200): icmp_seq=1 ttl=102 time=80.6 ms
--- a-0001.a-msedge.net ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 80.608/80.608/80.608/0.000 ms
在节点上面进入容器的名称空间,使用tcpdump抓包。
[root@ip-172-31-53-61 ~]$ tcpdump -i any -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
09:09:18.172140 IP 172.31.48.162.46458 > 10.100.0.10.domain: 22082+ A? www.bing.com.default.svc.cluster.local. (56)
09:09:18.172181 IP 172.31.48.162.46458 > 10.100.0.10.domain: 59743+ AAAA? www.bing.com.default.svc.cluster.local. (56)
09:09:18.172406 IP 10.100.0.10.domain > 172.31.48.162.46458: 59743 NXDomain*- 0/1/0 (149)
09:09:18.172470 IP 10.100.0.10.domain > 172.31.48.162.46458: 22082 NXDomain*- 0/1/0 (149)
09:09:18.172513 IP 172.31.48.162.51210 > 10.100.0.10.domain: 59281+ A? www.bing.com.svc.cluster.local. (48)
09:09:18.172533 IP 172.31.48.162.51210 > 10.100.0.10.domain: 43411+ AAAA? www.bing.com.svc.cluster.local. (48)
09:09:18.172635 IP 10.100.0.10.domain > 172.31.48.162.51210: 43411 NXDomain*- 0/1/0 (141)
09:09:18.172707 IP 10.100.0.10.domain > 172.31.48.162.51210: 59281 NXDomain*- 0/1/0 (141)
09:09:18.172743 IP 172.31.48.162.36420 > 10.100.0.10.domain: 41442+ A? www.bing.com.cluster.local. (44)
09:09:18.172773 IP 172.31.48.162.36420 > 10.100.0.10.domain: 6893+ AAAA? www.bing.com.cluster.local. (44)
09:09:18.172947 IP 10.100.0.10.domain > 172.31.48.162.36420: 41442 NXDomain*- 0/1/0 (137)
09:09:18.172985 IP 10.100.0.10.domain > 172.31.48.162.36420: 6893 NXDomain*- 0/1/0 (137)
09:09:18.173030 IP 172.31.48.162.54253 > 10.100.0.10.domain: 9603+ A? www.bing.com.cn-north-1.compute.internal. (58)
09:09:18.173053 IP 172.31.48.162.54253 > 10.100.0.10.domain: 61573+ AAAA? www.bing.com.cn-north-1.compute.internal. (58)
09:09:18.173130 IP 10.100.0.10.domain > 172.31.48.162.54253: 9603 NXDomain* 0/1/0 (173)
09:09:18.173863 IP 10.100.0.10.domain > 172.31.48.162.54253: 61573 NXDomain 0/1/0 (173)
09:09:18.173893 IP 172.31.48.162.49537 > 10.100.0.10.domain: 64334+ A? www.bing.com. (30)
09:09:18.173941 IP 172.31.48.162.49537 > 10.100.0.10.domain: 31567+ AAAA? www.bing.com. (30)
09:09:18.174269 IP 10.100.0.10.domain > 172.31.48.162.49537: 64334 5/0/0 CNAME www-www.bing.com.trafficmanager.net., CNAME cn-bing-com.cn.a-0001.a-msedge.net., CNAME a-0001.a-msedge.net., A 13.107.21.200, A 204.79.197.200 (311)
09:09:18.174345 IP 10.100.0.10.domain > 172.31.48.162.49537: 31567 3/1/0 CNAME www-www.bing.com.trafficmanager.net., CNAME cn-bing-com.cn.a-0001.a-msedge.net., CNAME a-0001.a-msedge.net. (325)
09:09:18.174537 IP 172.31.48.162 > 13.107.21.200: ICMP echo request, id 45375, seq 1, length 64
09:09:18.255142 IP 13.107.21.200 > 172.31.48.162: ICMP echo reply, id 45375, seq 1, length 64
退出的方式直接使用exit即可, 就回到了host本身的ns里面。
源地址检查造成丢包的分析
问题网络访问路径:Client –> NLB –> Application Pod
特别的部分:NLB 开启了保留源地址就意味着 NLB 在转发网络流量的时候不会改变数据包五元组内的SourceIP, 这时候对于后端的Pod和节点来说, 收到的数据包的ip地址来源直接是Client 的IP 地址, 这会导致所有的流量其实都是来自于VPC之外的。 NLB在这其中变成了一个透明代理。从 NLB 发到后端application pod 中的流量会有偶尔timeout的情况, 并不是所有的请求都会timeout ,从客户端抓包显示tcp握手的时候就没有成功, 第一个syn包出去就一直没有回复,然后继续重发等待。 由于后端的pod比较多, 并且nlb在四层做了负载均衡,很难定位到某次的请求具体到了哪个后端pod上面。
写下这些的时候我已经知道了问题的答案是由于 VPC CNI 的一个 env , AWS_VPC_K8S_CNI_EXTERNALSNAT 会指定是否做 SNAT, 比较早的版本, cni 插件没有对 SNAT 在iptables上面进行标记和处理, 这样会导致 过不了操作系统的网络报文地址校验, 进而导致丢包。关于 linux 的数据包源地址校验,也就是 rp_filter 的参数, 在EKS中默认都是1 。
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
rp_filter参数用于控制系统是否开启对数据包源地址的校验。rp_filter参数有三个值,0、1、2,具体含义:0:不开启源地址校验。1:开启严格的反向路径校验。对每个进来的数据包,校验其反向路径是否是最佳路径。如果反向路径不是最佳路径,则直接丢弃该数据包。2:开启松散的反向路径校验。对每个进来的数据包,校验其源地址是否可达,即反向路径是否能通(通过任意网口),如果反向路径不同,则直接丢弃该数据包。
默认的情况下, 这个参数是1, 因此 :数据包发到了eth1网卡,如果这时候开启了rp_filter参数,并配置为1,则系统会严格校验数据包的反向路径。从路由表中可以看,返回响应时数据包要从eth0网卡出,即请求数据包进的网卡和响应数据包出的网卡不是同一个网卡,这时候系统会判断该反向路径不是最佳路径,而直接丢弃该请求数据包。
排查路由表 和 Iptables当 AWS_VPC_K8S_CNI_EXTERNALSNAT 为 False 的时候查看节点的路由rule:
[root@ip-172-31-53-61 ~]# ip rule ls
0: from all lookup local
512: from all to 172.31.49.222 lookup main
512: from all to 172.31.52.222 lookup main
512: from all to 172.31.59.203 lookup main
512: from all to 172.31.61.61 lookup main
512: from all to 172.31.49.246 lookup main
512: from all to 172.31.58.47 lookup main
512: from all to 172.31.53.79 lookup main
512: from all to 172.31.63.97 lookup main
512: from all to 172.31.53.74 lookup main
512: from all to 172.31.54.196 lookup main
512: from all to 172.31.51.218 lookup main
512: from all to 172.31.63.248 lookup main
512: from all to 172.31.54.200 lookup main
512: from all to 172.31.51.108 lookup main
512: from all to 172.31.56.137 lookup main
512: from all to 172.31.54.85 lookup main
512: from all to 172.31.51.82 lookup main
512: from all to 172.31.57.72 lookup main
512: from all to 172.31.59.200 lookup main
512: from all to 172.31.58.18 lookup main
512: from all to 172.31.55.235 lookup main
512: from all to 172.31.48.162 lookup main
512: from all to 172.31.60.128 lookup main
512: from all to 172.31.50.246 lookup main
512: from all to 172.31.61.216 lookup main
512: from all to 172.31.55.130 lookup main
512: from all to 172.31.49.208 lookup main
512: from all to 172.31.55.135 lookup main
512: from all to 172.31.50.208 lookup main
512: from all to 172.31.63.121 lookup main
512: from all to 172.31.52.205 lookup main
512: from all to 172.31.52.71 lookup main
512: from all to 172.31.51.7 lookup main
512: from all to 172.31.53.238 lookup main
1024: from all fwmark 0x80/0x80 lookup main
1536: from 172.31.56.137 lookup 2
1536: from 172.31.54.85 lookup 2
1536: from 172.31.51.82 lookup 2
1536: from 172.31.57.72 lookup 2
1536: from 172.31.59.200 lookup 2
1536: from 172.31.58.18 lookup 2
1536: from 172.31.55.235 lookup 2
1536: from 172.31.48.162 lookup 2
1536: from 172.31.60.128 lookup 2
1536: from 172.31.50.246 lookup 2
1536: from 172.31.61.216 lookup 2
1536: from 172.31.55.130 lookup 2
1536: from 172.31.49.208 lookup 2
1536: from 172.31.55.135 lookup 2
1536: from 172.31.50.208 lookup 3
1536: from 172.31.63.121 lookup 3
1536: from 172.31.52.205 lookup 3
1536: from 172.31.52.71 lookup 3
1536: from 172.31.51.7 lookup 3
1536: from 172.31.53.238 lookup 3
32766: from all lookup main
32767: from all lookup default
路由表 main:
[root@ip-172-31-53-61 ~]# ip r s table main
default via 172.31.48.1 dev eth0
169.254.169.254 dev eth0
172.31.48.0/20 dev eth0 proto kernel scope link src 172.31.53.61
172.31.48.162 dev enicfd09b318e8 scope link
172.31.49.208 dev eni3512a649bfb scope link
172.31.49.222 dev eni8cf9e51b58a scope link
172.31.49.246 dev eni9f41e641d45 scope link
172.31.50.208 dev eniffd28bfda2d scope link
172.31.50.246 dev eni1abad8afd57 scope link
172.31.51.7 dev eni76e381844d3 scope link
172.31.51.82 dev eni524077fd828 scope link
172.31.51.108 dev eni08e24263c30 scope link
172.31.51.218 dev enicb4edb90122 scope link
172.31.52.71 dev eni61a9554ce92 scope link
172.31.52.205 dev eniabe0e0a95e5 scope link
172.31.52.222 dev eni60eac028c5b scope link
172.31.53.74 dev enibecb9c399f8 scope link
172.31.53.79 dev eni6c5800d82af scope link
172.31.53.238 dev eni5a413534f4e scope link
172.31.54.85 dev eni2acd4947ff6 scope link
172.31.54.196 dev eni54f6d6a6254 scope link
172.31.54.200 dev eni47a45758006 scope link
172.31.55.130 dev eni1658effeb9c scope link
172.31.55.135 dev enibe97b10ca0d scope link
172.31.55.235 dev eni179f5c3696c scope link
172.31.56.137 dev eni099fbf41bd0 scope link
172.31.57.72 dev eni10574b2dfef scope link
172.31.58.18 dev eni5434103f7b9 scope link
172.31.58.47 dev enif4a18529a5f scope link
172.31.59.200 dev eni308c9b2c04f scope link
172.31.59.203 dev enia94f8f21d94 scope link
172.31.60.128 dev eni11239f98883 scope link
172.31.61.61 dev eni84312c21e65 scope link
172.31.61.216 dev enid75abf4f5e0 scope link
172.31.63.97 dev eni3ea3753c729 scope link
172.31.63.121 dev eni8bac6c0ff3f scope link
172.31. ...
安装 headscale 建立自己的 Tailnet
Tailscale 虽然是 mesh 的网络模式, 可以点对点的连接所有设备, 能直连会尽量直接连接, 然鹅还是需要一个默认的 server 来进行服务发现和临时中转流量.
那么大概的配置框架就已经出现了, 一个服务发现中心, 和多个不同的客户端.开始的时候直接使用的 tailscale + github 账户登录的方式使用, 然后发现 github 账户直接托管的中心服务不能关闭国外的中转服务器, 这就比较难受, 本来可以直通的线路走了国外的中转不稳定, 会断, 最后还是走国内的便宜云服务器自己维护了一个开源的 headscale 作为中心服务.
安装 headscaleheadscale 官方文档现在的 headscale 容器镜像的是有问题的, 不太好用, 还得花时间修。
我准备了这些:
域名 和 域名证书
一台 Debian 的云服务器
公网 ip 地址
具体的安装步骤就是按照官方网站走下来就可以了.
需要注意的地方就是备案, 不备案会导致无法使用 443.那么需要在tailscale的配置文件中指定端口, 让 tailscale 监听在一个不常用的端口上.
因为没有写清楚 server 的地址和端口, 折腾了一天… 一度怀疑是不是换端口也不行, 必须备案 …
配置文件headscale ~$ vim /etc/headscale/config.yaml
# 大约在配置文件的 13行左右.
server_url 这个字段需要写成 https://YOURHOSTNAME.YOURDOMAIN:PORT 就可以了.
大约在配置文件的77行左右, 会有一个 DEPR 集成的服务, 开启这个服务之后, 可以使用当前的服务器直接作为DEPR中转节点.
77 derp:
78 server:
79 # If enabled, runs the embedded DERP server and merges it into the rest of the DERP config
80 # The Headscale server_url defined above MUST be using https, DERP requires TLS to be in place
81 enabled: true
绑定证书的位置在大约 151行之后, 我使用自己签发的证书, 就是直接配置 182 183 两行的参数就行.
181 ## Use already defined certificates:
182 tls_key_path: /path/to/cert.key
183 tls_cert_path: /path/to/cert.pem
安装tailscale客户端按照安装文档直接安装, 不一样的发行版(是的, Windows 也是 Linux 发行版) 安装方式不同, 基本上启动之后都能用, 完成度非常高.
注册节点的命令节点注册命令记录.
sudo tailscale up --accept-dns=false --advertise-exit-node --advertise-routes=192.168.31.0/24 --login-server=https://YOURHOSTNAME.YOURDOMAIN:PORT --accept-routes
# 也可以是:
sudo tailscale up --accept-dns=false --advertise-exit-node --login-server=https://YOURHOSTNAME.YOURDOMAIN:PORT --accept-routes
# 或者是:
sudo tailscale up --accept-dns=false --login-server=https://YOURHOSTNAME.YOURDOMAIN:PORT --accept-routes
# 最后是:
sudo tailscale up --accept-dns=true --login-server=https://YOURHOSTNAME.YOURDOMAIN:PORT --accept-routes
这个命令执行完毕之后, 会返回一个网页, 网页打开里面有一个命令, 复制命令去 headscale 服务器的命令行(bash)里面执行一下,节点就注册进来了.
更新现在已经存在的设置最近需要更新我的一个节点, 发布 VPC 内的 CIDR 块, 这个设置之前没有 advertise-route 参数进行更新 。这个实例在我的默认VPC里面, 这样的话这个实例就直接变成了这个VPC内的IGW, 流量会通过这个实例发送到VPC内, 可以直接将这个网络范围内的DNS指向VPC内的 .2 Resolver, 甚至感觉可以直接在家用 EFS 这类的东西了.
当然, 如果节点需要变动之前运行的参数, 是需要给出和之前一致的参数, 然后添加 或者 变更其中的一部分的.
sudo tailscale up --accept-dns=false --advertise-exit-node --login-server=https://YOURHOSTNAME.YOURDOMAIN:PORT --accept-routes --advertise-routes=172.31.0.0/16
tailscale 结合 Sunshine 串流
既然 EC2 显卡都已经能用了, 为啥不能直接启动一个 steam 呢? 于是。。。
安装的步骤比较简单:
# Sunshine 用来作为 stream hosting
sudo pacman -S sunshine
# Steam 以及 Steam-navtive 是 steam 的runtime, ttf 是为了可以正常的显示中文字体, 好像字体上面是最常见的问题, 比如显示出来是一堆方块。
sudo pacman -S steam-native-runtime
sudo pacman -S steam ttf-liberation
这些安装完成之后我自己直接 drun 去调用 steam 就可以了。
启动使用的desktop 文件叫作 steam(native) 启动之后登录, 然后可以正常下载游戏了。
默认可以下载的游戏都是原生支持linux的, 不支持的可以在steam的设置里面打开proton兼容, 之后steam的界面上就不会区分任何平台了。
手柄支持手柄默认不能传递到hosting, sunshine 的启动日志里面有报错:
[2023:12:27:21:35:33]: Error: Could not create Sunshine Gamepad: Permission denied
[2023:12:27:21:35:33]: Error: Could not create Sunshine Gamepad: Permission denied
给予这个报错的搜索结果有两个:
添加当前的用户到 input 组里面 usermod -a -G input ec2-user
# checking
groups ec2-user
第二个方案是, 确保自己的内核装载 uinput 模块。 modprobe uinput
同时还在文档里面找到了另一个地方,需要配置一个 udev ruleecho 'KERNEL=="uinput", SUBSYSTEM=="misc", OPTIONS+="static_node=uinput", TAG+="uaccess"' | \
sudo tee /etc/udev/rules.d/85-sunshine.rules
或者这文档 以及这个Issue
我目前不太确定具体哪个是正确的, 因为重启这个实例的时候把tailscale给玩儿没了.. 现在连不上了…闹心
Redis 笔记
ElastiCache主要概念
ElastiCache nodesA node is the smallest building block of an ElastiCache deployment.node is a fixed-size chunk of secure, network-attached RAM.总结来说, 就是ec2实例上面跑了相同版本的Engine for Redis。
ElastiCache for Redis shards
A Redis shard (called a node group in the API and CLI) is a grouping of one to six related nodes. A Redis (cluster mode disabled) cluster always has one shard.Redis (cluster mode enabled) clusters can have up to 500 shards, with your data partitioned across the shards.
Shard 是多个 Nodes 的集合, 或者叫作节点组. 一个Shard包括的节点有不同的角色, 一个Primary 和 最多5个 Replica.
ElastiCache for Redis clustersA Redis cluster is a logical grouping of one or more ElastiCache for Redis shards. Data is partitioned across the shards in a Redis (cluster mode enabled) cluster.集群模式关闭, 1 Cluster – 1 Shard – 1 - 6 Nodes: 1 Primary and 5 Nodes集群模式开启, 1 Cluster – 500 Shard – 1 - 6 Nodes: 1 Primary Per Shard
ElastiCache for Redis replicationRedis (cluster mode disabled) clusters support one shard (in the API and CLI, called a node group).Redis (cluster mode enabled) clusters can have up to 500 shards, with your data partitioned across the shards.
每个Replica都是Primary中全部数据的复制。 Replica使用异步的方式进行与Primary的数据同步。
第一次 psync2 全量复制所有内存当前存储的数据, 后续使用redis的增量复制。
AWS Regions and availability zones目前中国区不能使用 GlobalStore, 并且Region之间也是无法相互同步数据的。 在不同的az 之间可以做高可用来降低停机的时间。
ElastiCache for Redis endpointsAn endpoint is the unique address your application uses to connect to an ElastiCache node or cluster.
Redis 禁用集群模式的单节点端点单节点的集群端点用于读取和写入. In one word, Single Primary, read and write at one point.用这个节点的 Endpoint 即可。
Redis 禁用集群模式的多节点端点多节点有两个不同的端点, Primary 端点总是连接到Primary 节点,故障切换会切换这个dns指向的后端。Reader Endpoint 在所有的 Replica节点之间分流Connection 用来提供读取行为。
用控制台上面的 Primary Endpoint 和 Reader Endpoint
Redis 启用集群模式的端点集群模式下, 只是配置一个endpoint, 但是连接到Endpoint之后, 客户端可以发现所有的主节点和从节点。
** 用Configuration Endpoint 可以找到集群内部的每一个 Primary 和 Replica节点 **
ElastiCache parameter groupsCache parameter groups are an easy way to manage runtime settings for supported engine software. Parameters are used to control memory usage, eviction policies, item sizes, and more.
简单来说, 创建每一个配置文件在aws的控制台上, 可以调整一部分Redis的参数, 然后可能在集群内可以实时生效, 也可能需要触发一次Failover才能生效。elasticache的参数管理
通常来说, 改动了参数组中的几个参数可能会需要重启节点:
activerehashing
databases
ElastiCache for Redis securityPENDING… Maybe follow offical guide.
ElastiCache security groups创建集群的时候需要指定一个安全组, 需要放行 6379 port, DONE。
ElastiCache subnet groups子网组是运行 Redis 的所有子网的集合, 创建一个子网, 让托管的节点的把ENI放在这个子网里面, 私有子网。
ElastiCache for Redis backups备份特定时间的数据, 可以使用这个用来还原成新的集群, 或者是创建一个集群的种子, 备份中包括所有的data, 和一部分集群的metadata。bgsave 或者 forkless save, 这两个取决于是否有足够的内存空间, 当空间充足的时候使用的默认的 bgsave, 但是肯定的是, bhsave 会有性能的开销,所以最好是使用副本节点来创建备份或者快照。对于forkless本身是另一个机制,但是forkless会尝试delay 写入的延迟,从而确保不会积累过多的change 导致内存压力过大失败。
ElastiCache eventsPENDING… Maybe follow offical guide.
Redis 缓存穿透, 击穿 和 雪崩穿透主要是指异常的查询请求, 缓存未命中的场景下直接访问后端, 后端承载了大量请求导致的崩溃。目前看到需要两个条件:
数据在缓存层未命中。
大量的请求转移到了后端。
请求的数据可能在缓存层 以及 后端都不存在。
避免的方法可能有:
在redis 前面添加一个 BloomFilter, 在请求进来的时候进行过滤, 对于异常的请求直接拒绝或者返回一个NULL。
提前缓存一部分数值为 NULL 的数据, 在缓存层直接接下这些请求。这个本身是有点问题的。
击穿这个概念是指的 少部分热点数据被大量的查询时候, 数据突然从缓存中消失, 可能是过期也可能删除等等, 这样有大量的相同请求发送到后端。解决方案:
设置部分数据永远不过期。
后端进行加分布式锁。
雪崩批量的热数据过期, 这时候大部分的请求可能还在继续尝试查询不同的数据,直接将这些请求发送到了后端的数据库, 数据库崩了。解决方案:
创建多个实例, 和副本来提高可用性。
后端加锁限流。
调整过期时间, 让不同的数据过期的时间错开,防止批量的key 相同的时间过期。
总结起来就是, 避免缓存在特定的场景下失效, 想各种方法保护缓存的可用性。
More NoteCOB: Client Output Buffer, Primary 向 Replica 同步数据使用, Max Memory 没有计算这部分的内存, 使用的是Redis引擎之外的内存(os管理的内存)。
Policy : LRU 不严格, 随机选择三个。maxmemory-sample: 3, 可以调大。 sample越大, 信号的时间越长。
Reclaim策略里面, 每秒扫描200个key, 如果数据量比较大的话, Redis会进入loop阶段, 反复尝试Reclaim到25% 以下。
可以尝试使用 scan 命令扫描全部的数据, 遍历所有的key,这样会触发lazyway主动进行回收。扫描的指令:
scan 0 match * count 20000
LRU vs LFU
为了保持主从一致, primary 上过期或者删除的key, 会同步发送一个显式的 delete 指令, 这个会统计在replica的settypecmd。
snapshot里面的数据在恢复的时候,会检查key 的ttl , 如果超过会直接删除。在Redis内存与物理内存mapping由操作系统的虚拟内存管理器 和 指定的内存分配器进行处理。
关于Info指令的所有参数的解释:https://redis.io/commands/info/
localhost:6379> info memory
# Memory
used_memory:938464
used_memory_human:916.47K # Redis 存储数据的使用量
used_memory_rss:10747904
used_memory_rss_human:10.25M # Redis 常住内存集的用量
used_memory_peak:1134048
used_memory_peak_human:1.08M # Redis 的内存峰值
used_memory_peak_perc:82.75%
used_memory_overhead:888488
used_memory_startup:865904
used_memory_dataset:49976
used_memory_dataset_perc:68.88%
allocator_allocated:1479168 # 内存分配器已经分配的量
allocator_active:1601536
allocator_resident:4575232
total_system_memory:8182751232
total_system_memory_human:7.62G # 系统内存的总量
used_memory_lua:31744
used_memory_vm_eval:31744
used_memory_lua_human:31.00K # Lua 引擎的内存使用量
used_memory_scripts_eval:0
number_of_cached_scripts:0
number_of_functions:0
number_of_libraries:0
used_memory_vm_functions:32768
used_memory_vm_total:64512
used_memory_vm_total_human:63.00K
used_memory_functions:184
used_memory_scripts:184
used_memory_scripts_human:184B
maxmemory:0 # Max Memory 当前的配置
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.08
allocator_ ...