<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
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的想法。
下載地址: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.解壓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 &
接下來將介紹使用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
下面的內容針對Docker部署的Code-Server。
想象這樣一個場景,我們開發了一個類似Cloud Studio的雲IDE,每啟動一個工作空間我們就通過Docker或者Kubernetes來建立一個容器,然後在容器中部署一個Code-Server,最後通過將埠暴露出去給使用者使用。
雲IDE使用起來很方便,開啟和銷燬的很迅速,即開即用。使用者使用Golang在雲IDE中寫了一個http伺服器,想要在他電腦的瀏覽器上存取,卻發現存取不了。那麼為什麼Code-Server的埠就可以存取,其它埠無法存取呢。因為我們在啟動容器的時候就已經預想到要存取這個埠,然後將埠暴露出去了,也就是建立了埠對映。在容器中新啟動的埠並沒有建立對映,因此只能在伺服器內部存取,而不能在使用者電腦上存取。
那麼如何讓使用者也可以存取到呢,我們可以在主機上部署一個代理伺服器,使用者存取這個代理伺服器,然後轉發請求到容器中,再將響應轉發給使用者。
那麼如何發現使用者啟動伺服器監聽了哪個埠呢,首先我能想到的就是啟動一個程式每隔一段時間查詢一下是否有新的埠被監聽。獲取埠資訊可以使用netstat命令或者lsof命令,在此我選擇了netstat,就有了下面的一個程式:
這個程式會每隔一秒獲取一下有哪些埠處於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 }
但是上面的程式也無法通知到使用者,在使用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伺服器的,因為埠沒有被對映到主機埠上。
在此,為了驗證我的想法是否能成功,只是實現了一個簡單的代理伺服器,它將請求轉發的容器中,然後再轉發容器中伺服器的響應。(因為代理伺服器是直接執行在主機上的,因此可以通過容器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搭建內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45