一步一步教你写kubernetes sidecar

打印 上一主题 下一主题

主题 663|帖子 663|积分 1989

本文分享自华为云社区《一步一步教你写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模板如下
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4.   name: webserver
  5. spec:
  6.   volumes:
  7.     - name: shared-logs
  8.       emptyDir: {}
  9.   containers:
  10.     - name: nginx
  11.       image: ttbb/nginx:mate
  12.       volumeMounts:
  13.         - name: shared-logs
  14.           mountPath: /opt/sh/openresty/nginx/logs
  15.     - name: sidecar-container
  16.       image: ttbb/base
  17.       command: ["sh","-c","while true; do cat /opt/sh/openresty/nginx/logs/nginx.pid; sleep 30; done"]
  18.       volumeMounts:
  19.         - name: shared-logs
  20.           mountPath: /opt/sh/openresty/nginx/logs
复制代码
使用kubectl create -f 创建pod,通过kubectl logs命令就可以看到sidecar-container打印的日志输出
  1. kubectl logs webserver sidecar-container
复制代码
转发请求sidecar

这一节我们来实现,一个给应用程序转发请求的sidecar,应用程序代码如下
  1. use std::io::prelude::*;
  2. use std::net::{TcpListener, TcpStream};
  3. fn main() {
  4.     let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
  5.     for stream in listener.incoming() {
  6.         let stream = stream.unwrap();
  7.         handle_connection(stream);
  8.     }
  9.     println!("Hello, world!");
  10. }
  11. fn handle_connection(mut stream: TcpStream) {
  12.     let mut buffer = [0; 1024];
  13.     stream.read(&mut buffer).unwrap();
  14.     let contents = "Hello";
  15.     let response = format!(
  16.         "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
  17.         contents.len(),
  18.         contents
  19.     );
  20.     println!("receive a request!");
  21.     stream.write(response.as_bytes()).unwrap();
  22.     stream.flush().unwrap();
  23. }
复制代码
我们再来写一个sidecar,它会每15秒向应用程序发出请求
  1. use std::thread;
  2. use std::time::Duration;
  3. fn main() {
  4.     loop {
  5.         thread::sleep(Duration::from_secs(15));
  6.         let response = reqwest::blocking::get("http://localhost:7878").unwrap();
  7.         println!("{}", response.text().unwrap())
  8.     }
  9. }
复制代码
通过仓库下的intput/build.sh脚本构造镜像,运行yaml如下
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4.   name: webserver
  5. spec:
  6.   containers:
  7.     - name: input-server
  8.       image: sidecar-examples:input-http-server
  9.     - name: input-sidecar
  10.       image: sidecar-examples:sidecar-input
复制代码
通过查看kubectl logs input input-http-server可以看到input-http-server收到了请求
  1. receive a request!
  2. receive a request!
复制代码
拦截请求sidecar

应用程序代码,它会每15s向localhost发出请求
  1. package com.shoothzj.sidecar
  2. import akka.actor.typed.ActorSystem
  3. import akka.actor.typed.scaladsl.Behaviors
  4. import akka.http.scaladsl.Http
  5. import akka.http.scaladsl.model._
  6. import scala.concurrent.{ExecutionContextExecutor, Future}
  7. import scala.util.{Failure, Success}
  8. object HttpClient {
  9.     def main(args: Array[String]): Unit = {
  10.         while (true) {
  11.             Thread.sleep(15_000L)
  12.             implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "SingleRequest")
  13.             // needed for the future flatMap/onComplete in the end
  14.             implicit val executionContext: ExecutionContextExecutor = system.executionContext
  15.             val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://localhost:7979/hello"))
  16.             responseFuture
  17.                     .onComplete {
  18.                         case Success(res) => println(res)
  19.                         case Failure(_) => sys.error("something wrong")
  20.                     }
  21.         }
  22.     }
  23. }
复制代码
我们再来写一个sidecar,它会拦截http请求并打印日志
  1. package com.shoothzj.sidecar
  2. import akka.actor.typed.ActorSystem
  3. import akka.actor.typed.scaladsl.Behaviors
  4. import akka.http.scaladsl.Http
  5. import akka.http.scaladsl.model._
  6. import akka.http.scaladsl.server.Directives._
  7. import scala.concurrent.ExecutionContextExecutor
  8. import scala.io.StdIn
  9. object HttpServer {
  10.     def main(args: Array[String]): Unit = {
  11.         implicit val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "my-system")
  12.         // needed for the future flatMap/onComplete in the end
  13.         implicit val executionContext: ExecutionContextExecutor = system.executionContext
  14.         val route =
  15.             path("hello") {
  16.                 get {
  17.                     println("receive a request")
  18.                     complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
  19.                 }
  20.             }
  21.         val bindingFuture = Http().newServerAt("localhost", 7979).bind(route)
  22.         while (true) {
  23.             Thread.sleep(15_000L)
  24.         }
  25.     }
  26. }
复制代码
通过仓库下的output/build.sh脚本构造镜像,运行yaml如下
  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4.   name: output
  5. spec:
  6.   volumes:
  7.     - name: shared-logs
  8.       emptyDir: {}
  9.   containers:
  10.     - name: output-workload
  11.       image: sidecar-examples:output-workload
  12.       imagePullPolicy: Never
  13.     - name: sidecar-output
  14.       image: sidecar-examples:sidecar-output
  15.       imagePullPolicy: Never
复制代码
通过查看kubectl logs output output-workload可以看到output-sidecar收到了请求
  1. 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))
  2. 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))
  3. 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))
  4. 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))
  5. 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))
  6. 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))
  7. 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))
  8. HttpResponse(200 OK,List(Server: akka-http/10.2.9, Date: Tue, 29 Mar 2022 00:17:32 GMT),HttpEntity.Strict(text/html; charset=
复制代码
点击关注,第一时间了解华为云新鲜技术~
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

盛世宏图

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表