使用 Docker 在 Linux 上托管 ASP.NET Core 应用程序
说在前面#
在阅读本文之前,您必须对 Docker 的中涉及的基本概念以及常见命令有一定了解,本文侧重实战,不会对相关概念详述。
同时请确保您本地开发机器已完成如下安装:
- Docker 18.06 或更高版本的 Docker 客户端
- .NET Core SDK 2.2 或更高版本
- Visual Studio Code 代码编辑器,以及 C# 语法插件 1.17.1 或更高版本
注:本文实验环境是 Ubuntu 18.04 LTS。如果您的机器是 Window,也可以把 Docker 装在虚拟机或服务器上。
创建演示项目#
开始之前要先准备一个需要 Docker 容器化的 ASP.NET Core 应用程序,用于下面的操作演示。这里我用 .NET Core CLI 快速搭建一个全新的 Web API 项目。
启动 VS Code,打开集成终端,输入如下命令:
以上便创建了一个名为TodoApi
的 Web API 样板项目。
打开集成终端,输入dotnet run
命令编译运行程序,然后打开浏览器跳转到 URL http://localhost:5000/api/values
,如正常返回如下 JSON 数据,说明应用程序本地成功运行。
现在让我们更进一步,在 Docker 中构建并运行该应用程序。
创建 Dockerfile 文件#
Dockerfile 是一个文本文件,其用来定义单个容器的内容和启动行为,按顺序包含构建镜像所需的所有指令。Docker 会通过读取 Dockerfile 中的指令自动构建镜像。
在项目TodoApi
根目录中,创建一个名为Dockerfile
的文件,并粘贴以下内容:
FROM
指令必须放在第一位,用于初始化镜像,为后面的指令设置基础镜像。
WORKDIR
指令为其他指令设置工作目录,如果不存在,则会创建该目录。
COPY
指令会从源路径复制新文件或目录,并将它们添加到路径目标容器的文件系统中。
RUN
指令可以在当前镜像之上的新 层 中执行任何命令并提交结果,生成的已提交镜像将用于 Dockerfile 中的下一步。
ENTRYPOINT
指令支持以可执行文件的形式运行容器。
有关 Dockerfile 中指令用法的更多信息请参阅 Dockerfile reference。
同时,为了避免构建项目中的一些调试生成文件,可以在项目文件夹中新增.dockerignore
文件,并粘贴如下内容:
构建应用容器镜像#
在项目TodoApi
根目录中,打开集成终端,执行如下命令构建容器镜像:
-t
参数用来指定镜像的名字及标签,通常是name:tag
或者name
格式。本例todoapi
便是我们给镜像起的名字,没有设置标签即使用默认标签latest
。
如命令执行成功,终端会有类似如下输出:
如果您的机器是第一次构建,速度可能会有些慢,因为要从 Docker Hub 上拉取应用依赖的
dotnet-sdk
和aspnetcore-runtime
基础镜像。
构建完成后,我们可以通过docker images
命令确认本地镜像仓库是否存在我们构建的镜像todoapi
。
运行应用容器#
容器镜像构建完成后,就可以使用docker run
命令运行容器了,有关该命令参数的更多信息请参阅 Reference - docker run 。
开发环境下,通常会通过docker run --rm -it
命令运行应用容器,具体命令如下:
-it
参数表示以交互模式运行容器并为容器重新分配一个伪输入终端,方便查看输出调试程序。
--rm
参数表示将会在容器退出后自动删除当前容器,开发模式下常用参数。
-p
参数表示会将本地计算机上的5000
端口映射到容器中的默认80
端口,端口映射的关系为host:container
。todoapi
便是我们要启动的本地镜像名称。
如命令执行成功,终端会有类似如下输出:
生产环境下,通常会通过docker run -d
命令运行应用容器,具体命令如下:
-d
参数表示会将容器作为服务启动,不需要终端交互。--name
参数用来指定容器名称,本例指定容器名称为myapp
。--restart
是一个面向生产环境的参数,用来指定容器非正常退出时的重启策略,本例always
表示始终重新启动容器,其他可选策略请参考 Restart policies (--restart)。
如命令执行成功,终端会有类似如下输出:
容器启动后,在 Web 浏览器中再次访问http://localhost:5000/api/values
,应该会和本地测试一样返回如下 JSON 数据:
至此,我们的 ASP.NET Core 应用就成功运行在 Docker 容器中了。
多容器应用部署#
目前我们创建的演示项目TodoApi
过于简单,真实的生产项目肯定会涉及更多其他的依赖。例如:关系数据库 Mysql、文档数据库 MongoDB、分布式缓存 Redis、消息队列 RabbitMQ 等各种服务。
还有就是,生产环境我们一般不会将 ASP.NET Core 应用程序的宿主服务器 Kestrel 直接暴露给用户,通常是在前面加一个反向代理服务 Nginx。
这些依赖服务还要像传统部署方式那样,一个一个单独配置部署吗?不用的,因为它们本身也是可以被容器化的,所以我们只要考虑如何把各个相互依赖的容器联系到一起,这就涉及到容器编排,而 Docker Compose 正是用来解决这一问题的,最终可以实现多容器应用的一键部署。
Docker Compose 是一个用于定义和运行多容器的 Docker 工具。其使用
YAML
文件来配置应用程序的服务,最终您只要使用一个命令就可以从配置中创建并启动所有服务。
安装 Docker Compose#
Linux 系统下的安装过程大致分为以下几步:
Step1:运行如下命令下载 Compose 最新稳定版本,截止发稿前最新版本为1.24.0
。
Step2:对下载完成的二进制程序添加可执行权限。
Step3:测试安装是否成功。
若您在安装过程中遇到问题,或是其他系统安装请参阅 Install Docker Compose。
改造演示项目#
现在来改造一下我们的演示项目TodoApi
,添加 Redis 分布式缓存、使用 Nginx 做反向代理,准备构建一个具如下图所示架构的多容器应用。
在TodoApi
项目根目录下,打开集成终端,输入如下命令新增 Redis 依赖包。
修改应用启动配置文件Startup.cs
中的ConfigureServices
方法:
在TodoApi
项目Controllers
目录下新建控制器HelloController
,具体代码如下所示:
以上控制器,提供了两个接口/api/hello
和/api/hello/{name}
,分别用来测试 Nginx 负载均衡和 Redis 的联通性。
创建 docker-compose.yml#
准备工作就绪,下面我们就可以使用 Docker Compose 来编排容器。
同样是在TodoApi
项目根目录中,创建一个名为docker-compose.yml
的文件,并粘贴以下内容:
其中version
用来指定 Compose 文件版本号,3.7
是目前最新版本,具体哪些版本对应哪些特定的 Docker 引擎版本请参阅 Compose file versions and upgrading。
Compose 中强化了服务的概念,简单地理解就是, 服务是一种用于生产环境的容器。一个多容器 Docker 应用由若干个服务组成,如上文件即定义了 5 个服务:
- 3 个应用服务
myproject-todoapi-1
、myproject-todoapi-2
和myproject-todoapi-3
- 1 个 Nginx 服务
myproject-reverse-proxy
- 1 个 Redis 服务
myproject-redis
以上 5 个服务的配置参数相差无几、也很简单,我就不展开叙述,不清楚的可以参阅 Compose file reference。
这里只讲一个配置参数volumes
:
我们知道,容器中的文件在宿主机上存在形式复杂,修改文件需要先通过如下命令进入容器后操作。
容器一旦删除,其内部配置以及产生的数据也会丢失。
为了解决这些问题,Docker 引入了数据卷 volumes 机制。即 Compose 中 volumes 参数用来将宿主机的某个目录或文件映射挂载到 Docker 容器内部的对应的目录或文件,通常被用来灵活挂载配置文件或持久化容器产生的数据。
PS:自己动手编写
docker-compose.yml
的时候,可以尝试实验更多场景。比如:新增一个 MySQL 依赖服务、把容器内产生的数据持久化到宿主机等等。
创建相关配置文件#
接下来,需要根据如上docker-compose.yml
文件中涉及的volumes
配置创建三个配置文件。要知道,它们最终是需要被注入到 Docker 容器中的。
首先,在TodoApi
项目根目录中,创建三个应用服务myproject-todoapi-*
需要的程序配置文件appsettings.json
,具体内容如下:
以上配置,指定了 Redis 服务myproject-redis
的连接字符串,其中myproject-redis
可以看到是 Redis 服务的服务名称,当该配置文件注入到 Docker 容器中后,会自动解析为容器内部 IP,同时考虑到 Redis 服务的安全性,为其指定了密码,即password=todoapi@2019
。
然后,在TodoApi
项目根目录中创建一个子目录conf
,用来存放 Nginx 和 Redis 的配置文件。
先来创建 Redis 服务myproject-redis
的配置文件。
可以通过如下命令,下载一个 Redis 官方提供的标准配置文件redis.conf
:
然后打开下载后的redis.conf
文件,找到SECURITY
节点,根据如上应用服务的 Redis 连接字符串信息,启用并改下密码:
再来创建 Nginx 服务myproject-nginx
的配置文件。
在conf
目录中,创建一个名为nginx.conf
的配置文件,并粘贴如下内容:
以上配置,是一个 Nginx 中具备负载均衡的代理配置,其默认采用轮循策略将请求转发给 Docker 服务myproject-todoapi-1
、myproject-todoapi-2
和myproject-todoapi-3
。
运行并测试多容器应用#
经过以上几个小节,容器编排的过程就完成了,接下来就可以直接定义并启动我们创建的多容器应用实例了。
切换到docker-compose.yml
文件所在的目录,也就是TodoApi
项目的根目录,执行如下命令:
如命令执行成功,终端最后会有类似如下输出:
至此,我们的多容器应用就已经在运行了,可以通过docker-compose ps
命令来确认下。
可以通过连续三次请求/api/hello
接口测试应用的负载均衡。
三个应用服务分别部署在不同容器中,所以理论上来讲,他们的容器内部 IP 也是不同的,所以/api/hello
接口每次输出信息不会相同。
请求/api/hello/{name}
接口测试 Redis 服务连通性。
小结#
本文从零构建了一个 ASP.NET Core 应用,并通过 Docker 部署,然后由浅入深,引入 Docker Compose 演示了多容器应用的部署过程。通过本文的实战您可以更深入地了解 Docker。本文涉及的代码已托管到以下地址,您在实验过程中遇到问题可以参考。
https://github.com/esofar/dockerize-aspnetcore-samples
Docker 命令附录#
Docker Compose 命令附录#
相关阅读#
- ASP.NET Core Docker Sample
- Dockerize a .NET Core application
- Best practices for writing Dockerfiles
- Fun With Docker-Compose Using .NET Core And NGINX
- ASP.NET Core 2.2: implementando Load Balancing com Nginx, Docker e Docker Compose
转自:https://www.cnblogs.com/esofar/p/10694319.html?from=timeline
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!