是不是现在每个团队都需要上K8s才够潮流,不用K8s是不是就落伍了。今天,我就通过这篇文章来回答一下。

一、先给出我的看法和建议

我想说的是,对于很多的微小团队来说,可能都不是一定要上K8s,毕竟上K8s也是需要成本和人力的。对像我司一样的传统企业做数字化转型的信息团队来讲,人数不多,没有专门的Ops人员,领导又想要尽快迭代支持公司业务发展,而且关键还要节省成本(内心想法是:WTF)。

在此之下,信息团队需要综合引入先进技术带来的价值以及需要承担的成本和风险。任何架构的产生,都会解决一定的问题,但是同样也会引入新的复杂度,正如微服务架构风格,看着香实际吃着才知道需要承受很多的“苦”(比如数据一致性又比如服务的治理等等)。

因此,结合考虑下来,我的建议是开发测试环境使用Docker Compose进行容器编排即可,而UAT或生产环境则建议使用云厂商的K8s服务(比如阿里云ACK服务)而不选择自建K8s集群

那么,今天就跟大家介绍一下如何使用Docker Compose这个轻量级的编排工具实现.NET Core微服务的持续发布。

二、Docker Compose

Docker主要用来运行单容器应用,而Docker Compose则是一个用来定义和应用多容器应用的工具,如下图所示:

使用Docker Compose,我们可以将多容器的定义和部署方式定义在一个yml文件中,这种方式特别是微服务这种架构风格,可以将多个微服务的定义及部署都规范在一个yml文件中,然后一键部署、启动或销毁整个微服务应用。所有的一切操作,只需要下面的一句话:

$docker-compose up

Compose 的安装请参考:https://docs.docker.com/compose/install/#install-compose,这里就不再赘述,它不是本文重点。

安装后验证:

$docker-compose --version
docker-compose version 1.25., build a82fef07

三、一个简单的发布流程示例

本文演示示例的流程大概会如下图所示:

阅读过我之前的一篇文章《基于Jenkins Pipeline的ASP.NET Core持续集成实践》的童鞋应该对这个流程比较熟悉了。这里,我仍然延续这个流程,作为一个平滑过渡。首先,我们在Jenkins上触发容器的发布流水线任务,此任务会从Git服务器上拉取指定分支(一般都是测试分支)的最新代码。

其次,在CI服务器上使用.NET Core SDK执行Build编译和发布Release文件,基于发布后的Release文件进行镜像的打包(确保你的项目里面都有Dockerfile且设置为“始终复制”)。然后,基于打包后的镜像,将其推送到企业的私有Registry服务器上(即本地镜像仓库,可以基于Harbor搭建一个,也可以直接用Docker Registry搭建一个,不建议使用docker hub的公有库,如何搭建私有镜像仓库可以参考我的这一篇文章:《Docker常用流行镜像仓库的搭建》)。

最后,在测试服务器或要运行容器的服务器上执行docker compose up完成容器的版本更新。当然,也可以直接在docker-compose.yml文件内设置编译路径完成编译和发布的操作(Dockerfile里面定义进行Build和Publish)。这里目的在于让实例更简单,且能让初学者更容易理解,于是我就分开了。

四、.NET Core微服务发布示例

微服务示例准备

假设我们有一堆使用ASP.NET Core开发的微服务,这些微服务主要是为了实现诸如API网关、Identity鉴权、Notification通知、Job中心等基础设施服务,因此我们将他们整合在一起进行持续集成和部署。

这里为了让示例尽可能简单,每个微服务的Dockerfile只有以下几句(这里以一个通知API服务为例):

FROM reg.xdp.xi-life.cn/xdp-service-runtime:2.2
WORKDIR /app
COPY . /app
EXPOSE
ENTRYPOINT ["dotnet", "XDP.Core.Notification.API.dll"]

其中这里的容器镜像来自于私有镜像仓库,是一个封装过的用于ASP.NET Core Runtime的容器镜像。当然,上面说过,也可以在Dockerfile里面进行服务的编译和发布。

流水线任务脚本

同样,为了在Jenkins上快速进行微服务的镜像构建和推送以及部署,我们也需要编写一个流水线构建任务。

下面是这个示例流水线任务的脚本:

pipeline{
agent any
environment {
API_CODE_BRANCH="*/master"
SSH_SERVER_NAME_REGISTRY="XDP-REGISTRY-Server"
SSH_SERVER_NAME_DEV="XDP-DEV-Server"
SSH_SERVER_NAME_AT="XDP-AT-Server"
SSH_SERVER_NAME_SIT="XDP-SIT-Server"
}
stages {
stage('XDP Core APIs Checkout & Build') {
steps{
checkout([$class: 'GitSCM', branches: [[name: env.API_CODE_BRANCH]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '35b9890b-2338-45e2-8a1a-78e9bbe1d3e2', url: 'http://192.168.18.150:3000/XDP.Core/XDP.Core.git']]])
echo 'Core APIs Dev Branch Checkout Done'
bat '''
dotnet build XDP.Core-InfraServices.sln
dotnet publish "%WORKSPACE%\\src\\services\\XDP.Core\\Components\\XDP.Core.ApiGateway\\XDP.Core.ApiGateway.csproj" -o "%WORKSPACE%\\XDP.Core.ApiGateway.API\\publish" --framework netcoreapp2.
dotnet publish "%WORKSPACE%\\src\\services\\XDP.Core\\Components\\XDP.Core.ApiGateway.Internal\\XDP.Core.ApiGateway.Internal.csproj" -o "%WORKSPACE%\\XDP.Core.ApiGateway.Internal.API\\publish" --framework netcoreapp2.
dotnet publish "%WORKSPACE%\\src\\services\\XDP.Core\\Services\\XDP.Core.Authorization.API\\XDP.Core.Authorization.API.csproj" -o "%WORKSPACE%\\XDP.Core.Authorization.API\\publish" --framework netcoreapp2.
dotnet publish "%WORKSPACE%\\src\\services\\XDP.Core\\Services\\XDP.Core.Authorization.Job\\XDP.Core.Authorization.Job.csproj" -o "%WORKSPACE%\\XDP.Core.Authorization.Job\\publish" --framework netcoreapp2.
dotnet publish "%WORKSPACE%\\src\\services\\XDP.Core\\Services\\XDP.Core.Identity.API\\XDP.Core.Identity.API.csproj" -o "%WORKSPACE%\\XDP.Core.Identity.API\\publish" --framework netcoreapp2.
dotnet publish "%WORKSPACE%\\src\\services\\XDP.Core\\Services\\XDP.Core.Notification.API\\XDP.Core.Notification.API.csproj" -o "%WORKSPACE%\\XDP.Core.Notification.API\\publish" --framework netcoreapp2.
dotnet publish "%WORKSPACE%\\src\\services\\XDP.Core\\Services\\XDP.Core.JobCenter\\XDP.Core.JobCenter.csproj" -o "%WORKSPACE%\\XDP.Core.JobCenter.API\\publish" --framework netcoreapp2.
'''
echo 'Core APIs Build & Publish Done'
}
}
stage('XDP API Gateway Docker Image') {
steps{
bat '''
docker rmi reg.xdp.xi-life.cn/core-apigateway-portal:latest;
cd XDP.Core.ApiGateway.API/publish;
docker build -t reg.xdp.xi-life.cn/core-apigateway-portal:latest .;
docker push reg.xdp.xi-life.cn/core-apigateway-portal:latest;
'''
echo 'XDP Portal API Gateway Deploy Done' bat '''
docker rmi reg.xdp.xi-life.cn/core-apigateway-internal:latest;
cd XDP.Core.ApiGateway.Internal.API/publish;
docker build -t reg.xdp.xi-life.cn/core-apigateway-internal:latest .;
docker push reg.xdp.xi-life.cn/core-apigateway-internal:latest;
'''
echo 'XDP Internal API Gateway Deploy Done'
}
}
stage('Core Identity API Docker Image') {
steps{
......
}
}
stage('Core Authorization Job Docker Image') {
steps{
......
}
}
stage('Core Notification API Docker Image') {
steps{
......
}
}
stage('Core JobCenter API Docker Image') {
steps{
......
}
}
stage('Deploy to Local SIT Server') {
steps{
sshPublisher(publishers: [sshPublisherDesc(configName: env.SSH_SERVER_NAME_SIT,
transfers: [sshTransfer(cleanRemote: false, excludes: '',
execCommand: '''
cd compose/xdp;
IMAGE_TAG=latest docker-compose down;
docker rmi $(docker images -q)
IMAGE_TAG=latest docker-compose up -d;
''',
execTimeout: , flatten: false, makeEmptyDirs: false,
noDefaultExcludes: false, patternSeparator: '[, ]+',
remoteDirectory: 'compose/xdp/', remoteDirectorySDF: false,
removePrefix: '',
sourceFiles: '',
excludeFiles: '')],
usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
echo 'Deploy to XDP SIT Server Done'
}
}
}
}

这个脚本我省去了一些重复的内容,只需要了解它的职责即可。

需要注意的地方有几点:

(1)在进行dotnet build的时候,要明确SDK使用哪个版本,比如因为这里的示例代码是基于.NET Core 2.2开发的因此这里使用的是2.2。如果你使用的是2.1,则标注2.1,如果是3.1,则标注3.1。

(2)在进行docker build的时候,要明确镜像使用哪个Tag,这里因为是本地开发测试环境,所以直接简单暴力的直接使用了latest这个Tag。

(3)在进行sshPublish的时候,要提前将docker-compose.yml配置拷贝到对应的指定目录下。当然,这一块建议也将其纳入git仓库进行统一管理和统一发布到不同的环境的指定目录下。

(4)如果你的Jenkins是装在Windows Server上,要记住只有Windows Server 2016及以上版本才支持Docker,否则无法直接进行docker的命令行操作。如果低于2016,Windows 10专业版也可以,不过不建议。

扩展点:

是否可以一套docker-compose方案标准化部署到多个测试环境?是可以的,我们可以在Jenkins构建任务中配置Parameters,这样就可以一次性部署到多个环境。例如,下面的示例中我设置了一个每次发布可以选择到底要发布到哪个环境,这里是单选,你也可以设置为多选。

效果如下:

docker-compose.yml

终于来到了compose的重点内容:docker-compose.yml

这里我给出上面这个示例的yml示例内容(同样,也省略了重复性的内容):

version: ''

services:
core_apigateway_portal:
image: reg.xdp.xi-life.cn/core-apigateway-portal:${IMAGE_TAG}
container_name: xdp_core_apigateway_portal
restart: always
privileged: true
mem_limit: 1024m
memswap_limit: 1024m
env_file:
- ../docker-variables.env
ports:
- :
volumes:
- /etc/localtime:/etc/localtime core_apigateway_internal:
image: reg.xdp.xi-life.cn/core-apigateway-internal:${IMAGE_TAG}
container_name: xdp_core_apigateway_internal
restart: always
privileged: true
mem_limit: 1024m
memswap_limit: 1024m
env_file:
- ../docker-variables.env
ports:
- :
volumes:
- /etc/localtime:/etc/localtime core_identity_api:
image: reg.xdp.xi-life.cn/core-identity-api:${IMAGE_TAG}
container_name: xdp_core_identity_api
restart: always
privileged: true
mem_limit: 512m
memswap_limit: 512m
env_file:
- ../docker-variables.env
ports:
- :
volumes:
- /etc/localtime:/etc/localtime core_authorization_api:
...... core_authorization_job:
...... core_notification_api:
...... core_jobcenter_api:
...... bff_xams_api:
......

备注:这里使用的是version:2的语法,因为3开始不支持内存限制mem_limit等属性设置。当然,你可以使用3的语法,去掉mem_limit和memswap_limit属性即可。

这里的env环境变量配置是定义在另外一个单独的env文件里面的,建议每个环境建立一个单独的env文件供docker-compose.yml文件使用,比如下面是一个AT(自动化测试)环境的env文件内容示例:

# define xdp containers env
ASPNETCORE_ENVIRONMENT=at
ALIYUN_ACCESS_KEY=sxxdfdskjfkdsjkds
ALIYUN_ACCESS_SECRET=xdfsfjiwerowuoi
JWT_TOKEN=sdfsjkfjsdkfjlerwewe
IDENTITY_DB_CONNSTR=Server=192.168.16.150;Port=;Database=identity_at;Uid=xdpat;Pwd=xdpdba;Charset=utf8mb4
APIGATEWAY_DB_CONNSTR=Server=192.168.16.150;Port=;Database=services_at;Uid=xdpat;Pwd=xdpdba;Charset=utf8mb4
......
API_VERSION=AT-v1.0.0

这里,最主要的环境变量就是ASPNETCORE_ENVIRONMENT,你需要指定这些要编排的微服务容器使用哪个环境的appSettings。同样,这里也引申出另一个问题,那就是配置的集中管理,可能你会说出类似Apollo,Spring Cloud Config,K8s Configmap之类的解决方案。这里不是本文的重点,也就跳过。

快速实操体验

现在我们来通过在Jenkins中触发构建任务,可以看到如下图所示的流水线任务状态示意:

这样,一个简单的快速发布流水线就完成了,在单机多容器编排部署方面,Docker Compose是个不错的选择。

五、一些扩展

Consul服务发现容器编排

相比很多童鞋也都在使用Consul作为服务发现组件,我们也可以将Consul纳入到Compose中来统一编排。例如,我们可以这样来将其配置到docker-compose.yml中:

services:
consul_agent_server:
image: reg.xdp.xi-life.cn/xdp-consul-runtime:${IMAGE_TAG}
container_name: xdp_consul_agent_server
restart: always
privileged: true
mem_limit: 1024m
memswap_limit: 1024m
env_file:
- ../docker-variables.env
ports:
- :
command:
agent -server -bootstrap-expect= -ui -node=xdp_local_server -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=xdp_local_dc
volumes:
- /etc/localtime:/etc/localtime
- /docker/consul/data:/consul/data
- /docker/consul/conf:/consul/config

这里只使用到了一个Consul Server Agent,你可以配置一个3个Server节点的Consul Server集群,请自行查阅相关资料。此外,基于Compose我们也可以为API网关设置links从而实现服务发现的效果,当然前提是你的服务数量不多的前提下。这种方式是通过网络层面帮你做了一层解析,从而实现多个容器之间的互连。这里也推荐一下俺们成都地区的小马甲老哥的一篇《docker-compose真香》的文章,他讲解了docker的网桥模式。

基于Compose的编译发布一体化

我们可以看到在很多开源项目中都是将编译发布一体化的,因此我们可以看到在这些项目的Dockerfile中是这样写的:

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /app COPY ./*.sln ./NuGet.Config ./
COPY ./build/*.props ./build/ # Copy the main source project files
COPY src/*/*.csproj ./
RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done RUN dotnet restore # Copy everything else and build app
COPY . .
RUN dotnet build -c Release # api-publish FROM build AS api-publish
WORKDIR /app/src/Exceptionless.Web RUN dotnet publish -c Release -o out # api FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS api
WORKDIR /app
COPY --from=api-publish /app/src/Exceptionless.Web/out ./
ENTRYPOINT [ "dotnet", "Exceptionless.Web.dll" ] ......

在Dockerfile中我们看到的是拉取.NET Core SDK来进行Restore、Build和Publish,进一步地提高了标准化的迁移性,也尽可能发挥Docker的集装箱作用。
这时你可以在docker-compose.yml中定义Dockerfile告诉compose先帮我进行Build镜像(这里的build配置下就需要指定Dockerfile的位置):

services:
api:
build:
context: .
image: exceptionless/api:latest
restart: always
......

六、小结

Docker是容器技术的核心、基础,Docker Compose是一个基于Docker的单主机容器编排工具,功能并不像Docker Swarm和Kubernetes是基于Docker的跨主机的容器管理平台那么丰富。

我想你看到这里也应该有了自己的答案,结合我在最开头给的建议,如果你处在一个小团队中,综合人员水平、技能储备、运维成本 及 真实业务量要求,可以在开发测试环境(一般都是单主机环境的话)中使用Docker Compose进行初步编排。而在生产环境,即使是小团队也建议上云主机,利用云的弹性为未来的业务发展做基础,然后可以考虑使用云上的K8s服务来进行生产级的容器编排。

作者:周旭龙

出处:https://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

基于Docker Compose的.NET Core微服务持续发布的更多相关文章

  1. 使用Docker Compose编排Spring Cloud微服务

    文章目录 微服务构建实例 简化Compose的编写 编排高可用的Eureka Server 编排高可用Spring Cloud微服务集群及动态伸缩 微服务项目名称 项目微服务中的角色 microser ...

  2. .Net Core微服务入门全纪录(八)——Docker Compose与容器网络

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 前言 上一篇[.Net Core微服务入门全纪录(七)--IdentityServer4-授权认证]中使用IdentityServer4 ...

  3. .NET Core微服务之基于Jenkins+Docker实现持续部署(Part 1)

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.CI, CD 与Jenkins 互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous i ...

  4. 基于.net core微服务(Consul、Ocelot、Docker、App.Metrics+InfluxDB+Grafana、Exceptionless、数据一致性、Jenkins)

    1.微服务简介 一种架构模式,提倡将单一应用程序划分成一组小的服务,服务之间互相协调.互相配合,为用户提供最终价值.每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(RESTfu ...

  5. .NET Core微服务之基于Consul实现服务治理

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.Consul基础介绍 Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其他分布式服务注册与发 ...

  6. .NET Core微服务之基于Exceptionless实现分布式日志记录

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.Exceptionless极简介绍 Exceptionless 是一个开源的实时的日志收集框架,它可以应用在基于 ASP.NET,AS ...

  7. .NET Core微服务之基于Apollo实现统一配置中心

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.关于统一配置中心与Apollo 在微服务架构环境中,项目中配置文件比较繁杂,而且不同环境的不同配置修改相对频繁,每次发布都需要对应修改 ...

  8. .NET Core微服务之ASP.NET Core on Docker

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 一.Docker极简介绍 1.1 总体介绍 Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源.D ...

  9. .NET Core微服务之基于Steeltoe使用Hystrix熔断保护与监控

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 =>  Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ...

随机推荐

  1. 八、【spring】web应用安全设计

    内容 Spring Security 使用Servlet规范中的Filter保护Web应用 基于数据库和LDAP进行认证 关键词 8.1 理解Spring Security模块 Spring Secu ...

  2. 和付费网盘说再见,跟着本文自己起个网盘(Java 开源项目)

    本文适合有 Java 基础知识的人群,跟着本文可学习和运行 Java 网盘项目. 本文作者:HelloGitHub-秦人 HelloGitHub 推出的<讲解开源项目>系列. 今天给大家带 ...

  3. Java四种权限修饰符

    四种权限修饰符

  4. C# Winform 学习(五)

    目标 1.MDI应用程序 2.图片框控件 3.图片集控件 4.定时器控件 一.MDI应用程序 1.理解: 单文档界面:SDI(word) 多文档界面:MDI(excel) 2.特点: 1)每个MDI程 ...

  5. Java实现 LeetCode 652 寻找重复的子树(两个map的DFS)

    652. 寻找重复的子树 给定一棵二叉树,返回所有重复的子树.对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可. 两棵树重复是指它们具有相同的结构以及相同的结点值. 示例 1: 1 / \ ...

  6. Java实现 LeetCode 524 通过删除字母匹配到字典里最长单词(又是一道语文题)

    524. 通过删除字母匹配到字典里最长单词 给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到.如果答案不止一个,返回长度最长且字典顺序最小的字符 ...

  7. Java实现 蓝桥杯VIP 算法提高 数字黑洞

    算法提高 数字黑洞 时间限制:1.0s 内存限制:256.0MB 问题描述 任意一个四位数,只要它们各个位上的数字是不全相同的,就有这样的规律: 1)将组成该四位数的四个数字由大到小排列,形成由这四个 ...

  8. Java实现二分图的最大权匹配

    1 问题描述 何为二分图的最大权匹配问题? 最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大. 2 解决方案 解决这个问题可以用KM算法.理解KM算法需要首先理解& ...

  9. java实现拍7游戏

    ** 拍7游戏** 许多人都曾经玩过"拍七"游戏.规则是:大家依次从1开始顺序数数,数到含有7或7的倍数的要拍手或其它规定的方式表示越过(比如:7,14,17等都不能数出),下一人 ...

  10. OAuth2.0系列之基本概念和运作流程(一)

    @ 目录 一.OAuth2.0是什么? 1.1 OAuth2.0简介 1.2 OAuth2.0官方文档 二.OAuth2.0原理 2.1 OAuth2.0流程图 三. OAuth2.0的角色 四.OA ...