--- tags: Docker,Docker Compose,DevOps,Visual Studio,stonehenge --- # 使用druidfi/stonehenge,實現 https + Domain Name 在Windows本機開發網路應用 撰寫者 : Cymon Dez 撰寫時間 : 2025-09-11 ## 前言 筆者曾於2020年時撰寫過[使用 SSL 與 Domain Name 在Windows本機開發後端應用](https://hackmd.io/@sf_cGmuJRQS2k1Z3FHd-zA/S1WqDTifP)一文。 隨著時間推移,如今(2025)已經有良好的開源套件[druidfi/stonehenge](https://github.com/druidfi/stonehenge)可以輕鬆搭建此功能的開發環境,故決定寫一篇在Windows10/11 wsl2環境下,使用`stonehenge`搭建能夠使用SSL+Domain的本地開發環境。 ## 目標 1. 本機上的專案(前端、後端均可)可以使用`SSL` + `Domain Name`進行測試時 2. 80,443 port 的複用,減少port衝突 3. 減少修改 `hosts`檔案的麻煩 4. 使用 `VS2022`整合`docker-compose` debug環境 ### 適用場景 1. 專案容器化開發 2. 產品使用容器部屬 ### 建置環境 以下是本篇蚊帳撰寫時,所使用到的系統以及工具版版本 + `Win10` 22H2 + `powershell` 7.5.2 + `wsl2` 2.5.10.0 with `distribution Ubuntu-20.04` + `Docker Desktop` 4.45.0 + `docker` 28.3.3 + `docker compose` v2.39.2-desktop.1 + `Visual Studio 2022` 17.14.11 + `vscode` 1.103.2 + `druidfi/stonehenge` 5.1.0 + `traefik` v3.5.0 + `Mailpit` v1.27.7 ## stonehenge ### 簡介 stonehenge是一套藉由traefik + mkcert 實現本地https domain name開發環境的一套開源專案。 套件(5.x)由以下幾項整合 + traefik:具備服務發現器與自動憑證申請的新一代服務發現器,與docker的相容性良好 + mkcert:產生本機測試用途之可信任的SSL檔 + Mailpit:email快取,用於測試專案的郵件發送機制 ### 安裝前檢查 1. 請先確認您的Windows10/11環境,已經安裝好`wsl2`與`docker` 2. 確認套件stonehenge預設會使用到的port未被其他服務占用,以下列出所需要的port以及功能: 1. 80 port: http 2. 443 port: https 3. 1025 port: Mailpit ### 詳細安裝 (與快速安裝則一) 1. 開啟`powershell`,在`wsl2`下安裝`build-essential` ```ps wsl sh -c "sudo apt update && sudo apt upgrade && sudo apt install build-essential" ``` 2. git clone stonehenge專案 ```ps wsl git clone -b 5.x https://github.com/druidfi/stonehenge.git ~/stonehenge ``` 3. 如果需要調整安裝參數,請進到stonehenge/.env裡進行設定 這邊使用vscode修改,您也可以將code改為nano或vim ```ps wsl code ~/stonehenge/.env ``` 可修改的參數如下: + `DOCKER_DOMAIN`:反向代理的root domain,預設值是`docker.so`,其原理是將`*.docker.so`指向`127.0.0.1`;如果想修改成自己單位使用的domain,請記得將設定的domain指向`127.0.0.1` | type | domain | ip address | | :--: | :--------------------------: | :--------: | | A | <your_organization_domain> | 127.0.0.1 | | A | *.<your_organization_domain> | 127.0.0.1 | + `HTTP_PORT`:http使用的port,預設值是80 + `HTTPS_PORT`:https使用的prot,預設值是443 + `SMTP_PORT`:smtp(email)使用的prot,預設值是1025 ```sh # Root domain for all local services DOCKER_DOMAIN=docker.so # Prefix for containers PREFIX=stonehenge # Stonehenge major version STONEHENGE_VERSION=5 # Image versions STONEHENGE_TAG=5.1 # http port ,預設是 80,原始檔案沒有,須自己加上 # HTTP_PORT=80 # https port ,預設是 443,原始檔案沒有,須自己加上 # HTTPS_PORT=443 # smtp port ,預設是 1025,原始檔案沒有,須自己加上 # HTTP_PORT=1025 ``` 4. 安裝 ```ps wsl make -s -C ~/stonehenge up ``` 5. 設定開發用憑證 ```ps $WSL_NAME = $(wsl sh -c 'echo $WSL_DISTRO_NAME') $WSL_USER = $(wsl whoami) Import-Certificate -Filepath \\wsl$\$WSL_NAME\home\$WSL_USER\stonehenge\certs\rootCA.pem -CertStoreLocation cert:\CurrentUser\Root ``` ### 快速安裝 (與詳細安裝則一) 如果打算都使用預設值,可以考慮使用作者撰寫的ps1直接安裝 ```ps iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/druidfi/stonehenge/5.x/install.ps1')) ``` ### 檢測 在瀏覽器上,輸入下列網址,如果能順裡開起,代表安裝與設定皆正常: + traefik 管理頁面 [traefik.docker.so](https://traefik.docker.so) + mailpit 管理頁面 [mailpit.docker.so](https://mailpit.docker.so) !!! 提示:如果安裝時有修改`DOCKER_DOMAIN`,請使用您定的domain加上服務前綴測試 如: traefik.<your_organization_domain> mailpit.<your_organization_domain> ### 試著佈署一個服務 - 使用 traefik/whoami traefik/whoami是一個輕量容器的Echo服務,功能如下: + 請求方法(GET、POST 等) + 請求的 Host 與 URL + HTTP Header + 來源 IP + TLS/HTTPS 相關資訊(如果有) 很適合用來測試traefik網路環境 1. 在你的專案資料夾下,建一個新資料夾`whoami` 2. 在`whoami`資料夾下,產生`compose.yml`檔並加入以下內容 ```yaml networks: # stonehenge 的traefik 所使用的docker network,於安裝時建立 stonehenge-network: external: true services: app: image: traefik/whoami:latest labels: # 要使用字串的方式才能套用到環境變數 # container啟用traefik反向代理功能 - "traefik.enable=true" # stonehenge 的traefik 所使用的docker network,於安裝時建立 - "traefik.docker.network=stonehenge-network" # ${COMPOSE_PROJECT_NAME} 會預設套用compose.yml所在的資料夾名稱,這樣可以盡量避免traefik route 名稱重複造成的衝突問題 - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.entrypoints=https" - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`whoami.docker.so`)" - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.tls=true" - "traefik.http.services.${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=80" networks: - default # 需要反向代理的服務才加入 stonehenge-network - stonehenge-network ``` 3. 啟動docker compose ```sh docker compose up -d ``` 4. 測試 使用wsl的curl,輸入`whoami.docker.so`,檢測是否能連線並自動重新導向到https ```sh wsl curl -vL --ssl-revoke-best-effort whoami.docker.so # -v: 顯示詳細訊息 # -L: 代表 follow redirect ``` 如果成功會看到以下訊息: ```sh * Host whoami.docker.so:80 was resolved. * IPv6: (none) * IPv4: 127.0.0.1 * Trying 127.0.0.1:80... * Connected to whoami.docker.so (127.0.0.1) port 80 > GET / HTTP/1.1 > Host: whoami.docker.so > User-Agent: curl/8.9.1 > Accept: */* > < HTTP/1.1 301 Moved Permanently < Location: https://whoami.docker.so/ < Date: Thu, 11 Sep 2025 16:35:00 GMT < Content-Length: 17 * Ignoring the response-body < * Connection #0 to host whoami.docker.so left intact * Clear auth, redirects to port from 80 to 443 * Issue another request to this URL: 'https://whoami.docker.so/' * Host whoami.docker.so:443 was resolved. * IPv6: (none) * IPv4: 127.0.0.1 * Trying 127.0.0.1:443... * Connected to whoami.docker.so (127.0.0.1) port 443 * schannel: disabled automatic use of client certificate * ALPN: curl offers http/1.1 * ALPN: server accepted http/1.1 * using HTTP/1.x > GET / HTTP/1.1 > Host: whoami.docker.so > User-Agent: curl/8.9.1 > Accept: */* > < HTTP/1.1 200 OK < Content-Length: 377 < Content-Type: text/plain; charset=utf-8 < Date: Thu, 11 Sep 2025 16:35:00 GMT < Hostname: 36529239ef8f IP: 127.0.0.1 IP: ::1 IP: 172.19.0.3 IP: 172.21.0.2 RemoteAddr: 172.19.0.2:59848 GET / HTTP/1.1 Host: whoami.docker.so User-Agent: curl/8.9.1 Accept: */* Accept-Encoding: gzip X-Forwarded-For: 172.19.0.1 X-Forwarded-Host: whoami.docker.so X-Forwarded-Port: 443 X-Forwarded-Proto: https X-Forwarded-Server: 68578e0bf36c X-Real-Ip: 172.19.0.1 * Connection #1 to host whoami.docker.so left intact ``` 在traefik dashboard中,應該能看見http route有新增了 whoami.docker.so這項 ![traefik-whoami](https://hackmd.io/_uploads/H1m2QFZogl.png) ### 範例參考 stonehenge專案有提供很多佈署相關的範例,可以到stonehenge專案的[examples](https://github.com/druidfi/stonehenge/tree/5.x/examples)查詢 ## 如何用於開發專案 ### `VS2022` 使用 `docker compose 支援` 詳細內容請參考[微軟官方教學](https://docs.microsoft.com/zh-tw/visualstudio/containers/tutorial-multicontainer?view=vs-2022) 1. 建立一個`asp.net core 8 MVC`專案 ![create-aspnetcore-mvc-porj](https://hackmd.io/_uploads/BJk57YWoll.png) ![create-aspnetcore-mvc-porj-settings](https://hackmd.io/_uploads/ByO5mFZigg.png) 2. 加入`docker compose 支援` 專案上按右鍵,選擇 [ 加入 > 容器協調器支援]。 [ Docker 支援選項 ] ![container-supports](https://hackmd.io/_uploads/SJMeEFZixl.png) 對話框出現後,選擇 [ Docker Compose]。 ![docker-compose-supports](https://hackmd.io/_uploads/HJiVXt-sle.png) 3. 設定`docker-compose.override.yml`,加入`stonehenge`的網路設定 docker-compose.override.yaml ```yaml services: webapplication_stonehege_demo: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_HTTP_PORTS=8080 ports: - "8080" networks: - default - traefik-proxy labels: # 要使用字串的方式才能套用到環境變數 # container啟用traefik反向代理功能 - "traefik.enable=true" # stonehenge 的traefik 所使用的docker network,於安裝時建立 - "traefik.docker.network=stonehenge-network" # ${COMPOSE_PROJECT_NAME} 會預設套用compose.yml所在的資料夾名稱,這樣可以盡量避免traefik route 名稱重複造成的衝突問題 - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.entrypoints=https" - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`webapp.docker.so`)" - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.tls=true" - "traefik.http.services.${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=8080" networks: traefik-proxy: name: stonehenge-network external: true ``` 請確認檔案是以UTF-8編碼儲存,不然啟動時會遇到 `yaml: invalid leading UTF-8 octet`這個錯誤 ![yaml_invalid_leading_UTF-8_octet](https://hackmd.io/_uploads/rk3-7tZsxg.png) 4. 修改`docker-compose.dcproj` ```xml <?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk"> <PropertyGroup Label="Globals"> <ProjectVersion>2.1</ProjectVersion> <DockerTargetOS>Linux</DockerTargetOS> <DockerPublishLocally>False</DockerPublishLocally> <ProjectGuid>81dded9d-158b-e303-5f62-77a2896d2a5a</ProjectGuid> <DockerLaunchAction>LaunchBrowser</DockerLaunchAction> <!--將DockerServiceUrl修改改成 與override中相同的網址--> <DockerServiceUrl>https://webapp.docker.so</DockerServiceUrl> <DockerServiceName>webapplication_stonehege_demo</DockerServiceName> </PropertyGroup> <ItemGroup> <None Include="docker-compose.override.yml"> <DependentUpon>docker-compose.yml</DependentUpon> </None> <None Include="docker-compose.yml" /> <None Include=".dockerignore" /> </ItemGroup> </Project> ``` 5. `F5`啟動專案測試 如果設定正確 `vs2022`會啟動瀏覽器 ![vs2022-debug-browser](https://hackmd.io/_uploads/Byck7KZsll.png) `traefik dashboard` 上 `http` `route`會看到此專案的路由 ![vs2022-debug-traefik](https://hackmd.io/_uploads/S1i9MFZoxg.png) ## 結語 透過 `stonehenge`,Windows 本機開發環境能夠輕鬆實現 SSL + Domain Name 的需求,並且大幅簡化設定流程。無論是前端或後端專案,都能享受一致且安全的本地測試體驗。`stonehenge` 讓容器化開發更貼近正式環境,減少 port 衝突與 hosts 檔案修改的困擾,也方便團隊協作與 CI/CD 部署。