2019 年 5 月

第 34 卷,第 5 期

[資料點]

Docker 容器化應用程式中的 EF Core,第 2 部

藉由Julie Lerman

Julie Lerman在上個月的資料行 (msdn.com/magazine/mt833405),我建立了要在 Docker 容器內執行的 ASP.NET Core API 專案。專案會使用 EF Core 來保存資料。在該資料行中,我一開始先使用 SQL Server LocalDB,但因為目前的設定會假設 SQL Server LocalDB 存在於容器內的時間來執行 Docker 容器內的應用程式時,按 [但是。做為快速的解決方案,我切換到使用 SQLite,會在容器內建置新的專案,有安裝。使用此方法時,您可以看到完整的解決方案,在偵錯期間成功。

切換至 SQLite 是只要立即同步處理的方法。讓我們繼續了解適當方面的解決方案,針對 SQL Server 發行的 API 內的 Docker 映像時的旅程圖。本專欄將著重於目標一致可用的 Azure SQL Database 可從任何地方。

指向 Azure SQL Database 中的 API

我的起點是我將它保留在我之前在專欄結尾處的解決方案。如果您還沒,讀到尚未,您可能想要那里啟動內容。在本文中,我將持續改進該方案逐位元。

雖然 EF Core 可以為您建立的資料庫在 Azure SQL Database 上,SQL Server 必須預先存在。我已經有幾個 SQL 伺服器上設定 Azure 中,因此我將使用其中一個,此資料行的示範。

首先,我將在其中新增新的連接字串,名為 MagsConnectionAzSql — 指向該伺服器 — 到 appsettings.json,其中已包含我在第 1 部分中使用的連接字串。雖然 JSON 不接受傳回已包裝的長的行,以提高可讀性。我也包含假的認證:

"ConnectionStrings": {
  "MagsConnectionMssql":"Server=(localdb)\\mssqllocaldb;
    Database=DP0419Mags;Trusted_Connection=True;",
  "MagsConnectionSqlite": "Filename=DP0419Mags.db;",
  "MagsConnectionAzSql": "Server=tcp:msdnmaglerman.database.windows.net,1433;
    Initial Catalog=DP0419Mags;Persist Security Info=False;
    User ID=lerman;Password=eiluj;
    MultipleActiveResultSets=False;Encrypt=True;
    TrustServerCertificate=False;Connection Timeout=30;"

接下來,我要變更啟動檔案的 ConfigureServices 方法,若要使用 SQL Server 提供者和此連接字串中的 DbContext 設定:

services.AddDbContext<MagContext>(options =>   
  options.UseSqlServer(
    Configuration.GetConnectionString("MagsConnectionAzSql")));

這可讓 EF Core 移轉,以在設計階段尋找連接字串,所以我可以執行任何所需的命令。在執行階段,應用程式將能夠尋找連接字串,以及。事實上,因為我的最後一個工作是使用 SQLite,我需要重設移轉,也就是說,在我們的示範刪除 [移轉] 資料夾,並執行新增移轉 initSqlAz,若要取得資料庫的正確描述的移轉。

移轉一旦存在之後,我執行使用 Docker 設定檔的應用程式。在 program.cs 中的移轉方法是能夠在雲端中建立資料庫,而且控制器能夠進行查詢,這會證明我 — 全都從 Docker 容器內 — 如預期般運作。第一次應用程式執行移轉,並必須建立資料庫,預期會在短暫的延遲。完成時,不僅不瀏覽器轉送三個的雜誌,我植入資料庫 (在上一個資料行),作為**[圖 1**示,但是我可以看到列在 Azure 入口網站和 Visual Studio 的 SQL Server 資料庫物件總管] 中。

API 的輸出中,列出使用 DbContext 中植入定義的三個雜誌
[圖 1API 的輸出,列出使用 DbContext 中植入定義的三個雜誌

請注意,EF Core 所建立的資料庫的預設組態所設定的定價層:  標準 S0:10 個 Dtu。您可以變更,在入口網站或使用 Azure CLI 的生產應用程式時。事實上,用於生產環境,您可能想要明確地建立資料庫,以確保它的設定專為您的需求。然後您可以使用 EF Core 移轉管理結構描述。

處理機密資料的考量

雖然曾因此得很好,所以尚未備妥這。有幾個考量的問題。

第一個是連接字串和密碼是硬式編碼到 appsettings.json 檔案。它很容易修改 JSON 檔案中的該連接字串,而不必重新編譯專案,因此 「 硬式編碼 」 是有點誇大其詞。但它不是動態 Docker 是而言,因為 appsettings 檔案將會 「 現成 」 至 Docker 映像。您可能會想要更充分掌控開發、 預備、 測試和生產環境的連接字串。這不是新的問題,以及各種解決方案已經存在於好一段時間。相對於保護機密資料,在 ASP.NET Core 中的新祕密管理工具可協助在開發期間。請參閱文件,網址bit.ly/2HeVgVr如需詳細資訊。此外,appsettings.json 檔案是文字,而且任何人都可以讀取它從原始檔控制如果您不小心將其推播至公用儲存機制以及應用程式。

一種進展的容器化的解決方案是使用 Docker 環境變數,其中應用程式可以讀取在執行階段,同時繼續能夠在設計階段執行移轉命令。這也可讓您彈性地以動態方式提供這些變數的值。

計劃如下:在設計階段,以及測試 」 上 metal"; 的應用程式,我將使用 SQL Server LocalDB也就是針對 Kestrel 或 IIS 伺服器進行偵錯時。LocalDB 不需要認證,因為我不必擔心其連接字串中的祕密。執行和本機偵錯在 Docker 中,我可以切換至 SQL Server 中,我的網路上,或指向 Azure SQL Database。最後,容器化應用程式實際執行版本,我會確保它指向 Azure SQL Database。您會看到如何使用 Windows 和 Docker 的環境變數,以及在將您的密碼祕密。

而以下是而言很方便的項目:由於所有這些方法會使用 SQL Server 的某些類別,我一律可以使用相同的提供者,Microsoft.EntityFrameworkCore.SqlServer。

將開發連接字串移至 [開發設定

ASP.NET Core 會預設為 appsettings 中。執行從 Visual Studio 中,但在生產環境中時,Development.json 設定檔預設為 appsettings.json。

我將 appsettings.json 中移除整個 connectionStrings 區段中,相反地,將 [appsettings 加入 [LocalDB 連接字串。Development.json。如果您展開 [方案總管] 中的 appsettings.json 旁邊的箭號圖像 (glyph),您會發現此檔案:

"ConnectionStrings": {
    "MagsConnectionMssql":
      "Server=(localdb)\\mssqllocaldb;Database=
        DP0419Mags;Trusted_Connection=True;"
  }

因為我想要能夠讀取此連接字串,在設計階段和環境變數所提供的 Dockerfile,在執行階段應用程式時,我需要採用不同的語法,UseSqlServer 選項。目前 (和最常),您就會用於 Configuration.GetConnectionString 讀取 appsettings 檔案中的字串。沒有作用,不過,對於環境變數,不論它們是從 Windows 或 Docker。GetConnectionString 是協助程式方法,取代了直接參考的屬性名稱。

不過,我可以使用此語法的索引鍵 / 值組為讀取 appsettings 值和任何環境的值:

services.AddDbContext<MagContext>(options =>
  options.UseSqlServer(
    Configuration["ConnectionStrings:MagsConnectionMssql"]));

讓我們確認 EF Core 移轉可以在 appsettings 中找到的連接字串。Development.json,您可以執行 PowerShell 移轉命令 Get DbContext。這會強制執行任何其他的移轉命令,為相同工作的 EF Core,並會導致顯示提供者名稱、 資料庫名稱和資料來源的輸出:

providerName                            databaseName dataSource             options
------------                            ------------ ----------             -------
Microsoft.EntityFrameworkCore.SqlServer DP0419Mags  (localdb)\mssqllocaldb  None

建立連接字串用於 Docker 的安全無虞

現在我會在設計階段已知 appsettings 能正常運作。什麼讓 Docker 容器正在執行,而不需要修改您來回移 startup.cs 時尋找其替代的連接字串嗎?

您最好知道的 CreateWebHostBuilder 方法呼叫中 program.cs 呼叫 AddEnvironmentVariables,它會讀取使用環境變數,並將它們儲存為組態資料中的索引鍵 / 值組。

Docker 有呼叫 ENV,可讓您設定索引鍵 / 值組的命令。首先我要硬式編碼此到 Dockerfile。我可以在 JSON 檔案中,也就是 UseSqlServer 組態需要的內容與設定具有相同名稱的新金鑰。我甚至可以在 [索引鍵的名稱包含冒號。  建立組建映像之前,我可以放在檔案中的我的環境變數。[圖 2示範 Dockerfile 中,包括新的環境變數。如果您需要進行重新整理程式,我會說明此系列中,第 1 部分中的這個檔案的內容。請注意,我以簡寫為便於閱讀此處的連接字串。

[圖 2 的連接字串,就地 DataAPIDocker 專案的 Dockerfile

FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
ENV ConnectionStrings:MagsConnectionMssql=
  "Server=tcp:msdnmaglerman.database.windows.net ...”
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY ["DataAPIDocker/DataAPIDocker.csproj", "DataAPIDocker/"]
RUN dotnet restore "DataAPIDocker/DataAPIDocker.csproj"
COPY . .
WORKDIR "/src/DataAPIDocker"
RUN dotnet build "DataAPIDocker.csproj" -c Release -o /app
FROM build AS publish
RUN dotnet publish "DataAPIDocker.csproj" -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "DataAPIDocker.dll"]

我們來看看這有何影響。確定偵錯設定檔會指向 Docker,並讓我們實際偵錯此時間。我只在 Startup.cs 和偵錯,AddDbContext 命令之後設定中斷點,然後檢查 [組態 ["ConnectionStrings:MagsConnectionMssql"] 的值。我可以看到它現在會傳回 Azure SQL Database 連接字串,而不是 LocalDb 連接字串。偵錯的所有可用的組態資料,我可以看到 appsettings 連接字串也會載入組態物件。但中所述的 Randy Patterson 邀請他的部落格文章, bit.ly/2F5jfE8,最後一項設定會覆寫先前的設定。並且之後 appsettings 讀取環境變數。因此,即使有兩個 ConnectionStrings:MagsConnectionMssql 值,Dockerfile 中指定的一個就是正在使用的一個。如果您已在 Kestrel 或 IIS 中執行,不會執行 Dockerfile,而且它的環境變數不會存在於組態。

建立祕密的預留位置

但是環境變數不是變數。因為它是硬式編碼,才靜態現在。此外,別忘了,還包含我的密碼 (登入和密碼)。而不是讓它們在連接字串中,首先我要為自己的環境變數中擷取這些兩個密碼。預留位置名稱中,我將使用 ENVID 和 ENVPW。然後我將在 Dockerfile 中的使用者識別碼和密碼來建立兩個環境變數並為第一個階段中,我要直接指定其值:

ENV ConnectionStrings:MagsConnectionAzSql=
  "Server=tcp:msdnmaglerman.database.windows.net,1433;
  Initial Catalog=DP0419Mags;
  User ID=ENVID;Password=ENVPW; [etc...]
ENV DB_UserId="lerman"
ENV DB_PW="eiluj"

回 Startup.cs 在 ConfigureServices 中,我會閱讀所有三個環境變數,並建置其認證的連接字串:

var config = new StringBuilder
   (Configuration[“ConnectionStrings:MagsConnectionMssql”]);
string conn = config.Replace(“ENVID”, Configuration[“DB_UserId”])
                    .Replace(“ENVPW”, Configuration[“DB_PW”])
                    .ToString();
services.AddDbContext<MagContext>(options => options.UseSqlServer(conn));

輕鬆地這是因為所需的所有值都都會在 Dockerfile 中。但仍變數都是靜態,並在 Dockerfile 中公開祕密。

移出 Dockerfile 並進入 Docker Compose 的祕密

Docker 容器只能存取在容器內的環境變數,因為我目前的安裝程式不提供定義的密碼或在 Dockerfile 中指定其他環境變數值的方式。但還有一個方法,可讓您的其中一個步驟設定您 Docker 的專業能力更進一步。Docker 有一個功能稱為 Docker Compose,其可讓您使用多個映像。這由 docker 控制-compose 指示檔,可以觸發並執行一或多個 Dockerfile,每個控制它自己的 Docker 映像的 Dockerfile。目前,我有單一映像,而我要維持原意。但我仍然可以充分善用 Docker Compose 來將值傳遞至映像的環境變數。

為何如此重要?它可讓我把出 Dockerfile,並從映像,以及我祕密。當容器執行個體正在啟動,以及任何動態組態資訊,例如不同的連接字串時,我可以傳入祕密。在本主題中沒有一篇bit.ly/2Uuhu8F,我找到很有幫助。

使用 docker-compose 檔案,來協調多個容器被指容器協調流程。這可協助 Visual Studio docker 工具。以滑鼠右鍵按一下方案總管] 中的專案,然後選取 [新增] 並選擇容器協調器支援。您將會看到下拉式清單,從中您應該選取 [Docker Compose,出現提示時,選擇 Linux 作為目標 OS。因為已經有 Dockerfile,系統會要求您想要將它重新命名,並建立新的 Dockerfile。請回答該問題的回答 [否],以保留您現有的 Dockerfile 保持不變。接下來,您將會問您是否要覆寫隱藏的.dockerignore 檔案。因為您未接觸到該檔案,其中一個選項是 [確定]。

這項作業完成時,您會看到新的方案資料夾,稱為 docker compose,搭配兩個檔案:.dockerignore 和 docker compose.yml。YAML 語言 yml 擴充功能參考 (yaml.org),依賴縮排來表示的檔案結構描述非常簡潔的文字格式。

建立中的下列工具 docker compose.yml:

version: '3.4'
services:
  dataapidocker:
    image: ${DOCKER_REGISTRY-} dataapidocker
    build:
      context: .
      dockerfile: DataAPIDocker/Dockerfile

它有只有一個服務: dataapidocker 專案。它會註明的映像名稱是 dataapidocker 和時就可以開始建置該映像的 docker 檔案的位置。

有很多方法可以使用 docker compose 來傳遞至容器的環境變數 (dockr.ly/2TwfZub)。我會先將變數放直接在 docker-compose.yml 檔案中。首先,我將在其中新增 dataapidocker 服務內的 [環境] 區段,在相同的層級做為映像,並建置。然後,您也可以在新的區段中,我會定義 DP_PW 變數使用特定的格式如下所示:

version: '3.4'
services:
  dataapidocker :
    image: ${DOCKER_REGISTRY-}dataapidocker
    build:
      context: .
      dockerfile: DataAPIDocker/Dockerfile
    environment:
      - DB_PW=eiluj

別忘了 Dockerfile 的完全移除 DB_PW 變數。Docker compose 可確保變數取得傳遞至執行中的容器,但它不會存在於本身的映像。

現在,若要執行專案時,您必須確定 docker compose 的方案資料夾會設定為啟始專案。請注意,[偵錯] 按鈕會設定為 Docker Compose。若要查看 magic unfold,將中斷點放在啟動程式碼建置連接字串的位置,然後偵錯應用程式您應該會看到 ["DB_PW"] 的組態是確實能夠找出從 docker-compose 傳入的值。

而且,最後,移動的祕密值超出 Docker Compose

但是 docker-compose 檔案中仍有我的密碼和您知道,而且我知道,在某個時間點我不小心按到我的公用的原始檔控制。Docker compose 我的電腦,不能在 Docker 映像上執行。表示 docker compose,可以存取主機上的資訊。我無法建立環境變數儲存的密碼,並讓 docker compose 我開發電腦上探索它。您甚至可以在 Visual Studio Package Manager 主控台中建立暫存環境變數。但是,Docker 提供更好的選項,其支援讀取.env 檔案。

根據預設,docker compose 會讀取檔案,稱為 「.env。 」 沒有任何基底檔案名稱,而只是 「.env"。 它也可命名.env 檔案中 docker compose,使用 env_file 對應,以指定服務描述中。我的部落格文章,請參閱 < bit.ly/2CR40x3如需有關具名的.env 檔案。

您可以使用這些儲存變數,例如連接字串,例如 dev.env、 test.env 或 production.env;或機密資料。當我使用的工具來新增容器協調流程時,工具建立的 docker.ignore 檔案會列出.env 檔案,讓那些不會不小心推送至原始檔控制。

Visual Studio 不會讓您將檔案加入 docker-compose 專案,我有解決的方法,加入 [方案項目] 資料夾中,新的文字檔,然後將它移到 docker-compose 專案。

我將我的祕密放.env 檔案和檔案內容就是:

 

DB_PW=eiluj

[圖 3顯示我最終的解決方案的樣貌。

其中包括新的.env 檔案的最終解決方案
[圖 3 包括新的.env 檔案的最終解決方案

如此一來,我可以只設定密碼,我要開發及偵錯應用程式時,而不必擔心有可能共用的任何檔案。此外,我有提供其他變數的設定資訊的選項。

我的密碼仍然是純文字,不過,這沒有問題的我的電腦上。您可能會想要加密這些生產環境中,不過。Elton Stoneman 提供這個他的書,「 Windows,Second Edition 上的 Docker 」 中的指導方針 (Packt Publishing 是 2019 年 2 月)。

後續步驟

我一個明顯的下一個步驟是部署容器及如何取得我的 Azure SQL database 中的密碼環境變數,到容器執行個體。這項挑戰採用大量的讀取和實驗,以及我已執行沒有這一期的足夠空間時,我有部落格文章,在這個完全在 azure 的相關bit.ly/2FHdbAM。我也寫了解發行至 Docker 和裝載在 Azure 虛擬機器中的 Docker 部落格。我會更新這篇文章的線上版本 url,當針對其可用。

此多部分的資料行下一期的計劃便轉換為目標 Azure SQL Database,以在其本身的容器中的 SQL Server 資料庫。這會結合為已學到目前為止有關 docker-compose 與課程,從較早的資料行 (「 上即時個 SQL Server 使用 Docker 」 msdn.com/magazine/mt784660)。兩個參考的部落格文章將涵蓋發行映像,和在雲端中執行的容器。


Julie LermanMicrosoft 地區主管、 Microsoft MVP、 軟體小組指導,以及位於 Vermont 山區顧問。您可以找到其呈現在資料存取和使用者群組和所做的心得世界各地的其他主題。在她部落格thedatafarm.com/blog和以及 Code First DbContext 版本中的,所有從 O'Reilly Media 是"程式設計 Entity Framework"的作者。在 Twitter 上關注她: @julielerman並查看在她 Pluralsight 課程bit.ly/PS-Julie

感謝閱本篇文章的下列技術專家:Steven 綠色和 Mike Morton (Microsoft),Elton Stoneman (Docker)
Elton Stoneman 是 Pluralsight 的作者,Microsoft MVP 和 Docker 開發人員提倡者。他已建構和傳遞自 2000,最近的巨量資料與在 Azure 中的 API 實作和使用 Docker 的分散式應用程式的成功與 Microsoft 技術的解決方案。
目前他有想要了解 Microsoft 堆疊中的演進探索現代化現有的.NET Framework 應用程式,使用 Docker,並搭配新的.NET Core 應用程式在 Windows 和 Linux 容器中執行這些絕佳的機會。
他是一般的主講人和研討會主機在研討會發表演說。他一直榮幸在 DockerCon、 NDC、 DevSum、 ProgNet、 SDD、 容器 Camp 和未來解碼。您通常會看到我在 [使用者群組太-Docker 倫敦、 倫敦 DevOps 及 WinOps 是我的區域變數。


MSDN Magazine 論壇中的這篇文章的討論