一招解決開發環境問題 —— 遠程容器開發指南

前言

使用C++作為主要開發語言的程序猿們應該會認同搭建開發環境是一件煩人的事情。為了編譯一個程序不僅需要下載各種依賴包,還可能面臨本地系統不兼容、編譯器版本不一致、包版本沖突等各種問題。筆者在運營iLogtail開源社區的過程中發現開發和調試環境問題也是成員問的最多的問題之一,那么有沒有一種方法可以徹底解決這一問題呢?

有。容器技術使應用在各種環境可以一鍵部署,一致執行,同樣的原理也適用于開發環境部署。利用 VSCode 的 Remote-Development 插件就可以使整個開發環境運行在遠程容器中。使用這種方式不但可以直接使用一致的環境開發編譯,而且還自然實現了多個開發環境的隔離。下面讓就我們由淺到深搭建這樣的遠端容器開發環境。

原理簡介

為什么要遠程+容器?遠程解決的是開發機資源問題和代碼安全問題,本地電腦的CPU和內存比較有限,為了提高編譯、測試效率一般都會準備一臺專門用于開發測試的機器,而部分公司為了防止代碼外泄,只允許內部開發機訪問代碼庫。容器解決的是開發環境一致性問題。兩者結合起來便能構建最理想的開發環境。

在使用 Remote-Development 插件時,插件會ssh連接到遠程開發機,然后根據配置直接啟動或是鑄造開發環境鏡像后啟動開發環境容器。啟動時將開發機的Workspace目錄作為源掛載到容器中。開發環境容器啟動后,插件會自動安裝VS Code Server,并安裝配置指定的VS Code插件。一旦容器內的VS Code Server啟動后,本地的VS Code就會直接與容器內的VS Code Server建立通信。容器內可以訪問Workspace所有文件,并且修改不會因容器退出而丟失。容器開發環境可以使用的VS Code插件,在Workspace的devcontainer.json配置中指定,下文會有詳細描述。

為了提高啟動速度并保留容器內插件的配置,開發容器內的/vscode目錄其實掛載了一個docker volume,不會自動隨docker退出而回收,因此從第二次連接容器開發環境開始,無需重新安裝VS Code Server、插件等,啟動速度大大提高。

8f6da8352a104086dca58a705ba4a090.png

環境準備

要使VS Code可以遠程連接開發機,最好使用ssh密鑰建立本地電腦和開發機的信任關系。要使用容器進行開發,開發機上必須安裝Docker。這兩步相關教程網上較多,在這里就不再贅述。

需要注意的是,要使VS Code通過ssh連上開發機并通過docker啟動開發環境容器,建立信任關系的賬戶必須具備docker使用權限。如果使用root賬戶,那么自然具備。如果非root則可以使用任意一種方式使賬戶獲得docker使用權限:

  1. 將用戶加入docker組。參考Post-installation steps for Linux(https://docs.docker.com/engine/install/linux-postinstall)。
    sudo usermod -aG docker $USER

  2. 將docker.sock權限修改為777(不太推薦,除非上述方法無法奏效)。
    sudo chmod 777 /var/run/docker.sock

下面假設開發機使用的是Linux系統,并且與本地電腦已經建立好信任關系,而且安裝并具備Docker訪問權限。

安裝插件

在VS Code的Marketplace中搜索“Remote Development”安裝插件。

a334e6f2262184061a2a88f8c08a4365.png

安裝完成后,會發現多出了3個子插件。

  • Remote - Containers:連接容器開發

  • Remote - SSH:連接ssh遠程開發

  • Remote - WSL:連接WSL(Windows Linux子系統)開發

使用鏡像開發

使用 Remote Development 插件最直接的方式就是利用現成的編譯鏡像啟動開發容器。這里以使用C++和Go語言編寫、依賴環境相對復雜的開源項目iLogtail數據采集器項目為例,說明如何利用 Remote Development 插件進行遠程容器開發。

1. 創建Remote Development配置

在iLogtail Workspace的頂層目錄創建.devcontainer目錄,并在里面創建devcontainer.json文件。

9ac088edf37ac05006d0ba8fb20dd69c.png

配置文件的內容如下:

{"image": "sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/ilogtail-community-edition/ilogtail-build-linux:latest","customizations": {"vscode": {"extensions": ["golang.Go","ms-vscode.cpptools-extension-pack"]}}
}

其中,image字段是Remote Development插件啟動開發環境的鏡像地址,customizations.vscode.extensions指定了開發環境的插件。部分插件介紹如下,開發者也可以按照自己的習慣進行修改。

插件名

用途

golang.Go

Go開發必備插件

ms-vscode.cpptools-extension-pack

C++開發必備插件

2. 在容器中打開代碼庫

使用Shift + Command + P(Mac)或Ctrl + Shift + P(Win)打開命令面板,輸入reopen,選擇Remote-Containers: Reopen in Container

7053fd2387c7199cf720197406d6e0d8.png

或者若出現如下圖提示,則可以直接點擊在容器中重新打開。

0a79ad5a8345cb88a5b92bcc718d518f.png

首次打開時會比較慢,因為要下載鏡像并安裝插件,后面再次打開時速度會很快。按照提示進行鏡像Build。

完成上述步驟后,我們已經可以使用VS Code進行代碼編輯,并在其中進行代碼編譯。

注:如果以前拉取過編譯鏡像,可能需要觸發Remote-Containers: Rebuild Container重新構建。

3. 在容器中進行開發

開發容器啟動后,我們已經可以在VS Code中瀏覽Workspace代碼了。但是隨便打開一個文件,滿眼都是錯誤提示,代碼的跳轉功能也不能正常工作。這是因為C++開發環境的includePath沒有被正確配置。

37c2d0a2cfa2b8c6945c4d8745d91735.png

打開命令面板,輸入C++ config,選擇C/C++: Edit Configurations(UI)

576d677ba4544769b669347a2d54d482.png

找到Include path,輸入鏡像內依賴庫的路徑。

d2f70fd5b07917f0f3f6091c02e68e5f.png

再回來看代碼時,錯誤提示都消失了,并且函數定義跳轉正常。

4. 在容器中進行編譯

打開新Terminal(找不到的可以在命令面板中輸入Terminal,選擇新開一個)

bcf39605773b7119ec59193530aaf926.png

  • 編譯iLogtail Go插件

make vendor       # 若需要更新插件依賴庫
make plugin_local # 每次更新插件代碼后從這里開始

8bb6ae6b3be5a67328acd67913979181.png

  • 編譯iLogtail C++代碼

mkdir -p core/build # 若之前沒有建過
cd core/build
cmake .. # 若增刪文件,修改CMakeLists.txt后需要重新執行
make -sj$(nproc)    # 每次更新core代碼后從這里開始

0d6667bb9c617c7c393c820e50b8f29d.png41f136609f8fc47410e871b128e0fb42.png

5. 獲取編譯產出

由于VS Code是直接將代碼庫目錄掛載到鏡像內的,因此主機上可以直接訪問鏡像內的編譯產出。

c2e3fb66d863951d298148486e901943.png

到這里,如果要求不高的話就可以結束了,但細心的讀者一定發現了一個問題,容器內生成的文件在主機上都是root權限,必須執行sudo chown -R $USER .進行修復。如果社區的成員開發機沒有sudo權限怎么辦?作為iLogtail社區核心貢獻者,當然不能把這樣的坑留給隊友了。

使用Dockerfile開發

那有沒有辦法做到容器內權限自動適配主機呢?這樣的問題當然不會難倒VS Code了。Remote Development 插件支持使用 Dockerfile 在容器中進行開發,即在啟動開發容器前先使用docker build一個開發鏡像,這給了修正容器內賬戶權限的機會。

修正的原理如下:

  1. 在Remote Development 插件 docker build 前將開發機的賬戶名、賬戶ID、組名和組ID暴露給 docker。

  2. docker build時利用這些賬戶信息修正容器執行賬戶和容器內文件權限。

接下來我們進行實際操作。

1. 修改.devcontainer.json配置文件

在配置文件中,將image部分修改為build部分,使用Dockerfile啟動開發容器。同時,加入initializeCommand,在build鏡像前,將賬戶信息暴露給docker。

{"build": {"dockerfile": "Dockerfile","args": {"USERNAME": "${localEnv:USER}"}},"initializeCommand": ".devcontainer/gen_env.sh","customizations": {"vscode": {"extensions": ["golang.Go","ms-vscode.cpptools-extension-pack"]}}
}

2. 創建Dockerfile

以編譯鏡像作為基礎鏡像,編寫Dockerfile對鏡像內賬戶和文件權限進行修正。

FROM sls-opensource-registry.cn-shanghai.cr.aliyuncs.com/ilogtail-community-edition/ilogtail-build-linux:latestARG USERNAME=admin
USER root# Create the user
COPY .env /tmp/.env
RUN source /tmp/.env && rm /tmp/.env; \if getent passwd $USERNAME; then userdel -f $USERNAME; fi; \if [ $HOST_OS = "Linux" ]; then \if getent group $GROUPNAME; then groupdel $GROUPNAME; fi; \groupadd --gid $GROUP_GID $GROUPNAME; \fi; \useradd --uid $USER_UID --gid $GROUP_GID -m $USERNAME; \echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME; \chmod 0440 /etc/sudoers.d/$USERNAME; \chown -R $USERNAME:$GROUPNAME /root/go /opt/logtail $(eval echo ~$USERNAME); \chmod 755 $(eval echo ~$USERNAME);USER $USERNAME

COPY .env /tmp/.env將主機的賬戶信息通過文件形式復制到容器中。

接下來的幾行根據這些信息在容器內創建對應的賬戶。

echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME;授予該用戶sudo權限。

chmodchown的幾行對文件權限進行修正,使新建的用戶有權限讀寫對應目錄。其中對HOME(~$USERNAME)目錄的修正必須在這里進行,否則會導致VS Code Sever沒有權限安裝,導致插件啟動失敗。

3. 創建腳本暴露主機賬戶信息

gen_env.sh腳本內容如下。該腳本對開發機為Mac系統也做了兼容。

set -ue
set -o pipefailif uname -s | grep Linux; thenecho -e "HOST_OS=Linux\nUSERNAME=$USER\nUSER_UID=$(id -u $USER)\nGROUPNAME=$(id -gn $USER)\nGROUP_GID=$(id -g $USER)" > .devcontainer/.env;
elseecho "HOST_OS=Darwin\nUSERNAME=$USER\nUSER_UID=$(id -u $USER)\nGROUPNAME=root\nGROUP_GID=0" > .devcontainer/.env;  
fi

前3步完成后,Workspace中的配置目錄應該有這樣3個文件:

16269f8b317d23ee9f54a283c8a49050.png

4. 運行觀察效果

使用Shift + Command + P(Mac)或Ctrl + Shift + P(Win)打開命令面板,輸入reopen,選擇Remote-Containers: Rebuild Container

442858ddea28e7041eab780653c89d3f.png

在容器內重新執行id命令查看賬戶信息,可以看到與開發機一致。

cfc4d3cb72c006af2891fb8a23bcf177.png

在容器內重新執行之前的編譯命令。然后會到開發機上查看生成的文件權限,可以看到容器內生成的文件,在開發機上都已經變成正確的權限了。

42e36e9de52726e55bd01520de5c1081.png

在容器內調試

除了編譯代碼,開發環境另一個重要功能是進行本地調試。打開一個iLogtail插件的單元測試文件,設置斷電然后點擊“debug test”。

176362584ca7f420745b8d84aeb9a974.png

什么?Failed to launch: could not launch process: fork/fork/exec ...: operation not permitted,出錯了!

354c4e52a4297d3ce7a2b598c87617e7.png

查閱資料,原來是docker默認的安全策略使用Secure computing mode (seccomp)僅允許白名單系統調用,debug所需的系統調用被拒絕了。我們嘗試在配置文件中添加一行"runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],禁用該功能。

{"build": {"dockerfile": "Dockerfile","args": {"USERNAME": "${localEnv:USER}"}},"initializeCommand": ".devcontainer/gen_env.sh","runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],"customizations": {"vscode": {"extensions": ["golang.Go","ms-vscode.cpptools-extension-pack","DavidAnson.vscode-markdownlint"]}}
}

Rebuild Container后,再次嘗試調試功能。It works!

84f9379bbc12911e7fd0efe298f931ff.png

總結

至此,我們已經可以happy地通過VS Code的Remote Development插件在遠程容器內開發了。并且使用的編譯鏡像和插件配置文件都是可移植,可重復的,CI到代碼庫后可以供任何開發者使用。文章中提到的代碼都可以到iLogtaill的GitHub倉庫(https://github.com/alibaba/ilogtail)獲取。

Remote Development插件還有很多的功能沒有在本篇文章中使用到,感興趣的讀者可以根據文末參考資料進一步研究探索。

參考資料

  1. Developing inside a Container using Visual Studio Code Remote Development:

    https://code.visualstudio.com/docs/remote/containers

  2. Create a development container using Visual Studio Code Remote Development:

    https://code.visualstudio.com/docs/remote/create-dev-container

  3. Running Docker Containers as Current Host User - Juan Treminio - Senior Web Developer Blog:

    https://jtreminio.com/blog/running-docker-containers-as-current-host-user/

  4. Add non-root user to a container:

    https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user

  5. devcontainer.json reference:

    https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user

  6. Seccomp security profiles for Docker:

    https://docs.docker.com/engine/security/seccomp/

  7. alibaba/ilogtail: Fast and Lightweight Observability Data Collector:

    https://github.com/alibaba/ilogtail

推薦閱讀:

  • 全宇宙首本 VS Code 中文書,來了!

  • Code Runner for VS Code,下載量突破 4000 萬!支持超過50種語言

  • 微軟也愛 Python!VS Code Python 全新發布!Jupyter Notebook 原生支持終于來了!

  • 微軟也愛 Java!微軟在 SpringOne 大會上宣布 Azure Spring Cloud 云服務!

  • 在微軟(Microsoft)工作是怎樣一番體驗?

  • 微軟內推,長期有效

  • 代碼編輯器橫評:為什么 VS Code 能拔得頭籌

  • 知否知否,VS Code 不止開源

  • 那些年,我們一起追的 VS Code

玩轉VS Code

VS Code?·?編程開發?·?業界資訊

beae3e7c7be95a32709556a64744ac3c.jpeg

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282249.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282249.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282249.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

php中常用的加密方式

一、md5 要說php中的最常用的加密方式,md5可以說是當仁不讓。 使用起來也很簡單便捷。 注:使用方式請看 六、md5加密方式的漏洞及解決方案 二、password_hash 作為php5.5以上版本專門用于加密的方式,自然有其獨到之處。 使用方式鏈接&a…

解決問題的策略-分而治之

一個宏偉的目標看上去遙不可及,這怎么可能做成呢?但是你把這些目標分解成一個一個的小目標,小目標再往下分解,分解到最后,分解成細枝末節時你會發現,這事其實是可以做的。這個做成了再往下走一步&#xff0…

UITabBarController的基本原理及使用(一)

前言 UITabBarController在iOS開發中是一個高頻使用的控制器,典型的案例如QQ、微信均使用UITabBarController布局。本文將從一個新建工程,和大家一起了解UITabBarController的基本原理和使用方法。 基本概念 UITabBarController能夠方便地管理多個控制器…

word-vba-microsoft(中英文)

中文 https://msdn.microsoft.com/zh-cn/vba/word-vba/articles/view-displaypageboundaries-property-word 英文 https://msdn.microsoft.com/en-us/vba/word-vba/articles/view-displaypageboundaries-property-word轉載于:https://www.cnblogs.com/itzxy/p/7625915.html

C# 多線程ThreadPool用法舉例

概述ThreadPool是.Net Framework 2.0版本中出現的。自從Task出來以后,ThreadPool已經很少用了,但是一些老的代碼或者一些古老的程序猿還是會用到他,所以我們可以不用它,但是還是有必須學習和了解他.ThreadPool用法舉例static void…

Mysql實現主從復制(一主雙從)

一、環境介紹 LNMP(centos7,mysql5.6) vmware workstation pro配置了3個虛擬機,均安裝了LNMP環境: master: 192.168.0.105 slave: 192.168.0.106 、192.168.0.107 二、原理 &a…

Elasticsearch學習筆記-04.3批處理

除了創建、更新和刪除個別文檔,Elasticsearch還提供了使用_bulk API的上述操作的批量操作方法。這個功能很重要,因為他提供了一種有效的機制來在盡可能少的網絡傳輸過程中執行多次操作。 作為一個快速示例,下面的命令在一次批量操作中索引了兩…

接口文檔神器Swagger(下篇)

本文來自網易云社區作者:李哲二、Swagger-springmvc原理解析上面介紹了如何將springmvc和springboot與swagger結合,通過簡單配置生成接口文檔,以及介紹了swagger提供的一些注解。下面將介紹swagger是如何做到與springmvc結合,自動…

php實現mysql分表

一、場景說明 1、為什么要進行分表 隨著數據量的不斷增大,一張表中的數據肯定也會越來越多,甚至達到百萬甚至千萬級。我們通常會通過搭建mysql集群(主從同步),讀寫分離來實現優化數據庫查詢執行效率。 但是由于數據…

利用python進行數據分析D1——ch02引言

基礎的課程還沒學完,就來這本了,因為我平時的研究還是以數據的處理為主。把自己的事做好做細致讀了一下介紹部分,下載書里用到的數據,下載地址:https://github.com/wesm/pydata-book 如果你需要完成以下幾大類任務&…

記一次Memory Leak分析

起因:最近公司的一個web產品遇到了內存溢出,于是開始著手調查。調查:首先當務之急是找到那個或那些API導致Memory Leak,這個應該不難,根據監控分析,在內存上升時間段內有哪些API被訪問,再就是根…

【t057】任務分配

Time Limit: 1 second Memory Limit: 128 MB 【問題描述】 現有n個任務,要交給A和B完成。每個任務給A或給B完成,所需的時間分別為ai和bi。問他們完成所有的任務至少要多少時間。 【輸入格式】 第一行一個正整數n,表示有n個任務。 接下來有n行&#xf…

LeetCode 366. Find Leaves of Binary Tree

實質就是求每個節點的最大深度。用一個hash表記錄&#xff0c;最后輸出。 class Solution { public:unordered_map<TreeNode *,int> hash; // record the level from bottomvector<vector<int>> findLeaves(TreeNode* root) {vector<vector<int>>…

C#比較對象的相等性

對于相等的機制全部不同&#xff0c;這取決于比較的是引用類型還是值類型。以下分別介紹引用類型和值類型的相等性。1.比較引用類型的相等性 System.Object定義了三種不同的方法&#xff0c;來比較對象的相等性&#xff1a;ReferenceEquals()和兩個版本號的Equals()。再加上比較…

WebSocket教程

一、為什么需要 WebSocket&#xff1f; 初次接觸 WebSocket 的人&#xff0c;都會問同樣的問題&#xff1a;我們已經有了 HTTP 協議&#xff0c;為什么還需要另一個協議&#xff1f;它能帶來什么好處&#xff1f; 答案很簡單&#xff0c;因為 HTTP 協議有一個缺陷&#xff1a…

C# WPF十個美觀的界面設計展示

概述很多時候&#xff0c;我們設計的界面總是感覺缺乏美感&#xff0c;不是我們不會開發好看的界面&#xff0c;而是不知道怎么才算美觀&#xff0c;這時候我們不妨看看別人好的頁面是怎么做的.下面展示一些我覺得做的比較好的cs界面&#xff0c;希望能給大家在平時做界面設計時…

BZOJ3172: [Tjoi2013]單詞

【傳送門&#xff1a;BZOJ3172】 簡要題意&#xff1a; 給出n個單詞&#xff0c;你可以理解為將這些單詞變成一個個段落&#xff0c;然后求出每個單詞在所有段落中出現的次數 題解&#xff08;一&#xff09;&#xff1a; 剛開始不是很懂題目&#xff0c;結果發現將所有單詞看成…

MySQL5.6二進制軟件包編譯安裝詳解(三)

一、軟件環境 [rootlocalhost ~]# uname -r 3.10.0-862.el7.x86_64 [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) 二、安裝部署過程詳解 MySQL安裝3種方式&#xff1a;1>rpm包安裝應用文件默認安裝在/usr/local 目錄下2>源碼編譯需…

Java反射學習總結五(Annotation(注解)-基礎篇)

Annotation(注解)簡單介紹&#xff1a; 注解大家印象最深刻的可能就是JUnit做單元測試,和各種框架里的使用了。本文主要簡介一下注解的用法&#xff0c;下篇文章再深入的研究。 annotation并不直接影響代碼語義。可是它可以被看作類似程序的工具或者類庫。它會反過來對正在執行…

使用autok3s 安裝k3s 集群 和 kuboard 管理集群

一、k3s介紹1.1 什么是k3s?k3s 是經過 CNCF 認證的由 Rancher 公司開發維護的一個輕量級的 Kubernetes 發行版&#xff0c;內核機制還是和 k8s 一樣&#xff0c;但是剔除了很多外部依賴以及 K8s 的 alpha、beta 特性&#xff0c;同時改變了部署方式和運行方式&#xff0c;目的…