Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
How to create a
Devcontainer for your
Python project 🐳
How to create a Devcontainer for your Python project
How to create a Devcontainer for your Python project
The use case
Youareassignedtosetupanewrepoforateam.Therequirementsareasfollows:
Soweneedtoalignon:
📌 AspecificJavaversion
📌 AspecificPythonversion
📌 Aspecific pyspark version
→otherwisewedonotenjoytheguaranteeswewantinproductioncode
How to create a Devcontainer for your Python project
Devcontainers to the rescue ⛑!
Dockerhelpsuscreateaformaldefinitionofourenvironment.Devcontainersallowyouto
connectyoureditor(IDE)tothatcontainer.
📝 NotethatthisdoesmeanrunningDockerimagesonyourlaptop(performance
requirement).
Devcontainerscanhelpus:
🔄 Reproducibledevelopmentenvironment
⚡️ Fasterprojectsetup→fasteronboarding
👨‍👩‍👧‍👦 Betteralignmentbetweenteammembers
⏱ Forcedtokeepyourdevenvironmentup-to-date&reproducible
→savesyourteamtimegoingintoproductionlater
👷🏻‍♂️Let's build a Devcontainer!
Let’ssaywehaveareallysimpleprojectthatlookslikethis:
$ tree .
.
├── README.md
├── requirements.txt
├── requirements-dev.txt
├── sales_analysis.py
└── test_sales_analysis.py
The .devcontainer folder
YourDevcontainerspecwillliveinsidethe .devcontainer folder.
Therewillbetwomainfiles:
devcontainer.json
Dockerfile
Createanewfilecalled devcontainer.json :
{
"build": {
"dockerfile": "Dockerfile",
"context": ".."
}
}
Sohowdoesthis Dockerfile looklike?
FROM python:3.10
# Install Java
RUN apt update && 
apt install -y sudo && 
sudo apt install default-jdk -y
## Pip dependencies
# Upgrade pip
RUN pip install --upgrade pip
# Install production dependencies
COPY requirements.txt /tmp/requirements.txt
RUN pip install -r /tmp/requirements.txt && 
rm /tmp/requirements.txt
# Install development dependencies
COPY requirements-dev.txt /tmp/requirements-dev.txt
RUN pip install -r /tmp/requirements-dev.txt && 
rm /tmp/requirements-dev.txt
Opening the Devcontainer
The .devcontainer folderinplace,nowit’stimetoopenourDevcontainer.
Openupthecommandpallete( CMD + Shift + P )andselect“DevContainers:Reopen
inContainer”:
Uponopeningarepowithavalid .devcontainer folder,youarealreadynotified:
YourVSCodeisnowconnectedtotheDockercontainer🙌🏻:
What is happening under the hood 🚗
BesidesstartingtheDockerimageandattachingtheterminaltoit,VSCodeisdoinga
couplemorethings:
1. isbeinginstalledonyourDevcontainer.
VSCodeServerisinstalledasaserviceinthecontaineritselfsoyourVSCode
installationcancommunicatewiththecontainer.
Forexample,installandrunextensions.
VSCodeServer
2.Configiscopiedover.
Configlike ~/.gitconfig and ~/.ssh/known_hosts arecopiedovertotheir
respectivelocationsinthecontainer.
3.Filesystemmounts.
VSCodeautomaticallytakescareofmounting:
ThefolderyouarerunningtheDevcontainerfrom.
YourVSCodeworkspacefolder.
Opening your Devcontainer with the click of a button
YourentireprojectsetupisnowencapsulatedintheDevcontainer.Soactuallywecanadd
aMarkdownbuttontoopenuptheDevcontainer:
JustmodifytheGitHubURLafter url= ✓.
[
![Open in Remote - Containers](
https://img.shields.io/static/v1?label=Remote%20-
%20Containers&message=Open&color=blue&logo=visualstudiocode
)
](
https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?
url=https://github.com/godatadriven/python-devcontainer-template
)
Thisrendersthefollowingbutton:
Remote - Containers
Remote - Containers Open
Open
WhatkindofREADMEwouldyouratherlike?
Manualinstallation UsingaDevcontainer🙌🏻
How to create a Devcontainer for your Python project
Extending the Devcontainer
WehavebuiltaworkingDevcontainer,thatisgreat!Butacouplethingsarestillmissing.
Installanon-rootuserforextrasafetyandgood-practice
PassincustomVSCodesettingsandinstallextensionsbydefault
BeabletoaccessSparkUI(openingupport4040)
RunContinuousIntegration(CI)intheDevcontainer
Let'sseehow.
Installing a non-root user
Ifyou pip install anewpackage,youwillseethefollowingmessage:
Solet'sgoaheadandcreateauserforthisscenario.
# Add non-root user
ARG USERNAME=nonroot
RUN groupadd --gid 1000 $USERNAME && 
useradd --uid 1000 --gid 1000 -m $USERNAME
## Make sure to reflect new user in PATH
ENV PATH="/home/${USERNAME}/.local/bin:${PATH}"
USER $USERNAME
Addthefollowingpropertyto devcontainer.json :
"remoteUser": "nonroot"
That'sgreat!Whenwenowstartthecontainerweshouldconnectastheuser nonroot .
Passing custom VSCode settings
"customizations": {
"vscode": {
"extensions": [
"ms-python.python"
],
"settings": {
"python.testing.pytestArgs": [
"."
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.formatting.provider": "black",
"python.linting.mypyEnabled": true,
"python.linting.enabled": true
}
}
}
Accessing Spark UI
Sinceweareusingpyspark,itwouldbenicetobeabletoaccessSparkUI.
"portsAttributes": {
"4040": {
"label": "SparkUI",
"onAutoForward": "notify"
}
},
"forwardPorts": [
4040
]
Whenwenowrunourcode,wegetanotificationwecanopenSparkUIinthebrowser:
ResultingintheSparkUIlikeweknowit:
✨
Running our CI in the Devcontainer
Therearetwobasicoptions:
1.BuildtheDockerimagewithintheCI/CDpipeline
2.Prebuildingtheimage
Let'sseeaboutoptionnumber(1).
1. Build the Docker image within the CI/CD pipeline
Luckily,aGitHubActionwasalreadysetupforustodoexactlythis:
devcontainers/ci
Tonowbuild,pushandrunacommandintheDevcontainerisaseasyas:
name: Python app
on:
...
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout (GitHub)
uses: actions/checkout@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and run dev container task
uses: devcontainers/ci@v0.2
with:
imageName: ghcr.io/${{ github.repository }}/devcontainer
runCmd: pytest .
SeebelowatraceoftheexecutedGitHubAction:
Awesome!
The final Devcontainer definition
WebuiltthefollowingDevcontainerdefinitions.
First, devcontainer.json :
{
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"remoteUser": "nonroot",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python"
],
"settings": {
"python.testing.pytestArgs": [
"."
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.formatting.provider": "black",
"python.linting.mypyEnabled": true,
"python.linting.enabled": true
}
}
},
...
}
"portsAttributes": {
"4040": {
"label": "SparkUI",
"onAutoForward": "notify"
}
},
"forwardPorts": [
4040
]
Andour Dockerfile :
FROM python:3.10
# Install Java
RUN apt update && 
apt install -y sudo && 
sudo apt install default-jdk -y
# Add non-root user
ARG USERNAME=nonroot
RUN groupadd --gid 1000 $USERNAME && 
useradd --uid 1000 --gid 1000 -m $USERNAME
## Make sure to reflect new user in PATH
ENV PATH="/home/${USERNAME}/.local/bin:${PATH}"
USER $USERNAME
## Pip dependencies
# Upgrade pip
RUN pip install --upgrade pip
# Install production dependencies
COPY --chown=nonroot:1000 requirements.txt /tmp/requirements.txt
RUN pip install -r /tmp/requirements.txt && 
rm /tmp/requirements.txt
# Install development dependencies
COPY --chown=nonroot:1000 requirements-dev.txt /tmp/requirements-
dev.txt
RUN pip install -r /tmp/requirements-dev.txt && 
rm /tmp/requirements-dev.txt
Three environments 🎁
Theaboveideasetsusupforhaving3differentimagesforourentirelifecycle.Onefor
Development,oneforCI,andfinallyoneforproduction.
Going further 🔮
Devcontainerfeatures
Devcontainertemplates
💡 Protip:mountyourAWS/GCP/Azurecredentials
...andmuchmore:
Mountingdirectories
Awesome resources
.RunyourCIinyourDevcontainers.Builtonthe .
.TheofficialDevcontainerspecification.
.Acollectionofready-to-useDevcontainerimages.
.Moreexplanations&instructionsforaddinga
non-rootusertoyour Dockerfile and devcontainer.json .
.Arepopointingtoyetevenmoreawesomeresources.
devcontainers/ci DevcontainerCLI
https://containers.dev/
devcontainers/images
Addanon-rootusertoacontainer
Pre-buildingdevcontainerimages
awesome-devcontainers
Concluding
Devcontainershaveprovenusefulto:
🔄 Reproducibledevelopmentenvironment
⚡️ Fasterprojectsetup→fasteronboarding
👨‍👩‍👧‍👦 Betteralignmentbetweenteammembers
⏱ Forcedtokeepyourdevenvironmentup-to-date&reproducible
→savesyourteamtimegoingintoproductionlater
NowonlyVSCode,but takingshape.
openspecification
Thanks! 🙌🏻
Repo:godatadriven/python-devcontainer-template

More Related Content

How to create a Devcontainer for your Python project