我常用的一個場景是一台Linux底下有多個Spring Application,可以透過Linux systemd控制他們,然後使用Nginx的反向代理產生唯一對外窗口,讓外界透過Nginx與Spring Application溝通。以下教學在Debian與Ubuntu都是可以使用的。CentOS可能也可以,但自從RedHat宣布準備停止支援後我就沒再用CentOS了,所以沒測試過。
Spring Boot設置
Spring Application可以用gradle bootJar
產生jar檔案執行,或是直接對專案原始碼執行gradle bootRun
執行。沒安裝Gradle的時候通常專案內會有gradlew可以免安裝使用,可以在專案根目錄使用./gradlew bootRun
。
安裝Java、JDK
在Linux上還沒安裝Java的話,這邊選擇最新的LTS版比較穩定,也就是Java 17。
# 包含JRE與JDK的Headless版(推薦)
sudo apt install openjdk-17-jdk-headless
# 包含JRE與JDK的完整版
sudo apt install openjdk-17-jdk
# 只有Java Runtime環境的版本,沒有javac等編譯指令
sudo apt install openjdk-17-jre-headless
新增使用者
我習慣增加一個不可登入的使用者,名為springboot,專門給Spring Boot使用。如果更嚴謹一點,也可以為每個不同的Spring Boot都新增一個使用者。
sudo useradd -r -s /bin/false springboot
- 參數
-r
:建立系統使用者 - 參數
-s
:指定Login Shell為不能登入
如果要讓springbooot帳號可以執行gradle指令,例如./gradlew bootRun
之類的方法,就不要在新增使用者時增加-r
參數,因為執行gradlew時需要一個家目錄給他存放gradle相關的檔案。如果像是以下範例那樣直接執行jar檔案就沒這個差別,可以使用-r
避免新增帳號家目錄。
建立Service設置
建立一個檔案「/etc/systemd/system/spring-app.service」,其中檔案名稱「spring-app」可以替換成你的服務的名稱。檔案內的「ExecStart」設置可以依照個人需求調整,也可以改成啟動一個shell腳本,將詳細的啟動指令寫在腳本內。
[Unit]
Description=Klab.tw DEMO
After=syslog.target
[Service]
Restart=always
RestartSec=3
User=springboot
ExecStart=/usr/bin/java -Xmx1024m -Xms1024m -Dserver.port=8080 -jar /home/springboot/app.jar --spring.profiles.active=prod
[Install]
WantedBy=multi-user.target
上面把各種指令都寫上ExecStart
看了有點亂,也可以改用以下寫法。
[Unit]
Description=Klab.tw DEMO
After=syslog.target
[Service]
Restart=always
RestartSec=3
User=springboot
WorkingDirectory=/home/springboot
Environment="SPRING_PROFILES_ACTIVE=prod"
Environment="JAVA_OPTS=-Xmx1024m -Xms1024m -Dserver.port=8080"
ExecStart=/usr/bin/java $JAVA_OPTS -jar app.jar
[Install]
WantedBy=multi-user.target
用了WorkingDirectory
指定了工作資料夾,Environment
設定環境變數,這樣ExecStart
就可以簡化許多,讀起來也更容易囉。
Linux Systemd
systemd由systemctl控制,需要最高權限,所以要使用sudo,或是直接轉成root身份喔。
讀取Service設置
每次新增或是修改systemd內的.service檔案後,要呼叫systemctl重新讀取設置。
sudo systemctl daemon-reload
控制方式
sudo systemctl start spring-app # 啟動服務
sudo systemctl stop spring-app # 停止服務
sudo systemctl restart spring-app # 重新啟動服務
sudo systemctl enable spring-app # 開機後自動啟動
sudo systemctl disable spring-app # 開機後不要自動啟動
檢查執行狀況
sudo systemctl status spring-app # 檢查服務狀態,可以順便看見一些服務輸出的訊息
sudo systemctl is-active spring-app # 檢查是否正在啟動著,如果是會回傳active
sudo systemctl is-enabled spring-app # 檢查是否在開機後會自動啟動,如果是會回傳enabled
sudo systemctl is-failed spring-app # 檢查是否啟動失敗,如果不是會回傳active
sudo journalctl -u spring-app # 觀看Spring Application輸出的訊息,包含Stdout與Stderr等。
最後一行提到的journalctl
,參數-u spring-app
是篩選只要觀看spring-app的輸出。的還可以加上-f參數,會顯示最後數行訊息,而且會在有新訊息時自動更新。還可以用-r
參數將訊息反向排序輸出,但是-r
不可與-f
一同使用。
Nginx設置
安裝方式
sudo systemctl disable --now apache2 # 避免已經有apache會衝突,先將其關閉
sudo apt install nginx -y
反向代理Spring服務
新增一個設定檔案放在「/etc/nginx/conf.d/spring-app.conf」,一樣檔案名稱「spring-app」的部分可以改成自己需要的名字。
server {
listen 80;
server_name klab.tw; # Domain name 依需求自行修改
# 一些靜態資源可以不經過反向代理,由Nginx直接返回
location ~ ^/(|css|ext|img|js)/ {
root /opt/Spring-App/src/main/resources/static/;
}
# 需要反向代理的部分
location / {
proxy_pass http://localhost:8080; # 依需求自行修改
proxy_set_header Host $http_host; # 這些是Nginx傳給Spring的HTTP Header
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
}
}
檢查Nginx設置是否正確
sudo nginx -t
每次更改Nginx內的設定檔後,先不要急著生效,可以先用nginx -t
來檢查是否有錯誤。
呼叫Nginx重新讀取設置
sudo systemctl reload nginx
Nginx多一個reload
指令可以使用,可以不重啟Nginx就讓新的設定生效,是個方便的功能。