ASP.NET Core 實戰:使用 Docker 容器化部署 ASP.NET Core + MySQL + Nginx

来源:https://www.cnblogs.com/danvic712/archive/2019/03/21/10566750.html
-Advertisement-
Play Games

一、前言 在之前的文章(ASP.NET Core 實戰:Linux 小白的 .NET Core 部署之路)中,我介紹瞭如何在 Linux 環境中安裝 .NET Core SDK / .NET Core Runtime、Nginx、MySQL,以及如何將我們的 ASP.NET Core MVC 程式部 ...


 一、前言

  在之前的文章(ASP.NET Core 實戰:Linux 小白的 .NET Core 部署之路)中,我介紹瞭如何在 Linux 環境中安裝 .NET Core SDK / .NET Core Runtime、Nginx、MySQL,以及如何將我們的 ASP.NET Core MVC 程式部署到 Linux 上,同時,使用 supervisor  守護程式守護我們的 .NET Core 程式。如果,你有看過那篇文章,並且和我一樣是個 Linux 小白用戶的話,可能第一感覺就是,把 .NET Core 項目部署在 IIS 上也挺好。

  將 .NET Core 項目部署到 Linux 上如此複雜,就沒有簡單的部署方式嗎?

  你好,有的,Docker 瞭解一下~~~

  PS:這裡的示例代碼還是採用之前的畢業設計項目,在這篇文章發佈的時候,我已經在程式的倉庫中添加了對於 Docker 的支持,你可以下載下來,自己嘗試一下,畢竟,實踐出真知。

   代碼倉儲:https://github.com/Lanesra712/Danvic.PSU

 二、Step by Step

  1、安裝 Docker & Docker Compose

  在代碼交付的過程中,偶爾會遇到這樣的問題,在本地測試是好的,但是部署到測試環境、生產環境時就出這樣那樣的問題,同時,因為本地與測試環境、生產環境之間存在差異,我們可能無法在本地復現這些問題,那麼,有沒有一種工具可以很好的解決這一問題呢?隨著歷史的車輪不斷前行,容器技術誕生了。

  Docker,作為最近幾年興起的一種虛擬化容器技術,他可以將我們的運行程式與操作系統做一個隔離,例如這裡我們需要運行 .NET Core 程式,我們不再需要關心底層的操作系統是什麼,不需要在每台需要需要運行程式的機器上安裝程式運行的各種依賴,我們可以通過程式打包成鏡像的方式,將應用程式和該程式的依賴全部置於一個鏡像文件中,這時,只要別的機器上有安裝 Docker,就可以通過我們打包的這個鏡像來運行這個程式。

  1.1、卸載 Docker

  在安裝 Docker 之前,我們應該確定當前的機器上是否已經安裝好了 Docker,為了防止與現在安裝的 Docker CE 發生衝突,這裡我們先卸載掉以前版本的 Docker,如果你確定你的機器上並沒有安裝 Docker 的話此步可以跳過。

  在 Linux 中可以使用 \ 加 Enter 在輸入很長很長的語句時進行換行,這裡和後面的命令都是採用這樣的方式。

sudo yum remove docker \
  docker-client \
  docker-client-latest \
  docker-common \
  docker-latest \
  docker-latest-logrotate \
  docker-logrotate \
  docker-engine

  1.2、添加 yum 源

  在安裝 Docker CE 的方式上,我是採用將 Docker CE 的源添加到 yum 源中,之後我們就可以直接使用 yum install 安裝 Docker CE,整個的安裝過程如下。

# 安裝工具包從而可以讓我們在 yum 中添加別的倉儲源
sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

# 設置 docker ce 的穩定庫地址
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

# 安裝 docker ce
sudo yum install docker-ce docker-ce-cli containerd.io

  當我們安裝好 Docker 之後,我們就可以使用 docker 命令驗證我們是否在機器上成功安裝了 Docker,同時,也可以使用 docker --version 命令查看我們安裝的 Docker CE 版本。

  1.3、設置開機自啟

  當 Docker 已經在我們的機器上安裝完成後,我們就可以將 Docker 設置成機器的自啟服務,這樣,如果出現伺服器重啟的情況下,我們的 Docker 也可以隨伺服器的重啟自動啟動 Docker 服務。

# 啟動 Docker 服務並允許開機自啟
sudo systemctl start docker

# 查看當前 dokcer 的運行情況
sudo systemctl status docker

  1.4、Hello World

  就像我們在學習一門新的語言時,運行的第一句代碼,幾乎都是列印出 Hello World,而在 Docker Hub 中,也有這麼一個鏡像,在無數的 Docker 教程中,安裝完 Docker 後,第一件事就是拉取這個鏡像文件,“告訴” Docker,我來了。

  Docker Hub 是存放鏡像的倉庫,裡面包含了許多的鏡像文件,因為伺服器在國外的原因,下載的速度可能不理想,像國內的阿裡雲、騰訊雲也有提供對於 Docker 鏡像的加速器服務,你可以按需使用,當然,你也可以創建屬於你的私有鏡像倉庫。

docker run hello-world

  docker run 命令,它會在我們的本地鏡像庫中先尋找這個鏡像,然後運行。如果在本地沒有找到的話,則會自動使用 docker pull 從 Docker Hub 中尋找,能找到的話,則會自動下載到本地,然後運行,找不到的話,這條命令也就運行失敗了。

  1.5、安裝 Docker Compose

  在實際的項目開發中,我們可能會有多個應用鏡像,例如在本篇文章的示例中,為了在 Docker 中運行我們的程式,我們需要三個鏡像:應用程式自身鏡像、MySQL Server 鏡像、以及 Nginx 鏡像,為了將我們的程式啟動起來,我們需要手敲各個容器的啟動參數,環境變數,容器命名,指定不同容器的鏈接參數等等一系列的操作,又多又煩,可能某一步操作失敗後程式就無法正常運行。而當我們使用了 Docker Compose 之後,我們就可以把這些命令一次性寫在 docker-compose.yml 配置文件中,以後每次啟動我們的應用程式時,只需要通過 docker compose 命令就可以自動幫我們完成這些操作。

# 從 github 下載 docker compose 二進位文件
sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 對下載的二進位文件應用可執行許可權
sudo chmod +x /usr/local/bin/docker-compose

# 查看 docker compose 版本
docker-compose --version

  2、構建程式鏡像

  當我們在伺服器上安裝好 docker 和 docker compose 之後,就可以開始構建我們的程式鏡像了。首先我們需要對我們的運行程式添加對於 Docker 的支持。你可以自己手動在 MVC 項目中添加 Dockerfile 文件,或是通過右鍵添加 Docker 支持。

  Dockerfile 就像一個執行的清單,它告訴 Docker,我們這個鏡像在構建和運行時需要按照什麼樣的命令運行。打開 VS 為我們自動創建的 Dockerfile,可以看到清晰的分成了四塊的內容。

  我們知道,.NET Core 程式的運行需要依賴於 .NET Core Runtime(CoreCLR),因此,為了使我們的程式可以運行起來,我們需要從 hub 中拉取 runtime ,併在 此基礎上構建我們的應用鏡像。同時,為了避免因為基礎的環境的不同造成對程式的影響,這裡的 Runtime 需要同程式開發時的 .NET Core SDK 版本保持一致,所以這裡我使用的是 .NET Core 2.1 Runtime。

  一個鏡像中包含了應用程式及其所有的依賴,與虛擬機不同的是,容器中的每個鏡像最終是共用了宿主機的操作系統資源,容器作為用戶空間中的獨立進程運行在主機操作系統上。

  PS:圖片版權歸屬於微軟的技術文檔,如有侵權,請聯繫我刪除,源文件地址:什麼是 Docker?

  鏡像可以看成一個個小型的“虛擬主機”,這裡我們在鏡像中創建了一個 /app 路徑作為我們程式在鏡像中的工作目錄,同時,將 80 埠暴露給 Docker,從而可以使我們在鏡像外面通過埠訪問到當前鏡像中的運行的程式。

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

  因為我們的應用是一個多層架構的單體應用,最終的 MVC 項目依賴於解決方案中的各個類庫以及我們從 Nuget 中下載的各種第三方組件,在部署時,需要將這些組件打包成 dll 引用。所以,這裡我們需要使用 .NET Core SDK 中包含的 .NET Core CLI 進行還原和構建。

  就像在下麵的代碼中,我們在鏡像的內部創建了一個 /src 的路徑,將當前解決方案下的類庫都複製到這個目錄下,之後通過 dotnet restore 命令還原我們的主程式所依賴的各個組件。當我們還原好依賴的組件後,就可以使用 dotnet build 命令生成 Release版本的 dll 文件,同時輸出到之前創建的 /app 路徑下。

FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ["PSU.Site/PSU.Site.csproj", "PSU.Site/"]
COPY ["03_Logic/PSU.Domain/PSU.Domain.csproj", "03_Logic/PSU.Domain/"]
COPY ["03_Logic/PSU.Repository/PSU.Repository.csproj", "03_Logic/PSU.Repository/"]
COPY ["01_Entity/PSU.Entity/PSU.Entity.csproj", "01_Entity/PSU.Entity/"]
COPY ["02_Infrastructure/PSU.Utility/PSU.Utility.csproj", "02_Infrastructure/PSU.Utility/"]
COPY ["04_Rule/PSU.Model/PSU.Model.csproj", "04_Rule/PSU.Model/"]
COPY ["02_Infrastructure/PSU.EFCore/PSU.EFCore.csproj", "02_Infrastructure/PSU.EFCore/"]
COPY ["04_Rule/PSU.IService/PSU.IService.csproj", "04_Rule/PSU.IService/"]
COPY ["Controllers.PSU/Controllers.PSU.csproj", "Controllers.PSU/"]
RUN dotnet restore "PSU.Site/PSU.Site.csproj"
COPY . .
WORKDIR "/src/PSU.Site"
RUN dotnet build "PSU.Site.csproj" -c Release -o /app

  上面一步可以看成我們在使用 VS 生成 Release 版本的解決方案,當生成沒有出錯之後,我們就可以進行程式的發佈。

FROM build AS publish
RUN dotnet publish "PSU.Site.csproj" -c Release -o /app

  當已經生成發佈文件之後,按照我們平時部署在 Windows 上的過程,這時就可以通過 IIS 部署運行了,因此,構建我們應用鏡像的最後一步就是通過 dotnet 命令執行我們的程式。

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "PSU.Site.dll"]

  似乎到這一步構建程式鏡像就結束了,按照這樣流程做的話,就需要我們將整個的解決方案上傳到伺服器上了,可是,很多時候,我們僅僅是把我們在本地發佈好的項目上傳到伺服器上,這與我們現在的構建流程具有很大的不同,所以這裡我們來修改 Dockerfile 文件,從而符合我們的發佈流程。

  從上面分析 Dockerfile 的過程中不難看出,在伺服器上構建鏡像的第二步、第三步就是我們現在在開發環境中手動完成的部分,所以這裡,我們只需要對這部分進行刪除即可,修改後的 Dockerfile 如下。

FROM microsoft/dotnet:2.1-aspnetcore-runtime
WORKDIR /app
COPY . /app 
EXPOSE 80
ENTRYPOINT ["dotnet","PSU.Site.dll"]

  在修改後的 Dockerfile 中,可以看到,我們刪去了 build 和 release 的過程,選擇直接將我們 Dockerfile 路徑下的文件拷貝到鏡像中的 /app 路徑下,然後直接執行 dotnet 命令,運行我們的程式。

  為了確保 Dockerfile 與發佈後的文件處於同一路徑下,這裡我們需要使用 VS 修改 Dockerfile 的屬性值,確保會複製到輸出的目錄下,這裡選擇如果較新則複製即可。

  3、編寫 docker-compose.yml

  當我們構建好應用的鏡像,對於 Nginx 和 MySQL 我們完全可以從 hub 中拉取下來,再執行一些配置即可。所以,我們現在就可以編寫 docker compose 文件,來定義我們的應用鏡像運行時需要包含的依賴以及每個鏡像的啟動順序。

  右鍵選中 MVC 項目,添加一個 docker-compose.yml 文件,同樣的,需要修改該文件的屬性,以便於該文件可以複製到輸出目錄下。註意,這裡的文件名和上文的 Dockerfile 都是特定的,你不能做任何的修改。如果你的電腦上已經安裝了 Docker for Windows,你也可以使用 VS,右鍵添加,選中容器業務流程協調程式支持自動對 docker compose 進行配置。

  在 yml 文件中,我定義了三個鏡像:psu.site、docker.mysql、docker.nginx。三個鏡像的定義中有許多相同的地方,都設置了自動重啟(restart),以及都處於同一個橋接網路下(psu-net)從而達到鏡像間的通信。

  docker.mysql 是 MySQL 的鏡像,我們通過環境變數 MYSQL_ROOT_PASSWORD 設置了 MySQL 的資料庫連接密碼,並通過掛載捲的方式將鏡像中的資料庫文件持久化到我們的伺服器本地路徑中。同時,將鏡像的 3306 埠映射到伺服器的 3306 埠上。

  psu.site 則是我們的程式鏡像,採用位於 /usr/wwwroot/psu/ 路徑下的 Dockerfile 文件進行構建的,因為主程式的運行需要依賴於資料庫,所以這裡採用 depends_on 屬性,使我們的應用鏡像依賴於 docker.mysql 鏡像,即,在 docker.mysql 啟動後才會啟動應用鏡像。

  docker.nginx 則是我們的 nginx 鏡像,這裡將鏡像中的 80 埠和 443 埠都映射到伺服器 IP 上,因為我們需要配置 Nginx 從而監聽我們的程式,所以通過掛載捲的方式,將本地的 nginx.conf 配置文件用配置映射到鏡像中。同時,因為我們在構建應用鏡像的 Dockerfile 文件時,對外暴露了 80 埠,所以這裡就可以通過 links 屬性進行監聽(如果構建時未暴露埠,你可以在 docker compose 文件中通過 Expose 屬性暴露鏡像中的埠)。

  Nginx 的配置文件如下,這裡特別需要註意文件的格式,縮進,一點小錯誤都可能導致鏡像無法正常運行。如果你和我一樣將 nginx.conf 放到程式運行路徑下的,別忘了修改文件的屬性。

server {
    listen 80;
	
    location / {
      proxy_pass http://psu.site;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Host $http_host;
      proxy_cache_bypass $http_upgrade;
    }
}

  一個完整的 docker compose 文件如下,包含了三個鏡像以及一個橋接網路。

version: '3.7'

services:
  docker.mysql:
    image: mysql
    ports:
      - "3306:3306"
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=123456@sql
    volumes:
      - /usr/mysql:/var/lib/mysql
    networks:
      - psu-net

  psu.site:
    build: /usr/wwwroot/psu/
    restart: always
    depends_on:
      - docker.mysql
    networks:
      - psu-net

  docker.nginx:
    image: nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    links:
      - psu.site
    networks:
      - psu-net

networks:
  psu-net:
    driver: bridge

  這裡需要註意,所有有用到鏡像間的通信的地方,我們都需要使用鏡像名進行指代,例如上面的 nginx 的配置文件中,我們需要將監聽的地址改為鏡像名稱,以及,我們需要修改程式的資料庫訪問字元串的伺服器地址,修改後的資料庫連接字元串如下所示。

 "ConnectionStrings": {
    "SQLConnection": "server=docker.mysql;database=PSU.Site;user=root;password=123456@sql;port=3306;persistsecurityinfo=True;"
  }

  4、發佈部署程式

  當我們構建好 docker compose 文件後就可以把整個文件上傳到伺服器上進行構建 docker 鏡像了。這裡我將所有的部署文件放在伺服器的 /usr/wwwroot/psu/ 路徑下,這時我們就可以通過 docker compose 命令進行鏡像構建。

  定位到部署文件在的位置,我們可以直接使用下麵的命令進行鏡像的(重新)構建,啟動,並鏈接一個服務相關的容器,整個過程都會在後臺運行,如果你希望看到整個過程的話,你可以去掉 -d 參數。

# 執行鏡像構建,啟動
docker-compose up -d

  當 up 命令執行完成後,我們就可以通過 ps 命令查看正在運行的容器,若有的容器並沒有運行起來,則可以使用 logs 查看容器的運行日誌從而進行排錯。

# 查看所有正在運行的容器
docker-compose ps

# 顯示容器運行日誌
docker-compose logs

 三、總結

    本章主要是介紹瞭如何通過 docker 容器,完整的部署一個可實際使用的 .NET Core 的單體應用,相比於之前通過 Linux 部署 .NET Core 應用,可以看到整個步驟少了很多,也簡單很多。文中涉及到了一些 docker 的命令,如果你之前並沒有接觸過 docker 的話,可能需要你進一步的瞭解。當我們將程式打包成一個鏡像之後,你完全可以將鏡像上傳到私有鏡像倉庫中,或是直接打包成鏡像的壓縮文件,這樣,當需要切換部署環境時,只需要獲取到這個鏡像之後即可快速完成部署,相比之前,極大的方便了我們的工作。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 普通的bean的初始化是在容器啟動初始化階段執行的,而被lazy-init修飾的bean 則是在從容器里第一次進行context.getBean(“”)時進行觸發。Spring 啟動的時候會把所有bean信息(包括XML和註解)解析轉化成Spring能夠識別的BeanDefinition並存到Has ...
  • 1.spring約束的導入 2.SSH常用約束 3.application.xml的引入方式 <1.通過ClassPathXmlApplicationContext引入配置文件application.xml <2.application.xml <3.命名空間的引入 4.在過濾器中配置applica ...
  • 今天遇到一問題,js文件中調用字元串的replace方法,不起作用。 後來排查可能覺得replace("<option value='1'>admin</option>","")中,前邊的字元串單引號也要和頁面上的一致才能。 果然發現頁面中的value用的是“”雙引號,我自己寫的是''單引號,導致匹 ...
  • C語言中的記憶體分配與釋放 對C語言一直都是抱著學習的態度,很多都不懂,今天突然被問道C語言的記憶體分配問題,說了一些自己知道的,但感覺回答的並不完善,所以才有這篇筆記,總結一下C語言中記憶體分配的主要內容。 相關問題 剛剛在一篇博文看到一個簡單的問題: 兩段代碼都很簡單,輸出一段字元,類型不同,一個是c ...
  • 使用python requests模塊調用vmallarg.vmall.com介面API時報如下錯誤: requests.exceptions.ConnectionError: HTTPSConnectionPool(host='vmallrag.vmall.com', port=443): Max ...
  • 由題意可得出遞推式$f[i ,j]=\sum_{e\in S} f[i 1,\frac{j}{e}]$,初值$f[0,0]=1$,答案為$f[n,x]$,具體意義不表。 分析可知$f[1,e(e\in S)]=1$,$f[i,ab]=\sum_{a\in S,b\in S}f[i 1,a]f[1,b ...
  • ! 本文編輯中 `centos ssh 無法連接 ` ...
  • 不要62 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 64679 Accepted Submission(s): 25710 Problem ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...