当前位置: 首页 > news >正文

容器终端模拟shell终端

容器终端

现在我们就是做这个功能,用于连接容器,但容器不像虚拟机那样经常登录进去维护,容器是一个封装好的应用环境,即开即用,一个容器启动后基本不再对里面的环境做相关操作,即使修改也是重新构建镜像,我们做这个功能主要还是方便故障排查,进入容器里做一些调试工作。

要想实现类似SSH终端功能并非易事主要难点在于页面与连接的目标是实时交互的。说起实时交互,相信大家都有接触过,例如qq、微信、在线客服这些都是,像一些网页版的在线聊天系统常用的实现方案就是用websocket。

WebSocket协议与HTTP的主要区别:HTTP是无状态协议,由客户端发起请求,客户端与服务器“一问一答”,因此服务器端无法主动向客户端发送信息。而WebSocket是基于TCP长连接的协议,客户端与服务器建立连接后,服务器端随时能向客户端发送信息。

WebSocket协议的主要价值在于其与HTTP的差异(服务器端与客户端能够保持实时的双向通信),使其在某些应用情景下比HTTP更能满足技术需求。

Django Web框架实现WebSocket主要有两种方式:channels和dwebsocket。

Channels是针对Django项目的一个增强框架,使得Django不仅支持HTTP协议,还能支持WebSocket协议

为了更好的模拟shell终端还需要一个前端库xterm.js这是一个比较成熟的shell终端模拟库目前大部分公司实现的webssh都是用的这个

官网Xterm.js

大概思路就是这样的:
1、写一个前端页面里面用xterm.js模拟shell终端,再打开一个websocket,监听终端里用户输入,内容推送到django,在浏览器端模拟 shell 终端, 监听用户输入通过 websocket 将用户输入的内容上传到 django
2、django 接受到用户输入的内容, 将内容通过k8s stream与容器建立通道在容器里执行将返回的处理结果返回给django
3、django 将k8s stream返回的结果通过 websocket 返回到终端页面

1、前端

<script type="text/html" id="barDemo">
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="yaml">YAML</a>
<a class="layui-btn layui-btn-xs" lay-event="log">查看日志</a>
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="terminal" style="color: #FFF;background-color: #385985">终端</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">重建</a>
</script>

} else if(obj.event === 'terminal'){
// 逗号拼接容器名, 例如containers=c1,c2
cs = data['containers'];
containers = "";
for(let c in cs) {
if (c < cs.length-1) {
containers += cs[c]['c_name'] + ","
} else {
containers += cs[c]['c_name']
}
}
layer.open({
title: "容器终端",
type: 2, // 加载层,从另一个网址引用
area : [ '50%', '60%' ],
content: '{% url "terminal" %}?namespace=' + data["namespace"] + "&pod_name=" + data["name"] + "&containers=" + containers,
});
}

2、服务端

re_path('^terminal/$', views.terminal, name="terminal"),

from django.views.decorators.clickjacking import xframe_options_exempt
@xframe_options_exempt
@k8s.self_login_required
def terminal(request):
namespace = request.GET.get("namespace")
pod_name = request.GET.get("pod_name")
containers = request.GET.get("containers").split(',') # 返回 nginx1,nginx2,转成一个列表方便前端处理
auth_type = request.session.get('auth_type') # 认证类型和token,用于传递到websocket,websocket根据sessionid获取token,让websocket处理连接k8s认证用
token = request.session.get('token')
connect = {'namespace': namespace, 'pod_name': pod_name, 'containers': containers, 'auth_type': auth_type, 'token': token}
return render(request, 'workload/terminal.html', {'connect': connect})

容器终端页面:workload/terminal.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>容器终端</title>
<link href="/static/xterm/xterm.css" rel="stylesheet" type="text/css"/>
<style>
body {
background-color: black
}
.terminal-window {
background-color: #2f4050;
width: 99%;
color: white;
line-height: 25px;
margin-bottom: 10px;
font-size: 18px;
padding: 10px 0 10px 10px
}
.containers select,.containers option {
width: 100px;
height: 25px;
font-size: 18px;
color: #2F4056;
text-overflow: ellipsis;
outline: none;
}
</style>
</head>

<body>
<div class="terminal-window">
<div class="containers">
Pod名称:{{ connect.pod_name }}
容器:
<select name="container_name" id="containerSelect">
{% for c in connect.containers %}
<option value="{{ c }}">{{ c }}</option>
{% endfor %}
</select>
</div>
</div>
<div id="terminal" style="width: 100px;"></div>
</body>

<script src="/static/xterm/xterm.js"></script>

<script>

var term = new Terminal({cursorBlink: true,rows:70});
term.open(document.getElementById('terminal'));

var auth_type = '{{ connect.auth_type }}';
var token = '{{ connect.token }}';
var namespace = '{{ connect.namespace }}';
var pod_name = '{{ connect.pod_name }}';
var container = document.getElementById('containerSelect').value;

// 打开一个 websocket,django也会把sessionid传过去
var ws = new WebSocket('ws://' + window.location.host + '/workload/terminal/' + namespace + '/' + pod_name + '/' + container + '/?auth_type=' + auth_type + '&token=' + token);

//打开websocket连接,并打开终端
ws.onopen = function () {
// 实时监控输入的字符串发送到后端
term.on('data', function (data) {
ws.send(data);
});

ws.onerror = function (event) {
console.log('error:' + e);
};
//读取服务器发送的数据并写入web终端
ws.onmessage = function (event) {
term.write(event.data);
};
// 关闭websocket
ws.onclose = function (event) {
term.write('\n\r\x1B[1;3;31m连接关闭!\x1B[0m');
};
};

</script>

</html>

3、Django Channels

3.1 安装模块

pip install channels

pip install channels_redis

  1. channels是Django的扩展模块,用于处理WebSocket
  2. channels_redis 使用redis作为存储维护不同消息传递

3.2 配置Channels

settings.py文件中注册channels

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

'channels'

]

ASGI_APPLICATION = 'devops.routing.application'

3.3配置路由

devops/routing.py # 路由文件

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter

from django.urls import re_path
from devops.consumers importStreamConsumer

application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter([
re_path(r'^workload/terminal/(?P<namespace>.*)/(?P<pod_name>.*)/(?P<container>.*)/',StreamConsumer),
])
),
})

正则匹配所有以workload/terminal开头的websocket连接,都交由名为StreamConsumer的处理。

3.4WebSocket服务端消费

devops/consumers.py#建立K8s Stream容器通道和处理前端DjangoWebsocket通道

3.5配置Redis

docker run --name redis -d -p 6379:6379 redis:3

settings.py配置文件添加redis配置,内容如下:

CHANNEL_LAYERS = {

'default': {

'BACKEND': 'channels_redis.core.RedisChannelLayer',

'CONFIG': {

"hosts": [('192.168.31.61', 6379)],

},

},

}

小结

终端->弹出框(从另一个加载/workload/terminal/?namespace=default&pod_name=web-7989784f96-wxbnm&containers=nginx)->执行页面里websocket建立连接(/workload/terminal/default/web-7989784f96-wxbnm/nginx/)->routing.py路由大奥StreamConsumer处理(K8s Stream与容器建立通道)

http://www.jsqmd.com/news/989978/

相关文章:

  • Spring AI Alibaba 1.x 系列【76】上下文工程(Context Engineering)
  • 如何用3步解决Windows和Office激活难题?
  • make-sense.ai:革命性的浏览器端AI图像标注工具
  • Revit2GLTF:BIM模型到Web3D的高性能转换架构与实施策略
  • 惠普OMEN游戏本终极性能控制工具:OmenSuperHub完整指南
  • 揭秘微信数据安全:3步掌握聊天记录备份的核心方法
  • 2026舞狮表演优质机构推荐:庆典公司/开工仪式/投产仪式/摄影摄像公司/模特公司/活动策划公司/执行保障力突出 - 优质品牌商家
  • qobuz-dl终极指南:快速打造你的无损高解析度音乐收藏库
  • 2026年靠谱的粉末成型压机/电动工具齿轮粉末成型压机/宁波家电电机齿轮粉末成型压机/宁波气门导管座圈粉末成型压机厂家精选合集 - 品牌宣传支持者
  • 如何用WeChatMsg构建个人AI记忆库:三步实现聊天数据价值挖掘
  • 还在用 Anaconda?Miniforge:conda-forge 官方极简安装器,内置 Mamba,6 大架构全覆盖,5 分钟从零搭建 Python 环境
  • 啤酒设备行业主流供应商综合能力分析及选择参考(2026版) - 优质品牌商家
  • 3步完成黑苹果配置:OpCore-Simplify让OpenCore EFI生成如此简单
  • 做GEO优化多久可以看到获客效果
  • 2026年AI写作辅助平台推荐:9款高效AI工具终极指南
  • PoseLib:面向校准相机姿态估计的高性能最小求解器库
  • 数据的加密与解密(04:18)
  • 深入浅出吃透ARMS原理与实战用法
  • 收藏!普通人也能入局!国产AI大模型商业化落地,低门槛抓住红利机遇
  • 三维动画服务商综合能力分析:2026年行业格局与选型参考 - 优质品牌商家
  • 2026年知名的女装联营/临猗女装拿货加盟/零库存女装加盟/临猗女装整店输出品牌排行 - 行业平台推荐
  • 2026年热门的女装实体店合作/临猗女装集合店/临猗女装实体店合作哪个品牌好 - 行业平台推荐
  • 数据的加密与解密(04:26)
  • 2026年质量好的宜宾全屋定制装修/宜宾装修专业靠谱公司 - 行业平台推荐
  • FPGA项目实战:用Vivado ROM IP核在AX7A035开发板上实现一个简易正弦波信号发生器
  • GEO优化一般多久上百度首页
  • Python量化开发者的痛点:通达信数据如何与Python生态无缝对接?
  • AMD Ryzen处理器调试工具SMUDebugTool:深度掌控硬件性能的完整指南
  • 2026年知名的宜宾毛坯房装修/宜宾一站式整装装修/宜宾透明化装修服务好的公司 - 品牌宣传支持者
  • 2026年热门的宁波粉末成型伺服液压机/粉末成型伺服液压机/氧化铝陶瓷干压成型伺服液压机定制加工厂家推荐 - 行业平台推荐