ASP.NET Core应用程序容器化、持续集成与Kubernetes集群部署(一)(转载)
本文结构
ASP.NET Core应用程序的构建
构建ASP.NET Core应用程序的方式有很多种,你可以使用Visual Studio 2017的项目模板直接创建,也可以在安装了.NET Core SDK之后,使用dotnet new命令创建,具体步骤在此也就不再细表,我仍然使用Visual Studio 2017的ASP.NET Core项目模板进行创建。在新建项目对话框中,我们可以选择启用Docker容器支持,这样的话,Visual Studio会在新建的ASP.NET Core项目中添加Dockerfile文件,同时会在解决方案中增加一个Docker Compose的项目,用以实现容器编排。然而,我并不太喜欢使用这一功能,虽然它能够带来很多方便,原因主要有二。首先,一个复杂的应用程序解决方案,项目往往不止一个,各项目的运行环境和配置都会有所不同,使用项目模板创建的Dockerfile和Docker Compose文件有可能还是需要进行修改,甚至重写;其次,我们需要对IDE自动生成的代码了如指掌,这样才能理解并在实际项目中正确使用,与其如此,不如自己根据实际需要自己编写,这样可以让自己对整个项目的各个技术细节都有着深刻的理解和认识。
新建ASP.NET Core项目之后,就可以开始编写代码来实现我们的业务逻辑了。有关Visual Studio 2017开发ASP.NET Core应用程序的详细步骤在这里就不多介绍了,作为这次线下活动的演示案例,我开发了一个简单的App:tasklist,这个App使用Angular 6作为前端框架,TypeScript进行前端编程,后端使用ASP.NET Core Web API构建,基于MongoDB数据库,完整的代码可以在https://github.com/daxnet/tasklist找到。该案例项目使用MIT许可协议开源。
Tasklist的业务非常简单,就是允许用户能够增加、删除任务项目,它的界面如下:
在这个界面中,用户可以在文本框中输入需要完成的任务项目,点击“新增”按钮可以将任务项目添加到列表,也可以在列表中点击“删除”按钮删除指定的项目,文本框下方列出了所有已添加的任务项目。整个后端ASP.NET Core Web API解决方案中各项目的依赖关系如下:
具体的代码实现部分就不多介绍了,这里重点介绍一下ASP.NET Core应用程序容器化时需要注意的几点问题。
ASP.NET Core应用程序容器化所需注意的问题
应用程序的配置信息
容器化的应用程序往往都是在容器启动的过程中,将所需的配置信息通过环境变量注入容器,此时运行于容器中的应用就可以读取环境变量来获得运行参数。比如,使用docker run命令启动容器时,就可以使用-e参数来指定环境变量。因此,理解ASP.NET Core应用程序的配置系统是非常重要的,它有助于应用程序配置体系的设计。在《ASP.NET Core应用程序的参数配置及使用》一文中,我已经简要介绍过ASP.NET Core应用程序的配置系统,可供参考。
在此需要注意的一点是,ASP.NET Core配置系统通常使用冒号(:)来分隔配置数据模型中不同层次的名称。比如,有如下配置数据模型:
"mongo": {
"server": {
"host": "localhost",
"port": 27017
},
"database": "tasklist"
}
如果在C#代码中要访问host,那么就需要使用下面的代码:
var mongoServerHost = Configuration["mongo:server:host"];
然而,如果应用程序需要运行在容器中,这个配置就需要写在容器的编排文件里,比如docker-compose.yml文件。但是,有些容器的编排系统,例如Kubernetes,就不支持在环境变量设置时出现冒号这样的“非法字符”,为此,ASP.NET Core的配置也支持使用双下划线分隔。比如:
这样的话,不仅ASP.NET Core应用程序在容器中能够获得环境变量的配置,而且诸如Kubernetes这样的系统也能在启动容器时,将配置信息设置到环境变量中。
端口侦听
ASP.NET Core应用程序的端口侦听设置也是一个在容器化过程中非常重要的内容。通常,在开发阶段,我们偏向于在Main方法中通过代码的方式指定应用程序所侦听的端口号,比如:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:8087")
.UseStartup<Startup>();
UseUrls支持传入多个参数(因为其参数类型为params string[] urls),所以我们可以使用UseUrls方法给ASP.NET Core应用程序绑定多个端口号和协议(http、https):
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:8087", "http://*:8088", "https://*:8089")
.UseStartup<Startup>();
在cmd中运行dotnet命令,启动发布后的ASP.NET Core应用程序,结果如下:
但是这种方法缺点是很明显的:我们无法在部署应用程序的时候动态设置需要侦听的端口。对于ASP.NET Core应用程序而言,常见的做法有两种:通过命令行参数指定侦听端口,或者使用环境变量。通过命令行参数,只需要在启动应用程序时,指定--server.urls参数即可:
通过分号";",--server.urls参数也可以指定多个端口号和协议(http、https):
或者,如果是使用环境变量,只需要配置ASPNETCORE_URLS变量即可,如下:
通过分号";",ASPNETCORE_URLS环境变量也可以指定多个端口号和协议(http、https):
因此,事实上我们并不需要在Main函数中去显式地指定侦听端口,只需要在最终部署的时候,设置ASPNETCORE_URLS环境变量即可。现在,让我们看看tasklist代码库中,docker-compose.yml文件中有关后端服务的环境变量配置:
service:
image: daxnet/tasklist-service
build:
context: service/tasklist
dockerfile: TaskList.Service/Dockerfile
links:
- db
depends_on:
- db
ports:
- 9020:9020
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ASPNETCORE_URLS=http://*:9020
- mongo__server__host=tasklist-db
- mongo__server__port=27017
- mongo__database=tasklist
container_name: tasklist-service
在上面的配置中:
- ASPNETCORE_ENVIRONMENT:指定ASP.NET Core应用程序运行环境,该参数将决定应用程序配置信息的读取方式
- ASPNETCORE_URLS:指定ASP.NET Core应用程序的侦听端口
- mongo__server__host:MongoDB的服务器名称
- mongo__server__port:MongoDB的侦听端口
- mongo__database:MongoDB的数据库名称
ASP.NET Core的容器版本
微软官方发布了.NET Core/ASP.NET Core的docker容器镜像,可以在https://hub.docker.com/r/microsoft/dotnet/中找到。开发人员需要根据不同的场景来选用不同的tag。比如:
- 2.1-sdk:包含了.NET Core 2.1 SDK
- 2.1-aspnetcore-runtime:包含了ASP.NET Core 2.1的运行库
- 2.1-runtime:包含了.NET Core 2.1的运行库
此外,在这个repo下,还有一些预览版的tag,可以在https://hub.docker.com/r/microsoft/dotnet/tags/页面找到所有的tag。就ASP.NET Core而言,在2.0(含)之前,需要使用microsoft/aspnetcore这个docker容器镜像,而从2.1开始,则需要使用上面提到的microsoft/dotnet这个容器镜像。总之,对于容器镜像和tag的选择需要慎重,否则有可能出现一些奇奇怪怪的问题。
docker镜像构建上下文(Build Context)与Dockerfile的配套使用
在上面的docker-compose.yml片段中,我们指定了ASP.NET Core应用程序的docker镜像构建上下文,为service/tasklist目录,于是,接下来所有与构建docker镜像相关的操作,都会基于这个构建上下文来执行。首先,通过dockerfile指定了Dockerfile的位置是:service/tasklist/TaskList.Service/Dockerfile(注意这里已经将构建上下文路径带入进来);然后,我们了解一下Dockerfile的具体内容:
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 9020 FROM microsoft/dotnet:2.1-sdk AS publish
WORKDIR /src
COPY . .
RUN dotnet restore
WORKDIR "/src/TaskList.Service"
RUN dotnet publish "TaskList.Service.csproj" -c Release -o /app FROM base AS final
WORKDIR /app
COPY --from=publish /app .
CMD ["dotnet", "TaskList.Service.dll"]
这个Dockerfile分成三个部分:第一部分指定运行时会采用microsoft/dotnet:2.1-aspnetcore-runtime这个tag,运行目录为/app目录,并会向外界暴露9020端口;第二部分就是应用程序的编译部分,这里采用microsoft/dotnet:2.1-sdk作为编译环境,先设置容器中的工作目录为/src,然后,将service/tasklist目录下的所有内容全部复制到容器中的/src目录(注意,虽然COPY指令后面是两个点号,但由于我们已经指定了镜像构建上下文,因此,第一个点号就表示service/tasklist目录,第二个点号就表示容器中的当前目录,也就是/src目录),接着就是标准的dotnet restore命令,然后就是进入到/src/TaskList.Service目录,执行dotnet publish指令,从而编译整个项目,并将编译结果输出到/app目录;到了第三部分,将第二部分的输出结果复制到第一部分容器中的/app目录(也就是最后那个点号所指定的目录),然后执行dotnet命令启动服务。
事实上,如果你在创建ASP.NET Core应用程序时,启用了docker支持,那么Visual Studio会在你的项目中添加一个Dockerfile,内容与上面的Dockerfile类似,不过需要注意的是,使用这个自动生成的Dockerfile之前,需要弄清楚镜像构建上下文,否则直接通过docker build命令是无法正常完成镜像构建的。
在容器化ASP.NET Core应用程序方面,我暂时先介绍这些内容;接下来看看前端部分需要做些什么。
前端应用:nginx的反向代理
在tasklist案例中,前端我采用的是Angular 6框架,使用TypeScript编写。由于是一个单页面应用,因此,我没有选择相对比较重的Jetty、Tomcat、IIS等Web容器,而是选择使用了比较轻量的nginx。当然,前端通过http请求访问ASP.NET Core Web API应用程序所提供的RESTful API接口,那么这里就有一个访问URL的问题。使用过Angular框架的开发者都知道,通过environment.ts(或者environment.prod.ts)代码文件,可以针对不同的运行环境(Development, Staging或者Production)来选择设置不同的配置数据,那么,后端服务的URL地址又该如何设置呢?
- 使用绝对路径:这不是个好的做法,这就要求将后端API的全路径都写死(Hard Code)在environment.prod.ts里,显然不是一种合理的做法
- 使用相对路径:这种做法会使得前端App调用后端API时,产生一个错误的URL。比如,假设前端运行在localhost:80,而后端是localhost:9020,那么如果我们指定API的URL是相对路径/api/service,那么当前端程序运行时,它请求的API地址就成了http://localhost/api/service,而不是http://localhost:9020/api/service
在tasklist中,我选择了使用相对路径,然后更改nginx的配置,设置了一条反向代理规则:
events {
worker_connections 4096;
} http { server {
listen 80;
server_name localhost; include /etc/nginx/mime.types; location / {
root /usr/share/nginx/html;
index index.html index.htm;
} location /api {
proxy_pass http://tasklist-service;
}
} upstream tasklist-service {
server tasklist-service:9020;
}
}
在这里,当前端页面请求/api路径时,nginx会自动重定向到http://tasklist-service:9020/api,此时就能正确完成RESTful API调用。注意:这里的tasklist-service是ASP.NET Core应用程序的运行机器名,请参考docker-compose.yml文件中service配置部分的container_name设置。
同样,基于docker镜像构建上下文,我们可以使用容器来编译和运行前端代码:
# 基于node 8容器作为编译环境
FROM node:8 AS build # 首先安装Angular CLI
RUN npm install -g @angular/cli@6.1.5 # 然后将源代码复制到容器中
WORKDIR /src
COPY . .
# 执行npm install以及Angular的编译
RUN npm install
RUN ng build --prod # 基于nginx容器作为运行环境
FROM nginx AS final # 将nginx.conf配置文件复制到容器指定目录
COPY nginx.conf /etc/nginx/nginx.conf # 将Angular编译输出复制到nginx的指定目录
COPY --from=build /src/dist/tasklist /usr/share/nginx/html
在容器中运行整个应用程序
在此,我选择使用Docker for Windows来运行整个tasklist应用程序。首先启动Docker for Windows,然后打开Windows命令行工具,进入到tasklist目录,执行:
docker-compose up --build
经过一段漫长时间的构建过程之后,所有的服务都会启动:
在浏览器中打开我们的应用:
总结
本文为ASP.NET Core应用程序容器化、持续集成、持续部署话题的第一部分,重点介绍了ASP.NET Core应用程序容器化时需要注意的地方,并展示了整个案例的运行效果。下文会接着讨论基于Azure DevOps的持续集成,看看如何使用Azure DevOps的服务来完成项目的自动编译。
ASP.NET Core应用程序容器化、持续集成与Kubernetes集群部署(一)(转载)的更多相关文章
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(二):部署
前面又是废话 我之前写过: Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署 Asp.Net Core 程序部署到Linux(centos)生产环境(二):doc ...
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(三):搭建jenkins集群环境
写在前面 大家可以看到本文的配图,左边是jenkins单机环境,右边是jenkins集群.个中区别,不言而喻,形象生动. 前面我分别介绍了.net core 程序的多种部署方式(无绝对孰优孰劣): 1 ...
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(一):Jenkins安装
2019/1/31更新,经过我一段时间的使用 建议大家的jenkins还是不要使用docker方式安装 建议大家的jenkins还是不要使用docker方式安装 建议大家的jenkins还是不要使用d ...
- ASP.NET Core & Docker & Jenkins 零基础持续集成实战
原文:ASP.NET Core & Docker & Jenkins 零基础持续集成实战 一.本系列教程说明 源代码管理工具:Gogs 持续集成工具:Jenkins 容器:Docker ...
- 容器化|自建 MySQL 集群迁移到 Kubernetes
背景 如果你有自建的 MySQL 集群,并且已经感受到了云原生的春风拂面,想将数据迁移到 Kubernetes 上,那么这篇文章可以给你一些思路. 文中将自建 MySQL 集群数据,在线迁移到 Kub ...
- Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(四):发布与回滚
写在前面 我们以前windows跑.net Framework程序的时候,发布,自己乖乖的替换程序:备份,也是自己一个一个的重命名备份:回滚,发布遇到问题的回滚更是不用说了:运维很是怕我们 这些用wi ...
- 在 Linux 部署多节点 Kubernetes 集群与 KubeSphere 容器平台
KubeSphere 是在 Kubernetes 之上构建的以应用为中心的企业级容器平台,所有供为用户提供简单易用的操作界面以及向导式操作方式.同时,KubeSphere Installer 提供了 ...
- 使用Docker部署ASP.NET Core应用程序实践
前言 最近把很火的Docker给看了,于是就磨拳擦掌要去实践一下.于是就拿之前一个aps.net core的项目(已被停止)去练手.该项目之前在ubuntu14.04上确保可以正常运行,所以docke ...
- 使用 Docker 在 Linux 上托管 ASP.NET Core 应用程序
说在前面 在阅读本文之前,您必须对 Docker 的中涉及的基本概念以及常见命令有一定了解,本文侧重实战,不会对相关概念详述. 同时请确保您本地开发机器已完成如下安装: Docker 18.06 或更 ...
随机推荐
- MySQL字符集、information_schema元数据(八)
一.SQL字符集 它是一个系统支持的所有抽象字符的集合.字符是各种文字和符号的总称,包括各国家的文字.标点符号.图形符号.数字等 常用的字符集有:utf8.utf8mb4.utf8mb3(8.0),现 ...
- 3. 上网调查一下目前流行的源程序版本管理软件和项目管理软件都有哪些, 各有什么优缺点? (提示:搜索一下Microsoft TFS、GitHub、Trac、Bugzilla、Rationale,Apple XCode),请用一个实际的源代码管理工具来建立源代码仓库,并签入/签出代码。
上网调查一下目前流行的源程序版本管理软件和项目管理软件都有哪些, 各有什么优缺点? ---------------答题者:徐潇瑞 (1)Microsoft TFS的优缺点: 优点:是对敏捷,msf,c ...
- django 基础进阶ORM COOKIE
ORM: class Book(models.Model): title=models.CharFiled(max_length=32) 类-----------------表 # Book- ...
- CEF 拦截打开超链接事件
使用 CEF 加载指定页面后,如果你希望控制页面在打开超链接时根据自己预定义的一些行为来操作,比如在自己的 UI 框架中新建一个 Tab 页又或者阻止打开新的页面等.我们就需要通过 CEF 提供的两个 ...
- unity客户端自动断开连接
问题描述: 当unity失去焦点一段时间后,与photon服务器端断开连接 问题根源: 默认unity失去焦点后会暂停运行,导致连接超时,服务器端超时断开连接 解决方案: 连接服务器后,填加一句代码让 ...
- 在执行一行代码之前CLR做的68件事
因为CLR是一个托管环境,所以运行时中有几个组件需要在执行任何代码之前初始化.本文将介绍EE(执行引擎)启动程序,并详细检查初始化过程.68只是一个粗略的指南,它取决于您使用的运行时版本.启用了哪些功 ...
- 以py脚本形式ORM操作 及 django终端打印sql语句的设置
1. 在Django项目的settings.py文件中,在最后复制粘贴如下代码: LOGGING = { 'version': 1, 'disable_existing_loggers': False ...
- 洛谷 P1965 转圈游戏
洛谷 P1965 转圈游戏 传送门 思路 每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,--,依此类推,第n − m号位置上的小伙伴走到第 0 号 ...
- 【图解】给面试官解释TCP的三次握手与四次挥手-Web运用原理及网络基础
作者 | Jeskson 来源 | 达达前端小酒馆 轻松了解HTTP协议 为什么要学习网络协议呢?为什么要学习计算机完了呢?显然这很重要,至少能够帮助你找到工作的原因之一,学习网络知识点太多太多,没有 ...
- (近万字)一篇文章带你了解HTML5和CSS3开发基础与应用-适合前端面试必备
作者 | Jeskson 来源 | 达达前端小酒馆 HTML5和CSS3开发基础与应用,详细说明HTML5的新特性和新增加元素,CSS3的新特性,新增加的选择器,新的布局,盒子模型,文本,边框,渐变, ...