1. 程式人生 > >.NET5都來了,你還不知道怎麼部署到linux?最全部署方案,總有一款適合你

.NET5都來了,你還不知道怎麼部署到linux?最全部署方案,總有一款適合你

隨著2020進入4季度,.NET5正式版也已經與大家見面了。不過,儘管 .NET Core釋出已經有四五年的時間,但到目前為止,依舊有很多.NET開發者在堅守者.NET4,原因不盡相同,但最大的問題可能還是不熟悉Linux,更別說在Linux伺服器中部署.NET服務了。 而 .NET Core在飛速發展的這四五年裡,微服務、雲原生等概念也在飛速發展。 .NET Core在微服務和雲原生的場景下,也已日趨穩定,生態也在逐步完善,相信.NET5正式釋出後,對於我們苦逼多年了的.NET開發者絕對是個機遇。所以,你還有什麼理由繼續堅守.NET4呢?筆者在開始使用 .NET Core時,在如何部署到Linux伺服器上也是踩了挺多坑,順便也總結了一些經驗,在此分享給大家,如有更好方案,還望不吝賜教。 --- #### 準備工作 1. 作業系統。 作業系統可選擇你比較熟悉的Linux發行版,如果你是第一次接觸Linux,那我推薦使用CentOs,因為本文的內容都是在CentOs中進行演示的。 至於系統的安裝,你可以選擇雲伺服器,或者使用虛擬機器安裝。虛擬機器安裝CentOs的方式比較簡單,在此就不贅述了。 2. 連線工具 筆者推薦使用XSHELL作為連線工具,下載地址:https://www.netsarang.com/zh/xshell-download/ 安裝完成後,開啟軟體,點選左上角的新建回話按鈕,開啟新建回話框,如下圖所示: ![20201105201340](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201105201340.png) 在【主機】中填寫伺服器的ip地址,然後點選【連線】按鈕,會依次提示輸入使用者名稱和密碼。連線成功的介面如下所示: ![20201105201748](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201105201748.png) 3. 檔案上傳工具 要想把服務部署到Linux伺服器,那就必須把檔案傳輸到伺服器中(這是句廢話)。根據不同的使用場景,筆者推薦兩種方案,分別是XFTP工具上傳和git倉庫中轉。 使用XFTP上傳的方式需要在開發的機器上安裝此工具,下載地址:https://www.netsarang.com/zh/xftp-download/ 安裝完成後,可以通過XSHELL一鍵開啟XFTP,XFTP開啟後,在軟體的右側可以切換要上傳檔案的目標路徑,然後將檔案拖到右側釋放後,就會自動上傳了,如下圖所示: ![a1](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/a1.gif) 使用git倉庫進行中轉的方式是筆者比較推薦的方式,因為在頻繁的迭代更新中,如果通過現在本地編譯釋出後,再將檔案拷貝到伺服器,這個操作流程稍顯繁瑣。而通過git倉庫週轉的方式則相對比較簡單,開發者僅需要將開發好的程式碼推到git倉庫,然後在伺服器中執行build,publish等操作,少了繁瑣的拷貝檔案的過程,同時由於build和publish都是在伺服器中執行,那麼我們就可以通過編寫部署指令碼的方式,可以最終實現一鍵快速部署。 使用git的方案需要在伺服器安裝git客戶端,並配置ssh公鑰(配置公鑰的目的是拉取私有的倉庫,公共參考無需配置公鑰)。下面來看具體的操作步驟: 首先,執行如下命令,安裝git客戶端: ``` yum -y install git ``` git安裝後,通過如下命令生成sshkey: ``` # 這裡的[email protected]只是生成的sshkey的名稱,並不約束貨要求具體命名為某個郵箱。 ssh-keygen -t rsa -C "[email protected]" ``` 按照提示,按三次回車,即可生成sshkey,如下圖所示: ![20201105212252](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201105212252.png) ,通過如下命令可檢視公鑰: ``` cat ~/.ssh/id_rsa.pub ``` 複製生成後的sshkey,配置到程式碼倉庫的公鑰中。下面簡單演示下gitee程式碼託管平臺下如何配置公鑰(其他平臺大同小異)。 進入私有倉庫的【管理】頁面,找到【公鑰管理】,點選【新增公鑰】,將剛剛生成的公鑰複製過去,如下圖所示: ![20201105213004](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201105213004.png) 新增後,在終端中輸入如下命令: ``` ssh -T [email protected] ``` 次使用需要確認並新增主機到本機SSH可信列表,如下圖所示: ![20201105213248](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201105213248.png) 輸入yes後,出現類似於Hi xxx的字樣,則表示git公鑰配置成功了。 4. .NET Core SDK和執行時 .NET執行時是.NET程式執行的先決條件,而SDK並不是必須的,但如果通過git方式進行檔案中轉的話,就涉及到在伺服器端進行編譯,所以SDK也需要安裝。(*注:docker部署方式無需在伺服器安裝SDK和執行時,下文回提到*) 下面一起看看在CentOs中如何安裝SDK和執行時(其他環境可參考官方文件:https://docs.microsoft.com/zh-cn/dotnet/core/install/linux)。 執行如下命令,將 Microsoft 包簽名金鑰新增到受信任金鑰列表,並新增 Microsoft 包儲存庫。 ``` sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm ``` 通過如下命令安裝SDK: ``` sudo yum install -y dotnet-sdk-3.1 ``` .NET Core SDK 使你可以通過 .NET Core 開發應用。 如果安裝 .NET Core SDK,則無需安裝相應的執行時。 通過如下命令安裝執行時: ``` sudo yum install -y aspnetcore-runtime-3.1 ``` 注:上述命令中的最後的3.1表示的是版本號,如果安裝其他版本,修改對應的版本號即可。參考資料:https://dotnet.microsoft.com/download/dotnet-core #### 釋出程式到伺服器 在準備工作中已經介紹了兩種將檔案釋出到伺服器的方式,下面具體演示下步驟。 1. 通過XFTP釋出到伺服器 首先,準備好要釋出的程式, 下圖是我建立的一個.NET Core3.1的示例程式碼: ![20201106132910](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106132910.png) 開啟vs的程式包管理器控制檯,執行如下命令: ``` dotnet publish -o ./publish ``` 執行結果如下圖所示: ![20201106133127](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106133127.png) 在上圖可以看到,釋出之後的檔案的路徑為:D:\code\test\BuildTest\publish\ 開啟XFTP,將publish資料夾拖到右側視窗,即可完成上傳。 ![a2](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/a2.gif) 2. 通過git中轉。 首先,將程式碼推送到git倉庫中,複製SSH地址。如下圖所示: ![20201106141159](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106141159.png) 然後再伺服器中,執行克隆命令: ``` mkdir code cd code git clone [email protected]:billsking/build-test.git ``` 執行結果如下圖所示: ![20201106141435](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106141435.png) 此時專案程式碼已經下載到伺服器中,切換工作到解決方案所在的目錄 ``` cd build-test ``` 然後我們需要執行dotnet publish命令對程式進行編譯釋出。 ``` dotnet publish -o /root/web/publish ``` 執行完畢後,編譯後的檔案將被儲存在/root/web/publish目錄中。將工作目錄切換到/root/web/publish,執行如下命令: ``` dotnet BuildTest.dll ``` 執行結果如下圖: ![20201106143301](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106143301.png) 到這裡為止,咱們的程式已經在linux伺服器執行起來了。但直接通過dotnet命令僅適合臨時測試使用,並不能成為生產場景的解決方案。下面進來跟我一起了解下可用於生產環境的部署方式吧。 #### Supervisor+Nginx組合 Supervisor是用Python開發的一套通用的程序管理程式,能將一個普通的命令列程序變為後臺daemon,並監控程序狀態,異常退出時能自動重啟。下面是Supervisor安裝方法。 執行如下命令: ``` yum install -y supervisor ``` 執行以上程式碼如果提示:沒有可用軟體包 supervisor。則需要先安裝EPEL源後,再執行上面的命令。安裝EPEL源的命令如下: ``` yum install -y epel-release ``` 設定開機啟動: ``` systemctl enable supervisord ``` 啟動supervisord ``` systemctl start supervisord ``` 檢視狀態 ``` systemctl status supervisord ``` ![20201106150000](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106150000.png) 通過vi命令或者XFTP修改配置檔案開啟web介面訪問,如下圖所示,分別取消inet_http_server等四個配置的註釋: ![20201106150527](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106150527.png) 執行如下命令,重新載入配置檔案: ``` supervisorctl reload ``` 然後在瀏覽器開啟http://你的ip:9001,輸入使用者名稱密碼後,如圖所示: ![20201106151109](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201106151109.png) 看到這個介面,就表示supervisor安裝完成了。 切換到/etc/supervisord.d目錄,在此目錄建立ini檔案,內容如下: ``` [program:buildtest] ;表示程式名稱,用於在supervisor中顯示,無特殊意義。 command=/bin/bash -c "dotnet BuildTest.dll"; 輸入執行命令,這裡表示執行的是dotnet BuildTest.dll directory=/root/web/publish ; 應用程式根目錄 autostart=true ; 是否自動啟動,當 supervisor 載入該配置檔案的時候立即啟動它 autorestart=true ; 是否自動重啟, 程式異常退出後自動重啟 logfile_maxbytes=50MB ; 該配置檔案輸出單個日誌檔案的大小,預設50M logfile_backups=10 ; 日誌備份個數 loglevel=info ; 記錄日誌級別 stdout_logfile=/root/data/logs/buildtest/buildtest.out.log ; 指定標準輸出日誌檔案 environment=ASPNETCORE_ENVIRONMENT=Production;環境變數。 user=root ;啟動服務的使用者 redirect_stderr=true;把 stderr 重定向到 stdout,預設 false ``` ==++注:stdout_logfile指向的資料夾一定要先建立,否則無法啟動。++== 然後執行如下命令: ``` supervisorctl reload ``` 命令執行成功後, 重新整理瀏覽器,可以看到如下介面: ![20201109100404](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109100404.png) 當介面顯示running時,則表示我們我們剛剛配置的應用程式執行起來了。 但現在還存在一個問題,我們的應用程式預設是繫結的5000埠,如果要指定80埠或者配置域名該怎麼處理呢?下面就該nginx登場了。 Nginx是一款輕量級的Web 伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器,在BSD-like 協議下發行。其特點是佔有記憶體少,併發能力強,事實上nginx的併發能力在同類型的網頁伺服器中表現較好,中國大陸使用nginx網站使用者有:百度、京東、新浪、網易、騰訊、淘寶等。(來源自百度百科) 安裝方式參考:http://nginx.org/en/linux_packages.html#RHEL-CentOS 安裝先決條件: ``` yum install -y yum-utils ``` 設定yum儲存庫,先建立一下內容的檔案:/etc/yum.repos.d/nginx.repo ``` [nginx-stable] name=nginx stable repo baseurl=http://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck=1 enabled=1 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true [nginx-mainline] name=nginx mainline repo baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/ gpgcheck=1 enabled=0 gpgkey=https://nginx.org/keys/nginx_signing.key module_hotfixes=true ``` 預設情況下,使用穩定 nginx 包的儲存庫。如果要使用主線 nginx 包,請執行以下命令: ``` yum-config-manager --enable nginx-mainline ``` 執行如下命令安裝nginx: ``` yum install -y nginx ``` 設定開機啟動: ``` systemctl enable nginx ``` 啟動nginx: ``` systemctl start nginx ``` 此時,就可以在瀏覽器通過ip訪問了:http://你的ip,介面如下: ![20201109101553](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109101553.png) nginx安裝完成後,切換到/etc/nginx/conf.d目錄,修改default.conf檔案內容,如下所示: ``` server { listen 80; server_name localhost; location / { proxy_pass http://0.0.0.0:5000; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } ``` 儲存後,執行如下命令,重新載入配置: ``` nginx -s reload ``` 然後再次訪問http://你的ip,幸運的話,你應該可以看到如下的介面,表示你的.NET Core程式已經完美執行在linux系統了。 ![20201109111932](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109111932.png) 如果你不幸的看到了如下介面: ![20201109112035](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109112035.png) 不要著急,繼續往下看。 出現這個問題的原因是因為SeLinux的限制,執行如下命令之後,再重新整理頁面: ``` setenforce 0 ``` 如果還是看到的錯誤頁面,就需要去檢視nginx的日誌了,預設的日誌路徑為:/var/log/nginx 通過setenforce 0命令,只是臨時實效,重啟後回失效的。一勞永逸的做法是,修改/etc/selinux/config 檔案, 將**SELINUX=enforcing改為SELINUX=disabled**,然後重啟機器。 後面如果你需要更新你的應用程式,只需要將程式碼提交到git倉庫,然後在伺服器中執行git pull和dotnet publish就行了。 如果你對shell比較熟悉的話,還可以通過編寫shell命令一鍵執行應用程式的更新,下面是我寫的示例: ``` # !/bin/bash cd /root/code/build-test git pull dotnet publish -o /root/web/publish supervisorctl restart buildtest ``` 將上述的程式碼儲存問sh檔案,上傳到伺服器,並設定許可權。如下圖所示: ![20201109142811](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109142811.png) 程式碼提交到git倉庫後,執行如下命令: ``` ./build.sh ``` 執行結果如下圖所示: ![20201109143151](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109143151.png) 總結一下,這種方式需要在服務端安裝兩個軟體,supervisor和nginx,supervisor一個作為守護執行緒,用於維護應用程式的生命週期的。nginx則是作為反向代理使用。維護起來相對麻煩,那麼有沒有一個像IIS一樣,既可以維護應用程式的生命週期,又可以對外暴露服務呢?答案是肯定的,繼續往下看。 Jexus >   Jexus是一款Linux平臺上的高效能WEB伺服器和負載均衡閘道器伺服器,以支援ASP.NET、ASP.NET CORE、PHP為特色,同時具備反向代理、入侵檢測等重要功能。可以這樣說,Jexus是.NET、.NET CORE跨平臺的最優秀的宿主伺服器,如果我們認為它是Linux平臺的IIS,這並不為過,因為,Jexus不但非常快,而且擁有IIS和其它Web伺服器所不具備的高度的安全性。同時,Jexus Web Server 是完全由中國人自主開發的的國產軟體,真正做到了“安全、可靠、可控”, 具備我國黨政機關和重要企事業單位資訊化建設所需要的關鍵品質。 以上內容摘自jexus官網:https://www.jexus.org/ 廢話不多說,直接進入正題,首先是安裝。 jexus的安裝非常簡單,執行如下命令: ``` curl https://jexus.org/release/x64/install.sh|sudo sh ``` 注:如果在這之前你已經安裝了nginx,需要先將nginx繫結的80埠釋放,或者解除安裝nginx。 安裝完成後,切換到/usr/jexus目錄,修改/usr/jexus/siteconf目錄下的default,內容如下所示: ``` port=80 #埠 hosts=* #域名 AppHost={cmd=dotnet BuildTest.dll; root=/root/web/publish; port=0} ``` 然後執行如下命令: ``` /usr/jexus/jws restart ``` ![20201109162034](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109162034.png) 當你看到執行結果為OK時,即可認為應用程式已經啟動了。最後,執行如下命令,看下執行的結果: ``` ps -aux ``` ![20201109162246](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109162246.png) 關於jexus更詳細的使用說明,請參考官方文件。 Docker+Nginx或Jexus 上面講到的, 不管是通過supervisor+nginx還是jexus,都需要在伺服器安裝 .NET Core的SDK或者執行時,假如咱們有好多個應用程式,有的用 .NET Core2.1,有的用 .NET Core3.1,有的用.NET5,那麼上面的做法就需要咱們分別安裝對應的SDK或者執行時,對於維護還是比較麻煩的。Docker的出現,可以完美解決上述問題。 關於Docker的介紹,有興趣的可自行百度。下面來跟我一起來看下如何安裝並使用Docker。 執行如下命令: ``` curl -fsSL get.docker.com -o get-docker.sh sudo sh get-docker.sh --mirror Aliyun ``` 執行這個命令後,指令碼就會自動的將一切準備工作做好,並且把Docker的穩定版本安裝在系統中。 執行如下命令設定docker開機啟動: ``` systemctl enable docker ``` 執行如下命令啟動docker: ``` systemctl start docker ``` 安裝docker之後,我們需要在程式碼裡增加dockerfile檔案。什麼?不會docker?不要緊,萬能的Visual Studio可以幫你自動生成。在解決方案中,右擊專案名稱,依次選擇【新增】,【Docker支援】,如下圖所示: ![20201109165524](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109165524.png) 生成的dockerfile檔案如下所示: ``` FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base WORKDIR /app EXPOSE 80 FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build WORKDIR /src COPY ["BuildTest/BuildTest.csproj", "BuildTest/"] RUN dotnet restore "BuildTest/BuildTest.csproj" COPY . . WORKDIR "/src/BuildTest" RUN dotnet build "BuildTest.csproj" -c Release -o /app/build FROM build AS publish RUN dotnet publish "BuildTest.csproj" -c Release -o /app/publish FROM base AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "BuildTest.dll"] ``` 將程式碼推送到git倉庫,然後再linux伺服器中拉取最新程式碼。 切換到/root/code/build-test目錄,執行如下命令,拉取最新程式碼: ``` git pull ``` 然後執行如下命令,將程式碼打包為映象: ``` docker build -f ./BuildTest/Dockerfile -t buildtest . ``` 由於首次打包映象的時候涉及到拉取.NET Core的官方映象,拉取速度可能較慢,請耐心等待。打包完成後,執行如下命令,可檢視當前伺服器已存在的映象: ``` docker images ``` ![20201109170929](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109170929.png) 映象打包完成後,我們就可以通過docker run執行下,命令如下: ``` docker run -p 82:80 -dit --restart=always --name buildtest buildtest ``` 執行結果如下: ![20201109171049](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/20201109171049.png) 通過瀏覽器訪問:http://ip:82。如無意外,你將看到你的應用程式已經成功運行了。 相同的道理,我們也是可以通過編寫shell指令碼的方式,實現一鍵更新應用程式的。在/root目錄中,建立dockerbuild.sh檔案,檔案內容如下: ``` # !/bin/bash cd /root/code/build-test git pull imtag=$(uuidgen |sed 's/-//g') docker build -f ./BuildTest/Dockerfile -t buildtest:${imtag} . docker stop buildtest docker rm buildtest docker run -dit --restart=always --name buildtest -p 82:80 buildtest:${imtag} ``` 給dockerbuild.sh檔案設定執行許可權,命令如下: ``` chmod 777 dockerbuild.sh ``` 當有新程式碼推送到git倉庫時,進入伺服器,執行dockerbuild.sh即可快速更新應用程式。 到這裡,.NET Core部署到linux伺服器的方案已“基本”介紹完畢了。 為什麼說是“基本”呢?不知道大家有沒有發現,雖然目前的方案可以實現一鍵部署更新,但。。。。,我們還是需要登陸到linux伺服器去執行這個命令,顯然,這不是最好的方法。有沒有更好的方案呢?如果當我們提交給git倉庫後,自動出發部署命令是不是就更方便了呢? 答案是肯定的,我們可以藉助jenkins來實現。限於篇幅,本篇文章就不講解了,如有興趣,你也可以先自行研究,或者敬請期待下次的講解。 最後說明下,以上的方案還是存在很多的問題,對於小型團隊已經夠用,大型專案的終極解決方案應該是基於k8s實現的devops。其實k8s實現devops的原理和我上述介紹的方案基本一致。藉助與gitlab的runner或者jenkins,監聽git倉庫的狀態,當發現指定的分支發生變化後,打包映象,然後通過替換k8s的deployment的映象來實現自動更新。同時,k8s實現了彈性伸縮、滾動更新等功能。 福祿ICH·架構組 福爾斯