云端IDE系列教程7:解决 WeTTY 在 Ubuntu 非 root 用户不能运行的问题

news2025/3/13 6:54:20

原文作者:行云创新技术总监 邓冰寒

概述

上一期在使用官方容器镜像快速成功地在 TitanIDE 运行起来了 WeTTY,但是不适合开发人员使用,而我自己编译构建出来的容器镜像无法直接运行指定的应用(/bin/bash 或 /bin/zsh),本来在 WeTTY的开源项目下面提了 issue,但貌似没有得到响应,如果能在一个小时左右快速解决问题,折腾 WeTTY 这个开源软件对我来说是有价值的。

折腾经历

在经过权衡利弊(内心挣扎)之后,为了给开发者提供最好的使用体验,我决定花一个小时左右的时间在 TitanIDE 上看看是什么原因导致 WeTTY 不理会我传递的参数 --command。

首先在 TitanIDE 创建一个 NodeJS 的项目,这里我采用了 VS Code for Node.js 作为 WeTTY 的开发环境。

体验TitanIDE>>www.titanide.cn

在这里插入图片描述

创建好项目之后,紧接着就是来执行使用开源项目的三把斧,fork,clone,run。
在这里插入图片描述

分析问题

如上一期文章所提到的,既然–command在官方提供的镜像可以正常工作的,没有理由我自己运行的不行,肯定是有环境的差异性。经过一番顺藤摸瓜式的盘查,在我预期的时间内找到了问题的根本原因。

首先,WeTTY 在启动的时候会先获取 cmmand,如下代码所示:

/**
 * Starts WeTTy Server
 * @name startServer
 * @returns Promise that resolves SocketIO server
 */
export async function start(
  ssh: SSH = sshDefault,
  serverConf: Server = serverDefault,
  command: string = defaultCommand,
  forcessh: boolean = forceSSHDefault,
  ssl: SSL | undefined = undefined,
): Promise<SocketIO.Server> {
    
// 此处省略部分代码以聚集问题
// ...
  
    logger.info('Connection accepted.');
    // WeTTY 在启动的时候会先获取 cmmand
    const [args, sshUser] = getCommand(socket, ssh, command, forcessh);
    const cmd = args.join(' ');
    logger.debug('Command Generated', { user: sshUser, cmd });
    wettyConnections.inc();
    
// 此处省略部分代码以聚集问题
// ...

  return io;
}

在 getCommand() 函数内又调用了 localhost() 函数来决定是否使用 --command 参数:

export function getCommand(...): [string[], boolean] {
    
  // 在获取 command 的时候去判断了所否所 localhost 的 IP
  const sshAddress = address(headers, user, host);
  if (!forcessh && localhost(host)) {
    return [loginOptions(command, remoteAddress), true];
  }
  
   // 此处省略部分代码以聚集问题
   // ...
    
  return [
    sshOptions(args, key),
    user !== '' || user.includes('@') || sshAddress.includes('@'),
  ];
}

localhost 的判断函数又有一行代码判断 uid 是否为 0, 即 root 用户才能在 localhost 启动自定义命令,这逻辑很怪,不知道设计者是怎么考虑的. TitanIDE 使用了 Ubuntu 作为操作系统,默认用户为非 root,因此在 Ubuntu 没法正常运行。

const localhost = (host: string): boolean =>
  process.getuid() === 0 &&
  (host === 'localhost' || host === '0.0.0.0' || host === '127.0.0.1');

解决问题

经过以上分析,问题已经得到解决,我传递的参数 --command /bin/zsh 已经起作用了,以下命令是完整的命令行:

yarn start --port=8080 --base / --command /bin/zsh

如下图所示,我将 process.getuid() === 0 这行代码去掉后,WeTTY 运行一切正常:
在这里插入图片描述

制作镜像

和上期所介绍的一样,我们也是在 TitanIDE 使用 TepmlateMaker 创建一个 WeTTY 的项目,用于制作 WeTTY for Ubuntu 的模板镜像。
在这里插入图片描述

创建好 template-wetty 之后,先编辑 WeTTY 的启动脚本以在 Dockerfile 备用:


#!/bin/zsh
# WeTTY 默认使用固定的 BASE (/wetty),需要修改成 TitanIDE 的命名规则
BASE="/ide/${POD_NAMESPACE}/${PROJECT_NAME}/"

# 启动 WeTTY
/usr/local/nodejs/current/bin/node ${WETTY_HOME} --port ${TERMINAL_SERVER_PORT} --base ${BASE} --command /bin/zsh

然后编辑 Dockerfile 以实现 WeTTY 在 Ubuntu 下面作为一个开发环境。这里使用了 TitanIDE 的基础镜像 template-core:v20230119-1cfbd2e,该基础镜像基于 Ubuntu 20.04,包含了开发者常用的工具,如 kubectl, helm,git 等 。以下是完整的 Dockerfile:


# TitanIDE Template
# 基础镜像的镜像仓库,根据用户实际需要修改入参,默认情况下保存不变
ARG from_hub
FROM ${from_hub}template-core:v20230119-1cfbd2e

# 指定 WeTTY 监听的端口号
ENV TERMINAL_SERVER_PORT=30000 \
    WETTY_HOME=/usr/src/app \
    NODE_HOME=/usr/local/nodejs/current \
    TMPDIR=/tmp/pkgs
# TitanIDE 的模板图标通过 icon 入参来指定,
ARG icon

# 镜像名字,默认取当前项目名称,如果用户没有修改的情况下,以本文创建的项目名称为例是 template-wetty,最终生成的镜像是 registry/ns/template-wetty:tag,例如
# 当然也可在构建镜像的时候由入参来覆盖,如 make app_name=foo, 则构镜像名称为 registry/ns/foo:tag
ARG app_name

# 镜像的版本(tag),如 titan.hub:5000/demo/template-wetty:v20230129-6a9aa54 中的 v20230129-6a9aa54 是有 TitanIDE 自动生成,用户也可以通过参数指定,如 make app_version=v0.1
ARG app_version

# 模板的图标,在 ARG icon 部分已经解释过
LABEL metadata.icon="${icon}"


# 镜像名称,在 ARG app_name 部分已经解释过
LABEL metadata.appname="${app_name}"

# 镜像版本,在 ARG app_version 部分已经解释过
LABEL metadata.version="${app_version} "

# 应用的类型,WeTTY 是 Web 原生应用,所以需要指定为 webapp
LABEL metadata.type="webapp"

# 右侧工具栏定义,以下选择了 file,port 两个工具,表示 WeTTY 启动后再右侧栏会有这两个工具
LABEL devtools="file,port"

# WeTTY 监听的端口号,TitanIDE 会根据这个端口号来创建访问的链接
LABEL metadata.port=${TERMINAL_SERVER_PORT}

# 告诉 TitanIDE,WeTTY 服务的访问路径 (URL base) 需要重写
LABEL metadata.rewritesubpath="true"

# 先切换为 root 方便安装软件
USER root

# 复制启动命令脚本到 /usr/bin 下
COPY ./bin /usr/bin

# github.com 的代理服务
ARG GH_PROXY

# 安装软件
RUN mkdir -p ${PYTHON_VENV} ${WETTY_HOME} $USER_HOME/.local/share /usr/local/nodejs $USER_HOME/.config \
    && apt update \
    # 安装常用的工具
    && apt install -y libwebsockets-dev coreutils openssh-client sshpass \
    # 获取并安装 nodejs
    && wget -P ${TMPDIR} https://nodejs.org/dist/v18.13.0/node-v18.13.0-linux-x64.tar.gz \
    && tar -xvzf ${TMPDIR}/node-v18.13.0-linux-x64.tar.gz -C /usr/local/nodejs/ \
    && ln -s /usr/local/nodejs/node-v18.13.0-linux-x64 ${NODE_HOME} \
    && ln -s ${NODE_HOME}/node /usr/bin/node \
    && ln -s ${NODE_HOME}/npm /usr/bin/npm \
     # 设置路径环境变量
    && echo "export PATH=${USER_HOME}/.local/bin:${NODE_HOME}/bin:$PATH" >> ${USER_HOME}/.zshrc \
    && export PATH=${NODE_HOME}/bin:$PATH \
    # 设置为国内的 npm 仓库镜像
    && npm config set registry https://registry.npm.taobao.org \
    && npm install -g npm@9.3.1 yarn cnpm snowpack \
    && ${NODE_HOME}/bin/yarn config set registry https://registry.npm.taobao.org \
    # 克隆 WeTTY 源码,下面是我修改后的版本
    && git clone --branch dev --depth 1 ${GH_PROXY}https://github.com/john-deng/wetty.git ${TMPDIR}/wetty \
    && cd ${TMPDIR}/wetty \
    # 编译 WeTTY 源码
    && yarn install \
    && yarn build \
    # 将源码复制到最终部署的文件夹
    && cp -r build node_modules package.json ${WETTY_HOME} \
    # 清理不再需要的源文件(当然,也可以采用独立的融容器来构建)
    && rm -rf ${TMPDIR}/wetty \
    && chown -R ide:ide /tmp $USER_HOME \
    # 过滤掉 WeTTY 的端口在 TitanIDE 工作区内应用端口列表的展示
    && echo "${WETTY_HOME}" > /etc/port-filters

# 切换为普通用户
USER ide

# 切换工作路径
WORKDIR $USER_HOME/workspace
EXPOSE ${TERMINAL_SERVER_PORT}

# 最后 TitanIDE 要求运行 finalize.sh 完成 HOME 目录下的安装文件备份
ARG app_version
RUN /finalize.sh

在 TitanIDE 的工作区直接执行 make 命令构建出 WeTTY 的模版镜像:
在这里插入图片描述


template-wetty git:(wetty) make 
2023-02-03T15:37:36 info workdir: /home/ide/workspace/template-wetty
2023-02-03T15:37:36 info Already contains credential for user admin in titan.hub:5000, if you want to update it, pleas input with force argument, e.g. make login force=true 
2023-02-03T15:37:36 info Building container image on TitanIDE ...

...

INFO[0514] Taking snapshot of full filesystem...        
INFO[0520] Pushing image to registry.cn-shenzhen.aliyuncs.com/titanide/template-wetty:v20230131-1b25d1b 
INFO[0558] Pushed registry.cn-shenzhen.aliyuncs.com/titanide/template-wetty@sha256:92cdbefce71f63644af1074ec793eecb21fd1b0a9f031fdc00b876fd2d571ec2 
2023-02-03T15:31:44 info build phase: Succeeded
2023-02-03T15:31:44 info pushed titan.hub:5000/demo/template-wetty:v20230131-1b25d1b

验证效果

然后使用 WeTTY 模板镜像 titan.hub:5000/demo/template-wetty:v20230131-1b25d1b 创建 WeTTY 模版:

在这里插入图片描述
在这里插入图片描述

最后,使用 WeTTY 模版来创建 WeTTY 项目。验证最终效果,如下图所示。

在这里插入图片描述

在云端使用 Vim,真香!
在这里插入图片描述

总结

在选用如 WeTTY 这样的开源软件过程中,和使用其他开源软件一样,并不是无偿完全无偿使用的,为了价值的最大化,这就需要像 TitanIDE 这样的开发环境,在 TitanIDE 上面折腾的这些开源软件有个好处就是,我不需要为了某个开源软件去准备开发环境,因为 TitanIDE 提供了我要的开发环境模板,开箱即用。在这个基础之上,我们又可以创建属于自己的模版,本文展示了如何在 TitanIDE 快速解决开源软件的问题,然后定制一个 WeTTY 云端 Terminal 作为开发环境的完整过程,希望能帮到有需要的开发者。

本文是系列文章《在 TitanIDE 玩转云原生 Terminal 系列》,我将会在下一篇继续就这个话题展开探索,敬请期待!

体验TitanIDE>>www.titanide.cn

最后,感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/378559.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HBase基础知识

1、HBase特点 1&#xff09;海量存储 Hbase适合存储PB级别的海量数据&#xff0c;在PB级别的数据以及采用廉价PC存储的情况下&#xff0c;能在几十到百毫秒内返回数据。这与Hbase的极易扩展性息息相关。正式因为Hbase良好的扩展性&#xff0c;才为海量数据的存储提供了便利。…

收下这份十万商家称赞的开店攻略,带你发家致富!

理想与现实之间的距离&#xff0c;大概就是开店吧&#xff01;总觉得自己投点钱&#xff0c;一两年回本&#xff0c;后面每月轻松赚几万、几十万&#xff1b;结果却发现房租太贵、人工太贵、自己什么都不懂&#xff0c;然后随波逐流的没有特色。其实&#xff0c;细心的朋友会发…

经常打电话的人用什么蓝牙耳机好?通话功能比较好的蓝牙耳机

无线耳机市场发生了翻天覆地的变化&#xff0c;开始越来越频繁地出现不一样的功能。现在&#xff0c;选择不于 Apple&#xff0c;还包括一大堆可用的耳机&#xff0c;下面就来看看以下通话功能好的蓝牙耳机。 第一款&#xff1a;南卡小音舱蓝牙耳机 售价&#xff1a;299元 推…

【数电基础】——逻辑代数运算

目录 1.概念 1.基本逻辑概念 2.基本逻辑电路&#xff08;与或非&#xff09; 逻辑与运算 与门电路&#xff1a; 逻辑或运算 或门电路&#xff1a; ​逻辑非运算&#xff08;逻辑反&#xff09; 非门电路​编辑 3.复合逻辑电路&#xff08;运算&#xff09; 与非逻辑…

初探推荐系统-01

文章目录一、什么是推荐系统是什么为什么长尾理论怎么做二、相似度算法杰卡德相似系数余弦相似度三、基于内容的推荐算法如何获取到用户喜欢的物品如何确定物品的特征四、推荐算法实验方法评测指标推荐效果实验方法1、离线实验2、用户调查3、在线实验评测指标1、预测准确度评分…

【组织架构】中国国家铁路集团有限公司

1 公司简介 中国国家铁路集团有限公司&#xff08;简称“中国铁路”&#xff09;是经国务院批准、依据《中华人民共和国公司法》设立、由中央管理的国有独资公司。经国务院批准&#xff0c;公司为国家授权投资机构和国家控股公司。公司注册资本为17395亿元&#xff0c;由财政部…

CAD拉伸后标注尺寸不变?快来看看是不是这个原因!

CAD拉CAD拉伸后标注尺寸不变&#xff1f;快来看看是不是这个原因&#xff01;CAD拉命令作为常用的图形编辑命令之一&#xff0c;有些设计师在使用过程中发现&#xff0c;CAD拉伸后标注尺寸没有变化&#xff0c;这是什么情况&#xff1f;本节课程小编就以浩辰CAD软件为例来给大家…

心系区域发展,高德用一体化出行服务平台“聚”力区域未来

交通&#xff0c;是城市的血脉。通过对人、资源、产业的连接&#xff0c;交通建设往往是城市和区域经济发展的前提。不过&#xff0c;在度过了“要想富&#xff0c;先修路”的初级建设阶段后&#xff0c;交通产业内部也出现了挑战&#xff0c;诸如城市秩序、发展成本、用户使用…

《爆肝整理》保姆级系列教程python接口自动化(二十五)--unittest断言——下(详解)

简介 本篇还是回归到我们最初始的话题&#xff0c;想必大家都忘记了&#xff0c;没关系看这里&#xff1a; 没错最初的话题就是登录&#xff0c;由于博客园的登录机制改变了&#xff0c;本篇以我找到的开源免费的登录API为案例&#xff0c;结合 unittest 框架写 2 个用例。同样…

想学计算机,应该学什么专业?

我们在考虑想学计算机&#xff0c;应该学什么专业&#xff1f;这个问题的时候&#xff0c;每个人都应该结合自己的兴趣来确定。有的喜欢编程、有的喜欢设计、有的喜欢做产品跟人打交道……自己有兴趣再加上自己的努力&#xff0c;掌握好专业技能&#xff0c;就一定能进入高薪的…

【Spark分布式内存计算框架——Spark Streaming】7. Kafka集成方式

集成方式 Spark Streaming与Kafka集成&#xff0c;有两套API&#xff0c;原因在于Kafka Consumer API有两套&#xff0c; 文档&#xff1a;http://spark.apache.org/docs/2.4.5/streaming-kafka-integration.html。 方式一&#xff1a;Kafka 0.8.x版本 老的Old Kafka Consum…

MyBatis学习笔记(八) —— 字段名和属性不一致的情况下,如何处理映射关系

EmpMapper.java /** * 根据id查询员工信息 * param empId * return */ Emp getEmpByEmpId(Param("empId") Integer empId);EmpMapper.xml <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//D…

Android仿QQ未读消息拖拽粘性效果

效果图原理分析首先是在指定某个位置画一个圆出来&#xff0c;手指按到这个圆的时候再绘制一个可以根据手指位置移动的圆&#xff0c;随着手指的移动两个圆逐渐分离&#xff0c;分离的过程中两圆中间出现连接带&#xff0c;随着两圆圆心距的增大&#xff0c;半径也是根据某一比…

LeetCode经典例题|134. 加油站|运用坐标系数学思维一步解决

134. 加油站 这道题刚看很容易就想到了暴力或者回溯剪枝。 这是一个有增有减的过程&#xff0c;就好像坐标系上的一个个点&#xff0c;连在一起形成一条上下起伏的折线。 1. 做坐标轴 比如 gas [1,2,3,4,5], cost [3,4,5,1,2] 从0号汽车站开始出发&#xff0c;一直到回到起…

计算机网络(第三版) 胡亮 课后习题第四章答案

计算机网络&#xff08;第三版&#xff09; 胡亮 课后习题第四章答案 1、数据链路层的任务和功能是什么&#xff1f; 数据链路层的任务是提供两个相邻的网络节点或主机及其相连的网络节点之间的可靠通信。 数据链路层的主要服务功能是线路规程、差错控制和流量控制。 2、什么是…

第四阶段08-基于element-ui的vue2.0脚手架(续)

42. VUE脚手架项目嵌套路由 在配置路由&#xff08;配置/src/router/index.js&#xff09;时&#xff0c;如果配置的路由对象是routes常量的直接数组元素&#xff0c;则此路由配置的视图会显示在App.vue的<router-view/>中。 在设计视图时&#xff0c;可能会出现<ro…

深度学习基础实例与总结

一、神经网络 1 深度学习 1 什么是深度学习&#xff1f; 简单来说&#xff0c;深度学习就是一种包括多个隐含层 (越多即为越深)的多层感知机。它通过组合低层特征&#xff0c;形成更为抽象的高层表示&#xff0c;用以描述被识别对象的高级属性类别或特征。 能自生成数据的中…

Spring常见面试题汇总(超详细回答)

1.什么是Spring框架&#xff1f;Spring框架是一个开源的Java应用程序开发框架&#xff0c;它提供了很多工具和功能&#xff0c;可以帮助开发者更快地构建企业级应用程序。通过使用Spring框架&#xff0c;开发者可以更加轻松地开发Java应用程序&#xff0c;并且可以更加灵活地组…

C++学习笔记

C学习笔记 学习视频资料 随手记 vector是一个能够存放任意类型的动态数组 unordered_set 不能放重复元素的容器 emplace放入 std::cin.get(); //使控制器不会立马关闭&#xff0c;保持窗口打开 总是通过 const引用传递对象 默认 动态链接 static 静态链接&#xff08;只在当…

WebRTC Qos策略

1.WebRTC 用于提升 QoS 的方法&#xff1a;NACK、FEC、SVC、JitterBuffer、IDR Request、PACER、Sender Side BWE、VFR&#xff08;动态帧率调整策略&#xff09;https://blog.csdn.net/CrystalShaw/article/details/80432267丢包重传NACK&#xff1a;一种通知技术&#xff0c;…