云vscode搭建之使用容器化部署的方法
Vscode作為一個輕量級的IDE,其支持豐富的插件,而通過這些插件我們就可以實現(xiàn)在Vscode中寫任何語言的代碼。Code-Server是Vscode的網(wǎng)頁版,啟動Code-Server之后我們就可以在瀏覽器中打開vscode來愉快的編寫代碼了。這種方式非常適合我們做linux編程。使用C/C++的時候,在windows上編寫的代碼在linux上可能跑不了,而且安裝linux圖形界面,然后在圖像界面中使用vscode又很麻煩。當然也可以使用vscode的遠程開發(fā)。但是我認為啟動code-server來在瀏覽器上使用vscode也是非常方便的。
隨著容器化的發(fā)展,現(xiàn)在涌現(xiàn)出了很多云IDE,比如騰訊的Cloud Studio,但是其也是基于Code-Server進行開發(fā)部署的,用了它的云IDE后,我便產(chǎn)生出了自己部署一個這樣的云IDE的想法。
1、Code-Server下載部署
1.1 Code-Server下載
下載地址:https://github.com/coder/code-server/releases/
在上面的網(wǎng)址中下載code-server,并將其傳輸?shù)絣inux服務(wù)器上。
也可以在linux服務(wù)器中直接使用命令來下載:
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.設(shè)置密碼到環(huán)境變量中
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創(chuàng)建了一個帶有Golang開發(fā)環(huán)境的容器,然后在容器中運行Code-Server,將Dockerfile放在跟code-server-4.4.0-linux-amd64.tar.gz同目錄。
1.創(chuàng)建名為Dockerfile的文件(Dockerfile要跟code-server的tar.gz文件在同一目錄中),將下面內(nèi)容復制進去
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.然后執(zhí)行命令構(gòu)建Docker鏡像:
docker build -t code-server .
3.運行容器
docker run -d --name code-server -p 9999:9999 code-server
2. 一個小問題
下面的內(nèi)容針對Docker部署的Code-Server。
想象這樣一個場景,我們開發(fā)了一個類似Cloud Studio的云IDE,每啟動一個工作空間我們就通過Docker或者Kubernetes來創(chuàng)建一個容器,然后在容器中部署一個Code-Server,最后通過將端口暴露出去給用戶使用。
云IDE使用起來很方便,打開和銷毀的很迅速,即開即用。用戶使用Golang在云IDE中寫了一個http服務(wù)器,想要在他電腦的瀏覽器上訪問,卻發(fā)現(xiàn)訪問不了。那么為什么Code-Server的端口就可以訪問,其它端口無法訪問呢。因為我們在啟動容器的時候就已經(jīng)預想到要訪問這個端口,然后將端口暴露出去了,也就是建立了端口映射。在容器中新啟動的端口并沒有建立映射,因此只能在服務(wù)器內(nèi)部訪問,而不能在用戶電腦上訪問。
那么如何讓用戶也可以訪問到呢,我們可以在主機上部署一個代理服務(wù)器,用戶訪問這個代理服務(wù)器,然后轉(zhuǎn)發(fā)請求到容器中,再將響應(yīng)轉(zhuǎn)發(fā)給用戶。
那么如何發(fā)現(xiàn)用戶啟動服務(wù)器監(jiān)聽了哪個端口呢,首先我能想到的就是啟動一個程序每隔一段時間查詢一下是否有新的端口被監(jiān)聽。獲取端口信息可以使用netstat命令或者lsof命令,在此我選擇了netstat,就有了下面的一個程序:
2.1 端口監(jiān)聽
這個程序會每隔一秒獲取一下有哪些端口處于LISTEN狀態(tài),然后對比上一次的狀態(tài),看是否有新的端口被監(jiān)聽。當我們監(jiān)聽了新的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狀態(tài)的端口 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狀態(tài)的端口信息 res, err := cmd.CombinedOutput() // 獲取結(jié)果 fmt.Println(string(res)) if err != nil { fmt.Println("Execute netstat failed") return nil } return p.parsePort(res) // 對結(jié)果進行解析 } func (p *PortListener) parsePort(msg []byte) []uint16 { // 解析出處于LISTEN狀態(tài)的端口,只要端口號 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就會提醒發(fā)現(xiàn)了新的端口,是否要在瀏覽器中訪問。因此我就想到了實現(xiàn)這樣一個插件,因此下面部分就是實現(xiàn)一個vscode的插件來發(fā)現(xiàn)是否有新的端口被監(jiān)聽了,然后提醒用戶是否在瀏覽器中訪問。
下面只是簡單介紹,想要了解vscode插件的詳細開發(fā)過程的自行搜索。
1.首先安裝yeoman腳手架工具,以及官方提供的腳手架工具:
npm install -g yo generator-code
2.創(chuàng)建項目,選擇要創(chuàng)建的項目以及其它信息
yo code
3.創(chuàng)建完成后,就可以編寫插件了
// 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() { // 設(shè)置定時器,每隔一秒獲取一次 listenPortChange() }, 1000) }) } function listenPortChange() { // 獲取處于LISTEN狀態(tài)的端口 getListeningPorts(function(ports) { var tmpSet = new Set() ports.forEach(p => { if (!s.has(p)) { // 發(fā)現(xiàn)新的端口被監(jiān)聽就提醒用戶是否在瀏覽器中打開 vscode.window.showInformationMessage("發(fā)現(xiàn)新開啟的端口:" + p + ",是否在瀏覽器中訪問?", "是", "否", "不再提示") .then(result=> { if (result === "是") { // 在瀏覽器中打開來訪問代理服務(wù)器,后面帶上端口信息,以便代理服務(wù)器知道訪問容器的哪個端口 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.然后構(gòu)建項目,首先安裝vsce
庫,再打包
npm i -g vsce vsce package
5.打包后生成了vsix文件,將vsix文件上傳到服務(wù)器,然后再拷貝到docker容器中
# docker拷貝命令 docker cp 主機文件名 容器ID或容器名:/容器內(nèi)路徑
然后在瀏覽器中的vscode中選擇vsix文件來安裝插件
安裝完之后,我們的插件在vscode打開后就會啟動,然后每隔一秒查詢一個端口情況。
測試
接下來,測試一下插件:
在vscode中寫了一個http服務(wù)器,然后啟動這個服務(wù)器,看插件是否能發(fā)現(xiàn)這個端口被監(jiān)聽了
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服務(wù)器:
go run main.go
可以看到,它彈出了提示,提示我們是否在瀏覽器中打開
但是現(xiàn)在在瀏覽器中打開是訪問不了容器中的http服務(wù)器的,因為端口沒有被映射到主機端口上。
2.3 代理服務(wù)器實現(xiàn)
在此,為了驗證我的想法是否能成功,只是實現(xiàn)了一個簡單的代理服務(wù)器,它將請求轉(zhuǎn)發(fā)的容器中,然后再轉(zhuǎn)發(fā)容器中服務(wù)器的響應(yīng)。(因為代理服務(wù)器是直接運行在主機上的,因此可以通過容器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后面的參數(shù) 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) // 訪問容器中的服務(wù)器 if err != nil { ctx.Status(http.StatusBadRequest) return } io.Copy(ctx.Writer, resp.Body) // 轉(zhuǎn)發(fā)響應(yīng) }) engine.Run(":80") }
在主機服務(wù)器上運行代理服務(wù)器,不要使用容器來啟動:
go build nohup ./porxy_server & # 后臺運行
然后我們再啟動瀏覽器vscode中的服務(wù)器看是否可以訪問到:
選擇"是",然后在新彈出的窗口中就可以訪問到容器中的服務(wù)了:
這里實現(xiàn)的只是一個非常簡易的版本,只是提供了一個這樣的思路。如何要實現(xiàn)一個類似Cloud Studio的云IDE要考慮的還要更多。
最終效果如下:
到此這篇關(guān)于云vscode搭建使用容器化部署的文章就介紹到這了,更多相關(guān)云vscode搭建內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Jar包一鍵重啟的Shell腳本及新服務(wù)器部署的一些經(jīng)驗分享
這篇文章主要介紹了Jar包一鍵重啟的Shell腳本及新服務(wù)器部署的一些經(jīng)驗,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04Visual Studio和Visual Studio Code之間有什么區(qū)別
本文給大家介紹的是Visual Studio和Visual Studio Code之間有什么區(qū)別,希望對大家的學習能夠有所幫助2020-02-02Scratch3.0二次開發(fā)之windows環(huán)境下打包成exe的流程
今天通過本文給大家分享Scratch3.0二次開發(fā)之windows環(huán)境下打包成exe的詳細流程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-08-08linux系統(tǒng)使用vscode進行qt開發(fā)的過程分享
最近在Linux上搞Qt,搞的一頭霧水,小編把整個過程記錄下,分享需要的朋友,如果大家對linux系統(tǒng)使用vscode進行qt開發(fā)相關(guān)知識感興趣的朋友跟隨小編一起看看吧2021-12-12