云vscode搭建之使用容器化部署的方法

目录
  • 1、Code-Server下载部署
    • 1.1 Code-Server下载
    • 1.2 Code-Server部署
    • 1.3 Docker部署Code-Server
  • 2. 一个小问题
    • 2.1 端口监听
    • 2.2 使用VS-Code插件
    • 2.3 代理服务器实现

Vscode作为一个轻量级的IDE,其支持丰富的插件,而通过这些插件我们就可以实现在Vscode中写任何语言的代码。Code-Server是Vscode的网页版,启动Code-Server之后我们就可以在浏览器中打开vscode来愉快的编写代码了。这种方式非常适合我们做linux编程。使用C/C++的时候,在windows上编写的代码在linux上可能跑不了,而且安装linux图形界面,然后在图像界面中使用vscode又很麻烦。当然也可以使用vscode的远程开发。但是我认为启动code-server来在浏览器上使用vscode也是非常方便的。

随着容器化的发展,现在涌现出了很多云IDE,比如腾讯的Cloud Studio,但是其也是基于Code-Server进行开发部署的,用了它的云IDE后,我便产生出了自己部署一个这样的云IDE的想法。

1、Code-Server下载部署

1.1 Code-Server下载

下载地址:https://github.com/coder/code-server/releases/

在上面的网址中下载code-server,并将其传输到linux服务器上。

也可以在linux服务器中直接使用命令来下载:

wget https://github.com/coder/code-server/releases/download/v4.6.1/code-server-4.6.1-linux-amd64.tar.gz

1.2 Code-Server部署

1.解压tar.gz文件:

 tar -zxvf code-server-4.6.1-linux-amd64.tar.gz

2.进入code-server目录:

cd code-server-4.6.1-linux-amd64

3.设置密码到环境变量中

export PASSWORD="xxxx"

4.启动code-server

./code-server --port 8888 --host 0.0.0.0 --auth password

在浏览器中输入ip:8888来访问如下:

后台运行的方式:

nohup ./code-server --port 8888 --host 0.0.0.0 --auth password &

1.3 Docker部署Code-Server

接下来将介绍使用Docker的方式来部署Code-Server:

下面的Dockerfile创建了一个带有Golang开发环境的容器,然后在容器中运行Code-Server,将Dockerfile放在跟code-server-4.4.0-linux-amd64.tar.gz同目录。

1.创建名为Dockerfile的文件(Dockerfile要跟code-server的tar.gz文件在同一目录中),将下面内容复制进去

FROM golang

WORKDIR /workspace

RUN cp /usr/local/go/bin/* /usr/local/bin

COPY code-server-4.4.0-linux-amd64.tar.gz .
RUN tar zxvf code-server-4.4.0-linux-amd64.tar.gz

ENV GO111MODULE on
ENV GOPROXY https://goproxy.cn

ENV PASSWORD abc123

WORKDIR /workspace/code-server-4.4.0-linux-amd64
EXPOSE 9999
CMD ["./code-server", "--port", "9999", "--host", "0.0.0.0", "--auth", "password"]

2.然后执行命令构建Docker镜像:

docker build -t code-server .

3.运行容器

docker run -d --name code-server -p 9999:9999 code-server

2. 一个小问题

下面的内容针对Docker部署的Code-Server。

想象这样一个场景,我们开发了一个类似Cloud Studio的云IDE,每启动一个工作空间我们就通过Docker或者Kubernetes来创建一个容器,然后在容器中部署一个Code-Server,最后通过将端口暴露出去给用户使用。

云IDE使用起来很方便,打开和销毁的很迅速,即开即用。用户使用Golang在云IDE中写了一个http服务器,想要在他电脑的浏览器上访问,却发现访问不了。那么为什么Code-Server的端口就可以访问,其它端口无法访问呢。因为我们在启动容器的时候就已经预想到要访问这个端口,然后将端口暴露出去了,也就是建立了端口映射。在容器中新启动的端口并没有建立映射,因此只能在服务器内部访问,而不能在用户电脑上访问。

那么如何让用户也可以访问到呢,我们可以在主机上部署一个代理服务器,用户访问这个代理服务器,然后转发请求到容器中,再将响应转发给用户。

那么如何发现用户启动服务器监听了哪个端口呢,首先我能想到的就是启动一个程序每隔一段时间查询一下是否有新的端口被监听。获取端口信息可以使用netstat命令或者lsof命令,在此我选择了netstat,就有了下面的一个程序:

2.1 端口监听

这个程序会每隔一秒获取一下有哪些端口处于LISTEN状态,然后对比上一次的状态,看是否有新的端口被监听。当我们监听了新的80端口后,就会输出:Find new port: 80

package main

import (
	"bytes"
	"fmt"
	"os/exec"
	"strconv"
	"time"
)

func main() {
	listener := NewPortListener()
	pc := listener.GetPortChan()
	go listener.FindNewPortLoop()
	for {
		port := <-pc
		fmt.Println("Find new port:", port)
	}
}

type PortListener struct {
	portChan chan uint16
}

func NewPortListener() *PortListener {
	return &PortListener{
		portChan: make(chan uint16, 1),
	}
}

func (p *PortListener) GetPortChan() <-chan uint16 {
	return p.portChan
}

func (p *PortListener) FindNewPortLoop() {
	ports := p.getListeningPorts()          // 程序启动后先获取一次处于Listen状态的端口
	set := map[uint16]struct{}{}
	for _, port := range ports {
		set[port] = struct{}{}
	}

	for {                                   // 然后每隔一秒获取一次,并与前一次的信息进行对比,查找是否启动了新的端口
		tmpSet := map[uint16]struct{}{}
		ports = p.getListeningPorts()
		for _, port := range ports {
			if _, ok := set[port]; !ok {
				p.portChan <- port
			}
			tmpSet[port] = struct{}{}
		}
		set = tmpSet
		time.Sleep(time.Second * 3)
	}
}

func (p *PortListener) getListeningPorts() []uint16 {
	cmd := exec.Command("netstat", "-ntlp")          // 运行netstat命令获取处于Listen状态的端口信息
	res, err := cmd.CombinedOutput()                 // 获取结果
	fmt.Println(string(res))
	if err != nil {
		fmt.Println("Execute netstat failed")
		return nil
	}

	return p.parsePort(res)                         // 对结果进行解析
}

func (p *PortListener) parsePort(msg []byte) []uint16 {       // 解析出处于LISTEN状态的端口,只要端口号
	idx := bytes.Index(msg, []byte("tcp"))
	colums := bytes.Split(msg[idx:], []byte("\n"))
	res := make([]uint16, 0, len(colums)-1)
	for i := 0; i < len(colums)-1; i++ {
		item := p.findThirdItem(colums[i])
		if item != nil {
			m := bytes.IndexByte(item, ':') + 1
			for item[m] == ':' {
				m++
			}
			p, err := strconv.Atoi(string(item[m:]))
			if err == nil {
				res = append(res, uint16(p))
			} else {
				fmt.Println(err)
			}
		}
	}

	return res
}

func (p *PortListener) findThirdItem(colum []byte) []byte {
	count := 0
	for i := 0; i < len(colum); {
		if colum[i] == ' ' {
			for colum[i] == ' ' {
				i++
			}
			count++
			continue
		}
		if count == 3 {
			start := i
			for colum[i] != ' ' {
				i++
			}
			return colum[start:i]
		}
		i++
	}

	return nil
}

2.2 使用VS-Code插件

但是上面的程序也无法通知到用户,在使用Cloud Studio的时候,启动了新的端口,这个云IDE就会提醒发现了新的端口,是否要在浏览器中访问。因此我就想到了实现这样一个插件,因此下面部分就是实现一个vscode的插件来发现是否有新的端口被监听了,然后提醒用户是否在浏览器中访问。

下面只是简单介绍,想要了解vscode插件的详细开发过程的自行搜索。

1.首先安装yeoman脚手架工具,以及官方提供的脚手架工具:

npm install -g yo generator-code

2.创建项目,选择要创建的项目以及其它信息

yo code

3.创建完成后,就可以编写插件了

// extension.js
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require('vscode');

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed

/**
 * @param {vscode.ExtensionContext} context
 */
function activate(context) {

	// Use the console to output diagnostic information (console.log) and errors (console.error)
	// This line of code will only be executed once when your extension is activated

	// The command has been defined in the package.json file
	// Now provide the implementation of the command with  registerCommand
	// The commandId parameter must match the command field in package.json
	let disposable = vscode.commands.registerCommand('port-finder.helloWorld', function () {
		// The code you place here will be executed every time your command is executed

		// Display a message box to the user
		vscode.window.showInformationMessage('Hello World from port_finder!');
	});

	context.subscriptions.push(disposable);

	initGetPorts()
}

var s = new Set() 

function initGetPorts() {
    getListeningPorts(function(ports) {
        ports.forEach(p => {
            s.add(p)
        })

        setInterval(function() {        // 设置定时器,每隔一秒获取一次
            listenPortChange()
        }, 1000)
    })
}

function listenPortChange() {
    // 获取处于LISTEN状态的端口
    getListeningPorts(function(ports) {
        var tmpSet = new Set()
        ports.forEach(p => {
            if (!s.has(p)) {
                // 发现新的端口被监听就提醒用户是否在浏览器中打开
                vscode.window.showInformationMessage("发现新开启的端口:" + p + ",是否在浏览器中访问?", "是", "否", "不再提示")
                .then(result=> {
                    if (result === "是") {
                        // 在浏览器中打开来访问代理服务器,后面带上端口信息,以便代理服务器知道访问容器的哪个端口
                        vscode.env.openExternal(vscode.Uri.parse(`http://192.168.44.100/proxy/` + p))
                    }
                })
            }
            tmpSet.add(p)
        })
        s = tmpSet
    })
}

function getListeningPorts(callback) {
    var exec = require('child_process').exec;

    exec('netstat -nlt', function(error, stdout, stderr){
        if(error) {
            console.error('error: ' + error);
            return;
        }

        var ports = parsePort(stdout)
        callback(ports)
    })
}

function parsePort(msg) {
    var idx = msg.indexOf("tcp")
    msg = msg.slice(idx, msg.length)
    var colums = msg.split("\n")
    var ret = new Array()
    colums = colums.slice(0, colums.length - 1)
    colums.forEach(element => {

        var port = findPort(element)
        if (port != -1) {
            ret.push(port)
        }
    });

    return ret;
}

function findPort(colum) {
    var idx = colum.indexOf(':')
    var first = colum.slice(0, idx)
    while (colum[idx] == ':') {
        idx++
    }
    var second = colum.slice(idx, colum.length)
    var fidx = first.lastIndexOf(' ')
    var sidx = second.indexOf(' ')
    var ip = first.slice(fidx + 1, first.length)
    var port = second.slice(0, sidx)

    if (ip == "127.0.0.1") {
        return -1
    } else {
        return Number(port)
    }
}

// this method is called when your extension is deactivated
function deactivate() {}

module.exports = {
	activate,
	deactivate
}

4.然后构建项目,首先安装vsce库,再打包

npm i -g vsce
vsce package

5.打包后生成了vsix文件,将vsix文件上传到服务器,然后再拷贝到docker容器中

# docker拷贝命令
docker cp 主机文件名 容器ID或容器名:/容器内路径

然后在浏览器中的vscode中选择vsix文件来安装插件

安装完之后,我们的插件在vscode打开后就会启动,然后每隔一秒查询一个端口情况。

测试

接下来,测试一下插件:

在vscode中写了一个http服务器,然后启动这个服务器,看插件是否能发现这个端口被监听了

package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

type KK struct {
	Name          string `json:"name"`
	Prictice_time string `json:"prictice time"`
	Hobby         string `json:"hobby"`
}

func main() {
	engine := gin.Default()
	engine.GET("/", func(ctx *gin.Context) {
		ctx.JSON(http.StatusOK, &KK{
			Name:          "kunkun",
			Prictice_time: "two and a half years",
			Hobby:         "sing jump and rap",
		})
	})

	engine.Run(":8080")
}

运行http服务器:

go run main.go

可以看到,它弹出了提示,提示我们是否在浏览器中打开

但是现在在浏览器中打开是访问不了容器中的http服务器的,因为端口没有被映射到主机端口上。

2.3 代理服务器实现

在此,为了验证我的想法是否能成功,只是实现了一个简单的代理服务器,它将请求转发的容器中,然后再转发容器中服务器的响应。(因为代理服务器是直接运行在主机上的,因此可以通过容器IP+端口来访问)

代码如下:

package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
)

func main() {
	engine := gin.Default()
	engine.GET("/proxy/*all", func(ctx *gin.Context) {
		all := ctx.Param("all")                    // 获取/proxy后面的参数
		if len(all) <= 0 {
			ctx.Status(http.StatusBadRequest)
			return
		}
		all = all[1:]                             // 丢弃第一个'/'
		idx := strings.Index(all, "/")
		var url string
		if idx < 0 {                              // 只有端口
			url = fmt.Sprintf("http://172.17.0.3:%s", all)
		} else {                                 // 有端口和其它访问路径
			port := all[:idx]
			url = fmt.Sprintf("http://172.17.0.3:%s%s", port, all[idx:])
		}
		resp, err := http.Get(url)               // 访问容器中的服务器
		if err != nil {
			ctx.Status(http.StatusBadRequest)
			return
		}
		io.Copy(ctx.Writer, resp.Body)            // 转发响应
	})

	engine.Run(":80")
}

在主机服务器上运行代理服务器,不要使用容器来启动:

go build
nohup ./porxy_server &           # 后台运行

然后我们再启动浏览器vscode中的服务器看是否可以访问到:

选择"是",然后在新弹出的窗口中就可以访问到容器中的服务了:

这里实现的只是一个非常简易的版本,只是提供了一个这样的思路。如何要实现一个类似Cloud Studio的云IDE要考虑的还要更多。
最终效果如下:

到此这篇关于云vscode搭建使用容器化部署的文章就介绍到这了,更多相关云vscode搭建内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • VSCode 云同步扩展设置Settings Sync插件

    关于 Settings Sync扩展: Settings Sync可以同步你当前的VSCode配置环境,当你需要在其它的电脑工作时,您不用重头再来一遍.新机器登录一下就搞定了.再也不用折腾环境了. 大致原理:使用GitHub Gist来同步多台计算机上的设置,代码段,主题,文件图标,启动,键绑定,工作区和扩展. 环境版本: Visual Studio Code 1.36 Settings Sync 3.4.0 教程分为 初次使用 如何上传配置(#1) 已有配置 直接下载(#2) 初次使用: 在V

  • TypeScript环境搭建并且部署到VSCode的详细步骤

    目录 TypeScript环境搭建 第一步:下载淘宝镜像 第二步:下载TypeScript 部署到VSCode 先创建一个文件夹tsDemo, 修改tsconfig.json配置 可能会遇到的报错 前言:要想搭建TypeScript,那么就要有npm,要想有cnpm,那么就得有node环境.要是都有了的话,那么接下来,往下看. TypeScript环境搭建 第一步:下载淘宝镜像 先检查自己有没有成功安装npm 有npm还不行,还要下载cnpm,命令如下(直接复制粘贴): npm install

  • 云开发 VSCode 插件 Cloudbase Toolkit 的正确打开方式及应用场景分析

    什么是 Cloudbase Toolkit Tencent CloudBase Toolkit 是云开发的 VS Code(Visual Studio Code)插件.该插件可以让您更好地在本地进行云开发项目开发和代码调试,并且轻松将项目部署到云端. Cloudbase Toolkit 将项目创建.函数上传.函数更新.函数本地调试等功能集成在 VSCode 的本地调试环境中,开发者可以通过简单的点击,完成云函数的更新.上传.同步等功能. 和 Cloudbase Cli 相比,Cloudbase

  • 程序员用vscode听网易云的实现

    当真正的听到了我本人的我喜欢的歌单里的歌时,惊呆了老铁,所以我此时此刻用激动的心颤抖的手敲下这篇文章. 本人亲测,不仅能登录上自己的账号,还能看自己的歌单和今日推荐,跟手机上的那个一模一样. 好了,现在开始真正的教程. 1.先点开右边的扩展,然后在搜索框里搜索music,如下图所示,再点击安装,安装好的界面如下图所示. 2.查看你所安装的Vs Code的版本和Electron版本,点击帮助下的关于,得到的结果如下图所示. 3.我所安装的 Vs Code的版本是1.63.0,Electron的版本

  • 云vscode搭建之使用容器化部署的方法

    目录 1.Code-Server下载部署 1.1 Code-Server下载 1.2 Code-Server部署 1.3 Docker部署Code-Server 2. 一个小问题 2.1 端口监听 2.2 使用VS-Code插件 2.3 代理服务器实现 Vscode作为一个轻量级的IDE,其支持丰富的插件,而通过这些插件我们就可以实现在Vscode中写任何语言的代码.Code-Server是Vscode的网页版,启动Code-Server之后我们就可以在浏览器中打开vscode来愉快的编写代码了

  • Prometheus容器化部署的实践方案

    环境 主机名 IP地址 服务 prometheus 192.168.237.137 prometheus.grafana node-exporter 192.168.237.131 node_exporter 容器化部署prometheus 1.安装docker [root@prometheus ~]# docker version Client: Docker Engine - Community Version: 20.10.11 API version: 1.41 Go version:

  • skywalking容器化部署docker镜像构建k8s从测试到可用

    目录 前言碎语 docker镜像构建 application.yml webapp.yml setApplicationEnv.sh setWebAppEnv.sh Kubernetes中部署 文末结语 前言碎语 skywalking是个非常不错的apm产品,但是在使用过程中有个非常蛋疼的问题,在基于es的存储情况下,es的数据一有问题,就会导致整个skywalking web ui服务不可用,然后需要agent端一个服务一个服务的停用,然后服务重新部署后好,全部走一遍.这种问题同样也会存在sk

  • 手把手教你在腾讯云上搭建hadoop3.x伪集群的方法

    一.环境准备 CentOS Linux release 7.5.1804 (Core) 系统下 安装 创建文件夹 $ cd /home/centos $ mkdir software $ mkdir module 将安装包导入software文件夹 $ cd software # 然后把文件拖进去即可 这里使用的安装包是 /home/centos/software/hadoop-3.1.3.tar.gz /home/centos/software/jdk-8u212-linux-x64.tar.

  • Django Docker容器化部署之Django-Docker本地部署

    本章将在本地搭建一个容器化的 Django 项目,感受 Docker 的运作方式. 前期准备 开发环境 虽然有基于 Windows 的 Docker 版本,但各方面兼容做得都不太好(安装也麻烦些),因此建议读者在学习前,自行安装好 Linux 或 Mac 系统.当然你愿意折腾的话,在 Windows 上搞也行. 别担心,以后开发 Django 项目仍然可以在 Windows 下进行,仅仅是开发时不使用 Docker 而已. 软件安装 Docker:学习 Docker 当然要安装 Docker 软

  • Docker容器化部署Python应用过程解析

    简单应用部署 一.目录结构: └── Pythonpro #目录 └── test.py #文件 └── requirements.txt #文件 └── Dockerfile #文件 二.编写Dockerfile文件 # 基于镜像基础 FROM python:3.6.4 # 创建代码文件夹工作目录 /code RUN mkdir /code # 复制当前代码文件到容器中 /code COPY . /code # 安装所需的包 RUN pip install -r /code/requireme

  • 详解angular应用容器化部署

    Intro 我自己有做一个个人主页,虽然效果不怎么样(不懂设计的典型程序猿...),但是记录了我对于前端框架及工具的一些实践, 从开始只有一个 angularjs 制作的页面到后面加入 less 动态写css, gulp 自动化的将 less 文件编译成 css 文件以及自动化的压缩 js 和 css,到后面加入的基于 vue 和 angular 实现,主要维护的是基于 angular 的,目前 angular 的个人主页已经支持 PWA(Progressive Web Application)

  • Docker容器化部署尝试——多容器通信(node+mongoDB+nginx)

    原因是这样的 想要部署一个mocker平台,就在朋友的推荐下选择了 api-mocker 这个现成的项目 该项目分为服务端node.客户端vue.以及数据库mongoDB 在尝试直接部署的时候发现需要装一大堆的环境,node.mongo.nginx啊,特别的麻烦,之前简单的使用过docker,就在想能不能用docker免环境直接部署呢?于是就有了这次的尝试 多容器通信 该项目分为3个部分,于是就要建立3个容器(node.mongo.nginx) 那容器之间怎么实现通信呢? # 通过link指令建

  • Docker Nginx容器制作部署实现方法

    快速开始 1.查找 Docker Hub 上的 nginx 镜像 docker search nginx 2.拉取官方的Nginx镜像 docker pull nginx 3.在本地镜像列表里查到 REPOSITORY 为 nginx 的镜像 docker images nginx REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest 4bb46517cac3 4 weeks ago 133MB 4.以下命令使用 NGINX 容器内的默认的配置来启动

  • .NETCore Docker实现容器化与私有镜像仓库管理

    一.Docker介绍 Docker是用Go语言编写基于Linux操作系统的一些特性开发的,其提供了操作系统级别的抽象,是一种容器管理技术,它隔离了应用程序对基础架构(操作系统等)的依赖.相较于虚拟机而言,Docker共享的是宿主机的硬件资源,使用容器来提供独立的运行环境来运行应用.虚拟机则是基于Supervisor(虚拟机管理程序)使用虚拟化技术来提供隔离的虚拟机,在虚拟机的操作系统上提供运行环境!虽然两者都提供了很好的资源隔离,但很明显Docker的虚拟化开销更低! Docker涉及了三个核心

随机推荐