?一、前言
?最近一段時間自己主要的學習計劃還是按照畢業后設定的計劃,自己一步步的搭建一個前后端分離的 ASP.NET Core 項目,目前也還在繼續學習 Vue 中,雖然中間斷了很長時間,好歹還是堅持下來了,嗯,看了看時間,原本決定的半年完成肯定是完不成了。這兩周重新拾起來學習 Vue,文章也在慢慢的更新中,這一篇文章主要是想提前試試水將 ASP.NET Core 部署到 Linux Server 上,原本的打算是把畢業設計就部署到 Linux 上,最終也未能成行,究其原因,還是自己太懶太拖了吧,哈哈哈,拖到最后,畢業設計差點都沒寫完。
因為目前自己的前后端分離的項目還沒開始寫,所以這里采用的還是自己原來寫的 .NET Core 項目,這篇文章的主要目的是操作下如何將我們 ASP.NET Core 項目部署到我們的 Linux 服務器上,如果對你有任何的幫助的話,不勝榮幸。當然,如果有不對的地方,歡迎大家提出。
?二、準備工作
1、一臺 Linux 操作系統的電腦,嗯,這里采用的是騰訊云學生套餐,服務器版本為 CnetOS 7.5 。
2、終端軟件,這里我使用的是putty,用來幫助我們遠程連接到我們的服務器。?
下載鏈接:https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html
3、文件上傳軟件,這里我使用的是 WinSC,上傳文件的,同時,如何你和我一樣對于使用命令行編輯文件不習慣的話,用這個還可以編輯下服務器上需要更改的配置文件,逃~~~。?
下載鏈接:https://winscp.net/eng/download.php
?三、Step by Step
1、安裝 .NET Core Runtime
因為這里并不需要在 Linux 服務器上進行開發工作,所以只需要安裝好 dotnet core runtime 就可以了,如果你需要在 Linux 上進行開發,就需要安裝 .NET Core SDK了,當然,如果安裝過了 SDK,也就不需要安裝 Runtime 了。
我們打開微軟的官方網站,使用 putty 連接到我們的服務器。?官網地址:https://www.microsoft.com/net/download/linux-package-manager/centos/runtime-current
a)在服務器上注冊 Microsoft 秘鑰,這里我使用的是 Centos ,這里你需要按照你自己的服務器版本進行選擇下載。
sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
b)更新可供安裝的版本,當控制臺出現 Complete 時,則代表我們更新完成。
sudo yum update
c)安裝 .NET Core,同樣的,當控制臺出現 Complete 時,則代表我們安裝完成。
sudo yum install aspnetcore-runtime-2.1 ##這里如果你要在 Linux 上開發,這里就安裝 dotnet-sdk-2.1
這時,如何判斷我們的 .NET Core 安裝成功了呢?我們可以在控制臺上輸入下面的命令,如果已經安裝成功的話,就可以把我們安裝的 dotnet 版本信息顯示出來,反之,你就需要重新執行了。
dotnet --info
這里我們可以看到,我們只是安裝了 .NET Core Runtime,并沒有安裝 SDK,我們的 Host 版本是2.1.5。
2、安裝 MySQL
a)添加 MySQL 源
首先打開 MySQL 的官網,需要根據自己的服務器信息選擇合適的源信息,我的服務器操作系統是 CentOS,這里我選擇的是 yum 源(yum 源地址:https://dev.mysql.com/downloads/repo/yum/)。
當我們點擊 Download 按鈕后發現,要登錄。。。如果你和我一樣不想又注冊一個賬戶,我們可以獲取到下面的下載地址,然后通過 rpm -Ivh 的方式安裝。我安裝的是最新的 MySQL 8.0 版本,當然,你也可以通過修改版本號下載不同的 MySQL 版本。
wget https://dev.mysql.com/get/mysql80-community-release-el7-1.noarch.rpm rpm -ivh mysql80-community-release-el7-1.noarch.rpm
b)校驗 md5 與官網上的是一致
md5sum mysql80-community-release-el7-1.noarch.rpm
c)安裝 MySQL Server
sudo yum install mysql-server
d)啟動 MySQL Server
當我們完成安裝之后,就可以啟動 MySQL Server 服務了。我們可以使用下面的命令啟動 MySQL 守護程序。
sudo systemctl start mysqld
可是上面的命令執行之后,不會有任何的反應,我們可能會疑問我們執行成功了嗎?所以為了確保我們執行成功,我們可以使用下面的命令來查看是否啟用了 MySQL 服務。此時,如果我們的 MySQL 服務已經啟動了,則會輸出我們的執行信息。
sudo systemctl status mysqld
當我們啟動后可能會疑問,我們并沒有設置管理員密碼啊?原來,我們在安裝的過程中,會自動的為 MySQL root 用戶生成一個臨時的密碼,我們可以通過下面的命令中找到這個默認的密碼。
sudo grep 'temporary password' /var/log/mysqld.log
我們可以使用下面的命令來修改我們的 root 密碼,系統會提示我們輸入默認密碼,默認密碼輸入正確后就可以自己設置新的密碼了。
sudo mysql_secure_installation
注意:重新設置的密碼至少包含一個大寫字母,一個小寫字母,一個數字和一個特殊字符的12個字符!!!在整個設置密碼的過程中,總共有五步:設置 root 密碼;是否禁止 root 賬號遠程登錄;是否禁止匿名賬號(anonymous)登錄;是否刪除測試庫;是否確認修改。按照你自己的需求后,設置完成后即可。
e)設置允許遠程登錄、
在上面的設置完成后,我們用自己本地的 Workbench 連接服務器上的數據庫,發現無法進行連接,如果你之前使用過遠程連接 MySQL Server 你應該會知道,我們需要在 user 表中設置 root 用戶允許訪問的地址。
mysql -h localhost -u root -p ##登錄數據庫,輸入密碼后完成登錄 use mysql; ##選擇 mysql 表 select user,host from user; ##查詢當前的用戶 update user set host='%' where user='root'; ##允許使用 root 賬戶進行遠程登錄 flush privileges; ##刷新設置
這時,我們就可以遠程連接到我們的 MySQL Server 上了。
3、發布部署程序
本次部署的項目,采用的還是之前的畢業設計的項目(ASP.NET Core 2.0 MVC 項目實戰),在這里發布測試的時候遇到了一個問題,因為當時項目采用的 MySQL 版本為5.7,服務器中所安裝的 MySQL 版本為8.0,而 Oracle 在最新的 MySQL 中將默認的版本身份驗證從 mysql_native_password 改為了 caching_sha2_password,這里在進行數據操作時就會出現問題。如果你和我遇到同樣的問題,你需要將 MySQL 官方的 EFCore 組件替換成 Pomelo.EntityFrameworkCore.MySql,嗯,替換,而不是升級,因為升級后又會出現新的問題(小聲BB:MySQL 的這個 EFCore 的驅動事不是一般的多)。如果你不想升級的話,可以參考這個做法,鏈接地址送上:mysql8 :客戶端連接caching-sha2-password問題。
? 當我們把項目丟到服務器上后,我們進去到放置的路徑下,執行 dotnet 命令就可以運行我們的項目了。這里要特別注意,Linux 中對于大小寫是區分的,這里輸入的路徑以及項目的名稱都要確保和實際相同。
cd /usr/local/wwwroot/psu/ ##注意:最后一定要加上這個 / !!!!! dotnet PSU.Site.dll
這里會有個問題,不管你是使用的虛擬機還是云服務器,因為 5000 端口并沒有開放給外部訪問,所以外部的機器采用 ip:port 的方式,是無法訪問到的,所以我們接下來需要安裝反向代理的服務器來達到訪問的目的。
在部署 .NET Core 項目的時候,我們應該保持我們的程序的 .NET Core 版本與服務器上的環境版本保持一致,這樣才可以避免因為環境的因素而導致的某些問題,所以這里我部署 .NET Core 2.0 版本的程序其實不是很好的選擇。
4、安裝 Nginx 服務器
在 Windows 服務器上,如果我們要部署 .NET 項目,肯定會選擇部署到 IIS 中,同樣的,雖然 .NET Core 可以實現自托管,內置的 Kestrel 也非常適合從 ASP.NET Core 提供動態內容。 但是,Kestrel 的 Web 服務功能不像專門的服務器(如 IIS、Apache 或 Nginx)那樣功能豐富。 而反向代理服務器可以從 HTTP 服務器卸載服務靜態內容、緩存請求、壓縮請求和 SSL 終端等工作。 反向代理服務器可能駐留在專用計算機上,也可能與 HTTP 服務器一起部署可是為了能使用更多的功能,所以這里我們還是會配合一個反向代理服務器進行使用,在這里,我采用的是 Nginx。
a)安裝 Nginx
從官網上下載 nginx 包并解壓,這里采用的是使用源碼編譯安裝的形式。
wget -c http://nginx.org/download/nginx-1.9.9.tar.gz tar -zxvf nginx-1.9.9.tar.gz
解壓完成后我們就可以進入到 nginx 目錄,在這里我們創建默認的配置文件。在我們創建配置文件之前,我們需要安裝 gcc 環境。
cd nginx-1.9.9 yum install gcc gcc-c++ ./configure
上面的步驟完成后,我們需要添加幾個插件去完善 Nginx 的功能。
Nginx 的 http 模塊使用 pcre 來解析正則表達式,PCRE(Perl Compatible Regular Expressions) 是一個Perl庫,包括 perl 兼容的正則表達式庫。
yum install -y pcre pcre-devel
zlib 庫提供了很多種壓縮和解壓縮的方式,Nginx 使用 zlib 對 http 包的內容進行 gzip ,所以我們也需要在服務器上安裝 zlib 庫。
yum install -y zlib zlib-devel
Nginx 不僅支持 http 協議,還支持 https 協議,而 OpenSSL 是一個強大的安全套接字層密碼庫,囊括主要的密碼算法、常用的密鑰和證書封裝管理功能及 SSL 協議,并提供豐富的應用程序供測試或其它目的使用,所以我們也會在 Nginx 上面添加。
yum install -y openssl openssl-devel
至此,我們對于 Nginx 插件的安裝就完成了,現在我們就可以重新執行配置命令,再編譯安裝了。
./configure make install
編譯安裝完成后,Nginx 就安裝到了我們的服務器上,可是安裝到哪里去了呢?這里我們可以使用下面的命令去找到我們安裝的 Nginx。知道了安裝路徑后,我們就可以進入 Nginx 的目錄中執行進一步的操作。至此 Nginx 的安裝就完成了(這里是我的路徑地址,你的可能與我不同,以你的實際為準)。
whereis nginx
cd /usr/local/nginx/sbin/
安裝完成后,我們一般會將 Nginx 設置為開機自啟動以及自動重啟,從而預防特殊情況導致的網站掛了。我們先創建一個軟連接用來指向 Nginx 的安裝目錄。
ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx
現在創建一個自啟動的腳本。
vim /etc/init.d/nginx
在腳本中,輸入下面的內容。
#!/bin/sh # # nginx - this script starts and stops the nginx daemon # # chkconfig: - 85 15 # description: NGINX is an HTTP(S) server, HTTP(S) reverse \ # proxy and IMAP/POP3 proxy server # processname: nginx # config: /usr/local/nginx/conf/nginx.conf # config: /etc/sysconfig/nginx # pidfile: /usr/local/nginx/logs/nginx.pid # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ "$NETWORKING" = "no" ] && exit 0 nginx="/usr/local/nginx/sbin/nginx" prog=$(basename $nginx) NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf" [ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx lockfile=/var/lock/subsys/nginx make_dirs() {# make required directoriesuser=`$nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -`if [ -z "`grep $user /etc/passwd`" ]; thenuseradd -M -s /bin/nologin $userfioptions=`$nginx -V 2>&1 | grep 'configure arguments:'`for opt in $options; doif [ `echo $opt | grep '.*-temp-path'` ]; thenvalue=`echo $opt | cut -d "=" -f 2`if [ ! -d "$value" ]; then# echo "creating" $valuemkdir -p $value && chown -R $user $valuefifidone } start() {[ -x $nginx ] || exit 5[ -f $NGINX_CONF_FILE ] || exit 6make_dirsecho -n $"Starting $prog: "daemon $nginx -c $NGINX_CONF_FILEretval=$?echo[ $retval -eq 0 ] && touch $lockfilereturn $retval } stop() {echo -n $"Stopping $prog: "killproc $prog -QUITretval=$?echo[ $retval -eq 0 ] && rm -f $lockfilereturn $retval } restart() {configtest || return $?stopsleep 1start } reload() {configtest || return $?echo -n $"Reloading $prog: "killproc $nginx -HUPRETVAL=$?echo } force_reload() {restart } configtest() {$nginx -t -c $NGINX_CONF_FILE } rh_status() {status $prog } rh_status_q() {rh_status >/dev/null 2>&1 } case "$1" instart)rh_status_q && exit 0$1;;stop)rh_status_q || exit 0$1;;restart|configtest)$1;;reload)rh_status_q || exit 7$1;;force-reload)force_reload;;status)rh_status;;condrestart|try-restart)rh_status_q || exit 0;;*)echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}"exit 2 esac
現在我們將自啟動的腳本賦予腳本可執行權限,并將 Nginx 服務加入 chkconfig 管理列表中。
chmod a+x /etc/init.d/nginx chkconfig --add /etc/init.d/nginx chkconfig nginx on
當上面的設置都完成后,啟動我們的 Nginx 守護服務就可以了。
systemctl start nginx
同樣的,我們也可以使用下面的命令來查看 Nginx 服務的狀態。
systemctl status nginx
一些常用的命令(需要你處于 Nginx 的安裝目錄下時執行,例如,這里我的 Nginx 安裝目錄為 /usr/local/nginx/sbin/,如果在不在安裝目錄下需要使用全路徑)
啟動 Nginx 服務
./nginx
停止 Nginx 服務
./nginx -s stop
./nginx -s quit
停止 Nginx 服務有兩種方式,第一種:-s stop:先查出 nginx 的進程 id ,再使用 kill 命令強制殺掉進程;第二種:-s quit:等待 Nginx 的進程處理完成后再進行停止。
重新加載 Nginx 配置
./nginx -s reload
5、配置守護程序以及自啟動
在上面我們已經使用 dotnet 命令將我們的項目在服務器上運行了,而我們目前通過 ip:port 的形式沒有辦法進行訪問,這時我們安裝的 Nginx 服務器就可以為我們提供幫助了。
在安裝 Nginx 的時候我們創建了一個配置文件,現在我們就可以通過編輯這個配置文件,將我們的項目使用 Nginx 進行代理。首先我們進入 Nginx 的配置文件所在的路徑,打開這個配置文件。
cd /usr/local/nginx/conf/ vim nginx.conf
打開 conf 文件后,我們找到 Server 節點,會看到以下的配置項。
server {listen 80; server_name localhost; location / { root html; index index.html index.htm; } }
當使用 Nginx 時,會根據 server_name 匹配服務器,例如當我們運行 Nginx 成功后,通過瀏覽器瀏覽本機的 ip 時,默認會顯示 Nginx 的默認頁面。而當沒有匹配的 server_name 時,Nginx 則會使用默認服務器。 如果沒有定義默認服務器,則配置文件中的第一臺服務器則成為默認服務器。
這里進行修改配置信息,將80端口的請求轉發到我們使用 Kestrel 監聽的5000端口上的應用上。
server {listen 80;server_name localhost;location / { # Kestrel proxy_pass http://localhost:5000;proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Host $http_host; proxy_cache_bypass $http_upgrade; } }
我們可以使用命令去驗證下修改的配置格式是否正確,配置正確后就可以執行 reload 命令使配置生效。
./nginx -t ##測試格式是否正確
./nginx -s reload ##重新加載配置
當我們完成上面的步驟后,通過瀏覽器打開我們的頁面,毫無意外的 Nginx 的錯誤頁面出現在了我們的面前。仔細梳理下我們的流程,用戶通過瀏覽器請求 ip,Nginx 將默認的 80 端口的請求反向代理轉接到我們應用程序的 5000 端口上,而現在我們并沒有使用 dotnet 命令來運行我們的程序,服務器上的 5000 端口也就沒有程序在監聽。因此當我們在使用 Nginx 進行反向代理我們的 .NET Core 程序時,我們同樣需要使用 dotnet 命令將我們的程序運行起來。
現在,將我們的程序重新使用 dotnet 命令運行起來,打開我們的瀏覽器訪問就會發現我們的網站已經部署成功了。而且瀏覽器的插件也已經識別出我們使用的 Web 服務器為 Nginx 的 1.9.9 版本。
所以這里會引出一個問題,萬一 dotnet 進程意外掛了,整個網站不就徹底掛了嗎,難道還要我們手動連接到服務器再次創建?所以我們需要能夠讓 dotnet 進程能夠自動重啟,從而避免這種情況。
微軟官方則建議我們使用 supervisor 守護程序的方式實現我們守護我們的 .NET Core 程序,確保應用服務即使閃退也會自動重啟。同時,Supervisor 也包含一個 web 管理頁面,從而方面我們的管理。
在 linux 或者 unix 操作系統中,守護進程(Daemon)是一種運行在后臺的特殊進程,它獨立于控制終端并且周期性的執行某種任務或等待處理某些發生的事件。由于在linux中,每個系統與用戶進行交流的界面稱為終端,每一個從此終端開始運行的進程都會依附于這個終端,這個終端被稱為這些進程的控制終端,當控制終端被關閉的時候,相應的進程都會自動關閉。但是守護進程卻能突破這種限制,它脫離于終端并且在后臺運行,并且它脫離終端的目的是為了避免進程在運行的過程中的信息在任何終端中顯示并且進程也不會被任何終端所產生的終端信息所打斷。它從被執行的時候開始運轉,直到整個系統關閉才退出。
輸入下面的命令安裝 Supervisor 程序。
sudo yum install supervisor
安裝完成后我們就可以配置我們的 Supervisor 程序。
首先,我們在 etc 目錄下創建存儲我們配置文件的目錄。
mkdir -m 700 -p /etc/supervisor
目錄創建完成后,我們創建 supervisor 的配置文件,這里采用的是將默認的配置文件復制一份到我們的目錄下面。
echo_supervisord_conf > /etc/supervisor/supervisord.conf
在 /etc/supervisor 目錄下我們創建一個存放我們 dotnet core 進程文件的存放目錄 conf.d。
mkdir -m 700 /etc/supervisor/conf.d
修改我們的配置文件,在配置文件的最后,將路徑指向我們創建的 conf.d 文件夾。注意注意,這里一定要把前面的 ; 去掉,否則的話這個 include 節點還是被注釋無法被應用的!!!
[include] files=/etc/supervisor/conf.d/*.conf
現在我們就創建一個 PSU.conf 配置文件來管理我們這個項目的 dotnet 進程。嗯,這里我還是采用 WinSCP 的方式進行編輯,同時,我們需要將注釋的信息刪除。
cd /etc/supervisor/conf.d/ touch PSU.conf
[program:PSU.Site] command=dotnet PSU.Site.dll #要執行的命令 directory=/usr/local/wwwroot/psu/ #命令執行的目錄 environment=ASPNETCORE__ENVIRONMENT=Production #環境變量 user=root #進程執行的用戶身份 stopsignal=INT autostart=true #是否自動啟動 autorestart=true #是否自動重啟 startsecs=3 #自動重啟間隔 stderr_logfile=/usr/local/wwwroot/logs/psu.err.log #標準錯誤日志 stdout_logfile=/usr/local/wwwroot/logs/psu.log #標準輸出日志
這里我們指明的日志輸出的文件,需要我們事先創建好。
配置完成后我們就可以創建 Supervisor 的自啟動服務。
vim /etc/systemd/system/supervisor.service
編輯我們的自啟動腳本。
[Unit] Description=supervisor [Service] Type=forking ExecStart=/usr/bin/supervisord -c /etc/supervisor/supervisord.conf ExecStop=/usr/bin/supervisorctl shutdown ExecReload=/usr/bin/supervisorctl reload KillMode=process Restart=on-failure RestartSec=42s [Install] WantedBy=multi-user.target
重新載入我們的設置。
systemctl daemon-reload
設置 supervisor.service 服務開機啟動。
systemctl enable supervisor.service
啟動我們的服務
systemctl start supervisor.service
現在我們就使用命令查看我們的程序是否運行,最后的 PSU.Site 則是你設置的配置文件里的 program 名稱。
[root@VM_0_3_centos ~]# ps -ef | grep PSU.Site root 1382 545 4 19:45 ? 00:00:05 dotnet PSU.Site.dll root 1648 1606 0 19:47 pts/0 00:00:00 grep --color=auto PSU.Site
如果這里你無法看到兩個進程的話,則說明你的程序沒有啟動成功,你可以去之前設置的程序的錯誤日志文件處查看因為什么原因導致的程序無法啟動。同時,當你對配置文件做了任何的改變后,你都需要將 Supervisor 進行重啟。
?四、總結
?這次的文章應該是目前寫過時間最長也是字數最多的一篇了,就像標題寫的一樣, Linux 小白,整個過程持續了很多天,中間在重裝系統的過程中碰巧還遇到了 aspnetcore 的一個 bug(Missing package dotnet-runtime 2.1.6 for CentOS),看到 bug 關閉后,周六又弄了四五個小時按照步驟一步步走下來才完成了整個的部署。網上有很多將 .NET Core 程序部署到 Linux 服務器的文章,可是,看再多遍,當你嘗試的時候,還是會發生很多的問題,如果你有將 .NET Core 程序部署到 Linux 服務器上的計劃時,希望你可以實際嘗試嘗試,畢竟,踩的坑多了,稍微也能避開一點坑了,哈哈哈。
?五、參考
1、如何在CentOS 7上安裝MySQL
2、CentOS 7 下 Nginx安裝以及配置
3、CentOS 7 源碼編譯安裝 Nginx
4、ASP.NET Core Linux下為 dotnet 創建守護進程(必備知識)
5、Centos 7 .Net core后臺守護進程Supervisor配置