经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Kubernetes » 查看文章
Gin+Xterm.js实现远程Kubernetes Pod(一)
来源:cnblogs  作者:KubeSec  时间:2023/8/2 9:18:09  对本文有异议

Xterm.js简介

xterm.js (https://xtermjs.org/)是一个开源的 JavaScript 库,它模拟了一个终端接口,可以在网页中嵌入一个完全功能的终端。这个库非常灵活,并且具有很多定制选项和插件系统。
下面是一些使用 xterm.js 的基本步骤:
  • 首先,需要在项目中安装 xterm.js。你可以直接从 npm 安装:
  1. npm install xterm
  • 然后在 HTML 中创建一个容器来承载终端
  1. <div id="terminal"></div>
  • 在你的 JavaScript 文件中,导入 Terminal 类并创建一个新的实例
  1. import { Terminal } from 'xterm';
  2. const term = new Terminal();
  • 把这个终端附加到 HTML 元素上
  1. term.open(document.getElementById('terminal'));
  • 现在你就可以向终端写入数据了
  1. term.write('Hello, World!');
  • 如果你想读取用户在终端中的输入,可以监听 onData 事件
  1. term.onData(data => {
  2. console.log(data);
  3. });

以上只是最基础的使用方法。xterm.js 提供了许多其他功能,如主题定制、附加插件(例如 FitAddon 可以自动调整终端大小,WebLinksAddon 可以捕获 URL 并将其变为可点击链接)、设置光标样式、更改字体大小等等。你可以访问 xterm.js 的 GitHub (https://github.com/xtermjs/xterm.js)仓库 或者 文档 来获取更详细的信息。

 

使用Gin、client-go的SPDYExecutor来执行远程命令

  1. package main
  2. import (
  3. "context"
  4. "encoding/json"
  5. "github.com/gin-gonic/gin"
  6. "github.com/gorilla/websocket"
  7. corev1 "k8s.io/api/core/v1"
  8. "k8s.io/client-go/kubernetes"
  9. "k8s.io/client-go/kubernetes/scheme"
  10. "k8s.io/client-go/tools/clientcmd"
  11. "k8s.io/client-go/tools/remotecommand"
  12. "log"
  13. "net/http"
  14. )
  15. // websocket 升级器配置
  16. var upgrader = websocket.Upgrader{
  17. CheckOrigin: func(r *http.Request) bool {
  18. return true
  19. },
  20. }
  21. // WSClient 结构体,封装了 WebSocket 连接和 resize 通道,用于在 WebSocket 和 remotecommand 之间进行数据交换。
  22. type WSClient struct {
  23. // WebSocket 连接对象
  24. ws *websocket.Conn
  25. // TerminalSize 类型的通道,用于传输窗口大小调整事件
  26. resize chan remotecommand.TerminalSize
  27. }
  28. // MSG 结构体,用于解析从 WebSocket 接收到的消息。
  29. type MSG struct {
  30. // 消息类型字段
  31. MsgType string `json:"msg_type"`
  32. Rows uint16 `json:"rows"`
  33. Cols uint16 `json:"cols"`
  34. // 输入消息的数据字段
  35. Data string `json:"data"`
  36. }
  37. // WSClient 的 Read 方法,实现了 io.Reader 接口,从 websocket 中读取数据。
  38. func (c *WSClient) Read(p []byte) (n int, err error) {
  39. // 从 WebSocket 中读取消息
  40. _, message, err := c.ws.ReadMessage()
  41. if err != nil {
  42. return 0, err
  43. }
  44. var msg MSG
  45. if err := json.Unmarshal(message, &msg); err != nil {
  46. return 0, err
  47. }
  48. // 根据消息类型进行不同的处理
  49. switch msg.MsgType {
  50. // 如果是窗口调整消息
  51. case "resize":
  52. winSize := remotecommand.TerminalSize{
  53. Width: msg.Cols,
  54. Height: msg.Rows,
  55. }
  56. // 将 TerminalSize 对象发送到 resize 通道
  57. c.resize <- winSize
  58. return 0, nil
  59. // 如果是输入消息
  60. case "input":
  61. copy(p, msg.Data)
  62. return len(msg.Data), err
  63. }
  64. return 0, nil
  65. }
  66. // WSClient 的 Write 方法,实现了 io.Writer 接口,将数据写入 websocket。
  67. func (c *WSClient) Write(p []byte) (n int, err error) {
  68. // 将数据作为文本消息写入 WebSocket
  69. err = c.ws.WriteMessage(websocket.TextMessage, p)
  70. return len(p), err
  71. }
  72. // Next WSClient 的 Next 方法,用于从 resize 通道获取下一个 TerminalSize 事件。
  73. func (c *WSClient) Next() *remotecommand.TerminalSize {
  74. // 从 resize 通道读取 TerminalSize 对象
  75. size := <-c.resize
  76. return &size
  77. }
  78. // podSSH 函数,这是主要的 SSH 功能逻辑,使用 kubernetes client-go 的 SPDY executor 来执行远程命令。
  79. func podSSH(wsClient *WSClient, q query) {
  80. // 使用 kubeconfig 文件初始化 kubernetes 客户端配置
  81. // 请注意,你应该替换 ./config 为你的 kubeconfig 文件路径
  82. restClientConfig, err := clientcmd.BuildConfigFromFlags("", "./config")
  83. if err != nil {
  84. log.Fatalf("Failed to build config: %v", err)
  85. }
  86. // 根据配置创建 kubernetes 客户端
  87. clientSet, err := kubernetes.NewForConfig(restClientConfig)
  88. if err != nil {
  89. log.Fatalf("Failed to create client: %v", err)
  90. }
  91. // 构造一个用于执行远程命令的请求
  92. request := clientSet.CoreV1().RESTClient().Post().
  93. Resource("pods").
  94. Namespace(q.Namespace).
  95. Name(q.PodName).
  96. SubResource("exec").
  97. VersionedParams(&corev1.PodExecOptions{
  98. Container: q.ContainerName,
  99. Command: []string{
  100. q.Command,
  101. },
  102. Stdout: true,
  103. Stdin: true,
  104. Stderr: true,
  105. TTY: true,
  106. }, scheme.ParameterCodec)
  107. // 创建 SPDY executor,用于后续的 Stream 操作
  108. exec, err := remotecommand.NewSPDYExecutor(restClientConfig, "POST", request.URL())
  109. if err != nil {
  110. log.Fatalf("Failed to initialize executor: %v", err)
  111. }
  112. // 开始进行 Stream 操作,即通过 websocket 执行命令
  113. err = exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
  114. Stderr: wsClient,
  115. Stdout: wsClient,
  116. Stdin: wsClient,
  117. Tty: true,
  118. TerminalSizeQueue: wsClient,
  119. })
  120. if err != nil {
  121. log.Fatalf("Failed to start stream: %v", err)
  122. }
  123. }
  124. // query 结构体,用于解析和验证查询参数
  125. type query struct {
  126. Namespace string `form:"namespace" binding:"required"`
  127. PodName string `form:"pod_name" binding:"required"`
  128. ContainerName string `form:"container_name" binding:"required"`
  129. Command string `form:"command" binding:"required"`
  130. }
  131. func main() {
  132. router := gin.Default()
  133. router.LoadHTMLGlob("templates/*")
  134. router.GET("/", func(ctx *gin.Context) {
  135. ctx.HTML(http.StatusOK, "ssh.html", nil)
  136. })
  137. // 设置 /ssh 路由
  138. router.GET("/ssh", func(ctx *gin.Context) {
  139. var r query
  140. if err := ctx.ShouldBindQuery(&r); err != nil {
  141. ctx.JSON(http.StatusBadRequest, gin.H{
  142. "err": err.Error(),
  143. })
  144. return
  145. }
  146. // 将 HTTP 连接升级为 websocket 连接
  147. ws, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
  148. if err != nil {
  149. log.Printf("Failed to upgrade connection: %v", err)
  150. return
  151. }
  152. // 使用 podSSH 函数处理 websocket 连接
  153. podSSH(&WSClient{
  154. ws: ws,
  155. resize: make(chan remotecommand.TerminalSize),
  156. }, r)
  157. })
  158. router.Run(":9191")
  159. }
  • 后端全部代码
https://gitee.com/KubeSec/pod-webssh/tree/master/pod-ssh

使用vue-admin-template和Xterm.js实现Web终端

https://github.com/PanJiaChen/vue-admin-template

https://github.com/xtermjs/xterm.js

  • 下载vue-admin-template项目

https://github.com/PanJiaChen/vue-admin-template.git

  • 安装xterm.js及插件

  1. npm install
  2. npm install xterm
  3. npm install --save xterm-addon-web-links
  4. npm install --save xterm-addon-fit
  5. npm install -S xterm-style
  • 打开vue-admin-template项目,在src/views目录下新建目录pod-ssh,在pod-ssh目录下新建index.vue代码如下

  1. <template>
  2. <div class="app-container">
  3. <!-- 使用 Element UI 的表单组件创建一个带有标签和输入框的表单 -->
  4. <el-form ref="form" :model="form" :inline="true" label-width="120px">
  5. <el-form-item label="namespace"> <!-- namespace 输入框 -->
  6. <el-input v-model="form.namespace" />
  7. </el-form-item>
  8. <el-form-item label="pod name"> <!-- pod 名称输入框 -->
  9. <el-input v-model="form.pod_name" />
  10. </el-form-item>
  11. <el-form-item label="container name"> <!-- 容器名称输入框 -->
  12. <el-input v-model="form.container_name" />
  13. </el-form-item>
  14. <el-form-item label="Command"> <!-- 命令选择框 -->
  15. <el-select v-model="form.command" placeholder="bash">
  16. <el-option label="bash" value="bash" />
  17. <el-option label="sh" value="sh" />
  18. </el-select>
  19. </el-form-item>
  20. <el-form-item> <!-- 提交按钮 -->
  21. <el-button type="primary" @click="onSubmit">Create</el-button>
  22. </el-form-item>
  23. <div id="terminal" /> <!-- 终端视图容器 -->
  24. </el-form>
  25. </div>
  26. </template>
  27. <script>
  28. import { Terminal } from 'xterm' // 导入 xterm 包,用于创建和操作终端对象
  29. import { common as xtermTheme } from 'xterm-style' // 导入 xterm 样式主题
  30. import 'xterm/css/xterm.css' // 导入 xterm CSS 样式
  31. import { FitAddon } from 'xterm-addon-fit' // 导入 xterm fit 插件,用于调整终端大小
  32. import { WebLinksAddon } from 'xterm-addon-web-links' // 导入 xterm web-links 插件,可以捕获 URL 并将其转换为可点击链接
  33. import 'xterm/lib/xterm.js' // 导入 xterm 库
  34. export default {
  35. data() {
  36. return {
  37. form: {
  38. namespace: 'default', // 默认命名空间为 "default"
  39. command: 'bash', // 默认 shell 命令为 "bash"
  40. pod_name: 'nginx', // 默认 Pod 名称为 "nginx"
  41. container_name: 'nginx' // 默认容器名称为 "nginx"
  42. },
  43. }
  44. },
  45. methods: {
  46. onSubmit() {
  47. // 创建一个新的 Terminal 对象
  48. const xterm = new Terminal({
  49. theme: xtermTheme,
  50. rendererType: 'canvas',
  51. convertEol: true,
  52. cursorBlink: true
  53. })
  54. // 创建并加载 FitAddon 和 WebLinksAddon
  55. const fitAddon = new FitAddon()
  56. xterm.loadAddon(fitAddon)
  57. xterm.loadAddon(new WebLinksAddon())
  58. // 打开这个终端,并附加到 HTML 元素上
  59. xterm.open(document.getElementById('terminal'))
  60. // 调整终端的大小以适应其父元素
  61. fitAddon.fit()
  62. // 创建一个新的 WebSocket 连接,并通过 URL 参数传递 pod, namespace, container 和 command 信息
  63. const ws = new WebSocket('ws://127.0.0.1:9191/ssh?namespace=' + this.form.namespace + '&pod_name=' + this.form.pod_name + '&container_name=' + this.form.container_name + '&command=' + this.form.command)
  64. // 当 WebSocket 连接打开时,发送一个 resize 消息给服务器,告诉它终端的尺寸
  65. ws.onopen = function() {
  66. ws.send(JSON.stringify({
  67. msg_type: 'resize',
  68. rows: xterm.rows,
  69. cols: xterm.cols
  70. }))
  71. }
  72. // 当从服务器收到消息时,写入终端显示
  73. ws.onmessage = function(evt) {
  74. xterm.write(evt.data)
  75. }
  76. // 当发生错误时,也写入终端显示
  77. ws.onerror = function(evt) {
  78. xterm.write(evt.data)
  79. }
  80. // 当窗口尺寸变化时,重新调整终端的尺寸,并发送一个新的 resize 消息给服务器
  81. window.addEventListener('resize', function() {
  82. fitAddon.fit()
  83. ws.send(JSON.stringify({
  84. msg_type: 'resize',
  85. rows: xterm.rows,
  86. cols: xterm.cols
  87. }))
  88. })
  89. // 当在终端中键入字符时,发送一个 input 消息给服务器
  90. xterm.onData((b) => {
  91. ws.send(JSON.stringify({
  92. msg_type: 'input',
  93. data: b
  94. }))
  95. })
  96. }
  97. }
  98. }
  99. </script>
  100. <style scoped>
  101. .line{
  102. text-align: center;
  103. }
  104. </style>

在src/router/index.js文件中增加路由

  1. {
  2. path: '/pod-ssh',
  3. component: Layout,
  4. children: [
  5. {
  6. path: 'pod-ssh',
  7. name: 'SSH',
  8. component: () => import('@/views/pod-ssh/index'),
  9. meta: { title: 'SSH', icon: 'form' }
  10. }
  11. ]
  12. },
  • 启动项目

  1. npm install
  2. npm run dev
  • 前端全部代码

https://gitee.com/KubeSec/pod-webssh/tree/master/pod-webssh

 

测试

  • 在kubernetes中创建测试的Pod

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: nginx
  5. spec:
  6. containers:
  7. - name: nginx
  8. image: nginx:1.14.2
  9. ports:
  10. - containerPort: 80

访问http://localhost:9528/#/pod-ssh/pod-ssh

 

原文链接:https://www.cnblogs.com/0x00000/p/17557770.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号