Confidential Space 圖片

Confidential Space 映像檔是精簡的單一用途 OS,可在 Confidential VM 執行個體上執行。這項資源旨在執行單一工作負載一次,不提供永久儲存空間。該工作負載會使用 Docker 疊加在 Confidential Space 映像檔上。

Confidential Space 映像檔是以 Container-Optimized OS 現有的安全性強化功能為基礎,並新增下列優點:

  • 加密磁碟分區,並提供完整性保護

  • 經過驗證的加密網路連線

  • 各種啟動測量

  • 停用遠端存取和雲端專屬工具

圖片類型

Confidential Space 映像檔有兩種:

  • 正式環境:正式環境映像檔用於執行實際正式環境工作負載,並使用實際正式環境資料。並受到嚴格限制,防止工作負載操作員存取處理過的資料。詳情請參閱「Confidential Space 安全總覽」。

  • 偵錯:偵錯映像檔用於在非正式環境資料上測試工作負載。偵錯映像檔已啟用 SSH,且操作員具備執行工作負載的 VM 的根層級存取權。工作負載完成後,執行偵錯映像檔的 VM 不會停止。

部署工作負載時,您可以設定要使用的映像檔類型。

查看 Confidential Space 映像檔

您可以使用下列 gcloud 指令,查看可用的 Confidential Space 映像檔:

gcloud compute images list \
    --project=confidential-space-images \
    --no-standard-images

下列旗標可變更結果中傳回的圖片:

  • 新增 --filter="family~'confidential-space$'" 旗標,即可顯示正式版映像檔。

  • 新增 --filter="family~'confidential-space-debug$'" 旗標,即可顯示偵錯圖片。

查看已淘汰的映像檔

執行下列指令,在結果中納入已淘汰的映像檔,以及可協助您做出決策的其他欄位:

gcloud compute images list \
    --project=confidential-space-images \
    --no-standard-images \
    --show-deprecated \
    --filter="name ~ '^confidential-space-[0-9]{6}$'" \
    --format="table(
      name,
      creationTimestamp.date('%Y-%m-%d'):label=CREATION_DATE,
      status,
      description.sub('.*COS: ', '').sub(',.*', ''):label=COS_VERSION,
      deprecated.state:label=STATE,
      deprecated.deprecated:label=DEPRECATE_ON,
      deprecated.obsolete:label=OBSOLETE_ON,
      deprecated.replacement
    )"

成功的回應會與下列內容相似:

NAME                       CREATION_DATE  STATUS  COS_VERSION               STATE       DEPRECATE_ON  OBSOLETE_ON  REPLACEMENT
confidential-space-230600  2023-06-09     READY   cos-dev-109-17637-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-230901
confidential-space-230900  2023-09-17     READY   cos-dev-109-17637-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-230600
confidential-space-230901  2023-10-02     READY   cos-dev-113-17877-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-231001
confidential-space-231001  2023-11-01     READY   cos-dev-113-17965-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-231100
confidential-space-231100  2023-11-17     READY   cos-dev-113-18026-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-231200
confidential-space-231200  2023-12-04     READY   cos-dev-113-18054-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-231201
confidential-space-231201  2023-12-15     READY   cos-dev-113-18059-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-240200
confidential-space-240200  2024-02-28     READY   cos-dev-113-18146-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-240402
confidential-space-240402  2024-04-22     READY   cos-dev-117-18342-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-240500
confidential-space-240500  2024-05-30     READY   cos-dev-117-18374-0-0     DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-240700
confidential-space-240700  2024-07-30     READY   cos-113-18244-85-54       DEPRECATED                             https://www.googleapis.com/compute/v1/projects/confidential-space-images/global/images/confidential-space-240800
confidential-space-240800  2024-09-03     READY   cos-113-18244-151-14
confidential-space-240900  2024-10-01     READY   cos-113-18244-151-80
confidential-space-241000  2024-10-18     READY   cos-113-18244-151-96
...

Confidential Space 映像檔生命週期

使用 Confidential Space 映像檔系列建立 Confidential VM 時,系統會使用最新版本的 Confidential Space 映像檔。如果您一律在工作負載完成時刪除 Confidential VM,並在每次執行工作負載時建立新的 VM,就能確保映像檔為最新版本。

不過,如果工作負載執行時間較長,或是在過去建立的 VM 上執行工作負載,可能會使用過時的 Confidential Space 映像檔,進而產生安全漏洞。

為降低這類風險,資料協作者可以使用支援屬性,檢查 VM 執行的正式版 Confidential Space 映像檔版本是否為最新版本,如果不是,則拒絕其存取資料。偵錯 Confidential Space 映像檔未設定支援屬性。

下表說明正式版 Confidential Space 映像檔的支援屬性。

支援屬性 說明

LATEST

LATEST 映像檔也包含下列支援屬性:

  • STABLE
  • USABLE

映像檔生命週期:約一個月。之後,系統會移除 LATEST 支援屬性。

這是最新版本的 Confidential Space 映像檔,可獲得支援,並會受到監控,以防出現安全漏洞。

如果是長時間執行的工作負載,建議您不要在認證政策中指定 LATEST。這是因為 Confidential Space 映像檔可能會在工作負載執行期間更新,導致您使用的 Confidential Space 映像檔不再是 LATEST,並在下次重新整理 Confidential Space 預設權杖時,導致工作負載無法通過認證。

STABLE

STABLE 映像檔也包含下列支援屬性:

  • USABLE

映像檔生命週期:約六個月。2027 年 4 月 1 日起,STABLE 支援屬性會在映像檔生命週期結束後移除。

系統會支援具有 STABLE 屬性的映像檔,並監控安全漏洞六個月。建議您根據 STABLE 屬性編寫認證政策。

在極少數情況下,STABLE 屬性可能會在正常的六個月支援期限內移除,例如發現重大安全漏洞時。

USABLE

僅含 USABLE 屬性的圖片仍可使用,但已視為淘汰。不支援已淘汰的映像檔,且映像檔已超過六個月。您必須自行承擔使用風險。

USABLE 圖片具有 deprecated.deprecated 屬性。其值為預期淘汰映像檔的時間戳記。您可以使用 gcloud compute images describe 指令或 images.get REST 方法查看圖片屬性。例如:

gcloud compute images describe confidential-space-240800 \
    --format=json \
    --project=confidential-space-images
EXPERIMENTAL

只有這項屬性的映像檔會使用預先發布版功能。EXPERIMENTAL 映像檔僅供測試,不應用於正式環境。

EXPERIMENTAL 圖片一律不含 LATEST、STABLE 或 USABLE 屬性。

建議您將 Confidential Space 映像檔保持在最新狀態。如要瞭解如何更新 Confidential Space 映像檔,請參閱「 更新 Confidential Space 映像檔」。

監控已淘汰的映像檔

如要監控執行已淘汰或即將淘汰 Confidential Space 映像檔的 VM 執行個體,您可以部署 Cloud Run 函式,並透過 Cloud Scheduler 工作定期執行。接著,您可以設定 Cloud Logging 通知,在映像檔淘汰時收到快訊。

部署 Cloud Run 函式,檢查是否有已淘汰的映像檔

如要設定 Cloud Run 函式,請完成下列操作:

  1. 在 Cloud Run 中,前往「Services」(服務) 頁面。

    前往「Service」分頁

  2. 點選「編寫函式」。

  3. 在「Service name」(服務名稱) 方塊中,輸入函式名稱。

  4. 選取要執行函式的區域。

  5. 將「執行階段」設為最新版本的 Go。

  6. 將「Authentication」(驗證) 設為「Require authentication」(需要驗證)。

  7. 將「輸入」設為「內部」。

  8. 展開「容器、網路、安全性」部分。

  9. 在「容器」分頁中,將「記憶體」設為「128 MiB」。

  10. 在「安全性」分頁中,按一下「服務帳戶」方塊。

  11. 按一下「建立新的服務帳戶」。

  12. 在「Service account name」(服務帳戶名稱) 方塊中輸入名稱。

  13. 點選「建立」。

  14. 新增「Compute 檢視者」角色,然後點選「完成」。

  15. 點選「建立」。

  16. 函式載入後,在「Source」(來源) 分頁中,將「Function entry point」(函式進入點) 設為 DeprecationCheck。

  17. 使用下列程式碼覆寫 function.go 中的內容:

    // Package imagechecker provides a function to check for deprecated CS images.
    package imagechecker
    
    import (
    	"context"
    	"encoding/json"
    	"fmt"
    	"log"
    	"log/slog"
    	"net/http"
    	"regexp"
    	"strings"
    	"time"
    
    	"cloud.google.com/go/compute/metadata"
    	"google.golang.org/api/compute/v1"
    	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
    )
    
    const (
    	sourceImageProject  = "confidential-space-images"
    	deprecationValueFmt = "2006-01-02"
    )
    
    // CS Images have the format confidential-space-.*.
    var prodImageNameRegex = regexp.MustCompile(`^confidential-space-.*`)
    
    // VMInfo represents the information logged about an VM instance of note.
    type VMInfo struct {
    	Name            string `json:"instance_name"`
    	Zone            string `json:"instance_zone"`
    	ImageName       string `json:"image_name"`
    	DeprecationDate string `json:"image_deprecation_date,omitempty"`
    	ObsoleteDate    string `json:"image_obsolete_date,omitempty"`
    }
    
    // VMList represents a list of notable VMs and their relevance.
    type VMList struct {
    	Description string   `json:"description"`
    	Instances   []VMInfo `json:"instances"`
    }
    
    // LogPayload is the payload for Cloud Logs written by this job.
    type LogPayload struct {
    	Message            string  `json:"message"`
    	Obsolete           *VMList `json:"vms_obsolete"`
    	ObsoleteWarning    *VMList `json:"vms_obsolete_warning"`
    	Deprecated         *VMList `json:"vms_deprecated"`
    	DeprecationWarning *VMList `json:"vms_deprecation_warning"`
    }
    
    // LogEntry represents a Cloud Logging entry.
    type LogEntry struct {
    	Message   string      `json:"message"`
    	Severity  string      `json:"severity"` // Special field for Cloud Logging.
    	Payload   *LogPayload `json:"payload,omitempty"`
    	ProjectID string      `json:"projectId"`
    }
    
    func init() {
    	log.SetFlags(0)
    	functions.HTTP("DeprecationCheck", checkForDeprecations)
    }
    
    func checkForDeprecations(w http.ResponseWriter, r *http.Request) {
    	ctx := context.Background()
    
    	// Retrieve project ID.
    	projectID, err := metadata.ProjectIDWithContext(ctx)
    	if err != nil {
    		errMsg := fmt.Sprintf("Failed to get project ID from metadata server: %v", err)
    		logError(projectID, errMsg)
    		http.Error(w, errMsg, http.StatusInternalServerError)
    		return
    	}
    	slog.Info(fmt.Sprintf("Checking for project: %s", projectID))
    
    	// Configure compute service.
    	computeService, err := compute.NewService(ctx)
    	if err != nil {
    		msg := fmt.Sprintf("Failed to create compute service: %v", err)
    		logError(projectID, msg)
    		http.Error(w, msg, http.StatusInternalServerError)
    		return
    	}
    
    	var obsoleteVMs []VMInfo           // Will contain VMs whose images are obsolete.
    	var obsoleteWarningVMs []VMInfo    // Will contain VMs using an image that will be obsolete within a month.
    	var deprecatedVMs []VMInfo         // Will contain VMs whose images are deprecated.
    	var deprecationWarningVMs []VMInfo // Will contain VMs using an image that will be deprecated within a month.
    	now := time.Now()
    
    	pageFunc := instancesPageFunc(ctx, projectID, &obsoleteVMs, &obsoleteWarningVMs, &deprecatedVMs, &deprecationWarningVMs, now, computeService)
    	if err = computeService.Instances.AggregatedList(projectID).Pages(ctx, pageFunc); err != nil {
    		msg := fmt.Sprintf("Failed to list instances: %v", err)
    		logError(projectID, msg)
    		http.Error(w, msg, http.StatusInternalServerError)
    		return
    	}
    
    	if len(obsoleteVMs) == 0 && len(obsoleteWarningVMs) == 0 && len(deprecatedVMs) == 0 && len(deprecationWarningVMs) == 0 {
    		returnmsg := "No VMs using obsolete or deprecated images found."
    		logEntry := LogEntry{
    			Message:   "Confidential Space Image Deprecation Alert - No Issues Detected",
    			Severity:  "INFO",
    			ProjectID: projectID,
    			Payload:   &LogPayload{Message: returnmsg},
    		}
    		writeLog(logEntry)
    
    		w.WriteHeader(http.StatusOK)
    		_, err := w.Write([]byte(returnmsg))
    		if err != nil {
    			http.Error(w, fmt.Sprintf("Failed to write HTTP response: %v", err), http.StatusInternalServerError)
    		}
    		return
    	}
    
    	respPayload := &LogPayload{
    		Message: "The following instances are using Confidential Space images that are obsolete, deprecated, or will be obsolete/deprecated soon. Obsolete and deprecated images are no longer maintained and scanned for vulnerabilities. Please review these instances and update their boot images as needed.",
    	}
    
    	if len(obsoleteVMs) > 0 {
    		respPayload.Obsolete = &VMList{
    			Description: "Instances using CS images that are obsolete.",
    			Instances:   obsoleteVMs,
    		}
    	}
    	if len(obsoleteWarningVMs) > 0 {
    		respPayload.ObsoleteWarning = &VMList{
    			Description: "Instances using CS images that will be obsolete within a month.",
    			Instances:   obsoleteWarningVMs,
    		}
    	}
    	if len(deprecatedVMs) > 0 {
    		respPayload.Deprecated = &VMList{
    			Description: "Instances using CS images that are deprecated.",
    			Instances:   deprecatedVMs,
    		}
    	}
    	if len(deprecationWarningVMs) > 0 {
    		respPayload.DeprecationWarning = &VMList{
    			Description: "Instances using CS images that will be deprecated within a month.",
    			Instances:   deprecationWarningVMs,
    		}
    	}
    
    	entry := LogEntry{
    		Message:   "Confidential Space Image Deprecation Alert",
    		Severity:  "WARNING",
    		ProjectID: projectID,
    		Payload:   respPayload,
    	}
    
    	writeLog(entry)
    
    	w.Header().Set("Content-Type", "application/json")
    	w.WriteHeader(http.StatusOK)
    	err = json.NewEncoder(w).Encode(respPayload)
    	if err != nil {
    		http.Error(w, fmt.Sprintf("Failed to write HTTP response: %v", err), http.StatusInternalServerError)
    		return
    	}
    }
    
    func instancesPageFunc(ctx context.Context, projectID string, obsoleteVMs *[]VMInfo, obsoleteWarningVMs *[]VMInfo, deprecatedVMs *[]VMInfo, deprecationWarningVMs *[]VMInfo, now time.Time, computeService *compute.Service) func(list *compute.InstanceAggregatedList) error {
    	return func(list *compute.InstanceAggregatedList) error {
    		for _, instanceList := range list.Items {
    			for _, instances := range instanceList.Instances {
    				sourceImage, err := getSourceImage(ctx, projectID, instances, computeService)
    				if err != nil {
    					slog.Warn(fmt.Sprintf("Failed to get source image for instance %s: %v", instances.Name, err))
    					continue
    				}
    
    				// Skip instance if not using a CS prod image.
    				imageProject, imageName := parseImage(sourceImage)
    				if imageProject != sourceImageProject || !prodImageNameRegex.MatchString(imageName) {
    					continue
    				}
    
    				// Get deprecation status ("deprecate-on" / "obsolete-on" / state settings).
    				depStatus, err := getDeprecationStatus(ctx, imageProject, imageName, computeService)
    				if err != nil {
    					slog.Error(fmt.Sprintf("Failed to get deprecation status for image %s in project %s: %v", imageName, imageProject, err))
    					continue
    				}
    				if depStatus == nil {
    					continue
    				}
    
    				vm := VMInfo{
    					Name:      instances.Name,
    					Zone:      getResourceName(instances.Zone),
    					ImageName: imageName,
    				}
    
    				var depTime, obsTime time.Time
    				if depStatus.Deprecated != "" {
    					depTime, err = parseDeprecationDate(depStatus.Deprecated)
    					if err != nil {
    						slog.Error(fmt.Sprintf("Failed to parse deprecation date %s for image %s in project %s: %v", depStatus.Deprecated, imageName, imageProject, err))
    						continue
    					}
    					vm.DeprecationDate = depTime.Format(deprecationValueFmt)
    				}
    
    				if depStatus.Obsolete != "" {
    					obsTime, err = parseDeprecationDate(depStatus.Obsolete)
    					if err != nil {
    						slog.Error(fmt.Sprintf("Failed to parse obsolete date %s for image %s in project %s: %v", depStatus.Obsolete, imageName, imageProject, err))
    						continue
    					}
    					vm.ObsoleteDate = obsTime.Format(deprecationValueFmt)
    				}
    
    				// Determine whether instance image is obsolete, upcoming obsolete, deprecated, or upcoming deprecated.
    				if depStatus.State == "OBSOLETE" || (!obsTime.IsZero() && isPast(obsTime, now)) {
    					*obsoleteVMs = append(*obsoleteVMs, vm)
    				} else if !obsTime.IsZero() && isWarning(obsTime, now) {
    					*obsoleteWarningVMs = append(*obsoleteWarningVMs, vm)
    				} else if depStatus.State == "DEPRECATED" || (!depTime.IsZero() && isPast(depTime, now)) {
    					*deprecatedVMs = append(*deprecatedVMs, vm)
    				} else if !depTime.IsZero() && isWarning(depTime, now) {
    					*deprecationWarningVMs = append(*deprecationWarningVMs, vm)
    				}
    			}
    		}
    
    		return nil
    	}
    }
    
    func getSourceImage(ctx context.Context, projectID string, instance *compute.Instance, service *compute.Service) (string, error) {
    	for _, disk := range instance.Disks {
    		// Only check boot disk.
    		if disk.Boot {
    			// Retrieve source image for disk.
    			diskName := getResourceName(disk.Source)
    			zone := getResourceName(instance.Zone)
    
    			d, err := service.Disks.Get(projectID, zone, diskName).Context(ctx).Do()
    			if err != nil {
    				slog.Warn(fmt.Sprintf("Failed to get details for disk %s in zone %s: %v", diskName, zone, err))
    				continue
    			}
    
    			if d.SourceImage != "" {
    				return d.SourceImage, nil
    			}
    		}
    	}
    
    	return "", fmt.Errorf("no suitable source image name found for instance %s", instance.Name)
    }
    
    func getDeprecationStatus(ctx context.Context, imageProject, imageName string, service *compute.Service) (*compute.DeprecationStatus, error) {
    	image, err := service.Images.Get(imageProject, imageName).Context(ctx).Do()
    	if err != nil {
    		slog.Warn(fmt.Sprintf("Failed to get details for image %s in project %s: %v", imageName, imageProject, err))
    		return nil, err
    	}
    
    	if image.Deprecated == nil || (image.Deprecated.Deprecated == "" && image.Deprecated.Obsolete == "" && image.Deprecated.State == "") {
    		// Skip if no deprecation/obsolete setting.
    		slog.Warn(fmt.Sprintf("Image %s in project %s has no deprecate-on or obsolete-on setting", imageName, imageProject))
    		return nil, nil
    	}
    
    	return image.Deprecated, nil
    }
    
    func parseDeprecationDate(dateStr string) (time.Time, error) {
    	formats := []string{
    		time.RFC3339,
    		time.RFC3339Nano,
    		"2006-01-02T15:04:05",
    		"2006-01-02",
    		"2006-01",
    	}
    	for _, format := range formats {
    		if t, err := time.Parse(format, dateStr); err == nil {
    			return t, nil
    		}
    	}
    	return time.Time{}, fmt.Errorf("unable to parse %q as a valid deprecation date", dateStr)
    }
    
    func isPast(obsTime time.Time, now time.Time) bool {
    	return !obsTime.After(now)
    }
    
    func isWarning(targetTime time.Time, now time.Time) bool {
    	warningWindowEnd := now.AddDate(0, 1, 0)
    	return targetTime.After(now) && !targetTime.After(warningWindowEnd)
    }
    
    func parseImage(image string) (string, string) {
    	parts := strings.Split(image, "/")
    
    	var project, imageName string
    	for i, part := range parts {
    		if part == "projects" && i+1 < len(parts) {
    			project = parts[i+1]
    		} else if part == "images" && i+1 < len(parts) {
    			imageName = parts[i+1]
    		}
    	}
    
    	slog.Info(fmt.Sprintf("Source image project: %s, name: %s", project, imageName))
    
    	return project, imageName
    }
    
    // getResourceName extracts the last part of a GCP resource URL.
    func getResourceName(url string) string {
    	parts := strings.Split(url, "/")
    	return parts[len(parts)-1]
    }
    
    func writeLog(entry LogEntry) {
    	jsonData, err := json.Marshal(entry)
    	if err != nil {
    		slog.Warn(fmt.Sprintf("Error marshaling final log: %v", err))
    		slog.Warn(fmt.Sprintf("Log contents: %v", entry))
    		return
    	}
    
    	fmt.Println(string(jsonData))
    }
    
    func logError(projectID, errMsg string) {
    	entry := LogEntry{
    		Message:   "Confidential Space Image Deprecation Alert - Job Failed",
    		Severity:  "ERROR",
    		ProjectID: projectID,
    		Payload: &LogPayload{
    			Message: errMsg,
    		},
    	}
    
    	writeLog(entry)
    }
    
    
  18. 使用下列程式碼覆寫 go.mod 中的內容:

    module example.com/imagechecker
    
    require (
        cloud.google.com/go/compute/metadata v0.5.0
        github.com/GoogleCloudPlatform/functions-framework-go v1.9.0
        google.golang.org/api v0.264.0
    )
    
    
  19. 點選「儲存並重新部署」。

  20. 服務部署成功後,請記下網址 (位於函式名稱附近),以供日後使用。

設定 Cloud Scheduler 工作

如要設定執行 Cloud Run 函式的 Cloud Scheduler 工作,請完成下列操作:

  1. 首先,請建立服務帳戶來執行排定的工作。請前往「建立服務帳戶」頁面:

    前往「Create a Service Account」(建立服務帳戶) 頁面

  2. 在「服務帳戶名稱」方塊中,新增服務帳戶名稱。

  3. 記下服務帳戶 ID,稍後會用到。

  4. 點按「Create and continue」(建立並繼續)。

  5. 新增「Cloud Run 叫用者」角色。

  6. 按一下「完成」。

  7. 如果您尚未啟用 Cloud Scheduler API,請先啟用。

  8. 在 Cloud Scheduler 中,前往「Jobs」(工作) 頁面。

    前往「Jobs」(工作) 頁面

  9. 按一下「Create job」(建立工作)。

  10. 在「Name」(名稱) 欄位中輸入工作名稱。

  11. 選取要執行排定工作的區域。

  12. 在「Frequency」(頻率) 欄位中新增 Cron 排程。例如,0 9 1 * *會在每個月第一天的上午 9 點執行工作。

  13. 設定時間表的時區。

  14. 按一下「繼續」。

  15. 將「Target type」(目標類型) 設為「HTTP」。

  16. 將「URL」(網址) 設為您建立的 Cloud Run 函式網址。

  17. 在「Auth header」(驗證標頭) 方塊中,選取「Add OIDC token」(新增 OIDC 權杖)。

  18. 按一下「Service account」(服務帳戶) 方塊,然後輸入您先前建立的服務帳戶 ID。

  19. 將目標對象設為您建立的 Cloud Run 函式網址

  20. 點選「建立」。

  21. 如要測試工作,請依序按一下「 Actions」和「Force run」(強制執行)。

設定記錄式警告

如要設定已淘汰映像檔的記錄檔快訊,請完成下列操作:

  1. 前往「Logs Explorer」頁面:

    前往「Logs Explorer」頁面

  2. 啟用「顯示查詢」。

  3. 在查詢方塊中,輸入下列其中一個 Logging 查詢語言查詢:

    • 找到的 VM 即將淘汰:

      resource.type="cloud_run_revision"
      severity="WARNING"
      jsonPayload.message="Confidential Space Image Deprecation Alert"
      
    • 淘汰檢查失敗:

      resource.type="cloud_run_revision"
      severity="ERROR"
      jsonPayload.message="Confidential Space Image Deprecation Alert - Job Failed"
      
    • 在 VM 機群中未偵測到已淘汰的映像檔:

      resource.type="cloud_run_revision"
      severity="INFO"
      jsonPayload.message="Confidential Space Image Deprecation Alert - No Issues Detected"
      
  4. 點選「執行查詢」。

  5. 按一下搜尋結果附近的「動作」。

  6. 按一下「Create log alert」(建立記錄警告),然後完成程序,根據輸入的查詢建立警告政策。

更新 Confidential Space 映像檔

為確保 Confidential Space 映像檔保持最新狀態,並避免服務中斷,建議您每次啟動 Confidential Space 工作負載時,都建立新的 Confidential VM 執行個體。每次根據 Confidential Space 映像檔建立新執行個體時,系統都會佈建最新的 Confidential Space 映像檔。

如果無法為每個 Confidential Space 工作負載執行作業建立新的 Confidential VM 執行個體,建議您定義維護期間,根據依賴方在認證政策中設定的支援屬性,重新建立 Confidential VM 執行個體:

  • 如果您設定的認證政策需要 LATEST 支援屬性,則必須至少每月更新一次 Confidential Space 映像檔。

  • 如果您設定的認證政策需要 STABLE 支援屬性,則必須至少每六個月更新一次 Confidential Space 映像檔。

您可以使用下列一或多項功能,輕鬆重新建立 VM:

如要瞭解如何設定這些功能,請參閱「使用自動調度資源、自動修復和映像檔更新功能,透過 MIG 部署 Confidential Space 工作負載」。

長時間執行的工作負載

在長時間執行的工作負載期間,Confidential Space 映像檔可能會遭到淘汰或撤銷。如果您設定的認證政策要求 STABLE Confidential Space 支援屬性,而工作負載使用的 Confidential Space 映像檔已淘汰或撤銷,則執行工作負載的 Confidential VM 執行個體將無法通過認證。為避免中斷,設計長時間執行的工作負載時,應考量恢復機制,並安全管理狀態。