写点什么

一步一步教你写 kubernetes sidecar

  • 2023-12-29
    广东
  • 本文字数:3638 字

    阅读完需:约 12 分钟

一步一步教你写kubernetes sidecar

本文分享自华为云社区《一步一步教你写kubernetes sidecar》,作者: 张俭。

什么是 sidecar?



sidecar,直译为边车。 如上图所示,边车就是加装在摩托车旁来达到拓展功能的目的,比如行驶更加稳定,可以拉更多的人和货物,坐在边车上的人可以给驾驶员指路等。边车模式通过给应用服务加装一个“边车”来达到控制逻辑的分离的目的。


对于微服务来讲,我们可以用边车模式来做诸如 日志收集、服务注册、服务发现、限流、鉴权等不需要业务服务实现的控制面板能力。通常和边车模式比较的就是像 spring-cloud 那样的 sdk 模式,像上面提到的这些能力都通过 sdk 实现。



这两种实现模式各有优劣,sidecar 模式会引入额外的性能损耗以及延时,但传统的 sdk 模式会让代码变得臃肿并且升级复杂,控制面能力和业务面能力不能分开升级。


本文的代码已经上传到gitee

sidecar 实现原理


介绍了 sidecar 的诸多功能,但是,sidecar 是如何做到这些能力的呢?


原来,在 kubernetes 中,一个 pod 是部署的最小单元,但一个 pod 里面,允许运行多个 container(容器),多个 container(容器)之间共享存储卷和网络栈。这样子,我们就可以多 container 来做 sidecar,或者 init-container(初始化容器)来调整挂载卷的权限


日志收集 sidecar


日志收集 sidecar 的原理是利用多个 container 间可以共用挂载卷的原理实现的,通过将应用程序的日志路径挂出,用另一个程序访问路径下的日志来实现日志收集,这里用 cat 来替代了日志收集,部署 yaml 模板如下:


apiVersion: v1kind: Podmetadata:  name: webserverspec:  volumes:    - name: shared-logs      emptyDir: {}
containers: - name: nginx image: ttbb/nginx:mate volumeMounts: - name: shared-logs mountPath: /opt/sh/openresty/nginx/logs
- name: sidecar-container image: ttbb/base command: ["sh","-c","while true; do cat /opt/sh/openresty/nginx/logs/nginx.pid; sleep 30; done"] volumeMounts: - name: shared-logs mountPath: /opt/sh/openresty/nginx/logs
复制代码


使用 kubectl create -f 创建 pod,通过 kubectl logs 命令就可以看到 sidecar-container 打印的日志输出


kubectl logs webserver sidecar-container
复制代码

转发请求 sidecar


这一节我们来实现,一个给应用程序转发请求的 sidecar,应用程序代码如下


use std::io::prelude::*;use std::net::{TcpListener, TcpStream};
fn main() { let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
for stream in listener.incoming() { let stream = stream.unwrap();
handle_connection(stream); } println!("Hello, world!");}
fn handle_connection(mut stream: TcpStream) { let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
let contents = "Hello";
let response = format!( "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", contents.len(), contents );
println!("receive a request!"); stream.write(response.as_bytes()).unwrap(); stream.flush().unwrap();}
复制代码


我们再来写一个 sidecar,它会每 15 秒向应用程序发出请求


use std::thread;use std::time::Duration;
fn main() { loop { thread::sleep(Duration::from_secs(15)); let response = reqwest::blocking::get("http://localhost:7878").unwrap(); println!("{}", response.text().unwrap()) }}
复制代码


通过仓库下的intput/build.sh脚本构造镜像,运行 yaml 如下


apiVersion: v1kind: Podmetadata:  name: webserverspec:  containers:    - name: input-server      image: sidecar-examples:input-http-server
- name: input-sidecar image: sidecar-examples:sidecar-input
复制代码


通过查看 kubectl logs input input-http-server 可以看到 input-http-server 收到了请求


receive a request!receive a request!
复制代码

拦截请求 sidecar


应用程序代码,它会每 15s 向localhost发出请求


package com.shoothzj.sidecar
import akka.actor.typed.ActorSystemimport akka.actor.typed.scaladsl.Behaviorsimport akka.http.scaladsl.Httpimport akka.http.scaladsl.model._
import scala.concurrent.{ExecutionContextExecutor, Future}import scala.util.{Failure, Success}
object HttpClient { def main(args: Array[String]): Unit = { while (true) { Thread.sleep(15_000L) implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "SingleRequest") // needed for the future flatMap/onComplete in the end implicit val executionContext: ExecutionContextExecutor = system.executionContext
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://localhost:7979/hello"))
responseFuture .onComplete { case Success(res) => println(res) case Failure(_) => sys.error("something wrong") } } }}
复制代码


我们再来写一个 sidecar,它会拦截 http 请求并打印日志


package com.shoothzj.sidecar
import akka.actor.typed.ActorSystemimport akka.actor.typed.scaladsl.Behaviorsimport akka.http.scaladsl.Httpimport akka.http.scaladsl.model._import akka.http.scaladsl.server.Directives._
import scala.concurrent.ExecutionContextExecutorimport scala.io.StdIn
object HttpServer {
def main(args: Array[String]): Unit = {
implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "my-system") // needed for the future flatMap/onComplete in the end implicit val executionContext: ExecutionContextExecutor = system.executionContext
val route = path("hello") { get { println("receive a request") complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>")) } }
val bindingFuture = Http().newServerAt("localhost", 7979).bind(route) while (true) { Thread.sleep(15_000L) } }}
复制代码


通过仓库下的output/build.sh脚本构造镜像,运行 yaml 如下


apiVersion: v1kind: Podmetadata:  name: outputspec:  volumes:    - name: shared-logs      emptyDir: {}
containers: - name: output-workload image: sidecar-examples:output-workload imagePullPolicy: Never
- name: sidecar-output image: sidecar-examples:sidecar-output imagePullPolicy: Never
复制代码


通过查看 kubectl logs output output-workload 可以看到 output-sidecar 收到了请求


HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:15:47 GMT),HttpEntity.Strict(text/html; charset=UTF-8,31 bytes total),HttpProtocol(HTTP/1.1))HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:16:02 GMT),HttpEntity.Strict(text/html; charset=UTF-8,31 bytes total),HttpProtocol(HTTP/1.1))HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:16:17 GMT),HttpEntity.Strict(text/html; charset=UTF-8,31 bytes total),HttpProtocol(HTTP/1.1))HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:16:32 GMT),HttpEntity.Strict(text/html; charset=UTF-8,31 bytes total),HttpProtocol(HTTP/1.1))HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:16:47 GMT),HttpEntity.Strict(text/html; charset=UTF-8,31 bytes total),HttpProtocol(HTTP/1.1))HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:17:02 GMT),HttpEntity.Strict(text/html; charset=UTF-8,31 bytes total),HttpProtocol(HTTP/1.1))HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:17:17 GMT),HttpEntity.Strict(text/html; charset=UTF-8,31 bytes total),HttpProtocol(HTTP/1.1))HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:17:32 GMT),HttpEntity.Strict(text/html; charset=
复制代码


点击关注,第一时间了解华为云新鲜技术~

发布于: 刚刚阅读数: 5
用户头像

提供全面深入的云计算技术干货 2020-07-14 加入

生于云,长于云,让开发者成为决定性力量

评论

发布
暂无评论
一步一步教你写kubernetes sidecar_开发_华为云开发者联盟_InfoQ写作社区
vConsole