當前位置: 妍妍網 > 碼農

升級了計畫的部署方式,坑死我了!

2024-04-29碼農

大家好,我是程式設計師魚皮。如標題所言,最近這兩天,我對我們公司部份計畫的部署方式進行了改造升級。

由於部署方式的調整可能會影響到線上使用者的正常存取,所以只能挑在使用者少的時間(淩晨)進行調整和測試。

結果沒想到踩了不少坑,直到昨天半夜我還在跟其他團隊的技術同學一起找 Bug:

這篇文章給大家分享下我們計畫部署方式升級的形式、過程以及遇到的一些坑點,說不定以後大家也會用到~

為什麽要進行升級?

最開始的時候,我們的計畫幾乎都是部署到伺服器上的,而且很多計畫是共用一台伺服器,像這樣:

為啥要這麽做呢?

答案很簡單,成本低啊!因為我正好有幾台配置很高的伺服器,這些伺服器如果只部署 1 - 2 個計畫,CPU、記憶體、頻寬都用不滿,妥妥的浪費資源。

而且現在小公司或個人部署計畫可以直接使用寶塔 Linux 面板,非常方便。

所以除非必要,我們盡量不會使用額外產生費用的 CDN、按量計費的容器平台等等。

轉眼從我創業到現在已經過去了一年多,為什麽我們現在要重新調整計畫的部署方式呢?

主要的幾個原因:

1)隨著業務增長,單體計畫未必能夠滿足訴求,我們可能要將同一個計畫部署在多個節點上,實作負載均衡和容錯,手動部署就太麻煩了。這就需要能夠靈活擴縮容機器節點的能力和流水線部署的能力。

2)計畫部署在同一伺服器,如果伺服器宕機,將同時影響多個計畫。

3)計畫之間存在資源競爭,比如某個計畫正在做大力推廣、占用大量頻寬資源,其他計畫的可用頻寬就很少了,存取會很慢。

4)許可權風險。一旦給開發者開通伺服器的存取許可權,將能改動所有計畫,還有誤操作的可能性。

基於這些原因,再加上出現過一些事故,我們決定升級計畫的部署方式。

部署方式變更

以前,我們的部署方式如下圖:

使用者要請求網站時,先透過 DNS 網域名稱解析,找到伺服器對應的 IP,經過高仿伺服器後請求發送到 Nginx Web 伺服器。然後 Nginx 根據請求路徑判斷,如果要存取檔,找到前端網站檔;如果請求的是介面,反向代理到後端服務。

升級後,我們的部署方式如下圖:

主要有 3 個改動:

1)接入有安全防護和資源加速能力的 CDN,可以提高前端網站的載入速度。

2)後端使用容器平台進行部署,擁有動態擴縮容、負載均衡的能力。

3)前後端部署分離,不再依賴 Nginx 進行轉發,而是區分不同的請求網域名稱,透過 DNS 解析到不同的 CDN 上。

CDN 平台我是同時使用了騰訊雲 CDN 和藍易雲 CDN,不同的計畫選擇了不同的 CDN。藍易雲 CDN(tsycdn.com)雖然不像騰訊雲那麽有名,但是價效比更高,能夠有效防止 DDOS 攻擊。在我網站被頻繁攻擊的那段時間,他們也幫了我不少。

而且最打動我的還是他們的技術支持,能耐心陪我一起改幾個小時的 Bug,沒誰了:

容器平台的話,我們將一部份服務放在了微信雲托管上,可以很方便地配置流水線,實作送出程式碼到 GitHub 後自動釋出和部署:

還可以檢視服務日誌、資源占用情況:

雖然微信雲托管平台感覺很久沒更新了,配置容器的靈活性也沒那麽高,但是能夠滿足大多數開發者的使用訴求了。

升級過程

1、後端服務遷移

既然後端服務要部署到容器平台,肯定要把計畫制作為 Docker 映像。

方法很簡單,在後端計畫根目錄建立一個 Dockerfile ,編寫構建映像的命令即可。

比如 Spring Boot 計畫可以使用類似下面的配置:

# 選擇基礎映像
FROM maven:3.8.1-jdk-8-slim as builder
# 解決容器時期與真即時間相差 8 小時的問題
RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone
# 復制程式碼到容器內
WORKDIR /app
COPY pom.xml .
COPY src ./src
# 打包構建
RUN mvn package -DskipTests
# 容器啟動時執行 jar 包
CMD ["java","-jar","/app/target/server.jar","--spring.profiles.active=prod"]


這裏有個比較坑的地方,要註意容器環境的時間,有可能會和真即時間相差 8 小時,導致日誌時間、以及插入到資料庫的時間錯誤。

2、配置 CDN

配置 CDN 的關鍵是配置源站的地址。CDN 相當於是緩存,如果使用者需要的數據在 CDN 上找不到,CDN 節點就會請求源站來獲取數據,所以源站配置一定不能錯。

上圖中的回源設定,是指 CDN 請求源站的方式,包括協定、網域名稱埠號等。

這裏有 2 個註意事項:

1)避免給源站添加任何的重新導向邏輯,否則可能重新導向時直接暴露了源站地址。

比如 cdn 地址是 "yupi.icu",源站地址是 "base.yupi.icu",一般源站地址是要隱藏起來的,否則使用者就可以繞過 CDN 直接攻擊你的源站。如果源站配置了重新導向邏輯,比如將字尾 "/" 路由到 "/aaa"。那麽使用者在存取 "yupi.icu/" 時,可能會被自動重新導向到 "base.yupi.icu/aaa"!暴露了!

2)如果 CDN 站點開啟了 HTTPS,回源協定盡量用 HTTP,否則可能會出現因為相同證書子網域名稱 SSL 配置不一致導致的 421 錯誤(Misdirected Request),這個錯誤可以說是非常冷門了,不自己上線個計畫,大機率聽都沒聽說過。

3、配置 DNS

打通 CDN 到源站(容器平台)的存取後,最後一步就是配置 DNS,讓使用者存取的網域名稱(比如 www.code-nav.cn)解析到 CDN。

需要註意的是,DNS 的解析生效時長在全國各地是不等的,所以有可能更改解析後,北京的使用者存取不了、上海的使用者能存取。所以不要急著把老服務下線掉!

本來以為很順利,結果呢,CDN 存取源站竟然失敗了,源站返回了 444 錯誤碼(連線已關閉)!又是一個冷門的錯誤!

這個錯誤可把我折騰壞了,為啥我的伺服器會拒絕國內 CDN 節點的連線呢?首先第一個猜測就是伺服器封禁了 IP,於是查了高防、查了伺服器防火墻、還咨詢了雲服務商的客服,結果都說沒有封禁 IP。。。

於是,我去看了下 Nginx 的日誌:

CDN 節點的 IP - - [29/Apr/2024:00:06:41 +0800] "GET /favicon.ico HTTP/1.1" 444 0 "https://www.code-nav.cn/""Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"

Nginx 既然已經收到了請求,說明大機率是 Nginx 配置拒絕了連線。但是我翻爛了 Nginx 的配置,也沒找到在哪配置了 IP 封禁。。。

最後你猜怎麽著?我突然想起來幾年前,我曾經在這個伺服器上購買過 Nginx 防火墻。雖然它早已過期,但貌似還能幫我自動封禁一些 IP。。。估計是因為昨天配 CDN 時我為了測試驗證,使得存取源站頻率過高導致的。

於是我把 Nginx 防火墻解除安裝了,就沒有這個錯誤了。

用 4 個字來形容,我想到了 「陰魂不散」。


以上就是本期分享。當然,實際的升級過程,可比我這篇文章描述地還要麻煩,因為還涉及到一些平台(比如微信公眾平台)的白名單配置。而且我們升級肯定是需要灰度的,先拿存取量最低的計畫去驗證流程,再陸續遷移其他計畫。
👇🏻 點選下方閱讀原文,獲取魚皮往期編程幹貨。

往期推薦