crontab 으로 스케쥴 잡 등록 시키기

python 과 crontab 을 잘 연결시키면 스케쥴 작업을 완벽하게 만들 수가 있습니다. 간단한 배치 작업들은 굳이 다른 시스템을 사용할 이유가 없습니다.

기본 editor 를 emacs 로 변경하기

$ export EDITOR=emacsclient

아무 설정을 하지 않으면 vi 로 설정되어 있습니다.

기본적인 기능

CRUD 에서 U 는 C 와 동일하니 기능을 살펴보기로 합니다.

입력 (Create)

$ crontab -e

보기 (Read)

$ crontab -l

지우기 (Delete)

$ crontab -d

주기 결정

*          *        *        *       *
분(0-59)  시간(0-23)  일(1-31)  월(1-12)   요일(0-7)

실제 예

* * * * * ls -la

인코딩 관련

crontab 으로 파이썬 스케쥴을 돌릴때 특히나 console 에 한글을 출력하는 경우에 에러가 자주 발생합니다. Unicode 에러 인데요. crontab 은 shell 환경을 가져오지 않기 때문에 발생합니다. 해결 방법은 의외로 간단합니다. crontab 에서 입력 할때 마치 쉘 환경 처럼 적어주면 끝입니다.

   PYTHONENCODING=utf-8
   LANG=ko_KR.UTF-8          
   0 * * * 1-5 source /Users/crazia/miniconda3/bin/activate /Users/crazia/miniconda3/envs/cr-mon/ && 
   /Users/crazia/miniconda3/envs/cr-mon/bin/python /Users/\
crazia/work/python-projects/cr_mon/manage.py stats >> /Users/crazia/work/stats.log 2>&1

miniconda 상에서 cr-mon 이라는 환경을 만들고 django style console 로 만들어서 실행시키고 로그를 남기는 예제 입니다.

일단

http://crazia.org

입니다. 별거 아니지만 누르고 들어가면 https://crazia.org 로 바뀝니다. 정말 너무 일반적인 형태의 웹사이트로 보이지만 나름 최신 기술들로 잘 버무려져 있습니다. http2 도 지원함 ㅋㅋ

1 AWS 의 Certificate Manager 를 이용해서 SSL 인증서를 발급 받았습니다.

  • 혁신적이죠. 기존에는 항상 싼 SSL 인증서를 사서 사용했는데, 여기는 클릭 몇번으로 공짜로 인증서를 받을 수 있습니다.

2 AWS 의 ELB (Elastic LoadBalancer) 를 사용하고 ELB 에 인증서를 포함시켰습니다.

  • 기존에는 NginX 에 인증서를 포함시키는 방법을 썼는데 이제는 이렇게 하는게 편하더군요. 알아서 갱신까지 해줘서 설정만 해주면 문제 없습니다.

3 Nginx 를 사용했습니다.

  • 웹서버는 Nginx 죠!! 아파치는 이제 설정하는 법도 까먹었네요 ㅎㅎ
  • SSL 인증서가 ELB 에 있기 때문에 기존에 SSL 인증서 가진 웹서버 세팅으로는 TOO_MANY_REDIRECT 가 발생할 수가 있습니다.
    server{
       listen 80;
    
       server_name crazia.org;
    
       access_log /var/log/portfolio_access.log;
       error_log /var/log/portfolio_error.log;
    
       if ($http_x_forwarded_proto != 'https') {
         rewrite ^ https://$host$request_uri? permanent;
       }
    
       server_tokens off;
       location / {
             proxy_pass http://localhost:3000/;
             proxy_set_header Host $http_host;
             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
             proxy_set_header X-Forwarded-Proto $scheme;
             proxy_redirect  off;
     }
    
      gzip on;
      gzip_disable "msie6";
    
      gzip_vary on;
      gzip_proxied any;
      gzip_comp_level 6;
      gzip_buffers 16 8k;
      gzip_http_version 1.1;
      gzip_types text/plain text/css application/json \
        application/x-javascript text/xml application/xml \ 
        application/xml+rss text/javascript
    }
    

    이렇게 80 포트만 신경 써주면 됩니다.

4 clojure 를 사용했습니다.

  • 무지하게 마이너 언어지만 제가 사랑하는 언어입니다.
  • 리습의 방언(dialect) 이고 JVM 기반의 프로그래밍 언어입니다.

5 Luminus 를 사용했습니다.

  • clojure 의 웹프레임워크중의 하나입니다. 그나마 자동화가 많이 되어 있고, 프레임워크 구조 설계가 좋아서 종종 애용하고는 합니다.

6 Jetty 임베디드 방식입니다.

  • WAR 를 만들고 앱엔진에 배포하는 방식이 아니라 Jetty 를 임베디드 시켜서 jar 파일 한개로 앱엔진 + WAS 의 기능을 하게 만들어 주는 것입니다.
  • 일반 스크립트 형 WAS 랑 차이가 뭔가? 바로 그 방식과 유사한 것입니다.

7 Bootstrap 을 이용해서 간단한 화면을 구성했습니다.

  • 말이 필요 없는 그 부트스트랩 입니다.
  • HTML, CSS, JS 를 한꺼번에 알맞게 제공해주죠. 저 같은 디자인 고자(?) 도 대충 이쁜 사이트를 구성하는게 가능해집니다.

8 Systemd 를 이용해서 관리합니다.

  • Ubuntu 16.04 부터 자주 이용해야 하는 관리 스크립트입니다.
  • /lib/systemd/system/myapp.service 위치에 스크립트를 만들고
    [Unit]
    Description=My Application
    After=network.target
    
    [Service]
    WorkingDirectory=/var/myapp
    EnvironmentFile=-/var/myapp/env
    Environment="DATABASE_URL=jdbc:postgresql://localhost/app?user=app_user&password=secret"
    ExecStart=/usr/bin/java -jar /var/myapp/myapp.jar
    User=deploy
    
    [Install]
    WantedBy=multi-user.target
    

    이와 같이 만들어주고 저장하고

    sudo systemctl daemon-reload
    sudo systemctl start myapp.service
    

    스크립트를 인식시키고 시작하면 됩니다. start|stop|restart 가 될 것이고요.

    sudo systemctl enable myapp.service
    

    인스턴스가 재부팅시에 자동으로 시작되게 세팅도 가능합니다. (멋진 기능!!)

systemd 와 연동해서 서비스를 구현하는 버릇을 들이다 보니 없으면 피곤하게 됐습니다. 그래서 python 과 uwsgi 와 systemd 를 연결해보자고 해서 시작했습니다.

1 Anaconda 설치하기

이거저거 써봤지만 저는 이게 제일 편한 virtualenv 관리자더군요.

$ wget https://repo.continuum.io/archive/Anaconda3-4.4.0-Linux-x86_64.sh
$ chmod +x Anaconda3-4.4.0-Linux-x86_64.sh
$ Anaconda3-4.4.0-Linux-x86_64.sh

다 설치하고 난 뒤 env 를 만들어 줍니다. app 정도 이름으로 합니다.

$ conda create -n app python=3.6
$ source activate app

2 uwsgi 용 스크립트 만들기

설명이 필요합니까? python 배포의 친구죠.

PROJECT_HOME = /home/[user-id]/[project-name] 이라고 하면 $PROJECT_HOME 에 app.ini 라고 만듭니다.

[user-id] 는 본인 아이디, [project-name] 은 파이선 프로젝트명입니다. 장고(django)를 가정합니다.

[uwsgi]
chdir=/home/[user-id]/[project-name]
module=[project-name].wsgi:application
socket=127.0.0.1:8900
buffer-size=65535
protocol=http
master=true
# daemonize=/home/[user-id]/uwsgi.log
pidfile=/home/[user-id]/uwsgi.pid
processes=1

이제 실행입니다.

$ cd $PROJECT_HOME
$ uwsgi app.ini

3 systemd 용 스크립트 만들기

  1. systemd 용 스크립트 만들기
    $ sudo emacs /lib/systemd/system/[project-name].service
    

    간단하게 프로젝트 이름으로 스크립트를 만들어줍니다. 내용은

    [Unit]
    Description=[project-name] appserver
    After=network.target
    
    [Service]
    User=[user-id]
    WorkingDirectory=/home/[user-id]/[project-name]
    Environment="MYSQL_HOST=127.0.0.1"
    Environment="MYSQL_NAME=[DB name]"
    Environment="MYSQL_USER=[DB user]"
    Environment="MYSQL_PWD=[DB password]"
    Environment="MYSQL_PORT=3306"
    Environment="REDIS_SERVER=127.0.0.1"
    Environment="LOG_LEVEL=DEBUG"
    Environment="IS_DEBUG=True"
    ExecStart=/bin/bash -c 'source /home/[user-id]/anaconda3/bin/activate \
    /home/[user-id]/anaconda3/envs/app/ \
    && /home/[user-id]/anaconda3/envs/app/bin/uwsgi \
    --ini /home/[user-id]/[project-name]/app.ini --die-on-term '
    KillSignal=SIGTERM
    Restart=on-failure
    Type=notify
    StandardError=syslog
    NotifyAccess=all
    
    [Install]
    WantedBy=multi-user.target
    

    Independent WEB 방식이라 환경 변수에 config 내용이 있으니 Environment 로 선언해줍니다. 각각에 자신의 정보를 채워넣으면 되고

    $ sudo systemctl daemon-reload
    $ sudo systemctl enable [project-name].service
    

    시작할때 자동으로 실행되게 등록하고

    $ sudo systemctl start aimserver.service
    $ sudo systemctl stop aimserver.service
    

    이제 시작하고 끄는것이 쉽게 가능해진다.

이번에 스칼라로 프로젝트를 진행하게 되서 급하게 공부하게 되었습니다. 도움이 되는 사이트 2종을 소개합니다. 



자바프로그래머를 위한 스칼라 튜토리얼


쉽게 스칼라의 기본중의 기본만 알게 해주는 훌륭한 페이지 입니다. 


스칼라 학교 


기초부터 중급까지 간단한 사이트까지 만들 수 있게 도와주는 사이트 입니다. 귀찮은 자바랑 같이 프로젝트 만드는 방법등 유용합니다. 추천 !



스칼라로 진행해본 결과는 스칼라로 사이트 만드는 건 좀.. 공부하는 건 모르는데 상용 사이트는 좀 에러가 있군요. 

가상환경에서는 Matplotlib 이 제대로 동작하지 않기 때문에 OSX 환경에서 사용하는 팁이 존재한다. 


The default python provided in (Ana)Conda is not a framework build. However, the Conda developers have made it easy to install a framework build in both the main environment and in Conda envs. To use this install python.app conda install python.app and use pythonw rather than python


위와 같이 이용한다. 즉 python.app 을 conda 를 이용해서 설치하고 pythonw 이용해서 실행시키면 된다. 


Slide로 이동하기



Emacs 의 Org 모드를 이용해서 간단하게 제작한 Slide 이다. 추후에 KeyNote 버젼을 만들기 전 아이디어 정리 단계로 제작해 본 것이다.  



클라우드 서비스의 종류

클라우드 서비스를 이용해서 자체 서비스를 개발하는 것은 이제 흔한 일이 됐습니다. 저는 처음 클라우드가 만들어졌을 때 대체 이걸 어떻게 상용화 할까? 라고 생각했지만 무엇인가를 팔고 이윤을 남기기 위한 인간의 욕망을 무시하면 안된다는 것을 잘 알게 됐습니다.

그렇다면 클라우드의 종류인 IaaS (Infrastructure-as-a-Service) 와 PaaS (Platform-as-a-Service) 와 SaaS (Software-as-a-Service) 는 어떻게 차이가 있는가?


(출처: blog.msdn.microsoft.com)




위 그림에서 가장 쉽게 이야기 해주고 있습니다. IaaS 는 Host에 주안을 두는 것이고, PaaS 는 build 에 주안점, SaaS 는 consume 입니다. 이보다 더 자세하고 명확하게 알아보기 위해서는 다음 그림에서 확실히 소개하고 있습니다.


(출처: blog.msdn.microsoft.com)




위 그림이 가장 클라우드의 핵심입니다.

Packaged Software

이 경우는 보통 IDC (Internet Data Center) 에 직접 서버를 두고 관리를 하는 경우에 해당합니다. 관리해야 할 내용이 많습니다. 네트워크도 설정해야 하고, 저장소 크기도 상태 확인해 가면서 키워야 하고 서버도 직접 관리 등등 모든것을 직접 관리해야 합니다. 보통 이런 경우에 전문적으로 관리하는 SE (System Engineer) 가 필요한 경우가 많습니다. 조그만 회사에서는 서버 개발자가 전부 관리를 해야 합니다.

클라우드 서비스 전의 IT 회사는 대부분 이런 형태의 서버군을 배포환경으로 구성해야 했습니다.

IaaS

이 경우에는 IDC 에서 해야할 일이 전부 사라진 경우입니다. 그 일과 함께 SE 가 해야할 일도 하드웨어 사이드의 일이 전부 사라집니다. 나쁘게 말하자면 개발자가 어느정도 서버 (보통은 리눅스)의 관리를 할 줄 알게 된다면 SE 가 전혀 필요 없는 경우가 생깁니다.

서버의 인스턴스를 마우스 클릭질 몇번으로 생성하고 내가 필요한 어플리케이션을 서버에 올려서 바로 배포가 가능합니다. 배포를 염두에 뒀을 때 하드웨어 적으로 고려할 사항이 극도적으로 적어졌습니다.

PaaS

이 경우는 IaaS 보다 더 극단적으로 쉬워진 경우입니다. 배포 세팅에 대한 고려도 거의 안합니다. 쉽게 말하면 서버가 구동하기 위한 실행 로직 (흔히들 말하길 Business Logic)만 신경쓰면 됩니다. 이쯤 되면 SE 는 필요하지 않습니다. 개발자는 개발 로직만 만들어서 PaaS 에 올리면 나머지는 클라우드가 알아서 확장이나 상태를 관리하기 쉽게 해줍니다.

SaaS

개발도 필요없는 이미 만들어진 서비스를 이용하는 경우입니다. SE 뿐만 아니라 개발자도 필요없습니다.

어떤 플랫폼이 좋았는가?

사실 이야기 하고 싶은 것은 이 주제였습니다.

만들어야 하는 사이트는 웹 소설 플랫폼이였습니다. 개발 기간을 짧게 잡고 있었기 때문에 주요 개발 언어로는 Python 을 선택했고, 웹프레임워크로는 Django 를 선택했습니다. 대략 개발에만 집중해야 하는 기간이 4개월 정도 였기 때문에 Java 언어 기반으로 하기에는 시간적 부담이 느껴졌습니다.

기본이 OpenMarket 플랫폼이기 때문에 필요한 부분이


  • CP (Contents Provider) 사이트 영역
  • Customer 영역
  • 결제 부분
  • 정산 부분


이 필요합니다. 보통 플랫폼 개발은 개발자도 많이 투입하고 개발이 오래 걸리지만 사이트의 특성상 빠르게 개발하고 빨리 오픈하고 계속해서 고쳐 나가는 방법을 정했습니다.

개발 플랫폼을 정하는 것이 개발 초기의 가장 핵심적인 결정 사항이였기 때문에 여러모로 고심을 하다가 구글 앱 엔진 으로 선택했습니다. 이를 결정한 이유는 위에서도 언급이 있지만 배포나 시스템 설정등을 고려하지 않고 개발에 집중하고 싶었기 때문입니다. 왜 레진 코믹스는 구글 앱 엔진을 선택했나 라는 슬라이드도 보고, 아는 지인도 레진 코믹스에서 개발자로 있어서 자문도 구할 수 있었기에 흔쾌히 결정을 했습니다.


PaaS(Platform as a Service)인 구글 앱 엔진은 일단 신경 쓸게 별로 없습니다. 앱 엔진용 SDK 를 받아서 그걸 이용해서 코딩을 하고 베포와 운영은 아주 쉽습니다. 다만 앱 엔진용 SDK 를 공부하고 익숙해져야 하는 단점이 존재했습니다. 빠르게 프로토타입을 만들때는 정말 좋을꺼라고 생각을 하지만 뭔가 대용량의 시스템을 만들어 갈때는 앱 엔진에 맞춰서 개발해야 하는 점이 부담이 되고 잘못됐을 때 바로잡을 레퍼런스가 부족하다는 단점도 큽니다. 개발자들은 구글 앱엔진용 SDK 가 별로라고 원성이 자자했습니다. 그리고 무엇보다 한국에 IDC 가 없기 때문에 무지하게 느렸습니다. 처음부터 멤캐쉬를 고려하고 개발을 시작해야 한다는 문제입니다. (실제로 레진 코믹스도 느린 속도때문에 고생했었나 봅니다). 그러나 무엇보다 좋은 점은 검색 엔진을 구글것을 쓸 수 있다는 점이 장점이였습니다.


초기의 개발은 문제 없이 진행됐습니다. Google App Engine - Django Skeleton 을 이용해서 장고(Django)를 이용한 서비스를 쉽게 '구글 앱 엔진'에 올릴 수가 있었습니다. 하지만 파일 업로드 기능부터 문제가 발생하기 시작했습니다. 일반적으로는 전혀 문제가 안되는 기능인데 구글 앱 엔진을 이용하면 문제가 되는 현상입니다.


결국 최종적으로 사이트가 완성이 되고 최초 공개가 됐을 때 사이트가 너무 느리다는게 문제로 작용했습니다. 웹이나 앱 서버등이 데이타를 가져오는 데 걸리는 시간이 너무 걸리는 것입니다. 너무 느려서 슬쩍 레진 코믹스 개발자에게 너무 느린게 아닌가. 자문을 했더니 뭐 보통 그정도 속도인데? 이런 반응을 보이는 것입니다. 아니 이 느린 환경에서 개발을 이뤄내고 회사를 그 정도 크기 까지 키운 레진 코믹스의 개발자들이 너무 대단스러워 보이는 것입니다. 그래서 마구 칭찬을 했더니 '니네는 투자도 받았다면서? 그냥 AWS 로 개발하는게 낫지 않겠어?' 라고 하더군요. 그래서 바로 IaaS (Infrastructure as a Service) 인 AWS 로 개발 플랫폼을 바로 바꿨습니다.


변경하는데 1주일 정도 걸리더군요. 아직 실제로 서비스 하는 중이 아니였지만 실제로 운영중이였다고 하더라도 그리 오래걸리진 않았을 것이라고 생각됩니다. 사실 편하게 바꿀 수 있었던 이유중의 한가지는 팀원중의 한명이 리눅스(Linux)를 설치하고 그 안에서 서비스 배포의 경험이 많았던 사람이라서. 마우스 클릭질 몇 번으로 서버가 생기고 그 안에서 배포하는 걸 쉽게 할 수 있었기 때문입니다. 구글 검색이 문제 였는데 이것은 엘라스틱서치 에 한글 자소분석기를 붙인것을 Docker 로 만들어서 배포한 걸 이용해서 2분도 안되서 설치해서 적용했습니다. 물론 검색 API 같은것은 따로 만들어 줘야 했지만 말입니다.


결론적으로 우리 회사는 AWS 를 이용한 IaaS 를 이용하는 것이 여러모로 편했습니다. 서버 이해도가 높은 개발자들이 있어서 더욱 그러했던것 같습니다. 만약 구글 앱 엔진이 그리 속도가 느리지만 않았다면 (구조적인 문제입니다. IDC 가 미국 중부나 심지어 동부에 있으니..) 계속 썼을 수도 있으나 만약 쓴다고 해도 아예 처음부터 멤캐쉬(memcache)를 이용한 방식의 아키텍쳐를 구성해야 했을 것입니다. (이 부분은 다시 잘 정리해야 할듯)


어떤 플랫폼을 쓰느냐에 대해서 정답이 없을 듯 합니다. 다만 조금이라도 복잡한 기능을 구현하려고 한다면 자유도가 높은 IaaS 를 추천합니다.





예전에 Emacs for OSX 관련 설치글에서 File Dired Mode 에서 한글이 깨지는 문제는

(require 'ucs-normalize)
(set-file-name-coding-system 'utf-8-hfs)

위 내용을 .emacs 에 써주면 된다고 했습니다. 그런데 shell-mode 에서 한글이 제대로 출력이 안되는 이슈가 존재합니다.

역시 .emacs 에

(setq default-process-coding-system '(utf-8-hfs . utf-8-hfs))

와 같은 내용을 써주면 해결됩니다.

프로그래밍 언어는 python을 이용하고 웹 프레임워크는 Django 를 사용하고, 데이타베이스로는 MySQL 을 사용중인데 admin 사이트 구현중에

date_hierarchy = 'created_at'

이 구문 때문에

Database returned an invalid value in QuerySet.dates(). Are time zone definitions and pytz installed?

이런 에러가 발생한다.

해결을 위해서는 일단 pytz 를 설치해줘야 한다.

$ pip install pytz

그리고 우분투에 설치된 mysql 에 한하여 (docker 로 설치한 mysql 도 괜찮다, 테스트 해보니 osx 도 된다)

$ mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p  mysql

하고 데이타베이스를 재시작 하면 된다. docker 데이타베이스는 설정을 바꾸면 재시작이 안되니 조심해야 한다. 원격 데이타베이스에 관해서는 (Google App Engine 의 Cloud SQL 도 가능하다)

$ mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p  -h $REMOTE_SERVER mysql



각각의 request_log 에 application_log 가 딸려있다. 그리고 각각의 어플리케이션 로그는 형식에 맞춰서 로그를 남길 수가 있으며 위 사진을 보면 알 수 있듯이 각각의 아이콘 그림까지 다르다!! 



+ Recent posts