檢視次數:
您可以使用 TrendAI Vision One 控制台和 Google Cloud 控制台,將 Google Cloud 專案從 TrendAI 雲端 One File Storage Security (C1FSS) 更新至 TrendAI Vision One™ File Security Storage (V1FSS)。本主題涵蓋適用於兩種更新路徑的準備、設定和行為變更。
注意
注意
此更新的三個部分在任一端的控制台頁面上均無顯示。雲端儲存通知配置在 Google Cloud 控制台中未顯示,V1FSS 部署包必須從 shell 使用 Terraform 應用,並且推廣和隔離目的地是在該包的 main.tf 文件中設置,而不是在任一控制台中。預期在雲端 Shell 中與兩個控制台一起工作。

選擇更新路徑

有兩條更新路徑可用。兩者執行相同的工作,只是兩個動作的順序不同:啟用 V1FSS 掃描和移除 C1FSS bucket 通知。

更新路徑

路徑
順序
結果
零停機時間
先開啟 V1FSS,再移除 C1FSS
重疊情況下,兩個產品會掃瞄每個新物件
短暫停機
先移除 C1FSS,再開啟 V1FSS
一個兩個產品都未掃描的空隙
  • Zero downtime:選擇此路徑可避免掃描中斷。請注意,在 C1FSS 掃描停止之前的間隔期間,每個新物件都會被 C1FSS 和 V1FSS 掃描。如果使用 promote-or-quarantine 插件並將其設定移至 V1FSS,兩個產品也會移動相同的物件,這可能導致競爭條件。這些情況會導致物件元數據不一致、檔案狀態不一致(其中一個產品移動物件,而另一個仍在讀取)、或重複處理(兩個產品嘗試相同的移動,贏得者刪除來源,而另一個記錄失敗)。
  • Brief downtime:如果您能容忍掃描的短暫中斷,請選擇此路徑。掃描僅在移除 C1FSS bucket 通知和在 TrendAI Vision One 控制台中啟動掃描之間暫停,通常只需幾分鐘。此路徑在更新過程中出現問題的機率最低,如果您使用 promote-or-quarantine 插件,這是更好的選擇。

在您開始之前

在您更改任何內容之前,請先完成以下六項檢查。這些檢查適用於兩種更新路徑。
  1. Confirm that V1FSS can replace your C1FSS layout.
    兩個 C1FSS 佈局沒有對應的 V1FSS。請在規劃其他事項之前檢查這兩個佈局。

    支援的 C1FSS 佈局

    C1FSS 佈局
    是否支援更新?
    全合一(掃描堆疊和存儲堆疊一起)
    僅限掃描器堆疊或僅限儲存堆疊,在同一專案中
    一個專案中的多個存儲堆疊
    是的。它們會合併成一個 V1FSS 堆疊。
    跨專案,儲存桶在一個專案中,掃描器在另一個專案中
    No
    多區域或雙區域儲存桶
    No. V1FSS for GCP 僅保護單一區域的儲存桶。
    重要
    重要
    跨專案佈局會改變您整個計劃。每個 V1FSS 資源都限定在單一專案內,且一個儲存桶只能由其自身專案中的掃描器提供服務。如果 C1FSS 以星狀架構運行,您需要為每個擁有儲存桶的專案部署一個 V1FSS,並且中心掃描器專案將不再存在。請檢查 Cloud One 控制台以確認您當前的設置是否為跨專案,如果是,請在更改任何內容之前計劃每個專案的分割。
  2. Check whether you use the promote-or-quarantine plugin.
    C1FSS 可以將乾淨或惡意物件移動到其他儲存桶,但只能通過每個掃描儲存桶部署一次的單獨插件來實現。V1FSS 內建此功能,但設定不會自動傳遞,且有一個預設值不同,會影響到您的檔案存放位置。該插件也不屬於任何 C1FSS 堆疊的一部分,因此此更新中的其他內容無法找到或移除它。
    在雲端 Shell 中執行以下命令。
    gcloud functions list --filter="name~promote-and-quarantine" --format="value(name,region)"
    如果命令返回任何內容,請在開始之前閱讀更新提升和隔離設定,並立即記錄插件設定。您需要在安裝 V1FSS 之前,而不是之後使用它們。
  3. 「Record what C1FSS protects, and how.」 請先執行此操作,再進行任何更改。
    C1FSS Bucket Listener 是一個具有 google.storage.object.finalize 觸發器的雲端函數(第一代)。雲端函數會在您的儲存桶上建立通知配置,以傳送這些事件,而在切換時您需要移除該通知配置。現在記錄其詳細資訊:這些是確認 C1FSS 已停止掃描的基準,並在回滾時確認其正確恢復的基準。一旦 C1FSS 堆疊消失,其中一些資訊將變得無法讀取。
    1. 在雲端 One 控制台中,前往「File Storage Security」「Stacks」,並記下每個堆疊的名稱、類型(掃描器、儲存或全方位)及地區。
    2. 在雲端 Shell 中,執行以下命令以查找每個 Bucket Listener 所保護的儲存桶,以及您需要進行切換的所有其他內容。
      OUTFILE="c1fss-inventory-$(date +%Y%m%d).csv"
      echo "function,region,bucket,c1_notification_id,c1_notification_topic,object_finalize_count" > "$OUTFILE"
      
      gcloud functions list --filter="name~bucket-listener" --format=json </dev/null \
      | jq -r '.[] | (.name | split("/")) | "\(.[5]),\(.[3])"' \
      | while IFS=, read -r fn region; do
          bucket=$(gcloud functions describe "$fn" --region="$region" \
            --format="value(eventTrigger.resource)" </dev/null | sed 's#.*/buckets/##')
      
          if [ -z "$bucket" ]; then
            echo "$fn,$region,,MISSING,MISSING,0" >> "$OUTFILE"
            continue
          fi
      
          notifs=$(gcloud storage buckets notifications list "gs://$bucket" --format=json </dev/null 2>/dev/null)
          id=$(echo "$notifs" | jq -r '[.[]."Notification Configuration" | select(.topic | test("cloud-functions-"))][0].id // "MISSING"')
          topic=$(echo "$notifs" | jq -r '[.[]."Notification Configuration" | select(.topic | test("cloud-functions-"))][0].topic // "MISSING"')
          count=$(echo "$notifs" | jq '[.[]."Notification Configuration" | select(.event_types == null or any(.event_types[]; . == "OBJECT_FINALIZE"))] | length')
          echo "$fn,$region,$bucket,$id,$topic,$count" >> "$OUTFILE"
        done
      
      column -s, -t "$OUTFILE"
      echo "Saved to: $(pwd)/$OUTFILE"
    3. 從雲端 Shell 下載 CSV 檔案並將其儲存到可存取的位置,與第一步中的 Cloud One 堆疊名稱和區域一起。文字檔或票據評論即可。
    在 CSV 檔案和堆疊列表之間,您擁有更新所需的一切:哪個函數保護哪個桶,生成部署包時要選擇的區域,C1FSS 通知 idtopic 在切換時刪除並在還原時確認,重新部署還原和在拆除時刪除的函數名稱,以及作為您下面第六次檢查基準的 OBJECT_FINALIZE 計數。
    注意
    注意
    如果儲存桶沒有指向雲端函數主題的通知配置,C1FSS不會掃描它。請將該儲存桶排除在更新之外。
  4. Identify which C1FSS generation you have.
    C1FSS 提供了兩種 GCP 部署機制,並且它們的拆除方式有所不同。請在雲端 Shell 中執行以下命令。
    gcloud deployment-manager deployments list

    C1FSS 部署代

    結果
    產生
    拆卸命令
    名為 <prefix>-scanner<prefix>-storage 的部署
    部署管理器
    gcloud deployment-manager deployments delete
    未列出部署
    Terraform
    terraform destroy,在您被告知要保留的目錄中執行
  5. Confirm that you have the required permissions and tools.
    • Permissions on the Google Cloud project:完整列表請參閱Google 雲端必要的權限
    • Terraform in Cloud Shell:雲端 Shell 提供 gcloudgsutiljq,但不再預先安裝 Terraform。請自行安裝 Terraform,因為 deploy.sh 無法在沒有它的情況下執行。
    注意
    注意
    任一更新路徑的第 1 步執行 ./deploy.sh precheck。該命令是一個 Terraform 計劃,顯示將創建的內容。這不是權限檢查,因此請單獨確認您的權限。
  6. 「Check the notification slots on each bucket.」 此檢查僅適用於 GCP。
    一個儲存桶最多可以容納 10 個單一事件類型的通知配置。C1FSS 使用了一個,而 V1FSS 需要再使用一個。如果一個儲存桶已經有 9 個或更多 OBJECT_FINALIZE 配置,則在 V1FSS 中啟用它會靜默失敗:TrendAI Vision One 會在內部記錄失敗並繼續,儲存桶仍然未受保護,且控制台中不會顯示錯誤。
    for b in <bucket-1> <bucket-2>; do
      n=$(gcloud storage buckets notifications list "gs://$b" 2>/dev/null | grep -c 'OBJECT_FINALIZE' || true)
      echo "$b: $n OBJECT_FINALIZE notification(s)"
    done
    任何在9或以上的桶在啟動之前需要釋放一個插槽。

更新促銷和隔離設定

如果 gcloud functions list --filter="name~promote-and-quarantine" 沒有返回任何內容,請跳過此部分。沒有插件的情況下,C1FSS 只寫入物件的元數據,V1FSS 也是如此。
在 C1FSS 中,提升和隔離是每個掃描桶部署一次的獨立插件,擁有自己的 Terraform 狀態。在 V1FSS 中,它們內建於掃描後行動服務中,並在部署包的 main.tf 文件中的 module "file-storage-security" 區塊中進行配置。V1FSS 出貨時目的地是空的,這會在原地標記對象,因此您需要在安裝前在包中設置它們。
請先記錄插件設定
每個插件實例使用共用的10個字元後綴命名其資源,該後綴在函數名稱中可見。在雲端 Shell 中執行以下命令以列出每個實例及其區域。
gcloud functions list --filter="name~promote-and-quarantine" --format=json </dev/null \
| jq -r '.[] | (.name | split("/")) | "\(.[5])\t\(.[3])"' | column -t
該插件的完整配置是四個環境變數。在 Google Cloud 控制台中,前往「Cloud Functions」「the plugin function」「Variables」,或執行以下命令。
gcloud functions describe <plugin-function-name> --format="value(environmentVariables)"

插件設定以進行記錄

設定
這意味著什麼
PROMOTE_STORAGE_BUCKET
清理對象的目的地。空值表示已關閉提升功能。
PROMOTE_MODE
移動,會刪除來源物件,或複製,則保留來源物件
隔離儲存桶
惡意物件的目的地。空值表示隔離已關閉。
隔離模式
移動複製
沒有失敗或隔離的目的地。該插件僅對乾淨和惡意結果進行操作。還請注意每個實例作用的存儲桶:它是與插件一起部署的 C1FSS 存儲堆疊的掃瞄存儲桶,您可以通過插件觸發的掃瞄結果 Pub/Sub 主題來匹配。
在 main.tf 中設定值
打開解壓縮套件中的 main.tf。雲端 Shell 包含一個編輯器,您可以使用 cloudshell edit main.tf 打開它。找到 module "file-storage-security" 封鎖並設定以下三個值。
  1. 「Where objects go.」 每個目的地都是一個 JSON 字串,解析順序為自訂、區域,然後是全球。
    # If every protected bucket used the same destination:
    clean_bucket      = jsonencode({ global = "my-clean-bucket" })
    quarantine_bucket = jsonencode({ global = "my-quarantine-bucket" })
    
    # If destinations differed per source bucket:
    quarantine_bucket = jsonencode({
      custom = {
        "source-bucket-1" = { destBucket = "quarantine-bucket-1", destRegion = "us-east1" }
        "source-bucket-2" = { destBucket = "quarantine-bucket-2", destRegion = "us-west1" }
      }
    })
    
    # Or per region, keyed by region name, with an optional global fallback:
    quarantine_bucket = jsonencode({ "us-east1" = "quarantine-use1", global = "quarantine-fallback" })
    如果該操作被關閉,則將變數保留為jsonencode({})。這些物件會在原地被標記,與之前完全相同。
    重要
    重要
    僅在您保護的每個儲存桶都有該動作的插件時使用globalglobal值適用於所有儲存桶,包括從未有過插件的儲存桶,以及僅在 C1FSS 下標記的儲存桶。如果您為它們指定目的地,V1FSS 會開始移動它們的物件。如果只有部分儲存桶有插件,請使用custom形式,這只會影響您列出的來源儲存桶。
  2. 「Where objects land inside the destination.」 您必須更改此值。
    use_source_bucket_as_folder = false
    此套件隨附 true,這是新部署的正確預設值,但不適用於更新。

    目標路徑

    組態設定
    設定您物件的落地路徑
    C1FSS 插件
    <目標>/<物件>
    V1FSS 與 false
    <destination>/<object>,與 C1FSS 相同
    V1FSS 與 true
    <destination>/<source-bucket>/<object>,這是不同的
    如果任何內容透過路徑讀取您隔離或提升的物件,true會在切換時中斷它。請注意此設定存在的權衡:使用false時,兩個來源儲存桶中擁有相同名稱的物件會在目的地互相覆蓋。C1FSS的行為方式相同,因此false符合您目前的行為。如果這只是因為每個插件實例都有自己的目的地儲存桶才安全,請保持這種安排,使用custom形式,而不是合併到一個global目的地。
  3. 「Leave the failure destination empty」,以符合 C1FSS。
    failure_bucket = jsonencode({})
    C1FSS 插件沒有失敗目的地,因此掃瞄失敗的物件會留在原地。V1FSS 可以將它們移動到其他地方,這是一項新功能,但不是您目前的行為。
如需有關 V1FSS 中目標儲存區的詳細資訊,請參閱 在 GCP 中自訂目標儲存桶
無法延續的兩種行為
  • 「Copy mode.」 如果 PROMOTE_MODEQUARANTINE_MODEcopy,則插件會保留原始物件。V1FSS 總是移動物件:它會將物件複製到目的地,然後刪除原始物件。請確保沒有任何依賴於原始物件留在來源儲存桶。
  • 「Destinations in another project.」 V1FSS 在 Terraform 應用過程中,會將其掃描後行動服務帳戶 roles/storage.objectAdmin 授予每個目標儲存區,因此您執行 deploy.sh 的身份需要有權限在該儲存區上設置 IAM。如果目標儲存區位於其他專案中且您無法授予該權限,則應用會在綁定時失敗。將儲存區移至此專案,或在您部署後請其他專案的管理員授予 …-action-tag 服務帳戶存取權。
重要
重要
該插件在將物件複製到目的地之前,會完全替換物件的元數據,而不僅僅是fss-鍵。對於插件作用的物件,目的地副本中將缺少V1FSS鍵和您自定義的任何元數據。這是現有的C1FSS行為,更新不會改變它。

更新後的變更內容是什么

V1FSS 並非 C1FSS 的精確功能對應。在刪除任何內容之前,請先檢閱以下內容。
有何不同
  • 「Bucket selection is a console setting, not a deployment parameter.」 C1FSS 在您部署堆疊時修正了掃描桶,每個桶一個堆疊。V1FSS 為每個專案部署一次,您可以在 TrendAI Vision One 控制台中開啟或關閉桶。
  • 「Only one V1FSS stack is supported for each project.」 V1FSS 將其部署元數據記錄在具有固定名稱的密碼中,因此同一專案中的第二個堆疊無法應用。幾個 C1FSS 存儲堆疊合併為一個 V1FSS 堆疊,這通常是您想要的,但這不是可選的。
  • 「V1FSS grants itself project-wide access to Cloud Storage.」 它不使用每個儲存桶的 IAM 綁定,而是創建兩個涵蓋專案中每個儲存桶的專案級自訂角色。沒有選項可以限制對個別儲存桶的存取。如果您依賴 C1FSS 中的每儲存桶模型作為控制,請在更新前檢閱此項。
  • 「V1FSS uses Cloud Run v2 instead of Cloud Functions (1st gen)」,因此不依賴於雲端建置、雲端函數或部署管理器 API。
  • 「Promote and quarantine always move objects, never copy them」,並且它們是在部署包中配置一次,而不是在每個存儲桶中分別配置一次插件。
  • 「The scan-result Pub/Sub topic changed」,以及其訊息的格式。如果您在 C1FSS …-scan-result-topic 主題上有自己的訂閱者,請將其訂閱到 V1FSS 主題並更新您的解析器。
  • 「Scan history stays in Cloud One.」 TrendAI Vision One 在切換時重新開始。在您的雲端 One 租戶被移除之前,請匯出您需要保留的任何內容。
不會延續的項目
  • A cross-project scanner:V1FSS 部署僅限於一個專案。中心輻射式 C1FSS 佈局會為每個擁有儲存桶的專案建立一個 V1FSS 部署。
  • 「Object prefix filtering」 (objectFilterPrefix):V1FSS Terraform 支援此功能,但 TrendAI Vision One 管理的部署路徑無法設定此功能。如果 C1FSS 只掃描桶的前綴,V1FSS 則會掃描整個桶。請先檢查您的物件容量,因為對於覆蓋大型桶一小部分的前綴而言,這是掃描範圍的重大變化。
  • 「Per-bucket IAM scoping」 (disableScanningBucketIAMBinding): 在 V1FSS 中無對應項。
  • 「Reporting object keys to the backend」 (reportObjectKey): 在 V1FSS 中沒有對應。物件鍵在遙測中始終是經過雜湊的。
  • Multi-region and dual-region buckets:未受任何產品的 V1FSS 路徑保護。它們始終報告為未部署。
您將獲得的內容,以及之後可能想要設定的項目
  • 「Detections in TrendAI Vision One XDR」 和 Workbench 的可見性,以及您其他的安全資料。
  • 「Object moving without a separate plugin」,內建於 V1FSS 中,並且只需配置一次,而不是每個儲存桶都配置一次,無需管理額外的服務帳戶或自訂角色。
  • 「A destination for failed scans」 (failure_bucket)。C1FSS 插件將這些物件保留在原處。
  • 「Better failure handling」,具有三個死信佇列和指數退避機制,而不是一個佇列,並且每日自動憑證輪替。
  • A configurable metadata format:分離的密鑰,這是預設值並符合 C1FSS,單一合併密鑰,或完全沒有元數據。

常見問題

Which update path should I use?
如果您無法暫停掃描,請使用零停機時間。如果可以,請使用短暫停機時間,尤其是在使用 promote-or-quarantine 插件並且攜帶其設定時。該插件是唯一一個重疊會導致實際問題而不僅僅是重複工作的情況。
How long is the brief downtime?
僅限於移除 C1FSS 存儲桶通知與在 TrendAI Vision One 控制台中開啟掃描之間的間隔。兩者都是單一操作,因此通常只需幾分鐘。時間長短取決於您,而非任何部署步驟。
In the zero-downtime path, why not remove the C1FSS bucket notification before validating?
在重疊期間進行驗證會使結果難以歸因,因此您改為在 C1FSS 從未保護的桶上進行驗證。該桶上沒有重疊,因此沒有任何因素保持重疊窗口開啟,每個結果都可歸因於 V1FSS。先移除 C1FSS 通知不會縮短更新時間,因為 V1FSS 已經在兩個順序中開啟。這只會使風險提前:如果 V1FSS 被錯誤配置,您會發現您的真實桶已經未受保護。您仍然可以在 C1FSS 保護的桶上獲得端到端確認,在步驟 3 結束時的檢查點,C1FSS 通知消失但其其他資源尚未刪除。在那時,歸因是明確的,還原仍然可用。
Which buckets does C1FSS actually protect?
Cloud One 控制台的「File Storage Security」「Stacks」頁面會命名每個儲存堆疊後的掃描儲存區。使用gcloud storage buckets notifications list gs://<bucket>確認儲存區本身。如果沒有指向雲端函數主題的通知配置,C1FSS 就不會掃描該儲存區。
Why are my objects being scanned twice?
在零停機路徑中,兩個產品在重疊期間都會開啟。這就是該路徑避免停機的方式,當您移除 C1FSS 存儲桶通知時,該路徑將停止。
My fss- object metadata keeps changing, or keys keep disappearing.
這是在重疊期間預期的行為。C1FSS 在每次掃瞄時會替換整個 fss- 中繼資料集,這會移除 V1FSS 寫入的額外鍵,而 V1FSS 則是合併其鍵。當 C1FSS 被拆除時,這個過程會停止。在此之前,請勿依賴 fss- 中繼資料。
I turned on a bucket, the console accepted it, but nothing is being scanned.
使用 gcloud storage buckets notifications list gs://<bucket> 檢查通知數量。雲端儲存對每個事件類型的通知數量上限為 10,當達到上限時,TrendAI Vision One 會在內部記錄失敗並繼續,因此儲存桶保持未受保護且沒有可見錯誤。請釋放一個空位並重新啟用儲存桶。請參閱 開始之前
My buckets do not appear on the Inventory page.
TrendAI Vision One 透過在您的部署註冊時探測您的專案來發現儲存桶,然後大約每小時進行一次。請稍候並刷新。
A bucket shows as not deployed and will not turn on.
該儲存桶的區域沒有註冊的 V1FSS 掃描器。可能是該區域不受支持,或者您在生成部署包時未選擇該區域,亦或探測器尚未更新。您也可能在多區域或雙區域儲存桶中看到此情況,這些儲存桶無法受到保護。
The deploy.sh script fails immediately, saying that Terraform is not a real binary.
雲端 Shell 不再預先安裝 Terraform。請安裝它並再次執行命令。請參閱開始之前
The deploy.sh install command failed. Can I run it again?
是的。該腳本是使用 Terraform 在您自己的專案中運行於雲端儲存後端,因此再次運行時會從記錄的狀態繼續,而不是重新開始。修正它報告的任何問題,然後再次運行相同的命令。不要在嘗試之間刪除狀態儲存桶,並檢查 deploy_gcp.log 以了解發生了什麼。
The Terraform apply failed on a disabled API.
deploy.sh 腳本僅啟用 V1FSS 所需的一部分 API。最常缺少的是 run.googleapis.comcloudscheduler.googleapis.com。請啟用完整列表並再次執行 ./deploy.sh install
The Terraform apply failed, saying that the secret exists.
此專案中已經有一個 V1FSS 堆疊,且僅支援一個。請使用現有的堆疊,或在重新部署之前,從其套件目錄中使用 ./deploy.sh uninstall 將其移除。
The Terraform destroy command refuses to delete the C1FSS custom roles.
這是預期的,因為在 C1FSS gcp-configuration 模組中,角色被標記為 prevent_destroy。請直接從 Google Cloud 控制台中的 「IAM & Admin」「Roles」 刪除角色,或使用 gcloud iam roles delete,並保持 gcp-configuration 不變。
How do I remove the V1FSS deployment?
從套件目錄執行 bash ./deploy.sh uninstall。如果此專案有其他 TrendAI Vision One 功能連接,請小心,因為該命令會摧毀整個雲端帳號管理 Terraform 狀態,這意味著該套件部署的每個功能都會受到影響,而不僅僅是 File Security Storage。若只需移除 File Security Storage,請在 TrendAI Vision One 控制台中清除它,下載重新生成的套件,然後再次執行 ./deploy.sh installuninstall 命令也會保留 TrendAI Vision One 雲端帳號記錄,除非您添加 --delete_cloud_account
Teardown left buckets, roles, or APIs behind. Is that a problem?
trend_micro_fss_* 自訂角色和 appspot 服務帳戶綁定是對雲端 One 服務帳戶的過時授權,因此請將它們移除。Artifact、pattern-update 和 gcf-sources-* 儲存區是無害的,如果您不想保留它們,可以刪除。已啟動的 API 是無害的。遺留的 qua-pro-plugin-sa-… 服務帳戶則不無害,因為它可以刪除您儲存區中的物件。
I already installed V1FSS, and now I want to set the promote and quarantine destinations.
在套件目錄中編輯 main.tf,然後再次執行 bash ./deploy.sh install。這是更新,不是新的部署。如果您不再擁有該套件,請從 TrendAI Vision One 控制台重新下載。重新生成的套件目的地是空的,因此在安裝之前請再次應用您的編輯。
Objects are moving, but landing at a different path than they did under C1FSS.
這是 use_source_bucket_as_folder 設定。套件預設為 true,這會在目標路徑前加上來源儲存桶名稱,而 C1FSS 插件則不會。將其設為 false,然後再次執行 ./deploy.sh install。請參閱 更新推廣和隔離設定
Can I do this without Cloud Shell?
是的,從任何已安裝gcloudjq和Terraform的機器上,並且已使用gcloud auth login進行身份驗證且指向正確的專案。建議使用雲端Shell,因為它已經具備大多數這些工具,並且具有環境憑證。
Can I update several projects at once?
每個專案都是獨立的更新,擁有自己的部署包、註冊和儲存桶。該包還包括 org_deploy.sh,它會針對組織或資料夾中的每個專案執行 deploy.sh。這對於大量專案非常有用,但不會改變此處描述的每個專案的流程。