Tạo môi trường develop NodeJS với Docker
Bài đăng này đã không được cập nhật trong 8 năm
Mở đầu
Ngày nay, việc phát triển web có rất nhiều lựa chọn, không còn gói gọn trong những stack lâu đời như LAMP, Ruby on Rails ... nữa. Đặc biệt nổi lên gần đây là MEAN Stack (MongoDB, ExpressJS, AngularJS, NodeJS), sử dụng hoàn toàn 1 loại ngôn ngữ là Javascript để phát triển website (à đương nhiên vẫn có HTML, CSS (yaoming) ).
Việc cài đặt môi trường để phát triển sử dụng MEAN Stack cũng là một vấn đề nếu bạn không quen sử dụng Linux, Mac, hay đang dùng Windows (tin tôi đi, bạn không muốn cài đặt cái gì để dev trên Win đâu, hiện thời nó vẫn là 1 cực hình ). Do đó trong bài viết này, tôi sẽ giới thiệu với các bạn một cách sử dụng Docker và Docker-Compose để thiết lập môi trường lập trình với MEAN Stack mà có thể chạy tốt trên Windows, Mac hay Linux, đúng với tinh thần lười của anh em ta, install one and develop anywhere
Cài đặt Docker
Nếu như Windows, Mac và Linux là các môi trường khác nhau dùng để khởi chạy máy tính, thì khi phát triển phần mềm, chúng ta mong muốn có một môi trường thống nhất và gần với môi trường thực tế hệ thống sẽ sử dụng nhất để tránh những khó khăn có thể gặp phải khi cài đặt môi trường khác nhau, hoặc "em không hiểu sao máy em chạy ngon mà server thì lỗi". Và Docker là một ứng dụng được sinh ra để làm điều này. Với những khả năng như ABC, xyz ... (ABC, xyz là gì mời các bạn tra Google), Docker sẽ cho chúng ta một môi trường tách biệt hoàn toàn với môi trường của hệ điều hành trong máy của chúng ta, và đảm bảo cho dù bạn có sử dụng trên hệ điều hành nào đi nữa thì môi trường đó vẫn trước sau như một.
Nguyên lý khi sử dụng Docker là mỗi lần chạy Docker, bạn sẽ khởi tạo một container từ một image đã được tạo ra, container đó sẽ chạy một câu lệnh để làm một tác vụ gì đó. Dựa trên đặc điểm này, chúng ta sẽ tạo hai image, một image cho việc chạy database MongoDB, một image cho việc chạy Server NodeJS sử dụng ExpressJS. Lý do tại sao lại sử dụng 2 image như sau :
- 2 image cho 2 việc khác nhau, 1 là lưu trữ dữ liệu (DB), 1 là server web, đúng tinh thần server nào làm việc đó, không ôm đồm.
- Việc cấu hình cho 2 image làm 2 việc này dễ và sẵn có, hầu hết chỉ có kéo về và chạy thôi (yaoming).
Đương nhiên vẫn có cách để tạo một image chứa cả NodeJS và MongoDB, các bạn có thể thử với thứ tự sau :
- Tạo một Image chạy Centos
- Thêm các câu lệnh cài đặt NodeJS
- Thêm các câu lệnh cài đặt MongoDB
- Thiết lập câu lệnh khi khởi chạy container thì đồng thời khởi chạy cả NodeJS và MongoDB
Và sau đây tôi xin hướng dẫn sử dụng cách đầu tiên, mỗi image một chức năng khác nhau bằng cách sử dụng Docker-compose cho nó đơn giản.
Tuy nhiên trước hết chúng ta cần cài Docker, tạo file Dockerfile để cài NodeJS, các bạn có thể dùng các image sẵn có trên mạng, tuy nhiên cá nhân tôi dùng file setup riêng từ CentOS 6, vì server tôi sẽ deploy là dùng CentOS 6, các image của Node từ Docker dùng image khác, thừa này thiếu nọ, tôi không thích vì sẽ khác với môi trường production
Dưới đây là file Dockerfile của tôi:
FROM centos:6
RUN yum -y update
RUN yum -y groupinstall "Development Tools"
RUN yum -y install cronie
# gpg keys listed at https://github.com/nodejs/node
RUN set -ex \
&& for key in \
9554F04D7259F04124DE6B476D5A82AC7E37093B \
94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
0034A06D9D9B0064CE8ADF6BF1747F4AD2306D93 \
FD3A5288F042B6850C66B31F09FE44734EB7990E \
71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
B9AE9905FFD7803F25714661B63B535A4C206CA9 \
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
; do \
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
done
ENV NPM_CONFIG_LOGLEVEL info
ENV NODE_VERSION 4.4.5
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components=1 \
&& rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt
CMD [ "node" ]
RUN mkdir -p /usr/src/app
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN chmod +x startup.sh
RUN npm install -g nodemon
Giải thích: Các câu lệnh trên sẽ lần lượt lấy image CentOS 6 làm gốc, cài các lib dev cần thiết, download và cài node phiên bản 4.4.5, sau đó copy thư mục hiện hành vào thư mục /usr/src/app
và set thư mục mặc định chạy là usr/src/app
. Sau khi copy set quyền execute cho file startup.sh
và cài đặt nodemon
Việc copy thư mục hiện hành và set thư mục mặc định sẽ giúp các bạn việc sau: code của các bạn ở trên thư mục chứa file Dockerfile sẽ được copy vào container trên Docker và có thể dùng để chạy Node server. 2 lệnh set chmod cho startup.sh
và cài đặt nodemon
là optional, cái này mình sẽ giải thích sau, đại khái là giúp bạn dev sướng hơn
Vậy là đã xong file Dockerfile. Tiếp đến sẽ là Docker-compose
Cài đặt và cấu hình Docker-compose
Sau khi có file Dockerfile, mình sẽ sử dụng file image này cho web server (NodeJS, Express), còn Database mình sẽ sử dụng image MongoDB có sẵn của Docker. Tuy nhiên việc cài đặt, rồi link giữa 2 container khá là lằng nhằng, mất thời gian, nên mình sẽ sử dụng tool có tên là Docker-compose để làm hộ mình.
Giá trị của Docker-compose sẽ là giúp các bạn khởi chạy nhiều container, liên kết chúng vào với nhau thông qua 1 file setting, giúp chúng ta không cần viết script mỗi lần khởi chạy nữa, chỉ cần một câu lệnh docker-compose up
là môi trường của bạn sẽ sẵn sàng để làm việc.
Không nói nhiều nữa, dưới đây là nội dung file docker-compose.yml
cần thiết cho việc chạy docker-compose
:
version: '2'
services:
mongo:
image: mongo
ports:
- "32769:27017"
web:
build: .
working_dir: /usr/src/app
command: /bin/bash startup.sh
volumes:
- .:/usr/src/app
ports:
- "32768:3000"
depends_on:
- mongo
Chi tiết ý nghĩa của từng dòng lệnh các bạn có thể tìm hiểu trên google, ở đây ý nghĩa của chúng đại khái là:
- Tạo 2 service có tên là
mongo
vàweb
- Service mongo sử dụng image có tên là mongo
- Port của mongo là 27017 ở trong container, ở ngoài hệ điều hành thật là 32769
- Service web sử dụng image build từ thư mục hiện hành (là thư mục chứa file Dockerfile ở trên)
- Service web có thư mục làm việc là
/usr/src/app
, có volumes là thư mục hiện tại đến thư mục/usr/src/app
, nhằm mục đích khi thay đổi nội dung trên thư mục hiện tại ( code ), thì dữ liệu sẽ lập tức được cập nhật trên thư mục/usr/src/app
- Service web có command khởi chạy là
/bin/bash startup.sh
- Service web phụ thuộc vào service mongo, nếu service mongo không chạy thì service web sẽ không hoạt động.
Vậy là xong, bạn đã tạo ra 2 service có thể dùng để chạy server NodeJS và server database (mongo), còn một bước cuối cùng sẽ là file package.json
cùng file startup.sh
.
File package.json
(có thể tạo bằng npm init
, nhưng trong trường hợp bạn không thích cài npm lên OS thật, thì copy ở đây là xong ):
{
"name": "appname",
"version": "1.0.0",
"description": "app description",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"authors": [
"Ta Duy Anh <anhtd37@gmail.com>"
],
"dependencies": {
"aws-sdk": "^2.3.12",
"bluebird": "^2.9.32",
"body-parser": "^1.13.1",
"change-case": "^2.3.0",
"cheerio": "^0.19.0",
"cors": "^2.7.1",
"crypto": "0.0.3",
"debug": "^2.2.0",
"express": "^4.13.0",
"formidable": "^1.0.17",
"gm": "^1.23.0",
"kue": "^0.11.1",
"kue-scheduler": "^0.6.0",
"lodash": "^4.13.1",
"moment": "^2.10.3",
"mongo-express": "^0.30.59",
"mongoose": "^4.4.17",
"multer": "^1.1.0",
"mysql": "^2.0.0",
"node-cron": "^1.1.1",
"path-to-regexp": "^1.5.0",
"query-string": "^4.2.2",
"redis": "^0.12.1",
"request": "^2.58.0",
"request-promise": "^4.1.1",
"sequelize": "^3.3.2",
"sleep": "^3.0.1"
}
}
Ở đây file của mình hơi nhiều dependencies, nhưng chủ yếu bạn chỉ cần express + mongooes lúc đầu là đủ
Tiếp theo là file startup.sh
:
if [ ! -d /usr/src/app/node_modules ]; then
echo "Install dependencies..."
cd /usr/src/app && npm install --no-bin-links
fi
cd /usr/src/app && nodemon -L app.js
File này sẽ là file khởi chạy của service web mỗi khi docker-compose up
.
Mục đích của file này là sẽ chạy lệnh npm install
nếu như không tìm thấy thư mục node_modules trong thư mục hiện tại, với các đặc điểm sau:
- Cài đặt các node_modules cho việc phát triển một cách tự động
- Tránh việc phải upload node_modules lên repo
- Vì cài đặt bên trong container nên nếu muốn update node_modules, cần vào container -> suy nghĩ kỹ trước khi update =))
Sau khi xong xuôi các file trên, các bạn sẽ có một môi trường chạy ExpressJS cùng MongoDB để phát triển, nếu muốn dùng MySQL, các bạn chỉ cần cần đổi service database sang mysql (hoặc thêm service), sau đó nhớ add depends để service db chạy trước service web.
Một lưu ý nữa là mình dùng nodemon
để khởi động web server với câu lệnh : nodemon -L app.js
. Trong trường hợp webservice của các bạn sử dụng file khởi chạy là file khác, mời đổi lại tên file.
Đồng thời việc sử dụng nodemon
sẽ giúp code của các bạn ngay khi có thay đổi sẽ được cập nhật lên web service bằng cách... khởi động lại web service
Vì thời gian khởi động webservice của Express rất là không đáng kể, nên bạn có thể bật Sublime, bấm Ctrl + S liên tục để xem output thông báo server khởi động lại của ExpressJS
Ngoài ra có một lưu ý nhỏ khi dùng service mongo db như trên thì connection link sang MongoDB sẽ là : mongo/<database-name>
với mongo
là tên service. Ở đây mình dùng cổng mặc định là 27017 nên không cần phải config cổng, nếu dùng cổng khác các bạn tự thêm thành mongo:<tên_cổng>
là ok.
Trong trường hợp bạn muốn update file package.json thì sao? Chúng ta sẽ có những cách sau:
- Edit file package.json, thêm thư viện cần thêm, sau đó vào
/bin/bash
của container và chạynpm install
. Cách vào/bin/bash
của container :docker exec -it /bin/bash <container_name>
- Update file package.json, xóa thư mục node_modules và chạy lại lệnh
docker-compose up
- Cài npm lên hệ điều hành hiện tại và
npm install --save <package_name>
bình thường
Trên đây là hướng dẫn cài đặt môi trường phát triển MEAN Stack sử dụng Docker đang được mình sử dụng trong công việc, hy vọng rằng sẽ giúp ích được cho các bạn muốn làm quen với NodeJS
All rights reserved