Pub/Sub 알림

이 문서에서는 메모 및 어커런스 업데이트 알림을 설정하는 방법을 설명합니다.

Container Registry는 취약점 및 기타 메타데이터를 검사할 때마다 Pub/Sub를 통해 알림을 제공합니다. 메모 또는 어커런스가 생성되거나 업데이트되면 각 API 버전에 해당하는 주제에 메시지가 게시됩니다. 현재 API 버전과 관련된 주제를 사용하세요.

시작하기 전에

  1. Container Analysis API 사용 설정

  2. 컨테이너 분석 개요 읽기

Pub/Sub 주제 만들기

Container Analysis API를 활성화하면 다음 Pub/Sub 주제가 프로젝트에 자동으로 생성됩니다.

  • container-analysis-notes-v1 + container-analysis-occurrences-v1

주제가 실수로 삭제되거나 누락된 경우 직접 추가할 수 있습니다.

Console

  1. Cloud Console에서 Pub/Sub 주제 페이지로 이동합니다.

    Pub/Sub 주제 페이지 열기

  2. 주제 만들기를 클릭합니다.

  3. URI와 함께 메모의 주제를 입력합니다.

    projects/[PROJECT-ID]/topics/container-analysis-notes-v1
    

    여기서 [PROJECT-ID]는 Google Cloud 프로젝트 ID입니다.

  4. 만들기를 클릭합니다.

  5. URI와 함께 또 다른 어커런스 주제를 만듭니다.

     projects/[PROJECT-ID]/topics/container-analysis-occurrences-v1
    

gcloud 명령어

셸 또는 터미널 창에서 다음 명령어를 실행합니다.

gcloud pubsub topics create projects/[PROJECT-ID]/topics/container-analysis-notes-v1
gcloud pubsub topics create projects/[PROJECT-ID]/topics/container-analysis-occurrences-v1

gcloud pubsub topics 명령어에 대한 자세한 내용은 topics 문서를 참조하세요.

메모나 어커런스가 작성되거나 업데이트될 때마다 해당 주제에 메시지가 게시됩니다.

Pub/Sub 페이로드는 JSON 형식이며 스키마는 다음과 같습니다.

메모:

{
    "name": "projects/[PROJECT_ID]/notes/[NOTE_ID]",
    "kind": "[NOTE_KIND]",
    "notificationTime": "[NOTIFICATION_TIME]",
}

어커런스:

{
    "name": "projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]",
    "kind": "[NOTE_KIND]",
    "notificationTime": "[NOTIFICATION_TIME]",
}

각 항목의 의미는 다음과 같습니다.

  • [NOTE_KIND]NoteKind의 값 중 하나입니다.
  • [NOTIFICATION_TIME]는 RFC 3339 UTC 'Zulu'형식의 타임 스탬프로, 나노초 단위까지 정확합니다.

Pub/Sub 구독 만들기

이벤트를 수신하려면 주제와 연결된 Pub/Sub 구독을 만듭니다.

Console

  1. Cloud Console의 Pub/Sub 구독 페이지로 이동합니다.

    Pub/Sub 구독 페이지 열기

  2. 구독 만들기를 클릭합니다.

  3. 구독 이름을 입력합니다. 예를 들어 메모가 있습니다.

  4. 메모의 주제 URI를 입력합니다.

    projects/[PROJECT-ID]/topics/container-analysis-notes-v1
    

    여기서 [PROJECT-ID]는 Google Cloud 프로젝트 ID입니다.

  5. 만들기를 클릭합니다.

  6. URI와 함께 또 다른 어커런스 구독을 만듭니다.

     projects/[PROJECT-ID]/topics/container-analysis-occurrences-v1
    

gcloud 명령어

Pub/Sub 이벤트를 수신하려면 먼저 container-analysis-occurrences-v1 주제와 관련된 구독을 만들어야 합니다.

gcloud pubsub subscriptions create \
    --topic container-analysis-occurrences-v1 occurrences

이후에는 새 구독을 이용해 어커런스에 대한 메시지를 가져올 수 있습니다.

gcloud pubsub subscriptions pull \
    --auto-ack occurrences

자바

Container Registry용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Container Registry 클라이언트 라이브러리에서 확인할 수 있습니다. 자세한 내용은 Container Registry 자바 API 참조 문서를 확인하세요.

import com.google.cloud.pubsub.v1.AckReplyConsumer;
import com.google.cloud.pubsub.v1.MessageReceiver;
import com.google.cloud.pubsub.v1.Subscriber;
import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.pubsub.v1.ProjectSubscriptionName;
import com.google.pubsub.v1.ProjectTopicName;
import com.google.pubsub.v1.PubsubMessage;
import com.google.pubsub.v1.PushConfig;
import com.google.pubsub.v1.Subscription;
import io.grpc.StatusRuntimeException;
import java.io.IOException;
import java.lang.InterruptedException;
import java.util.concurrent.TimeUnit;

public class Subscriptions {
  // Handle incoming Occurrences using a Cloud Pub/Sub subscription
  public static int pubSub(String subId, long timeoutSeconds, String projectId)
      throws InterruptedException {
    // String subId = "my-occurrence-subscription";
    // long timeoutSeconds = 20;
    // String projectId = "my-project-id";
    Subscriber subscriber = null;
    MessageReceiverExample receiver = new MessageReceiverExample();

    try {
      // Subscribe to the requested Pub/Sub channel
      ProjectSubscriptionName subName = ProjectSubscriptionName.of(projectId, subId);
      subscriber = Subscriber.newBuilder(subName, receiver).build();
      subscriber.startAsync().awaitRunning();
      // Sleep to listen for messages
      TimeUnit.SECONDS.sleep(timeoutSeconds);
    } finally {
      // Stop listening to the channel
      if (subscriber != null) {
        subscriber.stopAsync();
      }
    }
    // Print and return the number of Pub/Sub messages received
    System.out.println(receiver.messageCount);
    return receiver.messageCount;
  }

  // Custom class to handle incoming Pub/Sub messages
  // In this case, the class will simply log and count each message as it comes in
  static class MessageReceiverExample implements MessageReceiver {
    public int messageCount = 0;

    @Override
    public synchronized void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) {
      // Every time a Pub/Sub message comes in, print it and count it
      System.out.println("Message " + messageCount + ": " + message.getData().toStringUtf8());
      messageCount += 1;
      // Acknowledge the message
      consumer.ack();
    }
  }

  // Creates and returns a Pub/Sub subscription object listening to the Occurrence topic
  public static Subscription createOccurrenceSubscription(String subId, String projectId)
      throws IOException, StatusRuntimeException, InterruptedException {
    // This topic id will automatically receive messages when Occurrences are added or modified
    String topicId = "container-analysis-occurrences-v1";
    ProjectTopicName topicName = ProjectTopicName.of(projectId, topicId);
    ProjectSubscriptionName subName = ProjectSubscriptionName.of(projectId, subId);

    SubscriptionAdminClient client = SubscriptionAdminClient.create();
    PushConfig config = PushConfig.getDefaultInstance();
    Subscription sub = client.createSubscription(subName, topicName, config, 0);
    return sub;
  }
}

Go

Container Registry용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Container Registry 클라이언트 라이브러리에서 확인할 수 있습니다. 자세한 내용은 Container Registry Go API 참조 문서를 확인하세요.


import (
	"context"
	"fmt"
	"io"
	"sync"
	"time"

	pubsub "cloud.google.com/go/pubsub"
)

// occurrencePubsub handles incoming Occurrences using a Cloud Pub/Sub subscription.
func occurrencePubsub(w io.Writer, subscriptionID string, timeout time.Duration, projectID string) (int, error) {
	// subscriptionID := fmt.Sprintf("my-occurrences-subscription")
	// timeout := time.Duration(20) * time.Second
	ctx := context.Background()

	var mu sync.Mutex
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return -1, fmt.Errorf("pubsub.NewClient: %v", err)
	}
	// Subscribe to the requested Pub/Sub channel.
	sub := client.Subscription(subscriptionID)
	count := 0

	// Listen to messages for 'timeout' seconds.
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	err = sub.Receive(ctx, func(ctx context.Context, msg *pubsub.Message) {
		mu.Lock()
		count = count + 1
		fmt.Fprintf(w, "Message %d: %q\n", count, string(msg.Data))
		msg.Ack()
		mu.Unlock()
	})
	if err != nil {
		return -1, fmt.Errorf("sub.Receive: %v", err)
	}
	// Print and return the number of Pub/Sub messages received.
	fmt.Fprintln(w, count)
	return count, nil
}

// createOccurrenceSubscription creates a new Pub/Sub subscription object listening to the Occurrence topic.
func createOccurrenceSubscription(subscriptionID, projectID string) error {
	// subscriptionID := fmt.Sprintf("my-occurrences-subscription")
	ctx := context.Background()
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("pubsub.NewClient: %v", err)
	}
	defer client.Close()

	// This topic id will automatically receive messages when Occurrences are added or modified
	topicID := "container-analysis-occurrences-v1"
	topic := client.Topic(topicID)
	config := pubsub.SubscriptionConfig{Topic: topic}
	_, err = client.CreateSubscription(ctx, subscriptionID, config)
	return fmt.Errorf("client.CreateSubscription: %v", err)
}

Node.js

Container Registry용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Container Registry 클라이언트 라이브러리에서 확인할 수 있습니다. 자세한 내용은 Container Registry Node.js API 참조 문서를 확인하세요.

/**
 * TODO(developer): Uncomment these variables before running the sample
 */
// const projectId = 'your-project-id', // Your GCP Project ID
// const subscriptionId = 'my-sub-id', // A user-specified subscription to the 'container-analysis-occurrences-v1' topic
// const timeoutSeconds = 30 // The number of seconds to listen for the new Pub/Sub Messages

// Import the pubsub library and create a client, topic and subscription
const {PubSub} = require('@google-cloud/pubsub');
const pubsub = new PubSub({projectId});
const subscription = pubsub.subscription(subscriptionId);

// Handle incoming Occurrences using a Cloud Pub/Sub subscription
let count = 0;
const messageHandler = message => {
  count++;
  message.ack();
};

// Listen for new messages until timeout is hit
subscription.on('message', messageHandler);

setTimeout(() => {
  subscription.removeListener('message', messageHandler);
  console.log(`Polled ${count} occurrences`);
}, timeoutSeconds * 1000);

Ruby

Container Registry용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Container Registry 클라이언트 라이브러리에서 확인할 수 있습니다. 자세한 내용은 Container Registry Ruby API 참조 문서를 확인하세요.

# subscription_id = "A user-specified identifier for the new subscription"
# timeout_seconds = "The number of seconds to listen for new Pub/Sub
#                    messages"
# project_id      = "Your Google Cloud project ID"

require "google/cloud/pubsub"

pubsub = Google::Cloud::Pubsub.new project: project_id
topic = pubsub.topic "container-analysis-occurrences-v1"
subscription = topic.subscribe subscription_id

count = 0
subscriber = subscription.listen do |received_message|
  count += 1
  # Process incoming occurrence here
  puts "Message #{count}: #{received_message.data}"
  received_message.acknowledge!
end
subscriber.start
# Wait for incomming occurrences
sleep timeout_seconds
subscriber.stop.wait!
subscription.delete
# Print and return the total number of Pub/Sub messages received
puts "Total Messages Received: #{count}"
count

Python

Container Registry용 클라이언트 라이브러리를 설치하고 사용하는 방법은 Container Registry 클라이언트 라이브러리에서 확인할 수 있습니다. 자세한 내용은 Container Registry Python API 참조 문서를 확인하세요.

def pubsub(subscription_id, timeout_seconds, project_id):
    """Respond to incoming occurrences using a Cloud Pub/Sub subscription."""
    # subscription_id := 'my-occurrences-subscription'
    # timeout_seconds = 20
    # project_id = 'my-gcp-project'

    import time
    from google.cloud.pubsub import SubscriberClient

    client = SubscriberClient()
    subscription_name = client.subscription_path(project_id, subscription_id)
    receiver = MessageReceiver()
    client.subscribe(subscription_name, receiver.pubsub_callback)

    # listen for 'timeout' seconds
    for _ in range(timeout_seconds):
        time.sleep(1)
    # print and return the number of pubsub messages received
    print(receiver.msg_count)
    return receiver.msg_count

class MessageReceiver:
    """Custom class to handle incoming Pub/Sub messages."""
    def __init__(self):
        # initialize counter to 0 on initialization
        self.msg_count = 0

    def pubsub_callback(self, message):
        # every time a pubsub message comes in, print it and count it
        self.msg_count += 1
        print('Message {}: {}'.format(self.msg_count, message.data))
        message.ack()

def create_occurrence_subscription(subscription_id, project_id):
    """Creates a new Pub/Sub subscription object listening to the
    Container Analysis Occurrences topic."""
    # subscription_id := 'my-occurrences-subscription'
    # project_id = 'my-gcp-project'

    from google.api_core.exceptions import AlreadyExists
    from google.cloud.pubsub import SubscriberClient

    topic_id = 'container-analysis-occurrences-v1'
    client = SubscriberClient()
    topic_name = client.topic_path(project_id, topic_id)
    subscription_name = client.subscription_path(project_id, subscription_id)
    success = True
    try:
        client.create_subscription(subscription_name, topic_name)
    except AlreadyExists:
        # if subscription already exists, do nothing
        pass
    else:
        success = False
    return success

구독자 애플리케이션은 구독이 생성된 후에만 주제에 게시된 메시지를 수신합니다.

다음 단계

  • 컨테이너 분석을 이용해 고객의 메타데이터를 저장하고 관리하는 방법에 대한 안내는 이미지의 메타데이터 제공을 참조하세요.

  • Binary Authorization을 취약점 스캔과 통합하면 알려진 보안 문제가 있는 이미지가 배포 환경에서 실행되는 일을 막을 수 있습니다. 이 작업을 수행하는 방법에 대한 안내는 취약점 스캔 통합을 참조하세요.