/// 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


Web List
BT on Jun 21, 2024 11:00 PM
GCP hands-off 3 on Jun 01, 2024 3:24 PM
Pubsub on May 09, 2024 12:00 AM
HELM on Apr 27, 2024 11:27 PM
GKE on Jan 14, 2024 9:59 PM
GCP Network Connectivity on Oct 31, 2023 10:57 PM
Machine learning(Bigquery ML) on Jun 21, 2023 8:00 PM
GCP Python Google doc編集 on Jun 01, 2023 12:00 AM
GCP hands-off 2 on May 29, 2023 7:30 PM
HSTS/CORS/CSPOAuth/OpenID/SAML/XSS/CSRF/JSOP/SSO/SSL/SVG/JWT/WebAssembly on Feb 11, 2023 1:46 AM
Docker on Jul 24, 2022 3:46 AM
LPIC on May 18, 2022 3:20 AM
Goo ana 4 on Apr 23, 2022 11:00 AM
I drive or test driven on Apr 17, 2022 9:54 AM
GCP Python script on Apr 01, 2022 12:00 AM
GCP runs off functions pubsub on scheduler on Mar 30, 2022 7:59 PM
GCP script on Feb 26, 2022 2:52 AM
リンク踏合組合 on Dec 25, 2021 5:46 PM
k8s on Jun 09, 2021 12:01 AM
GCP Hands Off on May 22, 2021 12:00 AM
GCP ログ・アセット調査 Logging/Bigquery information schema/Asset inventory on May 22, 2021 12:00 AM
GCP part2 on May 21, 2021 12:00 AM
GCP on May 20, 2021 9:00 PM
Terrafirma on May 02, 2021 10:14 PM
Linux cmd2 on Apr 02, 2021 1:00 AM
Linux cmd on Apr 02, 2021 12:00 AM
BigQuery part2 on Feb 22, 2021 12:00 AM
BigQuery on Feb 21, 2021 1:00 AM
Python Python on Feb 11, 2021 12:00 AM
Python on Feb 10, 2021 7:30 PM


June 21, 2024

BT
あそびはここで終わりにしようぜ~

Big Table
Cloud Bigtableを触ってみよう - Uzabase for Engineers
でっかいテーブル、読み書き低レイテンシー、RDBは負荷高いときにレプ数位でスケールが難しいがBTはするので正規化せずに単一テーブルにしておく感じ
row keyが主役
データを追加するのに3パターンある(行追加、列追加、セル追加)
 行に複数カラムファミリーにカラムが幾つか入れられるのでKVSだが結局Where句みたいに使う?
  行キー「企業ID#日付」,COLUMN FAMILY「STOCK PRICE」,COLUMN「HI PRICE」「LO PRICE」に対してJSONデータを入れておく等 
  時間はバージョン管理として持っている
 複雑な条件は無理でデータを事前整理して入れておき、JSONカラムを使ったりで一行にまとめスキャンを一発で済ます等で高スループットのみ
  Google検索のようにキーワードを入れると、検索結果が数多く一瞬で返る等
  複雑な条件はDataprocを使うらしい

Big table構成
Bigtableを徹底解説! - G-gen Tech Blog
インスタンスの中に一つ以上のクラスタ(ゾーン別に設定しレプリケーション)> 各クラスタには1つ以上の同数のノード 
 クラスタに table > 複数Column family > 複数Column > セル
bigtable_app_profilesで転送クラスタ先の設定する(単一行トランザクション設定を含む)
 -マルチクラスタ(自動フェイルオーバ、単一行transaction不可でレプリケーションによる不整合あり) 
 -シングルクラスタ(手動フェイルオーバ、一行transaction) 
デフォルトをマルチにして、通常のクラスタ転送をシングル、問題があるときだけアプリで判定しマルチに行く
Bigtableで複数クラスタ構成におけるデータ整合性の保証 - Carpe Diem (hatenablog.com)

スキーマ:
 テーブル
 行キー(row key)
 カラムファミリー(カページコレクションポリシーを含む)
 カラム
更新したデータはタイムスタンプによりセル内で保存される
 解消するにはガベージコレクション
  期限切れ値、バージョン数で設定する

仕様:
KVS、行指向の行単位でスキャン
各テーブルのインデックス (行キー)は1つのみで一意である必要がある
行は、行キーの辞書順に並べ替えられます。
列は、列ファミリー別にグループ化され、列ファミリー内で辞書順に並べ替えられます
列ファミリーは特定の順序では保存されません
集計列ファミリーには集計セルが含まれます
行レベルでアトミック (複数行だと知らんという意)
 アトミック性:トランザクション整合性がある(一部の操作だけ実行した状態とならずに)
特定の行にread/writeが集中するより分散が良い
Bigtable のテーブルはスバース、空白行での消費はない

cbt CLI の概要  |  Bigtable Documentation  |  Google Cloud
cbt リファレンス  |  Bigtable Documentation  |  Google Cloud
gcloud components update
gcloud components install cbt
(-/cbtrcに以下記載すれば-projectと-instance はデフォルト値で省略できる)
cd ~
echo project unco > ~/.cbtrc
echo instance = chinco >> ~/.cbtrc
cbt -project unco listinstances
cbt -instance chinco listclusters
cbt -project unco -instance chinco ls | grep kuso-t
 テーブル名取得
cht -project unco -instance chinco ls kuso-table
 カラムファミリやポリシー等取得
cbt -project unco -instance chinco deletefamily kuso-table shikko-family
cbt -project unco -instance chinco deletetable kuso-table
 テーブルを消せばカラムファミリも削除になる

Posted by funa : 11:00 PM | Web | Comment (0) | Trackback (0)


June 1, 2024

GCP hands-off 3
■Cloud SQL Python Connector (Cloud SQL language Connector)
CloudSQL proxyでないやり方、簡単
Cloud SQL 言語コネクタの概要  |  Cloud SQL for MySQL  |  Google Cloud
GitHub - GoogleCloudPlatform/cloud-sql-python-connector: A Python library for connecting securely to your Cloud SQL instances.

事前必要 pip install Flask mysql-connector-python
import mysql.connector
db_config = {
'host': 'localhost',
'user': 'your_username',
'password': 'your_password',
'database': 'your_database'
}
def items():
#データベースの返りをdictで取得
connection = mysql.connector.connect(**db_config)
cursor = connection.cursor(dictionary=True)
cursor.execute("SELECT COUNT(*) AS total FROM item")
#単一カラムのとき
total_items = cursor.fetchone()['total']
cursor.execute("SELECT FROM item")
items = cursor.fetchall()
cursor.close()
connection.close()

コネクションプールを使うSQLAlchemy が良い?
from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 
from google.cloud.sql.connector import Connector

# initialize Python Connector object 
connector = Connector()

#Python Connector database connection function
def getconn():
conn = connector.connect(
"project region instance-name", # Cloud SQL Instance Connection Name
"pymysql", user="my-user", password="my-password", db="my-database",
ip_type="public" # "private" for private IP
)
return conn

app Flask(name)

#configure Flask-SQLAlchemy to use Python Connector
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://"
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {"creator": getconn}

# initialize the app with the extension
db = SQLAlchemy()
db.init_app(app)

下記のオプションも使える
connector = Connector(
ip_type="public", # can also be "private" or "psc"
enable_iam_auth=False,
timeout=30,
credentials=custom_creds, #google.auth credentials.Credentials
refresh_strategy="lazy", # can be "lazy" or "background"
)

■Cloud SQL MySQL設定
ロールは Cloud SQL 管理者 (roles/cloudsql.admin)、Cloud SQL インスタンスユーザー (roles/cloudsql.instance User)等のIAM?
【開発環境】mysql_dbso
Enterprise/Sandbox/AsiaNorthEast1 (Tokyo) / Single zone
MySQL ver 8.4
Shared core / 1cpu 0.6GB/HDD/10GB(auto increase) 
PrivatelP/設定にvpcnwが必要/Enable private path
Auto daily backup 7days (1-5AM) / Enable point-in-time recovery
Week1 sun 0-1am/ Enable query insights
PW: x
【本番環境】
Enterprise plus? キャッシュ使う?

データベースフラグ (confが直接変更できなためフラグとしてパラメータを渡せる)
Cloud SQL studio (コンソールでMySQLが使える)
 MySQLクライアントを使いたいならAuth proxyが必要
HA構成だとフェールオーバーやリードレプリカ等が使える

●Cloud SQLが内部IPだとサーパレスVPCコネクタ、or 外部IPならSQL+auth proxy
内部IPで良いのでVPCを作る、CloudSQLを内部IPで作る
サーバレスVPCコネクタを作る
ファイアウォールルールでポート (デフォルトで3306など)を開放
Cloud Run のNW設定で、サーバーレス VPC コネクタを選択、ルートオプションとしてすべてのトラフィックをVPC コネクタ経由で送信を選択

■対象アセットに対する付与可能なロールの一覧表示
Full Resource Name(フルでのアセット名を探せる)

import google.auth
import googleapiclient.discovery

def view_grantable_roles(full_resource_name: str) -> None:
credentials.google.auth.default(
scopes=["https://www.googleapis.com/auth/cloud-platform"]
)
service = googleapiclient.discovery.build('iam', 'v1', credentials credentials)
roles = (
service roles()
queryGrantableRoles (body=["fullResourceName": full_resource_name}).execute()
)
for role in roles["roles"]
if "title" in role:
print("Title: role["title"])
print("Name: role["name"])
if "description" in role:
print("Description:" + role["description"])
print("")

project_id = "prj"
#resource = f"//bigquery.googleapis.com/projects/prj/datasets/ds"
#resource + f"//bigquery googleapis.com/projects/prj/datasets/ds/tables/tbl"
resource = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
view_grantable_roles(resource)

■ロールの一覧表示
https://cloud.google.com/iam/docs/roles-overview?hl=ja#role-types
1)事前定義ロールの場合は roles.get() を使用します。
2)プロジェクトレベルのカスタムロールの場合は、projects.roles.get() を使用します。
3)組織レベルのカスタムロールの場合は、organizations.roles.get() を使用します。
 これら3種類で全てを網羅すると思われます
 projectIDがsys-のものはGAS、lifecycleStateがACTIVE以外のものも含まれるので注意

■bqへの書き込み
export GOOGLE_APPLICATION_CREDENTIALS="path/to/your-service-account-key.json"
pip install google-cloud-bigquery

from google.cloud import bigquery
client = bigquery Client()
#書き込み先のテーブル情報
table_ref = f"{project_id}.{dataset_id}.{table_id}"

#サンプルデータの生成
def generate_sample_data(num_rows)
data = [
{
"organization": f"org_(num_rows)",
"permission". "view",
}
for _ in range(num_rows)
]
return data

data_to_insert = generate_sample_data(5000)
errors = client.insert_rows_json(table_ref, data_to_insert)

if errors:
print("Errors occurred: {errors}")
else:
print("Data successfully written to BigQuery!")

■データカタログ
データアセットを検索する  |  Data Catalog Documentation  |  Google Cloud
Class SearchCatalogRequest (3.23.0)  |  Python client library  |  Google Cloud
サンプルで仕様書のAPIを使っているがqueryが空白刻みで入れる等の使い方が分かる

■BQスキーマ+ポリシータグ取得
from google.cloud import bigquery
def get_policy_tags_from_bq_table(project_id, dataset_id, table_id):
    print("################ bigquery.Client.get_table().schema start ################")
    print(f"Target table: {project_id}.{dataset_id}.{table_id}")
    bq_client = bigquery.Client()
    table = bq_client.get_table(f"{project_id}.{dataset_id}.{table_id}")
    schema = table.schema
    policy_tags = []
    for field in schema:
        print(f"Column: {field.name}")
        if field.policy_tags:
            tags = [tag for tag in field.policy_tags.names]
            policy_tags.extend(tags)
            print(f"Policy Tags: {tags}")
        else:
            print("> No Policy Tags assigned.")
    return policy_tags

PROJECT_ID = "prj"
DATASET_ID = "ds"
TABLE_ID = "test001"

policy_tags = get_policy_tags_from_bq_table(PROJECT_ID, DATASET_ID, TABLE_ID)
print("Collected Policy Tags:", policy_tags)

■ポリシータグ設定
from google.cloud import datacatalog_v1
from google.cloud import bigquery

PROJECT_ID = "prj"
DATASET_ID = "ds"
TABLE_ID = "tbl01"
COLUMN_NAME = "aaa"
POLICY_TAG_PROJECT = "prj"
POLICY_TAG_NAME = "projects/prj/locations/us/taxonomies/83893110/policyTags/11089383"

def list_taxonomy_and_policy_tag():
    print("############# Start #############")
    list_policy_tags = []
    client = datacatalog_v1.PolicyTagManagerClient()
    request = datacatalog_v1.ListTaxonomiesRequest(
        parent=f"projects/{POLICY_TAG_PROJECT}/locations/us"
    )
    try:
        page_result = client.list_taxonomies(request=request)
    except google.api_core.exceptions.PermissionDenied as e:
        print(f"Skipping project {POLICY_TAG_PROJECT} due to PermissionDenied error: {e}")
        return []
    except Exception as e:
        print(f"An error occurred for project {POLICY_TAG_PROJECT}: {e}")
        return []

    for taxonomy in page_result:
        print(f"############ Taxonomy display_name: {taxonomy.display_name} #############")
        print(f"############ Taxonomy name: {taxonomy.name} #############")
        request_tag = datacatalog_v1.ListPolicyTagsRequest(parent=taxonomy.name)
        try:
            page_result_tag = client.list_policy_tags(request=request_tag)
        except Exception as e:
            print(f"Error on {request_tag}: {e}")
            break
        for policy_tag in page_result_tag:
            print("Policy tag:")
            print(policy_tag)
            list_policy_tags.append({
                "project_id": POLICY_TAG_PROJECT,
                "taxonomy_display_name": taxonomy.display_name,
                "taxonomy_name": taxonomy.name,
                "policy_tag_name": policy_tag.name,
                "policy_tag_display_name": policy_tag.display_name,
            })
    return list_policy_tags

def update_table_schema_with_policy_tag(list_policy_tags):
    for policy_tag in list_policy_tags:
        if policy_tag['policy_tag_name'] == POLICY_TAG_NAME:
            print(
                f"Target policy tag:\n"
                f"  Project ID: {policy_tag['project_id']}\n"
                f"  Taxonomy Display Name: {policy_tag['taxonomy_display_name']}\n"
                f"  Taxonomy Name: {policy_tag['taxonomy_name']}\n"
                f"  Policy Tag Name: {policy_tag['policy_tag_name']}\n"
                f"  Policy Tag Display Name: {policy_tag['policy_tag_display_name']}"
            )
            client = bigquery.Client()
            table_ref = f"{PROJECT_ID}.{DATASET_ID}.{TABLE_ID}"
            table = client.get_table(table_ref)
            new_schema = []
            for field in table.schema:
                if field.name == COLUMN_NAME:
                    new_schema.append(
                        bigquery.SchemaField(
                            name=field.name,
                            field_type=field.field_type,  # Keep original field type
                            mode=field.mode,             # Keep original mode
                            description=field.description,
                            policy_tags=bigquery.PolicyTagList([POLICY_TAG_NAME]),
                        )
                    )
                else:
                    new_schema.append(field)
            table.schema = new_schema
            updated_table = client.update_table(table, ["schema"])
            print(
                f"Updated table {updated_table.project}.{updated_table.dataset_id}.{updated_table.table_id} schema\n"
                f"with policy_tag {POLICY_TAG_NAME} on the column {COLUMN_NAME} successfully."
            )
if __name__ == "__main__":
    list_policy_tags = list_taxonomy_and_policy_tag()
    update_table_schema_with_policy_tag(list_policy_tags)

■Workload identity federation(GCP外との連携)
まずWIF用のSAを作成する>SAに権限を付与する>
1)Workload identity provider+SAの情報をgithub actionに埋めて使う
 GitHub Actions から GCP リソースにアクセスする用途
2)Workload identity poolから構成情報をDLしAWSアプリに埋めて使う
 AWSからGCP リソースにアクセス
する用途
  gcloud auth login-cred-file=構成情報ファイルパス
3)Workload identity poolから構成情報をEKSのOIDC ID token のパスを指定しDL
 EKS から GCP リソースにアクセス
する用途
- EKSのマニフェストのサービスアカウントのアノテーションにIAMロールを記載
- EKSのサービスアカウントを使用したい Podのアノテーションに追加
- マウント先のパスを環境変数 GOOGLE APPLICATION_CREDENTIALS に設定
- Pod内でSDK またはコマンドにてGCP リソースヘアクセス可能か確認

Posted by funa : 03:24 PM | Web | Comment (0) | Trackback (0)


May 9, 2024

Pubsub
■pubsub
Publisher app → |GCPの壁| Topic(Schema) → Subscription 1や2 |GCPの壁| → Subscriber app
 サブスクライバーappにPull/PushさせるPull/Pushのサブスクリプションをトピックに紐づける設定をしておく

【図解付き】Cloud Pub/Subに概要や使い方についてわかりやすく解説 - KIYONO Engineer Blog (kiyono-co.jp)
Pub/Sub サービスの概要  |  Pub/Sub ドキュメント  |  Google Cloud
GCPのCloud PubSubで考慮すること - Carpe Diem (hatenablog.com)
Pub/Sub の割り当てと上限  |  Pub/Sub ドキュメント  |  Google Cloud
アプリで簡単にPubsubにパブリッシュや、サブスクもできるので、アプリ間の連携にPubsubが使える
 • 非同期処理(画像処理とか重めのもの
 • IDの種類 (message id, subscription id, topic id, ack id, project idあたりがアプリでは使われるっぽい
  ※ack idはpull時のみでPushのときはhttpステータスコードが200でackとなる
GCP - Pub/Sub サービス概要 #GoogleCloud - Qiita
Pub/Sub メッセージの作成とレスポンス  |  Python 用 App Engine フレキシブル環境に関するドキュメント  |  Google Cloud
トピック(メッセージのパブリッシュ先)
 • スキーマ/外部アクセス許可/リテンション/GCS/バックアップの設定がある (Push/Pullの設定はない)
 • パブリッシュ側のベストプラクティス (JWT) 
  Pub/Sub トピックにパブリッシュするためのベスト プラクティス  |  Pub/Sub ドキュメント  |  Google Cloud
サブスクライバのPushとPull (PushはEndpointが必要、デフォルトはpull)
 GCP - Pub/Sub サービス概要 #GoogleCloud - Qiita
 • at-least-once (少なくとも1回) 配信を提供します
 • 同じ順序指定キーを持ち、同じリージョンに存在している場合は、メッセージの順序指定を有効にできます
 • サブスクライバーが31日間未使用、またはサブスクリプションが未更新の場合、サブスクリプションは期限切れ
 • メッセージ数が多いとpull向き サブスクリプション タイプを選択する  |  Pub/Sub ドキュメント  |  Google Cloud
pushはhttpsが必要?
 push サブスクリプションを作成する  |  Pub/Sub ドキュメント  |  Google Cloud
 • push エンドポイントのサーバーには、認証局が署名した有効な SSL証明書が必要でhttps
 • Cloud run でEvent Arcを設定するとサブスクが自動作成されrunのデフォルトhttpsのURLが使われるが、これはPullよりPushで安定した
 • CronバッチならPullで安定するのでは?大量リクエストはPull向きとある(Pullは失敗処理込みの話かも知れん)
トピックのリテンション:デフォルトなし、最小値:10分、最大値:31日
サブスクのリテンション:デフォルト値:7日、最小值:10分、最大値:7日
 サブスクリプション プロパティ  |  Pub/Sub ドキュメント  |  Google Cloud
pubsub ack期限(Ack Deadline)
 •デフォルト60秒> 設定10分>ack延長で最大1時間まで伸ばせると思われる
 リース管理で確認時間を延長する  |  Pub/Sub ドキュメント  |  Google Cloud
 •exactly onceを設定しなければ期限の延長は保証されない
 •ack期限を過ぎる、あるいはNackを返す場合、メッセージは再配送される
 •ack応答期限の延長は99パーセンタイル(上位1%の値よりも小さい値のうち最大の値)で
 modifyAckDeadlineを返し、延長してもMaxExtension (ack期限を延長 する最大値) 60minまで?
  modifyAckDeadlineリクエストを定期的に発行すればよいらしい
メッセージの再試行を強制するには
 •nack リクエストを送信
 •高レベルのクライアント ライブラリを使用していない場合は、ackDeadlineSeconds を0に設定して modifyAckDeadline リクエストを送信する
exactly once
1 回限りの配信  |  Pub/Sub ドキュメント  |  Google Cloud
 •pullなら設定できる。他には、Cloud Dataflowを組み合わせる(プログラムコードでDataflowを使う感じかり、あるいはmessageについているunique idを利用して、KVS を用いたステート管理をして自前で重複を排除する
 •再配信は、メッセージに対してクライアントによる否定確認応答が行われた場合、または確認応答期限が切れる前にクライアントが確認応答期限を延長しな かった場合のいずれかか原因で発生することがある。
  ※exactly onceはエラーでも再配信でPubsubパニックしないようにしたいために使うものではない?
pubsubはトピックにPublishされたメッセージをDataflowに引き継げる
 •Apache Beamのウィンドウ処理とセッション分析とコネクタのエコシスエムがある
 •メッセージ重複の削除ができる
 •pubsub>dataflow>BQやGCS: この流れでログ等をストーリミングで入れ込める
BQサブスクリプション (PubSubはBigQuery Storage Write API を使用してデータを BigQueryテーブルに送信、GCSサブスクもある)
 Langganan BigQuery  |  Dokumentasi Pub/Sub  |  Google Cloud
 BigQuery サブスクリプションの作成  |  Pub/Sub ドキュメント  |  Google Cloud
サブスクライバーApp側のコードでのフロー制御によりちょっと待てよのトラフィック急増対応
 フロー制御を使用して一時的な急増を処理する  |  Pub/Sub ドキュメント  |  Google Cloud
デッドレタートピック (配信試行回数が見れる)やエラーでの再配信
 メッセージ エラーの処理  |  Pub/Sub ドキュメント  |  Google Cloud
  • Pub/Subサブスクリプションにデッドレタートピックを設定しておくと、一定の回数再送信が失敗したメッセージの宛先がデッドレタートピックに変更され貯められる
メッセージのフィルタ、同時実行制御により多いメッセージに対応
 サブスクリプションからのメッセージをフィルタする  |  Pub/Sub ドキュメント  |  Google Cloud
Pubsubをローカルでエミュレートする
 エミュレータを使用したローカルでのアプリのテスト  |  Pub/Sub ドキュメント  |  Google Cloud
pubsubのスナップショットやリテンション
クイックスタート: スナップショットまたはタイムスタンプまでシークして Pub/Sub でメッセージを再生する  |  Pub/Sub ドキュメント  |  Google Cloud
トピックにリテンションを設定しスナップショット作成> 過去のサブスクしたメッセは見えなさそう
サブスクにリテンションを設定しスナップショット作成> 過去のAckしたメッセは見えなさそう
スナップショットでどう使うのか?
 cloud pubsubで配信済みのメッセージを再送する #PubSub - Qiita
 キューがたまっているときに撮るものと思われる。またシーク時間のポイントを設定する意味がある
 スナップショットとシークを使いこなして特定期間の再実行を行う機能
  スナップショットで再実行する
  シークは指定時間か最後のスナップショット以降のサブスク再実行(実際pushでrunが再実行された)
Pubsubにどんなメッセージが入ってきているか確認する方法
 pull形式ならAckしなければpullボタンで拾い見れる (トピックでパブリッシュしてサブスクでPull し見る)
 トラブルシュートはログを見るかデッドレタートピックかGCSバックアップを見る?
デッドレターキュー(ドロップしたものの確認と救済?)
 サブスクでDLQのONしデッドレタートピックを設定し転送する>GCSにもバックアップできる
 DLTでメッセージ(実行済みOR未実行)の再生
データ形式:スキーマを使うか、スキーマなしならdataで取得できる
 トピックのスキーマを作成する  |  Pub/Sub ドキュメント  |  Google Cloud
 Cloud Pub/Subの概要とPythonでの実践 - case-kの備忘録
from google cloud import pubsub_v1
from avro.io import DatumReader, BinaryDecoder
from avro schema import Parse
project_id="your-project-id"
subscription id="your-subscription-id"
subscriber pubsub_v1.SubscriberClient()
subscription_path = subscriber.subscription_path(project_id, subscription_id)
avro_schema = Parse("""
{
"type": "record",
"name": "Avro".
"fields": [
{
"name": "ProductName",
"type": "string",
"default":""
},
{
"name": "SKU",
"type": "int",
"default": 0
}
}
def callback(message):
print(f"Received message: {message}")
reader = DatumReader(avro_schema)
decoder = Binary Decoder (message.data)
avro_record = reader.read(decoder)
message_id=message.message id
message.ack()
print("Message ID: (message_id}")
product_name = avro_record['ProductName']
sku= avro_record['SKU']
print("Product Name: (product_name}")
print("SKU: (sku}")
subscriber.subscribe(subscription_path, callback=callback)

def callback(message):
print("Received message: (message)")
data message data
message_id=message.message_id
message.ack()
print("Date (data)")
print("Message ID: (message_id)")

Pub/SubでStreamingPull APIを使用してメッセージをリアルタイムで処理する - G-gen Tech Blog
StreamingPull API を使用するとアプリとの間で永続的な双方向接続が維持され、Pub/Sub でメッセージが利用可能になるとすぐに pullされる。1 つの pull リクエストで 1 つの pull レスポンスが返る通常の 単項 Pull と比較すると、高スループット・低レイテンシ。必要なメッセージを残す処理をしたりも?GCP側の問題であっても通信が切れた場合は別サーバに繋ぎなおすためmodifyAckDeadlineも切れ再配信されるバグがある

/// BANGBOO BLOG /// - GCP runs off functions pubsub on scheduler

Posted by funa : 12:00 AM | Web | Comment (0) | Trackback (0)


April 27, 2024

HELM
Helm Templateについて色々説明してみる #kubernetes - Qiita
Highway to Helm (zenn.dev)
Helmの概要とChart(チャート)の作り方 #Docker - Qiita
Helm | 一般的な慣習

helmはコマンド一発だが生k8sはマニフェストファイルの数だけkubectl apply(delete)を繰り返す必要がある
 helm upgrade chart名 -f 環境毎yamlファイル
 文法覚えるより繰り返した方がええんじゃない
helmはテンプレートフォルダ以下がマニフェスのようなもの
 ループ処理が記述可、関数が使える、関数を作れる

helmは基本はテキストの整形用と言える(ヘルパー関数やビルトイン関数を使い外部ファイルを取り込んで変形したり、変数yamlを環境yamlで上書きし外部の値を使う等で沢山のGKEアセットをループ的に生成しようとしている)

helm create <チャート名>
templates/ マニフェスト (テンプレート)
env/ 自分で作成するが環境毎に異なる値の入る変数を記述
┣dev.yaml
┣prd.yaml
values.yaml 繰り返す値等 (dev/prd.yamlが優先され上書きされる) 
helm upgrade-install <release名> <Helmチャートの圧縮ファイル名>

●●helmテンプレートの文法 (.ファイル名.親.子で表す、.はルートオブジェクト、Valuesはvaluesオブジェクト、$変数:=値、ymlインデントはスペース2つ)
●templates/deployment.yaml
{{ $env := Values.environment }}
{{ $serviceAccountName := Values.serviceAccountName }}
image: {{ .Values.deployment.image }}:{{.Values deployment.imageTag }} //nginx:latest
serviceAccountName: {{ $serviceAccountName }}-{{ $env }} //sample-sa-dev
●values.yaml
deployment:
  image: nginx
  imageTag: latest
serviceAccountName: sample-sa
●env/dev.yaml
environment: dev
※values.yaml よりdev/prd.yamlが優先され上書きされ.Valueで使う

●●helmテンプレートのループ (range~end)
●templates/es.yaml
spec:
  nodeSets:
  ((- range .Values.es.nodeSets }}
  name: {{ .name }}
  config:
    node.attr.zone: {{ .zone }}
  {{- end }}
●values yami
es:
  nodeSets:
  - name: node-a
    zone: asia-northeast1-a
  - name, node-b
    zone: asia-northeast1-b

●●helmテンプレートのIF (if-end)
●templates/ingress.yaml
((- if .Values.ingress.enabled -))
apiVension: networking k8s.io/v1
kind: Ingress
{(- end }}
●env/prd.yaml
ingress:
  enabled: true
●env/dev.yaml
ingress:
  enabled: false

●●helmテンプレートの複数値 (toYaml、nindentは関数)
●templates/ingress.yaml
metadata:
  annotations:
    {{- toYaml .Values.ingress.annotations | nindent 4 }}
●values.yaml
ingress:
  annotations:
    kubernetes.io/ingress.global-static-ip-name: sample-ip-name
    kubernetes.io/ingress.class: "gce-internal"

●●その他
中括弧内側の前後にダッシュ {{--}} をつけることができ、前に付けた場合は前の半角スペースを、 後ろにつけた場合は改行コードを取り除く
hoge:
  {{- $piyo := "aaa" -}}
  "fuga"
/* */で囲まれた部分はコメント構文
{{-/* a comment */ -}}
.Releaseでリリースの情報を使用できる
{{ .ReleaseName }}とか{{ .ReleaseNamespace }}

●●_helpers.tpl
Helmの_helpers.tplを使える人になりたい #kubernetes - Qiita
helm create [チャート名]で自動でtemplates ディレクトリに_helpers.tplが作成されるが、 partialsやhelpersと呼ばれる共通のコードブロック (defineアクションで定義されtemplateアクションで呼び出される)や、ヘルパー関数などが定義される。
_アンスコ始まりのファイルは、他のテンプレートファイル内のどこからでも利用できるという共通部品。 これは内部にマニフェストがないものとみなされる。
種類としては、values.yamlが差し替え可能な変数、ローカル変数が定義したTemplateファイル内でのみ使える変数、_helpers.tplはチャート内で自由に使える変数
●templates/_helpers.tpl
{{- define "deployment" -}}
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: {{.name }}
    name: {{ .name }}-deployment
spec:
  replicas: {{ .replicas }}
  selector:
    matchLabels:
      app: {{ .name }}
  template:
    metadata:
      labels:
        app: {{.name}}
    spec:
      containers:
      - image: {{ .image }}
        name: {{ .name }}
{{- end -}}
●values.yaml
nginx:
  replicas: "1"
  name: nginx
  image: docker.io/nginx:1.25.1
httpd:
  replicas: "3"
  name: httpd
  image: docker.io/httpd:2.4.57
●deployment-nginx.yami
{{ include "deployment" .Values.nginx }}
※{{ include "deployment" 引数 }}で関数を呼ぶ

●●英語サイトだともっと情報がある
Helm | Built-in Objects
.Filesなどのビルトインオブジェクトがあったりと、、、

GKEクラスタを作成しておく
kubectlでArgo adminとシークレット作成?
brew install argocd
Argo cd設定ファイルリポジトリのclone
argocd cluster add <context name>
argocd repo add <repo url> --ssh-private-key-path ~/.ssh/id_rsa
argocd-configuration に設定を追加
argocd-insallation に設定を追加
argo cd上からinstallationをsyncする
argocd login --grpc-web --sso dev-argocd.dev.bb.com

===
ArgoはSettingsにリポジトリ、クラスター、プロジェクト、他にUserの設定
 アプリ設定でhelmのパス等を指定(Argo内部でhelm upgradeでなくkubectrl applyに変換しでやってもらえるお作法:helmコマンドのインストール不要でArgoでhelm文法が使える)

Posted by funa : 11:27 PM | Web | Comment (0) | Trackback (0)


January 14, 2024

GKE
モダンか何か知らんが、豚玉かイカ玉で十分じゃ

===========
kubectlチートシート | Kubernetes

フォルダに .py と requirements.txt と .dockerignore と Dockerfile を入れてアップロードしている
gcloud builds submit --tag asia-northeast2-docker.pkg.dev/bangboo-prj/xxx/image001

helloworld@bangboo-prj.iam.gserviceaccount.com 作成
アクセス元のIPを確認するCloud run作成
 ドメインないと無理なのでLBとIAPをあきらめ生成されるURLで十分
 Cloud runでアクセス元IPを表示するヤツ
 runのallUsersのinvokerを削除したらアクセス不可になった(この方法で管理する)
curl http://ifconfig.me/ で十分だったが

GKE
k8sの内部NWは通常別途いるがGKEは速い奴が動作
GKEはクラスタ内部のDNSでサービス名で名前解決できる
サービスのIPとポートは環境変数で参照可
kubectlを使うには、gcloud container cluters get-credentials を打つ必要がある

GKE設定
-クラスタ:側の設定(IP範囲とかセキュリティとか?)
 一般/限定公開:外部IPを使うか使わないか
 コントロール プレーン承認済みネットワーク:CPにアクセスできるセキュリティ範囲
-ワークロード:マニフェストで設定

一般か限定公開か?コントロールプレーンが外部IPか?CPがグローバルアクセス可か?承認NWか?
 一般公開で承認NWが良いのでは?簡単だし、
 限定公開で使うには>CPに外部IPで承認NWでいいのでは?
  NW:default subnet:default
  外部IPでアクセス許可
  CP アドレスの範囲 192.168.1.0/28とか172.16.0.0/28(サブネット重複しない奴)
  コントロール プレーン承認済みネットワーク home (169.99.99.0/24ではなくGCPのIPぽい)
  限定公開ならnatが要る
 CPの VPCのIP範囲は、クラスタの VPC 内のサブネットと重複不可。CPとクラスタは VPC ピアリングを使用してプライベートで通信します
 グローバルアクセスは別リージョンからという意味っぽい、cloud shellからのkubectlのためONが良い
デフォルト設定なら作成したサブネットのIP範囲でなくクラスタが作られない
 面倒ならdefault-defaultで良いかも

サブネットをVPCネットワークを考えて指定する方が偉いかも知れんが
default asia-northeast2 10.174.0.0/20 の場合
 サブネットは asia-northeast2 10.174.27.0/24 とか

ARにあるコンテナからGKEをデプロイが簡単にできる
Cloud Source Repositories でソース管理gitが下記のようにできる
 gcloud source repos clone bangboo-registry --project=bangboo-prj
 cd bangboo-registry
 git push -u origin master
run使用中のコンテナがGKE上では上手くいかない runのコンテナは8080のようだ
 Dockerfileとmain.py上ではポートは何でもよい仕様だが、runで自動的に8080割り当てるようだ
  それが駄目でありGKEは環境変数でPORT 8080を指定
  CrashLoopBackOff問題がでる
  https://www.scsk.jp/sp/sysdig/blog/container_security/content_7.html
デプロイ公開でポート80 ターゲットポート8080に(クラスタを作成後、ワークロードでデプロイする)

developmentのspec: containers: ports: - containerPort: 8080 を入れる?
 yamlでなく、コンソールで設定時に入れると良い

$ kubectl get all
NAME                      TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)        AGE
service/flask-1-service   LoadBalancer   10.48.4.134   34.97.169.72   80:32147/TCP   20m

us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
 これは簡単に上手く行く、環境変数PORT8080不要
 クイックスタート: アプリを GKE クラスタにデプロイする  |  Google Kubernetes Engine (GKE)  |  Google Cloud

ワークロードでyamlの spec: replicas: 0を保存するとアクセスを止められる

コンフィグマップ:構成ファイル、コマンドライン引数、環境変数、ポート番号を別途持っていてPodにバインドする(マニフェストに書くと抜き出され見れる)
シークレット:Base64の値?(マニフェストに書くと抜き出され見れる)甘いのでsecret mgrを使う方が良い?
 config map/secretはマニフェストで編集する必要がある(見れるだけと思われる)
エディタで見てみる:yamlとかステータスが見れる

■LBに静的IPを振る
hello-app-addressと名付けたIPを取得
LBのアノテーションで設定
# ingress.yaml(NWはNodePort、Route
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.global-static-ip-name: hello-app-address  # IP
    networking.gke.io/managed-certificates: hello-managed-cert      # 証明書
    kubernetes.io/ingress.class: "gce"  # 外部 HTTP(S)LB
spec:
  defaultBackend:
    service:
      name: hello-deployment
      port:
        number: 8080

ServiceのLBはリージョン指定するタイプの静的IP
IngressはグローバルIPOK
apiVersion: v1
kind: Service
metadata:
  name: hoge
  labels:
    app: hoge
spec:
  ports:
    - port: 80
  selector:
    app: hoge
    tier: frontend
    environment : stage
  type: LoadBalancer
  loadBalancerIP: xxx.xxx.xxx.xxx


ArmorでIP制限
1)サービスから対象を選択しingressを作成することでLBを追加しArmorも設定可能
2)デフォルトLBに付けるにはkubectl要りそう、backendconfig.yamlはどこに置く
 Cloud ArmorでGKE IngressへのアクセスをIPで制御する #GoogleCloud - Qiita
サービス画面のkubectrlから
# backend-config.yaml を作り kubectl apply -f backend-config.yaml
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  namespace: default
  name: hello-backend-config
spec:
  securityPolicy:
    name: "bangboo-armor"

serviceのyamlに下記を追加
metadata:
  annotations:
    cloud.google.com/backend-config: '{"ports": {"8080":"hello-backend-config"}}'
↑これでは不足する 
どこで設定状態を見るか?
ingress作成してLBとArmorつけて、デフォルトLBを削除してみる?

GKEの外部からのアクセスを制限するには?
 限定公開+コントロールプレーンは承認済み等でアクセスしKubectlする
 ArmorでIP制限+アダプティブ設定(ArmorはLBが要る)
GKEでNodePort TypeのServiceに対してインターネットアクセス許可する - IK.AM

限定公開クラスタ+踏み台サーバにIAPで入りKubectl(承認済みNWでの制御はIPのみなので危ういらしい)
GKE(Google Kubernetes Engine) Autopilotの限定公開クラスタにIAPを利用してアクセスする | Tech-Tech (nddhq.co.jp)
【GKE/Terraform】外部ネットワークからの全てのアクセスを制限した限定公開クラスタを作成し、踏み台サーバーからkubectlする (zenn.dev)
コントロールプレーンとPod間で自動FWされない場合もありFirewall要チェック

 Cloud shellのグローバルIPを取得しシェルを承認済みNWにできないか?>OK
 curl http://ifconfig.me/

GKEでPythonをCron定期実行させたい
ArgoでDAGを実行させたい
 https://zenn.dev/ring_belle/articles/2c4bbe4365b544
ArgoでGKEのCICD(Argoは別ホストでGithubにアクセスし、GKEを操る)
 https://www.asobou.co.jp/blog/web/argo-cd

サービスアカウント
Workload Identity Federation for GKEの新しい設定方法を解説 - G-gen Tech Blog
1)ノードに紐付いたサービスアカウントKSAをそのまま使用する(裏でimpersonate)
gkeのサービスアカウントとIAMサービスアカウントの紐づけが不要になった
VPCサービスコントロールで管理したい場合impersonateのSAを指定できないためWIFが要る
2)サービスアカウントのキーを Kubernetes Secret として GKE クラスタに登録する
3)Workload Identity Federationをつかう
GCP の Workload Identity サービスについてのまとめ #GoogleCloud - Qiita
Githubとか外部のサービスから利用するためSAを連携させる
IAM>Workload identity連携画面で設定が見れる
※KSAはノード単位で設定、Pod単位でGCPのリソースにアクセスできるように管理したい?

●メモ
忙しいときはスケールアウトするが、落ち着き始めるとスケールinし、必要なPodも落とされてしまう
safe-to-evict をymlのannotationで明示して特定Podはスケールinしない等にしておく
annotations:
  cluster-autoscaler.kubernetes.io/safe-to-evict:"false"
クラスタのオートスケーラー イベントの表示  |  Google Kubernetes Engine (GKE)  |  Google Cloud

■Workloads リソース
Pod:Workloadsリソースの最小単位
ReplicaSet:Podのレプリカを作成し、指定した数のPodを維持し続けるリソースです。
Deployment:ローリングアップデートやロールバックなどを実現するリソースです。
DaemonSet(ReplicaSet亜種):各ノードにPodを一つずつ配置するリソースです。
StatefulSet(ReplicaSet亜種):ステートフルなPodを作成できるリソースです。
Job:Podを利用して、指定回数のみ処理を実行させるリソースです。(使い捨てPod)
CronJob:Jobを管理するリソースです。
Config connector:GKEでGCPリソースを調節してくれるアドオン。Podの増加減少にあたり必要なアカウントや権限やPubSub等々を自動作成や管理する。マニフェストのymlにcnrmのAPIを記載したりする(Config connector resource nameの略)

■GKE関連の運用
GKEクラスタ認証ローテーション
30日以内になると自動ローテーションするが危険なので手動が由
GKEはマイクロサービスのエンドポイントでのサービス提供かgcloud api利用が前提といえるのでこれでOK
1) ローテ開始 (CPのIPとクレデンシャル)
2) ノード再作成
3) APIクライアントを更新 (クレデンシャル再取得)
4) ローテ完了 (元IPと旧クレデンシャルの停止)
GKEクラスタ認証ローテーションの考慮
セキュアなGKEクラスタ
コントロールプレーンの自動アップグレード&IPローテーション&ノードブールの自動アップグレードで死ぬ
 CPの更新後はクレデンを取得しなおす必要がある、ArgoでCICDを組んでいるとクラスタに認証入りなおす必要がある
 ノードが入れ替わりに時間が掛かり、時間差で問題がでることがあるので注意

Posted by funa : 09:59 PM | Web | Comment (0) | Trackback (0)


October 31, 2023

GCP Network Connectivity
●共有 VPC
 同組織のプロジェクトのホストプロジェクト(親)のVPCをサービスプロジェクト(子)に共有
●VPC ネットワーク ピアリング
 異なる組織間の接続(双方のVPCでコネクションを作成する、内部IPで通信する、サブネットは重複しないこと、2ホップ制限で1:1=3つ以上の場合は古メッシュでコネクション作成要)、k8sサービスとPod ipをVPCピアリング経由する利用法もある

●ハイブリッド サブネット
 Cloud VPN/Interconnect等が必要、オンプレルータとCloud RouterをBGPでつなぐ、オンプレとGCPをつなぐ
●Cloud Interconnect
 DCと専用線で閉域網接続、Cloud VPNより低レイテンシ/帯域安定
●Cloud VPN
 オンプレとIPsec VPN接続、アドレス帯の重複だめ、Cloud VPN側でBGPIP設定やIKEキー生成をしオンプレルータ側でそれらを設定する

●内部範囲
 VPCで使うIPをCIDRで定義しIP範囲の使用方法を事前に決定しておく、IPが勝手に使われたりしない等ができる

●限定公開の Google アクセス(Private Google Access)
 外部IPを持たないGCE等はデフォルトのインターネットゲートウェイ0.0.0.0を経由してGoogle APIにアクセスする、VPC>Routesで見れる
●オンプレミス ホスト用の限定公開の Google アクセス
 CloudVPNやInterconnectを経由してオンプレから内部IPを利用してGoogleAPIにアクセス、GCP側ではCloudDNSで特定のドメインのAレコードを入れる、選択したドメインのIPアドレス範囲を静的カスタムルートでVPC内のプライベートIPからルーティングできるように設定する、オンプレにはCloudRouterからドメインのIPアドレス範囲をBGPでルーティング広報する、VPNやInterconnectがないと0.0.0.0でGoogleAPIにアクセスするがこれだとRFC1918に準拠しない199.33.153.4/30などのIPを使う必要がありルーティングが複雑化したり、オンプレを通る場合があり通信は慎重に設計をすること
●Private Service Connect
 「限定公開の Google アクセス」の発展版、オンプレをNATでVPCに接続、内部IPでGoogleAPIにアクセスできる、PSCエンドポイントを介して内部IPで公開できる、NATされ内部IPの公開先での重複OK

●プライベート サービス アクセス
 VPCペアリングを併用してサービスプロデューサをVPCに接続し内部IPで次のようなサービスに内部IPでアクセスできるようにする(Cloud VPNまたはInterconnectを付け足せばオンプレからも可)、Cloud SQL/AlloyDB for posgre/Memorystore for Redis/Memcached/Cloud build/Apigee等の限られたもの
●サーバーレス VPC アクセス
 サーバレスからVPC内リソースにアクセスするためのコネクタ(通常は外部IP通信になるがコレだと内部IPでVPCにルーティングされる、/28のサブネットを指定)、例えば既存のcloud runサービスを編集しても付けられず初期構築時のみ設定できる

●外部 IP アドレスを持つ VM から API にアクセスする
IPv6をVMに設定し限定公開DNSゾーン設定をすればトラフィックはGCP内にとどまりインターネットを通りません

●CDN Interconnect
 Cloud CDNもあるが他社のCDNに接続する、Akamai/Cloud flare/fastly等々

●Network Connectivity Center 
 ハブとなりCloudVPN/InterconnectをメッシュしGCP/オンプレ含め通信させる、Googleのバックボーンでユーザ企業の拠点間を接続できる
●ダイレクト ピアリング
 GoogleのエッジNWに直接ピアリング接続を確立し高スループット化、Google workspaceやGoogleAPI用だが普通は使わずInterconnectを使う
●キャリア ピアリング
 ダイレクトピアリングの高度な運用が自社対応できない等でサービスプロバイダ経由でGoogle workspaceなどのGoogleアプリに品質よくアクセスする

Google CloudのVPCを徹底解説!(応用編) - G-gen Tech Blog

●トンネル系の下記は色々権限が要りそうで候補
Compute OS login/IAP-secured tunnel user/Service account user/viewer/compute.instance*

■ポートフォワードは止めないと別につないで繋いでいるつもりでも同じところに繋ぎ続ける
lsof -i 3128
ps ax | grep 3128
ps ax | ssh
kill [PID]

■IAPトンネル
export http_proxy=http://localhost:3128
export https_proxy=http://localhost:3128
gcloud compute start-iap-tunnel --zone asia-northeast1-a gce-proxy001 3128 --local-host-port=localhost:3128 --project=gcp-proxy-prj
でコマンドを打てばIAP踏み台トンネルを通って外部に通信できる

■踏み台コマンド
gcloud compute ssh --projet gcp-prj-unco --zone asia-northeast1-a gce-step-svr
でSSHログインしそこからcurl等で操作する

■シークレットからPWを取りつつコマンドを打つ
gcloud compute ssh --project gcp-prj --zone asia-northeast1-b stp-srv --tunnel-through-iap fNL 3306:mysql.com: 3306
mysql -u baka_usr -p"$(gcloud secrets versions access latest --secret mysql_pw --project=gcp-prj)" -h 127.0.0.1-P 3306
 mysqlコマンドのpオプションは空白なしでPWを入れる、baka_usrはMySQLのユーザ、mysql_pwはsecret mgrに保存した名前
 $()のLinuxコマンドでgcloudコマンドを入れ子。ワンライナーの形で利用ができる
secret mgrのコマンド 
ワンライナー解説(変数と$()とバッククォート

■SSHトンネル
sshの基本はこれ、セキュアシェル、トンネルは特殊?
SSH [オプション] [ログイン名@]接続先 [接続先で実行するcmd]
接続先に権限があること

SSHの疎通確認
ssh baka@stp_srv.unco.com

設定例
.ssh/config
User baka
Hostname step_srv
ProxyCommand ssh -W %h:%p baka@step_srv.unco.com
PubkeyAuthentication no
PasswordAuthentication yes

※sshはPWは危険なので鍵認証のみにしたい
 IPアドレス元を制限や同一IPのログイン試行は拒否する仕組み等は欲しい

SSHコネクション上でトンネル作る
ssh step_srv -L 8080:dest.benki.com:80
 とか ssh -L 8080:dest.benki.com:80 ahouser@step_srv.unco.com
※ポート22でstep_srvにSSHコネクションを貼り、ローカル:8080のリクエストはdest:80に転送する
ブラウザか新規ターミナルでcurl
http://localhost:8080
ダメなら転送設定したターミナルはstep_srvにいるのでcurl

GCP、AWS、Azure 別に見るクラウド VM への攻撃経路まとめ (paloaltonetworks.jp)

=============
なぜレッドオーシャン化する前にサービスを グロースできなかったのか? - フリマアプリ編 - (フリル)
サービスを急拡大させる意思決定が遅く競合に遅れ
競合出現後も経営方針を大きく変えなかった
勝利条件はユーザ数で機能差ではなかった
パワープレーでいかにプロモーションばら撒いて認知広げて第一想起をとるかだった
先行者優位で過ごせる期間は短い
スタープレイヤーの採用、手数料無料化、TVCM等PLを超えた手法があった、BS経営すべきだった
成長のキャップが創業者の能力になっていた
有能な人材:耳の痛いことを言ってくれる人材を経営チームに採用しても良かった
CTOが開発をし、組織運営の雑務をし、採用もやっていた
CEOは机の組み立てをするな。CTOはPCの購入をするな
役割の変化に素早く適用し権限移譲を行い、やるべきことをやれる状況を作る
あるいは必要な組織を大きくすることに注力する、例えば開発組織を大きくする
戦時のCEO、皆に戦時であることを伝える、企業文化に背く意思決定も行う
研究や教育等、やった方が良さそうな耳障りの良いタスクも拒否する
どうやったら市場で勝てるかの戦略

IPOとか目指さなければConfort zoneを見つけてじっくりまったりビジネスを継続させる手もある
メルカリやPay2をみた結果論、このやり方も古いというかアレ

Posted by funa : 10:57 PM | Web | Comment (0) | Trackback (0)


June 21, 2023

Machine learning(Bigquery ML)
機械学習:
 マシーンラーニング、ML。マッシーンがLearnしデータの背景にあるルールやパターンを発見する。
モデル:
 機械学習における入力データに対して結果(出力)を導き出す仕組み。モデルは入力されたデータを解析し、評価/判定を行った結果を出力として返す。つまり、機械学習は「入カ>モデル>出力」から成る。

学習データ
 モデルをつくるために学習させるデータ
適用データ
 モデルに対して予測を適用させるデータ
教師あり
 学習データに対して正解ラベルを付けて学習する方法
  例)過去にDMを送付した結果(目的変数)を用いて学習させる
教師ありは、回帰と分類の2つに分けられます。
 回帰(予测)
  連続する数値を予測するもので、売上、重量、温度などを算出する
 分類(識別)
  データがどのクラスに属するかを予測するもので、販売商品が売れる見込み/売れない見込みなどに分類
  DMにおいては分類モデル(買う・買わないの識別)を使用

目的変数
 結果側の変数
 例)ある特定のカテゴリやブランドをその顧客が注文する
  ●確率(購入する・しない)
   ー学習データにターゲットフラグ (購入あり=1、購入無し=0)を立てる
   ー適用後に出てくる結果は0~1 (0%~100%) の範囲
  ●金額
説明変数(特微量とも呼ぶ) 
 原因側のデータ
 目的変数を予測するために使用する顧客の様々な情報、大きく分けて2種類
  ●顧客属性
   ー法人/個人事業主など事業形態、登録住所の郵便番号、業種コードなど
  ●購買履歴
   ー購買 いつ、何を、どのくらい購入したか等の情報
   ー各商品カテゴリ毎の購入金額 etc.
質的変数
 数値で推し測ることができ、数字の大小に意味をもつデータ
  (例)売上、購入回数、従業員数、DM送付回数
單的変数
 単に分類や種類を区別するためだけのデータ
  (例)都道府県、茎種コード、購入有無

統計手法
 重回帰分析:
  説明変数xが目的変数yの値を変化させます。そのため、説明変数から、目的変数の値を予測可能です。
 ロジスティック回帰
  yが1になる確率を判別します。いくつかの要因(説明変数)から「2値の結果(目的変数)」が起こる確率を説明・予測
   2値の結果とは合格/不合格、採用/不採用など、答えが2つしかない場合を指し

線形回帰(エクセル)
 slope関数:回帰直線の傾きを取得
 inercept関数:回帰直線の切片を取得
 forecast関数:単回帰分析からの推測値を取得
+++++++++++++

BigQueryを利用して機械学習を簡易に学びたいです。Webアプリのログよりサーバーエラーを予測するようなもので方法を教えてください > BigQueryを利用して機械学習を学び、Webアプリのログからサーバーエラーを予測する方法について簡単に解説します。以下はその手順の概要です。

●ステップ1: データの準備
まず、WebアプリのログデータをBigQueryにインポートします。ログデータには、タイムスタンプ、リクエストの詳細、ステータスコード、エラー メッセージなどの情報が含まれているはずです。

例:ログデータスキーマ server_log
timestamp: TIMESTAMP
request_id: STRING
status_code: INTEGER
error_message: STRING
user_agent: STRING
response_time: FLOAT

●ステップ2:データの前処理
エラーログの解析を容易にするために、データをクリーンアップし、特徴量を作成します。

CREATE OR REPLACE TABLE ds.cleaned_log AS
SELECT
timestamp,
status_code,
error_message,
user_agent,
response_time,
IF(status_code >= 500, 1, 0) AS is_error
FROM
ds.server_log
WHERE
timestamp BETWEEN 2023-01-01 AND 2023-12-31;

このクエリでは、ステータスコードが500以上のものをエラーと見なし、is_error列を追加しています。

●ステップ3:特徴量エンジニアリング
機械学習モデルに投入するための特徴量を作成します。例えば、以下のような特徴量を考えます。
-時間帯 (hour)
-ユーザーエージェント (user_agent) 
-過去のエラーレート(past_error_rate)
※past_error_rateを省くとどうなのか?

CREATE OR REPLACE TABLE ds.features AS
SELECT
EXTRACT(HOUR FROM timestamp) AS hour,
user_agent,
response_time,
is_error,
--AVG(is_error) OVER (PARTITION BY user_agent ORDER BY timestamp RANGE BETWEEN INTERVAL 1 DAY PRECEDING AND CURRENT ROW) AS past_error_rate
AVG(is_error) OVER (PARTITION BY user_agent) AS past_error_rate
FROM
ds.cleaned_log;

●ステップ4: 機械学習モデルの作成
BigQuery MLを使用してモデルを作成します。ここではロジスティック回帰モデルを使用してエラーの予測を行います。
他には、The CREATE MODEL statement  |  BigQuery  |  Google Cloud
MODEL_TYPE = { 'LINEAR_REG' |'LOGISTIC_REG' |'KMEANS' |'MATRIX_FACTORIZATION' |'PCA' |'AUTOENCODER' |'AUTOML_CLASSIFIER' |'AUTOML_REGRESSOR' |'BOOSTED_TREE_CLASSIFIER' |'BOOSTED_TREE_REGRESSOR' |'RANDOM_FOREST_CLASSIFIER' |'RANDOM_FOREST_REGRESSOR' |'DNN_CLASSIFIER' |'DNN_REGRESSOR' |'DNN_LINEAR_COMBINED_CLASSIFIER' | 'DNN_LINEAR_COMBINED_REGRESSOR' |'ARIMA_PLUS' |'ARIMA_PLUS_XREG' |'TENSORFLOW' |'TENSORFLOW_LITE' |'ONNX' |'XGBOOST'}

CREATE OR REPLACE MODEL ds.error_prediction_model
OPTIONS(model_type='logistic_reg', input_label_cols=['is error'] AS
SELECT
hour, 
user_agent, 
response_time, 
past_error_rate,
is_error
FROM
ds.features;

●ステップ5: モデルの評価
モデルを評価し、精度を確認します。

SELECT
*
FROM
 ML.EVALUATE(MODEL, `ds.error.prediction_model`,(
SELECT hour,
user_agent,
response_time,
past_error_rate,
is_error
FROM
ds.features
));

●ステップ6: エラーの予測
新しいデータを使ってエラーを予測します。

SELECT
*
predicted_is_error,
predicted_is_error_probs
FROM
ML.PREDICT(MODEL `ds.error_prediction_model`,(
SELECT
EXTRACT(HOUR FROM timestamp) AS hour,
user_agent,
response_time,
--AVG(CASE WHEN status_code 500 THEN 1 ELSE 0 END) OVER (PARTITION BY user_agent ORDER BY timestamp RANGE BETWEEN INTERVAL 1 DAY PRECEDING AND CURRENT ROW) AS past_error_rate
AVG(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END) OVER (PARTITION BY user_agent) AS past_error_rate
FROM
ds.new_server_log
));


●ダミーデータ
INSERT INTO ds.server_log (timestamp, request_id, status_code, error_message, user_agent, response_time)
VALUES
('2024-06-28 18:00:00 UTC', 'req 801, 208, '', 'Mozilla/5.0 (Windows NT 18.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 0.123), 
(2024-06-20 10:01:00 UTC', 'req 002, 588, Internal Server Error', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 8.456),
(2024-06-28 10:02:00 UTC', 'req 003', 484, 'Not Found', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14,6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1, 8.234),
(2024-06-20 10:03:00 UTC', 'req 004', 200, '', 'Mozilla/5.0 (Windows NT 18.8; Win64; x64; rv:89.0) Gecko/20100181 Firefox/89.8, 0.345),
(2024-06-28 10:04:00 UTC, 'req 005', 502, Bad Gateway', 'Mozilla/5.0 (Linux; Android 11; SM-G9918) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Mobile Safari/537.36, 0.678),
(2024-86-28 10:05:00 UTC, 'req 006', 503, 'Service Unavailable', 'Mozilla/5.0 (iPad; CPU OS 14.6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/6084.1, 0.789), (2824-86-28 18:06:00 UTC, req 007, 200, Chrome/91.0.4472.124 Safari/537.36, 0.567), Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
(2024-06-2010:07:00 UTC, 'req 008, 500, Internal Server Error', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.361, 0.890),
(2024-06-20 18:08:00 UTC, req 009, 404, Not Found', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14 6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.11', 8.345),
('2024-06-28 18:09:00 UTC', 'req 010', 200, '', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0 Gecko/20100101 Firefox/89.0', 0.456);


INSERT INTO ds.new_server_log (timestamp, request_id, status_code, error_message, user_agent, response_time)
VALUES
(2024-06-21 09:00:00 UTC', 'req 101', 200, '', 'Mozilla/5.0 (Windows NT 18.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 0.112), 
(2024-06-21 09:01:08 UTC, req 102', 500, Internal Server Error', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.36, 0.478),
(2024-06-21 09:02:00 UTC', 'req 183, 484, 'Not Found', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safar1/684.1, 0.239),
(2024-06-21 09:03:00 UTC', 'req 104, 200, Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0, 8.301),
(2024-06-21 09:04:08 UTC, req 185', 502, 'Bad Gateway', 'Mozilla/5.0 (Linux; Android 11; SM-G9918) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Mobile Safari/537.36', 8.683),
(2024-06-21 09:05:00 UTC, req 106', 503, Service Unavailable', 'Mozilla/5.0 (iPad; CPU OS 14,6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1, 0.756),
(2024-06-21 09:06:00 UTC, req 107, 208, ", Mozilla/5.0 (Windows NT 18.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.36, 0.523),
(2024-06-21 09:07:00 UTC, req 188, 500, Internal Server Error, Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.36, 0.812),
('2024-06-21 09:08:08 UTC', 'req 109,, 404, 'Not Found', 'Mozilla/5.0 (iPhone: CPU iPhone OS 14,6 1ike Mac OS X) AppleWebKit/685.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1', 0.267),
('2024-06-21 09:09:08 UTC', 'req 110', 200, '', 'Mozilla/5.0 (Windows NT 18.8; Win64: x64; rv:89.0) Gecko/20180101 Firefox/89.8', 8.412);

Posted by funa : 08:00 PM | Web | Comment (0) | Trackback (0)


June 1, 2023

GCP Python Google doc編集
Google Docのコピーや編集
https://developers.google.com/docs/api/how-tos/documents?hl=ja
https://rimever.hatenablog.com/entry/2019/10/16/060000
クイックスタート
https://developers.google.com/docs/api/quickstart/python?hl=ja
スコープ情報 https://developers.google.com/identity/protocols/oauth2/scopes?hl=ja#docs
ディスカバリ ドキュメント
例えばこれはDriveAPIの分だが、RESTAPIで何ができるか全記載しているっぽい
https://www.googleapis.com/discovery/v1/apis/drive/v3/rest
Drive API
https://developers.google.com/drive/api/guides/about-sdk?hl=ja
https://developers.google.com/drive/api/reference/rest/v3?hl=ja
Docs API
https://developers.google.com/docs/api/concepts/document?hl=ja
https://developers.google.com/docs/api/reference/rest?hl=ja
https://googleapis.github.io/google-api-python-client/docs/epy/index.html
https://googleapis.github.io/google-api-python-client/docs/dyn/docs_v1.html
https://developers.google.com/docs/api/reference/rest/v1/documents/get?hl=ja

文字置換 https://developers.google.com/docs/api/how-tos/merge?hl=ja

※DocAPIからdriveld folderidは取得できなさそう、getは使えそう
※DriveAPIが使えない?
コピーでなくDocAPIでget body からの新規createで行く?
共有ドライブ時は、supports All Drives=True が必要だったでOK
file_metadata = service.files().get(fileld=DOCUMENT_ID, fields=id, name, mimeType, driveld', supports AllDrives=True) execute()

サービスアカウントでGWSにアクセスするにはGWS OU設定等が必要な場合がある>Google一般共有Docで検証も可

あるGoogle Docをコピーし、
本文を編集した上で
 本文の編集は((sample))となっている文字列をAAAに置換する
特定のドライブのフォルダに移動

from google.oauth2.service_account import Credentials
from googleapiclient.discovery
import build import re
#1. サービスアカウントの認証情報を設定
SCOPES = ['https://www.googleapis.com/auth/documents',
'https://www.googleapis.com/auth/drive']
SERVICE_ACCOUNT_FILE = 'path/to/your/service-account-file.json' #サービスアカウント のJSONファイルのパス

creds = Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)

#2. Google Docs と Driveのサービスをビルド
docs_service = build('docs', 'v1', credentials=creds)
drive_service = build('drive', 'v3', credentials=creds)

#3. コピー元のGoogle DocのIDと、移動先のフォルダIDを設定
SOURCE_DOCUMENT_ID = 'source_doc_id' #コピー元のドキュメントID
TARGET_FOLDER_ID = 'target_folder_id' #移動先のフォルダID

#4. Google Docをコピー
copied_doc = drive_service.files().copy(fileld=SOURCE_DOCUMENT_ID, body={"name": "Copied Document"), supportsAllDrives=True).execute()
copied_doc_id = copied_doc['id']

#5、本文を取得し、{{sample}} をAAAに置換
def replace_text(document_id, old_text, new_text)
#ドキュメントの内容を取得
document = docs_service.documents().get(documentid=document_id).execute()
content = document.get('body').get('content')
#リクエストリスト
requests = []
#検索と置換を行う
for element in content:
if 'paragraph' in element:
for paragraph_element in element['paragraph']['elements']:
if 'textRun' in paragraph_element:
text = paragraph_element['textRun']['content']
if old_text in text:
start_index = paragraph_element('startindex']
end_index = paragraph_element['endIndex']
requests append({
'replaceAllText': {
'containsText': {
'text': re.escape(old_text), #エスケープなしにする必要有
'matchCase': True
},
'replaceText': new_text
}
})
#置換リクエストを実行
if requests:
docs_service.documents().batchUpdate(documentid=document_id, body={'requests':requests}).execute()

#置換処理の実行
replace_text(copied_doc_id, '((sample))', 'AAA')

#6、コピーしたドキュメントを指定のフォルダに移動
drive_service.files().update(fileld=copied_doc_id, addParents=TARGET_FOLDER_ID, removeParents=copied doc['parents'][0], supportsAllDrives=True).execute() #親が取れないのでフォルダはハードコード

print(f"Document copied, edited, and moved successfully! Document ID: {copied_doc_id)")


Posted by funa : 12:00 AM | Web | Comment (0) | Trackback (0)


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/日/ユーザー
    • 書き込みはユーザーごとの制限が異なるため、大量のデータ処理を行う場合は注意が必要

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


February 11, 2023

HSTS/CORS/CSPOAuth/OpenID/SAML/XSS/CSRF/JSOP/SSO/SSL/SVG/JWT/WebAssembly
2024-10-6
クレカ情報の流出があったタリーズオンラインストアのWebアーカイブから原因を特定した猛者が現れる→集まった有識者たちにより巧妙な手口が明らかに - Togetter [トゥギャッター]
レスポンスヘッダーに Content-Security-Policy が適切に設定されていれば防げた可能性は高い。
具体的には
▼ connect-src ディレクティブの設定
スクリプトからの外部リソースへの通信先を制限して、マルウェアが悪意あるドメインにデータを送信するを防ぐ。
例. Content-Security-Policy: connect-src 'self' https://api.trustedservice.com;
▼ eval() の禁止
Content-Security-Policy はデフォルトで eval() の使用を禁止しているが、 'unsafe-eval' を指定することで許可できる。
slick.jsのライブラリに仕込まれていたので、管理者がやったんじゃないの?
slickスライダー実装まとめ16選【サンプル付き】 - じゅんぺいブログ (junpei-sugiyama.com)

2024-7-5
Webサービス公開前のチェックリスト (zenn.dev)

2023-02-11
フロントエンド開発のためのセキュリティ入門 - Speaker Deck
 HTTPとHTTPSが混ざっているwebサイトはHSTS(http strict transport securityヘッダ)でHTTPS強制できる
 JSのfetch,xhr/iframe/canvas/WebStorage,IndexedDBでクロスオリジンは危険
 Access-Control-Allow-OriginレスポンスヘッダでCORS(cross origin resource sharing)許可を判定できる
 CSP(Content-Security-Policy)レスポンスヘッダあるはmetaタグで許可するJSを判定できる

SVG
SVG Repo - Free SVG Vectors and Icons
Vector Icons and Stickers - PNG, SVG, EPS, PSD and CSS (flaticon.com)
SVGはテキストファイルなので開いてタグとして使える
ChatGPTにSVGでお絵描きさせる|temoki / Tomoki Kobayashi (note.com)
タグで図が書ける
SVGファイルについて (zenn.dev)
sizeを決めてviewBoxの座標を設定するのがやりやすい

JWT (JSON WEB TOKEN?)
JWTセキュリティ入門 - Speaker Deck

WebAssemblyとは?〜実際にC言語をブラウザで動かす〜【2019年6月版】 #JavaScript - Qiita
コンパイルしてバイナリをWebで実行する、速いJSみたいな、ゲームやエミュやCアプリ的な奴

=========================
2022-04-06
SAML
 SSOのログイン状態を保持する認証プロバイダー(IdP)を使い各アプリ(ServiceProvider)でSSOを実現する
 SSOの仕組みにはエージェント方式、リバースプロキシ方式、代理認証方式など
  ユーザはWebサービスにアクセス
  WebサービスからSSO認証プロバイダーサーバにSAML認証要求をPostする
  SSO認証プロバイダーサーバでSAML認証を解析、ユーザに認証を転送
  ユーザはそれでWebサービスにログインする

SAMLはログイン時にユーザー情報をチェック、OAuthはユーザーの情報を登録
OAuthはアプリケーションを連動させるAPIで有効なアクセストークンかを見る
 アクセストークンには「いつ」「どこで」「なんのために」作られたのか分からない
OpenIDはIDトークンを使い「いつ」「どこで」「なんのために」作られたのか分かる

OAuth 2.0、OpenID Connect、SAMLを比較
OAuth 2.0:新しいアプリケーションに登録して、新しい連絡先をFacebookや携帯電話の連絡先から自動的に取得することに同意した場合は、おそらくOAuth 2.0が使われています。この標準は、安全な委任アクセスを提供します。つまり、ユーザーが認証情報を共有しなくても、アプリケーションがユーザーに代わってアクションを起こしたり、サーバーからリソースにアクセスしたりすることができます。これは、アイデンティティプロバイダー(IdP)がユーザーの承認を得て、サードパーティのアプリケーションにトークンを発行できるようにすることで実現されます。

OpenID Connect:Googleを使ってYouTubeなどのアプリケーションにサインインしたり、Facebookを使ってオンラインショッピングのカートにログインしたりする場合に使用されるのが、この認証オプションです。OpenID Connectは、組織がユーザーを認証するために使用するオープンスタンダードです。IdPはこれを利用して、ユーザーがIdPにサインインした後、他のWebサイトやアプリにアクセスする際に、ログインしたりサインイン情報を共有したりする必要がないようにします。

SAML:SAML認証は、多くの場合に仕事環境で使用されます。たとえば、企業のイントラネットやIdPにログインした後、Salesforce、Box、Workdayなどの多数の追加サービスに、認証情報を再入力せずにアクセスできるようになります。SAMLは、IdPとサービスプロバイダーの間で認証・認可データを交換するためのXMLベースの標準で、ユーザーのアイデンティティとアクセス許可を検証し、サービスへのアクセスの許可/拒否を決定します。
OAuth、OpenID Connect、SAMLの違いとは? | Okta

Oath: idpがトークンを発行、Webサイト間でユーザを認識し3rdからでも個人情報を使えるようになる
OpenID(OIDC): idpとJWT(トークン)で認証するOauth系のSSO、Oauthの拡張でログインが3rdからもできるようになる
SAML: idpで各アプリやAD間をXMLメッセージにより認証管理しSSOを実現
 OpenIDとSAMLが同じような機能
 SAMLはユーザ固有の傾向で大企業SSOが多い、OpenIDはアプリ固有の傾向でWebサイトやモバイルアプリが多い
 SAMLよりOIDCの方が新しくSPAやスマホと親和性が高い
 SaaSとしてOpenIDはOktaのidp、SAMLはPingFederateのidpがメジャー

=========================
2016-01-03
■XSS対策、CSRF対策、脆弱性チェック
情報処理推進機構にチェックリスト有
https://www.ipa.go.jp/security/vuln/websecurity.html
XSS対策
 フォーム送信後の確認画面ではHTMLエスケープ等でサニタイズされた内容の結果を表示
 DBへのクエリについてはプレースホルダやエスケープ等でSQLインジェクションを防ぐ
 target="_blank"はXSSになるので危ない、rel="noopener noreferrer"を付ける
  https://b.hatena.ne.jp/entry/s/webtan.impress.co.jp/e/2020/03/13/35510
  https://laboradian.com/test-window-opener/
CSRF対策
 前ページでhidden値を入れる

 クッキーに具体的なものは入れない、CookieにHttpOnly属性、HTTPS通信ではsecure属性
 エラーメッセージを表示しない
 不要なファイルは削除

 XMLの外部実態参照は禁止、サーバ上でコードが実行される
  →libxml_disable_entity_loader(true)で止める、xmlをアップロードさせない/使用しない、JSON使う
 PDFからHTTPリクエストが発行される
  →PDFをアップロードさせない

------

//SQLインジェクション対策
$sql = "UPDATE users SET name='.mysql_real_escape_string($name).'WHERE id='.mysql_real_escape_string ($id).'";
\ " ' を最低限、\エスケープ
NUL (ASCII 0) /n /r / ' " およびCTRL+Zをエスケープしたい

//クロスサイトスクリプティング対策
表示時には<>&"をメタ文字へ変換
echo htmlspecialchars($_GET['username'], ENT_QUOTES);
$ent = htmlentities($ent, ENT_QUOTES, "UTF-8"); //100個の文字を変換

//クロスサイトスクリプティング対策
別サイトからのポストを弾く
refferを送信しないリクエストもある(別サイトのリファラを弾き、nullもしくは適切ページからを許可する)
セッションIDで判断する

//DOS対策
2重ポスト
IPと日付で2重ポストを防ぐ(同IPのポストがx秒以内を弾く)

■サニタイズの方法
DOCには生のテキスト、DBとHTMLにはエスケープ済みのものを入れる
• 入力があればhtmlエスケープしDBに入れる
• html表示はそのままhtmlエスケープ状態で出力
• Docへはhtmlエスケープを解除し表示
• htmlフォーム内表示はhtmlエスケープを解除し表示
htmlエスケープ
https://weblan3.com/html/special-character
バッククォート以外は分かり易いで文字表記でエスケープする、改行はエスケープしない
< &#60 &lt 不等号(より小さい)
> &#62 &gt; 不等号(より大きい)
& &#38; &amp; アンパサンド
" &#34; &quot; 二重引用符
' &#39; &apos; シングルクォート,アポストロフィ
; &#59: &semi; セミコロン
\ &#92; &bsol バックスラッシュ
` &#096 バッククォート

========

■JSONP
scriptタグを使用してクロスドメインなデータを取得する仕組みのことである。 HTMLのscriptタグ、JavaScript(関数)、JSONを組み合わせて実現される

GoogleAnalyticsのクッキーは1stパーティでサイト側がオーナでありGoogleがオーナーではない
 サイト側のJSでクッキーが作成されるようなっている
 クッキーが送信される相手はどこか?が重要でGoogleでなくサイト側に送信される
 アクセス履歴は別途データをGoogleに送信しており、クッキーはセッション管理に使用される


■SSO
色々な方法がある、SAMLや、サイトにエージェントを組み込み+SSO認証サーバ等
ロジックを確認しないと詳しくは分からない

=========

■SSL
CAはサーバに証明書(署名)を発行
====
クライアントが接続要求
サーバが証明書(署名とRSA公開鍵を含む)を送る
クライアントが証明書を検証(
 どっち?
 1)署名をルート証明書のチェーン(RSA公開鍵)で複合化しドメインを確認
  該当のルート証明書(RSA公開鍵)がブラウザにないと該当CAに要求?
   CRLやOCSPで失効について問合せができるようだがRSA公開鍵の要求は出来なさそう
  (ルート証明書はブラウザにある結局オレオレ証明書に違いない:厳密な審査の上で組込まれている)
 2)クライアントが公開鍵をサーバに送りRSA秘密鍵で暗号化し送り返すとクライアントが複合化してRSA秘密鍵が正しい事を確認
)
クライアントが共通鍵(セッションキー)を生成し公開鍵で暗号化し送る
サーバが秘密鍵で共通鍵を複合
以降共通鍵暗号で通信
 ※ホンマか??
====
ホスト(ドメイン)を持って接続要求をし、暗号化通信後にパスやクエリパラメータを送るので、詳細は暗号化され守られている

Posted by funa : 01:46 AM | Web | Comment (0) | Trackback (0)


Navi: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14  >
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
<     December 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