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

news/2024/4/23 23:18:52/文章来源:https://blog.csdn.net/weixin_44592002/article/details/129261599

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

概述

上一期在使用官方容器镜像快速成功地在 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 在启动的时候会先获取 cmmandconst [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 的 IPconst 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.luyixian.cn/news_show_75514.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GO中sync 包的 RWMutex 读写互斥锁

文章目录背景RWMutex 简介代码验证多个协程请求读锁 RLock() 和 RLock()读写交错 RLock() 和 Lock()写入的时候读取读取的时候写入请求多个写Lock() 和 Lock()背景 Mutex 互斥锁是严格锁定读和写&#xff0c;如果我们需要单独对读或者写添加锁需要使用 sync包的RWMutex 针对读…

PHP使用chilkat入门教程

前言&#xff1a; 我们需要先确认自己的版本&#xff0c;在PHP中&#xff0c;可以利用phpinfo()函数来查看php是ts版本还是nts版本&#xff0c;该方法可以展示出当前phpinfo信息&#xff0c;若“Thread Safety”项的信息是“enabled”&#xff0c;一般来说就表示ts版本&#xf…

备战英语6级——记录复习进度

开始记录—— 学习&#xff1a;如何记录笔记&#xff1f; 1&#xff1a;首先我认为&#xff1a;电脑打字比较适合我&#xff01; 2&#xff1a;先记笔记&#xff0c;再“填笔记”&#xff01; 记笔记就是一个框架&#xff0c;记录一个大概的东西。后面需要在笔记中&#xff0…

实例10:四足机器人运动学逆解可视化与实践

实例10&#xff1a; 四足机器人运动学逆解单腿可视化 实验目的 了解逆运动学的有无解、有无多解情况。了解运动学逆解的求解。熟悉逆运动学中求解的几何法和代数法。熟悉单腿舵机的简单校准。掌握可视化逆向运动学计算结果的方法。 实验要求 拼装一条mini pupper的腿部。运…

Qt 事件机制

【1】事件 事件是可以被控件识别的操作。如按下确定按钮、选择某个单选按钮或复选框。 每种控件有自己可识别的事件&#xff0c;如窗体的加载、单击、双击等事件&#xff0c;编辑框&#xff08;文本框&#xff09;的文本改变事件等等。 事件就是用户对窗口上各种组件的操作。…

LibAFL的安装及基本使用

本教程安装LibAFL使用的是Ubuntu 22.04 操作系统 1. 安装 1.1 Rust 安装 Rust的安装&#xff0c;参照Rust官网&#xff1a;https://www.rust-lang.org/tools/install curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh1.2 LLVM安装 直接apt安装&#xff0c;安…

表格形式的Sarsa与Q_learning算法

环境如下&#xff1a; 这是一个简单的环境&#xff0c;绿色方块代表终点&#xff0c;白色方块代表可行点&#xff0c;灰色方块代表陷阱 用Sarsa算法和Q_learning算法训练得到value表格 代码如下&#xff1a; (jupyter notebook上的代码&#xff0c;所以顺序看起来有点儿奇怪) …

Java8 新特性强大的Stream API

一、Stream API 说明 Java8中有两大最为重要的改变。第一个是 Lambda 表达式&#xff1b;另外一个则是 Stream API。 Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充&#xff0c;因为Stream API可以极大提供Ja…

【博学谷学习记录】超强总结,用心分享丨人工智能 特征工程 特征变换 分箱学习总结

目录概念分箱的作用等频分箱等距分箱*卡方分箱公式例子概念 特征构造的过程中&#xff0c;对特征做分箱处理时必不可少的过程分箱就是将连续变量离散化&#xff0c;合并成较少的状态 分箱的作用 离散特征的增加和减少都很容易&#xff0c;易于模型的快速迭代&#xff1b;稀疏…

基于自定义训练函数的BP神经网络回归分析

目录 背影 BP神经网络的原理 BP神经网络的定义 BP神经网络的神经元 BP神经网络的激活函数 BP神经网络的传递函数 基于自定义训练函数的BP神经网络回归分析 背影 BP神经网络是一种成熟的神经网络&#xff0c;拥有很多训练函数&#xff0c;传递函数&#xff0c;激活函数&#x…

数学小课堂:无穷小(用动态和极限的眼光看世界)

文章目录 引言I 极限1.1 柯西对极限的认知1.2 极限准确的定义1.3 数列极限的定义1.4 函数极限的定义1.5 无穷小(特殊的极限)1.6 定量和逆向思维1.7 认知升级的过程引言 身处于渐变世界的人类,难以理解瞬间突变。 老师的作用,就是用大白话,把数学语言所写的知识,翻译成大…

腾讯安全与锐捷网络战略合作,威胁情报能力“被集成”

2月28日&#xff0c;腾讯安全和锐捷网络在北京联合举办“威胁情报”战略合作发布会。双方发布了一款集成了腾讯安全威胁情报的新一代防火墙&#xff0c;并举办战略合作签约仪式。会上&#xff0c;锐捷网络安全产品事业部总经理项小升、腾讯安全总经理陈龙代表双方签署战略合作协…

Python可视化界面编程入门

Python可视化界面编程入门具体实现代码如所示&#xff1a; &#xff08;1&#xff09;普通可视化界面编程代码入门&#xff1a; import sys from PyQt5.QtWidgets import QWidget,QApplication #导入两个类来进行程序界面编程if __name__"__main__":#创建一个Appl…

k8s学习之路 | Day17 k8s 工作负载

文章目录工作负载的定义工作负载资源分类工作负载的定义 官方参考链接&#xff1a;https://kubernetes.io/docs/concepts/workloads/ A workload is an application running on Kubernetes. Whether your workload is a single component or several that work together, on K…

《C++ Primer Plus》(第6版)第6章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第6章编程练习《C Primer Plus》&#xff08;第6版&#xff09;第6章编程练习1. 大小写转换2. 平均值3. 菜单4. 成员5. 收入所得税6. 捐款7. 统计单词8. 统计文件字符数9. 重写编程练习6《C Primer Plus》&#xff08;第6版&…

创建对象的方式和对属性的操作

javaScript支持多种编程范式&#xff0c;包括函数式编程和面向对象编程&#xff0c;javaScript的对象被设计成一组属性的无序集合&#xff0c;由key和value组成。 创建对象的两种方式 早期使用创建对象方式最多的是使用Object类&#xff0c;使用new关键字来创建一个对象&…

docker-compose安装kafka和php简单测试

docker-compose.yml内容&#xff1a; version: 3.1 services: zookeeper: container_name: zookeeper image: zookeeper:3.6 ports: - 2181:2181 kafka: image: wurstmeister/kafka container_name: kafka depends_on: - zookeeper …

解决Spring Data Jpa 实体类自动创建数据库表失败问题

先说一下我遇到的这个问题&#xff0c;首先我是通过maven创建了一个spring boot的工程&#xff0c;引入了Spring data jpa&#xff0c;结果实体类创建好之后&#xff0c;运行工程却没有在数据库中自动创建数据表。 找了半天发现是一个配置的问题! hibernate.ddl-auto节点的配…

【Python实战】激情澎湃,2023极品劲爆舞曲震撼全场,爬虫一键采集DJ大串烧,一曲醉人女声DJ舞曲,人人都听醉~(排行榜采集,妙啊~)

导语 哈喽&#xff01;大家好。我是木木子吖~今天给大家带来爬虫的内容哈。 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 今天教大家Python爬虫实战一键采集大家喜欢的DJ舞曲哦&#xff01; …

01-Oracle入门基础知识讲解

本章内容主要是讲解Oracle基础知识&#xff0c;安装完Oracle后第一次使用所必须了解的一些常用软件及命令&#xff0c;Oracle的体系结构等知识。 一、进入SQL Plus客户端软件 1.进入SQLPLUS客户端windows界面 2.进入DOS窗口界面 普通用户登录&#xff1a;conn 用户名称/密码 …