สำหรับคนที่ทำงานด้านการพัฒนา software คงพอจะรู้กันว่าปัญหา dependency management เป็นปัญหาที่เราต้องเจอกันบ่อยและเปลืองเวลาในการจัดการมากๆ เริ่มตั้งแต่การติดตั้ง development environment เพื่อใช้พัฒนาซอฟต์แวร์บนเครื่องตัวเอง บนเครื่องอื่นๆ ของสมาชิกในทีม ไปจนถึงการนำไปติดตั้งเพื่อใช้งานจริงบน production environment
งานสาย data science ก็เจอปัญหานี้ไม่ได้แตกต่างไปจากสายพัฒนา software อื่นๆ มากเท่าไหร่ โดยเฉพาะงาน deep learning ที่มี dependencies ค่อนข้างเยอะ ซึ่งถ้าใครเคยติดตั้ง Caffe ก็น่าจะพอเห็นภาพว่า เราต้องติดตั้ง libraries และ drivers หลายตัวหลายขั้นตอนกว่าที่ระบบจะพร้อมเริ่มงานได้จริงๆ ซึ่งก็มีปัจจัยหลายๆ อย่างที่พร้อมจะทำให้มันพังอยู่ตลอด และถึงแม้ว่าเราทำบนเครื่องตัวเองสำเร็จ คำถามต่อมาก็คือ.. เราจะเอาไปทำซ้ำที่เครื่องอื่นได้เหมือนกันรึเปล่า?
ยกตัวอย่างสถานการณ์ที่เราน่าจะเจอกันบ่อยๆ สมมติเราเลือกใช้ภาษา python สำหรับการทำ deep learning แล้วกันนะครับ มีหลายเหตุหลายปัจจัยมากๆ ที่ทำให้โปรแกรมของเราทำงานไม่ได้หรือทำงานได้ผลลัพธ์ไม่เหมือนกัน ยกตัวอย่างเช่น
วันนี้เลยอยากมาพูดคุยเกี่ยวกับการใช้งาน Docker เบื้องต้น ว่าจะช่วยเราจัดการเรื่องพวกนี้ให้เป็นเรื่องง่ายขึ้นได้ยังไงบ้าง (สำหรับคนที่คุ้นเคยกับ Docker อยู่แล้ว สามารถข้ามไปอ่านในส่วนของ nvidia-docker เพื่อใช้งาน container กับ GPUs ได้เลยครับ)
Docker เป็นเครื่องมือที่ช่วยเราในการสร้าง Software Container ครับ ลองจินตนาการว่ามันจะดีแค่ไหน ถ้าเราสามารถห่อโค้ดของเราพร้อมกับ dependencies ทั้งหมดที่จำเป็นต้องใช้รันโค้ดชุดนั้น เรียงใส่เข้าไปในตู้คอนเทนเนอร์(container) เรียกได้ว่าตู้นี้มีของทุกอย่างที่เราต้องการพร้อมเสร็จสรรพ และทุกครั้งที่เราอยากจะรันหรือใช้งานโค้ดชุดนั้น เราก็แค่ยกเอาตู้คอนเทนเนอร์นี้ออกมาวางไว้แล้วเรียกใช้งานโค้ดของเราได้เลย โค้ดของเราก็จะทำงานได้ปกติไม่ผิดเพี้ยนไปจากเดิม ไม่เกี่ยงว่าจะใช้ระบบปฏิบัติการอะไร สภาพแวดล้อมเป็นยังไง หรือจะตั้งอยู่ที่ไหนก็ตาม โคตรเทพ!
นอกจากนั้นเจ้าเครื่องมือที่ชื่อว่า Docker เนี่ย สามารถช่วยเราจัดการเกี่ยวกับคอนเทนเนอร์ในด้านต่างๆ ได้อีก เริ่มตั้งแต่การออกแบบพิมพ์เขียว(image)ของตู้คอนเทนเนอร์ การสร้างคอนเทนเนอร์จากพิมพ์เขียวนั้น การเรียกใช้งานคอนเทนเนอร์ รวมไปถึงการฝากการแชร์พิมเขียวพวกนั้นด้วย
ถึงแม้ว่ามันออกจะเข้าใจยากสำหรับมือใหม่อยู่บ้าง แต่ถ้าเราใช้มันได้แค่ในระดับเบื้องต้น มันก็สามารถช่วยเราและทีมประหยัดเวลาไปได้เยอะพอสมควรแล้วครับ
แล้วหลังจากที่เราแพ็คของทุกอย่าง(scripts, notebooks, application, compilers, libraries, drivers และของทุกอย่างที่สามารถติดตั้งบนเซิร์ฟเวอร์ได้) เข้าไปอยู่ในคอนเทนเนอร์ได้แล้ว มันจะช่วยอะไรพวกเราได้บ้าง?
พอกันทีกับนั่งอ่าน tutorial ยาวหลายหน้า ติดตั้งเหล่าไดรเวอร์คอมไพเลอร์ไลบรารี่ต่างๆ อีกหลายสิบขั้นตอน เลิกเสียเวลาเป็นวันกับการงมหาว่าเราทำผิดขั้นตอนไหนทำไมมันถึงไม่เวิร์ค ด้วยการเรียกใช้งานคอนเทนเนอร์แทน ซึ่งใช้เวลาเพียงไม่กี่นาทีเท่านั้น ยิ่งถ้าเราใช้งานไลบรารี่ที่ค่อนข้างเป็นที่นิยมอย่าง caffe, tensorflow หรือ keras ส่วนใหญ่จะมีคนสร้างและแชร์ docker image ไว้อยู่แล้ว ซึ่งเราสามารถหยิบใช้งาน image พวกนี้ได้ทันทีโดยที่เราแทบไม่ต้องเซ็ทอัพอะไรเองเลยด้วยซ้ำ!
เพื่อนๆ ในทีมของเราสามารถรันโค้ดของเราได้ และได้ผลลัพธ์เหมือนกันไม่ว่าจะบนเครื่องไหนๆ โดยไม่ต้องกังวลว่าเครื่องนั้นจะใช้ระบบปฏิบัติการอะไร หรือใช้ library เวอร์ชันอะไร ทำให้เราประหยัดเวลาของทีมในการ onboarding โปรเจกต์นั้นได้มาก
หลายครั้งที่ผลงานของเราจะอยู่ในรูปแบบ prediction model ซึ่งจะเป็นประโยชน์ก็ต่อเมื่อเราสร้างแอพพลิเคชันเพื่อให้คนอื่นสามารถเรียกใช้งานโมเดลของเราได้ Docker container ช่วยให้แอพที่เราสร้างขึ้นถูกนำขึ้นไปติดตั้งบน production server ได้ง่าย ไม่ว่าจะขึ้น dedicated server หรือ cloud service provider อย่าง AWS หรือ Google Cloud ก็ตาม ซึ่งก็เป็นผลดีทั้งกับทีม data science เอง แถมยังช่วยลดภาระของทีม dev และ devops ในการ deploy แอพของเราได้อีกด้วย
เรียกได้ว่าเป็นพิมพ์เขียว หรือเป็นต้นแบบของการสร้าง container ขึ้นมาใช้งาน ถ้าเปรียบเทียบกับการเขียนโปรแกรมเชิงวัตถุ(OOP) เจ้าตัว docker image นี้ก็เปรียบเสมือน class ซึ่งเป็นต้นแบบในการสร้าง object (docker container) อีกทีครับ
Docker image สามารถเรียกใช้ได้สองวิธี นั่นก็คือการสร้างจาก Dockerfile ที่เป็นสคริปต์ที่อธิบายว่าเราจะติดตั้งอะไรลงไปใน image บ้าง หรือดาวน์โหลดและเรียกใช้งาน docker image ที่มีอยู่แล้วบนอินเตอร์เน็ต
สร้างมาจาก docker image ซึ่งเป็นส่วนที่จะถูกนำไปใช้งานและโค้ดจะถูกรันจริงๆ ที่ส่วนนี้ ซึ่งแสดงให้ดูได้ตามรูปด้านล่าง
นอกจากเราจะสร้าง docker image มาเพื่อใช้รันบนเครื่องตัวเองแล้ว เราสามารถอัพโหลดเข้าไปสู่ server กลางสักอันเพื่อที่จะเอาไปใช้งานบนเครื่องอื่นๆ ได้ด้วย โดย server ที่ว่านี้เราจะเรียกว่าเป็น docker registry
มีอันหนึ่งที่มีชื่อเสียงและถูกใช้เป็น registry หลักนั่นก็คือ DockerHub (เปรียบเหมือน Github สำหรับ docker image นั่นเอง) ซึ่งที่นี่นี่เอง ที่เป็นจุดศูนย์รวมของ docker images ต่างๆ จากผู้คนทั่วโลก และเราสามารถหยิบจับ image ที่มีอยู่แล้วมาใช้งานเลย หรือเอามาดัดแปลงให้เข้ากับการใช้งานของเราเองก็ได้
สามารถศึกษาคู่มือและทำการติดตั้งได้จาก https://docs.docker.com/install/ ถ้าหากติดตั้งสำเร็จแล้ว สามารถตรวจสอบว่ามันเวิร์คหรือไม่ด้วยคำสั่ง docker run hello-world ถ้าเจอข้อความคล้ายๆ ด้านล่างขึ้นแสดงว่าเราติดตั้งมันได้เรียบร้อยดีแล้ว เย่!
$ docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
ในบทความนี้จะพูดถึงสองวิธีหลักๆ ในการได้มาซึ่ง docker image ของเรา ซึ่งสามารถใช้วิธีการใดก็ได้
เราจะสร้าง docker image ด้วยการสร้างไฟล์ Dockerfile ซึ่งเป็นโพยที่ใช้อธิบายว่าจะรันคำสั่งอะไรหรือติดตั้ง software อะไรบ้าง เพื่อให้ docker ช่วยสร้าง image ให้เราตามนี้โดยอัตโนมัติ
จากนั้นรันคำสั่งด้านล่างเพื่อสร้าง docker image โดยตั้งชื่อว่า datawow/tensorflow
docker build -t datawow/tensorflow .
เพียงเท่านี้ docker image ก็จะถูกสร้างขึ้นในเครื่องคอมพิวเตอร์ของเราแล้ว ซึ่งสามารถตรวจสอบได้ด้วยคำสั่ง docker images ครับ
นอกจากวิธีข้างต้น เราสามารถใช้จุดได้เปรียบอีกอย่างของการใช้ Docker คือเราสามารถดาวน์โหลด image ของคนอื่นมาใช้ได้เลย! ซึ่งอะไรที่คนเค้าฮิตใช้กันมักจะมีคนสร้าง image พวกนั้นไว้อยู่แล้ว โดยมี Dockerhub เป็น registry หลักที่เราไปเลือกหยิบ image มารันได้เลย และถ้าเราต้องการอ่านคำอธิบายหรือข้อมูลเพิ่มเติม ก็สามารถคลิกเข้าไปอ่านรายละเอียดของ image นั้นได้เหมือนกันครับ
หลังจากที่เราไปเจอ image ที่ต้องการจะใช้ ยกตัวอย่างในเคสนี้คือ tensorflow/tensorflow เราสามารถใช้คำสั่งด้านล่างในการดาวน์โหลด docker image นั้นมาเตรียมใช้งานในเครื่องของเราเอง
$ docker pull tensorflow/tensorflow
สามารถทำได้ด้วยคำสั่งด้านล่าง ซึ่งจะแสดงรายการ image ทั้งหมดที่มีในเครื่อง
$ docker images
ก่อนอื่นเราจะสร้างโฟลเดอร์ ที่ชื่อว่า ~/dockertut เพื่อใช้สำหรับสาธิตการทำงาน และมีไฟล์ตัวอย่าง mnist_softmax.py อยู่ภายในโฟลเดอร์นั้น สามารถดาวน์โหลดได้จาก github ของ tensorflow ซึ่งใช้เพื่อ train linear classification model อย่างง่ายและแสดง test accuracy หลังจบการทำงาน
$ mkdir ~/dockertut
$ cd ~/dockertut
$ wget https://raw.githubusercontent.com/tensorflow/tensorflow/master/tensorflow/examples/tutorials/mnist/mnist_softmax.py
เมื่อเรามี docker image พร้อมที่จะเรียกใช้งานแล้ว ขั้นตอนนี้ก็เป็นส่วนที่น่าตื่นเต้นที่สุด นั่นคือการรัน docker container จาก image ที่สร้างขึ้นในขั้นตอนที่แล้วนั่นเอง (tensorflow/tensorflow) สามารถทำได้ด้วยคำสั่ง docker run ตามตัวอย่างด้านล่าง
$ docker run --volume $(pwd):/workspace tensorflow/tensorflow python /workspace/mnist_softmax.py
ซึ่งคำสั่งประกอบด้วยสามส่วนหลักๆ ดังนี้
ผลลัพธ์จากการรันคำสั่งข้างต้น ตัวสคริปต์สามารถทำงานได้ตามปกติ ทั้งขั้นตอนการ training และแสดงผล testing accuracy ได้อย่างถูกต้องโดยที่เราไม่ต้องติดตั้ง dependency ต่างๆ ด้วยตัวเองเลย
ที่กล่าวมาห้าขั้นตอนข้างต้น ตั้งใจเขียนให้เป็นตัวอย่างที่เรียบง่ายที่สุดเพื่อความเข้าใจง่ายในการอ่าน แต่การเอาไปใช้งานจริงนั้น docker ยังมีความสามารถอื่นๆให้ศึกษาเพิ่ม และมีเรื่องที่จำเป็นต้องคำนึงถึงอีกมากกว่านี้ ซึ่งสามารถหาข้อมูลต่อที่ Guide ของ docker ครับ
ปัญหาของ Docker คือมันไม่สามารถใช้งาน GPU driver ได้ (อ้าว!) เพราะ docker container นั้นอำนวยความสะดวกเราด้วยการรันโค้ดบน environment พิเศษที่ไม่สนใจว่าจะมันจะตั้งอยู่บนแพล็ตฟอร์มหรือระบบปฏิบัติการไหนๆ แต่นั่นก็หมายความว่ามันก็ไม่สนใจฮาร์ดแวร์พิเศษอย่าง GPU ด้วยเหมือนกัน
แต่ขอแสดงความยินดีกับคนที่ใช้การ์ดจอจากค่าย Nvidia เรามีทางออกง่ายๆ มานำเสนอ นั่นก็คือการใช้ nvidia-docker นั่นเองครับ
nvidia-docker คือ wrapper ที่ห่อคำสั่งต่างๆ ของ docker อีกที สามารถทำคำสั่งได้ทุกอย่างที่ docker ทำได้แต่เพิ่มความสามารถในการเข้าถึง GPU เข้ามาด้วย ดังนั้นเราสามารถใช้ nvidia-docker แทนคำสั่ง docker ปกติได้เลยครับ ซึ่งแน่นอนว่าต้องทำการติดตั้งก่อนครับ
เมื่อเรารันคำสั่งเหมือนเดิม แต่เรียกด้วย nvidia-docker แทน ผลลัพธ์ก็จะออกมาหน้าตาเหมือนกับที่เคยพูดถึงในส่วนที่แล้วครับ
$ nvidia-docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
แล้วถ้าเรา pull pre-built image จาก Dockerhub ที่ติดตั้ง CUDA + Nvidia drivers มาแล้ว เราก็สามารถเรียกใช้งาน container ของเราที่มีการ์ดจอพร้อมใช้งานได้เลย สามารถทดสอบด้วย image ที่ชื่อ nvidia/cuda โดยการรันคำสั่ง nvidia-smi เพื่อเช็คว่าคอนเทนเนอร์ของเราเจอ GPU อย่างที่คาดไว้
สุดท้ายนี้ขอขอบคุณท่านผู้อ่านที่ยังติดตามมาจนถึงตอนจบนี้ได้นะครับ ยินดีน้อมรับทุกคำแนะนำติชม สามารถคอมเมนต์ไว้ได้เลยนะครับ :)
ส่งท้ายด้วยลิงค์ที่น่าสนใจ
Drop us a line and we will get back to you