.env를 crontab에서 인식하지 못하는 문제 수정
This commit is contained in:
22
.env.example
22
.env.example
@ -1,12 +1,19 @@
|
||||
# =====================================
|
||||
# 필수 설정 항목 (반드시 작성해야 함)
|
||||
# =====================================
|
||||
|
||||
# 게시판 설정
|
||||
BOARD_ID=news
|
||||
BOARD_CA_NAME=레이니데이
|
||||
BOARD_CONTENT=08:00 기상청 단기예보
|
||||
BOARD_CA_NAME=카테고리명
|
||||
BOARD_CONTENT=글 내용
|
||||
BOARD_MB_ID=user_id
|
||||
BOARD_NICKNAME=user_nickname
|
||||
|
||||
# 데이터베이스 설정
|
||||
DB_HOST=db.example.com
|
||||
# Docker 환경: DB_HOST는 docker-compose의 service name 사용 (예: db, mysql)
|
||||
# 일반 환경: 실제 MySQL 호스트 IP/도메인 사용 (예: 192.168.1.100, db.example.com)
|
||||
# Synology 환경: 호스트 IP 또는 도메인 명시 (localhost 사용 불가)
|
||||
DB_HOST=db
|
||||
DB_USER=db_username
|
||||
DB_PASSWORD=db_password
|
||||
DB_NAME=database_name
|
||||
@ -18,10 +25,15 @@ FTP_USER=ftp_username
|
||||
FTP_PASSWORD=ftp_password
|
||||
FTP_UPLOAD_DIR=/data/file/news/
|
||||
|
||||
# 날씨 API 서비스 키
|
||||
# 날씨 API 서비스 키 (기상청 API)
|
||||
SERVICE_KEY=your_weather_api_key_here
|
||||
|
||||
# Mattermost 알림 설정
|
||||
# =====================================
|
||||
# 선택적 설정 항목 (없어도 실행 가능)
|
||||
# =====================================
|
||||
|
||||
# Mattermost 알림 설정 (오류 발생 시 알림 받기 원할 때 설정)
|
||||
# URL 반드시 http:// 또는 https://로 시작해야 함
|
||||
MATTERMOST_URL=https://mattermost.example.com
|
||||
MATTERMOST_TOKEN=your-personal-access-token
|
||||
MATTERMOST_CHANNEL_ID=channel_id
|
||||
|
||||
211
README.md
211
README.md
@ -1,41 +1,206 @@
|
||||
## 퍼스트가든용 기상청 API를 활용해 공지사항 자동 등록하는 이미지
|
||||
기상정보에 따른 이벤트 진행에 대한 정확한 기준 부여를 위해 기상청 API를 사용, 영업시간 내 강수정보를 파악하고 해당 공지를 올리기 위한 프로젝트.
|
||||
## 퍼스트가든용 기상청 API를 활용해 공지사항 자동 등록 시스템
|
||||
|
||||
### weather.py
|
||||
- 기상청 API를 활용해 데이터 출력
|
||||
기상정보에 따른 이벤트 진행에 대한 정확한 기준 부여를 위해 기상청 API를 사용하여, 영업시간 내 강수정보를 파악하고 자동으로 공지를 올리기 위한 프로젝트.
|
||||
|
||||
### weather_capture.py
|
||||
- 기상청 날씨누리 단기예보 페이지를 캡처하여 이미지로 저장
|
||||
- 클레임 방지를 위해 '최근발표시각'을 표시하여 캡처함
|
||||
---
|
||||
|
||||
### gnu_autoupload.py
|
||||
- 위 파일들의 데이터를 그누보드 게시판에 등록하는 일을 수행
|
||||
## 📁 폴더 구조
|
||||
|
||||
### config.sample.py
|
||||
- 환경정보 저장(DB정보 등), config.py 로 파일명 변경하여 사용
|
||||
|
||||
### 폴더 구조
|
||||
```
|
||||
project-root/
|
||||
├── data/ # sqlite DB, 캡처 이미지 저장 경로 (공용 볼륨)
|
||||
├── logs/ # 크론 실행 로그 저장 경로
|
||||
│ └── cron.log # Crontab 실행 로그
|
||||
│
|
||||
├── data/ # SQLite DB, 캡처 이미지 저장 경로 (공용 볼륨)
|
||||
│ ├── weather.sqlite # 날씨 DB (precipitation, summary 테이블)
|
||||
│ └── weather_capture_YYYYMMDD.png # 일자별 날씨 캡처 이미지
|
||||
│
|
||||
├── autouploader/ # gnu-autouploader 앱 소스
|
||||
├── app/ # gnu-autouploader 앱 소스 (Dockerfile에서 복사)
|
||||
│ ├── gnu_autoupload.py # 메인 실행 스크립트 (Selenium → FTP → DB)
|
||||
│ ├── weather_capture.py # Selenium 기반 날씨 이미지 캡처
|
||||
│ ├── weather.py # 기상청 API 데이터 처리 및 sqlite 저장
|
||||
│ ├── config.py # 설정값 (DB, FTP, API KEY 등)
|
||||
│ └── run.sh # 자동 실행용 셸 스크립트 (cron에서 호출)
|
||||
│ ├── send_message.py # Mattermost 알림 발송
|
||||
│ ├── selenium_manager.py # Selenium 브라우저 관리
|
||||
│ ├── config.py # 설정값 로드 (DB, FTP, API KEY 등)
|
||||
│ ├── requirements.txt # Python 의존성
|
||||
│ └── run.sh # 수동 실행용 셸 스크립트 (개발 시 사용)
|
||||
│
|
||||
├── webhook/ # 카카오 챗봇 응답 서버
|
||||
│ ├── webhook.py # Flask 기반 응답 서버
|
||||
│ └── config.py # 환경 설정 (예: IMAGE_SERVER_URL)
|
||||
├── webhook/ # Synology Chat 웹훅 응답 서버
|
||||
│ └── webhook.py # Flask 기반 응답 서버
|
||||
│
|
||||
├── build/
|
||||
│ ├── autouploader/
|
||||
│ │ └── Dockerfile # gnu-autouploader용 Dockerfile
|
||||
│ ├── app/
|
||||
│ │ ├── Dockerfile # gnu-autouploader 컨테이너 이미지
|
||||
│ │ └── run.sh # (위의 app/run.sh와 동일)
|
||||
│ └── webhook/
|
||||
│ └── Dockerfile # webhook 서버용 Dockerfile
|
||||
│
|
||||
├── docker-compose.yml # 전체 서비스 구성 정의
|
||||
├── .env.example # 환경 변수 템플릿 (.env로 복사하여 수정)
|
||||
├── docker-compose.yml # Docker Compose 서비스 정의
|
||||
└── README.md # 프로젝트 문서
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 주요 스크립트 설명
|
||||
|
||||
### `app/gnu_autoupload.py` (메인)
|
||||
- **역할**: 날씨 캡처 → FTP 업로드 → 그누보드 DB에 게시글 등록
|
||||
- **실행 방식**:
|
||||
- 매일 09:00 Crontab 자동 실행
|
||||
- `docker exec` 또는 `run.sh`로 수동 실행 가능
|
||||
- **오류 발생 시**: Mattermost으로 알림 발송
|
||||
|
||||
### `app/weather_capture.py`
|
||||
- Selenium을 사용해 기상청 날씨누리 웹 페이지 캡처
|
||||
- '최근발표시각' 표시 (출처 명시)
|
||||
|
||||
### `app/weather.py`
|
||||
- 기상청 API에서 시간별 강수량 데이터 수집
|
||||
- 10:00 ~ 22:00 영업시간 강수 데이터 HTML 테이블 생성
|
||||
- SQLite DB에 저장
|
||||
|
||||
### `app/config.py`
|
||||
- 환경 변수 로드 (`.env` 또는 컨테이너 환경 변수)
|
||||
- 필수 변수 부재 시 즉시 오류 출력 후 종료
|
||||
|
||||
---
|
||||
|
||||
## 🚀 설치 & 실행
|
||||
|
||||
### 1. 환경 설정
|
||||
|
||||
```bash
|
||||
# .env 파일 생성
|
||||
cp .env.example .env
|
||||
|
||||
# 필수 정보 입력
|
||||
vim .env
|
||||
```
|
||||
|
||||
### 필수 환경 변수
|
||||
|
||||
```env
|
||||
# 게시판 정보
|
||||
BOARD_ID=news
|
||||
BOARD_CA_NAME=카테고리명
|
||||
BOARD_CONTENT=글 내용
|
||||
BOARD_MB_ID=user_id
|
||||
BOARD_NICKNAME=닉네임
|
||||
|
||||
# MySQL 연결 (로컬: localhost, Docker: db 서비스명, Synology: 실제 호스트 IP/도메인)
|
||||
DB_HOST=localhost
|
||||
DB_USER=db_user
|
||||
DB_PASSWORD=db_password
|
||||
DB_NAME=database_name
|
||||
|
||||
# FTP 업로드
|
||||
FTP_HOST=ftp.example.com
|
||||
FTP_USER=ftp_user
|
||||
FTP_PASSWORD=ftp_password
|
||||
FTP_UPLOAD_DIR=/data/file/news/
|
||||
|
||||
# 기상청 API
|
||||
SERVICE_KEY=your_api_key_here
|
||||
|
||||
# Mattermost 알림 (선택사항)
|
||||
MATTERMOST_URL=https://mattermost.example.com
|
||||
MATTERMOST_TOKEN=token
|
||||
MATTERMOST_CHANNEL_ID=channel_id
|
||||
```
|
||||
|
||||
### 2. Docker Compose 실행
|
||||
|
||||
```bash
|
||||
# 빌드 및 실행
|
||||
docker-compose up -d
|
||||
|
||||
# 로그 확인
|
||||
docker-compose logs -f gnu-autouploader
|
||||
|
||||
# 크론 실행 로그 확인
|
||||
docker exec gnu-autouploader tail -f /logs/cron.log
|
||||
```
|
||||
|
||||
### 3. 수동 실행
|
||||
|
||||
```bash
|
||||
# 컨테이너에서 직접 실행
|
||||
docker exec -it gnu-autouploader /usr/bin/python /app/gnu_autoupload.py
|
||||
|
||||
# 또는 run.sh 사용
|
||||
docker exec -it gnu-autouploader /app/run.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 크론탭 설정
|
||||
|
||||
Docker 컨테이너 내부에서 매일 **09:00**에 자동 실행됩니다.
|
||||
|
||||
```
|
||||
0 9 * * * /usr/bin/python /app/gnu_autoupload.py >> /logs/cron.log 2>&1
|
||||
```
|
||||
|
||||
**로그 확인:**
|
||||
```bash
|
||||
docker exec gnu-autouploader tail -f /logs/cron.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 문제 해결
|
||||
|
||||
### MySQL 연결 오류
|
||||
- **오류**: `Can't connect to MySQL server on 'localhost'`
|
||||
- **원인**: Docker 컨테이너에서 localhost는 컨테이너 자신을 가리킴
|
||||
- **해결**:
|
||||
- Docker 환경: `DB_HOST=db` (docker-compose 서비스명)
|
||||
- Synology: `DB_HOST=192.168.x.x` (호스트 IP)
|
||||
|
||||
### Mattermost 알림 실패
|
||||
- **오류**: `Invalid URL '/api/v4/posts': No scheme supplied`
|
||||
- **원인**: URL에 `http://` 또는 `https://`가 없음
|
||||
- **해결**: `MATTERMOST_URL=https://mattermost.example.com` 명시
|
||||
|
||||
### 크론탭에서 환경 변수 미로드
|
||||
- **원인**: 기존 버전에서 crontab이 `.env` 파일 접근 불가
|
||||
- **해결**: 현재는 `docker-compose.yml`에서 `.env`를 volume mount + env_file로 처리
|
||||
|
||||
---
|
||||
|
||||
## 📝 설정 변경 후 재배포
|
||||
|
||||
```bash
|
||||
# 컨테이너 재시작
|
||||
docker-compose restart gnu-autouploader
|
||||
|
||||
# 또는 재빌드
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 개발 & 디버깅
|
||||
|
||||
### 로컬 테스트 (가상환경)
|
||||
|
||||
```bash
|
||||
# 가상환경 생성
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
|
||||
# 의존성 설치
|
||||
pip install -r app/requirements.txt
|
||||
|
||||
# .env 파일 생성 후 직접 실행
|
||||
python app/gnu_autoupload.py
|
||||
```
|
||||
|
||||
### Docker 내 직접 실행
|
||||
|
||||
```bash
|
||||
docker exec -it gnu-autouploader bash
|
||||
cd /app
|
||||
python gnu_autoupload.py
|
||||
```
|
||||
|
||||
70
app/config.py
Normal file
70
app/config.py
Normal file
@ -0,0 +1,70 @@
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# .env 파일 로드 (python-dotenv 사용)
|
||||
# Docker 환경: docker-compose.yml에서 env_file로 환경 변수 주입 + volume mount로 .env 파일 접근
|
||||
# 로컬 개발 환경: python-dotenv로 .env 파일 로드
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
TODAY = datetime.now().strftime('%Y%m%d')
|
||||
|
||||
|
||||
def _get_required_env(key: str, description: str = "") -> str:
|
||||
"""필수 환경 변수 조회. 없으면 에러 출력 후 종료"""
|
||||
value = os.getenv(key)
|
||||
if not value:
|
||||
desc = f" ({description})" if description else ""
|
||||
error_msg = f"[ERROR] 필수 환경 변수가 설정되지 않았습니다: {key}{desc}"
|
||||
print(error_msg)
|
||||
sys.exit(1)
|
||||
return value
|
||||
|
||||
|
||||
def _get_optional_env(key: str) -> str:
|
||||
"""선택적 환경 변수 조회. 없으면 빈 문자열 반환"""
|
||||
return os.getenv(key, '')
|
||||
|
||||
|
||||
# 게시판 설정 (필수)
|
||||
MAIN = {
|
||||
'board': _get_required_env('BOARD_ID', '게시판 ID'),
|
||||
'ca_name': _get_required_env('BOARD_CA_NAME', '게시판 카테고리'),
|
||||
'subject': '',
|
||||
'content': _get_required_env('BOARD_CONTENT', '게시판 기본 내용'),
|
||||
'mb_id': _get_required_env('BOARD_MB_ID', '게시자 ID'),
|
||||
'nickname': _get_required_env('BOARD_NICKNAME', '게시자 닉네임'),
|
||||
'file1': '',
|
||||
'file2': '',
|
||||
}
|
||||
|
||||
# 데이터베이스 설정 (필수)
|
||||
DB_CONFIG = {
|
||||
'HOST': _get_required_env('DB_HOST', 'MySQL 호스트'),
|
||||
'USER': _get_required_env('DB_USER', 'MySQL 사용자명'),
|
||||
'DBNAME': _get_required_env('DB_NAME', 'MySQL 데이터베이스명'),
|
||||
'PASS': _get_required_env('DB_PASSWORD', 'MySQL 비밀번호'),
|
||||
'CHARSET': _get_optional_env('DB_CHARSET') or 'utf8mb4',
|
||||
}
|
||||
|
||||
# FTP 설정 (필수)
|
||||
FTP_CONFIG = {
|
||||
'HOST': _get_required_env('FTP_HOST', 'FTP 호스트'),
|
||||
'USER': _get_required_env('FTP_USER', 'FTP 사용자명'),
|
||||
'PASS': _get_required_env('FTP_PASSWORD', 'FTP 비밀번호'),
|
||||
'UPLOAD_DIR': _get_required_env('FTP_UPLOAD_DIR', 'FTP 업로드 디렉토리'),
|
||||
}
|
||||
|
||||
# 날씨 API 서비스 키 (필수)
|
||||
serviceKey = _get_required_env('SERVICE_KEY', '기상청 API 서비스 키')
|
||||
|
||||
# Mattermost 설정 (선택적 - 없어도 실행 가능하지만 알림은 미발송)
|
||||
MATTERMOST_CONFIG = {
|
||||
'URL': _get_optional_env('MATTERMOST_URL'),
|
||||
'TOKEN': _get_optional_env('MATTERMOST_TOKEN'),
|
||||
'CHANNEL_ID': _get_optional_env('MATTERMOST_CHANNEL_ID'),
|
||||
}
|
||||
@ -40,10 +40,23 @@ logger = logging.getLogger(__name__)
|
||||
def init_message_sender():
|
||||
"""Mattermost 메시지 발송기 초기화"""
|
||||
try:
|
||||
mattermost_url = MATTERMOST_CONFIG.get('URL', '')
|
||||
mattermost_token = MATTERMOST_CONFIG.get('TOKEN', '')
|
||||
mattermost_channel_id = MATTERMOST_CONFIG.get('CHANNEL_ID', '')
|
||||
|
||||
# 설정 확인 로그
|
||||
logger.info(f"Mattermost 설정: URL={'설정됨' if mattermost_url else '미설정'}, "
|
||||
f"TOKEN={'설정됨' if mattermost_token else '미설정'}, "
|
||||
f"CHANNEL_ID={'설정됨' if mattermost_channel_id else '미설정'}")
|
||||
|
||||
if not mattermost_url:
|
||||
logger.warning("Mattermost URL이 설정되지 않았습니다")
|
||||
return None
|
||||
|
||||
msg_sender = MessageSender(
|
||||
mattermost_url=MATTERMOST_CONFIG.get('URL', ''),
|
||||
mattermost_token=MATTERMOST_CONFIG.get('TOKEN', ''),
|
||||
mattermost_channel_id=MATTERMOST_CONFIG.get('CHANNEL_ID', '')
|
||||
mattermost_url=mattermost_url,
|
||||
mattermost_token=mattermost_token,
|
||||
mattermost_channel_id=mattermost_channel_id
|
||||
)
|
||||
return msg_sender
|
||||
except Exception as e:
|
||||
@ -184,6 +197,9 @@ def write_board(board, subject, content, mb_id, nickname, ca_name=None, file_lis
|
||||
"""
|
||||
conn = None
|
||||
try:
|
||||
# DB 연결 정보 디버깅 로그
|
||||
logger.info(f"DB 연결 시도: HOST={DB_CONFIG['HOST']}, USER={DB_CONFIG['USER']}, DB={DB_CONFIG['DBNAME']}")
|
||||
|
||||
conn = pymysql.connect(
|
||||
host=DB_CONFIG['HOST'],
|
||||
user=DB_CONFIG['USER'],
|
||||
@ -46,6 +46,11 @@ class MessageSender:
|
||||
|
||||
def _send_to_mattermost(self, message: str, use_webhook: bool):
|
||||
try:
|
||||
# Mattermost URL 검증
|
||||
if not self.mattermost_url or not self.mattermost_url.startswith(('http://', 'https://')):
|
||||
print(f"[ERROR] Mattermost URL이 유효하지 않습니다: {self.mattermost_url}")
|
||||
return False
|
||||
|
||||
if use_webhook:
|
||||
response = requests.post(
|
||||
self.mattermost_url,
|
||||
@ -1,62 +0,0 @@
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# .env 파일 로드 (python-dotenv 사용)
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
TODAY = datetime.now().strftime('%Y%m%d')
|
||||
|
||||
# 게시판 설정
|
||||
MAIN = {
|
||||
'board': os.getenv('BOARD_ID', 'news'),
|
||||
'ca_name': os.getenv('BOARD_CA_NAME', '레이니데이'),
|
||||
'subject': '',
|
||||
'content': os.getenv('BOARD_CONTENT', '08:00 기상청 단기예보'),
|
||||
'mb_id': os.getenv('BOARD_MB_ID', 'user_id'),
|
||||
'nickname': os.getenv('BOARD_NICKNAME', 'user_nickname'),
|
||||
'file1': '',
|
||||
'file2': '',
|
||||
}
|
||||
|
||||
# 데이터베이스 설정
|
||||
DB_CONFIG = {
|
||||
'HOST': os.getenv('DB_HOST', 'localhost'),
|
||||
'USER': os.getenv('DB_USER', 'db_user'),
|
||||
'DBNAME': os.getenv('DB_NAME', 'database'),
|
||||
'PASS': os.getenv('DB_PASSWORD', 'password'),
|
||||
'CHARSET': os.getenv('DB_CHARSET', 'utf8mb4'),
|
||||
}
|
||||
|
||||
# FTP 설정
|
||||
FTP_CONFIG = {
|
||||
'HOST': os.getenv('FTP_HOST', 'ftp.example.com'),
|
||||
'USER': os.getenv('FTP_USER', 'ftp_user'),
|
||||
'PASS': os.getenv('FTP_PASSWORD', 'ftp_password'),
|
||||
'UPLOAD_DIR': os.getenv('FTP_UPLOAD_DIR', '/data/file/news/'),
|
||||
}
|
||||
|
||||
# 날씨 API 서비스 키
|
||||
serviceKey = os.getenv('SERVICE_KEY', '')
|
||||
|
||||
# Mattermost 설정
|
||||
MATTERMOST_CONFIG = {
|
||||
'URL': os.getenv('MATTERMOST_URL', ''),
|
||||
'TOKEN': os.getenv('MATTERMOST_TOKEN', ''),
|
||||
'CHANNEL_ID': os.getenv('MATTERMOST_CHANNEL_ID', ''),
|
||||
}
|
||||
|
||||
|
||||
def write_board():
|
||||
# ... existing code ...
|
||||
conn.close()
|
||||
return True, None # 명시적 반환 추가
|
||||
|
||||
def some_function():
|
||||
# ... existing code ...
|
||||
success, error = capture_image(capture_script, weather_file) # msg_sender 추가
|
||||
if not success:
|
||||
return
|
||||
@ -1,42 +0,0 @@
|
||||
from datetime import datetime
|
||||
|
||||
TODAY = datetime.now().strftime('%Y%m%d')
|
||||
|
||||
# FTP 서버 설정
|
||||
FTP_CONFIG = {
|
||||
'HOST' : 'ftp.example.com',
|
||||
'USER' : 'ftp_username',
|
||||
'PASS' : 'ftp_password',
|
||||
'UPLOAD_DIR' : '/data/file/news/'
|
||||
}
|
||||
|
||||
# 데이터베이스 설정 (MySQL)
|
||||
DB_CONFIG = {
|
||||
'HOST' : 'db.example.com',
|
||||
'USER' : 'db_username',
|
||||
'DBNAME' : 'database_name',
|
||||
'PASS' : 'db_password',
|
||||
'CHARSET': 'utf8mb4',
|
||||
}
|
||||
|
||||
# 게시판 설정 (subject, content, file1, file2는 스크립트 진행중 설정됨)
|
||||
MAIN = {
|
||||
'board' : 'news',
|
||||
'ca_name' : 'category_name',
|
||||
'subject' : '',
|
||||
'content' : '',
|
||||
'mb_id' : 'user_id',
|
||||
'nickname' : 'user_nickname',
|
||||
'file1' : '',
|
||||
'file2' : '',
|
||||
}
|
||||
|
||||
# 날씨 API 서비스 키
|
||||
serviceKey = 'your_weather_api_key_here'
|
||||
|
||||
# Mattermost 알림 설정
|
||||
MATTERMOST_CONFIG = {
|
||||
'URL': 'https://mattermost.example.com',
|
||||
'TOKEN': 'your-personal-access-token',
|
||||
'CHANNEL_ID': 'channel_id',
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
echo "run.sh 실행됨: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
python3 /app/gnu_autoupload.py >> /proc/1/fd/1 2>&1
|
||||
@ -27,20 +27,22 @@ RUN pip install --no-cache-dir --upgrade pip && \
|
||||
ftputil \
|
||||
pillow \
|
||||
pyvirtualdisplay \
|
||||
requests
|
||||
requests \
|
||||
python-dotenv
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN fc-cache -f -v
|
||||
|
||||
# autouploader 디렉토리의 모든 Python 스크립트 및 설정 파일 복사
|
||||
# context: . (프로젝트 루트) 이므로 autouploader/ 에서 복사
|
||||
COPY autouploader/ /app/
|
||||
COPY app/ /app/
|
||||
|
||||
RUN chmod +x /app/run.sh
|
||||
# 로그 디렉토리 생성
|
||||
RUN mkdir -p /logs && chmod 777 /logs
|
||||
|
||||
# crontab 등록 (docker logs에 출력되도록 설정)
|
||||
RUN echo "0 9 * * * /app/run.sh 2>&1" | crontab -
|
||||
# Crontab 설정: 매일 09:00에 절대 경로로 Python 실행
|
||||
# cron은 컨테이너의 환경 변수를 상속받으므로 env_file로 주입된 변수들을 사용 가능
|
||||
RUN echo "0 9 * * * /usr/bin/python /app/gnu_autoupload.py >> /logs/cron.log 2>&1" | crontab - && \
|
||||
chmod 666 /logs
|
||||
|
||||
# Cron 로그를 docker logs로 보내기 위해 포그라운드에서 실행
|
||||
CMD ["/bin/bash", "-c", "cron -f"]
|
||||
# Cron을 포그라운드에서 실행 (docker logs에 출력되도록)
|
||||
CMD ["/usr/sbin/cron", "-f"]
|
||||
@ -1,6 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 로그 출력 함수
|
||||
# 이 스크립트는 수동 실행 시 사용됩니다.
|
||||
# Crontab은 python을 직접 실행하므로 이 스크립트를 거치지 않습니다.
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
@ -9,10 +11,9 @@ log "========================================"
|
||||
log "날씨 정보 자동 게시글 생성 시작"
|
||||
log "========================================"
|
||||
|
||||
# Python 스크립트 실행
|
||||
cd /app
|
||||
if [ -f "gnu_autoupload.py" ]; then
|
||||
python3 /app/gnu_autoupload.py 2>&1
|
||||
/usr/bin/python gnu_autoupload.py 2>&1
|
||||
EXIT_CODE=$?
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
log "✅ 실행 완료 (종료 코드: $EXIT_CODE)"
|
||||
@ -2,12 +2,14 @@ services:
|
||||
gnu-autouploader:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./build/autouploader/Dockerfile
|
||||
dockerfile: ./build/app/Dockerfile
|
||||
image: reg.firstgarden.co.kr/gnu-autouploader:latest
|
||||
container_name: gnu-autouploader
|
||||
volumes:
|
||||
- ./data:/data
|
||||
# - ./autouploader:/app
|
||||
- ./logs:/logs
|
||||
- ./.env:/app/.env:ro
|
||||
# - ./app:/app
|
||||
env_file:
|
||||
- .env
|
||||
restart: unless-stopped
|
||||
|
||||
Reference in New Issue
Block a user