2016: จากนี้เหลือเพียงอดีตของกาลเวลา ที่จะไม่ย้อนคืนมาอีกแล้ว

นี่มันช่วงสุดท้ายของท่อนแรกเพลงหวานขมแล้วสิ ปีหน้าจะขึ้นหัวบล็อคยังไงดีล่ะ (หวังว่ามันจะเป็น “ชีวิตที่มีเธออยู่ ยังรู้สึกทุกเวลา”)

สำหรับปีนี้คงมี event สำคัญแค่ลงเครื่องมาโดกะใหม่ กับฝึกงานที่วงใน…

Madoka v3

ตามที่เขียนไว้ใน New year resolution ว่าปีนี้เราจะจับเครื่องมาโดกะลงใหม่เป็น Docker ซึ่งระบบใหม่ลงแล้วผมชอบมันมากๆ

เรื่องแรกคือระบบ shared hosting หายไปสักที เพราะเอาเข้าจริงผมไม่ได้อยากแบ่งขายอะไรอยู่แล้ว แต่ว่าก็มี requirement มาก็เลยต้องทำ ข้อดีอีกอย่างของการมี shared hosting คือมันทำให้ manage ง่ายด้วย อยากได้อะไรก็กดจากเว็บ แต่ผมก็เสียวๆ security เพราะ ISPCP ที่ใช้ไม่ได้อัพเดตมาเป็นชาติ (ตอนนั้นมันไม่รองรับ Debian 8 ด้วย) ตอนนี้บนเครื่องก็จะมีแต่เว็บผมเกือบทั้งหมดแล้ว และแอพลูกค้าอีกเล็กๆ น้อยๆ ที่โดนแปลงมาเป็น Docker ทั้งหมด

เรื่องที่สองคือ Docker ครับ มันทำให้เล่นอะไรง่ายมากกกกก อยากลงอะไรก็ลง ลงเสร็จก็กดลบ หายแบบ clean หมด 100% แถมลงง่ายด้วย ไม่ต้องกังวลว่าจะมีแพคเกจสำหรับดิสโตรเรามั้ย

เรื่องที่สามคือ Docker Compose ครับ แอพใหม่ๆ ผมใช้ compose หมดแล้ว ซึ่งทำให้ deploy ง่ายมากแค่ sudo docker-compose pull && sudo docker-compose up -d จบ แถมมันยังแยก network ด้วย ทำให้ผม isolate แอพได้ค่อนข้างมาก (แอพไหนที่ไม่จำเป็นต้องใช้ MariaDB ก็จะไม่มี route ไปหา MariaDB เลย) และ firewall ก็แน่นขึ้นมาก ไม่ต้องกังวลว่า app จะแอบเปิด port บนเครื่องเลย

ทั้งสามข้อข้างบนมันยังแก้อีกปัญหาที่มีมานานมาก คือระบบเก่าผม deploy แอพที่ไม่ใช่ PHP ยากมาก ผมพยายามจะไม่เอา Django ลงเครื่อง (บางเว็บที่ลง demo ลูกค้าก็ใช้แค่ manage.py runserver) ส่วน Node.js ก็ใช้ forever จัดการซึ่งมันก็ต้องจำให้ได้ว่าแอพไหนอยู่ตรงไหน พอเป็นระบบใหม่ทุกแอพมันเป็น Docker หมด ก็จัดการรวมศูนย์ง่ายเลย (แต่ก็จะไม่ใช่ multiuser แล้วนะ)

screenshot-from-2016-12-03-15-12-13

เรื่องสุดท้ายคือ Monitoring ครับ ผมพยายามหา solution monitoring ดีๆ จากที่วงในมาหลายตัว ตอนแรกลอง ELK Stack แต่มันกินแรมเยอะมาก (ดีที่ Docker จำกัด memory ได้ แต่หลังๆ Kibana restart ทุก 5 วิเลยเพราะโดน memory limit) ปัจจุบันใช้ InfluxDB + Telegraf + Grafana รันใน Docker ทั้งหมด ซึ่งกิน resource น้อยกว่าเยอะมากๆ ครับ

ผมรู้สึกว่า monitoring ลักษณะนี้มันทันสมัยขึ้นเยอะเลยครับ แต่ก่อนผมจะใช้ Munin ซึ่งมันไม่ real time แต่ Stack นี้จะเห็นกราฟวิ่งตลอดเลย และสามารถทำ email alert ได้ด้วย

สำหรับข้อเสียของระบบใหม่ก็คือ memory usage ครับ ตอนนี้แอพที่เดิมๆ เป็น PHP ผมใช้อิมเมจ php:apache หมดเลย ผลคือแต่ละอิมเมจกินแรม 250mb ตลอดเวลา โชคดีว่าเว็บที่ใช้ PHP มีไม่เยอะเท่าไร (บล็อคนี้ก็เว็บนึงล่ะ) ในอนาคตอาจจะต้องลองทำอิมเมจ php-nginx-fpm ใช้เองดูว่าจะลดลงไปเท่าไร (ใช้ php-fpm แล้วเอา load balancer คุย fcgi เข้าไปไม่ได้ครับ เพราะต้อง serve static ด้วย)

Wongnai

วันแรกที่ผมเข้ามาผมนึกสงสัยว่าผมไม่อยากทำ Java EE แล้วเลือกบริษัทที่ Java EE จ๋าๆ นี่คิดถูกแล้วหรอ แต่พอถึงวันสุดท้ายผมคิดถึงบริษัทนี้มาก เพราะบริษัทให้อิสระกับผมมากๆ ผมได้เห็น ได้ทำงานทั้งจากโปรเจกท์ของตัวเอง, งาน Infrastructure จนไปถึงงาน automation และได้ทำงานกับ dev LINE MAN จากเกาหลีด้วย

ผมไม่รู้ว่าคนอื่นมาจะได้ประสบการณ์แบบผมหรือเปล่านะ คิดว่าผมเข้ามาถูกจังหวะมากๆ ในตอนที่บริษัทกำลังจะขยายตัว เลยได้เริ่ม product ใหม่ตั้งแต่ 0, infra ใหม่ตั้งแต่ 0 แล้วมันเลยทำให้ผมมี access และเข้าไปเล่นได้กับทุกส่วนในบริษัท: ผมส่ง Pull request ให้เว็บวงใน, ผมเข้าไปโม LINE official account และ Internal tools

ส่วนนึงคือตำแหน่งงานผมที่ค่อนข้างพิเศษ คือ dev ปกติ (รวมถึงฝึกงาน summer) จะอยู่ในทีมเว็บหลักที่มีเป้าหมายค่อนข้างชัดเจนและ management ก็คิดว่า velocity ทีมนี้ยังต้องเพิ่มไปอีก แต่สำหรับทีมผมที่แยกออกมาแล้ว เราใช้ tool สมัยใหม่อย่าง Node.js, React Native ทำให้การพัฒนาคล่องตัวสูงมาก (เราสามารถเลือก dependency ใส่ได้เรื่อย ไม่ต้องขอใคร) แล้วมันเลยทำให้มีเวลาที่จะไปแก้ของชาวบ้านได้แบบนี้ รวมถึงว่าผมทำ Infra ให้ ก็เลยมี permission ทุกอย่างในระบบด้วยที่ Intern อาจจะไม่ได้ทุกคน

พี่ๆ ที่วงในทุกคนใจดีมากๆ ผมขอเปิด source โปรเจกท์ผมออกมา ทั้ง imagecleanup และ kube-slack ซึ่งก็ไม่ถูกปฏิเสธเลย (นี่ยังไม่พูดถึงของกินที่เค้าเลี้ยงมาทั้งหมดนะ)

แต่การได้ลองทำทั้งโปรแกรม ทั้ง infra กลับทำให้ผมกลับมาอยู่ในสถานะล่องลอยเหมือนเด็กม. 6 ที่ยังไม่รู้จะเรียนอะไรต่อดี ซึ่งตอนม. 6 ผมมีเป้าหมายที่ชัดเจนมากๆ เลยไม่มีความรู้สึกแบบนั้น แต่ตอนนี้ผมกลับไม่แน่ใจตัวเองว่าอยากจะทำเว็บต่อไป ทำ infra หรือไปสู่โลก System กับ Rust ดี…

Rust

อาจารย์ท่านหนึ่งมีการบ้านมาว่าให้ทำโปรแกรมตามโจทย์ที่กำหนด แล้ววัดคะแนนแต่ละกลุ่มตาม performance ซึ่งมันทำให้ผมต้องไปหาดูภาษาว่าอะไรเร็วที่สุด ก็พบว่า Rust ไวมากๆ โดยไม่ต้องลงไปมือเปื้อนกับ C/C++ ผมก็เลยตัดสินใจเรียน Rust

(ผมชอบ project แบบนี้มากเลยนะ มันทำให้ผมต้องไปขุด every tricks in the book มาใช้ อีก project หนึ่งผมได้ลองเขียน data structure เองด้วย แต่เหมือนเพื่อนผมส่วนมากจะไม่ enjoy เท่าไร)

ถึงตอนนี้ผมรู้สึกว่า Rust เป็นสกิลอันหนึ่งที่มีค่ามากใน skillset ของผม ถ้าตามบล็อคนี้มาอาจจะพบว่าผมเคยเขียน Ruby แล้วไปไม่รอดเท่าไร เพราะผมหา niche มันไม่เจอว่ามันจะใช้ทำงานอะไรที่ภาษาเดิมๆ ของผมทำไม่ได้ แต่ Rust มีคำตอบให้ชัดเจน ก็หวังว่าในอนาคตผมจะยังได้ใช้ Rust จนกระทั่งฟิตพอที่จะเป็นภาษาที่ 4 ที่ผมขาย (PHP, Python, JavaScript)

Conference

ทำงานมาสักพักเพื่อนก็เริ่มชวนว่าไปออกงานบ้าง ซึ่งตอนแรกๆ ก็ไม่ค่อยสนใจเพราะหัวข้องานไม่ได้ทำให้เราสนใจเท่าไร แต่พอไปก็พบว่ามันเป็นโอกาศ networking ที่ดีมาก พี่ๆ speaker งาน React เองเค้าก็บอกว่าอยากให้มาพูด แต่ไม่รู้ว่าจะติดต่อยังไง และก็ได้เห็นอะไรหลายๆ อย่างที่เล่นเองก็ไม่ได้สนใจจะลอง (นั่นขนาดนั่ง Beginner’s track นะ)

หลังจากนั้นก็มีบล็อคเรื่อง Docker ที่พี่เดียร์เห็นแล้วก็ชวนเข้ากลุ่ม Docker และก็ชวนไปพูดที่งาน Meetup ด้วย งานนั้นก็ตื่นเต้นดีครับจนกระทั่งว่าตอนออกไปลืมเก็บของ วางคอมและอุปกรณ์ทุกอย่างไว้ออฟฟิสหมดเลย 555

จริงๆ จากความสำเร็จของ Blog Docker นี่ก็อยากเขียน content แบบนั้นเพิ่มอยู่ครับ แต่ก็ไม่รู้จะเขียนอะไรดี อย่างที่เกริ่นไว้ว่ามันมาจาก content ที่สอนคนอื่นแล้วเอามารวบรวมไว้ พอเป็นเรื่องอื่นๆ ที่ไม่ได้เห็นคนหัดใช้แล้วติดปัญหาเลยไม่รู้จะเขียนอะไรเท่าไร และบล็อคนี้ก็ตั้งเป้าไว้ตั้งแต่แรกว่ามันคือบล็อคส่วนตัว ก็ไว้เขียนเรื่องส่วนตัวด้วย ไม่ใช่ technical blog ถ้ามี technical content เยอะก็ค่อยว่ากันอีกที

TMStreamlabs

ช่วงกลางปีผมค่อนข้างแอคทีฟใน MyLive แล้วก็เห็นคำถามนึงที่สตรีมเมอร์ถามมาในกลุ่มว่า “ทำโดเนทด้วยทรูมันนี่ยังไง” แรกๆ ผมก็ไม่ได้คิดอะไรล่ะ แต่ตอนหลังก็ลองหาวิธีแล้วทำดู ก็ออกมาเป็น TMMyLive ซึ่งปรากฏว่าคนใน YouTube เอาไปใช้เยอะมาก (อ้าว…) เยอะจนขนาดว่าผมเห็นเงินวิ่งผ่านแล้วผมก็อิจฉาล่ะ เพราะตอนเปิดตัวก็บอกว่าผมให้ใช้ฟรี ไม่หักอะไรทั้งนั้น แต่ถ้าใช้แล้วช่วยโดเนทมาด้วย ปรากฏว่าไม่มีโดเนทอะไรมาเลยสักบาทเดียว แต่ยอดเดือนเดียววิ่งเป็นแสน

ก็เลยตัดสินใจว่าต้องทำอะไรสักอย่าง เลยไปเขียนเว็บมาใหม่ให้ระบบมันดีขึ้นกว่าเดิม แล้วสักพักก็เพิ่มฟีเจอร์ที่หักทุกๆ 10 donate เข้าตัวเอง (ตอนแรกจะใส่ 5 มีเพื่อนทักว่าเยอะมาก) ซึ่งรายได้ตอนนี้ก็พอจะจ่ายค่าเซิร์ฟเวอร์นี่เองได้แล้วล่ะครับ และด้วยความอยากรู้และไม่อยากหักหลังคนใช้ก็เลยให้เป็น optional ไป ปรากฏว่าก็มีเปิดครึ่งไม่เปิดครึ่ง ตรงนี้ผมก็คงทำใจเพราะมันเหมือน adblock ล่ะ คลิกเดียวปิดไม่เสียอะไรเลยใครจะไม่ปิด ก็เลยคิดว่าจะล็อคไปว่าฟีเจอร์ใหม่ๆ ถ้าออกมาก็จะให้เฉพาะคนโดเนทไป (แต่ด้วยว่าระบบมันเป็น integration ตรงกลางก็จะทำได้ไม่เยอะเท่าไร)

ก็แปลกดี เป็นอีกโปรเจกท์นึงที่ไม่ได้ตั้งใจแต่ดันประสบความสำเร็จมากๆ (ก่อนหน้านี้ก็คงเป็น Kyou) เพราะมันออกมาจากความต้องการของ user จริงๆ ไม่ใช่ผมคิดมาแล้วไปขาย user ซึ่งผมก็ขายไม่เก่งเท่าไรล่ะ

2016 New Year resolution

ปีที่ผ่านมาพิเศษคือผม retarget New year resolution ใหม่ ลองมาดูกันครับ

อยากสตรีมเกม

ไม่รู้จะเรียก Success ดีไหม คือผมก็สตรีมเป็นระยะๆ คนดูมีบ้าง ไม่มีบ้าง แต่ก็ต้องยอมรับว่าผมไม่มีสกิลแบบมืออาชีพเค้า ถ้าสตรีมก็เลยจะไปทาง niche มากกว่า เช่น ผมสตรีมงาน Apple/Google events ซึ่งยอดคนดูสูงสุดก็เกือบ 300 คนมาแล้ว (ขอบคุณจ่าพิชิต ผมนี่ช็อคเลย) หรือจะเป็นเกมที่มัน geek หน่อยๆ แต่ตรงนี้ก็ทำบ่อยไม่ได้ เพราะไปโชว์เล่นเกมแล้วติดก็ไม่ค่อยดีเท่าไร

ที่ไม่ได้ทำเลยคืออยากทำ Gaming talk แต่ไม่รู้จะ co-host ใคร (ตอน Barcamp Bangkok @Fantasier แนะนำ @winwanwon มา แต่ยังไม่ได้ follow up เลย)

ซื้อเกมใน GOG

ผมพบว่าราคาใน Steam ล่อตาล่อใจมากๆ และใน Humble เองก็ DRM-Free พอสมควร (แต่ไม่ค่อยอยากนับ เพราะเกมบนนั้นไม่ค่อยอัพเดต)

แต่โดยรวมแล้วยังคิดว่าสำเร็จอยู่ ปีนี้ผมซื้อ Never Alone Arctic Collection, Grim Fandango Remastered, Firewatch (ในราคาที่แพงสะใจ), Another World (ยังไม่ทันเล่นก็ลง Humble), Undertale และ Sam & Max Hit the Road จาก GOG

เก็บเงิน

Success!

ผมซื้อ YNAB ตัวใหม่มาแล้ว ซึ่งรู้สึกว่าห่วยกว่ารุ่นเดิมเยอะมาก ยกเว้นอย่างเดียวคือ Goal ผมก็เซตเลยว่าสิ้นปีอยากมีเงินแสน ทุกเดือนมันก็จะบอกเลยว่าต้องใส่เท่าไร ยิ่งพอทำงานมีเงินเดือนแล้วเงินเก็บครบแสนนี่ไม่ยากเลย อย่างนึงคือผมค่อนข้างจะเที่ยวน้อยด้วยแหละ (ตั้งแต่ทำงานมาผมใช้เงินเที่ยวเดือนละประมาณเกือบสองพัน หรือสี่เท่าจากตอนเรียนอยู่ ซึ่งคิดว่าคนปกติก็ใช้ประมาณนี้มั้ง?)

เอ๊ะ… ปีที่แล้วเขียนว่าหมื่นห้า?? ตลกเถอะหมื่นห้านั่นเก็บสองสามเดือนเองนะ!

Tw3

ผมคิดว่าคงต้องประกาศไว้ตรงนี้แล้วแหละว่าผมจะล้มโครงการ Twitica แล้ว เพราะผมเองเล่น Twitter น้อยลงมากๆ ในขณะที่ Twitter มัน friendly กับ Developer น้อยลงเรื่อยๆ อย่างที่เห็นชัดๆ เลยคือมันไม่มี Poll API หรือ Notification API

(ผมกำลังสงสัยว่าที่เล่นลดลงเพราะผมเกลียด UI ของ Tw3 หรือเปล่า)

สรุปโดยรวมแล้ว New year resolution ที่ผ่านมาเป็นไปได้สวยมากๆ ครับ 3/4 success แน่ะ ขอให้ปีหน้าเป็นไปได้อย่างนี้บ้าง…

2017 New Year Resolution

พูดเพราะ

เดี๋ยวนี้รู้สึกว่าคำบางคำที่แต่ก่อนแย่มาก “อะไรวะ” “กู” พวกนี้เดี๋ยวนี้มันจะไม่ใช่คำหยาบคายสักเท่าไรแล้วนะ เพราะดูคนรอบตัวก็จะใช้อยู่บ้าง แต่ก็เวลาใช้ก็รู้สึกไม่ค่อยดีเท่าไร ปีหน้าคิดว่าจะเลิกใช้คำพวกนี้ไปเลยจะได้ไหม…

(จะว่าไป เคยมีคนทักในเน็ตประมาณ 2 รอบว่านึกว่าเราเป็นผู้หญิง ถามว่าทำไมเค้าบอกว่าจากสไตล์การพิมพ์ที่ใช้คำว่าเราแทนตัวเอง)

อีกลักษณะนึงที่ไม่ชอบมากคือสไตล์การพูดแบบ “เขียนโค้ดก็ทำกับคอมน่ะสิ ทำกับพิมพ์ดีดละมั้ง!” ซึ่งคนแถวนี้ๆ นี่แหละใช้อยู่บ้าง แล้วเราก็ติดมาเพราะเห็นว่ามันเท่ดี (มันทำให้เห็นว่าอีกคนคิดตามไม่ทันอยู่) ก็คิดว่าถ้าเค้าจะไม่เลิกเราก็คงต้องเลิกเอง

สไตล์สุดท้ายที่ไม่ควรจะใช้เลย อันนี้ก็ไม่ค่อยใช้ แต่ก็เหมือนจะเคยมีบ้างคือแบบ “ปิดเบาๆ ไม่ได้หรอ” ซึ่งมัน passive-aggressive แรงมากๆ อันนี้ไม่รู้คนพูดเค้าคิดว่ามัน soft กว่าประโยคคำสั่งหรือเปล่า แต่เราว่ามัน aggressive มากๆ

Backup

รู้สึกว่าชีวิตละเลยการ backup มากๆ เคยเขียนในเฟสว่า notebook ทำงานมีนโยบายว่า format มันเมื่อไรก็ได้ แต่นั่นไม่ใช่กับ Workstation ถ้าหาย Library เพลงที่สะสมมาเกลี้ยงแน่นอน รวมถึงบน server นี่เองก็ไม่ค่อยได้ backup (เคย backup เทพสุดก็ตอนทำ menome ที่ database daily backup ขึ้น cloud เลย)

ปีหน้าน่าจะเริ่มทำ automated backup ได้แล้ว แต่คอมส่วนตัวนี่ไม่รู้ว่ายังไงดี… encrypted backup ลง cloud? ซื้อ NAS?

ซื้อเพลง

(ทำไมชอบเขียน wishlist เสียเงิน…)

ปลายๆ ปีจัด YNAB มาใหม่แล้วก็คิดว่าควรจะจัดงบซื้อเพลงแยกไป แต่เอาเข้าจริงก็ไม่ค่อยได้ซื้อเพลงเท่าไร (เพราะมันรวมกับค่าดูหนังด้วย) และ music streaming ก็ไม่ได้ใช้แล้ว เพราะเดิมใช้ Deezer แต่คลังเพลงมันหดลงเรื่อยๆ ในขณะที่ Apple Music client ก็ไม่ค่อยดี, Joox free ก็ bitrate ต่ำกว่า YouTube (คิดว่าเสียเงินจะดีขึ้น แต่ยังไม่อยากลอง) ก็เลยคิดว่าน่าจะเริ่มต้องซื้อเพลงที่ฟังบ่อยๆ แล้ว

ปีที่ผ่านมาเหมือนจะซื้อไปแค่ Your Name OST (บางเพลง) กับ Mekakucity Records (CD) เองแฮะ

บริจาค

ปีสองปีก่อนผมตั้งงบใน You Need a Budget ว่าจะบริจาค รวมได้พันนิดๆ หมดปีก็ว่าจะบริจาค แต่สุดท้ายผมไม่รู้จะบริจาคให้ใครก็เลยเอาเงินเข้ากระเป๋า

ตอนนั้นเงื่อนไขเป็นแบบนี้ครับ

  1. เป็นโครงการ open source ที่ใช้ใน product ได้เงิน
  2. ไม่มี commercial offering (เช่น รับ consult หรือมี product เสียเงินอย่าง Docker)
  3. ถ้าเป็นไปได้ไม่อยากให้เจ้าที่มี sponsorship อยู่แล้วด้วย (เช่น Django ได้ Mozilla Grant) เพราะอยากให้ developer ตัวเล็กๆ มากกว่า

ปัญหาคือตอนนั้นติดข้อ 2 ผมนึกไม่ออกว่าจะให้ใครดี ก็เลยพับไป

ตอนนี้เอาเป็นว่า 5% จากรายได้ของ TMStreamlabs ตลอดปี 2017 จะเก็บไว้ Donate สักอย่างครับ ถ้าหาตามเงื่อนไขไม่ได้ผมจะสนับสนุน Software Freedom Conservancy องค์กรที่สนับสนุนการฟ้องร้องบริษัทที่ละเมิด Open source software license ซึ่งในปีที่ผ่านมาเคสดังๆ ก็จะมีเคส VMware ที่ SF Conservancy สนับสนุนอยู่

อยาก pledge โครงการอื่นๆ ด้วยนะครับ แต่คงต้องดูก่อนล่ะว่าปีหน้าจะได้ทำอะไรบ้าง

2017 Predictions

ผมกลับไปอ่านหัวข้อ Prediction จากบล็อค 2015 แล้วรู้สึกว่าชอบหัวข้อนี้มากๆ อ่านแล้วรู้สึกว่าน่ารักดี สำหรับปีหน้าเราจะมีอะไรบ้างล่ะ…

Project จบ

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

ทำงาน

ผมบอกพ่อว่าเรียนจบแล้วอยากอยู่บ้านสักสามเดือน ถ้ามี project อะไรทำ (ตอนนี้อยู่บ้านทำ TMStreamlabs ก็สนุกดี) ถ้ามองจากตอนนี้ยังคิดว่ากลับไปหางานทำดีกว่า

สำหรับงานที่จะได้ทำ คิดว่าสุดท้ายแล้วอาจจะหางานเงินเดือนดีอย่างที่อยากได้ไม่ได้ คิดว่าคงจะหางานในบริษัทที่อยากทำงานด้วยมากกว่า แต่ก็ไม่รู้ว่าทางบ้านจะว่ายังไงบ้างล่ะ (ก่อนผมจะได้มาวงในนี่ก็ไฟท์พอสมควรนะ) เผลอๆ ถ้าเรียงเงินเดือนเข้างานในรุ่นแล้วผมอาจจะไม่ได้สูงสุดก็ได้

คำถามหนึ่งที่ฝึกงานที่วงในให้ผมไว้คือผมอยากทำอะไรกันแน่ ผมนั่งเซ็ต Kubernetes ให้วงในทั้งสัปดาห์ ก็รู้สึกว่าน่าเบื่อ ไม่ได้เขียนโค้ดเลย ต่อมาเขียนโค้ดทุกวันก็โอเค แต่ก็อยากมีเบรคบ้าง ตอนเดือนที่เป็น part time นี่ล่ะที่ได้ทำอะไรตามใจแล้วก็ได้ทำทั้งสองอย่างพร้อมๆ กันคือ scaling ระบบ ก็รู้สึกว่าสนุกดีแต่ทรัพยากรที่ให้มันจำกัดไปหน่อย คิดว่างานแบบนี้ในไทยคงไม่ได้มีเยอะเท่าไร สุดท้ายแล้วก็ยังสงสัยว่าจบมาแล้วทำอะไรดี… แต่ถ้าให้ตอบตอบนี้คิดว่าคงได้ทำงานเขียนโปรแกรมนี่ล่ะ่

เรียนต่อ?

ปีสองปีที่ผ่านมา อ. ภุชงค์ บอกผมหลายรอบว่าอยากให้ไปเรียนต่ออเมริกา แต่ดูท่าแล้วผมอาจจะต้องเก็บเงินเองบางส่วนด้วย ในขณะเดียวกันผมก็รู้สึกว่าถ้าเรียนจบแล้วไปทำงานโอกาสเหล่านี้จะหลุดลอยไป ถึงเวลานั้นแล้วคงเป็นเวลาล่องลอยของผมอีกทีหนึ่งว่าจะทำอะไร

Docker: init daemon

หัวข้อนี้จะลงลึกไปถึงระบบลินุกซ์นะครับ แต่ก็เป็นจุดที่คนใช้ Docker ควรจะทราบไว้เพราะเป็นอะไรที่้ใช้ OS ปกติไม่เจอ ใช้ Docker จะเจอ แล้วเราจึงจะต้องเตรียมรับมือเอาไว้อย่าง signal forwarding

Signal forwarding

เมื่อเราสั่ง sudo docker stop นั้น Docker จะส่งสัญญาณ SIGTERM ให้โปรแกรมแรกใน container (process ID 1 หรือ PID 1) ซึ่งโปรแกรมทั่วๆ ไปเมื่อได้รับสัญญาณนี้แล้วก็จะปิดตัวเองไป หรือเราอาจจะเขียนโค้ดมาให้โปรแกรมเซฟข้อมูลก่อนก็ได้ (แทบทุกภาษาทำได้ครับ แม้แต่ shell script)

ปัญหาก็คือ… แน่ใจได้อย่างไรว่าโปรแกรมของเราได้รับสัญญาณจาก Docker เพราะถ้าไม่ได้รับแล้วสั่ง stop ไปมันก็จะไม่หยุดทำงาน (จน Docker timeout แล้ว kill ทิ้งแบบไม่สนใจ)

  • จุดแรกที่บางคนอาจจะไม่เคยสังเกตเลยคือ ถ้าเราใช้คำสั่ง CMD/ENTRYPOINT ในรูปแบบ string (CMD x) โปรแกรมแรกที่เปิดใน container คือ sh -c "x" ทำให้ sh เป็น PID 1 ไม่ใช่โปรแกรมของเรา วิธีที่ถูกคือต้องใช้ในรูปของ array (CMD ["x"]) (เลิกใช้แบบ string เถอะครับ ไม่มีเหตุผลจำเป็นเลย)
  • จุดที่สองคือคำสั่ง su/sudo นั้นก็ทำตัวเป็น PID 1 ได้ครับ ถ้าจำเป็นต้องใช้จริงๆ แนะนำ gosu ซึ่งมันทำงานเสร็จแล้วมันจะสลายตัวไปเอง ทั้งนี้ถ้าใช้ในลักษณะ shell script ใช้ sudo ก็ไม่ผิดครับ
  • จุดถัดมา บางคนเอา shell script เป็น entrypoint เพื่อให้มัน setup ก่อนเรียกโปรแกรม อันนี้ก็ต้องตรวจดูว่าการเรียกโปรแกรมของเรานั้นจะต้องใช้คำสั่ง exec นำหน้าด้วยนะครับ เช่น exec python /app/main.py เพื่อให้ sh ถูกแทนที่ด้วย python ไม่ใช่ให้ sh เป็นแม่ของ python

เหตุผลคือโปรแกรมทั้ง sh/bash/su/sudo นั้นจะไม่มีการส่งต่อสัญญาณหาโปรแกรมลูกครับ

ปัญหายังไม่จบครับ ปัญหาต่อมาคือโดยปกติ OS จะมี default behavior เวลาได้รับ signal ต่างๆ แต่พอโปรแกรมถูกรันเป็น PID 1 OS จะไม่มี default ให้ครับ นั่นคือถ้าไม่ได้เขียนระบุไว้ว่า SIGTERM ให้ปิด โปรแกรมของเราจะไม่ทำอะไรเลย (ใน Docker เองถ้า stop 10 วินาทีแล้วไม่ปิดมันจะ kill ทิ้งครับ นี่คือเหตุผลที่ stop โปรแกรมบางตัวนานมาก)

ปัญหานี้จะหมดไปถ้า container ของเราทำงานตามหลักที่ OS กำหนดให้ครับ โดยวิธีง่ายที่สุดก็คือการลง init daemon

Init daemon

ในระบบ UNIX ทั่วๆ ไปแล้วโปรแกรม process ID 1 นั้นจะเป็นโปรแกรมประเภท init daemon

init ที่ใช้กันก็มีหลายเจ้า ไม่ว่าจะเป็น sysvinit, upstart ของ Ubuntu, systemd ที่กำลังมาแรงในขณะนี้, OpenRC หรือแม้แต่ OS X ก็มี init ของตัวเองชื่อ launchd แต่ใน Docker เราจะไม่เอาโปรแกรมพวกนี้มาใช้ เพราะมันไม่ได้ออกแบบมาเหมาะสำหรับ container เท่าไร (ผมเคยมี container ที่รัน systemd เวลารันต้อง mount ไฟล์ระบบเข้าไป แถม host os ต้องเป็น systemd อีกต่างหาก)

เท่าที่เห็นกันในโลก Docker ตัวที่ใช้หลักๆ มีดังนี้ครับ

tini

สำหรับการใช้งาน tini นั้นค่อนข้างง่ายครับ เพิ่มใน Dockerfile ว่า

ENV TINI_VERSION v0.10.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]

หรือถ้าใช้ alpine ก็

RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]

(โปรแกรมของเราให้ใช้ CMD ใส่ได้เหมือนเดิมครับ)

s6

อีกตัวหนึ่งที่อยากแนะนำตัว และผมใช้อยู่ปัจจุบันคือ s6 ครับ โดย s6 จะเหมือนกับ init ทั่วไปของลินุกซ์มากกว่า คือสามารถ start process หลายๆ ตัวได้ด้วย และมีความสามารถอื่นๆ หลายอย่าง เช่นคำสั่งแทน sudo, logrotate แต่มีขนาดเล็ก

ผมใช้ s6 กับ s6-overlay ซึ่งเหมือนเป็น s6 ที่ config เพิ่มความสามารถเข้ามาดังนี้ครับ

  • การ set permission file ก่อนรัน (อาจจะใช้กับ volume ที่ mount มาตอน runtime)
  • รันคำสั่งก่อนเริ่มทำงาน

วิธีการใช้ s6-overlay ไม่ยาก ทำตาม guide ของเค้าได้เลย

ติดตั้ง

วิธีติดตั้งไม่ยากครับ ให้ใส่ใน Dockerfile ว่า

ADD https://github.com/just-containers/s6-overlay/releases/download/v1.11.0.1/s6-overlay-amd64.tar.gz /tmp/
RUN tar xzf /tmp/s6-overlay-amd64.tar.gz -C /
ENTRYPOINT ["/init"]

แล้วโปรแกรมของเราก็ใช้ CMD ["..."] ได้ตามปกติเลย หรืออาจจะใช้วิธี start service จาก config ก็ได้ครับ

Config

สำหรับไฟล์ config หลักๆ ของ s6-overlay มีดังนี้ครับ

/etc/fix-attrs.d/

สำหรับ folder นี้จะถูกรันเป็นลำดับแรก โดยมีหน้าที่ setup permission ต่างๆ มีรูปแบบดังนี้

/var/lib/mysql true mysql 0600 0700

แปลว่าให้กำหนดให้ /var/lib/mysql เป็นของ user mysql (สามารถระบุแบบ user:group ได้เลย) ให้ไฟล์ภายในมีสิทธิ์ 0600 โฟลเดอร์ 0700 และให้เซตแบบ recursive ด้วย (จากคำสั่ง true)

สำหรับการตั้งชื่อไฟล์ตั้งอะไรก็ได้ครับ แต่โดยปกติแล้วจะตั้งเป็น 01-name เพื่อจะได้กำหนดลำดับการรันก่อน-หลังได้

/etc/cont-init.d

ใน folder นี้จะเก็บ shell scripts ที่จะรันก่อนเปิดโปรแกรมของเรา อย่าลืม chmod +x ให้ไฟล์ด้วยนะครับ และตั้งชื่อไฟล์ปกติก็จะเหมือนกันคือ 01-name

shell script ใน s6 จะไม่เห็น environment ที่เรากำหนดเข้ามา (docker run -e ...) นะครับ ถ้าจะให้เห็นให้เราเปลี่ยน #!/bin/sh เป็น #!/usr/bin/with-contenv sh

/etc/services.d

มาถึงจุดสำคัญแล้วครับ นั่นคือการกำหนดโปรแกรมที่จะรันใน container ของเรา ซึ่งจะมีหลายตัวก็ได้ครับ

(แต่อย่าลืมว่า container ไม่ใช่ VM รันที่จำเป็นจริงๆ ก็พอ ถ้าเป็นไปได้แยกโปรแกรมละ container ไปเลยยิ่งดี)

วิธีใช้งานคือให้สร้าง folder ย่อยลงไป เช่น /etc/services.d/nginx/ แล้วด้านในมีไฟล์ชื่อ run (อย่าลืม chmod +x ไฟล์นี้) เป็น shell script เขียนคำสั่งที่ต้องการรัน (อย่าลืมใช้คำสั่ง exec)

ถ้าโปรแกรมของเราปิดตัว s6 จะพยายาม restart ให้เองครับ ถ้าไม่ต้องการให้เราสร้างอีกไฟล์มาชื่อ finish (เช่นกัน chmod +x ด้วย) และเขียนข้างในว่า

#!/usr/bin/execlineb -S0

s6-svscanctl -t /var/run/s6/services

โดย finish จะเป็น script ที่รันทุกครั้งที่โปรแกรมเราถูก restart และคำสั่งในนี้ก็คือคำสั่งบอกให้ s6 ปิดตัวนั่นเอง

s6 ยังมีฟีเจอร์อีกมากมาย ลองศึกษาได้จากเว็บของ s6 เอง และของ s6-overlay ดูครับ

ไม่ใช้ init ได้ไหม

หน้าที่ที่ PID 1 ที่ OS กำหนดให้มีดังนี้ครับ

  1. ส่งสัญญาณที่ OS ให้มาไปหาโปรแกรมลูก
  2. ทำตามสัญญาณที่ OS ส่งมาให้ ซึ่งปกติโปรแกรมทั่วไป OS จะเซต default ให้ แต่ไม่ใช่กับ PID 1
  3. ถ้ามี zombie process (process ที่ตายแล้วและ process ผู้สร้างมันก็ตายไปแล้วเช่นกัน) PID 1 จะต้องบอก OS ว่าไม่ใช้งานแล้ว เพื่อให้ OS คืนหน่วยความจำ

จะเห็นว่าหน้าที่พวกนี้โปรแกรมของเราไม่ได้ทำเลยครับ (แม้แต่ sh/bash ก็ไม่ได้ทำ) ฉะนั้นแล้วการใช้ init จึงจะทำให้ container ของเราทำงานได้อย่างที่เราคิดไว้ คือสั่ง stop แล้วปิดได้จริงๆ ถ้าโปรแกรมของเรามีการเรียกโปรแกรมอื่นๆ ก็จะทำให้ไม่เกิด memory leak ขึ้น

ตรงข้อ 3 นี้ถ้าไม่ได้แยก process ลูกไปมั่วๆ ก็ไม่จำเป็นเท่าไรครับ และโปรแกรมบางตัวมีการจัดการ SIGTERM เองอยู่แล้ว (เพราะจะทำอะไรบางอย่างก่อนปิดโปรแกรม) เราเลยจะเห็นว่าอิมเมจของโปรแกรมดังๆ จะไม่ได้ใช้ init เท่าไร แต่ในโปรแกรมของเราก็ลองพิจารณาดูครับ