Python Django擼個WebSSH操作Kubernetes Pod(下)- 終端視窗自適應Resize
追求完美不服輸的我,一直在與各種問題鬥爭的路上痛並快樂著
上一篇文章Django實現WebSSH操作Kubernetes Pod最後留了個問題沒有解決,那就是terminal內容視窗的大小沒有辦法調整,這會導致的一個問題就是瀏覽器上可顯示內容的區域太小,當檢視/編輯檔案時非常不便,就像下邊這樣,紅色可視區域並沒有被用到
RESIZE_CHANNEL
前文說到kubectl exec
有兩個引數COLUMNS
和LINES
可以調整tty內容視窗的大小,命令如下:
kubectl exec -i -t $1 env COLUMNS=$COLUMNS LINES=$LINES bash
這實際上就是將COLUMNS
LINES
兩個環境變數傳遞到了容器內,由於Kubernetes stream底層也是通過kubernetes exec
實現的,所以我們在啟動容器時也將這兩個變數傳遞進去就可以了,就像這樣
exec_command = [ "/bin/sh", "-c", 'export LINES=20; export COLUMNS=100; ' 'TERM=xterm-256color; export TERM; [ -x /bin/bash ] ' '&& ([ -x /usr/bin/script ] ' '&& /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) ' '|| exec /bin/sh']
添加了export LINES=20; export COLUMNS=100;
,可以實現改變tty的輸出大小,但這有個問題就是隻能在建立連結時指定一次,不能動態的更新,也就是在一次websocket會話的過程中,如果頁面大小改變了,後端輸出的LINES和COLUMNS是無法隨著改變的
在解決問題的過程中發現官方原始碼中有個RESIZE_CHANNEL
的配置,同樣可以控制視窗的大小,使用方法如下:
cont_stream = stream(api_instance.connect_get_namespaced_pod_exec, name=pod_name, namespace=self.namespace, container=container, command=exec_command, stderr=True, stdin=True, stdout=True, tty=True, _preload_content=False ) cont_stream.write_channel(4, json.dumps({"Height": int(rows), "Width": int(cols)}))
這樣我們就可以修改stream輸出的視窗大小了
xterm.js fit
一頓操作後,開啟頁面,咦?怎麼頁面不行,原來視窗的調整不僅需要調整stream輸出資料的視窗大小,前端頁面也要跟著一併調整
這裡用到了xterm.js的另一個元件fit,fit可以調整終端大小的cols
和rows
適配父級元素
首先調整terminal塊的寬度和高度為整個頁面可視區域的大小,要讓整個可視區域為終端視窗
document.getElementById('terminal').style.height = window.innerHeight + 'px';
然後引入fit元件,在term初始化之後執行fit
操作
<script src="/static/plugins/xterm/xterm.js"></script>
<script src="/static/plugins/xterm/addons/fit/fit.js"></script>
<script>
// 修改terminal的高度為body的高度
document.getElementById('terminal').style.height = window.innerHeight + 'px';
var term = new Terminal({cursorBlink: true});
term.open(document.getElementById('terminal'));
// xterm fullscreen config
Terminal.applyAddon(fit);
term.fit();
console.log(term.cols, term.rows);
</script>
fit之後就可以通過term.cols
和term.rows
取到xterm.js根據字型大小自動計算過的cols
和rows
的值了,然後把這兩個值傳遞給kubernetes,kubernetes再根據這兩個值輸出視窗大小,這樣前後端匹配就完美了
資料傳遞
xterm.js可以通過如下的方法動態的將cols
和rows
傳遞給後端
term.on('resize', size => {
socket.send('resize', [size.cols, size.rows]);
})
但當視窗由大變小時,之前輸出的內容會有樣式錯亂,我為了方便直接在WebSocket連線建立時採用url傳參的方式把cols
和rows
兩個值傳遞給後端,kubernetes根據這兩個值來設定輸出內容的視窗大小,這樣做的缺點是不會隨著前端頁面的變化動態的去調整後端stream輸出視窗的大小,不過問題不大,如果頁面調整大小,重新整理下頁面重新建立連線就可以啦,具體實現如下
首先需要修改的就是WebSocket的url地址
前端增加term.cols
和term.rows
兩個引數的傳遞
var socket = new WebSocket(
'ws://' + window.location.host + '/pod/{{ name }}/'+term.cols+'/'+term.rows);
Routing增加兩個引數的解析
re_path(r'^pod/(?P<name>\w+)/(?P<cols>\d+)/(?P<rows>\d+)$', SSHConsumer),
Consumer解析URL將對應引數傳遞給Kubernetes stream
class SSHConsumer(WebsocketConsumer):
def connect(self):
self.name = self.scope["url_route"]["kwargs"]["name"]
self.cols = self.scope["url_route"]["kwargs"]["cols"]
self.rows = self.scope["url_route"]["kwargs"]["rows"]
# kube exec
self.stream = KubeApi().pod_exec(self.name, cols=self.cols, rows=self.rows)
kub_stream = K8SStreamThread(self, self.stream)
kub_stream.start()
self.accept()
最後Kubernetes stream接收引數並修改視窗大小
def pod_exec(self, RAND, container="", rows=24, cols=80):
api_instance = client.CoreV1Api()
exec_command = [
"/bin/sh",
"-c",
'TERM=xterm-256color; export TERM; [ -x /bin/bash ] '
'&& ([ -x /usr/bin/script ] '
'&& /usr/bin/script -q -c "/bin/bash" /dev/null || exec /bin/bash) '
'|| exec /bin/sh']
cont_stream = stream(api_instance.connect_get_namespaced_pod_exec,
name=pod_name,
namespace=self.namespace,
container=container,
command=exec_command,
stderr=True, stdin=True,
stdout=True, tty=True,
_preload_content=False
)
cont_stream.write_channel(4, json.dumps({"Height": int(rows), "Width": int(cols)}))
return cont_stream
至此,每次WebSocket連線建立,前後端就會有一樣的輸出視窗大小,問題解決~
相關文章推薦閱讀:
- 堡壘機WebSSH全功能實現教程
- DevOps運維自動化工具系統平臺