Streamline your Workflow with VSCode Dev Containers & Red Hat Images
Tags: vscode, python, tutorial
Published: 2023-11-28 15:56:19

In this tutorial I’ll describe how I set up my dev environment using VSCode Dev Containers, Python/Poetry/FastAPI/PyTest and Red Hat Base Images.

VSCode’s Dev Containers offer a seamless way to manage project environments. By containerizing your development environment, you achieve a consistent setup across different machines, avoiding the dreaded “it works on my machine” syndrome. This approach keeps your primary OS clean from various project dependencies and ensures your development environment closely resembles the production setup, reducing deployment surprises.

## Installing Dev Containers

First, install Docker and the Dev Containers extension in VSCode. If you use Podman, set the Docker path to your podman CLI path:


![Specify the podman CLI path. You can determine the path with 'which podman'](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nzbpbtqaqyhjitmdikjs.jpeg) _Specify the podman CLI path. You can determine the path with `which podman`_

## Setting up the Dev Container

To setup Dev Containers, create a `.devcontainer/devcontainer.json` file:

```json
{
    "build": {
        "context": "..",
        "dockerfile": "./Dockerfile"
    },
    "workspaceMount": "source=${localWorkspaceFolder},target=/opt/app-root/src/workspace/,type=bind",
    "workspaceFolder": "/opt/app-root/src/workspace/",
    "customizations": {
        "vscode": {
            "settings": {
                "python.defaultInterpreterPath": "/opt/app-root/bin/python",
                "python.formatting.autopep8Path": "/opt/app-root/bin/autopep8",
                "python.linting.pylintPath": "/opt/app-root/bin/pylint"
            },
            "extensions": [
                "ms-python.python",
                "ms-python.vscode-pylance",
                "ms-python.pylint",
                "ms-python.autopep8",
                "ms-azuretools.vscode-docker"
            ]
        }
    },
    "postCreateCommand": "poetry install --no-interaction --no-ansi --no-root --with dev",
    "runArgs": [
        "--env-file",
        ".env"
    ],
    "forwardPorts": [
        4000
    ]
}
```

This file specifies the container build context, workspace config and essential Python settings. The `postCreateCommand` ensures all Poetry dependencies are installed when the container is created. `runArgs` loads environment variables from a local *.env* file and `forwardPorts` allows access to services running in the container.

Next, let’s explore the `.devcontainer/Dockerfile` to understand how the container is built:

```Dockerfile
FROM registry.access.redhat.com/ubi9/python-311:latest

# Install terraform & oh-my-zsh
USER root
RUN yum install -y yum-utils && \
    yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo && \
    yum -y install terraform zsh && \
    wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O - | zsh
USER 1001

# Install poetry
RUN pip install poetry && \
    poetry config virtualenvs.create false

# Set default shell to zsh
ENV SHELL /bin/zsh

# Create workspace dir used by VSCode
RUN mkdir /opt/app-root/src/workspace/

# Expose ports
EXPOSE 4000
```

As a base container I use Red Hat’s UBI Python image to provide a stable and secure foundation, which brings my dev environment to production. Furthermore, I install Terraform, Oh-My-Zsh and Poetry as a package manager for Python.

In your `pyproject.toml` file, specify your dependencies, pytest config etc:

```toml
[tool.poetry.dependencies]
...

[tool.poetry.dev-dependencies]
autopep8 = "^2.0.4"
pylint = "^3.0.2"
pytest = "^7.4.3"
...

[tool.pytest.ini_options]
python_files = "*_tests.py"
testpaths = ["lib", "tests"]
```


## Running the Dev Container

Now, let’s open the VSCode project in your container:


![Running the VSCode project inside the dev container](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74h95f14sc4itgu23xsl.jpg) _Running the VSCode project inside the dev container_


## Run, Debug & Test Integration

Next, we can further customize our VSCode setup by enabling linting, pytest-integration and auto-format. Therefore, you can either extend the `devcontainer.json` or create a `.vscode/settings.json` file:

```json
{
    "editor.formatOnSave": true,
    "python.linting.enabled": true,
    "python.testing.pytestEnabled": true,
    "python.testing.unittestEnabled": false,
    "python.editor.defaultFormatter": "ms-python.autopep8"
}
We can also specify how VSCode can launch & debug our python app, for example for FastAPI we can use this vscode/launch.json configuration:
{
    "configurations": [
        {
            "name": "Start API",
            "type": "python",
            "request": "launch",
            "module": "uvicorn",
            "args": [
                "main:app",
                "--port",
                "4000",
                "--reload"
            ],
            "justMyCode": true
        }
    ]
}
```

With the setup now complete, you can efficiently run and test your FastAPI project directly from the IDE. This integration offers the convenience of streamlined debugging and testing, enhancing your dev workflow:

![Running & debugging the application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4uvpo8gg9olspzo0d03g.jpeg) _Running & debugging the application_


![Running unit tests with pytest](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wtkvcve6o2r1jqzughxa.jpeg) _Running unit tests with pytest_