Linux通过 Docker 可以托管

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以点击这里获取!

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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()
.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 生产环境下的指导教程。我把说明总结在这儿:


1. 通过 dotnet publish 来给应用程序产生一个自包含包。
2. 把已发布的应用程序复制到服务器上
3. 安装并配置 Nginx(作为反向代理服务器)
4. 安装并配置 supervisor(用于确保 Nginx 服务器处于运行状态中)
5. 安装并配置 AppArmor(用于限制应用的资源使用)
6. 配置服务器防火墙
7. 安全加固 Nginx(从源代码构建和配置 SSL)


这些内容已经超出了本文的范围,因此我将侧重于如何把 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


![Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!](http://www.linuxprobe.com/wp-content/uploads/2016/11/214147ff5if5556rqmi5h8.png "Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!")


我们可以看到这个容器的 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 响应头:


![Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!](http://www.linuxprobe.com/wp-content/uploads/2016/11/214211az6777pz7pcc9ici.png "Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!")



**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**

![Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!](http://www.linuxprobe.com/wp-content/uploads/2016/11/u40588085403739727668fm21gp0.jpg "Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!")  
 这个与 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来验证网络已经存在:


![Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!](http://www.linuxprobe.com/wp-content/uploads/2016/11/214239m1h9dndn6z71ckk1.png "Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!")


你可以使用 docker network inspect mvclibrary\_default 来看到新的网络的细节:


![Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!](http://www.linuxprobe.com/wp-content/uploads/2016/11/214309g0ac0uzvkchvuoov.png "Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!")


注意 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 来确认应用程序的容器正在使用该网络:


![Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!](http://www.linuxprobe.com/wp-content/uploads/2016/11/214338m0d52xft4z6wds5g.png "Linux通过 Docker 可以托管 .NET Core啦!Linux通过 Docker 可以托管 .NET Core啦!")


注意容器的两个别名("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


我知道我在这篇文章中所写的内容不是一个真正的生产环境就绪的设备,因为我没有写任何有关下面这些的内容,绝大多数下面的这些主题都需要用单独一篇完整的文章来叙述。


* 安全考虑比如防火墙和 SSL 配置
* 如何确保应用程序保持运行状态
* 如何选择需要包含的 Docker 镜像(我把所有的都放入了 Dockerfile 中)
* 数据库 - 如何在容器中管理它们


对我来说这是一个非常有趣的学习经历,因为有一段时间我对探索 ASP.NET Core 的跨平台支持非常好奇,使用 “Configuratin as Code” 的 Docker Compose 方法来探索一下 DevOps 的世界也是非常愉快并且很有教育意义的。


如果你对 Docker 很好奇,那么我鼓励你来尝试学习它 或许这会让你离开舒适区,不过,有可能你会喜欢它?


  
 


  
 


  


### 最后的话

最近很多小伙伴找我要Linux学习资料,于是我翻箱倒柜,整理了一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

![](https://img-blog.csdnimg.cn/img_convert/6bd0ce48d605ae3c0052bd32e320432a.png)

给大家整理的电子书资料:

  

![](https://img-blog.csdnimg.cn/img_convert/fa37ccc7e84c9f794fa42026fb1ca0ce.png)



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

一些优质资源,涵盖视频、电子书、PPT等共享给大家!

### 资料预览

给大家整理的视频资料:

[外链图片转存中...(img-n3y7M7Qz-1715831371110)]

给大家整理的电子书资料:

  

[外链图片转存中...(img-iC8fD84B-1715831371111)]



**如果本文对你有帮助,欢迎点赞、收藏、转发给朋友,让我有持续创作的动力!**

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以点击这里获取!](https://bbs.csdn.net/topics/618635766)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值