ลองนึกภาพต้องพิมพ์คำสั่งที่ยาวซ้ำๆ สำหรับงานต่างๆ เช่น การสร้างและใช้งานคอนเทนเนอร์ Docker มันจะน่าเบื่อและใช้เวลานาน
อย่างไรก็ตาม ด้วยการกำหนดสคริปต์ npm เราสามารถทริกเกอร์งานเหล่านี้ได้อย่างง่ายดายด้วยคำสั่งที่สั้นและชัดเจน มันปรับปรุงขั้นตอนการทำงานของเราและลดความพยายามที่จำเป็นในการทำงานทั่วไปให้สำเร็จในฐานะนักพัฒนา ทำให้กระบวนการพัฒนามีประสิทธิภาพและสนุกสนานมากขึ้น
สคริปต์ Npm มอบแนวทางที่สะดวกและมีประสิทธิภาพในการทำงานซ้ำๆ โดยอัตโนมัติ ปรับปรุงประสิทธิภาพการทำงาน และช่วยให้เรามุ่งเน้นไปที่ธุรกิจหลักของเราได้
ในคำแนะนำนี้ เราจะสร้างคอนเทนเนอร์นักเทียบท่าอย่างง่ายและจะทดลองกับสคริปต์ npm ต่างๆ เพื่อดูวิธีเพิ่มประสิทธิภาพกระบวนการสร้างของเรา
เริ่มต้นใช้งาน
มาตั้งค่าพื้นที่เก็บข้อมูลกันเถอะ จากนั้นเราจะทดลองใช้สคริปต์ npm เพื่อให้การพัฒนาง่ายขึ้น
ในโฟลเดอร์ใหม่ ให้เรียกใช้สิ่งต่อไปนี้:
npm init -y
ธง -y
ย่อมาจาก 'ใช่' หรือ 'ใช่ทั้งหมด' เมื่อคุณใช้งาน จะช่วยให้คุณสามารถเริ่มต้นไฟล์ package.json
ใหม่ด้วยค่าเริ่มต้นทั้งหมดโดยไม่ต้องแจ้งให้ผู้ใช้ทราบ หมายความว่าคุณยอมรับการตั้งค่าเริ่มต้นทั้งหมดสำหรับการกำหนดค่า package.json
ของคุณโดยอัตโนมัติ
ตอนนี้ มาติดตั้ง Express เป็นการขึ้นต่อกันกัน เราจะต้องมีสิ่งนี้ในไฟล์ server.js ที่เราจะสร้างล่วงหน้า
npm i express
ตอนนี้สร้างไฟล์ใหม่ —server.js
ที่รูทของที่เก็บนี้ เพื่อให้ความสำคัญกับคำแนะนำแบบทีละขั้นตอนนี้เกี่ยวกับสคริปต์ npm เราจะสร้างแอปพลิเคชัน Node.js พื้นฐานโดยใช้เฟรมเวิร์ก Express เพื่อสร้างเซิร์ฟเวอร์ HTTP แบบง่ายที่ทำสิ่งต่อไปนี้:
- นำเข้าโมดูล Express และสร้างแอปพลิเคชัน Express
- ตั้งค่าพอร์ตเซิร์ฟเวอร์เป็นพอร์ต
4005
นอกจากนี้เรายังสามารถใช้ตัวแปรสภาพแวดล้อมPORT
โดยใช้process.env
หรือตั้งค่าเริ่มต้นเป็น 4005 - กำหนดข้อความที่จะส่งเป็นการตอบสนองต่อไคลเอนต์เมื่อพวกเขาเข้าถึงเส้นทางรูท ('/')
- ตั้งค่าเส้นทางเพื่อจัดการคำขอ HTTP GET ไปยังเส้นทางรูท เมื่อเข้าถึงแล้วจะบันทึกข้อความไปยังคอนโซลเซิร์ฟเวอร์และส่งข้อความที่กำหนดเป็นการตอบกลับ
- สตาร์ทเซิร์ฟเวอร์บนพอร์ตที่ระบุ และเมื่อเซิร์ฟเวอร์เริ่มทำงาน เซิร์ฟเวอร์จะบันทึกข้อความที่กำหนดไปยังคอนโซล
server.js
จะมีลักษณะดังนี้:
const express = require('express'); const app = express(); const port = process.env.PORT || 4005; let runningMessage = 'Server is running on port ' + port; app.get('/', (req, res) => { console.log('API was requested'); res.send(runningMessage); } ); const server = app.listen(port, () => { console.log(runningMessage); });
ตอนนี้เรามาสร้าง Dockerfile ซึ่งตั้งค่าอิมเมจ Docker สำหรับแอปพลิเคชันของเรา โดยจะติดตั้งการขึ้นต่อกันของแอปพลิเคชัน คัดลอกโค้ดแอปพลิเคชัน และระบุคำสั่งเริ่มต้นเพื่อเริ่มแอปพลิเคชัน Node.js โดยใช้ node server.js
ในขณะที่เปิดเผยพอร์ต 4005 ภายในคอนเทนเนอร์ ในการเรียกใช้แอปพลิเคชัน เราจะต้องสร้างอิมเมจจาก Dockerfile นี้ จากนั้นจึงสร้างและเริ่มต้นคอนเทนเนอร์ตามอิมเมจนั้น ในไดเร็กทอรีราก ให้สร้าง Dockerfile:
FROM node:18.10.0 # Create app directory WORKDIR /usr/src/app # Install app dependencies COPY package*.json ./ RUN npm install # Bundle app source COPY . . # Expose port EXPOSE 4005 # Run app CMD [ "node", "server.js" ]
สร้าง .dockerignore
ในโฟลเดอร์รูท:
node_modules npm-debug.log
ไฟล์ .dockerignore
จำเป็นสำหรับการควบคุมเนื้อหาของอิมเมจ Docker ลดขนาด เพิ่มความปลอดภัย และทำให้มั่นใจว่าข้อมูลที่ละเอียดอ่อนจะไม่ถูกเปิดเผยโดยไม่ได้ตั้งใจ
ตอนนี้ทุกอย่างได้รับการตั้งค่าแล้ว เรามาเพิ่มสคริปต์พื้นฐานสองตัวใน package.json
กัน ไฟล์. ภายใต้วัตถุ scripts
ให้เพิ่มสคริปต์ npm ต่อไปนี้:
"docker:build": "docker build -t npm_docker .", "docker:run": "docker run -p 4005:4005 -d --name npm_d npm_docker",
แฟล็ก -t
ในคำสั่ง docker:build
ใช้เพื่อแท็กรูปภาพด้วยชื่อ (npm_docker
ในกรณีนี้) ชื่อนี้จะใช้เพื่ออ้างอิงรูปภาพเมื่อเรียกใช้คอนเทนเนอร์ตามรูปภาพนั้น
ในคำสั่ง docker:run
-p 4005:4005
maps พอร์ต 4005 จากโฮสต์ไปยังพอร์ต 4005 ในคอนเทนเนอร์ ซึ่งช่วยให้สามารถส่งการรับส่งข้อมูลจากเครื่องโฮสต์ไปยังแอปพลิเคชันที่ทำงานภายในคอนเทนเนอร์ได้ -d
บ่งชี้ว่าคอนเทนเนอร์ทำงานในโหมดเดี่ยว ซึ่งหมายความว่าคอนเทนเนอร์จะทำงานในเบื้องหลังในฐานะดีมอน แฟล็ก --name
ตั้งชื่อให้กับคอนเทนเนอร์ (npm_d
) ซึ่งสามารถใช้เพื่อจัดการและโต้ตอบกับคอนเทนเนอร์ในภายหลัง
แบบแผนการตั้งชื่อ
ที่นี่เราใช้รูปแบบการตั้งชื่อเช่น category:name
เนื่องจากให้ความชัดเจน การจัดระเบียบ และความสอดคล้องในไฟล์ package.json
ในบริบทของ docker:build
และ docker:run
มันบ่งบอกถึงเจตนาและความสัมพันธ์ของสคริปต์กับการทำงานของ Docker คำนำหน้า docker:
ระบุอย่างชัดเจนว่าสคริปต์เหล่านี้เกี่ยวข้องกับงาน Docker
แบบแผนนี้ช่วยหลีกเลี่ยงความขัดแย้งในการตั้งชื่อ เพิ่มความสามารถในการอ่าน และช่วยให้จัดกลุ่มและดำเนินการสคริปต์ที่เกี่ยวข้องได้ง่าย ด้วยการปฏิบัติตามข้อตกลงนี้ นักพัฒนาสามารถเข้าใจและจัดการ Docker ในการสร้างและรันงานภายในโปรเจ็กต์ได้อย่างง่ายดาย
หากต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับแบบแผนการตั้งชื่อ โปรดอ่านสิ่งนี้: package-json-conventions
งานพัฒนานักเทียบท่า
ความท้าทายเกิดขึ้นกับ Docker เมื่อเราต้องอัปเดตโค้ดแอปพลิเคชันภายในคอนเทนเนอร์ที่ทำงานอยู่ แตกต่างจากการตั้งค่าการพัฒนาแบบดั้งเดิม เราไม่สามารถสร้างอิมเมจ Docker ใหม่และรันใหม่ได้เนื่องจากคอนเทนเนอร์ที่มีอยู่กำลังทำงานอยู่แล้ว
ด้วย Docker อันดับแรกเราต้องหยุดคอนเทนเนอร์ไม่ให้ทำงาน จากนั้นจึงลบออก จากนั้นสร้างใหม่และรันคอนเทนเนอร์ใหม่อีกครั้งโดยใช้อิมเมจใหม่ทุกครั้งที่เราทำการเปลี่ยนแปลงซอร์สโค้ดในแอปพลิเคชันของเรา
มาเพิ่มอีกสองสคริปต์สำหรับสิ่งนี้ เราจะตั้งชื่อมันว่า docker:stop
และ docker:remove
เพื่อให้สอดคล้องกับรูปแบบการตั้งชื่อของเรา
ตอนนี้ updatescripts
ใน package.json
ดังต่อไปนี้:
... "scripts": { "docker:stop": "docker container stop npm_d", "docker:remove": "docker container rm npm_d", "docker:build": "docker build -t npm_docker .", "docker:run": "docker run -p 4005:4005 -d --name npm_d npm_docker", "test": "echo \"Error: no test specified\" && exit 1" }, ...
ตอนนี้เมื่อคุณทำการเปลี่ยนแปลงใน server.js
ของคุณ คุณสามารถสร้างคอนเทนเนอร์ที่อัปเดตใหม่ได้ แต่คุณไม่สามารถเรียกใช้ได้เนื่องจากคอนเทนเนอร์ที่มีชื่อเดียวกันกำลังทำงานอยู่แล้ว ดังนั้นคุณจำเป็นต้องรัน docker:stop
และ docker:remove
ก่อนที่จะรันคำสั่ง docker:run
งานพัฒนานักเทียบท่าอัตโนมัติ
กระบวนการหยุดคอนเทนเนอร์ที่ทำงานอยู่ ลบออก สร้างอิมเมจ Docker ขึ้นมาใหม่ และสร้างคอนเทนเนอร์ใหม่ทุกครั้งที่เราทำการเปลี่ยนแปลงโค้ด ไม่ใช่ขั้นตอนการทำงานที่มีประสิทธิภาพที่สุด อย่างไรก็ตาม เรามีข้อได้เปรียบเนื่องจาก Docker ให้อาร์กิวเมนต์ที่ช่วยให้เราสามารถรับฟังการเปลี่ยนแปลงโค้ดและอัปเดตคอนเทนเนอร์โดยอัตโนมัติโดยไม่ต้องทำตามขั้นตอนด้วยตนเอง
ดังนั้น แม้ว่าสคริปต์ NPM จะไม่ใช่ปัจจัยจำกัดในสถานการณ์นี้ ความสามารถของ Docker ในการรับฟังการเปลี่ยนแปลงโค้ดและแม็ปการเปลี่ยนแปลงเหล่านั้นลงในคอนเทนเนอร์ทำให้เรามีวิธีที่มีประสิทธิภาพและสะดวกยิ่งขึ้นในการพัฒนาและทดสอบแอปพลิเคชันของเราในสภาพแวดล้อม Dockerized
นี่คือเมื่อมีอาร์กิวเมนต์อื่นที่มีอยู่ในเครื่องมือ Docker CLI ที่เรียกว่า "การติดตั้งระดับเสียง" มาช่วย
มาอัปเดต scripts
ของเราใน package.json
อย่างรวดเร็วและดูว่าการเพิ่มระดับเสียงทำอะไรได้บ้าง
... "scripts": { "docker:stop": "docker container stop npm_d", "docker:remove": "docker container rm npm_d", "docker:build": "docker build -t npm_docker .", "docker:run": "docker run -p 4005:4005 -d --name npm_d npm_docker", "docker:run:dev": "docker run -p 4005:4005 -d -v %cd%:/usr/src/app --name npm_d npm_docker", "test": "echo \"Error: no test specified\" && exit 1" }, ...
สคริปต์ใน docker:run:dev
เกือบจะคล้ายกับสคริปต์ใน docker:run
ยกเว้นอาร์กิวเมนต์ -v ตัวเลือกนี้จะเมานต์ไดเร็กทอรีการทำงานปัจจุบัน (%cd%
) จากโฮสต์ไปยังไดเร็กทอรี /usr/src/app
ภายในคอนเทนเนอร์
หมายเหตุ: เส้นทางทางด้านซ้ายของเครื่องหมายทวิภาค (:
) คือเส้นทางไปยังตำแหน่งของซอร์สโค้ดของคุณ บนเครื่อง Windows เราสามารถใช้ %cd%
และเครื่อง Linux เราสามารถใช้ $(pwd)
เพื่อรับไดเร็กทอรีการทำงาน
เส้นทางไปทางด้านขวาของโคลอน (:
) คือเส้นทางไดเร็กทอรีภายในคอนเทนเนอร์ Docker ตำแหน่งที่คุณต้องการเมานต์หรือแมปซอร์สโค้ดของคุณ กระบวนการนี้จะซ้อนทับซอร์สโค้ดในเครื่องของคุณจากเครื่องโฮสต์ลงในคอนเทนเนอร์ ทำให้สามารถอัปเดตแบบไดนามิกทุกครั้งที่คุณทำการเปลี่ยนแปลงโค้ดของคุณ
เมื่อคุณใช้ -v
เพื่อเมานต์ซอร์สโค้ดในเครื่องของคุณลงในคอนเทนเนอร์ จะทำให้การเปลี่ยนแปลงสะท้อนให้เห็นภายในคอนเทนเนอร์โดยไม่ต้องสร้างใหม่ อย่างไรก็ตาม สิ่งนี้จะทำให้โค้ดของคุณสามารถเข้าถึงได้ภายในคอนเทนเนอร์เท่านั้น ไม่ได้แก้ไขปัญหาการตรวจจับและการรีสตาร์ทเซิร์ฟเวอร์ Node.js โดยอัตโนมัติทุกครั้งที่มีการเปลี่ยนแปลงเกิดขึ้นในโค้ดของคุณ เพื่อแก้ไขปัญหานี้ เราจะใช้ nodemon
Nodemon เป็นยูทิลิตี้ที่ตรวจสอบแอปพลิเคชัน Node.js ของคุณเพื่อหาการเปลี่ยนแปลงไฟล์ โดยจะรีสตาร์ทเซิร์ฟเวอร์ Node.js โดยอัตโนมัติเมื่อตรวจพบการแก้ไขใดๆ ในโค้ดเบสของคุณ คุณลักษณะนี้มีความสำคัญอย่างยิ่งต่อกระบวนการพัฒนาเนื่องจากช่วยให้คุณประหยัดปัญหาในการหยุดและรีสตาร์ทเซิร์ฟเวอร์ด้วยตนเองทุกครั้งที่คุณทำการเปลี่ยนแปลง
ตอนนี้ใน Dockerfile
เราต้องทำการเปลี่ยนแปลงสองอย่าง:
- เพื่อให้แน่ใจว่า Nodemon พร้อมใช้งานภายในคอนเทนเนอร์ เราจึงติดตั้งมันทั่วโลก ต่างจากการพัฒนาตรงที่มักจะติดตั้ง Nodemon เป็นการพึ่งพา dev ที่นี่เราต้องการมันสำหรับระบบที่เหมือนจริงภายในคอนเทนเนอร์ เมื่อติดตั้งทั่วโลก Nodemon จะกลายเป็นเครื่องมือบรรทัดคำสั่งที่สามารถเข้าถึงได้ทั่วทั้งคอนเทนเนอร์ ด้วยวิธีนี้ เราสามารถใช้ Nodemon เพื่อเริ่มต้นและตรวจสอบแอปพลิเคชันของเราสำหรับการเปลี่ยนแปลงในสภาพแวดล้อมที่เหมือนการใช้งานจริง
- ในการเริ่มแอปพลิเคชันของเราโดยใช้ Nodemon ภายในคอนเทนเนอร์ เราจะแทนที่คำสั่ง
node
ด้วยnodemon
เพื่อเรียกใช้server.js
นอกจากนี้ เราจะเพิ่มอาร์กิวเมนต์-L
เพื่อระบุโหมดนาฬิกาแบบเดิมเนื่องจากเราทำงานภายในคอนเทนเนอร์
Dockerfile
ที่อัปเดตควรมีลักษณะดังนี้:
FROM node:18.10.0 # Create app directory WORKDIR /usr/src/app # Install app dependencies COPY package*.json ./ RUN npm install RUN npm install -g nodemon # Bundle app source COPY . . # Expose port EXPOSE 4005 # Run app CMD [ "nodemon", "-L", "server.js" ]
ด้วยการเปลี่ยนแปลงนี้ ตอนนี้ Dockerfile ของเราได้รับการตั้งค่าอย่างถูกต้องเพื่อใช้ Nodemon เพื่อเริ่มแอปพลิเคชันและติดตามการเปลี่ยนแปลงโค้ด
ห่อ
ก่อนที่จะรัน docker:run:dev
เราจำเป็นต้องตรวจสอบให้แน่ใจว่าไม่มีคอนเทนเนอร์ใดที่ทำงานด้วยชื่อเดียวกัน เราสามารถรันสคริปต์ docker:stop
และ docker:remove
ที่เรากำหนดไว้ด้านบน จากนั้นจึงตามด้วย docker:build
ก่อนที่จะรัน docker:run:dev
นี่คือที่ที่เราสามารถใช้ &&
และ &
operator
ฟังก์ชั่น &&
เหมือนตรรกะ AND คำสั่งแรกถูกดำเนินการ และหากดำเนินการสำเร็จ (ส่งคืนโค้ดทางออกเป็น 0) คำสั่งที่สองเท่านั้นที่จะรัน ลักษณะการทำงานนี้ช่วยให้แน่ใจว่าคำสั่งที่ตามมาจะถูกดำเนินการตามเงื่อนไขตามความสำเร็จของคำสั่งก่อนหน้า ช่วยให้คุณสร้างลำดับของคำสั่งโดยที่แต่ละขั้นตอนขึ้นอยู่กับความสำเร็จของขั้นตอนก่อนหน้า
การใช้ &
ในสคริปต์ npm จะเริ่มต้นการดำเนินการแบบขนาน โดยอนุญาตให้คำสั่งที่สองทำงานอย่างอิสระในเบื้องหลัง แม้ว่าคำสั่งแรก คำสั่งยังอยู่ในระหว่างดำเนินการหรือพบข้อผิดพลาด
ตอนนี้ ด้วยความรู้นี้ เรามาลองแก้ไข scripts
ใน package.json
:
... "scripts": { "docker:stop": "docker container stop npm_d", "docker:remove": "docker container rm npm_d", "docker:build": "docker build -t npm_docker .", "docker:run:dev": "docker run -p 4005:4005 -d -v %cd%:/usr/src/app --name npm_d npm_docker", "docker:run": "npm run docker:stop & npm run docker:remove & npm run docker:build && npm run docker:run:dev" }, ...
ที่นี่เราใช้ &
ระหว่าง npm run docker:stop
, npm run docker:remove
และ npm run docker:build
เนื่องจากแม้ในการรันใหม่ใดๆ คอนเทนเนอร์ที่มีชื่อที่กำหนด ( npm_d
ในกรณีของเรา) ไม่มีอยู่ ดังนั้นสายคำสั่งจะต้องไม่หยุด และกระบวนการสร้างควรดำเนินต่อไป
อย่างไรก็ตาม &&
จะใช้ระหว่าง npm run docker:build
ถึง npm:docker:run
เนื่องจากหากกระบวนการ build ล้มเหลวไม่ว่าด้วยเหตุผลใดก็ตาม คำสั่ง run จะต้องไม่รัน และสายคำสั่งจะยุติลง
ทีนี้ลองทำให้มันง่ายขึ้นอีก เราจะใช้แพ็คเกจ npm-run-all
เพื่อรันสคริปต์ npm ตามลำดับ ติดตั้ง npm-run all
โดยใช้คำสั่งต่อไปนี้:
npm i npm-run-all
ตอนนี้สังเกตการเปลี่ยนแปลงใน scripts
จาก package.json
:
... "scripts": { "docker:stop": "docker container stop npm_d || true", "docker:remove": "docker container rm npm_d || true", "docker:build": "docker build -t npm_docker .", "docker:run": "docker run -p 4005:4005 -d -v %cd%:/usr/src/app --name npm_d npm_docker", "docker:run:dev": "npm-run-all docker:*" }, ...
สังเกตว่าเราใช้ docker:*
ใน docker:run:dev
อย่างไร npm-run-all
มอบวิธีที่หลากหลายและมีประสิทธิภาพในการจัดการและควบคุมการทำงานของสคริปต์ npm ลดความซับซ้อนและปรับปรุงประสิทธิภาพของกระบวนการสร้างของคุณ
คำสั่งข้างต้นหมายความว่าสคริปต์ทั้งหมดที่ขึ้นต้นด้วยคำนำหน้า docker:
จะทำงานตามลำดับ นี่คือจุดที่แบบแผนการตั้งชื่อของเรามีประโยชน์
ในที่นี้ || true
ช่วยให้มั่นใจว่าแม้ว่า docker:stop
และ docker:remove
จะแสดงข้อผิดพลาด สายการบังคับบัญชาจะต้องไม่หยุด และการดำเนินการจะย้ายไปยังคำสั่ง build
และนั่นก็มาถึงแล้ว เพียงแค่เรียกใช้ docker:run:dev
คุณก็พร้อมที่จะไป เมื่อใช้สคริปต์ npm คุณสามารถจัดการคอนเทนเนอร์ Docker และงานซ้ำๆ อื่นๆ ได้อย่างง่ายดาย ช่วยให้คุณมุ่งเน้นไปที่ธุรกิจหลักของคุณได้มากขึ้นและลดการตั้งค่าด้วยตนเอง
เนื้อหาเพิ่มเติมได้ที่ PlainEnglish.io.
ลงทะเบียนเพื่อรับ จดหมายข่าวรายสัปดาห์ฟรี ของเรา ติดตามเราบน Twitter, LinkedIn, YouTube และ Discord .