天天看點

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

最近一直在研究微服務體系架構。微服務概念一直很火,但是作為一個初學者往往迷失在高深理論與紛繁多樣的技術,而失去了方向,慢慢的,_,還沒開始就已經放棄了。是以還是不得不誇一誇微軟一切以開發者為中心的價值觀:好文檔,好工具。

1.微服務學習線路

1.1 開卷有益

首先我們從微軟的微服務架構的白皮書(中文版,英文版)入手,開卷有益,這是一本無關平台,學習微服務,了解微服務的好書,雖然技術是.NET,但是書中更多的内容是介紹的微服務思想,理論,最佳實踐,其他平台同樣适用,适用于我們整體把控微服務架構體系中的核心問題:

  • 微服務之間的通信
  • 網關
  • 身份認證與授權
  • 資料庫服務
  • DDD
  • CQRS

雖然不一定能夠全部了解書中的所有理論概念,但是總能給到一些啟發,開拓思維。

1.2 實際項目

然後就是微軟架構師利用.net core技術,基于docker容器技術,實作的适用于容器化 .NET 應用程式的體系結構微服務架構demo項目-eShopOnContainer,這個項目在上面的白皮書中也有介紹。

下面大概介紹一下這個項目的架構,雖然是一個demo,其中有部分具有一定的局限性且并不适合生産環境,但是這并不妨礙我們去了解微服務體系架構。

eShopOnContainer

是一個在用戶端、服務端同時可以跨平台的項目。這都得益于 .NET Core能夠跑在不同系統的容器上,windows或者linux。項目還有

Xamarin

移動APP,

ASP.NET Core Web MVC

和一個

SPA

eShopOnContainer

的架構,是一種面向微服務體系架構的實作。這些微服務都是可以自我治理的:

  • 每個微服務有屬于自己的資料庫。
  • 每個微服務都有簡單的CRUD方法、和精細的DDD/CQRS模式方法。
  • 用戶端和微服務通過HTTP協定進行資料交換
  • 微服務之間通過異步消息進行通信
  • 消息隊列可以通過RabbitMQ或者AzureAzure Service Bus去傳遞內建事件。

事件總線

項目中有一個簡化的事件抽象總線,來處理內建事件。這個抽象事件總線在項目中有兩個實作:

  • RabbitMQ
  • Azure Service Bus

這裡對于生産級别的解決方案,微軟建議使用更加健壯的元件。

API 網關

整體架構中還包括了API網關和BFF模式的實作:

  • 釋出簡化的API
  • 在外部消費者和内部微服務之間增加安全措施,以此對外隐藏并保護内部微服務

這些API網關是通過Envoy實作的,我順帶翻閱了下官網,使用Envoy的公司還比較多,基本都是耳熟能詳,

Uber

ebay

,

airbnb

amazon

Google

IBM

Microsoft

,還有騰訊等等。在架構中,Envoy實作的網關,隻執行向内部微服務和自定義聚合器的請求轉發,進而為用戶端提供單一基本的URL.其實還可以通過Envoy實作:

  • 在gRPC于HTTP/REST之間的自動轉換
  • 身份驗證
  • 授權管理
  • 緩存支援

項目中,除了API網關之外,還提供了一組“自定義聚合器”。這些聚合器為某些操作的用戶端提供了一個簡單的API。

  • 移動購物:購物操作的聚合,供XamarinAPP調用
  • PC購物:購物操作的聚合,供Web用戶端調用,(mvc與spa)
之前eShop使用的是Ocelot實作網關的。對于Ocelot,官方給的說法是欲抑先揚:Ocelot很好,很優秀,也是.net core 優秀的開源項目,也支援許多特性,它可以作為.net core項目網關實作候選元件。但是,Ocelot缺乏對gRPC的支援,是以在最新的項目(這個eShopOnContainer項目一直在疊代更新與維護,從衆多分支就可以看出)中就換為Envoy提供網關服務。

自定義聚合器

這個主要用于公開一個具有涉及内部各個微服務之間的複雜方法的HTTP/JSON API,每個自定義聚合器的方法都能調用1個或者多個内部微服務,根據邏輯聚合多個結果并提供給用戶端。從聚合器到微服務的調用的都是使用gRPC

gRPC

在衆多微服務之間,大多數微服務都是通過事件總線和釋出者/觀察者模式進行異步通信。但是,自定義的聚合器和内部微服務之間的同步通信是用gRPC實作的。gRPC是一種基于RPC的協定,具有良好的性能,帶寬占比也低,是内部微服務通信協定中的最佳候選協定。項目中使用了4個網關實作BFF,目前它們是通過

Envoy

來實作的。每個BFF為其用戶端提供一個唯一的端點,然後将調用轉發到特定的微服務或自定義聚合器。

  • 1.用戶端通過Envoy代理暴露的URL調用BFF.
  • 2.通過請求資料,Envoy轉發請求至内部的微服務(簡單的增删改查),或者複雜的聚合器(複雜邏輯),這對用戶端都是透明的。

當調用直接從Envoy轉發到内部微服務時,它是使用HTTP/JSON執行的。也就是說,現在内部微服務公開了一組混合的方法:

  • 一些走gRPC(由聚集器調用)
  • 一些走HTTP/JSON中(由Envoy調用)。

這裡微軟官方進行了展望"這可能會在未來發生變化”,即所有的微服務方法都可以使用gRPC,如果需要,Envoy可以在gRPC和HTTP/JSON之間自動轉換。

微服務内部架構模式

不同類型的微服務可能采用不同的内部架構模式和方法,這取決于微服務的用途。

  • 4個

    SQL Server

    ,部署在同一個容器内

主要是降低記憶體的需求,生産部署不建議這樣做,應該使用High-availability的解決方案。

  • 1個

    Redis

    執行個體,單獨一個容器
  • MongoDb

Redis

MongoDb

都是單獨的容器,作為兩個廣泛使用的NO-SQL資料庫的示例。

其他

項目中除了,上面的架構内容,還有DDD領域驅動開發(Domain Drive Design),CQRS指令與查詢職責分離(Command and Query Responsibility Segregation)的實踐,日志,健康檢查等内容。是以涵蓋的範圍蠻廣,個人覺得非常值得研習。

2.容器化 .NET 應用程式的開發調試

鋪墊了這麼多,終于要進入本篇文章的主題,對于我們的微服務化的應用,我們可以說,我們的應用都是跑在容器上的,或者說我們所有的微服務都跑再容器上(當然容器指的就是docker,docker容器幾乎成為了行業标準)。我們如何進行開發呢,這裡再誇一下微軟,在白皮書中有

Docker 應用開發工作流

  • 編碼:建立應用
  • 為應用建立Dockerfile
  • 建立自定義docker鏡像
  • 定義docker-compose.yaml
  • 建構并運作docker應用
  • 測試docker應用(微服務)
  • 推送代碼送出或者繼續開發

下面就将開始把我的一個應用以容器的方式跑起來,根據上面的工作流進行實踐與書寫

項目概述

這本身是一個公司推送集中平台,接受公司多個産品線的推送請求,然後通過阿裡進行移動推送,然後每一次推送都有背景記錄,進行存儲。由于我接下來實踐的Docker應用開發的工作流,是以實際隻有一個webapi項目,也并不打算去拆分,這對我們實踐意義也不太大。

我們的目标

開發環境拆分為多個docker容器,且調試時能夠正常運作。

2.1 安裝docker-desktop

docker引擎需要運作在linux上,那麼win10就需要裝裝虛拟機:

hyper-v

,實際上docker是跑在這個虛拟機上,windows上的docker适用于測試和開發。生産環境還是linux哈。

即便是win10也請注意下版本:

Docker Desktop requires Windows 10 Pro/Enterprise (15063+) or Windows 10 Home (19018+).

  • 下載下傳:https://www.docker.com/get-started
  • 安裝:傻瓜式點下一步
  • 設定
Resources ADVANCED

選擇虛拟機cpu顆數,記憶體大小

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
Resources-FILE SHARING

docker容器能夠通過volume挂載主控端作業系統(linux)的檔案目錄或目錄,宿主作業系統在Windows的Docker Desktop中,就是指是 Hyper-v 裡的 Linux 系統。但是,如果隻能從hyper-v中的linux系統中進行挂載,顯然不足以達到我們的需求,最友善的方式肯定是直接從Hyper-v的宿主windows裡挂載檔案咯。(有點繞,多了解下,windows>hyper-v>docker) 最終效果:Docker 容器直接挂載主機系統的目錄,我們可以先将目錄挂載到虛拟 Linux 系統上,,再利用 Docker 挂載到容器之中。這個過程被內建在了 Docker Desktop 系列軟體中,我們不需要人工進行任何操作,整個過程已經實作了自動化。這就是FILE SHARING選項的意義。如果還不好了解,往下看。

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
Docker Engine

配置阿裡雲鏡像加速器,使用加速器可以提升擷取Docker官方鏡像的速度,親測還是有用,但是,2018 年五月之後,微軟将後續釋出的所有

docker image

都推送到了 自家的

MCR (Miscrosoft Container Registry)

,但在中國大陸,由于衆所周知的原因,它的速度實在是令人發指。後續有解決方案,文章會講到。

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

2.2 編碼-建立我們的應用

由于項目是現成的,那麼這一步我們可以省略,這個跟您開發一個webapi項目沒有任何差別,原來怎麼做的,現在還是怎麼做。我隻說一個關鍵點,那就是資料初始化,我們的推送資料需要存入資料庫中,你也可以等mysql容器啟動後,再去初始化容器中的mysql資料庫,但是我們能用代碼一步到位:

program.cs

:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace AliMobilePush.Webapi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            //CreateHostBuilder(args).Build().Run();
            var host = CreateHostBuilder(args).Build();
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;

                try
                {
                    SeedData.Initialize(services);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred seeding the DB.");
                }
            }
            host.Run();
        }
        
        //...CreateHostBuilder
    }
}
SeedData.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace AliMobilePush.Infrastructure
{
    public static class SeedData
    {
        public static void Initialize(IServiceProvider serviceProvider)
        {
            using (var context = new PushContext(
                serviceProvider.GetRequiredService<
                    DbContextOptions<PushContext>>()))
            {
                context.Database.EnsureCreated();
                context.SaveChanges();
            }
        }
    }
}
           

2.3 為應用建立Dockerfile

無論是通過

Visual Studio

自動部署,還是通過

Docker CLI

。都需要為應用建立

Dockerfile

。一般情況,Dockerfile是放到應用或者服務的根檔案夾下。這裡有三種方式建立dockerfile。

  • 建立項目時,勾選Enable Docker Support項
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
  • 已經建立好的webapi項目,右鍵 Solution Explorer 然後選擇 Add > Docker Support
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
  • 手寫dockerfile,不是我們本文的重點,請參考另外一篇文章【One by one系列】一步步學習docker(三)——實戰部署dotnetcore

不管哪種方式,一定會或者要在項目根目錄下增加Dockerfile

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base

WORKDIR /app

EXPOSE 80

EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build

WORKDIR /src

COPY ["Webapi/AliMobilePush.Webapi.csproj", "Webapi/"]

COPY ["Infrastructure/AliMobilePush.Infrastructure/AliMobilePush.Infrastructure.csproj", "Infrastructure/AliMobilePush.Infrastructure/"]

COPY ["Domain/AliMobilePush.Domain/AliMobilePush.Domain.csproj", "Domain/AliMobilePush.Domain/"]

COPY ["Application/AliMobilePush.Application/AliMobilePush.Application.csproj", "Application/AliMobilePush.Application/"]

RUN dotnet restore "Webapi/AliMobilePush.Webapi.csproj"

COPY . .

WORKDIR "/src/Webapi"

RUN dotnet build "AliMobilePush.Webapi.csproj"  -o /app/build

FROM build AS publish

RUN dotnet publish "AliMobilePush.Webapi.csproj"  -o /app/publish

FROM base AS final

WORKDIR /app

COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "AliMobilePush.Webapi.dll"]
           

上面dockerfile分為了

base

build

publish

三個階段的多階段建構.

1.base
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base

WORKDIR /app

EXPOSE 80

EXPOSE 443
           

Debian10的asp.net core 運作時image開頭,并建立公開端口80,443的中間image

base

2.build
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build

WORKDIR /src

COPY ["Webapi/AliMobilePush.Webapi.csproj", "Webapi/"]

COPY ["Infrastructure/AliMobilePush.Infrastructure/AliMobilePush.Infrastructure.csproj", "Infrastructure/AliMobilePush.Infrastructure/"]

COPY ["Domain/AliMobilePush.Domain/AliMobilePush.Domain.csproj", "Domain/AliMobilePush.Domain/"]

COPY ["Application/AliMobilePush.Application/AliMobilePush.Application.csproj", "Application/AliMobilePush.Application/"]

RUN dotnet restore "Webapi/AliMobilePush.Webapi.csproj"

COPY . .

WORKDIR "/src/Webapi"

RUN dotnet build "AliMobilePush.Webapi.csproj"  -o /app/build
           

build階段是從編譯工具—sdk鏡像開始,而不是

aspnet

,那是因為隻有

sdk

鏡像用後建構編譯工具,是以sdk鏡像也比aspnet鏡像大。先還原

restore

,再

publish

3.publish
FROM build AS publish

RUN dotnet publish "AliMobilePush.Webapi.csproj"  -o /app/publish

FROM base AS final

WORKDIR /app

COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "AliMobilePush.Webapi.dll"]
           

最後階段再次從

base

開始,包括

COPY --from=publish /app/publish .

将釋出的輸出複制到最終鏡像中。由于無需包含

sdk

鏡像中的建構編譯工具,是以此過程可以使最終鏡像小得多。

官方最佳實踐,多階段建構鏡像,這樣生成過程更高效,并使容器更小。官方文檔,整個多階段建構 可以讓後一個階段建構可以使用前一個階段建構的産物,形成一條建構階段的chain;最終結果僅産生一個image,避免産生備援的多個臨時images或臨時容器對象,這正是我們所需要的:我們隻需要個結果。

2.4 建立自定義docker鏡像

一個服務對應一個鏡像,需要知道,在

Visual Studio

的強大功能下,docker鏡像是自動建立的。

作為開發者,隻要功能沒完成,或者代碼不送出到版本控制。都是需要在本地部署和測試的。那麼這就意味你需要在本地的docker主機上建立docker鏡像,部署docker容器,并在這些容器上去運作,測試,調試。使用

Visual Studio

建立具有 Docker 支援的項目時,不會顯示的建立映像。 而是在按下 F5(或 Ctrl-F5)運作docker 化的應用程式或服務時建立映像 。

Visual Studio

會自動執行這個操作,開發人員不會看到該過程,但務必要了解其原理。

2.5 定義docker-compose.yaml

定義服務,建立多容器應用,主要是可以在

docker-compose.yml

中定義一系列的服務。通過部署指令将其部署為組合應用程式。 它還配置其依賴項關系和運作時配置。在主解決方案檔案夾或根解決方案檔案夾中建立該

docker-compose.yml

檔案,

docker-compose.yml

是可以拆分成多個

docker-compose

檔案。然後根據不同的環境去覆寫值。添加docker-compose.yml檔案也有兩種方式

  • 已經建立好的webapi項目,右鍵 Solution Explorer 然後選擇 Add>Container Orchestrator Support
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
  • 手寫

    docker-compose.yml

    ,這個後續博文會詳細介紹,亦不是本篇的重點。是以下面重點介紹第一種方式:

第一次作Solution Explorer > Add>Container Orchestrator Support操作

  • 會在api項目下增加

    Dockerfile

    ,如果原本沒有的話
  • 會在解決方案目錄增加
    • docker-compose.dcproj

    • docker-compose.override.yml

    • docker-compose.yml

    • .dockerignore

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

docker-compose.yml

version: '3.4'

services:
  webapi:
    build:
      context: .
      dockerfile: Webapi/Dockerfile
    networks:
        - asp-net
    depends_on:
        - "cachedata"     
        - "sqldata"
  cachedata:
    image: redis
    networks:
        - asp-net
  sqldata:
    image: mysql
    networks:
        - asp-net
    
networks:
    asp-net:
        driver: bridge
           

docker-compose.override.yml

version: '3.4'

services:
  webapi:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
    ports:
      - "5000:5000"
      - "5001:5001"
    volumes:
      - ./docker/log/alipush.log:/app/alipush.log

  cachedata:
    ports:
        - "6379:6379"
    volumes:
        - ./docker/data/redis:/data

  sqldata:
    ports:
        - "3307:3306"
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    volumes:
       - ./docker/data/mysql:/var/lib/mysql
           

注意:修改資料庫連接配接配置

這裡需要注意,所有有用到鏡像間的通信的地方,我們都需要使用鏡像名進行指代,例如我們需要修改程式的資料庫通路字元串的伺服器位址

Mysql:

"ConnectionStrings": {
    "PushContext": "Persist Security Info=False;database=pushcenter;server=sqldata;Connect Timeout=30;user id=pushcenter; pwd=123456"
  },
           

Redis:

"Redis": {
    "ConnectionString": "cachedata,defaultDatabase=1",
    "Instance": "push_request_",
    "Timeout": 1
  },
           

2.6 建構并運作docker應用

如果是單容器應用,直接跑。

如果多服務容器應用,就有兩個選擇

  • docker-compose up

  • Visual Studio

2.6.1 單容器應用

使用docker指令,

docker run

即可

docker run -t -d -p 80:5000 cesardl/netcore-webapi-microservice-docker:first
           

2.6.2 運作多容器應用

使用

docker-compose

指令,

docker-compose up

docker-compose up

docker run

指令(或在

Visual Studio

中運作和調試容器)足以在開發環境中測試容器。 但不應該将這種方法用于生産部署,在生産部署中應該以業務流程協調程式為目标,,比如K8S,或者

docker swarm

在Visual Studio 中運作和調試容器
  • 1.選擇解決方案中選擇

    docker-compose

    項目,Solution Explorer > Set as a Startup Project
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
  • F5

    開始運作調試吧

可以在output-build視窗下觀察:

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

實際上,是visual studio幫我們直接執行了

docker-compose -f docker-compose.yml

的指令

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

然後緊接着,docker-compose就會

  • 建立橋接網絡
  • 建立并啟動redis,mysql容器:按照docker-compose.yml的依賴 depends_on項
  • 建立并啟動webapi容器

建構的過程中,win10會一直提示,檔案是否共享,會一直不停的點share it.這時我們去觀察下:

docker-desktop>Resources>FILE SHARING

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

沒錯,我們把這些主機(win10)檔案夾挂載到hyper-v(虛拟機,docker主控端),hyper-v又挂載到容器,實作主機檔案夾與容器檔案夾的映射。

再看下結果:鏡像與容器

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

然後就可以打斷點調試容器應用了。

如果你發現建構的鏡像與容器有問題,想重新來過,vs大法提供了如下方法:

Solution>Clean Solution

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

再在output-build視窗下觀察:

  • 先kill服務
  • 然後在删掉容器
  • 最後删掉應用的鏡像-不過實際沒有删掉
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

應用容器倒是停了并且删除了,但是mysql,redis這些容器資料服務,僅僅隻是停了。

注意:dockerfile裡面的

mcr.microsoft.com/dotnet/core/sdk:3.1-buster

鏡像,下載下傳巨慢,建構一次,一碗番茄煎蛋面都要做好了。

國内下載下傳微軟鏡像慢的解決方案

https://github.com/newbe36524/Newbe.McrMirror

使用docker-mcr下載下傳鏡像

dotnet tool install newbe.mcrmirror -g 
docker-mcr -i mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim
docker-mcr -i mcr.microsoft.com/dotnet/core/sdk:3.1-buster
           

把建構過程需要下載下傳的鏡像,先提前下下來吧。

測試

測試用例1 webapi-swagger
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
測試用例2 mysql能否通路,且通過ef生成了資料庫
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
測試用例3 redis能否通路
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式
測試用例4 檔案挂載是否正常(舉例一個即可)
【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

2.7 推送代碼送出或者繼續開發

推送下班,避免996

或者繼續開發

3.Visual Studio大法好

實際上,使用

Visual Studio

進行開發的工作流比使用編輯器或CLI 方法的工作流簡單得多。

Visual Studio

隐藏或簡化了 Docker 需要執行的與 Dockerfile 和 docker-compose.yml 檔案相關的大部分步驟

  • 自動生成Dockerfile,可編輯
  • 自動生成docker-compose.yml,可編輯
  • 自動執行docker-compose up,且可調試
  • 可自動停止且并移除容器

微軟以開發者為中心的價值觀,為開發者省了不少事,Visual Studio不愧為宇宙第一的IDE。

參考連結

https://www.cnblogs.com/xianwang/p/12039922.html

https://zhuanlan.zhihu.com/p/147369525

http://www.imooc.com/article/259789

https://my.oschina.net/u/4285813/blog/3661653/print

https://github.com/dotnet-architecture/eShopOnContainers

https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/

作者:Garfield

同步更新至個人部落格:http://www.randyfield.cn/

本文版權歸作者所有,未經許可禁止轉載,否則保留追究法律責任的權利,若有需要請聯系[email protected]

微信公衆号

掃描下方二維碼關注個人微信公衆号,實時擷取更多幹貨

【One by one系列】微服務:一步步開發與調試容器化的 .NET 應用程式

同步更新至:http://www.randyfield.cn/

出處:http://www.cnblogs.com/RandyField/

本文版權歸作者和部落格園共有,未經許可禁止轉載,否則保留追究法律責任的權利,若有需要請聯系[email protected].