如何通过WebSocket连接Docker构建基于xterm.js的浏览器端Shell
Connecting xterm.js to Docker via WebSocket
Hey there! Glad you've got the xterm.js attach addon sorted out—getting it talking to Docker via WebSocket is totally manageable, and it needs a simple backend middleman (since browsers can't directly interact with the Docker API for security and compatibility reasons). Let's break this down step by step.
1. Core Flow Overview
Here's how the pieces fit together:
- Your browser runs xterm.js, which connects to a WebSocket server.
- The WebSocket server interacts with the Docker API to spin up or connect to a container.
- The server forwards input from xterm.js to the container's stdin, and sends the container's stdout/stderr back to xterm.js for display.
2. Build the WebSocket Backend (Node.js Example)
We'll use Node.js here since it pairs nicely with xterm.js, but you can use any language with WebSocket and Docker API support.
Step 2.1: Install Dependencies
First, set up a Node.js project and install required packages:
mkdir docker-websocket-server && cd docker-websocket-server npm init -y npm install ws dockerode
Step 2.2: Write the Server Code
Create a file server.js with this code:
const WebSocket = require('ws'); const Docker = require('dockerode'); // Initialize Docker client (uses local socket by default) const docker = new Docker({ socketPath: '/var/run/docker.sock' }); // Start WebSocket server on port 8080 const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', async (ws) => { console.log('New client connected'); let container; let exec; try { // 1. Create a new Docker container with interactive TTY container = await docker.createContainer({ Image: 'ubuntu:latest', // Use an image with a shell (adjust if needed) Tty: true, OpenStdin: true, StdinOnce: false, Cmd: ['/bin/bash'] }); await container.start(); console.log(`Container started with ID: ${container.id.slice(0, 12)}`); // 2. Create an exec instance to interact with the container's shell exec = await container.exec({ Cmd: ['/bin/bash'], Tty: true, AttachStdin: true, AttachStdout: true, AttachStderr: true }); // 3. Start the exec stream const stream = await exec.start({ hijack: true, stdin: true }); // Forward WebSocket messages (xterm input) to container stdin ws.on('message', (data) => { try { // Handle terminal resize events const msg = JSON.parse(data); if (msg.type === 'resize') { exec.resize({ h: msg.rows, w: msg.cols }); return; } } catch (err) { // If not a resize event, send the input to the container stream.write(data); } }); // Forward container output to WebSocket (xterm display) stream.on('data', (chunk) => { if (ws.readyState === WebSocket.OPEN) { ws.send(chunk); } }); // Clean up when client disconnects ws.on('close', async () => { console.log('Client disconnected, cleaning up container'); if (container) { await container.stop().catch(() => {}); await container.remove().catch(() => {}); } }); } catch (err) { console.error('Server error:', err); ws.send(`Error: ${err.message}`); ws.close(); } }); console.log('WebSocket server running at ws://localhost:8080');
3. Update Your xterm.js Frontend
Modify your frontend code to connect to the WebSocket server and attach it to xterm.js:
import { Terminal } from 'xterm'; import { AttachAddon } from 'xterm-addon-attach'; import 'xterm/css/xterm.css'; // Initialize xterm.js const term = new Terminal({ rows: 24, cols: 80, cursorBlink: true }); term.open(document.getElementById('terminal-container')); // Connect to WebSocket server const ws = new WebSocket('ws://localhost:8080'); ws.onopen = () => { console.log('Connected to WebSocket server'); // Attach WebSocket to xterm.js const attachAddon = new AttachAddon(ws); term.loadAddon(attachAddon); // Send resize events to the server term.onResize((size) => { ws.send(JSON.stringify({ type: 'resize', rows: size.rows, cols: size.cols })); }); }; ws.onerror = (error) => { console.error('WebSocket error:', error); term.write('Failed to connect to the shell server.\r\n'); };
4. Key Notes & Troubleshooting
- Docker Socket Permissions: On Linux, your Node.js process needs permission to access
/var/run/docker.sock. Add your user to thedockergroup withsudo usermod -aG docker $USER(log out and back in for changes to take effect). - Cross-Origin Issues: If your frontend is served from a different domain than the WebSocket server, configure CORS or use a reverse proxy like Nginx to serve both from the same domain.
- Security: Never expose this service publicly without adding authentication (e.g., JWT tokens). Unauthorized users could take control of your Docker environment.
- Image Compatibility: Ensure your Docker image has a shell installed (Ubuntu has bash by default; Alpine uses sh, which works too but has fewer features).
内容的提问来源于stack exchange,提问作者Will
相关产品推荐
相关产品推荐

