2025-01-21 10:02:05
在如今各种 AI 工具层出不穷的时代,找到一个好用又免费的绘图工具真的不容易。而 Raphael.app 就是一款非常值得一试的 AI 绘图工具。它简单易用,而且用英文提示词就能生成效果不错的图片。
这款以文艺复兴三杰之一拉斐尔(Raphael,全名:Raffaello Sanzio da Urbino,1483 年 4 月 6 日-1520 年 4 月 6 日)命名的工具,有以下特点:
操作非常简单:
Raphael 是一个用起来特别方便的 AI 绘图工具,既免费又能生成效果不错的图片。不管你是想找灵感还是单纯玩玩 AI,都非常值得一试。
2025-01-20 16:50:25
最近读了 TheNewStack 发布的电子书《Cloud Native Observability for DevOps Teams》,虽然这本书是 2022 年出品的,但给我了很大的启发。它不仅讨论了技术工具,还深入探讨了团队协作、文化建设和未来趋势的结合点。在这本书里,“观察”不仅仅是看到数据,而是看清背后的意义。可以说,它从根本上改变了我对可观测性的理解。
本书从基础定义到实际操作,系统地阐述了云原生可观测性的重要性及其实现方式。通过具体的工具和策略,它帮助读者理解如何整合指标、日志、追踪和混沌工程等维度,全面掌控分布式系统的健康状况,为 DevOps 团队提供高效的决策支持。
书中开篇就点明:可观测性是通过系统的外部信号推断内部状态的能力。不仅是传统的指标(Metrics)、日志(Logs)、追踪(Tracing)三根支柱的组合,而是一种综合性、全局化的分析方法。正如作者所说:
“Observability isn’t just the ability to see each piece at a time; it’s also the ability to understand the broader picture and how these pieces combine.”
书中特别强调了 Kubernetes 环境中日志和监控的复杂性。Kubernetes 没有内置的完整可观测性解决方案,只提供了基础功能,比如 kubectl
查看对象状态,而更高级的功能需要依赖第三方工具如 Fluentd 和 Prometheus。
书中在实践部分提到了多种实现可观测性的具体策略和工具:
kubectl get events
快速了解集群中资源的状态变化。这些实践指南强调了工具与策略的结合,从而实现全面的可观测性。
书中强调,单纯收集数据并不能解决问题,关键在于跨维度数据的整合与分析。例如,在性能问题排查时,指标和追踪往往无法直接关联,而这正是现有工具的短板。未来,统一数据存储和分析视角的工具,比如 OpenTelemetry 提倡的标准化方法,可能是突破口。
随着 AI 技术的发展,可观测性工具也可以更智能化。例如,通过机器学习预测异常,或是自动推荐优化策略。这不仅能减少人为干预,还能提升故障响应速度。正如作者在混沌工程部分提到的:
“Instead of waiting for something to happen and finding out how your application fares, you put it through duress under controlled conditions to identify weaknesses and fix them.”
书中提到“DevOps 的终极目标是跨团队的协作与同理心”,这点深有共鸣。尤其是在复杂分布式系统中,开发和运维团队往往各自为战,导致沟通断层。跨团队协作的关键在于工具提供的透明性与共享视角,而不仅仅是技术能力。
这本书的独到之处在于它从技术和人文两个角度同时切入,它让我意识到,可观测性不仅是一组工具的集合,而是一种文化、一种能力,帮助我们更深刻地理解系统,推动团队协作,并在复杂的云原生环境中建立起真正的“透明化”。
最后,我想引用书中一段非常打动我的话来结尾:
“Observability lets you see the beautiful and complete picture that is your production software systems.”
2025-01-14 18:28:37
在最近对 Istio Ambient 模式的研究中,我发现 HTTP2 Connect 方法被用作创建隧道的核心技术,以实现透明流量的拦截和转发。HTTP/2 CONNECT 隧道是一种强大的工具,可以在已有的 HTTP/2 连接中创建高效的隧道,用于传输原始的 TCP 数据。这篇文章通过一个简单的 Demo,展示了如何使用 Envoy 来实现 HTTP/2 CONNECT 隧道的基本功能。
HTTP2 Connect 方法是一种标准化的方式来创建隧道,用于透明地传输数据。特别是在 Istio 的 Ambient 模式中,它为代理数据平面之间的通信提供了一种高效的手段。HBONE(HTTP-Based Overlay Network Environment)隧道则是基于这种 HTTP2 Connect 技术的实现,用于 Istio 中的透明流量拦截和转发。通过使用 HBONE,数据可以有效地通过 HTTP2 隧道安全传输,替代了传统的 Sidecar 模式。这一创新设计极大地简化了服务网格的管理和部署。
HBONE 是 Istio 特有的术语,它是一种安全隧道协议,用于在 Istio 组件之间进行通信。在当前的 Istio 实现中,HBONE 协议包含了三个开放标准:
HTTP CONNECT 用于建立隧道连接,mTLS 用于安全地加密连接,而 HTTP/2 用于在单一安全隧道中多路复用应用连接流并传输附加的流级元数据。更多关于 HBONE 隧道的细节可以参考官方文档:HBONE 详细介绍。
HTTP2 Connect 方法允许我们创建一个类似于 VPN 的隧道,通过这个隧道可以安全地传递数据。建立隧道的基本步骤如下:
这种方法能够使得数据的传输过程更加透明且安全,特别适用于需要高效通信和端到端加密的场景。
下图展示了 HTTP2 Connect 方法建立隧道的基本过程。
本示例展示了一个基础场景:
架构图如下:
我们将使用 Node.js 来编写客户端和服务端,并将服务端和 Envoy 代理运行在容器中,在本地通过客户端访问 Envoy 代理从而达到访问客户端的目的。
完整的目录结构如下:
envoy-http2-tunnel/
├── certs/
│ ├── openssl.cnf
│ ├── server.crt
│ ├── server.key
├── client/
│ └── client.js
├── docker-compose.yml
├── envoy.yaml
└── server/
├── Dockerfile
└── server.js
确保你的系统已安装 Node.js(版本 >= 10.10.0),因为 http2
模块在该版本后稳定。
在你的工作空间中创建一个新目录并进入:
mkdir envoy-http2-tunnel
cd envoy-http2-tunnel
由于 Envoy 和服务器之间需要加密通信,我们需要生成包含正确配置的自签名证书。
创建 certs
目录:
mkdir certs
cd certs
创建 openssl.cnf
,内容如下:
[ req ]
default_bits = 2048
default_md = sha256
prompt = no
distinguished_name = dn
req_extensions = req_ext
[ dn ]
C = US
ST = California
L = San Francisco
O = My Company
OU = My Division
CN = server
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = server
DNS.2 = localhost
运行以下命令生成密钥和证书:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout server.key -out server.crt -config openssl.cnf
这将在 certs
目录中生成 server.key
和 server.crt
文件。
我们需要配置 Envoy,使其能够接受客户端的普通 TCP 连接,将数据通过 HTTP/2 CONNECT 隧道传递给服务器。
在项目根目录创建 envoy.yaml
,内容如下:
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.tcp_proxy
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy
stat_prefix: tcp_stats
cluster: tunnel_cluster
tunneling_config:
hostname: server:8080
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
log_format:
json_format:
start_time: "%START_TIME%"
method: "%REQ(:METHOD)%"
path: "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%"
protocol: "%PROTOCOL%"
response_code: "%RESPONSE_CODE%"
response_flags: "%RESPONSE_FLAGS%"
bytes_received: "%BYTES_RECEIVED%"
bytes_sent: "%BYTES_SENT%"
duration: "%DURATION%"
upstream_service_time: "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%"
x_forwarded_for: "%REQ(X-FORWARDED-FOR)%"
user_agent: "%REQ(USER-AGENT)%"
request_id: "%REQ(X-REQUEST-ID)%"
upstream_host: "%UPSTREAM_HOST%"
upstream_cluster: "%UPSTREAM_CLUSTER%"
downstream_local_address: "%DOWNSTREAM_LOCAL_ADDRESS%"
downstream_remote_address: "%DOWNSTREAM_REMOTE_ADDRESS%"
clusters:
- name: tunnel_cluster
connect_timeout: 5s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: server
common_tls_context:
validation_context:
trusted_ca:
filename: "/certs/server.crt"
alpn_protocols: [ "h2" ]
http2_protocol_options: {}
load_assignment:
cluster_name: tunnel_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: server
port_value: 8080
在项目根目录创建 server
目录:
mkdir server
在 server
目录中创建 server.js
和 Dockerfile
。
server.js
在 server/server.js
中添加以下代码:
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('/certs/server.key'),
cert: fs.readFileSync('/certs/server.crt'),
});
server.on('stream', (stream, headers) => {
const method = headers[':method'];
const path = headers[':path'];
if (method === 'CONNECT') {
console.log(`Received CONNECT request for ${path}`);
// 响应 200,建立隧道
stream.respond({
':status': 200,
});
// 在隧道内处理数据
stream.on('data', (chunk) => {
const message = chunk.toString();
console.log(`Received from client: ${message}`);
// 回应客户端
const response = `Echo from server: ${message}`;
stream.write(response);
});
stream.on('end', () => {
console.log('Stream ended by client.');
stream.end();
});
} else {
// 对于非 CONNECT 请求,返回 404
stream.respond({
':status': 404,
});
stream.end();
}
});
server.listen(8080, () => {
console.log('Secure HTTP/2 server is listening on port 8080');
});
注意:
secureConnection
事件,直接处理 TLS 连接后的 socket。Dockerfile
在 server/Dockerfile
中添加以下内容:
FROM node:14
WORKDIR /app
COPY server.js .
EXPOSE 8080
CMD ["node", "server.js"]
在项目根目录创建 client
目录:
mkdir client
在 client
目录中创建 client.js
。
client.js
在 client/client.js
中添加以下代码:
const net = require('net');
// 创建与 Envoy 的 TCP 连接
const client = net.createConnection({ port: 10000 }, () => {
console.log('Connected to Envoy.');
// 发送消息给服务器
let counter = 1;
const interval = setInterval(() => {
const message = `Message ${counter} from client!`;
client.write(message);
counter += 1;
}, 1000);
// 关闭连接
setTimeout(() => {
clearInterval(interval);
client.end();
}, 5000);
});
client.on('data', (data) => {
console.log(`Received from server: ${data.toString()}`);
});
client.on('end', () => {
console.log('Disconnected from server.');
});
client.on('error', (err) => {
console.error('Client error:', err);
});
说明:
在项目根目录创建 docker-compose.yml
:
version: '3.8'
services:
envoy:
image: envoyproxy/envoy:v1.32.1
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml
- ./certs:/certs # 挂载证书目录
ports:
- "10000:10000"
networks:
- envoy_network
depends_on:
- server
command: /usr/local/bin/envoy -c /etc/envoy/envoy.yaml --service-cluster envoy --log-level debug
server:
build:
context: ./server
networks:
- envoy_network
expose:
- "8080"
volumes:
- ./certs:/certs # 挂载证书目录
networks:
envoy_network:
在项目根目录下,运行:
docker-compose up --build
预期输出:
Secure HTTP/2 server is listening on port 8080
。打开新的终端窗口,进入 client
目录:
cd client
运行客户端:
node client.js
预期输出:
Connected to Envoy.
Received from server: Echo from server: Message 1 from client!
Received from server: Echo from server: Message 2 from client!
Received from server: Echo from server: Message 3 from client!
Received from server: Echo from server: Message 4 from client!
Received from server: Echo from server: Message 5 from client!
Disconnected from server.
在 Docker Compose 的输出中,你应该能看到服务器的日志:
envoy_1 | {"downstream_remote_address":"192.168.65.1:46306","path":null,"request_id":null,"bytes_sent":160,"protocol":null,"upstream_service_time":null,"bytes_received":88,"response_code":0,"user_agent":null,"downstream_local_address":"172.21.0.3:10000","upstream_host":"172.21.0.2:8080","start_time":"2024-12-03T11:37:59.542Z","upstream_cluster":"tunnel_cluster","duration":5012,"response_flags":"-","method":null,"x_forwarded_for":null}
server_1 | Secure HTTP/2 server is listening on port 8080
server_1 | New secure connection established.
server_1 | Received from client: Message 1 from client!
server_1 | Received from client: Message 2 from client!
server_1 | Received from client: Message 3 from client!
server_1 | Received from client: Message 4 from client!
server_1 | Received from client: Message 5 from client!
server_1 | Connection ended by client.
在 Envoy 的日志中,你可以看到它使用 HTTP/2 CONNECT 隧道与服务器建立连接的记录。
10000
(Envoy)和 8080
(服务器)未被占用。下图展示了客户端、Envoy 代理和服务器之间的交互,反映了数据的传递和隧道连接的建立的流程。
说明:
客户端与 Envoy 建立 TCP 连接:
Envoy 创建到服务器的连接:
tunnel_cluster
,创建新的连接(ConnectionId: 1)。建立 HTTP/2 CONNECT 隧道:
server:8080
。200 OK
,隧道建立成功。数据传输:
Message N
)到 Envoy。Echo Message N
)给 Envoy。Received from client: Message N from client!
。连接关闭:
日志记录:
Stream ended by client.
。虽然这是一个入门示例,但它为理解和进一步探索 HTTP/2 CONNECT 隧道功能提供了坚实的基础。在下一篇博客中讲解通过两个 Envoy 代理实现的隧道,带你进一步了解 Istio ambient 模式中的 HBONE 透明隧道。
2025-01-09 18:26:54
在工作中,我们经常需要清晰地传达复杂的想法,但单靠文字有时候实在不够直观。这时候,一张清晰的图表往往能让人一眼看懂你的思路。
今天想给大家推荐一个特别好用的工具——Napkin.ai。它可以自动把你的文字内容变成图表,比如信息图、流程图等等,让你的想法更容易被理解和传播。
不管你是市场营销人员、内容创作者,还是需要做商业演示的专业人士,Napkin.ai 都能帮你提升沟通效果,让你的内容更吸引人。
这款工具真的非常方便,试一次就会爱上!支持中文,而且免费使用,无次数限制。如果你也需要一个帮手来把复杂想法变得更直观,赶紧去试试吧!
2025-01-07 18:28:38
近日 Cilium 项目发布了 2024 年度报告,见 Github。这份报告详细回顾了 Cilium 在过去一年中所取得的重大成就,并展望了其在云原生网络和安全领域的未来发展方向。Cilium 项目自首次提交以来,历经近十年发展,其势头持续强劲,正引领云原生网络和安全领域的新变革。
2024 年是 Cilium 发展历程中至关重要的一年,它不仅巩固了其作为 Kubernetes 事实标准 CNI 的地位,更发展成为 Kubernetes 环境下的综合网络堆栈。Cilium 的演进展示了其应对现代云原生环境挑战的卓越能力,从最初的 pod 到 pod 连接方案,扩展到一个统一网络、可观察性和安全性的项目,这一切都由 eBPF 技术驱动。
来自用户的反馈和案例进一步印证了 Cilium 在性能、成本效益以及功能方面的优势。用户普遍认为,Cilium 有效降低了网络成本和 CPU 消耗,同时提供了卓越的性能和低延迟。此外,Cilium 的可观察性工具(如 Hubble)以及网络策略功能也受到了用户的广泛好评。
展望 2025 年,Cilium 的发展势头将持续增强。平台工程和整合趋势正在重塑组织管理 Kubernetes 网络的方式,而 Cilium 正处于这场变革的核心。我们预计:
Cilium 对外部工作负载、4 层负载均衡以及 BGP 增强功能的支持将弥合 Kubernetes 原生系统与传统系统之间的鸿沟。
Cilium 项目在 2024 年取得了巨大成功,确立了其在 Kubernetes 网络领域的领导地位。Cilium 不仅仅是一个 CNI,更是一个涵盖网络、可观察性和安全性的综合 Kubernetes 网络解决方案。随着云原生技术的不断发展,Cilium 将继续引领行业创新,为用户提供更强大、更可靠的云原生网络解决方案。
注:本文中的图片利用 napkin.ai 制作。
2025-01-07 15:07:30
本文围绕 Istio Ambient 模式下的数据包生命周期进行深入剖析,从初始数据包的流量拦截与目标解析,到后续数据包的快速转发与优化策略,帮助读者理解 Ambient 模式背后的技术逻辑和性能实践。
在 Ambient 模式中,数据包的处理路径从 Pod 内核态网络栈开始,经由 iptables
规则被拦截后进入 ztunnel 的用户态处理逻辑。ztunnel 负责透明代理、策略验证、加密隧道建立等任务,最终将数据包通过内核态网络再次转发给目标服务或下一个 ztunnel。其核心思想是通过首次数据包的详细解析和标记,为后续数据包铺路,从而减少重复开销。
下图展示了 Istio Ambient 模式中从 Pod 到 ztunnel 的数据包生命周期:
接下来,我们将详细介绍首个数据包与后续数据包的处理路径,并分析其中的技术要点与优化手段。
当应用程序在 Pod 内发出数据包(如 HTTP 请求),数据包首先经过 Pod 的网络命名空间和内核态网络栈进行处理。
iptables
规则对出站流量进行筛选,若发现目标地址为非本地资源且数据包未携带特定标记,则将数据包重定向至 ztunnel 的透明代理端口(如 15006
或 15008
)。借助 IP_TRANSPARENT
和 SO_ORIGINAL_DST
选项,ztunnel 可提取数据包的原始目标地址,实现无缝透明代理。
数据包进入 ztunnel 用户态后,将经历以下处理流程:
完成处理后,ztunnel 基于数据包解析结果建立出站连接(如 HTTP/2 隧道或明文 TCP),并将数据包送回内核态,最终转发至目标服务或下一个 ztunnel。
首个数据包完成解析与策略验证后,Linux 内核的连接跟踪(conntrack
)记录连接状态与标记。后续数据包无需再次经历复杂的拦截与解析,直接进入 ztunnel 的 inbound socket。
后续数据包基于 conntrack
跟踪机制,快速到达 ztunnel 的 inbound socket。ztunnel 可直接识别目标地址与安全策略,避免重复的解析与验证。
IP_TRANSPARENT
实现透明流量捕获与目标解析。conntrack
与 inbound socket 实现快速转发,降低上下文切换成本。Istio Ambient 模式通过数据包生命周期设计,在透明代理、性能优化与安全策略间实现平衡。zTunnel 通过高效的用户态处理与内核态快速转发,将应用程序的透明体验与底层网络优化有效结合,助力服务网格的实践与推广。