(2)打造小众软件的巅峰:devcontainer
Source: Dev.to
请提供您想要翻译的具体文本内容(除代码块和 URL 之外),我将按照要求将其翻译为简体中文并保留原有的格式。
Overview
我更倾向于使用超稳定、可共享且可复现的环境,因此我的首选方案是 devcontainer — 请参阅官方文档。
直到最近,我一直把 .devcontainer 文件夹视为项目的固有部分,里面只包含唯一的“真实”开发容器设置。开发者可以通过 dotfiles 带入自己的自定义配置。
我的观点已经转变。现在,我在 .devcontainer 中添加一个以我自己名字命名的文件夹,让每位开发者自行决定如何扩展它。
下面是我目前最喜欢的 Elm 开发环境设置,适用于我之前帖子中描述的技术栈。它涉及多个移动部件,请耐心阅读!
构建基础容器
构建基础容器(根据我的需求定制)的仓库是:
注意: 该名称特定于我的工作流,但欢迎借鉴。
devcontainer 设置
文件夹结构
project-root/
├── .devcontainer/
│ ├── vanilla/ # Basic example
│ └── theodor/ # Personal environment config
│ ├── ssl/
│ │ ├── squidex.crt
│ │ └── squidex.key
│ ├── devcontainer.json
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── Dockerfile.omnia
│ └── nginx.conf
├── backend/
├── backup/
├── build/
├── Documentation/
├── dotnet/
└── elm/
我正在考虑将 theodor 添加到 .gitignore,但尚未决定。
注释
- 此设置相当高级!
- 在本地使用自定义域名——而不是
localhost:port。 - 在类似生产环境的配置下运行 nginx。
- 通过 nginx 和 site‑config‑loader 工具设置环境变量。
devcontainer.json
{
"name": "Dotnet 9/10 and Elm Dev Container (Debian + Compose)",
"dockerComposeFile": "docker-compose.yml",
"service": "dev",
"workspaceFolder": "/workspace",
"mounts": [
"source=ktk-elm-devcontainer,target=/home/container-user/.elm,type=volume"
],
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh"
},
"extensions": [
"ms-vscode-remote.remote-containers",
"Elmtooling.elm-ls-vscode",
"ms-dotnettools.csharp",
"william-voyek.vscode-nginx",
"vscodevim.vim",
"ms-dotnettools.csdevkit",
"EditorConfig.EditorConfig",
"humao.rest-client",
"esbenp.prettier-vscode",
"DotJoshJohnson.xml",
"streetsidesoftware.code-spell-checker",
"streetsidesoftware.code-spell-checker-danish",
"bradlc.vscode-tailwindcss",
"kamikillerto.vscode-colorize",
"Ionide.Ionide-fsharp",
"ms-azuretools.vscode-containers",
"jebbs.plantuml",
"task.vscode-task",
"ecmel.vscode-html-css"
]
}
},
"remoteUser": "container-user",
"portsAttributes": {
"3033": { "label": "Elm" },
"5130": { "label": "Backend" },
"5140": { "label": "Gateway" },
"8314": { "label": "Nginx" },
"8376": { "label": "Squidex" },
"9876": { "label": "Elm" }
}
}
docker-compose.yml
services:
# 主要开发服务
dev:
# 为此服务构建镜像
build:
context: .
dockerfile: Dockerfile
# 要挂载的卷
volumes:
- ../..:/workspace:cached
- ktk-elm-devcontainer:/home/container-user/.elm
# --- Neovim 配置的替代方案 ---
# - ../.nvim:/root/.config/nvim:cached
# --- 添加此行 ---
# - ./.config/nvim:/root/.config/nvim:cached
# command: ["sleep", "infinity"]
保持容器运行
command: sleep infinity
networks:
- internal
服务
nginx:
image: nginx:alpine
container_name: ktk_nginx
ports:
- "80:80"
- "443:443"
- "8314:80"
networks:
- internal
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
- dev
- squidex
mongo:
image: "mongo:6"
volumes:
- ktk_mongo_data:/data/db
networks:
- internal
restart: unless-stopped
squidex:
image: "squidex/squidex:7"
ports:
- "8376:5000"
environment:
- URLS__BASEURL=https://squidex.ktk.dk
- IDENTITY__ALLOWHTTPSCHEME=false
- EVENTSTORE__MONGODB__CONFIGURATION=mongodb://mongo
- STORE__MONGODB__CONFIGURATION=mongodb://mongo
- IDENTITY__ADMINEMAIL=sukkerfrit@gmail.com
- IDENTITY__ADMINPASSWORD=0hSoS3cret!
- ASPNETCORE_URLS=http://+:5000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/healthz"]
start_period: 60s
depends_on:
- mongo
volumes:
- ktk_squidex_assets:/app/Assets
networks:
- internal
restart: unless-stopped
卷
volumes:
ktk-elm-devcontainer:
ktk_squidex_assets:
ktk_mongo_data:
网络
networks:
internal:
driver: bridge
Dockerfile
FROM isuperman/elm-devcontainer-foundation:0.1.7
# Project specifics could be added here
生产环境般的本地开发
此配置让您能够测试路由、陷阱(tarpit) 等功能。使用自定义域名进行本地开发,使环境与生产环境非常接近。
nginx.conf
worker_processes 1;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
resolver 127.0.0.11;
server {
listen 80;
server_name localhost ktk.dk;
location /api/ {
set $backend_upstream host.docker.internal:5130;
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://$backend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /cms/ {
set $backend_upstream host.docker.internal:5140;
rewrite ^/cms/(.*)$ /$1 break;
proxy_pass http://$backend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location / {
set $frontend_upstream host.docker.internal:3033;
proxy_pass http://$frontend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
sub_filter '' '';
sub_filter_once on;
}
location = /robots.txt {
set $frontend_upstream host.docker.internal:3033;
proxy_pass http://$frontend_upstream/robots.txt;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
location = /humans.txt {
set $frontend_upstream host.docker.internal:3033;
proxy_pass http://$frontend_upstream/humans.txt;
proxy_http_version 1.1;
proxy_set_header Host $host;
}
}
server {
listen 80;
server_name squidex.ktk.dk;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name squidex.ktk.dk www.squidex.ktk.dk;
ssl_certificate /etc/nginx/ssl/squidex.crt;
ssl_certificate_key /etc/nginx/ssl/squidex.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://squidex:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_cookie_path / /;
}
}
}
结论
本示例展示了如何使用 devcontainer 来创建一个与生产环境相匹配的开发环境。通过在本地 docker‑compose.yml 中使用这些设置,我在将容器推送到生产环境时非常有信心——生产环境中的 bug 已经变得极为罕见。
接下来我们将重点介绍如何设置 vite-plugin-elm-watch,它将…