2019 年 6 月

第 34 卷,第 6 期

此文章由机器翻译。

[数据点]

Docker 容器化应用中的 EF Core(第 3 部分)

通过Julie Lerman

Julie Lerman在过去的两篇文章,我介绍了在生成较小的 ASP.NET Core API 使用实体框架封装在 Docker 容器中。在第一篇文章,我包含在容器内的一个 SQLite 数据库。在第二个,我针对 Azure SQL 数据库可从任何位置访问。更大的难题在这种情况下是如何确保密码 (例如数据库密码) 的安全和配置 (例如开发或生产数据库的连接字符串) 流畅。这涉及到学习如何利用的 Docker Compose 读取环境变量从主机的功能。

没有保留数据,则需要具有数据库服务器和容器中的数据的另一个非常有趣的路径。我编写了有关使用适用于 Linux Docker 容器中早期的列,例如在一个 SQL Server msdn.com/magazine/mt784660。我非常喜欢 Microsoft 的 Steve Lasker 表达为开发和测试使用在容器中的 SQL Server 的角度来看的方式:

快速启动容器中的 SQL 开发人员可以通过以隔离/模拟环境。包括不同版本的 SQL Server。能够从完全相同的状态开始测试,每次时,是的精华内容。尤其是在测试数据库升级,而不是弄清如何回滚 DB 更新,只需重新启动该容器。

在适用于 Linux 的 SQL Server 上文章中,我交互使用仅 sqlcmd 命令行在数据库中。在较新的文章 (msdn.com/magazine/mt832858),我使用了 Azure 数据 Studio 与容器化的 SQL Server 和数据库进行交互。现在,我想要将专用的容器化的 SQL Server 集成到我的 API 开发。我将介绍您如何实现此目的在 Visual Studio 中,继续执行上期专栏中我使用的示例。

快速查看 API

该 API 是一个两个属性杂志类 (Id 和名称)、 杂志控制器与 EF Core DbContext 使用迁移来植入具有四个杂志的新数据库的简单。

在本系列第 2 部分中的应用程序的下一个迭代 (msdn.com/magazine/mt833438),引入了容器业务流程通过 docker-compose 文件。因为那是一种单容器解决方案,并不真正需要业务流程,docker compose 提供的功能将环境变量传递到容器,而无需直接在图像中存储值。但即使有了单容器解决方案,可以使用 compose 文件运行应用程序在 Docker Swarm 或 Kubernetes 群集上,为您提供缩放和自我修复。

使用环境变量,MagazinesContext 类将对数据库连接字符串的密码。

将移动到容器化数据库

本文的目的是面向 Linux 服务器的 SQL 服务器和我的开发计算机上的容器中的数据库。这意味着更新现有的 docker-compose 文件和更新应用中的连接字符串。在所有,不太大的精力。但如果您永远不会这样做之前,是让人向您的方法当然不错 !

最大的变化将到 docker-compose.yml 文件。和非常酷的是,Docker 将会负责处理最困难的部分,只需通过阅读中继在 docker 中的说明进行操作的组合。因为所有这些启动 Visual Studio 2017 中,运行或从 Visual Studio 调试时将加入包含的 Tools for Docker。如果使用的 Visual Studio 2019 (其中,在我写这,正式发布昨天,因此我将在 2017年中继续这一系列),此工具内置的和此体验应相同。

下面是以前版本的 docker-compose.yml 文件:

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

此 docker-compose 文件仅可管理 dataapidocker 服务中定义的一个映像。此工具将确保,DOCKER_REGISTRY 变量替换在运行时在我的开发计算机上运行的 Docker 引擎。然后,使用该映像,它将查找 Dockerfile (第 1 部分和本系列的第 2 部分中定义) 以获取更多有关要执行的操作与映像创建容器实例时的说明。因为我未直接在 docker-compose 文件中的 DB_PW 环境变量提供值,它使我可以从 shell 中,我正在其中运行容器或从其他源,例如 docker.env 文件传递一个值。第 2 部分中的.env 文件用于存储键-值对 DB_PW 和我的密码,eiluj。

因此现在我告诉 docker compose,我想要让它快速创建 SQL Server 容器。SQL Server for Linux 映像是官方映像中 Microsoft 容器注册表 (MCR) 中,但还列出 Docker Hub 上的可发现性。通过引用在此处,Docker (其中我正在开发计算机) 上,本地注册表中,将首先查找和图像并不存在,它然后将从 MCR 拉取映像。请参阅图 1有关这些更改的示例。

图 1 Docker Compose 文件发生变化以包括 mssql/服务器容器

version: '3.4'
services:
  dataapidocker:
    image: ${DOCKER_REGISTRY-}dataapidocker
    build:
      context: .
      dockerfile: DataAPIDocker/Dockerfile
    environment:
      - DB_PW
    depends_on:
      - db
  db:
    image: mcr.microsoft.com/mssql/server
    environment:
      SA_PASSWORD: "${DB_PW}"
      ACCEPT_EULA: "Y"
    ports:
      - "1433:1433"

但我添加了,我的名为 db 的新服务不止是指向 mssql/server 映像。让我们,正如他们所说,解包所做的更改。请注意,没有即将后通过此步骤中工作的另一种修改。

第一个更改是 dataapidocker 服务中,原始 api 中介绍了容器的那个。我添加了名为 depends_on,其中包含 YAML 所指的序列项,名为 db 的映射。这意味着,在运行之前 dataapidocker 容器,Docker 将检查 docker-compose 文件的名为 db 的另一个服务,需要使用它的详细信息来实例化该服务定义的容器。

通过指向 SQL Server for Linux 映像使用官方 Docker 名称开始 db 服务。此映像需要运行容器时传入两个环境变量-SA_Password 和 ACCEPT_EULA — 因此,服务说明还包含该信息。而且,最后,您需要指定将在服务器上可用的端口:1433:1433.第一个值是指主机的端口和第二个容器内的端口。公开主机的默认端口 1433年通过服务器可以轻松地从主计算机访问数据库。我将介绍您的工作原理后启动并运行,我已经指定了此项目。

在需要时若要运行此 docker compose Visual Studio 外部,我还需要公开端口从 dataapidocker 服务。Visual Studio 工具创建第二个 docker-compose 文件,docker compose.override.yml,Visual Studio 在开发期间使用。在该文件是几个其他映射,包括映射的端口 (端口:-80) dataapidocker 服务。因此现在我将让小心谨慎地让我可以在 Visual Studio 中调试时,浏览到网站上的工具。

持久性数据定义一个单独的卷

还有更多工作才能实现 docker compose,但是。使用现有的说明,任何数据库和数据将创建在运行 SQL Server 的同一容器内。这可能并不是用于测试如果干净的数据库需要为每个测试问题。它是更好的做法,但是,若要单独保留的数据。有几种方法执行此操作,但我将使用所谓的数据卷。看一看在我的博客文章bit.ly/2pZ7dDb,其中我详细介绍数据卷,演示如何持续即使你停止或删除运行 SQL Server 的容器。

您可以将说明留在 docker-compose 文件来指定一个卷用于持久保存数据独立于 SQL Server 容器,如中所做图 2。卷并不相同,则为一个容器,因此它不是另一个服务。相反,您将创建名为卷是同级到服务的新密钥。项中,你可以提供你自己的名称 (我已经称挖掘 mssql server julie 数据) 的卷。没有与此键关联的值。这种方式命名卷可以与其他容器重用它,如果您需要。你可以阅读更多有关 docker 的 Docker 引用中的卷-在撰写docs.docker.com/compose/compose-file或更多详细信息,请查看 Elton Stoneman Pluralsight 课程中使用 Docker 的有状态数据 (pluralsight.pxf.io/yoLYv)。

图 2 中 Docker Compose 定义的数据卷

services:
  dataapidocker: [etc]
  db:
    image: mcr.microsoft.com/mssql/server
    volumes:
      - mssql-server-julie-data:/var/opt/mssql/data
    environment:
      SA_PASSWORD: "${DB_PW}"
      ACCEPT_EULA: "Y"
    ports:
      - "1433:1433"
volumes:
  mssql-server-julie-data: {}

请注意 db 映射还具有卷的新映射。此映射包含序列项中的已命名的卷映射到存储的数据和日志文件的源容器中的目标路径。

为容器化的 SQL Server 设置连接字符串

请注意,在以前版本的应用程序中,我保留连接字符串到 SQL Server LocalDB 在 appsettings 中。对于时间 Development.json 我想要测试运行中的 Kestrel 或本地 IIS 服务器的 API。  名为 ConnectionStrings:MagsConnectionMssql,Azure SQL 数据库的连接字符串存储在 API 的 Dockerfile,正上方的用于定义在生成映像部分中的环境变量。以下是该 Dockerfile 的前几行。连接字符串具有用户 id 和密码的占位符。用户 id 在 Dockerfile 中仍只是因为我已重点介绍隐藏密码和尚未尚未将其删除:

FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
ENV ConnectionStrings:MagsConnectionMssql="Server=[Azure SQL endpoint];
    Initial Catalog=DP0419Mags;Persist Security Info=False;
    User ID=ENVID;Password=ENVPW;MultipleActiveResultSets=False;
    Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
ENV DB_UserId="lerman"
FROM microsoft/dotnet:2.2-sdk AS build

此连接字符串可以轻松移出到的 docker-compose.yml 和控制通过外部方式。API 的启动配置方法使用提供的环境变量,包括密码 DB_PW 我存储在其内容如下所示的 the.env 文件中的值将覆盖用户和密码占位符:

DB_PW=eiluj

Docker compose 和传递到 Dockerfile 以读取并将提供给应用程序的容器将通过读取.env 文件和其 DB_PW 值。最后,我要删除的连接字符串和 DB_UserId 变量从 Dockerfile。

但是,该连接字符串是针对 Azure SQL。我现在需要更改其值以指向 SQL Server 容器:

ENV ConnectionStrings:MagsConnectionMssql
  "Server=db;Database=DP0419Mags;User=sa;Password=ENVPW;"

我正在使用相同的变量名称,因为这是 Startup.ConfigureServices 的期望。对我来说,有趣之处是容器运行时可以理解的名称的数据库中定义的服务的 docker-compose 作为服务器名称。因此,在连接字符串中,我可以为数据库指定的服务器。Mssql/服务器映像使用设置服务器的默认用户是 sa,因此我已编写代码,直接在连接字符串,这就是为什么我不再需要针对的用户 id 的环境变量。但是,我仍使用占位符,ENVPW,密码的值。

与以前的专栏中的示例,占位符最终将由 DB_PW 环境变量的值替换。下面是什么代码现在看起来的读取连接字符串和 DB_PW 变量,然后将其传递到 SQL Server 提供程序之前更新连接字符串:

public void ConfigureServices(IServiceCollection services)
{
  services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  var config = new StringBuilder(
    Configuration["ConnectionStrings:MagsConnectionMssql"]);
  string conn = config.Replace("ENVPW", Configuration["DB_PW"])
                      .ToString();
  services.AddDbContext<MagContext>(options => options.UseSqlServer(conn));
}

这些就是所有所需的更改。现在让我们来调试和查看会发生什么情况。

使用数据库容器进行调试

请确保 docker-compose 项目是解决方案中的启动项目,调试按钮然后设置为 Docker Compose,按 F5 调试 API。

如果这是首次运行应用这种方式,并已永远不会提取 mssql/服务器映像,将第一件事发生这种情况。  SQL Server 映像不会小 — 它是 1.45 GB。因此,第一次提取时,请耐心等待。请注意,它需要 2 GB 的 RAM,因此请确保将足够的 RAM 分配给 Docker 引擎 (在 Docker 设置中) 来运行它。即便如此,这是比在计算机上安装 SQL Server 仍然更快,它仅使用已分配给 Docker,在主机上没有其他资源的资源。而且一旦映像存在,很轻松启动大量实例。另一个好处是基本映像的任何更新获取作为小型的层,不全部内容再次提取。请注意,Microsoft/dotnet sdk 和运行时的 aspnetcore 映像都已在我的系统上,由于工作我在以前的专栏。

如果你观看生成输出,则会看到应用创建之前处理 mssql/服务器容器中的数据量 — 我的示例在"处理"意味着首先将它从 Docker 中心拉取因为它是我的系统。操作的顺序发生服务器是依赖于该卷,因此该卷获取首先进行处理。完成后,它将启动生成 API 的图像,如果需要然后会启动容器。在生成输出中,由于工具清楚地中继步骤。

使用此应用的上一版本,如杂志控制器 GET 方法的结果显示在浏览器窗口中。

浏览容器创建的 docker compose

现在让我们看这怎么回事了 Docker。虽然适用于 Visual Studio Code 的 Docker 扩展是另一个很酷选项来实现此目的,我将在命令行执行的。

首先,我将列出与命令 docker 映像的映像。如果您熟悉如何使用 Docker CLI,您可能更愿意使用更明确的命令 docker 映像 ls。如中所示图 3,这将显示 mssql 映像现在是我的计算机上和创建 dataapidocker 的我的映像的新版本,以及。

检查图像
图 3 检查图像

接下来,我将查看有 docker ps,docker 容器 ls 的缩短等效项的容器。此输出是宽,因此我上面的右半部分在屏幕的一半呈现左侧图 4

运行容器

运行容器
图 4 运行容器

最后,若要查看的卷的创建,现在绑定到 db 容器中,我可以使用 docker 卷 lscommand,如中所示图 5

Docker 卷命令
图 5 Docker 卷命令

请注意,容器和数据卷的名称开头中组合这些工具的 Visual Studio 调试过程生成的相同文本。

请记住,我已公开从容器主机的端口 1433年上的数据库,因此我可以使用在我的开发计算机上工具来看一看的 db 容器中,以及附加的卷中的数据库的服务器。我将使用 Azure Data Studio 若要这样做;图 6显示了我是如何定义连接。

设置与我的容器化数据库的连接
图 6 设置与我的容器化数据库的连接

使用建立的连接,然后我可以与服务器和我的新数据库交互。图 7显示在左侧,以及查询数据库资源管理器和结果在右侧。

与 Azure 数据 Studio 中的容器化数据库进行交互
图 7 与 Azure 数据 Studio 中的容器化数据库进行交互

应用程序的容器是准备好运行

您的应用程序封装在 Docker 映像使其可以十分轻松地部署的应用和所有依赖项作为容器。但对于容器新手和资深,我认为,具有用于开发应用程序以使它们已与映像和业务流程相关联的工具可利用容器简单得多。和本系列中我一直专注于开发和仅限本地调试。以下第 2 部分,我发布了有关单一映像解决方案部署到 Azure,用于在测试的博客文章bit.ly/2CR40x3。该解决方案在 Azure SQL 数据库中存储其数据和运行使用 Azure 容器实例的测试容器。我还编写了一篇博客文章有关发布到 Docker 中心这篇文章 dataapidocker 映像和托管在 Azure 中,这是另一种很好的方法来测试已安排的容器上的 Linux 虚拟机中的完整解决方案。您可以找到该博客文章在bit.ly/2VBaN8M


Julie Lerman住在佛蒙特州的丘陵地区,担任 Microsoft 区域主管、Microsoft MVP、软件团队导师和顾问。可以在全球的用户组和会议中看到她对数据访问和其他主题的介绍。她的博客地址是 thedatafarm.com/blog。她是“Entity Framework 编程”及其 Code First 和 DbContext 版本(全都出版自 O’Reilly Media)的作者。请通过 Twitter 关注她 (@julielerman),并观看她在 bit.ly/PS-Julie 上的 Pluralsight 课程。

衷心感谢以下技术专家对本文的审阅:Elton Stoneman (Docker),Travis Wright (Microsoft)


在 MSDN 杂志论坛讨论这篇文章