在以前的文章(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 上也挺好。html
將 .NET Core 項目部署到 Linux 上如此複雜,就沒有簡單的部署方式嗎?mysql
你好,有的,Docker 瞭解一下~~~linux
PS:這裏的示例代碼仍是採用以前的畢業設計項目,在這篇文章發佈的時候,我已經在程序的倉庫中添加了對於 Docker 的支持,你能夠下載下來,本身嘗試一下,畢竟,實踐出真知。nginx
代碼倉儲:https://github.com/Lanesra712/Danvic.PSUgit
在代碼交付的過程當中,偶爾會遇到這樣的問題,在本地測試是好的,可是部署到測試環境、生產環境時就出這樣那樣的問題,同時,由於本地與測試環境、生產環境之間存在差別,咱們可能沒法在本地復現這些問題,那麼,有沒有一種工具能夠很好的解決這一問題呢?隨着歷史的車輪不斷前行,容器技術誕生了。github
Docker,做爲最近幾年興起的一種虛擬化容器技術,他能夠將咱們的運行程序與操做系統作一個隔離,例如這裏咱們須要運行 .NET Core 程序,咱們再也不須要關心底層的操做系統是什麼,不須要在每臺須要須要運行程序的機器上安裝程序運行的各類依賴,咱們能夠經過程序打包成鏡像的方式,將應用程序和該程序的依賴所有置於一個鏡像文件中,這時,只要別的機器上有安裝 Docker,就能夠經過咱們打包的這個鏡像來運行這個程序。sql
1.一、卸載 Dockerdocker
在安裝 Docker 以前,咱們應該肯定當前的機器上是否已經安裝好了 Docker,爲了防止與如今安裝的 Docker CE 發生衝突,這裏咱們先卸載掉之前版本的 Docker,若是你肯定你的機器上並無安裝 Docker 的話此步能夠跳過。數據庫
在 Linux 中可使用 \ 加 Enter 在輸入很長很長的語句時進行換行,這裏和後面的命令都是採用這樣的方式。centos
sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
1.二、添加 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.三、設置開機自啓
當 Docker 已經在咱們的機器上安裝完成後,咱們就能夠將 Docker 設置成機器的自啓服務,這樣,若是出現服務器重啓的狀況下,咱們的 Docker 也能夠隨服務器的重啓自動啓動 Docker 服務。
# 啓動 Docker 服務並容許開機自啓 sudo systemctl start docker # 查看當前 dokcer 的運行狀況 sudo systemctl status docker
1.四、Hello World
就像咱們在學習一門新的語言時,運行的第一句代碼,幾乎都是打印出 Hello World,而在 Docker Hub 中,也有這麼一個鏡像,在無數的 Docker 教程中,安裝完 Docker 後,第一件事就是拉取這個鏡像文件,「告訴」 Docker,我來了。
Docker Hub 是存放鏡像的倉庫,裏面包含了許多的鏡像文件,由於服務器在國外的緣由,下載的速度可能不理想,像國內的阿里雲、騰訊雲也有提供對於 Docker 鏡像的加速器服務,你能夠按需使用,固然,你也能夠建立屬於你的私有鏡像倉庫。
docker run hello-world
docker run 命令,它會在咱們的本地鏡像庫中先尋找這個鏡像,而後運行。若是在本地沒有找到的話,則會自動使用 docker pull 從 Docker Hub 中尋找,能找到的話,則會自動下載到本地,而後運行,找不到的話,這條命令也就運行失敗了。
1.五、安裝 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
當咱們在服務器上安裝好 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 的屬性值,確保會複製到輸出的目錄下,這裏選擇若是較新則複製便可。
當咱們構建好應用的鏡像,對於 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;" }
當咱們構建好 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 的話,可能須要你進一步的瞭解。當咱們將程序打包成一個鏡像以後,你徹底能夠將鏡像上傳到私有鏡像倉庫中,或是直接打包成鏡像的壓縮文件,這樣,當須要切換部署環境時,只須要獲取到這個鏡像以後便可快速完成部署,相比以前,極大的方便了咱們的工做。