当前位置: 首页 > 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.rkmt.cn/news/1501727.html

相关文章:

  • make-sense.ai:革命性的浏览器端AI图像标注工具
  • 如何用WeChatMsg构建个人AI记忆库:三步实现聊天数据价值挖掘
  • 揭秘微信数据安全:3步掌握聊天记录备份的核心方法
  • 收藏!普通人也能入局!国产AI大模型商业化落地,低门槛抓住红利机遇
  • 深入浅出吃透ARMS原理与实战用法
  • 数据的加密与解密(04:26)
  • 2026年热门的宁波粉末成型伺服液压机/粉末成型伺服液压机/氧化铝陶瓷干压成型伺服液压机定制加工厂家推荐 - 行业平台推荐
  • GEO优化一般多久上百度首页
  • 告别VGA大块头!用FPGA驱动ST7789V小屏的保姆级教程(附Verilog源码)
  • 2026年评价高的铁基粉末冶金伺服液压机/宁波锰锌粉末成型伺服液压机/宁波石墨粉末成型伺服液压机精选推荐公司 - 品牌宣传支持者
  • 会话安全防护:防盗用、防重放攻击实操详解
  • 2026年质量好的临猗女装实体店合作/临猗女装拿货加盟哪个品牌靠谱 - 品牌宣传支持者
  • 国产FPGA开发踩坑记:安路TD工具链下,如何用Verilog模块将标准FIFO“魔改”成FWFT模式
  • 亚洲封面人物深度|从流量乱象到标准秩序:香港品牌研究院IP体系行业价值
  • 2026年四川及重庆红木家具厂家选择指南:从定制到全屋整装的服务能力分析 - 优质品牌商家
  • 论文笔记智能化革命:从手动整理到AI驱动的知识管理新范式
  • 钢结构工程实用经验汇总!
  • 2026年兼具商务感与生活品味的轻奢行李箱推荐:适合商旅两用的高品质选择
  • 2026年质量好的芳纶纸蜂窝复合材料/长春芳纶纸蜂窝复合材料/芳纶纸蜂窝复合板源头工厂推荐 - 品牌宣传支持者
  • AhabAssistantLimbusCompany:如何用智能自动化解放你的游戏时间
  • 告别卡顿!在RK3588开发板上用QT+MPP实现四路RTSP硬解码拉流(附完整代码)
  • 数据的加密与解密(04:11)
  • 2026年质量好的贵州生猪销售/贵州富硒饲料/猪饲料/贵州富硒肉精选推荐公司 - 品牌宣传支持者
  • 钢结构工程要注意的几个重要质量控制点
  • 阴阳师自动化脚本:如何用智能辅助高效管理你的日常游戏任务
  • Pywinauto终极指南:用Python轻松实现Windows GUI自动化测试的完整解决方案
  • 数据的加密与解密(04:13)
  • 别再死记硬背GAN公式了!用Python和PyTorch从零复现经典论文,带你亲手跑出第一张‘假’MNIST
  • 6款优质降AI率软件 创作效率拉满
  • 计算机毕业设计之Django框架的boss直聘可视化分析系统