思有所皈,绪有所依

给万千思绪,寻一寄存之所

登鹳雀楼(唐代:王之涣)

白日依山尽,黄河入海流。
欲穷千里目,更上一层楼。

古代诗歌的“意象化”特性:诗歌通过调动人的形象思维来打动人心。这首诗的前两句“白日依山尽,黄河入海流”表面是写景物,实际是让人通过流水的易逝来感悟到时间的流逝。时间很抽象,于是古人给时间找了一个“意象化”的代言人,那就是流水。要抓住稍纵即逝的时间,就需要“更上一层楼”,这是诗人的答案,要抓紧时间,勇攀高峰。

登高(唐代:杜甫)

风急天高猿啸哀,渚清沙白鸟飞回。
无边落木萧萧下,不尽长江滚滚来。
万里悲秋常作客,百年多病独登台。
艰难苦恨繁霜鬓,潦倒新停浊酒杯。

阅读全文 »

准备离线文件

https://github.com/k3s-io/k3s/releases下载以下文件:
* k3s-airgap-images-amd64.tar.zst
* k3s

https://rancher-mirror.rancher.cn/k3s/k3s-install.sh下载k3s-install.sh文件。

离线安装(Ubuntu 22.04)

  1. 将k3s-airgap-images-amd64.tar.zst文件上传到服务器的/var/lib/rancher/k3s/agent/images/目录下

    1
    2
    sudo mkdir -p /var/lib/rancher/k3s/agent/images/
    sudo cp k3s-airgap-images-amd64.tar.zst /var/lib/rancher/k3s/agent/images/
  2. 将k3s文件上传到服务器的/usr/local/bin/目录下

    1
    2
    sudo cp k3s /usr/local/bin/
    sudo chmod +x /usr/local/bin/k3s
  3. 将k3s-install.sh文件上传到服务器

  4. 执行安装脚本

    1
    2
    3
    4
    5
    chmod +x k3s-install.sh
    sudo INSTALL_K3S_SKIP_DOWNLOAD=true \
    INSTALL_K3S_EXEC='server --token=23f0fba79fc84fd5962279908c86d05e --cluster-init' \
    ./k3s-install.sh

挂载NAS

在阿里云控制台查看具体操作

helm安装(ubuntu 22.04)

1
2
3
cp /etc/rancher/k3s/k3s.yaml ~/.kube/config

sudo snap install helm --classic

使用Lens访问

https://k8slens.dev/下载Lens安装包,支持windows,需要通过XShell的隧道来打通网络。

使用Docker下载镜像再导入到k8s

下载

1
docker save registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.9.2 -o kube-state-metrics-v2.9.2.tar

导入

1
2
k3s ctr image import kube-state-metrics-v2.9.2.tar
k3s ctr image ls|grep kube-state-metrics

注意:如果镜像的下载策略(imagePullPolicy)是Always,则始终会重新下载镜像,所以要使用导入的镜像,需要将镜像下载策略改为IfNotPresent

kube-prometheus

参考:https://github.com/prometheus-operator/kube-prometheus

1
2
3
4
5
6
7
8
9
kubectl apply --server-side -f manifests/setup
kubectl wait \
--for condition=Established \
--all CustomResourceDefinition \
--namespace=monitoring
kubectl apply -f manifests/

kubectl port-forward pod/grafana-748964b847-vtnk8 3000 3000 -n monitoring
默认账号密码: admin/admin

安装Docker(Ubuntu)

1
sudo apt install docker.io

配置镜像加速(阿里云)

1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://??????.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

rancher(弃用)

Rancher安装后默认开启了一个内置的k3s集群,但是无法增加自建的k8s集群?或许只用k3s才是最简单的方式?毕竟Rancher带来了更多的复杂性和不可控性。

持久化存储(longhorn)

1
2
wget https://raw.githubusercontent.com/longhorn/longhorn/v1.5.1/deploy/longhorn.yaml
kubectl apply -f longhorn.yaml

TODO: 部署Docker私有镜像服务,使用阿里云NAS存储镜像,然后让k3s使用私有镜像仓库。
https://zhuanlan.zhihu.com/p/78543733
https://blog.csdn.net/kidom1412/article/details/108128859

自建容器镜像仓库虽然存在单点瓶颈,还是比从文件中手动加载要,毕竟可以由k8s自动处理,新加入的节点也可以自动拉取镜像。

望岳(唐代:杜甫)

岱宗夫如何?齐鲁青未了。
造化钟神秀,阴阳割昏晓。
荡胸生曾云,决眦入归鸟。
会当凌绝顶,一览众山小。

阅读全文 »

Fix No chain/target/match by that name

最近在Alibaba Cloud Linux 3上使用audo apt update进行系统升级后,某个Docker容器无法重启,错误如下:

1
2
Error response from daemon: Cannot restart container some-app: driver failed programming external connectivity on endpoint some-app (88301987d72d07ba99c1630461e1ade9d1fb53879b1d6f94fc8571ac7db7e2a6):  (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9015 -j DNAT --to-destination 172.17.0.3:9015 ! -i docker0: iptables: No chain/target/match by that name.
(exit status 1))

首先排除了iptables的原因,因为我们在做容器化部署的服务器上,都禁用了iptables。

然后怀疑是系统更新的时候,影响到了Docker容器的某些配置,导致容器无法重启。但是重建容器后,还是无法重启,报一样的错误。

最后搜索了一下,找到了解决办法:
参考:docker启动容器报错(iptables failed)

重启docker服务即可:

1
systemctl restart docker

容器的健康检测

如果在Dockerfile或者docker run命令里进行了健康检测,容器启动后查看状态会多出一个信息:healthy或者unhealthy。

1
2
3
# docker ps
... STATUS
... Up 19 minutes (healthy)

在Dockerfile里配置健康检测

Dockerfile
1
HEALTHCHECK --interval=10s --timeout=2s --start-period=30s --retries=3 CMD curl --silent --fail http://localhost:7015/actuator/health || exit 1

在容器启动时指定健康检测参数

1
2
3
4
5
6
7
8
docker run ...
--health-interval=10s \
--health-timeout=2s \
--health-start-period=30s \
--health-retries=3 \
--health-cmd="curl --silent --fail http://localhost:7017/actuator/health || exit 1" \
--restart on-failure \
...

自动重启unhealthy的容器

参考Github项目: Autoheal
大致是说本来docker run有一个issue,或者说应该有那么一个功能,参数--exit-on-unhealthy,默认值true,在容器unhealthy时就退出。再配合--restart=on-failure,来实现容器的自动重启。在exit-on-unhealthy没实现之前,就用这个Autoheal来实现这个功能。

1
2
3
4
5
6
docker run -d \
--name autoheal \
--restart=unless-stopped \
-e AUTOHEAL_CONTAINER_LABEL=all \
-v /var/run/docker.sock:/var/run/docker.sock \
willfarrell/autoheal
  • AUTOHEAL_CONTAINER_LABEL的默认值是autoheal,表示Autoheal会监视带有标签--label autoheal=true的容器。
  • 设置AUTOHEAL_CONTAINER_LABEL=all则是监视所有正在运行的容器。
  • 也可以自定义AUTOHEAL_CONTAINER_LABEL的值,比如AUTOHEAL_CONTAINER_LABEL=myapp,这会监视所有带myapp=true标签的容器。(说实话,官方的这句Set ENV AUTOHEAL_CONTAINER_LABEL to existing label name that has the value true.,最初让我懵了一下。)

莫名其妙的“操作超时”

在用C#的HttpWebRequest写一个HTTP客户端请求时,遇到了一个莫名其妙的问题,就是在发送POST请求时,总是报“操作超时”的错误。核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
HttpWebRequest wbRequest = (HttpWebRequest)WebRequest.Create(url);
wbRequest.Method = "POST";
wbRequest.ContentType = "application/json";

byte[] data = Encoding.UTF8.GetBytes(content);
wbRequest.ContentLength = data.Length;
using (Stream requestStream = wbRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Flush();
}
...
HttpWebResponse wbResponse = (HttpWebResponse)wbRequest.GetResponse();

在执行bRequest.GetResponse()时会卡住,一直等待直到报“操作超时”的错误。

分析与排查

代码问题的几率很小

这段代码没啥好说的,是老系统里千锤百炼留下来的基础代码。从网络上找到的教程什么的,大致也是这样写的,所以代码的问题的几率很小。

也不是服务端处理慢

服务端的响应不涉及复杂的业务处理,这个可以直接排除。

用Postman测试可以正常访问

用Postman测试,没有出现“操作超时”的错误。这个问题就很奇怪了,Postman的请求和代码里的请求应该是一样的,为什么Postman可以正常访问,而代码里就不行呢?

用Fiddler抓包分析

只有用Fiddler抓包分析,来看看代码的request跟Postman的请求有什么差异。发现代码里的请求,主要差异是多了一个Expect: 100-continue的Header。

Postman复现问题

Postman加上100-continue的Header,就可以复现“操作超时”的问题。

罪魁祸首之100-continue

查了一下资料,100-continue是HTTP 1.1里设计的一个状态码,在发送请求体之前,先发送一个Expect: 100-continue,如果服务器响应了100-continue,那么就会继续发送请求体。

这个设计的目的是为了在发送大量数据时,先确认服务器是否接受这个请求,如果不接受,就不用发送大量的数据了,从而减少网络带宽的浪费。

这就跟本文的问题对上了,刚好是带请求体的POST请求,所以先发送Expect: 100-continue,但是服务器没有响应100-continue,就一直等待到“操作超时”。

解决方案

定位到具体的原因后,解决起来就容易了,有两种方案(任选其一):

客户端:禁用100-continue

1
wbRequest.ServicePoint.Expect100Continue = false;

服务端:支持100-continue或者忽略100-continue

这个得看服务端的网关是什么了,可以配置为HTTP协议为1.1,或者把请求头里的Expect: 100-continue过滤掉。

未能创建 SSL/TLS 安全通道

最近将服务器升级到Ubuntu 22.04.2 LTS后,部分终端的.Net framework应用连接时报错:“请求被中止:未能创建SSL/TLS 安全通道”。

这个问题以前也出现过,终端的操作系统是Windows 7,只能安装.Net Framefork 4.0,但是4.0不支持TLS1.2,而Ubuntu升级后不支持TLS1.0和1.1,这就导致无法建立SSL/TLS安全通道。

当时主要参考 ubuntu不支持tls1.0解决,修改openssl的配置/etc/ssl/openssl.cnf

1
2
3
4
5
6
7
8
9
10
11
12
13
//在第一行增加:
openssl_conf = default_conf

//在尾部增加:
[default_conf]
ssl_conf = ssl_sect

[ssl_sect]
system_default = system_default_sect

[system_default_sect]
MinProtocol = TLSv1
CipherString = DEFAULT:@SECLEVEL=1

Ubuntu升级过程中,都是选择的维持现有配置,所以/etc/ssl/openssl.cnf里还是有这些配置,不知道为什么没有生效。

修改Nginx的配置

参考 Enable TLS 1.0 and TLS 1.1 on Ubuntu 20.04

修改nginx的配置:

nginx.conf
1
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:@SECLEVEL=1";

使用openssl测试

nginx.conf
1
2
openssl s_client -connect xiaoboey.top:443 -tls1_1
openssl s_client -connect xiaoboey.top:443 -tls1

还是没解决。。。

Event Source,即Server-sent events,主要优点是基于HTTP协议进行长连接,除了在网页里用JavaScript可以接收消息,其他的应用只要支持HTTP协议都没问题;而RabbitMQ完整实现了AMQP协议,即Advanced Message Queuing Protocol,非常成熟稳定。本文所谓的“代替”,适合的场景非常有限,如果要追求消息推送的稳定可靠,还是建议直接使用RabbitMQ。

阅读全文 »
0%