我是首次接触 Docker 而且距离成为一名 Linux 高手还有很远的一段路程。所以,这里的不少想法是来自一个新手。html
内容linux
安装按照 https://www.microsoft.com/net/core 上的介绍在你的电脑上安装 .NET Core 。这将会同时在 Windows 上安装 dotnet 命令行工具以及最新的 Visual Studio 工具。nginx
源代码git
你能够直接到 GitHub 上找最到最新完整的源代码。
github
转换到 .NET CORE 1.0web
天然地,当我考虑如何把 API 从 .NET Core RC1 升级到 .NET Core 1.0 时想到的第一个求助的地方就是谷歌搜索。我是按照下面这两条很是全面的指导来进行升级的:docker
当你迁移代码的时候,我建议仔细阅读这两篇指导,由于我在没有阅读第一篇指导的状况下又尝试浏览第二篇,结果感到很是迷惑和沮丧。数据库
我不想描述细节上的改变由于你能够看 GitHub 上的提交。这儿是我所做改变的总结:json
惟一令我真正头疼的事是须要移动 Serilog。我本能够实现本身的文件记录器,可是我删除了文件记录功能,由于我不想为了此次操做在这件事情上花费精力。api
不幸的是,将有大量的第三方开发者扮演追赶 .NET Core 1.0 的角色,我很是同情他们,由于他们一般在休息时间还坚持工做但却依旧根本没法接近靠拢微软的可用资源。我建议阅读 Travis Illig 的文章 .NET Core 1.0 发布了,但 Autofac 在哪儿?这是一篇关于第三方开发者观点的文章。
作了这些改变之后,我能够从project.json 目录恢复、构建并运行 dotnet,能够看到 API 又像之前同样工做了。
经过 Docker 运行
在我写这篇文章的时候, Docker 只可以在 Linux 系统上工做。在 Windows 系统和 OS X 上有 beta 支持 Docker,可是它们都必须依赖于虚拟化技术,所以,我选择把 Ubuntu 14.04 看成虚拟机来运行。若是你尚未安装过 Docker,请按照指导来安装。
我最近阅读了一些关于 Docker 的东西,但我直到如今尚未真正用它来干任何事。我假设读者尚未关于 Docker 的知识,所以我会解释我所使用的全部命令。
HELLO DOCKER
在 Ubuntu 上安装好 Docker 以后,我所进行的下一步就是按照 https://www.microsoft.com/net/core#docker 上的介绍来开始运行 .NET Core 和 Docker。
首先启动一个已安装有 .NET Core 的容器。
docker run -it microsoft/dotnet:latest
-it 选项表示交互,因此你执行这条命令以后,你就处于容器以内了,能够如你所但愿的那样执行任何 bash 命令。
而后咱们能够执行下面这五条命令来在 Docker 内部运行起来微软 .NET Core 控制台应用程序示例。
mkdir hwapp cd hwapp dotnet new dotnet restore dotnet run
你能够经过运行 exit 来离开容器,而后运行 Docker ps -a 命令,这会显示你建立的那个已经退出的容器。你能够经过上运行命令 Docker rm <container_name> 来清除容器。
挂载源代码
个人下一步骤是使用和上面相同的 microsoft/dotnet 镜像,可是将为咱们的应用程序以数据卷的方式挂载上源代码。
首先签出有相关提交的仓库:
git clone https://github.com/niksoper/aspnet5-books.git cd aspnet5-books/src/MvcLibrary git checkout dotnet-core-1.0
如今启动一个容器来运行 .NET Core 1.0,并将源代码放在/book 下。注意更改 /path/to/repo这部分文件来匹配你的电脑:
docker run -it / -v /path/to/repo/aspnet5-books/src/MvcLibrary:/books / microsoft/dotnet:latest
如今你能够在容器中运行应用程序了!
cd /books dotnet restore dotnet run
做为一个概念性展现这的确很棒,可是咱们可不想每次运行一个程序都要考虑如何把源代码安装到容器里。
增长一个 DOCKERFILE
个人下一步骤是引入一个 Dockerfile,这可让应用程序很容易在本身的容器内启动。
个人 Dockerfile 和 project.json 同样位于 src/MvcLibrary目录下,看起来像下面这样:
FROM microsoft/dotnet:latest # 为应用程序源代码建立目录 RUN mkdir -p /usr/src/books WORKDIR /usr/src/books # 复制源代码并恢复依赖关系 COPY . /usr/src/books RUN dotnet restore # 暴露端口并运行应用程序 EXPOSE 5000 CMD [ "dotnet", "run" ]
严格来讲,RUN mkdir -p /usr/src/books 命令是不须要的,由于 COPY 会自动建立丢失的目录。
Docker 镜像是按层创建的,咱们从包含 .NET Core 的镜像开始,添加另外一个从源代码生成应用程序,而后运行这个应用程序的层。
添加了 Dockerfile 之后,我经过运行下面的命令来生成一个镜像,并使用生成的镜像启动一个容器(确保在和 Dockerfile 相同的目录下进行操做,而且你应该使用本身的用户名)。
docker build -t niksoper/netcore-books . docker run -it niksoper/netcore-books
你应该看到程序可以和以前同样的运行,不过这一次咱们不须要像以前那样安装源代码,由于源代码已经包含在 docker 镜像里面了。
暴露并发布端口
这个 API 并非特别有用,除非咱们须要从容器外面和它进行通讯。 Docker 已经有了暴露和发布端口的概念,但这是两件彻底不一样的事。
据 Docker 官方文档:
EXPOSE指令通知 Docker 容器在运行时监听特定的网络端口。EXPOSE指令不可以让容器的端口可被主机访问。要使可被访问,你必须经过 -p 标志来发布一个端口范围或者使用 -p 标志来发布全部暴露的端口
EXPOSE 指令只是将元数据添加到镜像上,因此你能够如文档中说的认为它是镜像消费者。从技术上讲,我本应该忽略 EXPOSE 5000 这行指令,由于我知道 API 正在监听的端口,但把它们留下颇有用的,而且值得推荐。
在这个阶段,我想直接从主机访问这个 API ,所以我须要经过-p 指令来发布这个端口,这将容许请求从主机上的端口 5000 转发到容器上的端口 5000,不管这个端口是否是以前经过 Dockerfile 暴露的。
docker run -d -p 5000:5000 niksoper/netcore-books
经过 -d 指令告诉 docker 在分离模式下运行容器,所以咱们不能看到它的输出,可是它依旧会运行并监听端口 5000。你能够经过 docker ps来证明这件事。
所以,接下来我准备从主机向容器发起一个请求来庆祝一下:
curl http://localhost:5000/api/books
它不工做。
重复进行相同 curl 请求,我看到了两个错误:要么是 curl: (56) Recv failure: Connection reset by peer,要么是 curl: (52) Empty reply from server。
我返回去看 docker run 的文档,而后再次检查我所使用的 -p 选项以及 Dockerfile 中的 EXPOSE指令是否正确。我没有发现任何问题,这让我开始有些沮丧。
从新振做起来之后,我决定去咨询当地的一个 Scott Logic DevOps 大师 - Dave Wybourn(也在这篇 Docker Swarm 的文章里提到过),他的团队也曾遇到这个实际问题。这个问题是我没有配置过 Kestral,这是一个全新的轻量级、跨平台 web 服务器,用于 .NET Core 。
默认状况下, Kestrel 会监听 http://localhost:5000。但问题是,这儿的localhost是一个回路接口。
据维基百科:
在计算机网络中,localhost 是一个表明本机的主机名。本地主机能够经过网络回路接口访问在主机上运行的网络服务。经过使用回路接口能够绕过任何硬件网络接口。
当运行在容器内时这是一个问题,由于 localhost 只可以在容器内访问。解决方法是更新 Startup.cs里的 Main 方法来配置 Kestral 监听的 URL:
public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseUrls("http://*:5000") // 在全部网络接口上监听端口 5000 .UseIISIntegration() .UseStartup<Startup>() .Build(); host.Run(); }
经过这些额外的配置,我能够重建镜像,并在容器中运行应用程序,它将可以接收来自主机的请求:
docker build -t niksoper/netcore-books . docker run -d -p 5000:5000 niksoper/netcore-books curl -i http://localhost:5000/api/books
我如今获得下面这些相应:
HTTP/1.1 200 OK Date: Tue, 30 Aug 2016 15:25:43 GMT Transfer-Encoding: chunked Content-Type: application/json; charset=utf-8 Server: Kestrel [{"id":"1","title":"RESTful API with ASP.NET Core MVC 1.0","author":"Nick Soper"}]
在产品环境中运行 KESTREL
微软的介绍:
Kestrel 能够很好的处理来自 ASP.NET 的动态内容,然而,网络服务部分的特性没有如 IIS,Apache 或者 Nginx 那样的全特性服务器那么好。反向代理服务器可让你不用去作像处理静态内容、缓存请求、压缩请求、SSL 端点这样的来自 HTTP 服务器的工做。
所以我须要在个人 Linux 机器上把 Nginx 设置成一个反向代理服务器。微软介绍了如何发布到 Linux 生产环境下的指导教程。我把说明总结在这儿:
这些内容已经超出了本文的范围,所以我将侧重于如何把 Nginx 配置成一个反向代理服务器。天然地,我经过 Docker 来完成这件事。
在另外一个容器中运行NGINX
个人目标是在第二个 Docker 容器中运行 Nginx 并把它配置成咱们的应用程序容器的反向代理服务器。
我使用的是来自 Docker Hub 的官方 Nginx 镜像。首先我尝试这样作:
docker run -d -p 8080:80 --name web nginx
这启动了一个运行 Nginx 的容器并把主机上的 8080 端口映射到了容器的 80 端口上。如今在浏览器中打开网址 http://localhost:8080 会显示出 Nginx 的默认登陆页面。
如今咱们证明了运行 Nginx 是多么的简单,咱们能够关闭这个容器。
docker rm -f web
把 NGINX 配置成一个反向代理服务器
能够经过像下面这样编辑位于 /etc/nginx/conf.d/default.conf 的配置文件,把 Nginx 配置成一个反向代理服务器:
server { listen 80; location / { proxy_pass http://localhost:6666; } }
经过上面的配置可让 Nginx 将全部对根目录的访问请求代理到 http://localhost:6666。记住这里的 localhost 指的是运行 Nginx 的容器。咱们能够在 Nginx容器内部利用卷来使用咱们本身的配置文件:
docker run -d -p 8080:80 / -v /path/to/my.conf:/etc/nginx/conf.d/default.conf / nginx
注意:这把一个单一文件从主机映射到容器中,而不是一个完整目录。
在容器间进行通讯
Docker 容许内部容器经过共享虚拟网络进行通讯。默认状况下,全部经过 Docker 守护进程启动的容器均可以访问一种叫作“桥”的虚拟网络。这使得一个容器能够被另外一个容器在相同的网络上经过 IP 地址和端口来引用。
你能够经过监测inspect容器来找到它的 IP 地址。我将从以前建立的 niksoper/netcore-books 镜像中启动一个容器并监测inspect它:
docker run -d -p 5000:5000 --name books niksoper/netcore-books docker inspect books
咱们能够看到这个容器的 IP 地址是 "IPAddress": "172.17.0.3"。
因此如今若是我建立下面的 Nginx 配置文件,并使用这个文件启动一个 Nginx 容器, 它将代理请求到个人 API :
server { listen 80; location / { proxy_pass http://172.17.0.3:5000; } }
如今我可使用这个配置文件启动一个 Nginx 容器(注意我把主机上的 8080 端口映射到了 Nginx 容器上的 80 端口):
docker run -d -p 8080:80 / -v ~/dev/nginx/my.nginx.conf:/etc/nginx/conf.d/default.conf / nginx
一个到http://localhost:8080 的请求将被代理到应用上。注意下面 curl 响应的 Server 响应头:
DOCKER COMPOSE
在这个地方,我为本身的进步而感到高兴,但我认为必定还有更好的方法来配置 Nginx,能够不须要知道应用程序容器的确切 IP 地址。另外一个当地的 Scott Logic DevOps 大师 Jason Ebbin 在这个地方进行了改进,并建议使用 Docker Compose。
概况描述一下,Docker Compose 使得一组经过声明式语法互相链接的容器很容易启动。我不想再细说 Docker Compose 是如何工做的,由于你能够在以前的文章中找到。
我将经过一个我所使用的 docker-compose.yml 文件来启动:
version: '2' services: books-service: container_name: books-api build: . reverse-proxy: container_name: reverse-proxy image: nginx ports: - "9090:8080" volumes: - ./proxy.conf:/etc/nginx/conf.d/default.conf
这是版本 2 语法,因此为了可以正常工做,你至少须要 1.6 版本的 Docker Compose。
这个文件告诉 Docker 建立两个服务:一个是给应用的,另外一个是给 Nginx 反向代理服务器的。
BOOKS-SERVICE
这个与 docker-compose.yml 相同目录下的 Dockerfile 构建的容器叫作 books-api。注意这个容器不须要发布任何端口,由于只要可以从反向代理服务器访问它就能够,而不须要从主机操做系统访问它。
REVERSE-PROXY
这将基于 nginx 镜像启动一个叫作 reverse-proxy 的容器,并将位于当前目录下的 proxy.conf 文件挂载为配置。它把主机上的 9090 端口映射到容器中的 8080 端口,这将容许咱们在http://localhost:9090上经过主机访问容器。
proxy.conf 文件看起来像下面这样:
server { listen 8080; location / { proxy_pass http://books-service:5000; } }
这儿的关键点是咱们如今能够经过名字引用books-service,所以咱们不须要知道 books-api 这个容器的 IP 地址!
如今咱们能够经过一个运行着的反向代理启动两个容器(-d意味着这是独立的,所以咱们不能看到来自容器的输出):
docker compose up -d
验证咱们所建立的容器:
docker ps
最后来验证咱们能够经过反向代理来控制该 API :
curl -i http://localhost:9090/api/books
怎么作到的?
Docker Compose 经过建立一个新的叫作 mvclibrary_default 的虚拟网络来实现这件事,这个虚拟网络同时用于books-api 和 reverse-proxy 容器(名字是基于 docker-compose.yml 文件的父目录)。
经过docker network ls来验证网络已经存在:
你可使用 docker network inspect mvclibrary_default 来看到新的网络的细节:
注意 Docker 已经给网络分配了子网:"Subnet": "172.18.0.0/16"。/16 部分是无类域内路由选择(CIDR),完整的解释已经超出了本文的范围,但 CIDR 只是表示 IP 地址范围。运行 docker network inspect bridge 显示子网:"Subnet": "172.17.0.0/16",所以这两个网络是不重叠的。
如今用 docker inspect books-api 来确认应用程序的容器正在使用该网络:
注意容器的两个别名("Aliases")是容器标识符(3c42db680459)和由 docker-compose.yml 给出的服务名(books-service)。咱们经过books-service 别名在自定义 Nginx 配置文件中来引用应用程序的容器。这本能够经过 docker network create 手动建立,可是我喜欢用 Docker Compose,由于它能够干净简洁地将容器建立和依存捆绑在一块儿。
结论
因此如今我能够经过几个简单的步骤在 Linux 系统上用 Nginx 运行应用程序,不须要对主机操做系统作任何长期的改变:
git clone https://github.com/niksoper/aspnet5-books.git cd aspnet5-books/src/MvcLibrary git checkout blog-docker docker-compose up -d curl -i http://localhost:9090/api/books
我知道我在这篇文章中所写的内容不是一个真正的生产环境就绪的设备,由于我没有写任何有关下面这些的内容,绝大多数下面的这些主题都须要用单独一篇完整的文章来叙述。
对我来讲这是一个很是有趣的学习经历,由于有一段时间我对探索 ASP.NET Core 的跨平台支持很是好奇,使用 “Configuratin as Code” 的 Docker Compose 方法来探索一下 DevOps 的世界也是很是愉快而且颇有教育意义的。
若是你对 Docker 很好奇,那么我鼓励你来尝试学习它 或许这会让你离开温馨区,不过,有可能你会喜欢它?