在 VS Code 中使用容器開發

緣起

我的主力操作系統是 windows, 但有時不得不需要一些 linux 下的特性,
比如某些工具沒有 windows 版本, 無法使用 MakeFile 等.

自從微軟推出了 Windows Subsystem for Linux (WSL) 之後,
這種情況已經好了不少了. 具體使用可以參考
官方 WSL 文檔.

但我不太習慣使用它, 日常中更偏愛的是 docker.
畢竟還是鏡像方便點, 需要什麼組件就 pull 下來,
用完了或者中間搞壞了, 重新開一個就行了, 成本很低.

遠程開發

jetbrains
的 IDE 是支持 remote development(遠程開發)的,
幸好, VS Code 也開始支持了.

所謂的遠程開發, 就是將遠程的服務器, 或者容器, 或 WSL 作爲開發環境.
本地的代碼實時同步到遠程中, 而各種工具比如命令行都是在遠程環境中運行的.

下面就來介紹下如何使用 VS Code 進行遠程開發.

安裝

首先, 需要在 VS Code 中安裝對應的插件,
Remote Development extension pack.

這其實是插件集合, 包括了 Remote-SSH , Remote-Containers, Remote-WSL 等.

安裝完成之後, 重啓 VS Code. 左下角會出現一個圖標, 類似於 ><.
點擊之後, 會彈出對應的命令選擇框.

qucik icon

這裏主要介紹 Remote-Containers, 其他的 Remote-SSH 和 Remote-WSL 也類似, 具體可以參考官方文檔說明.

使用 Remote-Containers

Remote-Containers

上圖是官方文檔上的構架圖, 可以看到源代碼是通過卷映射進去的,
命令行和運行 APP 和 Debugger 都是在容器中完成的.

系統要求
直接看官方文檔吧, 這裏不再解釋.
通常本地裝好 docker 就行了.

在容器中打開項目

要在容器中打開項目, 會面臨多種選擇:

  • 新建配置

    • 從預定義好的配置中
    • 從 docker-compose.yml 中
    • 從 Dockerfile 中
  • 附加到已運行的容器中

詳細梳理一下流程:

  1. 選擇 Remote-Containers: Reopen Folder in Container,
    如果此時本地還沒有對應的配置, 就會觸發 新建配置 的過程.
    這時可能有多種選項, 取決於本地項目中是否存在
    Dockerfiledocker-compose.yml 文件.

    1. 必有的選項: From a predefined container configuration definition....
      這個選擇會顯示預定好的配置文件, 可以根據自己使用的語言或技術棧選擇對應的配置.
    2. 本地項目中存在 Dockerfile: From Dockerfile.
      這會使用本地的 Dockerfile 構建容器.
    3. 本地項目中存在 docker-compose.yml: From docker-compose.yml.
      這個選擇本地的 docker-compose.yml 中的其中一個容器作爲開發環境.
  2. 選擇 Remote-Containers: Attach to Running Container...,
    會進入到 附加到已運行的容器中 的過程.
    此時, 選擇對應的本地容器就行了.

其他的打開方式包括:

  • 在容器中打開文件夾
    Remote-Containers: Open Folder in Container...
  • 在容器中打開工作空間
    Remote-Containers: Open Workspace in Container...
  • 在容器中打開 Repository(克隆代碼且不同步改動到本地)
    Remote-Containers: Open Repository in Container...

修改配置

配置文件保存在 .devcontainer 中:

  • devcontainer.json 配置選項
  • Dockerfile 如果新建時選擇 Dockerfile
  • docker-compose.yml 如果新建時選擇 docker-compose.yml

更多配置選項可以參考
devcontainer.json reference

特定配置

對於 C++, Go, Rust 等使用 ptrace-based debugger 的語言,
需要開啓以下配置:

修改 devcontainer.json 文件

"runArgs": ["--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"]

如果使用 docker-compose.yml
修改 docker-compose.yml 文件

# Required for ptrace-based debuggers like C++, Go, and Rust
cap_add:
  - SYS_PTRACE
security_opt:
  - seccomp:unconfined

管理擴展

擴展會被分爲兩個部分, 本地和遠程的.

本地的擴展主要是一些 UI 相關的, 其他擴展都安裝在容器中的.

這可能對於多語言開發者(潔癖患者)來說是個不錯的情況,
只要在容器中安裝相應語言所需的擴展就行了,
可以無縫切換工作流.

對於想要完全同步本地擴展的用戶, 可以使用命令
Install Local Extensions in Dev Container:
將所有的本地擴展都安裝在容器中.

有時候, 很多擴展是必裝的, 可以在設置中修改默認擴展:

"remote.containers.defaultExtensions": [
    "eamodio.gitlens",
    "mutantdino.resourcemonitor"
]

擴展只能運行在一個位置, uiworkspace,
可以在 devcontainer.json 中強制指定擴展運行的位置.

"settings": {
    "remote.extensionKind": {
        "ms-azuretools.vscode-docker": "workspace"
    }
},

端口轉發

如果你在開發 web 應用, 很多時候都需要進行端口轉發,
以便在本地的瀏覽器中調試, 這個時候可以使用端口轉發功能.

在命令面板(F1)中選擇 Remote-Containers: Forward Port from Container...

上面的操作通常用於臨時轉發端口.

如果你有固定的端口要轉發, 可以選擇以下兩種方式之一:

修改 devcontainer.json 配置文件:

"appPort": [ 3000, "8921:5000" ]

或者如果使用的是 docker-compose.yml 文件, 直接添加端口:

ports:
  - "3000"
  - "8921:5000"

終端

終端已經變成了容器中的終端, 而不是本地的終端了.

享受 linux 的便利吧.

容器設置

現在設置文件已經有三份了, 相比普通的用戶設置和工作空間設置,
多了一個容器設置, 優先級是最高的.

也可以在 devcontainer.json 設置一些默認的容器設置:

"settings": {
    "java.home": "/docker-java-home"
}

在容器環境中使用 Git

共享 Git 憑據

如果使用 HTTPS 克隆項目, 且使用
憑據助手
則無需任何操作就可以共享憑據.

如果使用 SSH 密鑰, 主要將 ~/.ssh 安裝到容器中就行了.

不過 windows 上有點小問題, .ssh 中的內容不會直接複製到容器中.

不過官方文檔也提供瞭解決方案.

第一步:

複製 .ssh 文件夾到容器中.

如果使用 Dockerfile 或預定義的配置, 將下面的內容添加到
devcontainer.json 文件中:

"runArgs": [ "-v", "${env:HOME}${env:USERPROFILE}/.ssh:/root/.ssh-localhost:ro" ]

如果使用 docker-compose.yml, 更新 volumes 字段:

version: "3"
services:
  your-service-name-here:
    # ...
    volumes:
      - ~/.ssh:/root/.ssh-localhost:ro

第二步:

複製密鑰, 並設置權限.

將以下內容複製到 devcontainer.json 文件中:

使用 root 身份運行容器時

"postCreateCommand": "mkdir -p /root/.ssh && cp -r /root/.ssh-localhost/* /root/.ssh && chmod 700 /root/.ssh && chmod 600 /root/.ssh/*"

使用非 root 身份運行容器時

"postCreateCommand": "sudo cp -r /root/.ssh-localhost ~ && sudo chown -R $(id -u):$(id -g) ~/.ssh-localhost && mv ~/.ssh-localhost ~/.ssh && chmod 700 ~/.ssh && chmod 600 ~/.ssh/*"

如果已經創建了容器了, 需要運行命令
Remote-Containers:Rebuild Container
重新構建容器, 以使得改變生效.

解決換行符問題

在項目的 .gitattributes 文件中添加以下內容:

* text=auto eol=lf
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf

配置文件示例

以下配置文件來自於
go_web,
這是一個使用 Go 語言建立 web 應用的示例項目.

.devcontainer/devcontainer.json

// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
{
  "name": "Existing Docker Compose (Extend)",

  // Update the 'dockerComposeFile' list if you have more compose files or use different names.
  // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
  "dockerComposeFile": ["..\\docker-compose.yml", "docker-compose.yml"],

  // The 'service' property is the name of the service for the container that VS Code should
  // use. Update this value and .devcontainer/docker-compose.yml to the real service name.
  "service": "web",

  // The optional 'workspaceFolder' property is the path VS Code should open by default when
  // connected. This is typically a file mount in .devcontainer/docker-compose.yml
  "workspaceFolder": "/workspace",

  // Use 'settings' to set *default* container specific settings.json values on container create.
  // You can edit these settings after create using File > Preferences > Settings > Remote.
  "settings": {
    // This will ignore your local shell user setting for Linux since shells like zsh are typically
    // not in base container images. You can also update this to an specific shell to ensure VS Code
    // uses the right one for terminals and tasks. For example, /bin/bash (or /bin/ash for Alpine).
    "terminal.integrated.shell.linux": null
  },

  // Uncomment the next line if you want start specific services in your Docker Compose config.
  // "runServices": [],

  // Uncomment the next line if you want to keep your containers running after VS Code shuts down.
  // "shutdownAction": "none",

  // Uncomment the next line to run commands after the container is created - for example installing git.
  // "postCreateCommand": "apt-get update && apt-get install -y git",

  // Add the IDs of extensions you want installed when the container is created in the array below.
  "extensions": [],

  // Mount your .ssh folder to /root/.ssh-localhost so we can copy its contents
  "runArgs": [
    // "-v",
    // "${env:HOME}${env:USERPROFILE}/.ssh:/root/.ssh-localhost:ro"
  ],

  // Copy the contents to the correct location and set permissions
  "postCreateCommand": "mkdir -p ~/.ssh && cp -r ~/.ssh-localhost/* ~/.ssh && chmod 700 ~/.ssh && chmod 600 ~/.ssh/*"
}

.devcontainer/docker-compose.yml

--- #-------------------------------------------------------------------------------------------------------------

#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
version: "3.7"
services:
  # Update this to the name of the service you want to work with in your docker-compose.yml file
  web:
    # You may want to add a non-root user to your Dockerfile. On Linux, this will prevent
    # new files getting created as root. See https://aka.ms/vscode-remote/containers/non-root-user
    # for the needed Dockerfile updates and then uncomment the next line.
    # user: vscode

    # Uncomment if you want to add a different Dockerfile in the .devcontainer folder
    # build:
    #   context: .
    #   dockerfile: Dockerfile

    # Uncomment if you want to expose any additional ports. The snippet below exposes port 3000.
    # ports:
    #   - 3000:3000

    volumes:
      # Update this to wherever you want VS Code to mount the folder of your project
      - .:/workspace
      - ~/.ssh:/root/.ssh-localhost:ro

      # Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-in-docker-compose for details.
      # - /var/run/docker.sock:/var/run/docker.sock

    environment:
      GOPROXY: "https://goproxy.io"

    # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
    cap_add:
      - SYS_PTRACE
    security_opt:
      - seccomp:unconfined

    # Overrides default command so things don't shut down after the process ends.
    command: sleep infinity

docker-compose.yml

version: "3.7"

services:
  web:
    image: golang:1.13
    # https://docs.docker.com/compose/compose-file/#init
    init: true
    volumes:
      - .:/home/web
    environment:
      GOPROXY: "https://goproxy.io"

  mysql:
    image: mysql:8
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: "1234"
    ports:
      - 3306:3306

  adminer:
    image: adminer:4
    ports:
      - 8080:8080

  dbclient:
    image: mysql:8
    command: mysql -hmysql -uroot -p1234 -D db_apiserver
    # mysql -hmysql -uroot -p1234
    # source /home/script/db.sql
    # select * from tb_users \G;
    volumes:
      - ./script:/home/script

  nginx:
    image: nginx:stable-alpine
    ports:
      - 80:80
    volumes:
      - ./conf/nginx_web.conf:/etc/nginx/conf.d/nginx_web.conf
    command: nginx -g 'daemon off;'

參考

VS Code Remote Development

Remote Development Tips and Tricks

Docker Compose dev container definitions

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章