背景
近期,一位星球小伙伴入职新公司,之前习惯使用kubectl命令进行Kubernetes运维,但新公司所有集群都托管在阿里云ACK上。于是,他产生了一个想法:能否构建一个支持Kubernetes多集群管理的Web Terminal,通过kubectl命令来管理这些集群?答案是肯定的!接下来,我们将使用Golang和xtermjs来实现这一Kubernetes多集群管理Web Terminal。
技术栈
- 前端:Vue Typescript ArcoDesign xtermjs
- 后端:Golang Gin Gorm WebSocket
- 依赖:Kubectl、Client.go
主要功能
xterm.js使用指南
xterm.js 是一个强大的终端仿真库,可以在网页上创建一个功能完整的终端。它广泛用于构建Web终端应用,比如Kubernetes多集群管理Web Terminal。
首先,使用npm安装xterm.js和xterm-addon-fit:
代码语言:javascript复制$ npm install xterm xterm-addon-fit
参考代码(Vue3):
代码语言:javascript复制<template>
<!-- 终端的容器 -->
<div ref="terminalContainer" style="width: 100%; height: 100%"></div>
</template>
<script lang="ts" setup>
// 从 Vue 和 xterm 导入必要的模块
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { Terminal } from 'xterm';
import 'xterm/css/xterm.css';
import { Base64 } from 'js-base64';
// 终端容器的引用
const terminalContainer = ref<HTMLDivElement | null>(null);
// 终端和 WebSocket 的变量
let term: Terminal;
let socket: WebSocket;
let inputBuffer = ''; // 用于存储输入内容的缓冲区
// 组件挂载时的生命周期钩子
onMounted(() => {
if (terminalContainer.value) {
// 使用特定选项初始化终端
term = new Terminal({
convertEol: true,
disableStdin: false,
cursorBlink: true,
fontSize: 14,
rows: 50,
cols: 200,
theme: {
foreground: "#ECECEC",
background: "#000000",
cursor: 'help',
},
});
// 在指定容器中打开终端
term.open(terminalContainer.value);
// 连接 WebSocket
socket = new WebSocket('ws://10.0.63.28:8087/ws');
// WebSocket 打开事件监听器
socket.addEventListener('open', () => {
term.write('rn集群连接成功!rn'); // 向终端写入成功消息
term.write('> '); // 显示提示符
});
// WebSocket 收到消息事件监听器
socket.addEventListener('message', (event) => {
if (event.data !== 'Cg==') {
console.log(event.data);
term.write(Base64.decode(event.data)); // 解码并将消息写入终端
}
term.write('> '); // 显示提示符
});
// 处理终端输入
term.onData((e) => {
const char = e;
if (char === 'r') { // 检测 Enter 键
socket.send(inputBuffer); // 将缓冲区中的输入发送到 WebSocket
inputBuffer = ''; // 清空输入缓冲区
term.write('rn'); // 在终端中换行
} else if (char === 'u007F') { // 检测 Backspace 键
if (inputBuffer.length > 0) {
inputBuffer = inputBuffer.slice(0, -1); // 从缓冲区中删除最后一个字符
term.write('b b'); // 从终端显示中删除最后一个字符
}
} else {
inputBuffer = char; // 将字符添加到缓冲区
term.write(char); // 在终端中显示字符
}
});
}
});
// 组件卸载前的生命周期钩子
onBeforeUnmount(() => {
if (term) {
term.dispose(); // 释放终端资源
}
if (socket) {
socket.close(); // 关闭 WebSocket 连接
}
});
</script>
<style scoped></style>
options配置:
代码语言:javascript复制cmOptions: {
mode: "application/json", // 语言及语法模式
theme: "idea", // 主题
autoRefresh: true, // 自动刷新
line: true, // 显示函数
lint: true, // 校验
matchBrackets: true, // 括号匹配显示
autoCloseBrackets: true, // 输入和退格时成对
indentUnit: 2, // 缩进单位,默认2
lineWrapping: true, // 软换行
tabSize: 4, // tab宽度
lineNumbers: true, // 显示行数
foldGutter: true,
smartIndent: true, // 智能缩进
gutters: [
"CodeMirror-linenumbers",
"CodeMirror-foldgutter",
"CodeMirror-lint-markers", // 实现语法报错
],
},
使用Gin框架实现WebSocket
首先,你需要确保已经安装了Gin和Gorilla WebSocket库。这两个库可以通过以下命令进行安装:
代码语言:javascript复制$ go get -u github.com/gin-gonic/gin
$ go get -u github.com/gorilla/websocket
参考代码:
代码语言:javascript复制package main
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
// WebSocket升级器
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
// 允许所有请求源
return true
},
}
// WebSocket处理程序
func handleWebSocket(c *gin.Context) {
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"message": "Failed to upgrade to WebSocket"})
return
}
defer conn.Close()
for {
// 读取消息
mt, message, err := conn.ReadMessage()
if err != nil {
break
}
// 打印消息
println("Received:", string(message))
// 回显消息
err = conn.WriteMessage(mt, message)
if err != nil {
break
}
}
}
func main() {
r := gin.Default()
// 设置WebSocket路由
r.GET("/ws", handleWebSocket)
// 启动服务器
r.Run(":8080")
}
操作展示
创建集群:
填写集群名称、集群凭证:
点击终端:
连接成功:
此时可以输入命令体验一下喽:
敬请期待后续文章!