如何使用 K8S 自動化定期 CronJob 抓網路公開資料


Posted by KD Chang on 2019-06-29

如何使用 K8S 自動化定期 CronJob 抓網路公開資料

前言

有使用 Linux 的讀者就知道,若是有定期需要執行的程式就可以 Crontab 把寫好的 script 透過定期的 scheduler 定期執行節省人力。一般常見的使用範疇就是定期更新檔案資料或是網路爬蟲等。今天我們則是要介紹,如何使用 Kubernetes(k8s) 的 CronJob 來自動化抓取網路公開資料(這邊我們使用政府公開資料的雨量資料 JSON 檔案),我們想要的定期執行程式的效果。好的,那就讓我們開始吧!

環境設定

若是對於 Kubernetes(k8s)比較不熟悉的讀者可以想成是 Kubernetes(k8s)是一個大型的 container 調度和管理工具,透過 config 設定可以管理你的 dockerize 後的 application。

在這篇文章中我們會使用 minikube 這個 local 開發測試用的 Kubenetes(k8s)cluster 當作測試 demo 使用。若你的電腦還沒有安裝 Kubernetes(k8s)的相關環境的話,可以先參考官方網站的教學我們之前的教學文章

這邊我們使用 macOS 當作範例,需要安裝的有 virtual box、kubernetes-cli 和 minikube 並登入好你的 docker hub 帳戶

確認一下若是你的 minikube 已經 start 成功,可以使用下列指令確認是否正常啟動:

$ kubectl cluster-info

另外也可以安裝 kubectx 這個好用小工具,方便你切換到不同 cluster,這邊我們要切換到 minikube。

撰寫 CronJob 程式和 Dockerfile

因為範例為求簡單,這邊我們使用 Python 撰寫一個簡單每分鐘定期抓取政府公開資料的 python 程式,主要功能為:

  1. 抓取網路公開資料
  2. 根據時間儲存成 {datatime}.json 檔案到 /data 資料夾下

範例程式 app.py

import json
from datetime import datetime
import glob

import requests


def main():
    url = 'https://opendata.cwb.gov.tw/fileapi/v1/opendataapi/O-A0002-001?Authorization=rdec-key-123-45678-011121314&format=JSON'
    resp = requests.get(url)
    data = resp.json()

    with open('/data/{}.json'.format(datetime.utcnow()), 'w') as f:
        json.dump(data, f)

if __name__ == '__main__':
    main()

參考 Dockerfile

FROM python:3.7-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

若是完成後可以透過將我們的程式打包成 docker image 然後上傳到 docker hub 上,讓之後的 k8s cronjob 可以抓下來(當然若是私人專案或是公司專案可以考慮使用 google cloud registry 來 host 你的 image)。

打包 image 檔案(xxxx 為你的 docker hub id,k8s-cronjob-pvc-example 為 image 名稱,v1 為 tag 名稱):

docker build -t xxxx/k8s-cronjob-pvc-example:v1

若是完成後可以使用 $ docker image list 觀看是否有正常顯示

接著就要推送到 docker hub 上面:

$ docker push xxxx/k8s-cronjob-pvc-example:v1

成功後應該就可以在你自己的 docker hub 上面看到上傳的 image 檔案!

撰寫 k8s CronJob config 檔案

上傳 image 到 docker hub 後,我們要開始將我們的程式 deploy 到 minikube 這個 local k8s cluster 上面!

首先我們先定義 schedule 格式為:*/1 * * * * (每分鐘執行一次)

cron 主要格式 * * * * * 就是由左到右分別為:

  • 分鐘
  • 小時
  • 每月中第幾天
  • 星期幾

若是你對於 cronjob 格式比較不熟悉,可以參考這個網站,他可以透過輸入你的設定值告訴目前格式的效果,十分方便!

以下是參考的 cronjob.yaml 檔案:

  1. 我們設定 apiVersion 版本和 k8s config 類型 kind 為 CronJob
  2. 從 kdchang/k8s-cronjob-pvc-example:v1 抓下來我們的程式
  3. 透過 /bin/sh 指令列印出 ls /data 寫入檔案列表
  4. 值得注意的是由於 k8s 資源利用的設計,每次 pod 重啟不一定會是在同一個 node 上部屬,另外隨著 pod 的重啟在 local 的檔案生命週期也會隨之消失。這對於定期產生的 pod 完成後就回收的 CronJob 來說會是一個問題:因為我們想要我們定期抓下來的網路資料可以持續存在。關於這個問題我們可以使用寫入資料庫或是宣告 k8s persistent volume 並掛載檔案路徑到 CronJob 中來解決。

以下我們宣告 hostPath volumes 並把 volume mount 到 /data 下面(hostPath 你可以想成若是 pod 重啟,會去找到上次的 pod 的檔案路徑和保留的檔案,當實務上會造成 pod 沒辦法有效 deploy 到適合的 node 上,此處因為使用 minikube 測試所以沒使用 GCP、AWS等 persistent volume 和外掛檔案系統)。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: k8s-cronjob-pvc-example
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: k8s-cronjob-pvc-example
            image: kdchang/k8s-cronjob-pvc-example:v1
            args:
            - /bin/sh
            - -c
            - ls /data
            - date; echo Hello from the Kubernetes cluster
            volumeMounts:
            - mountPath: /data
              name: crawl-data
          restartPolicy: OnFailure
          volumes:
          - name: crawl-data
            hostPath:
              # directory location on host
              path: /data
              # this field is optional
              type: Directory

接著執行我們的 cronjob.yaml

$ kubectl create -f cronjob.yaml
cronjob.batch/k8s-cronjob-pvc-example created

此時使用以下指令應該就會看到 cronjob 開始執行

$ kubectl get cronjob
NAME                      SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
k8s-cronjob-pvc-example   */1 * * * *   False     0        <none>          11s

一分鐘後看到 cronjob pod 成功開始執行!

$ kubectl get pod
NAME                                       READY   STATUS      RESTARTS   AGE
k8s-cronjob-pvc-example-1561892400-cbpc4   0/1     Completed   0          75s
k8s-cronjob-pvc-example-1561892460-t9ckq   0/1     Completed   0          15s

觀看 log

$ kubectl logs -f k8s-cronjob-pvc-example-1561892400-cbpc4
2019-06-29 10:27:57.140583.json
2019-06-29 10:27:57.492762.json
2019-06-29 10:27:57.737316.json
2019-06-29 10:27:58.996981.json
2019-06-29 10:27:59.125029.json
2019-06-29 10:27:59.140703.json
2019-06-29 10:28:12.885361.json
2019-06-29 10:28:12.942726.json
2019-06-29 10:28:15.883667.json
2019-06-29 10:28:40.844267.json
2019-06-29 10:28:43.823896.json
2019-06-29 10:28:44.787840.json
2019-06-29 10:28:47.097306.json
2019-06-29 10:28:48.122653.json
2019-06-29 10:29:02.764501.json
2019-06-29 10:29:21.785981.json
2019-06-29 10:29:29.779006.json
2019-06-29 10:29:35.822945.json
2019-06-29 10:30:10.770817.json
2019-06-29 10:30:53.822591.json
2019-06-29 10:31:09.812561.json

若你要移除的話可以使用以下指令:

$ kubectl delete cronjob k8s-cronjob-pvc-example
cronjob.batch "k8s-cronjob-pvc-example" deleted

總結

以上簡單介紹了如何使用 K8S 自動化定期 CronJob 抓網路公開資料。有許多開發者在從裸機 bare metal server 轉換到 Kubenetes(k8s) 的過程中常常會覺得 deubg 不太習慣,主要原因就是原本可以隨便 ssh 進去主機和抓取最新的資料並重啟機器的簡單粗暴方式變得麻煩,但若是能克服這一點的話,就能享受 dockerize 的可攜性和 k8s 的簡單擴展和部屬特性,更加專注在業務邏輯上。我們下回見囉,掰撲!

參考文件

  1. Running Automated Tasks with a CronJob
  2. Kubernetes, Docker, and Cron

關於作者:
@kdchang 文藝型開發者,夢想是做出人們想用的產品和辦一所心目中理想的學校。A Starter & Maker. JavaScript, Python & Arduino/Android lover.:)


#Python #k8s #cronjob #Kubernetes #crontab









Related Posts

React hook form(4) - useFormContext & useFormState & useWatch

React hook form(4) - useFormContext & useFormState & useWatch

Week1 筆記| [CMD101] Command Line 學習筆記

Week1 筆記| [CMD101] Command Line 學習筆記

[ week 4 ] HTTP協定

[ week 4 ] HTTP協定




Newsletter




Comments