Kontener deweloperski z z pluginem vscode devcontainers

Przygotowanie własnego obrazu

W pracy nad wieloma projektami infrastrukturalnymi (OpenTofu, Ansible, Vault, GitLab CI) szybko pojawia się ten sam problem: jak zapewnić powtarzalne, gotowe do pracy środowisko developerskie, bez ręcznego konfigurowania systemu na każdym laptopie lub VM.

Rozwiązaniem, które sprawdza się w praktyce, jest Dev Container w Visual Studio Code, uzupełniony o zewnętrzny katalog /userfiles z konfiguracją użytkownika oraz automatyczny bootstrap wykonywany przy starcie kontenera.

W tym wpisie pokażę, jak zbudować takie rozwiązanie krok po kroku.


Założenia architektoniczne

Celem jest środowisko, które:

  • działa identycznie na każdym hoście,

  • nie wymaga kopiowania kluczy SSH ani dotfiles do repozytorium,

  • automatycznie:

    • instaluje zaufane CA (Vault PKI),
    • konfiguruje .zshrc, Git, SSH,
    • przygotowuje użytkownika do pracy od razu po otwarciu repozytorium.

Kluczowe elementy:

  • Dev Container oparty o własny obraz Dockera,
  • bind-mount katalogu /userfiles,
  • postCreateCommand jako punkt wejścia do bootstrapu.

Struktura /userfiles

Na hoście (lub w VM) utrzymywany jest katalog:

/userfiles
├── bootstrap.bash
├── configure_user.bash
├── .zshrc
├── .gitconfig
├── .gitignore_global
└── .ssh/
    ├── id_ed25519
    ├── id_ed25519.pub
    └── known_hosts

To jest źródło prawdy dla konfiguracji użytkownika – niezależne od repozytoriów i kontenerów.


Konfiguracja devcontainer.json

Minimalna i stabilna konfiguracja wygląda następująco:

{
  "name": "Devcontainer",
  "image": "registry.rachuna-net.pl/pl.rachuna-net/containers/devcontainer:1.0.0",
  "remoteUser": "ubuntu",
  "postCreateCommand": "bash -lc '/userfiles/bootstrap.bash'",
  "mounts": [
    "source=/userfiles,target=/userfiles,type=bind"
  ]
}

Najważniejsze decyzje:

  • /userfilesln -s .devcontainer/userfiles w repozytorium kodu,
  • postCreateCommand uruchamiany jawnie przez bash -lc,
  • brak logiki konfiguracyjnej w samym repozytorium.

Bootstrap – jeden punkt wejścia

Plik bootstrap.bash odpowiada za konfigurację systemową.

#!/usr/bin/env bash
set -euo pipefail

echo "===> Run bootstrap script"

echo "===> Install Vault CA certificates"
sudo curl -ks https://vault.localhost/v1/pki-root/ca/pem -o /usr/local/share/ca-certificates/ca.crt

sudo update-ca-certificates

bash -lc '/userfiles/configure_user.bash'

Dlaczego tak?

  • brak interaktywnych shelli (zsh, bash bez -c),

  • jednoznaczny podział:

    • bootstrap → system,
    • configure_user → użytkownik.

Konfiguracja użytkownika

configure_user.bash zajmuje się wyłącznie $HOME.

#!/usr/bin/env bash
set -euo pipefail

echo "===> Configure user profile"

cp /userfiles/.zshrc "$HOME/.zshrc" || true
cp /userfiles/.gitconfig "$HOME/.gitconfig" || true
cp /userfiles/.gitignore_global "$HOME/.gitignore_global" || true

mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
cp -a /userfiles/.ssh/. "$HOME/.ssh/" || true
chmod 600 "$HOME/.ssh"/* 2>/dev/null || true

Efekt:

  • klucze SSH dostępne w kontenerze,
  • Git skonfigurowany globalnie,
  • brak duplikowania sekretów w repozytorium.

Efekt końcowy

Po otwarciu repozytorium w VS Code:

  • kontener buduje się automatycznie,
  • CA z Vaulta są zaufane,
  • Git i SSH działają od razu,
  • developer może od razu pracować, bez setupu lokalnego.

To podejście sprawdza się szczególnie dobrze w zespołach DevOps / Platform Engineering, gdzie:

  • środowiska muszą być powtarzalne,
  • bezpieczeństwo (PKI, klucze) ma znaczenie,
  • repozytoria nie powinny zawierać konfiguracji użytkownika.
Last modified December 19, 2025: docs: Update (72bba37)