/// BANGBOO BLOG ///

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

May 29, 2023

GCP hands-off 2
■プロジェクト削除時のサービスアカウント
プロジェクトは30日保留される。その間サービスアカウント権限は生きており他プロジェクトでは動作する。
しかし保留期間はプロジェクトを使用できずサービスアカウントを削除できず、個別に一つ一つ権限をはく奪するしかない。
サービスアカウントはプロジェクト削除前に、必要であれば事前に削除や無効化しておくことも検討する。

■連携
GoogleWorkspace -> GAS -> GCP Oauth + API -> GCP(Bigquery etc.)
Python -> Gcloud sdk -> gcloud auth -> GCP(Bigquery etc.) <-> federation query/Connected sheet
Python(Oauth key) -> GCP認証情報(Oauth Key) + API -> GoogleWorkspace
 GCPのクレデンシャルページでOauth2.0 client IDと鍵が発行でき、鍵でイケる
  Pythonコードで鍵を指定すると実行時にログインを求められ、client IDとユーザIDを紐づけして実行することになる
  Authentication — gspread 5.7.2 documentation
  Python でシンプルに OAuth 2 する (urllib + oauthlib) - Qiita
 GCPのクレデンシャルページでAPIキーも発行でき、これは可能性はある
Python -> local csv/tsvが基本
●Python(SA key) -> GCP認証情報(SA Key) + API -> GoogleWorkspace
 サービスアカウントでGWSにアクセスできないのでダメ
  信頼しているドメインとのみ外部共有を許可する - Google Workspace 管理者 ヘルプ
  サービス アカウント(ドメイン名の末尾が「gserviceaccount.com」)を信頼しているドメインにすることはできません
   OUで許可するとイケるはずだが、、
●Python でGoogle docをイジる
下記のURLの内容を検証すればよい
Google Cloudコンソールで
Oauth同意画面を設定
Google Docs APIを有効化
OAuth クライアントIDを作成
シークレットJson ファイルができるのでDL(リネーム)
コードにクレデンシャルJSONファイルとDocのURLに含まれるdocumentIDを記述
→Python実行するとDocのデータが取れる(ローカルの場合は楽)

/// runのデプロイ時に設定を入れる方法について
1)環境変数を(コンソール/cmd/コンテナymiのどれかで)設定:
env=os environ.get("ENV") で使う。ログに出やすく非推奨
2)シークレットマネージャ保存分を設定
環境変数+コードでやるのと同じ?
3)ボリュームを使う:
クレデンを入れ、トークンの一時保存ができる?
Cloud RunでSecret Managerを使いたい #Python - Qiita
※サービスアカウントでGWSを扱うにはGWSのOUで受け入れる設定が必要な場合がある

■secret managerに保存してコードで呼び出して使う 
Secret Managerのシークレットアクセサー権限 
 (checksumをかけている)

from google.cloud import secretmanager
import google.cloud.logging
import logging
def get_url(secret_key, project_num)
logging.warning('####### secret_key' + str(secret_key) + '######')
client = secretmanager.SecretManagerServiceClient()
resource_name = "projects/()/secrets/()/versions/latest.format(project_num, secret_key)
res = client.access_secret_version(resource_name)
slack_url = res.payload.data.decode("utf-8")
return slack_url

■API等でデータを取った時中身が分からない場合
pramsが何かわからん時
print(params)
print(type(params))
#<class 'proto.marshal.collections.maps.MapComposite'>
#よくわからんクラスでもdirで保持するAttributeが分かる
attributes = dir(params)
print(attributes)
#そこに含まれるメソッドも確認できるのでhelpする
help(params.get)
 #prams.get('query')すると含まれるSQLが分かりこれで進める等

■Protocol buffers
APIの返りはGoogleは自社で開発したProtocol buffersを使っていようだ
たとえば下記が返る
name: "projects/98765"
parent: "folders/12345"
project_id: "aaaaaa-bbbb-market"
state: "ACTIVE"
display_name: "aaaaaa-bbbb-market"
create_time{
seconds: 1601250933
nanos: 820000000
}
update_time{
seconds: 1632826231
nanos: 634000000
}
etag: "W/a06910d9093db111"
labels{
key: "budget_group"
value: "cccc"
}

これは
print (type(response))すると下記であり
<class "google.cloud. resourcemanager v3.types.projects.Project"> 
print (response.project_id) で簡単にデコードし値取得できることが分かる

APIからの値を取るときのコード
from google.cloud import resourcemanager_v3
client = resourcemanager_v3.ProjectsClient()
request resourcemanager v3.ListProjectsRequest{
#組織の場合、現状は権限がGOP側で用意がなく無理だった
#parent organizations/12345678.
parent="folders/1122233344"
}
page_result = client.list_projects(request=request)
for response in page result:
print(type(response))
print (response.project_id)

エンコードする場合 https://blog.imind.jp/entry/2019/12/28/124728
pip install googleapis-common-protos でインスコ?
sudo apt install protobuf-compiler でインスコ
sudo apt-get install protobuf-compiler でインスコ
 ※google提供のフォルダごと使用しようとして失敗した方法 
 ※にprotoファイルがあるが丸々必要なので下記でDL
 ※git clone https://github.com/googleapis/googleapis.git
 ※バスを合わせてprojects.protoを使うが失敗
 ※たとえば protoc python_out=. --proto_path=googleapis ./googleapis/google/cloud/resourcemanager/v3/projects.proto
projects.proto を下記の内容で一から作成することが必要だった
syntax proto3;

message Resource{
string name = 1;
string parent = 2; 
string project_id = 3;
string state = 4;
string display_name = 5; 
map<string, string> create_time = 6;
map<string, string> update_time = 7;
string etag = 8;
map<string, string> labels = 9;
}
そして下記を実行しコンパイル
protoc --python_out=. ./projects.proto
projects_pb2.pyが生成されるため、パッケージとして読みこみprotocol buffersを実行できるようになる
import projects_pb2.py
※なおエラーで pip install U protobuf=3.20.0でダウングレードした

注意点としては、pythonとprotocol bufferとBigqueryの型合わせが必要
 DateやTimestampはUNIXエポックからの日数や秒数に変換する必要がある
 Noneをstr 'None'や、int -1や、bool FalseにPythonで調整をする
//UNIXエポックからの日数
current_date = datetime.now()
epoch = datetime(1970, 1, 1)
record_date = (current_date - epoch).days

//UNIXエポックからの秒数
data_string = str(date_v)
dt_obj = datetime.fromisoformat(date_string.replace("Z","+00:00"))
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
seconds_since_epoch = (dt_obj - epoch).tatal_seconds()
microseconds_since_epoch = int(seconds_since_epoch * 1e6)
date_v = microseconds_since_epoch

■BQ APIクォータ割り当て超過(1000件insertしようとした)
テーブル変更1日1500件まで
テーブルメタデータ変更は10sあたり5回まで
テーブル当たりDMLの実行待ちキューは20件まで
テーブル当たり10sあたり25のDMLまで
→各insertでスリープを5秒入れた
 import time
 time.sleep(5)

上限がテーブル単位のためテーブル名を分けると回避できるらしい
■BQ streaming insert->BQ storage read/write APIの上限はDMLと別で、閾値が大きい
streaming insert -> Bigquery storage write API を使う
BigQuery Storage Write API を使用してデータを一括読み込み、ストリーミングする  |  Google Cloud
Storage Write API を使用したデータ読み込みのバッチ処理  |  BigQuery  |  Google Cloud

CreateWriteStream > AppendRows(ループ) > FinalizeWriteStream > BatchCommitWriteStreams
 をstart/append/send/close(write commit)の関数化し返り値でつなげた形にしたが
 sendをした後 proto_rows = types.ProtoRows() を掛け初期化する必要があった(offsetが倍々で増えたから)
offsetで送信毎の開始行の設定も必要(一連の処理で件数を記憶しており0固定で処理を書けないようだった)

■Python/Client libraryの値をBQに入れるにあたり
仕様書で型を調べる。STRUCTやクラスは紐解いて通常のカラムでBQに挿入
timestampやboolやint64はそのままの形でBQに挿入
BQ SQL:日付は値なしならNULLを入れる、数値やBool値はクォートで囲まない
PythonでSQLインサート文を作るとき改行コードが含まれるものをセットするとSyntax errror:Unclosed string literal
q = q.replace('//', '////') バックスラッシュをエスケープ、あるとイリーガルエスケープシーケンスとなる、raw文字列にしたい?
q = q.replace('/n', '//n') 改行をエスケープ
q = q.replace("'", "\\'") SQLが途切れないようシングルクォートをエスケープ
q = q.replace('/n', '    ') 改行を空白で置き換える

■変更の判断
変更で問題がでないか→PCにマウスと付けて問題が起こらないかという問題と相似、最終的に経験で判断するしか

■監査ログからSetIamのメソッドを取りBQ権限付与を検知するクエリ
WITH source AS(
SELECT
*
FROM `project-logging.organization_audit_log_v2.cloudaudit_googleapis_com_activity_20*`
WHERE_TABLE_SUFFIX = format_date('%y%m%d', current_date("Asia/Tokyo"))
),
project source AS(
SELECT
ROW_NUMBER() OVER (ORDER BY timestamp) as id,
*
FROM source
WHERE
protopayload_auditlog.methodName = 'SetlamPolicy'
project_authorizationinfo AS(
SELECT
DISTINCT
id,
__ori.resource.type as type,
__ori.resource.labels.project_id as project_id,
__ori.resource.labels.dataset_id as dataset_id,
protopayload_auditlog.methodName as method_name
protopayload_auditlog.resourceName as resource_name,
protopayload_auditlog.authenticationInfo.principal Email as email_manipulator,
authorizationInfo.resource as request_resource,
authorizationInfo.permission as request_permission,
authorizationInfo.granted as request_granted,
protopayload_auditlog.requestMetadata.callerlp as callerlp,
protopayload_auditlog.requestMetadata.callerSuppliedUserAgent as callerSuppliedUserAgent,
FROM project_source AS __ori
). UNNEST (protopayload_auditlog.authorizationInfo) AS authorizationInfo
project_bindingdeltas AS(
SELECT
id,
--array_binding Deltas_project as binding Deltas_project,
array_binding Deltas_project.action as action_project,
array_binding Deltas_project.member as member_project,
array_binding Deltas_project.role as role_project,
timestamp
FROM project_source AS_ori
,UNNEST (protopayload_auditlog.servicedata_v1_iam.policyDelta.bindingDeltas) AS array_binding Deltas_project
),
project_setiam AS(
SELECT
--*, except(id)
type,
project_id,
dataset_id,
method_name,
resource_name,
email_manipulator,
request_resource,
request_permission,
request_granted,
callerip,
callerSuppliedUserAgent.
action_project,
member_project,
role project.
CAST(NULL AS STRING) AS metadataJson,
CAST(NULL AS STRING) AS bindingDeltas_dataset,
CASTINULLAS STRING AS action_dataset,
CAST(NULL AS STRING) AS member_dataset,
CAST(NULL AS STRING) AS role_dataset,
CAST(NULL AS STRING) AS bindingDeltas_table,
CAST(NULL AS STRING) AS action_table,
CAST(NULL AS STRING) AS member_table,
CAST(NULL AS STRING) AS role_table,
timestamp
FROM project_authorizationinfo
LEFT JOIN project_bindingdeltas ON project_authorizationinfo.id = project_bindingdeltas.id
WHERE role_project LIKE 'roles/bigquery%
),
resource_source AS (
SELECT
__ori.resource.type as type,
__ori.resource.labels.project_id as project id,
__ori.resource.labels.dataset_id as dataset_id,
protopayload_auditlog.methodName as method_name,
protopayload_auditlog.resourceName as resource_name,
protopayload_auditlog.authenticationInfo.principalEmail as email_manipulator,
authorizationInfo.resource as request_resource,
authorizationInfo.permission as request_permission,
authorizationInfo.granted as request_granted,
protopayload_auditlog.requestMetadata.callerlp as callerlp,
protopayload_auditlog.requestMetadata.callerSuppliedUserAgent as callerSuppliedUserAgent,
protopayload_auditiog.metadataJson,
timestamp
FROM source AS __ori
,UNNEST(protopayload_auditlog.authorizationInfo) AS authorizationInfo 
WHERE
protopayload_auditlog.methodName = 'google.iam.v1.IAMPolicy.SetlamPolicy' 
--AND timestamp= "2024-03-11 04:11:30.885258 UTC"
),
resource_id AS (
SELECT
ROW_NUMBER() OVER (ORDER BY timestamp) as id,
*
FROM resource_source
),
resource_bq_dataset AS (
SELECT
id as id_dataset,
json_extract(metadataJson, '$.datasetChange bindingDeltas') as bindingDeltas_dataset,
json_extract(array_bindingDeltas_dataset, '$action') as action_dataset,
json_extract(array_bindingDeltas_dataset, $.member') as member_dataset,
json_extract(array_bindingDeltas_dataset, '$.role') as role_dataset,
FROM resource_id
,UNNEST(json query_array(metadataJson, '$.datasetChange.bindingDeltas')) AS array_bindingDeltas_dataset
),
resource_bq_table AS (
SELECT
id as id table,
json_extract(metadataJson, '$.tableChange.bindingDeltas') as bindingDeltas_table,
json extract(array_bindingDeltas_table, '$.action') as action table,
json_extract(array_bindingDeltas_table. '$.member') as member table,
json_extract(array_bindingDeltas_table, '$.role') as role_table,
FROM resource_id
,UNNEST(json query_array(metadataJson, '$.tableChange.bindingDeltas')) AS array_bindingDeltas_table
),
resource_setiam AS ( 
SELECT
--*except(id, id_dataset, id_table)
type,
project_id,
dataset_id,
method_name,
resource_name,
email_manipulator,
request_resource,
request_permission,
request_granted,
callerlp,
callerSuppliedUserAgent,
CAST(NULL AS STRING) AS action_project,
CAST(NULL AS STRING) AS member_project,
CAST(NULL AS STRING) AS role_project,
metadataJson,
bindingDeltas_dataset,
action_dataset,
member_dataset,
role_dataset,
bindingDeltas_table,
action_table,
member_table,
role_table,
timestamp
FROM resource_id
LEFT JOIN resource_bq_dataset ON resource_id.id = resource_bq_dataset.id_dataset
LEFT JOIN resource_bq_table ON resource_id.id = resource_bq_table.id_table
)
SELECT * FROM project_setiam
UNION ALL
SELECT * FROM resource_setiam

■BQからCloudSQLにデータを入れる (GCSを経由する、コマンドやPythonがある
bq query --use_legacy_sql=false 'CREATE OR REPLACE TABLE `prj.ds._table` AS SELECT FROM `prj.ds.view`';
bq extract -destination_format CSV 'prj.ds._table' gs://bucket/tbl.csv
gcloud sql import csv インスタンス名 
gs://bucket/tbl.csv --database=データベース名 --table=テーブル名

■ログの重複をなくす
import google.cloud.logging
import logging

# クライアントの作成
client = google.cloud.logging.Client()

# Cloud Logging ハンドラを追加
client.get_default_handler()
client.setup_logging()

# 既存のハンドラをすべて削除
for handler in logging.root.handlers[:]:
    logging.root.removeHandler(handler)

# 新しいハンドラを追加
logging.basicConfig(level=logging.INFO)
# logging.basicConfig(level=logging.DEBUG)  # DEBUG レベルからすべてのレベルを記録

# propagate を無効にして重複を防ぐ
logger = logging.getLogger()
logger.propagate = False

# 各ログレベルでテスト
logging.debug('This is a DEBUG log')
logging.info('This is an INFO log')
logging.warning('This is a WARNING log')
logging.error('This is an ERROR log')
logging.critical('This is a CRITICAL log')

■何度かAPIコールを繰り返す
def safe_replace_text(document_id, old_text, new_text, max_attempts=3):
    for attempt in range(max_attempts):
        try:
            replace_text(document_id, old_text, new_text)
            break  # 成功した場合はループを抜ける
        except Exception as e:
            print(f"Attempt {attempt + 1} failed: {e}")
            if attempt == max_attempts - 1:
                print("Reached maximum attempts.")

■Exponential Backoffで時間を指数級数的にゆらぎながら増やすリトライ
import time
import random
def exponential_backoff(max_retries=5, base_wait_time=1, max_wait_time=32):
    retries = 0
    while retries < max_retries:
        try:
            # APIリクエストの送信
            response = send_request()
            if response.status_code == 200:
                return response  # 成功時に結果を返す
        except Exception as e:
            wait_time = min(base_wait_time * (2 ** retries), max_wait_time)
            wait_time += random.uniform(0, 1)  # ランダムなズレを追加(Jitter)
            print(f"Retrying in {wait_time} seconds...")
            time.sleep(wait_time)
            retries += 1
    raise Exception("Max retries reached, request failed")

クォータの増加の依頼もできるが、基本的に下記の上限がある

1. Google Docs API の利用上限

  • ユーザーごとの1分あたりのリクエスト数:
    • 1,000 リクエスト/ユーザー/100秒
  • プロジェクトごとの1日あたりのリクエスト数:
    • プロジェクトごとに1日100万リクエスト(デフォルト)

これらの制限を超えると、リクエストが拒否されるか、APIを利用できなくなることがあります。

2. Google Drive API の利用上限

  • ユーザーごとの100秒あたりのリクエスト数:
    • 1,000 リクエスト/ユーザー/100秒
  • プロジェクトごとの1日あたりのリクエスト数:
    • 1日10億リクエスト(デフォルト)
  • ユーザーごとのデータ転送量の制限:
    • 読み込みは750GB/日/ユーザー
    • 書き込みはユーザーごとの制限が異なるため、大量のデータ処理を行う場合は注意が必要
twitter
Hatena
Google Buzz
newsing
Yahoo!
Buzzurl
Technorati
del.icio.us
Choix
Iza!
Livedoor Clip
Facebook
Evernote
 

Posted by funa : 07:30 PM | Web | Comment (0) | Trackback (0)


PhotoGallery


TWITTER
Search

Mobile
QR for cellphone  QR for smart phone
For mobile click here
For smart phone click here
Popular Page
#1Web
#2Hiace 200
#3Gadget
#4The beginning of CSSレイアウト
#5Column
#6Web font test
#7Ora Ora Ora Ora Ora
#8Wifi cam
#9みたらし団子
#10Arcade Controller
#11G Suite
#12PC SPEC 2012.8
#13Javascript
#14REMIX DTM DAW - Acid
#15RSS Radio
#16Optimost
#17通話SIM
#18Attachment
#19Summer time blues
#20Enigma
#21Git
#22Warning!! Page Expired.
#23Speaker
#24Darwinian Theory Of Evolution
#25AV首相
#26htaccess mod_rewite
#27/// BANGBOO BLOG /// From 2016-01-01 To 2016-01-31
#28竹書房
#29F☆ck CSS
#30Automobile Inspection
#31No ID
#32Win7 / Win10 Insco
#33Speaker
#34Arcade Controller
#35Agile
#36G Suite
#37Personal Information Privacy Act
#38Europe
#39Warning!! Page Expired.
#40GoogleMap Moblile
#41CSS Selectors
#42MySQL DB Database
#43Ant
#44☆od damnit
#45Teeth Teeth
#46Itinerary with a eurail pass
#47PHP Developer
#48Affiliate
#49/// BANGBOO BLOG /// From 2019-01-01 To 2019-01-31
#50/// BANGBOO BLOG /// From 2019-09-01 To 2019-09-30
#51/// BANGBOO BLOG /// On 2020-03-01
#52/// BANGBOO BLOG /// On 2020-04-01
#53Windows env tips
#54恐慌からの脱出方法
#55MARUTAI
#56A Rainbow Between Clouds‏
#57ER
#58PDF in cellphone with microSD
#59DJ
#60ICOCA
#61Departures
#62Update your home page
#63CSS Grid
#64恐慌からの脱出方法
#65ハチロクカフェ
#66/// BANGBOO BLOG /// On 2016-03-31
#67/// BANGBOO BLOG /// From 2017-02-01 To 2017-02-28
#68/// BANGBOO BLOG /// From 2019-07-01 To 2019-07-31
#69/// BANGBOO BLOG /// From 2019-10-01 To 2019-10-31
#70/// BANGBOO BLOG /// On 2020-01-21
#71Bike
#72Where Hiphop lives!!
#73The team that always wins
#74Tora Tora Tora
#75Blog Ping
#76無料ストレージ
#77jQuery - write less, do more.
#78Adobe Premire6.0 (Guru R.I.P.)
#79PC SPEC 2007.7
#80Google Sitemap
#81Information privacy & antispam law
#82Wifi security camera with solar panel & small battery
#83Hope get back to normal
#84Vice versa
#85ハイエースのメンテ
#86Camoufla
#87α7Ⅱ
#88Jack up Hiace
#89Fucking tire
#90Big D
#914 Pole Plug
#925-year-old shit
#93Emancipation Proclamation
#94Windows env tips
#95Meritocracy
#96Focus zone
#97Raspberry Pi
#98Mind Control
#99Interview
#100Branding Excellent
Category
Recent Entry
Trackback
Comment
Archive
<     October 2024     >
Sun Mon Tue Wed Thi Fri Sat
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
Link