/ python

ใช้ Jupyter ทำ REST API แบบง่าย

มีโค้ดที่เขียนไว้บน Jupyter notebook แล้วอยากเอามันมาส่งค่ากลับ แบบ JSON เรียกผ่าน Ajax ได้ คนขี้เกียจแบบผม ต้องเอา Jupyter ตอบ JSON กลับไปเลยซิ ด้วยความที่ ไม่ได้เชี่ยวชาญการใช้ Jupyter ก็เลยค้นๆไป เจอวิธี และใช้ได้ มาเล่าสู่กันอ่าน

jupyter-circle

ผมรู้จัก IPython มาเมื่อนานมาแล้ว นานมาก เกิน 5 ปี ตอนนั้นลองใช้เฉยๆ ยังหนุ่ม ยังแน่นลองไปเรื่อย ตอนกระโน้นเขียน Python ได้นิดหน่อย ใช้ print, for, if ได้ พอลองใช้ IPython ดูแล้วก็งงๆ ใช้ทำไมว่ะ แค่เปิดหน้าเว็บ และรันโค้ดได้ ไม่เห็นมีประโยชน์ เลยไม่ได้เล่นต่อเลย

ผ่านมาจากตอนนั้นก็นาน ผมเห็นโปรเจคหนึ่งชื่อ iTorch มันคือ IPython kernel ที่เขียนภาษา Lua ได้ เหมือนกับที่ เขียน Python บน IPython เลยซึ่ง มันต้องติดตั้ง IPython ก่อน แต่ติดตั้งไปมาตอนสั่ง pip install ipython ผมเจอคำเตือน และให้ใช้ Jupyter แทน ค้นๆไปมา เอ้อ มันดีเว้ย มันคือ IPython นั่นแหละ ก็เลยได้ลองใช้มาเรื่อยๆ แต่ไม่ได้จริงจังมาก

ช่วงสอง สามเดือนที่ผ่านมา มีงานที่ใช้ Python ทำการจัดการข้อมูลค่อนข้างเยอะ แรกๆเขียน Python script บน server เลย ssh ไปทำงาน พอใช้ๆไป ความต้องการเริ่มมาก เลยกลับมาหาเพื่อนเก่า IPython/Jupyter เพื่อความสะดวกในการจัดการข้อมูลที่เริ่มเยอะและซับซ้อนมากขึ้น

jupyterpreview
รูปจาก http://jupyter.org/

Jupyter เอง มันติดตั้งไม่ยาก ถ้าใครเคยใช้ Python ก็พอจะรู้จาก pip สั่งติดตั้งผ่าน pip install jupyter ได้เลย จบ แต่นี่มันปี 2017 แล้ว ใครๆก็ใช้ Docker ทำไมเราจะต้องใช้ pip vbvb ถ้าใครขี้เกียจติดตั้ง Docker และรัน Jupyter ก็ใช้ตามนี้ครับ

pip install jupyter
Requirement already up-to-date...
jupyter notebook
...
Copy/paste this URL into your browser when you connect for the first time,
to login with a token:
http://localhost:8888/?token=d744ce62a08ae4913d5265ae99df940db41168c78dd
[I 17:07:47.023 NotebookApp] Accepting one-time-token-authenticated connection from ::1

จากนั้นหน้าเว็บก็จะเปิดขึ้นมา

Screen-Shot-2560-09-13-at-10.51.06-PM

ใส่ token ที่ได้ตอนรันคำสั่ง เพื่อทำการ login หลังจาก login เสร็จจะเจอไฟล์ต่างๆที่ไดเรคทอรี่ ที่เรารันคำสั่งขึ้นมา

Screen-Shot-2560-09-13-at-10.51.12-PM

ลองสร้าง Notebook ขึ้นมา เลือกเป็น Python แล้วกัน

Screen-Shot-2560-09-13-at-10.56.30-PM

จะได้หน้าตามาประมาณนี้ แต่โล่งๆ ไม่มีโค้ดอะไร ลองใส่โค้ด และรันดูได้เลย

Screen-Shot-2560-09-13-at-10.59.49-PM

อันข้างบนๆคือเขียนโค้ดบน Jupyter แบบง่ายๆ เหมือนเขียน Python ปกติ แต่ทำผ่านหน้าเว็บ อาจจะยังไม่ตื่นเต้นอะไรก็ได้ รูปข้างล่างนี่ คือผมลองเอา Jupyter รันบนเครื่องตัวเอง แล้วใช้ PyMongo ซึ่งเป็น MongoDB Driver สำหรับ Python เรียกไปยัง MongoDB server ที่อยู่ในวง VPN เพื่อเอาค่ามาลองวาดกราฟดู โดยเรียกใช้ Matplotlib ได้เลย จริงๆแล้ว มันทำเป็น Inline ได้ ลืมแคปรูปไว้ และหาไฟล์ notebook ไม่เจอ

Screen-Shot-2560-07-19-at-1.22.40-AM

การจัดการข้อมูลเยอะๆ ใช้เครื่องคอมพับแค่เปิดไฟล์ ยังต้องรอนานเลย มันจึงเป็นเหตุผลว่า ทำไมจะต้องเอา Jupyter ไปรันบนเครื่อง Server แรงส์ๆ

ไหนละ REST API ไม่เห็นมีเลย ถถถถ

ได้เวลาแล้วๆ ตัว REST API นี่ จะใช้ Docker ร่วมด้วยน่ะ เพราะว่า อยากรันให้มันอยู่ใน Container มากกว่า จะทำให้เครื่องโฮสต์ต้องติดตั้งอะไรต่อมิอะไรเพิ่มข้างนอก เดี๋ยวมันรก ถ้าใช้ Docker ตอนไหนไม่ใช้ Jupyter แล้ว ก็ลบมันทิ้งไปเลย

เริ่ม!

ผมจะมโนไปเองว่า คนอ่านมี Docker ติดตั้งอยู่แล้ว บนเครื่องที่เป็น Linux โดยผมจะสร้างไดเรคทอรี่มาแบบนี้

mkdir -p jupyter/work
tree jupyter

jupyter
└── work


แล้วก็รัน Docker Jupyter base notebook ด้วยคำสั่งนี้

cd jupyter
chown 1000 work
docker run -d --name jupyter -v $(pwd)/work:/home/jovyan/work -p 8888:8888 -e NB_GID=100 -e GRANT_SUDO=yes jupyter/base-notebook

ต้อง chown 1000 work ก่อน เพื่อให้ Jupyter เขียนไฟล์ลง work ได้ หลังจากที่รันเสร็จแล้ว จะดู token ได้จากคำสั่ง

docker logs jupyter

...
Copy/paste this URL into your browser when you connect for the first time,
to login with a token:
http://localhost:8888/?token=4b1240ef5ae45d4b22a5338a9aec66144a2757bf5f3ce390


หลังจาก login เสร็จแล้ว สร้าง Notebook ชื่อ api ในไดเรคทอรี่ชื่อ work และลองรันดู แบบนี้

Screen-Shot-2560-09-13-at-11.30.55-PM

ตรวจสอบดูไฟล์ใน work จะได้ไฟล์ชื่อ api.ipynb

tree work

work
└── api.ipynb


ต่อไปมาสร้าง REST API ง่ายๆด้วย Jupyter Kernel Gateway สร้างไฟล์ชื่อ Dockerfile

FROM jupyter/base-notebook
MAINTAINER Narate Ketram <rate\[\@\]dome.cloud>
RUN pip install jupyter_kernel_gateway
ENTRYPOINT ["tini", "--", "jupyter", "kernelgateway", "--KernelGatewayApp.api=notebook-http", "--KernelGatewayApp.ip=0.0.0.0"]
COPY work/api.ipynb /srv/
CMD ["--KernelGatewayApp.seed_uri=/srv/api.ipynb"]

เสร็จแล้วสั่ง build docker image

docker build -t narate/jupyter_kernel_gateway .

และสั่งรัน docker container

docker run -d --name jupyer_kernel_gateway -p 8889:8888 narate/jupyter_kernel_gateway

ช้าก่อน เรายังไม่ได้บอก Notebook ว่าให้รับ Request มาที่ URI ไหน ให้แก้ api notebook ใส่ Annotations ให้มันที่บรรทัดแรก แบบนี้

Screen-Shot-2560-09-13-at-11.51.41-PM

จากนั้นรัน docker container ใหม่ ลบของเก่าทิ้งก่อน

docker rm -f jupyer_kernel_gateway

รันใหม่ โดยการ map ไฟล์ api.ipynb เข้าไป

docker run -d --name jupyter_kernel_gateway -p 8889:8888 -v $(pwd)/work/api.ipynb:/srv/api.ipynb narate/jupyter_kernel_gateway

จากนั้นทดสอบเรียก API ด้วย curl

Screen-Shot-2560-09-13-at-11.56.15-PM

In [99] : print('จบ')
จบ

ที่มาของทั้งหมดนี้ อ่านมาจากที่นี่ และปรับใช้ http://blog.ibmjstart.net/2016/01/28/jupyter-notebooks-as-restful-microservices/