×
Community Blog One Way to Solve Development Environment Problems: A Guide to Remote Container Development

One Way to Solve Development Environment Problems: A Guide to Remote Container Development

This article explains some of the functions of the Remote Development plug-in, including environment preparation and installation.

By Yu Tao (Xunfei), from Alibaba Cloud Storage Team

Preface

Programmers that use the C++ Programming Language as their main development language should agree that building a development environment is annoying. When compiling a program, in addition to downloading various dependent packages, we may face various problems (such as incompatibility with local systems, inconsistent compiler versions, and package version conflicts). In the process of operating the iLogtail open-source community, I found that the development and debugging environment is also one of the most frequently asked questions by members. Is there a way to completely solve this problem?

The answer is yes. Container technology enables applications to be deployed with one click and executed consistently in various environments. The same principle also applies to development environment deployment. We can make the entire development environment run in a remote container using the Remote-Development plug-in of VSCode. This approach ensures development and compilation in a consistent environment and isolates multiple development environments. Let's build such a remote container development environment from easy to difficult.

An Introduction to Principle

Why do we need Remote + Container? Remote solves the resource problem of development machines and code security risk. The CPU and memory of the local computer are limited. In order to improve the compilation and testing efficiency, a machine specially used for development and testing is generally prepared, while some companies only allow internal development machines to access the code library to prevent code leakage. Containers realize development environment consistency. The combination of the two can build an ideal development environment.

When using the Remote-Development plug-in, the plug-in will connect to the remote development machine through Secure Shell (SSH). Then it starts it according to the configuration or starts the development environment container after casting the development environment image. Mount the Workspace directory of the development machine as a source to the container at startup. After the development environment container is started, the plug-in will automatically install VS Code Server and the VS Code plug-in specified by configuration. Once the VS Code Server in the container is started, the local VS Code will establish communication with the VS Code Server in the container. All files in Workspace can be accessed within the container, and the files will not be lost if the container exits for modifications. The VS Code plug-ins available for container development environments are specified in the devcontainer.json configuration of Workspace, which is described in detail below.

The /vscode directory in the development container mounts a Docker volume to improve the startup speed and retain the configuration of plug-ins in the container, which will not be automatically recycled when Docker exits.

Therefore, from the second connection to the container development environment, it is unnecessary to reinstall VS Code Server and plug-ins, and the startup speed is significantly improved.

1

Environment Preparation

In order to enable VS Code to connect to the development machine remotely, it is best to use the ssh key to establish a trust relationship between the local computer and the development machine. Docker must be installed on the development machine to use containers for development. There are many related tutorials on the Internet, so I will not repeat them here.

Note: The account that establishes the trust relationship must have Docker usage permission to enable VS Code to connect to the development machine through ssh and start the development environment container through Docker. The root account has the permission. If it is not root account, we can use one of the following methods to grant Docker permission to the account.

  1. Add users to the Docker group. Please refer to Post-installation steps for Linux. sudo usermod -aG docker $USER
  2. Change the docker.sock permission to 777. (This is not recommended unless the method above does not work.) sudo chmod 777 /var/run/docker.sock

This assumes the development machine uses Linux, has established a trust relationship with the local computer, and has installed Docker with access rights.

Install Plug-In

Search Remote Development in the VS Code Marketplace to install the plug-in:

2

After the installation is complete, we will find that there are three more sub-plug-ins:

  1. Remote - Containers: Connect to Container Development
  2. Remote-SSH: Connect to SSH Remote Development
  3. Remote-WSL: Connected to WSL (Windows Subsystem for Linux) Development

Use Images to Develop

The most direct way to use the Remote Development plug-in is to start the development container using the existing compilation image. I will use iLogtail (an open-source project with a complex dependency environment written in the C++ Programming Language and Go Programming Language) as an example to illustrate how to use the Remote Development plug-in to develop remote containers.

1. Create a Remote Development Configuration

Create a .devcontainer directory in the top-level directory of the iLogtail Workspace and create a devcontainer.json file in the .devcontainer directory.

3

The contents of the configuration file are listed below:

{
  "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"
      ]
    }
  }
}

Among them, the image field is the image address of the Remote Development plug-in that starts the development environment and customizations.vscode.extensions specifies the plug-ins for the development environment. Part of the plug-ins are introduced below. Developers can modify them accordingly.

Plug-In Name Usage
golang.Go Essential plug-in for Go development
ms-vscode.cpptools-extension-pack Essential plug-in for C++ development

2. Open the Code Library in Container

Press Shift + Command + P (macOS) or Ctrl + Shift + P (Windows) to open the command panel, enter reopen, and select Remote-Containers: Reopen in Container:

4

If the following message appears, we can click Reopen in Container:

5

It will be slow the first time. Since we have to download the image and install the plug-in, it will be faster to open it again. Follow the prompts to build the image.

After completing the steps above, we can use VS Code for code editing and code compiling.

Note: If you have pulled a compiled image before, you may need to trigger Remote-Containers: Rebuild Container to rebuild.

3. Develop in Containers

After the development container is started, we can browse the Workspace code in VS Code. However, when we open a file, it is full of error prompts, and the jump function of the code does not work properly because the includePath of the C++ development environment is not configured correctly.

6

Open the command panel, enter C++ config, and select C/C++: Edit Configurations(UI):

7

Locate the Include path and enter the path of the dependency library in the image.

8

This way, we can find that all the error prompts disappear, and the function definition can jump normally.

4. Compile in Container

Open a new Terminal (if you can't find it, you can enter Terminal in the command panel and select a new one).

9

  • Compile the iLogtail Go plug-in
make vendor # If the update of plug-in dependency library is needed.
make plugin_local # Start from here after each update of the plug-in code.

10

  • Compile the iLogtail C++ code
mkdir -p core/build # If it has not been built before.
cd core/build
cmake .. # If you want to add or delete files, you need to execute them again after you modify CMakeLists.txt.
make -sj$(nproc) # Start from here after each update of core code. 

11
12

5. Obtain Compilation Output

Since VS Code directly mounts the code library directory to the image, the host can access the compilation output in the image.

13

Here, if the requirements are not high, they can be finished. If you are careful, you must have found a problem. The files generated in the container are all root permissions on the host and must execute sudo chown -R $USER for repairing.

Develop with Dockerfile

Is there any way to automatically adapt the permission in the container to the host? It is not difficult for VS Code to solve this problem. The Remote Development plug-in allows us to use Dockerfile to develop in a container, which means we can use Docker to build a development image before starting the development container. This allows us to correct the permissions of accounts in the container.

The correction works like this:

  1. Before the Docker build of the Remote Development plug-in, expose the account name, account ID, group name, and group ID of the development machine to Docker.
  2. Use the account information to correct the container execution account and file permissions in the container during the Docker build.

Next, let's move to the actual operation.

1. Modify the .devcontainer.json Configuration File

In the configuration file, change the image section to the build section and use Dockerfile to start the development container. At the same time, add initializeCommand to expose the account information to Docker before building the image.

{
  "build": {
    "dockerfile": "Dockerfile",
    "args": {
      "USERNAME": "${localEnv:USER}"
    }
  },
  "initializeCommand": ".devcontainer/gen_env.sh",
  "onCreateCommand": "sudo chown -R $(id -un):$(id -gn) /root",
  "customizations": {
    "vscode": {
      "extensions": [
        "golang.Go",
        "ms-vscode.cpptools-extension-pack"
      ]
    }
  }
}

2. Create a Dockerfile

Use the compiled image as the base image and write a Dockerfile to correct the account and file permissions in the image.

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 /opt/logtail $(eval echo ~$USERNAME); \
    chmod 755 $(eval echo ~$USERNAME);
USER $USERNAME

COPY .env /tmp/.env copies the account information of the host to the container in the form of files.

The next few lines create the corresponding accounts in the container based on this information.

echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME; Grant the user sudo premission.

The lines of chmod and chown are used to correct the file permissions so the new user has permission to read and write the corresponding directory.

We must correct the HOME(~$USERNAME) directory here. Otherwise, the VS Code Sever will not have permission to install, failing to start the plug-in.

3. Create a Script to Expose Host Account Information

The content of the gen_env.sh script is listed below. This script is also compatible with macOS.

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

After the first three steps are completed, the configuration directory in Workspace should have three files like this:

14

4. Run and See the Effect

Press Shift + Command + P (macOS) or Ctrl + Shift + P (Windows) to open the command panel, enter reopen, and select Remote-Containers: Rebuild Container.

15

Re-execute the id command in the container to view the account information. We can find that account information is the same as the development machine.

16

Re-execute the previous compilation command in the container. Then, view the generated file permissions on the development machine. We can see that the files generated in the container have become the correct permissions on the development machine.

17

Debug in the Container

In addition to compiling code, another important function of the development environment is local debugging. Open a unit test file of the iLogtail plug-in, set the power off, and click debug test.

18

What?

Failed to launch: could not launch process: fork/fork/exec ...: operation not permitted.

Something went wrong!

19

After looking up data, it turned out that the default security policy of Docker uses the Secure computing mode (seccomp) to only allow whitelist system calls. System calls required by debug were rejected. We will try to add a line of "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ] to the configuration file to disable the function.

{
  "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"
      ]
    }
  }
}

After rebuilding the container, we tried the debugging function again. It works!

20

Summary

So far, we can develop in the remote container smoothly through the Remote Development plug-in of VS Code. In addition, the compiled images and plug-in configuration files used are portable and repeatable. After continuously integrating into the code library, it can be used by any developer. All the code mentioned in the article is available in the GitHub repository of iLogtail.

There are still many functions of the Remote Development plug-in that are not used in this article. Interested readers can study and explore them according to the references at the end of this article.

References

  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
0 0 0
Share on

Alibaba Cloud Community

1,076 posts | 263 followers

You may also like

Comments