跳到主要內容

發表文章

使用 Terraform + Slack + Cloud Function 快速建立簡易通知系統

 使用 Terraform + Slack + Cloud Function 快速建立簡易通知系統 前言 要設計一個通知系統, 一般都會需要考慮到前端, 後端, 以及後續部署維運相關的問題, 但其實想要快速建立通知系統並不難, 只要善用雲端資源, 十分鐘也能建立一個簡易的通知系統 以下示範, 如何使用 Slack 作為訊息輸出的前端, 以及整合 Cloud PubSub 和 Cloud Function 作為訊息處理的後端, 最後再以 Terraform 將所有元件的部署流程自動化, 使日後維運更加輕鬆簡易 前端 整合 Slack 時, 需要先取得 Webhook Token  有了 token 就能透過 Slack 的 API 將訊息打到指定的 Channel 中, 使用以下方式來測試 Webhook token 有沒有作用 測試 andy@host: curl -X POST --data-urlencode "payload={\"channel\": \"#etl_job\", \"username\": \"webhookbot\", \"text\": \"This is posted to # etl_job  and comes from a bot named webhookbot.\", \"icon_emoji\": \":ghost:\"}" https://hooks.slack.com/services/T02G1HSTG/AAA4M9NN095/1VDjE3453234292ZTF4IRDEv ok 後端 後端的部分會整合 Cloud PubSub 作為觸發 Cloud Function 以習訊息輸入的來源 Source Code const { IncomingWebhook } = require('@slack/webhook'); exports.handler= async (message, context) => {  const messageData = message.data    ? Buffer.from(mes...

如何在 local 端使用 kubectl 操作 private GKE 叢集

如何在 local 端使用 kubectl 操作 private GKE 叢集 Private GKE cluster set up with bastion vm   基於安全考量,  通常在創建 GKE 叢集時, 會選擇 Private Cluster 的部署方式, 將 Control Plan 藏在內網裡, 若想要在 local 端使用 kubectl 來操作 GKE 的話, 會須要一個跳板機(Bastion Host) 將請求 轉送給內網的 Control Plan, 架構如下 然而這個做法有個安全疑慮, 就是當對外 IP 不小心被洩露了, 駭客就能透過它連進跳板機來進行惡意的操作 比較好的做法是,  Cloud IAP + Private 的 Bastion Host  透過 IAP 的TCP 轉送功能, 就不需要分配對外 IP 給 Bastion Host 實際做法如下 環境建立 Step 1. 建立一個新的 Subnet 給 GKE Cluster 使用 gcloud compute networks create k8s-proxy \   --subnet-mode=custom \   --project=backend gcloud compute networks subnets create subnet-cluster \     --network=k8s-proxy \     --range=10.50.0.0/16 \     --region=us-central1 gcloud compute firewall-rules create k8s-proxy-ssh \     --network k8s-proxy \     --allow tcp:22 因為 Cloud IAP 會從  35.235.240.0/20 這個網段連進內網, 所以防火牆需要開放從從 35.235.240.0/20 過來的流量 gcloud compute firewall-rules create allow-ssh-ingress-from-iap...

如何重跑 Cloud Composer 上的任務

如何重跑 Cloud Composer 上的任務 Retry Cloud Composer Task  想要重跑 Cloud Composer 上的任務可以透過 Airflow Cli 來清除任務狀態, 迫使 Airflow 重新排程這個任務 清除指令, 如下 airflow tasks clear -s ${EXECUTION_DATE} -t ${TASK_ID} -d -y ${DAG_ID}   註:EXECUTION_DATE 的格式是以 ISO-8601 來表示, 所以會長這樣 "2022-07-26T00:00:00+00:00" 由於 Cloud Composer 會將 Airflow 部署在 GKE 上, 若要執行上面的指令, 必須 1. 取得 GKE 得 crendential 2. 找到 Airflow 管理工具的 Pod 執行 Airflow 指令 另一個方法是 使用 gcloud composer 來將指令傳給 Airflow  EXECUTION_DATE ="2022-07-26T00:00:00+00:00" DAG_ID="sysops-daily-backup" TASK_ID ="^result_chk$" gcloud composer environments run \ --location="us-central1" \ --project="stage" sysops tasks clear -- {DAG_ID} -s {EXECUTION_DATE}  -t {TASK_ID} -y -d  -d 是連下游任務的狀態都清除 方法二:在 AirFlow DAG 中使用 retry 機制來清除上游任務的狀態 def clear_upstream_task(context):     execution_date = context.get("execution_date")     clear_tasks = BashOperator(         task_id='clear_tasks',         bash_command=f'...

如何在 GKE 上部署 Gitlab Runner (使用 Helm) 並整合 Workload Identity

  如何在 GKE 上部署  Gitlab Runner (使用 Helm) 並整合 Workload Identity Gitlab Runner + Helm + workload Identity to deploy Application on GKE 建立 Service Account 給 Gitlab Runner 使用 如果想讓 Gitlab Runner 可以在 GCP  上部署任何應用程式,  必須給他一組有特定權限的 Service Account Gitlab Runner 要使用這個 Service Account, 比較 Hardcore 一點的做法是, 直接下載 Service Account 的 Credential,  再透過 gcloud 指令拿 Credential 去做認證 gcloud auth activate-service-account $SERVICE_ACCOUNT_EMAIL \     --key-file=$CRED  \     --project=$PROJECT    這個做法的缺點是, 需要煩惱如何安全地保管 Credential  比較安全一點的做法是 透過 GKE Workload Identity 將 GKE 裡的 Service Account 與 GCP 上的 Service Account 連結在一起 假設 Gitlab Runner 架構規劃如下: 在 GCP 上總共有三個 Project (cicd/dev/prod), cicd 的 Project 裡有一座 GKE 用來跑 Gitlab Runner, Runner 會將應用程式部署到 dev, 以及 prod 的 App Engine 上 要實現以上的架構 首先 Step 1. 建立 Google Service Account  # Parameters KSA_NAME="gitlab-runner-pd" RUNNER_GSA_PROJECT="cicd" NAMESPACE="gitlab" RUNNER_NAME="gitlab-runner-pd" TOKEN="eZH45H6uQ...

GPU-based workloads as a part of Airflow DAGs

如何讓在 Airflow 上跑的任務使用 GPU 資源 Step 1. 在既有的 Airflow 下新增有 GPU 的 Pool 使用參數 --accelerator 指定 GPU 的規格 gcloud beta container   node-pools create "gpu-pool" \ --cluster "mlworkflow-24aeef46-gke" \ --zone "us-east1-b" \ --machine-type "n1-standard-4" \ --accelerator "type=nvidia-tesla-p100,count=1" \ --image-type "COS_CONTAINERD" \ --num-nodes "3"  Step 2. 測試 GPU 的功能是否正常 部署一個 Pod 來跑 nvidia-smi 指令 注意: GCP 在建立 GPU 的 Pool 時會自動加上 Taint 資訊如下  spec:   taints:   - effect: NoSchedule     key: nvidia.com/gpu     value: present Pod  的 deployment 要加上 Tolerations cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata:   labels:     run: mig-none-example   name: mig-none-example spec:   tolerations:   - key: "nvidia.com/gpu"     operator: "Exists"     effect: "NoSchedule"   containers:   - image: nvidia/cuda:11.0-base     name: mig-none-example     resou...

在既有 Cloud Storage Bucket 上加入 CORS 設定

  前言  當 Javascript 在存取資源的時候, 瀏覽器會禁止非同源的請求, 除非 Server 上有 CORS  設定讓瀏覽器知道哪些請求是可以允許被發送的 如何在 既有 Cloud Storage Bucket 上加入 CORS 設定 (以下使用 Terraform  來做設定) 假設 Cloud Storage 上有個非 Terraform 建立的 Bucket    首先, 建立資源描述 resource "google_storage_bucket" "andytest123" {   name          = "andytest123"   location      = "US" } Step 2. 把既有的資源設定從雲上讀下來 terraform import google_storage_bucket.image-store andy-prj/andytest123 Step 3. 補上 CORS 設定 resource "google_storage_bucket" "andytest123" {   name          = "andytest123"   location      = "US"   cors {     origin          = ["http://localhost:3000",     "https://web-stage.uc.r.appspot.com",     "https://web.uc.r.appspot.com"     ]     method          = ["GET"]     response_header = ["*"]     max_age_sec...