本文基于我之前的文章GettingStartedwith.NETCore。首先,我将RESTfulAPI从.NETCoreRC1升级到.NETCore1.0,然后我添加了对Docker的支持,并描述了如何在Linux的生产环境中托管它。我是Docker的新手,距离成为Linux大师还有很长的路要走。所以这里的很多想法都来自新手。安装按照https://www.microsoft.com/net/core上的说明在您的计算机上安装.NETCore。这将在Windows上安装dotnet命令行工具和最新的VisualStudio工具。源码可以直接到GitHub上查找最新完整的源码。过渡到.NETCORE1.0自然地,当我考虑如何将API从.NETCoreRC1升级到.NETCore1.0时,我首先想到的是Google搜索。我按照这两个非常全面的指南进行升级:从DNX迁移到.NETCoreCLI从ASP.NET5RC1迁移到ASP.NETCore1.0当您迁移代码时,我建议阅读这两个指南,因为我试图通过第二个指南没有阅读第一个指南,并且感到非常困惑和沮丧。我不想详细描述更改,因为您可以在GitHub上看到提交。以下是我所做更改的摘要:更新了global.json和project.json的版本号删除了project.json上的过时章节使用轻量级ControllerBase而不是Controller因为我不需要与MVC视图相关的方法(这是选择的解决方法改变)。从辅助方法中删除Http前缀,例如:HttpNotFound->NotFoundLogVerbose->LogTrace命名空间更改:Microsoft.AspNetCore.*在启动时使用SetBasePath(没有它appsettings.json将找不到)通过WebHostBuilder运行而不是通过WebApplication.Run运行到删除Serilog(在撰写本文时,它不支持.NETCore1.0)唯一真正让我头疼的是需要移动Serilog。我本可以实现自己的文件记录器,但我删除了文件记录功能,因为我不想为此操作花费精力。不幸的是,将会有大量的第3方开发人员扮演着赶上.NETCore1.0的角色,我非常同情他们,因为他们通常在休息时间继续工作,但仍然无法接近。Microsoft的可用资源。我建议阅读TravisIllig的文章.NETCore1.0已发布,但Autofac在哪里?这是一篇关于第三方开发人员观点的文章。进行这些更改后,我可以从project.json目录恢复、构建和运行dotnet,并看到API像以前一样工作。通过Docker运行在撰写本文时,Docker仅适用于Linux系统。Windows和OSX上都有对Docker的beta支持,但两者都必须依赖虚拟化技术,所以我选择将Ubuntu14.04作为虚拟机运行。如果您还没有安装Docker,请按照说明进行安装。我最近阅读了一些有关Docker的内容,但直到现在我还没有真正使用过它。我假设读者还不了解Docker,所以我将解释我使用的所有命令。HELLODOCKER在Ubuntu上安装Docker之后,我采取的下一步是开始运行.NETCore和Docker,如https://www.microsoft.com/net/core#docker中所述。首先启动一个安装了.NETCore的容器。dockerrun-itmicrosoft/dotnet:latest-it选项表示交互式,因此执行此命令后,您就在容器内,可以执行任何想要的bash命令。然后我们可以执行以下五个命令来运行Docker内部的Microsoft.NETCore控制台应用程序示例。mkdirhwappcdhwappdotnetnewdotnetrestoredotnetrun您可以通过运行exit离开容器,然后运行??Dockerps-a命令,这将显示您创建的退出容器。您可以通过在其上运行命令Dockerrm来清理容器。挂载源代码我的下一步是使用与上面相同的microsoft/dotnet映像,但将源代码挂载为我们应用程序的数据卷。首先使用相关提交检查存储库:gitclonehttps://github.com/niksoper/aspnet5-books.gitcdaspnet5-books/src/MvcLibrarygitcheckoutdotnet-core-1.0现在启动一个运行.NETCore1.0的容器并放置源代码/book下的代码。小心更改文件的/path/to/repo部分以匹配您的计算机:dockerrun-it\-v/path/to/repo/aspnet5-books/src/MvcLibrary:/books\microsoft/dotnet:latest现在你可以在容器中运行应用程序!cd/booksdotnetrestoredotnetrun作为概念演示非常棒,但我们不想在每次运行应用程序时都弄清楚如何将源代码安装到容器中。添加DOCKERFILE我的下一步是包含一个Dockerfile,这使得应用程序可以轻松地在其自己的容器内启动。我的Dockerfile和project.json一样,位于src/MvcLibrary目录中,如下所示:FROMmicrosoft/dotnet:latest#CreateadirectoryfortheapplicationsourcecodeRUNmkdir-p/usr/src/booksWORKDIR/usr/src/books#复制源代码并恢复依赖COPY./usr/src/booksRUNdotnetrestore#ExposeportandrunapplicationEXPOSE5000CMD["dotnet","run"]严格来说,不需要运行mkdir-p/usr/src/books命令,因为COPY自动创建丢失的目录。Docker镜像是分层构建的,我们从一个包含.NETCore的镜像开始,添加另一个从源代码构建应用程序的层,然后运行这个应用程序。添加Dockerfile后,我通过运行以下命令生成了一个图像,并使用生成的图像启动了一个容器(确保在与Dockerfile相同的目录中执行此操作,并且您应该使用自己的用户名)。dockerbuild-tniksoper/netcore-books.dockerrun-itniksoper/netcore-books你应该看到程序可以像以前一样运行了,但是这次我们不需要像以前那样安装源码,因为源码已经包含在泊坞窗图像。除非我们需要从容器外部与之通信,否则暴露和发布端口并不是一个特别有用的API。Docker已经有了暴露和发布端口的概念,但这是两个完全不同的东西。根据Docker官方文档:EXPOSE指令告诉Docker容器在运行时监听特定的网络端口。EXPOSE指令不会使容器的端口可供主机访问。要可访问,您必须使用-p标志发布一系列端口或使用-P标志发布所有公开的端口。EXPOSE指令只是将元数据添加到图像中,因此您可以将其视为文档所说的镜像消费者。从技术上讲,我应该忽略EXPOSE5000行,因为我知道API正在侦听的端口,但保留它们是有用的,也是推荐的。现阶段我想直接从宿主机访问这个API,所以需要通过-p命令发布这个端口,这样就可以让请求从宿主机的5000端口转发到容器的5000端口,而不管这个端口之前是否通过Dockerfile暴露了。dockerrun-d-p5000:5000niksoper/netcore-books-d指令告诉docker以分离模式运行容器,所以我们看不到它的输出,但它仍然会运行并监听端口5000。你可以验证这个与码头工人ps。所以接下来我要从主机向容器发出请求来庆祝:curlhttp://localhost:5000/api/books并且它不起作用。重复相同的curl请求,我看到两个错误:要么是curl:(56)Recvfailure:Connectionresetbypeer,要么是curl:(52)来自服务器的空回复。我返回到dockerrun的文档并再次检查我使用的是-p选项并且Dockerfile中的EXPOSE指令是正确的。我没有看到任何问题,这让我有点沮丧。振作起来后,我决定咨询当地的ScottLogicDevOps大师——DaveWybourn(在这篇DockerSwarm文章中也提到过),他的团队曾遇到过这个实际问题。问题是我没有配置Kestral,这是一个新的轻量级、跨平台的.NETCoreWeb服务器。默认情况下,Kestrel侦听http://localhost:5000。但问题是,这里的localhost是一个环回接口。根据维基百科:在计算机网络中,localhost是代表本地机器的主机名。本地主机可以通过网络环回接口访问运行在主机上的网络服务。可以使用环回接口绕过任何硬件网络接口。这是在容器内运行时的一个问题,因为localhost只能在容器内访问。解决方法是更新Startup.cs中的Main方法,配置Kestral监听的URL:("http://*:5000")//在所有网络接口上监听端口5000.UseIISIntegration().UseStartup().Build();host.Run();}有了这些额外的配置,我可以重建镜像,并在容器中运行应用程序,它将能够接收来自主机的请求:dockerbuild-tniksoper/netcore-books.dockerrun-d-p5000:5000niksoper/netcore-bookscurl-ihttp://localhost:5000/api/booksi现在得到以下响应:HTTP/1.1200OKDate:Tue,30Aug201615:25:43GMTTransfer-Encoding:chunkedContent-Type:application/json;charset=utf-8Server:Kestrel[{"id":"1","title":"RESTfulAPIwithASP.NETCoreMVC1.0","author":"NickSoper"}]在生产环境中运行KESTREL微软介绍:KestrelcanhandledynamiccontentfromASP.NETverywell然而,web服务部分的特性不如像IIS、Apache或Nginx这样的全特性服务器。反向代理服务器使您不必从HTTP服务器处理静态内容、缓存请求、压缩请求、SSL端点等工作。因此我需要在我的Linux机器上将Nginx设置为反向代理服务器。Microsoft推出了有关如何发布到Linux生产环境的指导教程。我在此处总结了说明:通过dotnet发布为应用程序生成一个独立的包。将发布的应用复制到服务器安装配置Nginx(作为反向代理服务器)安装配置supervisor(确保Nginx服务器运行)安装配置AppArmor(限制应用的资源使用)配置服务器防火墙安全加固Nginx(从源代码构建和配置SSL)这些内容超出了本文的范围,因此我将重点介绍如何将Nginx配置为反向代理服务器。自然地,我通过Docker来做到这一点。在另一个容器中运行NGINX我的目标是在第二个Docker容器中运行Nginx,并将其配置为我们应用程序容器的反向代理服务器。我使用的是来自DockerHub的官方Nginx镜像。首先我尝试了这个:dockerrun-d-p8080:80--namewebnginx这会启动一个运行Nginx的容器,并将主机上的端口8080映射到容器中的端口80。现在在浏览器中打开网址http://localhost:8080,会显示Nginx默认的登录页面。现在我们已经演示了运行Nginx是多么容易,我们可以关闭容器了。dockerrm-fweb将NGINX配置为反向代理服务器您可以通过编辑位于/etc/nginx/conf.d/default.conf的配置文件将Nginx配置为反向代理服务器,如下所示:server{listen80;location/{proxy_passhttp://localhost:6666;}}通过以上配置,Nginx可以将所有对根目录的访问请求代理到http://localhost:6666。请记住,这里的localhost指的是运行Nginx的容器。我们可以使用Nginx容器内部的卷来使用我们自己的配置文件:dockerrun-d-p8080:80\-v/path/to/my.conf:/etc/nginx/conf.d/default.conf\nginx注意:这会将主机中的单个文件映射到容器中,而不是完整的目录。容器之间的通信Docker允许内部容器通过共享虚拟网络进行通信。默认情况下,所有通过Docker守护进程启动的容器都可以访问称为“网桥”的虚拟网络。这使得容器可以通过IP地址和端口被同一网络上的另一个容器引用。您可以通过检查容器来找出容器的IP地址。我将从之前创建的niksoper/netcore-books镜像启动一个容器并检查它:dockerrun-d-p5000:5000--namebooksniksoper/netcore-booksdockerinspectbooks我们可以看到这个容器的IP地址是“IPAddress”:“172.17.0.3”。所以现在如果我创建以下Nginx配置文件,并使用该文件启动Nginx容器,它将代理请求到我的API:server{listen80;location/{proxy_passhttp://172.17.0.3:5000;}}现在我你可以使用这个配置文件来启动一个Nginx容器(注意我把主机上的8080端口映射到了Nginx容器上的80端口):dockerrun-d-p8080:80\-v~/dev/nginx/my.nginx。conf:/etc/nginx/conf.d/default.conf\nginx对http://localhost:8080的请求将被代理到应用程序。注意下面curl响应的服务器响应头:DOCKERCOMPOSE在这个地方,我为我的进步感到高兴,但我认为必须有更好的方法来配置Nginx,它不需要知道应用程序容器的确切IP地址.另一位本地ScottLogicDevOps大师JasonEbbin改进了这个地方并建议使用DockerCompose。总的来说,DockerCompose可以轻松启动一组通过声明性语法互连的容器。我不想深入探讨DockerCompose的工作原理,您可以在上一篇文章中了解到这一点。我将从我使用的docker-compose.yml文件开始:version:'2'services:books-service:container_name:books-apibuild:.reverse-proxy:container_name:reverse-proxyimage:nginxports:-"9090:8080"volumes:-./proxy.conf:/etc/nginx/conf.d/default.conf这是版本2语法,因此您至少需要DockerCompose版本1.6才能工作。该文件告诉Docker创建两项服务:一项用于应用程序,一项用于Nginx反向代理服务器。BOOKS-SERVICE,Dockerfile在docker-compose.yml同目录下构建的容器,叫做books-api。请注意,这个容器不需要发布任何端口,因为它只需要可以从反向代理服务器访问,而不是从主机操作系统访问。REVERSE-PROXY这将基于nginx镜像启动一个名为reverse-proxy的容器,并挂载位于当前目录的proxy.conf文件作为配置。它将主机上的端口9090映射到容器中的端口8080,这将允许我们从位于http://localhost:9090的主机访问容器。proxy.conf文件看起来像这样:server{listen8080;location/{proxy_passhttp://books-service:5000;}}这里的关键点是我们现在可以通过名称引用books-service,所以我们不用书本上不需要知道-api这个容器的IP地址!现在我们可以通过正在运行的反向代理启动两个容器(-d表示这是独立的,所以我们看不到容器的输出):dockercomposeup-dverifywehaveCreatedcontainer:dockerps最后验证我们可以控制通过反向代理的API:curl-ihttp://localhost:9090/api/books这是如何工作的?DockerCompose创建了一个名为mvclibrary_default的新虚拟网络,为此,该虚拟网络用于books-api和反向代理容器(名称基于docker-compose.yml文件的父目录)。使用dockernetworkls验证网络是否存在:您可以使用dockernetworkinspectmvclibrary_default来查看新网络的详细信息:请注意,Docker已为网络分配了一个子网:“子网”:“172.18.0.0/16”。/16部分是无类域内路由(CIDR),完整的解释超出了本文的范围,但CIDR只是表示IP地址范围。运行dockernetworkinspectbridge显示子网:“子网”:“172.17.0.0/16”,因此两个网络不重叠。现在使用dockerinspectbooks-api来确认应用程序的容器正在使用网络:注意两个容器别名(“Aliases”)是容器标识符(3c42db680459)和docker-compose.yml给出的服务名称(books-服务)。我们通过books-service别名在自定义Nginx配置文件中引用应用程序的容器。这本可以使用dockernetworkcreate手动完成,但我喜欢使用DockerCompose,因为它干净简洁地捆绑了容器创建和依赖项。结论现在我可以通过几个简单的步骤在Linux系统上使用Nginx运行应用程序,而无需对主机操作系统进行任何长期更改:gitclonehttps://github.com/niksoper/aspnet5-books.gitcdaspnet5-books/src/MvcLibrarygitcheckoutblog-dockerdocker-composeup-dcurl-ihttp://localhost:9090/api/books我知道我在这篇文章中写的不是真正的生产就绪设备,因为我没有写任何关于内容以下的大部分主题需要在一篇完整的文章中进行描述。防火墙和SSL配置等安全考虑如何确保应用程序保持运行如何选择包含哪些Docker镜像(我将它们都放在Dockerfile中)数据库-如何在容器中管理它们这对我来说是一个非常有趣的问题学习经验,因为有一段时间我很好奇探索ASP.NETCore的跨平台支持,使用“ConfiguratinasCode”DockerCompose方法探索DevOps的世界也非常愉快和有教育意义。如果您对Docker感到好奇,那么我鼓励您尝试学习它。也许它会让您走出舒适区,但也许您会喜欢它?