本文结构


  1. ASP.NET Core应用程序的构建
  2. ASP.NET Core应用程序容器化所需注意的问题
    1. 应用程序的配置信息
    2. 端口侦听
    3. ASP.NET Core的容器版本
    4. docker镜像构建上下文(Build Context)与Dockerfile的配套使用
  3. 前端应用:nginx的反向代理
  4. 在容器中运行整个应用程序
  5. 总结

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地址又该如何设置呢?

  1. 使用绝对路径:这不是个好的做法,这就要求将后端API的全路径都写死(Hard Code)在environment.prod.ts里,显然不是一种合理的做法
  2. 使用相对路径:这种做法会使得前端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集群部署(一)(转载)的更多相关文章

  1. Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(二):部署

    前面又是废话 我之前写过: Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署 Asp.Net Core 程序部署到Linux(centos)生产环境(二):doc ...

  2. Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(三):搭建jenkins集群环境

    写在前面 大家可以看到本文的配图,左边是jenkins单机环境,右边是jenkins集群.个中区别,不言而喻,形象生动. 前面我分别介绍了.net core 程序的多种部署方式(无绝对孰优孰劣): 1 ...

  3. Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(一):Jenkins安装

    2019/1/31更新,经过我一段时间的使用 建议大家的jenkins还是不要使用docker方式安装 建议大家的jenkins还是不要使用docker方式安装 建议大家的jenkins还是不要使用d ...

  4. ASP.NET Core & Docker & Jenkins 零基础持续集成实战

    原文:ASP.NET Core & Docker & Jenkins 零基础持续集成实战 一.本系列教程说明 源代码管理工具:Gogs 持续集成工具:Jenkins 容器:Docker ...

  5. 容器化|自建 MySQL 集群迁移到 Kubernetes

    背景 如果你有自建的 MySQL 集群,并且已经感受到了云原生的春风拂面,想将数据迁移到 Kubernetes 上,那么这篇文章可以给你一些思路. 文中将自建 MySQL 集群数据,在线迁移到 Kub ...

  6. Asp.net Core 使用Jenkins + Dockor 实现持续集成、自动化部署(四):发布与回滚

    写在前面 我们以前windows跑.net Framework程序的时候,发布,自己乖乖的替换程序:备份,也是自己一个一个的重命名备份:回滚,发布遇到问题的回滚更是不用说了:运维很是怕我们 这些用wi ...

  7. 在 Linux 部署多节点 Kubernetes 集群与 KubeSphere 容器平台

    KubeSphere 是在 Kubernetes 之上构建的以应用为中心的企业级容器平台,所有供为用户提供简单易用的操作界面以及向导式操作方式.同时,KubeSphere Installer 提供了 ...

  8. 使用Docker部署ASP.NET Core应用程序实践

    前言 最近把很火的Docker给看了,于是就磨拳擦掌要去实践一下.于是就拿之前一个aps.net core的项目(已被停止)去练手.该项目之前在ubuntu14.04上确保可以正常运行,所以docke ...

  9. 使用 Docker 在 Linux 上托管 ASP.NET Core 应用程序

    说在前面 在阅读本文之前,您必须对 Docker 的中涉及的基本概念以及常见命令有一定了解,本文侧重实战,不会对相关概念详述. 同时请确保您本地开发机器已完成如下安装: Docker 18.06 或更 ...

随机推荐

  1. MySQL数据库(六)-- SQL注入攻击、视图、事物、存储过程、流程控制

    一.SQL注入攻击 1.什么是SQL注入攻击 一些了解sql语法的用户,可以输入一些关键字 或合法sql,来导致原始的sql逻辑发生变化,从而跳过登录验证 或者 删除数据库 import pymysq ...

  2. Github清除历史提交,保留最新提交

    有时候,需要启动一个新的分支,同时想摒弃历史信息,那么可以使用下面的方法来实现 #克隆git仓库 git clone [URL] #进入git仓库 cd [仓库名] #创建一个名为 new_branc ...

  3. 解决Mac下SourceTree每次都让输入密码的问题

    在Mac上操作sourcetree当pull和push时每次都是让输入密码,非常烦人,虽然大概知道是因为SSH什么的问题,但搜索百度也没发现解决办法. 于是乎搜索谷歌,发现如下解决办法. Source ...

  4. 团队第四次作业:alpha1发布成绩汇总

    一.作业题目 团队第四次作业:alpha1发布 二.作业评分标准 博客评分规则(总分100)博客要求 给出开头和团队成员列表(10') 给出发布地址以及安装手册(20') 给出测试报告(40') 给出 ...

  5. 目标检测论文解读6——SSD

    背景 R-CNN系列算法检测速度不够快,YOLO v1检测准确率较低,而且无法检测到密集目标. 方法 SSD算法跟YOLO类似,都属于one stage的算法,即通过回归算法直接从原图得到预测结果,为 ...

  6. 关于ID命名 一个页面唯一

    1.一般ID在一个区域内必须是唯一的.这样是一个规范而且在IE中使用JS通过ID获取这个对象永远只能获取第一个. 2.js无法找到重复的ID,用js获取时,只能得到第一个ID元素,但,如果不同的区域范 ...

  7. django -- ORM建表

    前戏 ORM(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. ORM的优势: ORM解决的主要问题是对象和关系的映射 ...

  8. 凤翔中学高三数学[备课&作业]

    #tab{ font-size:22px ;font-family:楷体;text-align:center;} a:link { text-decoration: none;} 凤中2020届高三文 ...

  9. 深入js系列-类型(数字)

    开头 js数字没有明确区分浮点数和整数类型,统一用number类型表示. number 基于IEEE 754标准实现 js采用的是双精度(64位二进制) 我们看一个基于IEEE 754标准实现都有会有 ...

  10. Linux 进程树查看工具 pstree

    pstree 是 Linux 下的一个用于展示进程树结构的工具,类似于 tree 展示目录树一样,可视化地查看进程的继承关系.pstree 工具其实是 PSmisc 工具集的成员之一,PSmisc 工 ...