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
Promise on Dec 25, 2020 1:06 AM
Dexie on Apr 21, 2020 12:00 AM
PWA on Apr 20, 2020 6:00 PM
G Suite -> Google workspace - GWS on Apr 01, 2020 12:01 AM
CSS Grid on Mar 01, 2020 3:03 AM
Update your home page on Jan 21, 2020 12:20 AM
Cloud 9 on Nov 22, 2019 9:42 PM
Adobe Sensei on Jan 01, 2019 11:11 AM
Summer time blues on Aug 11, 2018 6:42 PM
Web font test on Jan 01, 2018 11:13 PM
Help desk on Jun 01, 2017 12:00 AM
RWD by HTML5 on Jun 07, 2016 8:28 PM
Big D on Jun 05, 2016 6:45 PM
SN on May 22, 2016 2:09 AM
Ant on Apr 03, 2016 10:01 PM
Windows env tips on Mar 31, 2016 1:44 AM
Cache on Mar 08, 2016 8:06 PM
Detected as bad site by virus company on Mar 08, 2016 1:07 AM
Google API on Feb 14, 2016 1:03 AM
OSS License on Jan 08, 2016 12:00 AM
Bash on Jan 07, 2016 10:41 PM
Regular expression on Jan 06, 2016 1:27 AM
Javascript on Jan 05, 2016 7:27 AM
Laravel on Jan 04, 2016 12:00 AM
Git on Jan 03, 2016 12:00 AM
Agile on Jan 02, 2016 12:00 AM
PHP Developer on Jan 01, 2016 10:47 PM
Photo Boo on Aug 30, 2015 1:17 AM
Camoufla on Jan 09, 2014 12:08 AM
A Bao A Qu on Sep 02, 2013 12:02 AM
Online softwares on Oct 06, 2011 1:21 AM
Halftone on Apr 05, 2011 6:50 AM
It tests you on Dec 16, 2010 5:16 PM
Secure your home on Oct 05, 2010 10:48 PM
F☆ck CSS on Sep 29, 2010 1:07 AM
Tentative on Sep 06, 2010 12:44 PM
今夜はKick it (WSH a.k.a VBscript) on Aug 26, 2010 4:18 AM
Who's BAT? (Batch file) on Aug 26, 2010 12:31 AM
After Effects on Aug 25, 2010 5:30 PM
Server transfer on Aug 18, 2010 5:05 PM
Tools - free icon etc on Aug 18, 2010 4:37 PM
ER on Aug 09, 2010 5:38 AM
Shooting star on Aug 07, 2010 3:38 AM
Even small things can't kill me on May 07, 2010 1:08 AM
Adobe Premire6.0 (Guru R.I.P.) on Apr 24, 2010 1:42 AM
jQuery - write less, do more. on Apr 23, 2010 6:09 AM
MySQL DB Database on Mar 11, 2010 12:25 AM
MySQL chara issue on Mar 09, 2010 5:02 AM
CSS Selectors on Mar 01, 2010 1:29 AM
Ora Ora Ora Ora Ora on Nov 16, 2009 6:54 PM
Use + Able + T on Oct 29, 2009 9:04 PM
A Rainbow Between Clouds on Jul 22, 2009 8:08 PM
No sense of direction on Jun 20, 2009 5:34 PM
Optimost on Mar 25, 2009 8:34 PM
Google Other---s on Mar 25, 2009 8:32 PM
Tora Tora Tora on Dec 20, 2008 9:05 PM
NO, WE CAN'T. on Oct 07, 2008 7:03 AM
Revolt against MT on Mar 03, 2008 12:24 AM
株式取引モバイルサイト - TOKIO Shock Exchange on Feb 03, 2008 11:51 PM
GoogleMap Moblile on Jan 25, 2008 12:52 AM
英語単語帳SNS on Dec 19, 2007 12:13 AM
Where Hiphop lives!! on Dec 18, 2007 9:56 AM
MARUTAI on Aug 04, 2007 7:45 PM
Consumer-Generated Media on May 09, 2007 12:48 AM
One of the net service TVCM on Mar 03, 2007 1:37 PM
ApacheBench on Feb 24, 2007 7:25 PM
Warning!! Page Expired. on Feb 21, 2007 7:56 PM
The No-Asshole Rule on Feb 20, 2007 7:51 PM
htaccess mod_rewite on Feb 17, 2007 6:29 PM
Copyright Protection on Feb 05, 2007 9:56 PM
Link Bait on Feb 03, 2007 8:34 PM
Personal Information Privacy Act on Feb 01, 2007 8:00 PM
Browser becoming a Push Media on Feb 01, 2007 12:10 AM
Classic Font on Jan 27, 2007 11:32 PM
Is this English??? on Jan 08, 2007 2:41 AM
Passwords on Jan 02, 2007 10:32 PM
Zero Test on Aug 26, 2006 5:12 AM
For a Presentation on Aug 23, 2006 9:36 AM
BANGBOO MAP on Aug 20, 2006 11:00 PM
Golden Ratio on Aug 15, 2006 9:53 PM
Tag Cloud on Aug 15, 2006 7:43 PM
Struggling with PHP on Aug 11, 2006 5:13 PM
Company Profile on Jul 25, 2006 4:35 AM
A good angle from variety of point of views on Jul 15, 2006 3:12 PM
Affiliate on Jun 11, 2006 7:46 PM
Garbled Characters on Jun 11, 2006 1:13 PM
Track Making on May 27, 2006 8:35 PM
動画をキャプチャーする on May 22, 2006 4:46 AM
BGM on May 16, 2006 9:05 AM
お役立ちツール on May 15, 2006 8:13 AM
映像制作 on May 14, 2006 12:52 PM
ウェブサイトサムネイルAPI on Apr 18, 2006 5:35 PM
ポストプロダクション on Apr 14, 2006 10:07 AM
WEB2.0? on Apr 13, 2006 7:03 AM
CSSの標準準拠と過去互換 on Apr 09, 2006 2:00 AM
The beginning of CSSレイアウト on Apr 08, 2006 11:22 PM
住所入力AJAX on Apr 08, 2006 5:39 AM
Google Sitemap on Apr 05, 2006 7:21 PM
無料ストレージ on Apr 05, 2006 4:40 AM
Blog Ping on Mar 30, 2006 11:40 PM
Movable Type on Mar 16, 2006 12:42 PM
着メロの掲載っていくらかかんの? on Mar 09, 2006 8:15 AM
RSS Radio on Mar 02, 2006 2:06 PM
June 21, 2024
BT
あそびはここで終わりにしようぜ~
Big Table
でっかいテーブル、読み書き低レイテンシー、RDBは負荷高いときにレプ数位でスケールが難しいがBTはするので正規化せずに単一テーブルにしておく感じ
row keyが主役
データを追加するのに3パターンある(行追加、列追加、セル追加)
行に複数カラムファミリーにカラムが幾つか入れられるのでKVSだが結局Where句みたいに使う?
行キー「企業ID#日付」,COLUMN FAMILY「STOCK PRICE」,COLUMN「HI PRICE」「LO PRICE」に対してJSONデータを入れておく等
時間はバージョン管理として持っている
複雑な条件は無理でデータを事前整理して入れておき、JSONカラムを使ったりで一行にまとめスキャンを一発で済ます等で高スループットのみ
Google検索のようにキーワードを入れると、検索結果が数多く一瞬で返る等
複雑な条件はDataprocを使うらしい
Big table構成
インスタンスの中に一つ以上のクラスタ(ゾーン別に設定しレプリケーション)> 各クラスタには1つ以上の同数のノード
クラスタに table > 複数Column family > 複数Column > セル
bigtable_app_profilesで転送クラスタ先の設定する(単一行トランザクション設定を含む)
-マルチクラスタ(自動フェイルオーバ、単一行transaction不可でレプリケーションによる不整合あり)
-シングルクラスタ(手動フェイルオーバ、一行transaction)
デフォルトをマルチにして、通常のクラスタ転送をシングル、問題があるときだけアプリで判定しマルチに行く
スキーマ:
テーブル
行キー(row key)
カラムファミリー(カページコレクションポリシーを含む)
カラム
更新したデータはタイムスタンプによりセル内で保存される
解消するにはガベージコレクション
期限切れ値、バージョン数で設定する
仕様:
KVS、行指向の行単位でスキャン
各テーブルのインデックス (行キー)は1つのみで一意である必要がある
行は、行キーの辞書順に並べ替えられます。
列は、列ファミリー別にグループ化され、列ファミリー内で辞書順に並べ替えられます
列ファミリーは特定の順序では保存されません
集計列ファミリーには集計セルが含まれます
行レベルでアトミック (複数行だと知らんという意)
アトミック性:トランザクション整合性がある(一部の操作だけ実行した状態とならずに)
特定の行にread/writeが集中するより分散が良い
Bigtable のテーブルはスバース、空白行での消費はない
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でないやり方、簡単
事前必要 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 CloudClass 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
アプリで簡単にPubsubにパブリッシュや、サブスクもできるので、アプリ間の連携にPubsubが使える
• 非同期処理(画像処理とか重めのもの
• IDの種類 (message id, subscription id, topic id, ack id, project idあたりがアプリでは使われるっぽい
※ack idはpull時のみでPushのときはhttpステータスコードが200でackとなる
トピック(メッセージのパブリッシュ先)
• スキーマ/外部アクセス許可/リテンション/GCS/バックアップの設定がある (Push/Pullの設定はない)
• パブリッシュ側のベストプラクティス (JWT)
サブスクライバのPushとPull (PushはEndpointが必要、デフォルトはpull)
• at-least-once (少なくとも1回) 配信を提供します
• 同じ順序指定キーを持ち、同じリージョンに存在している場合は、メッセージの順序指定を有効にできます
• サブスクライバーが31日間未使用、またはサブスクリプションが未更新の場合、サブスクリプションは期限切れ
pushはhttpsが必要?
• push エンドポイントのサーバーには、認証局が署名した有効な SSL証明書が必要でhttps
• Cloud run でEvent Arcを設定するとサブスクが自動作成されrunのデフォルトhttpsのURLが使われるが、これはPullよりPushで安定した
• CronバッチならPullで安定するのでは?大量リクエストはPull向きとある(Pullは失敗処理込みの話かも知れん)
トピックのリテンション:デフォルトなし、最小値:10分、最大値:31日
サブスクのリテンション:デフォルト値:7日、最小值:10分、最大値:7日
pubsub ack期限(Ack Deadline)
•デフォルト60秒> 設定10分>ack延長で最大1時間まで伸ばせると思われる
•exactly onceを設定しなければ期限の延長は保証されない
•ack期限を過ぎる、あるいはNackを返す場合、メッセージは再配送される
•ack応答期限の延長は99パーセンタイル(上位1%の値よりも小さい値のうち最大の値)で
modifyAckDeadlineを返し、延長してもMaxExtension (ack期限を延長 する最大値) 60minまで?
modifyAckDeadlineリクエストを定期的に発行すればよいらしい
メッセージの再試行を強制するには
•nack リクエストを送信
•高レベルのクライアント ライブラリを使用していない場合は、ackDeadlineSeconds を0に設定して modifyAckDeadline リクエストを送信する
•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サブスクもある)
サブスクライバーApp側のコードでのフロー制御によりちょっと待てよのトラフィック急増対応
デッドレタートピック (配信試行回数が見れる)やエラーでの再配信
• Pub/Subサブスクリプションにデッドレタートピックを設定しておくと、一定の回数再送信が失敗したメッセージの宛先がデッドレタートピックに変更され貯められる
メッセージのフィルタ、同時実行制御により多いメッセージに対応
Pubsubをローカルでエミュレートする
pubsubのスナップショットやリテンション
トピックにリテンションを設定しスナップショット作成> 過去のサブスクしたメッセは見えなさそう
サブスクにリテンションを設定しスナップショット作成> 過去のAckしたメッセは見えなさそう
スナップショットでどう使うのか?
キューがたまっているときに撮るものと思われる。またシーク時間のポイントを設定する意味がある
スナップショットとシークを使いこなして特定期間の再実行を行う機能
スナップショットで再実行する
シークは指定時間か最後のスナップショット以降のサブスク再実行(実際pushでrunが再実行された)
Pubsubにどんなメッセージが入ってきているか確認する方法
pull形式ならAckしなければpullボタンで拾い見れる (トピックでパブリッシュしてサブスクでPull し見る)
トラブルシュートはログを見るかデッドレタートピックかGCSバックアップを見る?
デッドレターキュー(ドロップしたものの確認と救済?)
サブスクでDLQのONしデッドレタートピックを設定し転送する>GCSにもバックアップできる
DLTでメッセージ(実行済みOR未実行)の再生
データ形式:スキーマを使うか、スキーマなしならdataで取得できる
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)")
Posted by funa : 12:00 AM
| Web
| Comment (0)
| Trackback (0)
April 27, 2024
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"
/* */で囲まれた部分はコメント構文
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不要
ワークロードで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はどこに置く
サービス画面の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する
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 Blog1)ノードに紐付いたサービスアカウントKSAをそのまま使用する(裏でimpersonate)
gkeのサービスアカウントとIAMサービスアカウントの紐づけが不要になった
VPCサービスコントロールで管理したい場合impersonateのSAを指定できないためWIFが要る
2)サービスアカウントのキーを Kubernetes Secret として GKE クラスタに登録する
3)Workload Identity Federationをつかう
※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
=============
なぜレッドオーシャン化する前にサービスを グロースできなかったのか? - フリマアプリ編 - (フリル)サービスを急拡大させる意思決定が遅く競合に遅れ
競合出現後も経営方針を大きく変えなかった
勝利条件はユーザ数で機能差ではなかった
パワープレーでいかにプロモーションばら撒いて認知広げて第一想起をとるかだった
先行者優位で過ごせる期間は短い
スタープレイヤーの採用、手数料無料化、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 CloudMODEL_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を紐づけして実行することになる
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 を使う
■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日あたりのリクエスト数:
- プロジェクトごとに1日100万リクエスト(デフォルト)
これらの制限を超えると、リクエストが拒否されるか、APIを利用できなくなることがあります。
2. Google Drive API の利用上限
- ユーザーごとの100秒あたりのリクエスト数:
- プロジェクトごとの1日あたりのリクエスト数:
- ユーザーごとのデータ転送量の制限:
- 読み込みは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
JSのfetch,xhr/iframe/canvas/WebStorage,IndexedDBでクロスオリジンは危険
Access-Control-Allow-OriginレスポンスヘッダでCORS(cross origin resource sharing)許可を判定できる
CSP(Content-Security-Policy)レスポンスヘッダあるはmetaタグで許可するJSを判定できる
=========================
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の違いとは? | OktaOath: 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.htmlXSS対策
フォーム送信後の確認画面では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
バッククォート以外は分かり易いで文字表記でエスケープする、改行はエスケープしない
< < < 不等号(より小さい)
> > > 不等号(より大きい)
& & & アンパサンド
" " " 二重引用符
' ' ' シングルクォート,アポストロフィ
; ;: ; セミコロン
\ \ &bsol バックスラッシュ
` ` バッククォート
========
■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)