Bigtable 및 Dataflow: Database Monitoring Art (HBase Java 클라이언트)

1. 소개

이 Codelab에서는 Cloud Bigtable의 모니터링 도구를 사용하여 Cloud DataflowJava HBase 클라이언트로 데이터를 쓰고 읽어 다양한 예술 작품을 만듭니다.

학습 목표

  • Cloud Dataflow를 사용하여 대량의 데이터를 Bigtable에 로드하기
  • 데이터가 수집될 때 Bigtable 인스턴스 및 테이블 모니터링
  • Dataflow 작업을 사용하여 Bigtable 쿼리
  • 스키마 설계로 인해 핫스팟을 찾는 데 사용할 수 있는 주요 시각화 도구 도구를 살펴보세요.
  • Key Visualizer를 사용하여 아트 만들기

d098cc81f78f02eb.png

귀하의 Cloud Bigtable 사용 경험을 평가해 주세요.

초급 중급 고급

본 가이드를 어떻게 사용하실 계획인가요?

<ph type="x-smartling-placeholder"></ph> 읽기 전용 읽고 연습 활동을 완료하세요

2. Bigtable 데이터베이스 만들기

Cloud Bigtable은 Google의 NoSQL 빅데이터 데이터베이스 서비스입니다. Google 검색, 애널리틱스, 지도, Gmail 등 Google의 수많은 핵심 서비스를 운영하는 바로 그 데이터베이스입니다. 대규모 분석 워크로드를 실행하고 지연 시간이 짧은 애플리케이션을 빌드하는 데 이상적입니다. Cloud Bigtable 소개 Codelab에서 자세한 소개를 확인하세요.

프로젝트 만들기

먼저 새 프로젝트를 만듭니다. 'Cloud Shell 활성화'를 클릭하여 열 수 있는 기본 제공 Cloud Shell 사용 버튼을 클릭합니다.

a74d156ca7862b28.png

Codelab 명령어를 더 쉽게 복사하고 붙여넣을 수 있도록 다음 환경 변수를 설정합니다.

BIGTABLE_PROJECT=$GOOGLE_CLOUD_PROJECT
INSTANCE_ID="keyviz-art-instance"
CLUSTER_ID="keyviz-art-cluster"
TABLE_ID="art"
CLUSTER_NUM_NODES=1
CLUSTER_ZONE="us-central1-c" # You can choose a zone closer to you

Cloud Shell에는 이 Codelab에서 사용할 도구, gcloud 명령줄 도구, cbt 명령줄 인터페이스, Maven이 이미 설치되어 있습니다.

다음 명령어를 실행하여 Cloud Bigtable API를 사용 설정합니다.

gcloud services enable bigtable.googleapis.com bigtableadmin.googleapis.com

다음 명령어를 실행하여 인스턴스를 만듭니다.

gcloud bigtable instances create $INSTANCE_ID \
    --cluster=$CLUSTER_ID \
    --cluster-zone=$CLUSTER_ZONE \
    --cluster-num-nodes=$CLUSTER_NUM_NODES \
    --display-name=$INSTANCE_ID

인스턴스를 만든 후 cbt 구성 파일을 채운 후 다음 명령어를 실행하여 테이블과 column family를 만듭니다.

echo project = $GOOGLE_CLOUD_PROJECT > ~/.cbtrc
echo instance = $INSTANCE_ID >> ~/.cbtrc

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

3. 알아보기: Dataflow로 Bigtable에 쓰기

작성 기본사항

Cloud Bigtable에 쓸 때는 CloudBigtableTableConfiguration 구성 객체를 제공해야 합니다. 이 객체는 테이블의 프로젝트 ID와 인스턴스 ID뿐만 아니라 테이블 자체의 이름을 지정합니다.

CloudBigtableTableConfiguration bigtableTableConfig =
    new CloudBigtableTableConfiguration.Builder()
        .withProjectId(PROJECT_ID)
        .withInstanceId(INSTANCE_ID)
        .withTableId(TABLE_ID)
        .build();

그러면 파이프라인은 Put 및 Delete를 포함할 수 있는 HBase Mutation 객체를 전달할 수 있습니다.

p.apply(Create.of("hello", "world"))
    .apply(
        ParDo.of(
            new DoFn<String, Mutation>() {
              @ProcessElement
              public void processElement(@Element String rowkey, OutputReceiver<Mutation> out) {
                long timestamp = System.currentTimeMillis();
                Put row = new Put(Bytes.toBytes(rowkey));

                row.addColumn(...);
                out.output(row);
              }
            }))
    .apply(CloudBigtableIO.writeToTable(bigtableTableConfig));

LoadData Dataflow 작업

다음 페이지에서는 LoadData 작업을 실행하는 방법을 보여주지만 여기서는 파이프라인의 중요한 부분을 살펴보겠습니다.

데이터를 생성하려면 GenerateSequence 클래스 (for 루프와 유사)를 사용하여 몇 MB의 무작위 데이터로 여러 행을 쓰는 파이프라인을 만듭니다. rowkey가 패딩되고 역전된 시퀀스 번호이므로 2500000000052가 됩니다.

LoadData.java

String numberFormat = "%0" + maxLength + "d";

p.apply(GenerateSequence.from(0).to(max))
    .apply(
        ParDo.of(
            new DoFn<Long, Mutation>() {
              @ProcessElement
              public void processElement(@Element Long rowkey, OutputReceiver<Mutation> out) {
                String paddedRowkey = String.format(numberFormat, rowkey);

                // Reverse the rowkey for more efficient writing
                String reversedRowkey = new StringBuilder(paddedRowkey).reverse().toString();
                Put row = new Put(Bytes.toBytes(reversedRowkey));

                // Generate random bytes
                byte[] b = new byte[(int) rowSize];
                new Random().nextBytes(b);

                long timestamp = System.currentTimeMillis();
                row.addColumn(Bytes.toBytes(COLUMN_FAMILY), Bytes.toBytes("C"), timestamp, b);
                out.output(row);
              }
            }))
    .apply(CloudBigtableIO.writeToTable(bigtableTableConfig));

4. Bigtable로 데이터 생성 및 유입 모니터링

다음 명령어는 Key Visualizer가 활성화하기에 충분한 용량인 40GB의 데이터를 테이블에 생성하는 Dataflow 작업을 실행합니다.

Cloud Dataflow API 사용 설정

gcloud services enable dataflow.googleapis.com

github에서 코드를 가져와 디렉터리로 변경합니다.

git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
cd java-docs-samples/bigtable/beam/keyviz-art

데이터 생성 (스크립트에 15분 정도 소요)

mvn compile exec:java -Dexec.mainClass=keyviz.LoadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"

가져오기 모니터링

Cloud Dataflow UI에서 작업을 모니터링할 수 있습니다. 또한 모니터링 UI를 통해 Cloud Bigtable 인스턴스의 부하를 확인할 수 있습니다.

Dataflow UI에서는 처리된 요소, 현재 vCPU, 처리량 등 다양한 작업 측정항목과 작업 그래프를 확인할 수 있습니다.

9cecc290f5acea15.png

abb0561342dc6b60.png

Bigtable에는 인스턴스, 클러스터, 테이블 수준에서 읽기/쓰기 작업, 사용된 스토리지, 오류율 등에 대한 표준 모니터링 도구가 있습니다. 그 외에도 Bigtable에는 최소 30GB의 데이터가 생성된 후 사용할 row key를 기준으로 사용량을 세분화하는 Key Visualizer가 있습니다.

996f8589332dfc19.png

5. 알아보기: Dataflow를 사용하여 Bigtable에서 읽기

읽기 기본사항

Cloud Bigtable에서 읽을 때는 CloudBigtableTableScanConfiguration 구성 객체를 제공해야 합니다. CloudBigtableTableConfiguration와 유사하지만 스캔하고 읽을 행을 지정할 수 있습니다.

Scan scan = new Scan();
scan.setCacheBlocks(false);
scan.setFilter(new FirstKeyOnlyFilter());

CloudBigtableScanConfiguration config =
    new CloudBigtableScanConfiguration.Builder()
        .withProjectId(options.getBigtableProjectId())
        .withInstanceId(options.getBigtableInstanceId())
        .withTableId(options.getBigtableTableId())
        .withScan(scan)
        .build();

그런 다음 이를 사용하여 파이프라인을 시작합니다.

p.apply(Read.from(CloudBigtableIO.read(config)))
    .apply(...

그러나 파이프라인의 일부로 읽기를 수행하려면 AbstractCloudBigtableTableDoFn를 확장하는 doFnCloudBigtableTableConfiguration를 전달하면 됩니다.

p.apply(GenerateSequence.from(0).to(10))
    .apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));

그런 다음 구성 및 getConnection()super()를 호출하여 분산 연결을 가져옵니다.

public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {
    public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
      super(config);
    }

    @ProcessElement
    public void processElement(PipelineOptions po) {
        Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
        ResultScanner imageData = table.getScanner(scan);
    }   
}

ReadData Dataflow 작업

이 Codelab의 경우 매초 테이블에서 읽어야 하므로 입력된 CSV 파일의 시간에 따라 여러 읽기 범위를 트리거하는 생성된 시퀀스로 파이프라인을 시작할 수 있습니다.

시간이 주어진 경우 스캔할 행 범위를 결정하는 수학은 약간 있지만, 더 자세히 알아보려면 파일 이름을 클릭하여 소스 코드를 볼 수 있습니다.

ReadData.java

p.apply(GenerateSequence.from(0).withRate(1, new Duration(1000)))
    .apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));

ReadData.java

  public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {

    List<List<Float>> imageData = new ArrayList<>();
    String[] keys;

    public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
      super(config);
      keys = new String[Math.toIntExact(getNumRows(readDataOptions))];
      downloadImageData(readDataOptions.getFilePath());
      generateRowkeys(getNumRows(readDataOptions));
    }

    @ProcessElement
    public void processElement(PipelineOptions po) {
      // Determine which column will be drawn based on runtime of job.
      long timestampDiff = System.currentTimeMillis() - START_TIME;
      long minutes = (timestampDiff / 1000) / 60;
      int timeOffsetIndex = Math.toIntExact(minutes / KEY_VIZ_WINDOW_MINUTES);

      ReadDataOptions options = po.as(ReadDataOptions.class);
      long count = 0;

      List<RowRange> ranges = getRangesForTimeIndex(timeOffsetIndex, getNumRows(options));
      if (ranges.size() == 0) {
        return;
      }

      try {
        // Scan with a filter that will only return the first key from each row. This filter is used
        // to more efficiently perform row count operations.
        Filter rangeFilters = new MultiRowRangeFilter(ranges);
        FilterList firstKeyFilterWithRanges = new FilterList(
            rangeFilters,
            new FirstKeyOnlyFilter(),
            new KeyOnlyFilter());
        Scan scan =
            new Scan()
                .addFamily(Bytes.toBytes(COLUMN_FAMILY))
                .setFilter(firstKeyFilterWithRanges);

        Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
        ResultScanner imageData = table.getScanner(scan);
      } catch (Exception e) {
        System.out.println("Error reading.");
        e.printStackTrace();
      }
    }

    /**
     * Download the image data as a grid of weights and store them in a 2D array.
     */
    private void downloadImageData(String artUrl) {
    ...
    }

    /**
     * Generates an array with the rowkeys that were loaded into the specified Bigtable. This is
     * used to create the correct intervals for scanning equal sections of rowkeys. Since Bigtable
     * sorts keys lexicographically if we just used standard intervals, each section would have
     * different sizes.
     */
    private void generateRowkeys(long maxInput) {
    ...
    }

    /**
     * Get the ranges to scan for the given time index.
     */
    private List<RowRange> getRangesForTimeIndex(@Element Integer timeOffsetIndex, long maxInput) {
    ...
    }
  }

6. 나만의 걸작 만들기

ad9c4c0b90626a3b.png

이제 Bigtable에 데이터를 로드하고 Dataflow를 사용하여 읽는 방법을 이해했으므로 8시간 동안 모나리자 이미지를 생성하는 최종 명령어를 실행할 수 있습니다.

mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"

기존 이미지가 있는 버킷을 사용할 수 있습니다. 또는 이 도구를 사용하여 자체 이미지에서 입력 파일을 만든 다음 공개 GCS 버킷에 업로드할 수 있습니다.

파일 이름은 gs://keyviz-art/[painting]_[hours]h.txt(예: gs://keyviz-art/american_gothic_4h.txt)로 만들어집니다.

페인트칠 옵션:

  • american_gothic
  • mona_lisa
  • pearl_earring
  • persistence_of_memory
  • starry_night
  • sunday_afternoon
  • the_scream

시간 옵션: 1, 4, 8, 12, 24, 48, 72, 96, 120, 144

allUsersStorage Object Viewer 역할을 부여하여 GCS 버킷 또는 파일을 공개로 설정합니다.

ee089815364150d2.png

이미지를 선택한 후에는 이 명령어에서 --file-path 매개변수를 변경하기만 하면 됩니다.

mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT \
--filePath=gs://keyviz-art/american_gothic_4h.txt"

7. 나중에 확인하세요.

전체 이미지가 나타나려면 몇 시간이 걸릴 수 있지만 30분이 지나면 Key Visualizer에 활동이 표시되기 시작합니다. 확대/축소, 밝기, 측정항목과 같은 여러 매개변수를 사용할 수 있습니다. 마우스의 스크롤 휠을 사용하거나 Key Visualizer 그리드에서 직사각형을 드래그하여 확대/축소할 수 있습니다.

밝기는 이미지의 배율을 변경하므로 매우 뜨거운 영역을 자세히 살펴볼 때 유용합니다.

8e847f03df25572b.png

표시할 측정항목도 조정할 수 있습니다. OP, 바이트 클라이언트 읽기, 바이트 클라이언트 쓰기 등이 있습니다. "클라이언트 바이트 읽기" 매끄러운 이미지를 생성하는 것으로 보이는 선이 더 많은 이미지를 생성합니다. 일부 이미지에서는 정말 멋지게 보일 수 있습니다.

33eb5dcf4e4be861.png

8. 완료

청구되지 않도록 정리하기

이 Codelab에서 사용한 리소스 비용이 Google Cloud Platform 계정에 청구되지 않도록 하려면 인스턴스를 삭제해야 합니다.

gcloud bigtable instances delete $INSTANCE_ID

학습한 내용

  • Dataflow로 Bigtable에 쓰기
  • Dataflow를 사용하여 Bigtable에서 읽기 (파이프라인 시작 시, 파이프라인 중간)
  • Dataflow 모니터링 도구 사용
  • Key Visualizer를 포함한 Bigtable 모니터링 도구 사용

다음 단계