ASP.NET Core应用程序是真正的跨平台,可以在nixes中运行,因此可以在Docker中运行。 让我们看看如何打包它们以在Linux上部署并与Nginx结合使用。 细节剪下!

注意:我们将继续阅读Hacker杂志的文章的完整版本系列。 作者的拼写和标点符号得以保存。
关于Docker
几乎每个人都听说过微服务架构。 将应用程序分为几部分的概念并不是说它是新的。 但是,新的是被遗忘和回收的旧的。
如果您尝试用几句话来谈论体系结构,那么Web应用程序将分为独立的单一部分-服务。 服务不会彼此直接交互,也没有公共数据库。 这样做是为了能够更改每个服务而不会给其他服务带来任何后果。 服务包装在容器中。 在这些容器中,Docker负责。
为了描述Docker经常被简化,使用术语“虚拟机”。 肯定有相似之处,但是这样说是错误的。 理解这种差异的最简单方法是查看来自官方Docker文档的以下图像:


容器使用当前操作系统的核心并将其划分。 使用虚拟机管理程序的虚拟机使用硬件资源。
Docker Image是一个只读对象,其本质上存储用于构建容器的模板。 容器是在其中执行代码的环境。 图像存储在存储库中。 例如,官方的Docker Hub存储库仅允许您私密存储一个映像。 但是,它是免费的,因此即使如此,您也需要感谢他们。
资讯
Docker并不是容器化的唯一代表。 除此以外,还有其他技术。 例如:
rkt (读作“火箭”),由CoreOS提供
Ubuntu的LXD (发音为``lexdi'')
Windows容器-您永远都不会猜到任何人。
现在我们已经熟悉了理论,让我们继续实践。
拆卸docker安装没有任何意义,因为它可以安装在许多操作系统上。 我只会指出您可以从Docker Store中为您的平台下载它。 如果在Windows下安装Docker,则必须在BIOS和OS中启用虚拟化。 您可以在以下文章中了解如何在10-ke中启用它: 在Windows10中安装Hyper-V
创建一个启用了docker的项目
Docker当然是Linux产品,但是如果需要,您可以在为Mac或Windows开发时使用它。 在Visual Studio中创建项目时,要添加Docker支持,只需选中Enable Docker Support复选框。
可以将Docker支持添加到现有项目中。 它以与添加各种新组件相同的方式添加到项目中。 上下文菜单添加-Docker支持。
如果在您的计算机上安装并运行了docker,将自动打开控制台并执行命令
docker pull microsoft/aspnetcore:2.0
这将开始下载图像的过程。 该图像实际上是空白,将根据其创建图像。 ASP.NET Core 2.1使用不同的映像-Microsoft / dotnet:sdk
以下文件将自动在包含解决方案的目录中创建:
.dockerignore(不包括docker映像中的文件和目录),docker-compose.yml(使用此文件,您可以配置多个服务的执行),docker-compose.override.yml(辅助配置docker-compose),docker-compose.dcproj( Visual Studio项目文件)。
Dockerfile文件将在项目目录中创建。 实际上,借助此文件,我们创建了映像。 默认情况下(如果项目名为DockerServiceDemo)可能看起来像这样:
FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo/DockerServiceDemo.csproj DockerServiceDemo/ RUN dotnet restore DockerServiceDemo/DockerServiceDemo.csproj COPY . . WORKDIR /src/DockerServiceDemo RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"]
.NET Core 2.0的初始配置将不允许您使用docker build命令立即构建映像。 它配置为从上一级目录启动docker-compose文件。 为了使构建成功进行,可以使Dockerfile具有类似的外观:
FROM microsoft/aspnetcore:2.0 AS base WORKDIR /app EXPOSE 80 FROM microsoft/aspnetcore-build:2.0 AS build WORKDIR /src COPY DockerServiceDemo.csproj DockerServiceDemo.csproj RUN dotnet restore DockerServiceDemo.csproj COPY . . WORKDIR /src RUN dotnet build DockerServiceDemo.csproj -c Release -o /app FROM build AS publish RUN dotnet publish DockerServiceDemo.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "DockerServiceDemo.dll"]
我所做的只是删除了额外的DockerServiceDemo目录。
如果使用Visual Studio Code,则必须手动生成文件。 尽管VS Code具有Docker扩展形式的辅助功能,但我将在VS Code-使用Docker中添加有关如何使用Docker的手册的链接。 是的,文章是英文的,但附带图片
三和弦Docker
对于docker的日常工作,仅需记住几个命令即可。
当然,最重要的团队是建立形象。 为此,您需要使用bash / CMD / PowerShell进入Dockerfile所在的目录并执行以下命令:
docker build -t your_image_name .
在此,在-t选项之后,设置图像的名称。 注意-命令末尾,空格后的空格。 此点表示正在使用当前目录。 可以使用标签(数字或名称)来标记图像。 为此,请在名称后加上一个冒号并指定一个标签。 如果未指定标签,则默认情况下将使用最新名称进行设置。 为了将图像发送到存储库,图像名称必须包含存储库的名称。 像这样:
docker build -t docker_account_name/image_name:your_tag .
此处your_docker_account_name是您的Docker中心帐户的名称。
如果仅使用不包含存储库的本地名称创建了映像,则可以在构建后使用以下命令将映像标记为其他名称:
docker tag image_name docker_account_name/image_name:your_tag
为了将更改发送到集线器,您现在需要运行以下命令:
docker push docker_account_name/image_name:your_tag
在此之前,您需要登录到您的Docker帐户。 在Windows上,这是通过应用程序的UI完成的,但是在* nix上,这是通过以下命令完成的:
docker login
实际上,三个团队是不够的。 您还必须能够检查容器的操作。 可用来启动容器的命令如下所示:
docker run -it -p 5000:80 image_name
-it选项将创建一个伪TTY,您的容器将响应请求。 运行该命令后,该服务将在以下位置可用: http://本地主机:5000 /
-p 5000:80将容器的端口5000与主机的端口80关联。
另外,还有这样的命令:
docker ps –a
向您显示容器列表。 由于已添加-a开关,因此将显示所有容器,而不仅仅是当前正在运行的那些容器。
docker rm container_name
此命令将删除名为container_name的容器。 rm-删除的简称
docker logs container_name
显示容器日志
docker rmi image_name
删除名为image_name的图像
通过反向代理服务器启动容器
实际上,.NET Core应用程序本身使用其Kestrel Web服务器。 不建议将该服务器用于生产。 怎么了 有几种解释。
如果有多个共享IP和端口的应用程序,则Kestrel将无法分发流量。 此外,反向代理服务器提供了额外的安全层,简化了负载平衡和SSL配置,还可以更好地集成到现有基础架构中。 对于大多数开发人员而言,需要反向代理的最重要原因是附加的安全性。
首先,还原原始的Dockerfile配置。 之后,我们将处理docker-compose.yml文件,并尝试单独运行我们的服务。 yml文件格式读为“ yaml”,是“另一种标记语言”或“ YAML不是标记语言”的缩写。 要么是另一种标记语言,要么根本不是一种标记语言。 不知何故,一切还不确定。
我的默认docker-compose文件如下所示:
version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile
docker-compose.override.yml文件将几个设置添加到配置中:
版本:“ 3.4”
services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development ports: - "80"
我们可以使用docker-compose build来构建创建的解决方案,通过调用docker-compose up命令,我们将启动容器。 一切正常吗? 然后转到下一步。 创建nginx.info文件。 该配置将大致如下:
worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:80; } server { listen 80; location / { proxy_pass http://app_servers; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } }
在这里,我们指示nginx将侦听端口80(侦听80;)。 并且收到的请求将重定向到dockerservicedemo容器中主机的第80端口。 此外,我们告诉nginx传递哪些标头。
我们可以在nginx中使用http,并通过https访问网站。 当HTTPS请求通过HTTP代理时,来自HTTPS的许多信息不会传递到http。 另外,使用代理时,外部IP地址会丢失。 为了在标题中传输此信息,您需要更改我们的ASP.NET项目的代码,并将以下代码添加到Startup.cs文件的Configure方法的开头:
app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto });
大多数代理服务器使用X-Forwarded-For和X-Forwarded-Proto标头。 现在在nginx配置中指定的是这些标头。
现在,在doker-compose配置中包括nginx映像和nginx.conf文件。 YAML空间中的注意事项很重要:
version: '3.4' services: dockerservicedemo: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile ports: - 5000:80 proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf ports: - 80:80
在这里,我们将代理添加为nginx图像到我们的配置中。 我们在此图像上附加了一个外部设置文件。 我们使用一种称为卷的机制将其挂载到容器文件系统中。 如果在末尾添加:ro,则对象将以只读方式挂载。
代理侦听运行容器的计算机的外部第80端口,并将请求发送到容器的内部第80端口。
通过运行doker-compose up命令,我们将进行填充,即从存储库中提取nginx图像,并与代理容器一起启动我们的容器。 现在在http://本地主机:80 /将可以通过nginx访问。 在第5000个端口上,应用程序也在Kestrel下“旋转”。
我们可以验证对Web应用程序的请求是否通过了反向代理。 在Chrome浏览器中打开开发人员工具,然后转到“网络”标签。 单击此处的本地主机,然后选择标题选项卡。

我们通过代理和HTTPS启动容器
ASP.NET Core 2.1带来了对HTTPS支持的改进。
假设以下中间件允许您从不安全的连接重定向到安全的连接:
app.UseHttpsRedirection();
下一个允许您使用HTTP严格传输安全协议-HSTS。
app.UseHsts();
HSTS是HTTP / 2协议的一项功能,其规范于2015年发布。 现代浏览器支持此功能,并通知该网站仅使用https。 因此,可以抵御降级攻击,在此期间,攻击者可以使用过渡到不安全的http协议的方式来利用这种情况。 例如,降级TLS甚至替换证书。
通常,这种类型的攻击与中间人攻击结合使用。 您应该了解并记住,当用户使用http协议访问站点然后重定向到https时,HSTS不会使您免于陷入困境。 有一个所谓的Chrome预加载列表 ,其中包含指向支持https的网站的链接。 其他浏览器(Firefox,Opera,Safari,Edge)也支持基于Chrome列表创建的https网站列表。 但是所有这些列表都远离所有站点。
首次在Windows上运行任何Core应用程序时,您将收到一条消息,指出已创建并安装了开发人员证书。 通过单击按钮并安装证书,您将使其可信。 在macOS上的命令行中,可以使用以下命令将信任添加到证书:
dotnet dev-certs https –trust
如果未安装dev-certs实用程序,则可以使用以下命令进行安装:
dotnet tool install --global dotnet-dev-certs
如何在Linux上将证书添加到受信任证书取决于发行版。
出于测试目的,我们使用开发人员的证书。 由CA签名的证书的操作类似。 如果需要,您可以使用免费的LetsEncrypt证书
您可以使用以下命令将开发人员证书导出到文件中
dotnet dev-certs https -ep ___.pfx
该文件必须在Windows下复制到%APPDATA%/ ASP.NET / Https /目录,或者在macOS / Linux下复制到/root/.aspnet/https/。
为了使容器能够选择证书及其密码的路径,请创建具有以下内容的用户密钥:
{ "Kestrel":{ "Certificates":{ "Default":{ "Path": "/root/.aspnet/https/__.pfx", "Password": "___" } } } }
该文件存储未加密的数据,因此仅在开发期间使用。 通过在项目图标上调用上下文菜单或在Linux上使用user-secrets实用程序,可以在Visual Studio中创建文件。
在Windows上,文件将保存在%APPDATA%\ Microsoft \ UserSecrets \ <user_secrets_id> \ secrets.json目录中,而在macOS和Linux上,文件将保存在〜/ .microsoft / usersecrets / <user_secrets_id> /secrets.json中
要保存生产设置,某些Linux发行版可能使用systemd,这些设置保存在Service属性下。 例如,像这样:
[Service] Environment="Kestrel _ Certificates _ Default _Path=/root/.aspnet/https/__.pfx" Environment="Kestrel _ Certificates _ Default _Password=___"
接下来,我将通过https立即给出并分析代理和容器的docker配置的工作版本。
Docker-compose文件:
version: '3.4' services: dockerservicedemo21: image: ${DOCKER_REGISTRY}dockerservicedemo build: context: . dockerfile: DockerServiceDemo/Dockerfile override: version: '3.4' services: dockerservicedemo: environment: - ASPNETCORE_ENVIRONMENT=Development - ASPNETCORE_URLS=https://+:44392;http://+:80 - ASPNETCORE_HTTPS_PORT=44392 ports: - "59404:80" - "44392:44392" volumes: - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro proxy: image: nginx:latest volumes: - ./DockerServiceDemo/nginx.conf:/etc/nginx/nginx.conf - ./DockerServiceDemo/cert.crt:/etc/nginx/cert.crt - ./DockerServiceDemo/cert.rsa:/etc/nginx/cert.rsa ports: - "5001:44392"
现在,我将描述难以理解的时刻。 ASPNETCORE_URLS允许我们不要使用app.UseUrl在应用程序代码中指示应用程序正在侦听的端口。
ASPNETCORE_HTTPS_PORT进行的重定向类似于以下代码将执行的操作:
services.AddHttpsRedirection(options => options.HttpsPort = 44392)
也就是说,来自http请求的流量将被重定向到https请求的特定端口。
使用端口表示来自第59404个外部端口的请求将重定向到第80个容器,从第44392个外部端口重定向到第44392个容器。 从理论上讲,由于我们已经配置了反向代理服务器,因此可以使用这些重定向删除端口。
使用卷时,将安装带有pfx证书的目录和UserSecrets应用程序,并带有密码和证书的链接。
代理部分指示来自第5001个外部端口的请求将重定向到第44392nd个nginx端口。 此外,还将安装一个nginx配置文件以及一个证书和一个证书密钥。
为了让他们创建一个pfx证书(我们已经拥有)来创建crt和rsa文件,可以使用OpenSSL。 首先,您需要提取证书:
openssl pkcs12 -in ./_.pfx -clcerts -nokeys -out domain.crt
然后是私钥:
openssl pkcs12 -in ./_.pfx -nocerts -nodes -out domain.rsa
Nginx配置如下:
worker_processes 4; events { worker_connections 1024; } http { sendfile on; upstream app_servers { server dockerservicedemo:44392; } server { listen 44392 ssl; ssl_certificate /etc/nginx/cert.crt; ssl_certificate_key /etc/nginx/cert.rsa; location / { proxy_pass https://app_servers; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } }
代理服务器正在侦听端口44392。 该端口从第5001个主机端口接收请求。 接下来,代理将请求重定向到dockerdemoservice容器的第44392个端口。
了解了这些示例之后,您将具有使用docker,microservices和nginx的良好背景。
我们提醒您,这是《黑客》杂志上文章的完整版。 它的作者是阿列克谢·索默 ( Alexey Sommer) 。