여러 노드에서 사용할 수 있게 곧 PV, PVC를 추가할 예정입니다!
최근 쿠버네티스를 써야하는 프로젝트 두개에서 동시에 같은 문제가 발생했습니다. 둘 다 주기적으로 어떤 페이지를 크롤링해서 가공한 데이터를 파일로 저장해두고 써야했습니다. 주기적인 작업이라서 크론잡을 생각해냈고 크론잡으로 [크롤링→추출 및 가공] 을 수행하는 컨테이너를 주기적으로 실행하고 종료할 계획을 세웠습니다. 크론잡이 끝나면 데이터가 사라질 것을 방지하여 볼륨에 데이터를 저장했습니다! 그래서 두 프로젝트 중 하나에서 쓸 크론잡 수행을 통해 로컬에 데이터 저장하기를 성공해서 신나게 적어보려고 합니다!
이 글은 쿠버네티스 클러스터 환경이 구축되어 있다는 가정 하에 읽어주시면 감사하겠습니다. 😊
이 프로젝트 해야할 일은 HTTP GET 요청으로 .csv와 .json 파일을 가져와서 로컬에 저장하고 해당 파일을 통해 어떠한 검증 과정을 거치는 것이었습니다. 가져올 파일은 주기적으로 업데이트되어 하루에 한 번 정도 GET 요청을 날려 로컬의 파일을 업데이트 하고자 했습니다.
해당 코드는 간단하게 python으로 작성했습니다.
import requests
import os
url_csv = "<url>.csv"
url_json = "<url>.json"
response_csv = requests.get(url_csv)
response_json = requests.get(url_json)
# /data 폴더 존재 확인
os.makedirs('/data', exist_ok=True)
# csv, json 파일 GET 요청에 에러가 없다면 파일 업데이트
if response_csv.status_code == 404 or response_csv.status_code == 429:
# with open('/data/verify_list.csv', 'w', encoding='utf-8') as f:
# f.write('nothing')
print("CSV file NOT Updated")
elif response_csv.status_code == 200:
with open('/data/verify_list.csv', 'w', encoding='utf-8') as f:
f.write(response_csv.text)
print("CSV file Updated")
if response_json.status_code == 404 or response_json.status_code == 429:
# with open('/data/verify_list.json', 'w', encoding='utf-8') as f:
# f.write('nothing')
print("JSON file NOT Updated")
elif response_json.status_code == 200:
with open('/data/verify_list.json', 'w', encoding='utf-8') as f:
f.write(response_json.text)
print("JSON file Updated")
print(response_csv.status_code)
print(response_json.status_code)
해당 코드는 정말 간단하게 파일을 GET 요청으로 가져와서 로컬에 작성하는 일을 수행합니다.
python <file_name>.py
로 해당 코드의 동작을 확인합니다.
쿠버네티스의 CronJob을 실행하려면 실행 코드를 컨테이너 이미지로 전달해야합니다. 그래서 이미지로 컨테이너를 실행하면 위의 파이썬 코드를 실행할 수 있게 Dockerfile을 작성했습니다.
FROM --platform=linux/amd64 python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY get-verify-list.py .
ENTRYPOINT ["python", "get-verify-list.py"]
처음 Dockerfile을 작성했을 때 실행하면 아래와 같은 문제가 발생했습니다.
WARNING: The requested image's platform (linux/arm64) does not match the detected host platform (linux/amd64/v2) and no specific platform was requested
exec /usr/local/bin/python: exec format error
stackoverflow를 참고했을 때 제가 이미지를 빌드한 환경을 arm이고 실행하는 환경이 amd라서 CPU 플랫폼의 차이로 인해 python을 인식하지 못한다는 것을 알게 되었습니다.
[Docker ]Multi-platform images
그래서 FROM에 --platform=linux/amd64
를 추가하여 amd 환경에서 실행할 때 문제가 없게끔 처리했습니다.
그런데 뒤늦게 생각해보니.. 그냥 ubuntu 환경에서 빌드해서 이미지를 만드는 건 어떨까 싶네요.. 하하
docker build -t <이미지_이름>:<태그> --push .
로 이미지를 빌드합니다. 외부 환경에서 해당 이미지를 활용하기 위해 DockerHub에 push하는 작업을 추가했습니다.
이미지가 빌드, 업로드 되었다면
docker run -it --rm -v <로컬_폴더_위치>:/data <이미지_이름>:<태그>
를 통해 해당 로컬 폴더 위치에 파일이 생성되는지 확인합니다.
위의 파이썬 코드에서 404 또는 429가 발생하면 파일이 생성되지 않기 때문에 일시적으로 주석을 해제하여 파일에 'nothing'을 작성하여 생성할 수 있습니다.
위에서 파이썬 코드와 이미지 실행에 문제가 없는 것을 확인했다면 cronjob을 설정하도록 yaml 파일을 작성합니다.
apiVersion: batch/v1
kind: CronJob
metadata:
name: file-updater
spec:
schedule: "0 0 * * *"
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
containers:
- name: file-updater
image: <이미지_이름>:<태그>
volumeMounts:
- name: file-storage
mountPath: /data # 파드 내부 경로
restartPolicy: OnFailure
volumes:
- name: file-storage
hostPath:
path: /usr/data # 로컬 디렉토리 경로
spec.schedule
: 하루에 한 번 실행spec.successfulJobsHistoryLimit
: 성공한 작업의 최대 기록 수spec.failedJobsHistoryLimit
: 실패한 작업의 최대 기록 수spec.containers.volumeMounts.mountPath
: 파드 내부에서 생성되는 경로/data
에 생성volumes.hostPath.path
: 파드에서 생성한 파일을 저장할 로컬 디렉토리 경로kubectl create -f <yaml_파일_이름>.yaml
로 크론잡을 생성합니다.
저는 빠른 테스트를 위해 spec.schedule
을 "* * * * *"
로 바꾸어 1분에 한 번 수행하도록 수정했습니다.
먼저 크론잡을 생성하고 해당 잡이 수행될 때까지 kubectl get jobs --watch
로 기다립니다. 잡이 생성되면 파드가 생성되어 작업을 수행하고 있는 것을 확인할 수 있습니다.
kubectl get po
를 통해 파드 상태를 확인했을 때 다음과 같이 Completed 상태가 된 것을 통해 작업을 수행하고 컨테이너가 종료되었음을 확인할 수 있습니다. 상태가 Error라면 describe, logs 등을 통해 추가적인 트러블슈팅이 필요합니다.
이제 로컬의 /usr/data
로 이동해서 파일이 생성되었는지 확인할 수 있습니다.
404, 429 에러가 많이나는 데이터 링크라 nothing
이 뜨면 캡쳐하려 했는데 데이터가 잘 들어와서 데이터 캡쳐는 생략하도록 하겠습니다..! 😂
가벼운 작업인데 requirements.txt를 놓치거나 플랫폼이나 볼륨에서 작은 문제가 이어져 시간이 꽤 오래 걸렸던 것 같습니다.
언제나 잘못된 점이 있다면 조언, 지적은 환영입니다!