เวลาเราเขียนโปรแกรมออกมาตัวนึง เราอาจจะมีการติดตั้งโปรแกรมที่เกี่ยวข้องต่าง ๆ หรืออาจจะเป็น Library ที่เราใช้งานต่าง ๆ แต่เมื่อเราเขียนเสร็จแล้ว เราต้องการเอาไปทำงานในเครื่องอื่น ๆ เช่น เครื่องที่ใช้งานจริง ๆ หรือที่เราเรียก Production ถ้าเราจะต้องมาติดตั้งทั้งหมดใหม่ ก็น่าจะไม่ใช่เรื่องที่ดีเท่าไหร่ ไหนจะเรื่องความเข้ากันได้ของ Software ต่าง ๆ หรือจะเป็นความผิดพลาดจากมนุษย์เอง เช่น การลืมลง Library หรือ Software ต่าง ๆ ก็ทำให้คนหัวหมุนกันมานัดต่อนัดแล้ว ทำให้วันนี้เราจะมาทำให้โปรแกรมของเรา สามารถนำไปใช้ต่อได้ง่ายขึ้นกันด้วย Docker
ก่อนเราจะไปดูว่า เราจะแก้ปัญหาด้วย Docker เราอยากจะมาบอกเล่าก่อนว่า ถ้าเกิดเราไม่ใช้ Docker เราเล่นมือเปล่าเลย เราจะต้องทำยังไง และ เราจะเจอปัญหาอะไรได้บ้างระหว่างนั้น
ใน Application ที่ง่ายที่สุด ส่วนประกอบของมันก้น่าจะเป็นพวก Source Code ที่เราเขียนเข้ามา ซึ่งใน Source Code เหล่านั้นเอง ก็อาจจะมีการ Import Dependencies อื่น ๆ เข้ามาร่วมด้วย ทำให้ตอนที่เราจะใช้งานตอนพัฒนา เราก็ต้องไป Download และติดตั้งผ่าน Package Manager ต่าง ๆ เช่น pip เป็นต้น
> pip install numpy pandas keras tensorflow tensorflow-gpu
ทำให้เมื่อเราจะต้องเอา Application ของเราไปทำงานที่อื่น เราก็ต้องทำการติดตั้งให้เหมือนกับเครื่องที่ใช้พัฒนาอีก เราก็อาจจะจด Dependencies ต่าง ๆ ใส่กระดาษ แล้วมาติดตั้งที่เครื่องที่จะทำงานก่อนที่เราจะสั่งให้ Application ของเราทำงาน แน่นอนว่า การทำแบบนี้ อย่างแรกเลยที่เจอได้คือ เราติดตั้งไม่ครบ พิมพ์ผิดบ้าง อะไรบ้างก็มึน ๆ กันไป อีกปัญหาที่ตามมา และเป็นเรื่องใหญ่ไม่แพ้กันเลยคือ Version ของ Dependencies ก็อาจจะไม่ตรงกับเครื่องที่เราใช้พัฒนา บางที Version เปลี่ยน บาง Function หรือ Module ก็อาจจะหายไป หรือเปลี่ยนที่อะไรก็ว่ากันไป ทำให้ Application ของเราทำงานไม่ได้นั่นเอง
> pip freeze >> requirements.txt
> pip install -r requirements.txt
จาก 2 ปัญหาที่ได้เล่าไป ทำให้เราเอาใหม่ ในบรรทัดแรก เราขอ Dependencies ทั้งหมดที่เราติดตั้งจาก pip ผ่านคำสั่ง pip freeze และให้มันเขียนไปที่ไฟล์ชื่อ requirements.txt จากนั้น เราก็ให้ pip มันไปอ่านไฟล์ที่พึ่งเขียนไป และให้ติดตั้งตามนั้นเลย ถ้าเราลองรันแล้วลองเปิดไฟล์ requirements.txt เราจะเจอแบบด้านล่างนี้
Flask==1.1.2 joblib==0.16.0 Keras==2.4.3 Keras-Preprocessing==1.1.2 scikit-learn==0.24.1 scipy==1.4.1 seaborn==0.10.1
เราจะเห็นได้ว่า มันจะยัดพวก Dependencies ที่เราติดตั้งทั้งหมดเข้ามาให้ พร้อมกับ Version ของแต่ละ Dependencies เลย ทำให้เมื่อเราสั่งให้ pip มาติดตั้งตามไฟล์นี้ มันก็จะเลือกใน Version ที่เราระบุไว้ในไฟล์ให้เลย ทำให้เรามั่นใจได้ว่า เราจะได้ Dependencies ครบ และ อยู่ใน Version เดียวกันกับที่เราใช้พัฒนาแน่นอน
แต่ปัญหามันก็ยังไม่หมด เพราะบาง Dependencies ก็ไม่ Support บาง Configuration ด้วย ตัวอย่างที่พึ่งเจอมาสด ๆ ร้อน ๆ เลยคือ Japronto ที่เราเจอปัญหาในการติดตั้ง เราลองเท่าไหร่ก็มีปัญหา ติดตั้งไม่ได้สักที พอมาดู Error Log ก็คือ มันมีปัญหากับ C Compiler ในเครื่องเรา ก็ถือว่าเป็นปัญหาที่เจอได้ นี่ละ คือสิ่งที่จะบอกว่า บาง Dependencies มันก็ทำตัวไม่น่ารักกับ Configuration บางอย่างได้เหมือนกัน เช่น OS หรือ C Compiler ต่าง ๆ ซึ่งต้องยอมรับว่า บางทีเราก็ไม่รู้เลยนะ จนมานั่งติดตั้งเองนี่แหละ ทำให้ Docker มันเข้ามาช่วยเราเรื่องนี้เยอะมาก เพราะมันเก็บเป็น Image ที่ Build ไว้ล่วงหน้า ทำให้เรามั่นใจได้ว่า ใน Container ที่เราทำงานจะเป็น Environment เดียวกันหมดได้แน่นอน การทำงานก็ เราก็ทำการ Copy Image ไปไว้ในเครื่องปลายทาง แล้วก็สั่งรันได้เลยตรง ๆ
Spoil เลยว่า ตอนที่ติดตั้ง Japronto ก็คือ เราจบที่การไปโหลด Docker Image เขามาใช้ก็รันได้เลย ฮ่า ๆ
ก่อนที่เราจะเริ่มลองตัวอย่างกัน ให้ทุกคนเข้าไปติดตั้ง Docker กันก่อนที่จะทำด้วยที่ Docker Website
Project
└─── src
└─── server.py
> pip install flask
from flask import Flask
server = Flask(__name__)
@server.route("/")
def index():
return "Hello World!"
if __name__ == "__main__":
server.run(host='0.0.0.0')
> python src/server.py
> python src/server.py
* Serving Flask app "server" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
> pip freeze | grep 'Flask' >> requirements.txt
FROM python:3.9
WORKDIR /code
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY src/ .
CMD ["python", "./server.py"]
FROM python:3.9
WORKDIR /code
คำสั่งด้านบนเปรียบเสมือนกับเราสั่ง cd ใน Shell เลย ก็คือเลือก Path ที่เราต้องการจะทำงานต่อไป
COPY requirements.txt .
สิ่งที่เราต้องการจะทำคือ การติดตั้ง Dependencies แต่ใน Image ที่เราต้องการจะสร้าง มันยังไม่มี File ที่ระบุ Dependencies เลย ทำให้ในขั้นตอนแรก เราจะต้องทำการ Copy requirements.txt จากเครื่องของเรา เข้าไปที่ Image ก่อน เราจะเห็นว่า หลังจาก COPY Argument ตัวแรก จะเป็น Path ไปที่ไฟล์ที่เราต้องการ Copy และอีกตัวจะเป็น Path ของ Image ในที่นี้เราใส่เป็นจุดเฉย ๆ ก็คือ Current Path หรือก็คือ /code ที่เราสั่งมันไว้ในบรรทัดก่อนหน้าแล้ว หลังจากจบบรรทัดนี้ไฟล์ requirements.txt ของเราก็จะเข้าไปอยู่ใน Image แล้ว
RUN pip install -r requirements.txt
COPY src/ .
CMD ["python", "./server.py"]
> docker build -t my-first-python-app .
เมื่อเราเขียน Dockerfile เสร็จแล้ว ย้อนกลับมาที่ Shell ของเราและรันคำสั่งด้านบนเพื่อสั่งให้ Docker สร้าง Image จาก Dockerfile ที่เราพึ่งเขียนขึ้นมากัน โดยที่เราตั้งชื่อว่า my-first-python-app ซึ่งเราสามารถเปลี่ยนได้ตามใจชอบเลย และตัวสุดท้ายที่เป็นจุดเฉย ๆ เป็นการบอกตำแหน่งของ Dockerfile ก็คือบน Path ปัจจุบันที่เราสั่งรัน และไฟล์ชื่อ Dockerfile นั่นเอง
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-first-python-app latest 7b12bedf9710 2 mins ago 895MB
เพื่อความมั่นใจว่า Image ของเราถูกสร้างเรียบร้อยแล้ว ถ้าเราลองสั่ง docker images เพื่อขอดู Image ที่เรามีในเครื่อง เราจะเห็นว่าใน List จะมี Image ชื่อ my-first-python-app ที่เราพึ่งสร้างขึ้นมาด้วย
> docker run -d -p 5000:5000 --name my-app my-first-python-app
> docker save -o my-image.tar my-first-python-app
อย่างที่เราบอกว่า เราสามารถ Copy Image ไปทำงานในเครื่องอื่น ๆ ได้ เราจะใช้คำสั่ง save กัน โดยที่เราใส่ Path ที่เราต้องการให้ Image มัน Save ในที่นี้เราตั้งเป็น my-image และให้นามสกุลเป็น .tar ด้วยนะ และสุดท้ายเราก็ใส่ชื่อของ Image เข้าไปก็เรียบร้อยในฝั่งต้นทาง
> docker load -i my-image.tar
กิจกรรมที่กำลังจะมาถึง
ไม่พลาดกิจกรรมเด็ด ๆ ที่น่าสนใจ
Event นี้จะเริ่มขึ้นใน April 25, 2023
รายละเอียดเพิ่มเติม/สมัครเข้าร่วมคอร์สเรียนไพธอนออนไลน์ที่เราได้รวบรวมและได้ย่อยจากประสบการณ์จริงและเพื่อย่นระยะเวลาในการเรียนรู้ ลองผิด ลองถูกด้วยตัวเองมาให้แล้ว เพราะเวลามีค่าเป็นอย่างยิ่ง พร้อมด้วยการซัพพอร์ตอย่างดี