前言
使用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、插件等,啟動速度大大提高。
環境準備
要使VS Code可以遠程連接開發機,最好使用ssh密鑰建立本地電腦和開發機的信任關系。要使用容器進行開發,開發機上必須安裝Docker。這兩步相關教程網上較多,在這里就不再贅述。
需要注意的是,要使VS Code通過ssh連上開發機并通過docker啟動開發環境容器,建立信任關系的賬戶必須具備docker使用權限。如果使用root賬戶,那么自然具備。如果非root則可以使用任意一種方式使賬戶獲得docker使用權限:
-
將用戶加入docker組。參考Post-installation steps for Linux(https://docs.docker.com/engine/install/linux-postinstall)。
sudo usermod -aG docker $USER
-
將docker.sock權限修改為777(不太推薦,除非上述方法無法奏效)。
sudo chmod 777 /var/run/docker.sock
下面假設開發機使用的是Linux系統,并且與本地電腦已經建立好信任關系,而且安裝并具備Docker訪問權限。
安裝插件
在VS Code的Marketplace中搜索“Remote Development”安裝插件。
安裝完成后,會發現多出了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文件。
配置文件的內容如下:
{
"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
。
或者若出現如下圖提示,則可以直接點擊在容器中重新打開。
首次打開時會比較慢,因為要下載鏡像并安裝插件,后面再次打開時速度會很快。按照提示進行鏡像Build。
完成上述步驟后,我們已經可以使用VS Code進行代碼編輯,并在其中進行代碼編譯。
注:如果以前拉取過編譯鏡像,可能需要觸發Remote-Containers: Rebuild Container
重新構建。
3. 在容器中進行開發
開發容器啟動后,我們已經可以在VS Code中瀏覽Workspace代碼了。但是隨便打開一個文件,滿眼都是錯誤提示,代碼的跳轉功能也不能正常工作。這是因為C++開發環境的includePath沒有被正確配置。
打開命令面板,輸入C++ config
,選擇C/C++: Edit Configurations(UI)
。
找到Include path,輸入鏡像內依賴庫的路徑。
再回來看代碼時,錯誤提示都消失了,并且函數定義跳轉正常。
4. 在容器中進行編譯
打開新Terminal(找不到的可以在命令面板中輸入Terminal,選擇新開一個)
-
編譯iLogtail Go插件
make vendor # 若需要更新插件依賴庫
make plugin_local # 每次更新插件代碼后從這里開始
-
編譯iLogtail C++代碼
mkdir -p core/build # 若之前沒有建過
cd core/build
cmake .. # 若增刪文件,修改CMakeLists.txt后需要重新執行
make -sj$(nproc) # 每次更新core代碼后從這里開始
5. 獲取編譯產出
由于VS Code是直接將代碼庫目錄掛載到鏡像內的,因此主機上可以直接訪問鏡像內的編譯產出。
到這里,如果要求不高的話就可以結束了,但細心的讀者一定發現了一個問題,容器內生成的文件在主機上都是root權限,必須執行sudo chown -R $USER .
進行修復。如果社區的成員開發機沒有sudo權限怎么辦?作為iLogtail社區核心貢獻者,當然不能把這樣的坑留給隊友了。
使用Dockerfile開發
那有沒有辦法做到容器內權限自動適配主機呢?這樣的問題當然不會難倒VS Code了。Remote Development 插件支持使用 Dockerfile 在容器中進行開發,即在啟動開發容器前先使用docker build一個開發鏡像,這給了修正容器內賬戶權限的機會。
修正的原理如下:
-
在Remote Development 插件 docker build 前將開發機的賬戶名、賬戶ID、組名和組ID暴露給 docker。
-
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:latest
ARG 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權限。
chmod
和chown
的幾行對文件權限進行修正,使新建的用戶有權限讀寫對應目錄。其中對HOME(~$USERNAME)目錄的修正必須在這里進行,否則會導致VS Code Sever沒有權限安裝,導致插件啟動失敗。
3. 創建腳本暴露主機賬戶信息
gen_env.sh腳本內容如下。該腳本對開發機為Mac系統也做了兼容。
set -ue
set -o pipefail
if uname -s | grep Linux; then
echo -e "HOST_OS=Linux USERNAME=$USER USER_UID=$(id -u $USER) GROUPNAME=$(id -gn $USER) GROUP_GID=$(id -g $USER)" > .devcontainer/.env;
else
echo "HOST_OS=Darwin USERNAME=$USER USER_UID=$(id -u $USER) GROUPNAME=root GROUP_GID=0" > .devcontainer/.env;
fi
前3步完成后,Workspace中的配置目錄應該有這樣3個文件:
4. 運行觀察效果
使用Shift + Command + P(Mac)或Ctrl + Shift + P(Win)打開命令面板,輸入reopen
,選擇Remote-Containers: Rebuild Container
。
在容器內重新執行id
命令查看賬戶信息,可以看到與開發機一致。
在容器內重新執行之前的編譯命令。然后會到開發機上查看生成的文件權限,可以看到容器內生成的文件,在開發機上都已經變成正確的權限了。
在容器內調試
除了編譯代碼,開發環境另一個重要功能是進行本地調試。打開一個iLogtail插件的單元測試文件,設置斷電然后點擊“debug test”。
什么?Failed to launch: could not launch process: fork/fork/exec ...: operation not permitted,出錯了!
查閱資料,原來是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!
總結
至此,我們已經可以happy地通過VS Code的Remote Development插件在遠程容器內開發了。并且使用的編譯鏡像和插件配置文件都是可移植,可重復的,CI到代碼庫后可以供任何開發者使用。文章中提到的代碼都可以到iLogtaill的GitHub倉庫(https://github.com/alibaba/ilogtail)獲取。
Remote Development插件還有很多的功能沒有在本篇文章中使用到,感興趣的讀者可以根據文末參考資料進一步研究探索。
審核編輯:湯梓紅
-
容器
+關注
關注
0文章
495瀏覽量
22060 -
C++
+關注
關注
22文章
2108瀏覽量
73623 -
開發環境
+關注
關注
1文章
225瀏覽量
16610
原文標題:一招解決開發環境問題 —— 遠程容器開發指南
文章出處:【微信號:玩轉VS Code,微信公眾號:玩轉VS Code】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論