Giữ lại một ứng dụng Node.js để phát triển với Docker Compose
Nếu bạn đang tích cực phát triển một ứng dụng, việc sử dụng Docker có thể đơn giản hóa quy trình làm việc và quy trình triển khai ứng dụng của bạn vào production . Làm việc với containers đang phát triển mang lại những lợi ích sau:- Các môi trường nhất quán, nghĩa là bạn có thể chọn ngôn ngữ và phụ thuộc bạn muốn cho dự án của bạn mà không cần lo lắng về xung đột hệ thống.
- Các môi trường được tách biệt, giúp khắc phục sự cố dễ dàng hơn và giới thiệu các thành viên mới trong group .
- Môi trường có tính di động, cho phép bạn đóng gói và chia sẻ mã của bạn với người khác.
Hướng dẫn này sẽ chỉ cho bạn cách cài đặt môi trường phát triển cho ứng dụng Node.js bằng Docker. Bạn sẽ tạo hai containers - một cho ứng dụng Node và một cho database MongoDB - với Docker Compose . Vì ứng dụng này hoạt động với Node và MongoDB, cài đặt của ta sẽ thực hiện như sau:
- Đồng bộ hóa mã ứng dụng trên server với mã trong containers để tạo điều kiện thay đổi trong quá trình phát triển.
- Đảm bảo rằng các thay đổi đối với mã ứng dụng hoạt động mà không cần khởi động lại.
- Tạo database được bảo vệ bằng password và user cho dữ liệu của ứng dụng.
- Kiên trì dữ liệu này.
Ở cuối hướng dẫn này, bạn sẽ có một ứng dụng thông tin cá mập đang hoạt động chạy trên containers Docker:
Yêu cầu
Để làm theo hướng dẫn này, bạn cần :
- Một server phát triển chạy Ubuntu 18.04, cùng với một user không phải root có quyền
sudo
và một firewall đang hoạt động. Để được hướng dẫn về cách cài đặt những điều này, vui lòng xem hướng dẫn Cài đặt Server Ban đầu này. - Docker được cài đặt trên server của bạn, làm theo các Bước 1 và 2 của Cách cài đặt và sử dụng Docker trên Ubuntu 18.04 .
- Docker Compose được cài đặt trên server của bạn, làm theo Bước 1 của Cách cài đặt Docker Compose trên Ubuntu 18.04 .
Bước 1 - Nhân bản dự án và sửa đổi dependencies
Bước đầu tiên trong việc xây dựng cài đặt này sẽ là sao chép mã dự án và sửa đổi file package.json
của nó, bao gồm các phụ thuộc của dự án. Ta sẽ bổ sung thêm nodemon
tới của dự án devDependencies
, chỉ rõ rằng ta sẽ sử dụng nó trong quá trình phát triển. Chạy ứng dụng với nodemon
đảm bảo nó sẽ được tự động khởi động lại khi nào bạn áp dụng các thay đổi đối với mã của bạn .
Đầu tiên, sao chép kho lưu trữ nodejs-mongo-mongoose
từ tài khoản GitHub Cộng đồng DigitalOcean . Kho lưu trữ này bao gồm mã từ cài đặt được mô tả trong Cách tích hợp MongoDB với ứng dụng Node của bạn , phần này giải thích cách tích hợp database MongoDB với ứng dụng Node hiện có bằng Mongoose .
Sao node_project
repository vào một folder có tên là node_project
:
- git clone https://github.com/do-community/nodejs-mongo-mongoose.git node_project
Điều hướng đến folder node_project
:
- cd node_project
Mở file package.json
của dự án bằng nano
hoặc editor bạn quen dùng :
- nano package.json
Bên dưới các phần phụ thuộc của dự án và phía trên dấu ngoặc nhọn đóng, hãy tạo một đối tượng devDependencies
mới bao gồm các nodemon
:
... "dependencies": { "ejs": "^2.6.1", "express": "^4.16.4", "mongoose": "^5.4.10" }, "devDependencies": { "nodemon": "^1.18.10" } }
Lưu file khi bạn hoàn tất chỉnh sửa.
Với mã dự án tại chỗ và các phụ thuộc của nó đã được sửa đổi, bạn có thể chuyển sang cấu trúc lại mã cho một quy trình làm việc được tích hợp.
Bước 2 - Cấu hình ứng dụng của bạn để hoạt động với containers
Sửa đổi ứng dụng của ta cho một quy trình làm việc được chứa nghĩa là làm cho mã của ta trở nên module hơn. Các containers cung cấp khả năng di động giữa các môi trường và mã của ta phải phản ánh điều đó bằng cách được tách rời khỏi hệ điều hành cơ bản nhất có thể. Để làm điều này, ta sẽ cấu trúc lại mã của bạn để sử dụng nhiều hơn thuộc tính process.env của Node, thuộc tính này sẽ trả về một đối tượng với thông tin về môi trường user của bạn trong thời gian chạy. Ta có thể sử dụng đối tượng này trong mã của bạn để gán động thông tin cấu hình trong thời gian chạy với các biến môi trường.
Hãy bắt đầu với app.js
, điểm nhập ứng dụng chính của ta . Mở tập tin:
- nano app.js
Bên trong, bạn sẽ thấy định nghĩa cho hằng số port
, cũng như hàm listen
sử dụng hằng số này để chỉ định cổng mà ứng dụng sẽ lắng nghe:
... const port = 8080; ... app.listen(port, function () { console.log('Example app listening on port 8080!'); });
Hãy xác định lại hằng số port
để cho phép gán động trong thời gian chạy bằng cách sử dụng đối tượng process.env
. áp dụng các thay đổi sau đối với định nghĩa không đổi và hàm listen
:
... const port = process.env.PORT || 8080; ... app.listen(port, function () { console.log(`Example app listening on ${port}!`); });
Định nghĩa hằng số mới của ta chỉ định port
động bằng cách sử dụng giá trị được truyền vào lúc chạy hoặc 8080
. Tương tự như vậy, ta đã viết lại hàm listen
để sử dụng một ký tự mẫu , sẽ nội suy giá trị cổng khi lắng nghe các kết nối. Bởi vì ta sẽ ánh xạ các cổng của bạn ở nơi khác, những bản sửa đổi này sẽ ngăn ta phải liên tục sửa đổi file này khi môi trường của ta thay đổi.
Khi bạn hoàn tất chỉnh sửa, hãy lưu file .
Tiếp theo, ta sẽ sửa đổi thông tin kết nối database của bạn để xóa bất kỳ thông tin đăng nhập cấu hình nào. Mở file db.js
, chứa thông tin sau:
- nano db.js
Hiện tại, file thực hiện những việc sau:
- Nhập Mongoose, Trình lập bản đồ tài liệu đối tượng (ODM) mà ta đang sử dụng để tạo schemas và mô hình cho dữ liệu ứng dụng của ta .
- Đặt thông tin xác thực database làm hằng số, bao gồm tên user và password .
- Kết nối với database bằng phương thức
mongoose.connect
.
Để biết thêm thông tin về file , vui lòng xem Bước 3 của Cách tích hợp MongoDB với Ứng dụng Node của bạn .
Bước đầu tiên của ta khi sửa đổi file sẽ là xác định lại các hằng số bao gồm thông tin nhạy cảm. Hiện tại, các hằng số này trông giống như sau:
... const MONGO_USERNAME = 'sammy'; const MONGO_PASSWORD = 'your_password'; const MONGO_HOSTNAME = '127.0.0.1'; const MONGO_PORT = '27017'; const MONGO_DB = 'sharkinfo'; ...
Thay vì mã hóa cứng thông tin này, bạn có thể sử dụng đối tượng process.env
để nắm bắt các giá trị thời gian chạy cho các hằng số này. Sửa đổi khối để trông như thế này:
... const { MONGO_USERNAME, MONGO_PASSWORD, MONGO_HOSTNAME, MONGO_PORT, MONGO_DB } = process.env; ...
Lưu file khi bạn hoàn tất chỉnh sửa.
Đến đây, bạn đã sửa đổi db.js
để hoạt động với các biến môi trường của ứng dụng, nhưng bạn vẫn cần một cách để chuyển các biến này vào ứng dụng của bạn . Hãy tạo một file .env
với các giá trị mà bạn có thể chuyển vào ứng dụng của bạn trong thời gian chạy.
Mở tập tin:
- nano .env
Tệp này sẽ bao gồm thông tin mà bạn đã xóa khỏi db.js
: tên user và password cho database ứng dụng của bạn, cũng như cài đặt cổng và tên database . Hãy nhớ cập nhật tên user , password và tên database được liệt kê ở đây với thông tin của bạn :
MONGO_USERNAME=sammy MONGO_PASSWORD=your_password MONGO_PORT=27017 MONGO_DB=sharkinfo
Lưu ý ta đã xóa cài đặt server lưu trữ ban đầu xuất hiện trong db.js
Bây giờ ta sẽ xác định server của bạn ở cấp file Docker Compose, cùng với thông tin khác về các dịch vụ và containers của ta .
Lưu file này khi bạn hoàn tất chỉnh sửa.
Vì file .env
của bạn có chứa thông tin nhạy cảm, bạn cần đảm bảo nó có trong các .dockerignore
và .gitignore
của dự án để nó không sao chép vào containers hoặc điều khiển version của bạn.
Mở file .dockerignore
của bạn:
- nano .dockerignore
Thêm dòng sau vào cuối file :
... .gitignore .env
Lưu file khi bạn hoàn tất chỉnh sửa.
Tệp .gitignore
trong repository lưu trữ này đã bao gồm .env
, nhưng hãy kiểm tra xem nó có ở đó không:
- nano .gitignore
... .env ...
Đến đây, bạn đã extract thành công thông tin nhạy cảm từ mã dự án của bạn và thực hiện các biện pháp để kiểm soát cách thức và vị trí thông tin này được sao chép. Như vậy, bạn có thể tăng cường độ mạnh mẽ hơn cho mã kết nối database của bạn để tối ưu hóa nó cho quy trình làm việc được tích hợp.
Bước 3 - Sửa đổi Cài đặt Kết nối Database
Bước tiếp theo của ta sẽ là làm cho phương thức kết nối database của ta mạnh mẽ hơn bằng cách thêm mã xử lý các trường hợp ứng dụng của ta không kết nối được với database của ta . Giới thiệu mức độ phục hồi này cho mã ứng dụng của bạn là một phương pháp được khuyến khích khi làm việc với containers bằng Soạn thư.
Mở db.js
để chỉnh sửa:
- nano db.js
Bạn sẽ thấy mã mà ta đã thêm trước đó, cùng với hằng số url
cho URI kết nối của Mongo và phương thức connect
Mongoose :
... const { MONGO_USERNAME, MONGO_PASSWORD, MONGO_HOSTNAME, MONGO_PORT, MONGO_DB } = process.env; const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`; mongoose.connect(url, {useNewUrlParser: true});
Hiện tại, phương thức connect
của ta chấp nhận một tùy chọn yêu cầu Mongoose sử dụng trình phân tích cú pháp URL mới của Mongo. Hãy thêm một vài tùy chọn khác vào phương thức này để xác định các tham số cho các nỗ lực kết nối lại. Ta có thể làm điều này bằng cách tạo một hằng số options
bao gồm thông tin liên quan, ngoài tùy chọn phân tích cú pháp URL mới. Bên dưới hằng số Mongo của bạn, hãy thêm định nghĩa sau cho hằng số options
:
... const { MONGO_USERNAME, MONGO_PASSWORD, MONGO_HOSTNAME, MONGO_PORT, MONGO_DB } = process.env; const options = { useNewUrlParser: true, reconnectTries: Number.MAX_VALUE, reconnectInterval: 500, connectTimeoutMS: 10000, }; ...
Các reconnectTries
tùy chọn bảo Mongoose để tiếp tục cố gắng để kết nối vô thời hạn, trong khi reconnectInterval
xác định khoảng thời gian giữa những nỗ lực kết nối trong mili giây. connectTimeoutMS
xác định 10 giây là khoảng thời gian mà trình điều khiển Mongo sẽ đợi trước khi kết nối thất bại.
Bây giờ ta có thể sử dụng hằng số options
mới trong phương thức connect
Mongoose để tinh chỉnh cài đặt kết nối Mongoose của bạn . Ta cũng sẽ thêm một lời hứa để xử lý các lỗi kết nối tiềm ẩn.
Hiện tại, phương thức connect
Mongoose trông giống như sau:
... mongoose.connect(url, {useNewUrlParser: true});
Xóa phương thức connect
hiện có và thay thế nó bằng mã sau, bao gồm hằng số options
và một lời hứa:
... mongoose.connect(url, options).then( function() { console.log('MongoDB is connected'); }) .catch( function(err) { console.log(err); });
Trong trường hợp kết nối thành công, chức năng của ta ghi lại một thông báo thích hợp; nếu không nó sẽ catch
và ghi lại lỗi, cho phép ta khắc phục sự cố.
Tệp đã hoàn thành sẽ giống như sau:
const mongoose = require('mongoose'); const { MONGO_USERNAME, MONGO_PASSWORD, MONGO_HOSTNAME, MONGO_PORT, MONGO_DB } = process.env; const options = { useNewUrlParser: true, reconnectTries: Number.MAX_VALUE, reconnectInterval: 500, connectTimeoutMS: 10000, }; const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`; mongoose.connect(url, options).then( function() { console.log('MongoDB is connected'); }) .catch( function(err) { console.log(err); });
Lưu file khi bạn đã chỉnh sửa xong.
Bây giờ, bạn đã thêm khả năng phục hồi vào mã ứng dụng của bạn để xử lý các trường hợp ứng dụng của bạn có thể không kết nối được với database của bạn. Với mã này, bạn có thể chuyển sang xác định các dịch vụ của bạn với Soạn thư.
Bước 4 - Xác định Dịch vụ với Docker Compose
Với mã của bạn đã được cấu trúc lại, bạn đã sẵn sàng để ghi file docker-compose.yml
với các định nghĩa dịch vụ của bạn . Dịch vụ trong Soạn là một containers đang chạy và các định nghĩa dịch vụ - mà bạn sẽ đưa vào file docker-compose.yml
- chứa thông tin về cách mỗi containers images sẽ chạy. Công cụ Soạn thư cho phép bạn xác định nhiều dịch vụ để xây dựng các ứng dụng đa containers .
Tuy nhiên, trước khi xác định các dịch vụ của bạn , ta sẽ thêm một công cụ vào dự án của bạn có tên là wait-for
đảm bảo rằng ứng dụng của ta chỉ cố gắng kết nối với database của ta khi các việc khởi động database hoàn tất. Tập lệnh shell bọc này sử dụng netcat
để thăm dò xem server và cổng cụ thể có chấp nhận kết nối TCP hay không. Sử dụng nó cho phép bạn kiểm soát nỗ lực của ứng dụng để kết nối với database của bạn bằng cách kiểm tra xem database đã sẵn sàng chấp nhận kết nối hay chưa.
Mặc dù tính năng Soạn thư cho phép bạn chỉ định dependencies giữa các dịch vụ bằng cách sử dụng tùy chọn depends_on
, thứ tự này dựa trên việc containers có đang chạy hay không chứ không phải là tính sẵn sàng của nó. Việc sử dụng depends_on
sẽ không tối ưu cho cài đặt của ta , vì ta muốn ứng dụng của bạn chỉ kết nối khi các việc khởi động database , bao gồm thêm user và password vào database xác thực admin
, hoàn tất. Để biết thêm thông tin về cách sử dụng wait-for
và các công cụ khác để kiểm soát thứ tự khởi động, vui lòng xem các khuyến nghị liên quan trong tài liệu Soạn .
Mở một file có tên là wait-for.sh
:
- nano wait-for.sh
Dán mã sau vào file để tạo chức năng bỏ phiếu:
#!/bin/sh # original script: https://github.com/eficode/wait-for/blob/master/wait-for TIMEOUT=15 QUIET=0 echoerr() { if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi } usage() { exitcode="$1" cat << USAGE >&2 Usage: $cmdname host:port [-t timeout] [-- command args] -q | --quiet Do not output any status messages -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout -- COMMAND ARGS Execute command with args after the test finishes USAGE exit "$exitcode" } wait_for() { for i in `seq $TIMEOUT` ; do nc -z "$HOST" "$PORT" > /dev/null 2>&1 result=$? if [ $result -eq 0 ] ; then if [ $# -gt 0 ] ; then exec "$@" fi exit 0 fi sleep 1 done echo "Operation timed out" >&2 exit 1 } while [ $# -gt 0 ] do case "$1" in *:* ) HOST=$(printf "%s\n" "$1"| cut -d : -f 1) PORT=$(printf "%s\n" "$1"| cut -d : -f 2) shift 1 ;; -q | --quiet) QUIET=1 shift 1 ;; -t) TIMEOUT="$2" if [ "$TIMEOUT" = "" ]; then break; fi shift 2 ;; --timeout=*) TIMEOUT="${1#*=}" shift 1 ;; --) shift break ;; --help) usage 0 ;; *) echoerr "Unknown argument: $1" usage 1 ;; esac done if [ "$HOST" = "" -o "$PORT" = "" ]; then echoerr "Error: you need to provide a host and port to test." usage 2 fi wait_for "$@"
Lưu file khi bạn hoàn tất việc thêm mã.
Làm cho tập lệnh có thể thực thi:
- chmod +x wait-for.sh
Tiếp theo, mở file docker-compose.yml
:
- nano docker-compose.yml
Đầu tiên, xác định dịch vụ ứng dụng nodejs
bằng cách thêm mã sau vào file :
version: '3' services: nodejs: build: context: . dockerfile: Dockerfile image: nodejs container_name: nodejs restart: unless-stopped env_file: .env environment: - MONGO_USERNAME=$MONGO_USERNAME - MONGO_PASSWORD=$MONGO_PASSWORD - MONGO_HOSTNAME=db - MONGO_PORT=$MONGO_PORT - MONGO_DB=$MONGO_DB ports: - "80:8080" volumes: - .:/home/node/app - node_modules:/home/node/app/node_modules networks: - app-network command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js
Định nghĩa dịch vụ nodejs
bao gồm các tùy chọn sau:
-
build
: Tùy chọn này xác định các tùy chọn cấu hình, bao gồmcontext
vàdockerfile
, sẽ được áp dụng khi Compose xây dựng hình ảnh ứng dụng. Nếu bạn muốn sử dụng hình ảnh hiện có từ một nơi đăng ký như Docker Hub , bạn có thể sử dụng hướng dẫnimage
thay thế, với thông tin về tên user , repository và thẻ hình ảnh của bạn. -
context
: Điều này xác định bối cảnh xây dựng cho bản dựng hình ảnh - trong trường hợp này là folder dự án hiện tại. -
dockerfile
: Điều này chỉ địnhDockerfile
trong folder dự án hiện tại của bạn làm file Compose sẽ sử dụng để xây dựng hình ảnh ứng dụng. Để biết thêm thông tin về file này, vui lòng xem Cách tạo ứng dụng Node.js bằng Docker . -
image
,container_name
: Các tên này áp dụng cho hình ảnh và containers . -
restart
: Điều này xác định policy khởi động lại. Mặc định làno
, nhưng ta đã đặt containers khởi động lại trừ khi nó bị dừng. -
env_file
: Điều này cho Soạn biết rằng ta muốn thêm các biến môi trường từ một file có tên là.env
, nằm trong ngữ cảnh xây dựng của ta . -
environment
: Sử dụng tùy chọn này cho phép bạn thêm cài đặt kết nối Mongo mà bạn đã xác định trong file.env
. Lưu ý ta không đặtNODE_ENV
đểdevelopment
, vì đây là hành vi mặc định của Express nếuNODE_ENV
không được đặt. Khi chuyển sang production , bạn có thể đặt cài đặt này thànhproduction
để bật chế độ xem bộ nhớ đệm và ít thông báo lỗi dài dòng hơn . Cũng lưu ý ta đã chỉ định containers databasedb
làm server lưu trữ, như đã thảo luận trong Bước 2 . -
ports
: Điều này ánh xạ cổng80
trên server đến cổng8080
trên container . volumes
: Ta bao gồm hai loại mount ở đây:- Đầu tiên là một liên kết mount gắn mã ứng dụng của ta trên server lưu trữ vào folder
/home/node/app
trên containers . Điều này sẽ tạo điều kiện phát triển nhanh chóng, vì bất kỳ thay đổi nào bạn thực hiện đối với mã server của bạn sẽ được điền ngay vào containers . - Thứ hai là một khối được đặt tên,
node_modules
. Khi Docker chạynpm install
hướng dẫn liệt kê trong đơnDockerfile
,npm
sẽ tạo ra một mớinode_modules
folder trên thùng bao gồm các gói cần thiết để chạy các ứng dụng. Tuy nhiên, mount mount mà ta vừa tạo sẽ ẩn foldernode_modules
mới được tạo này. Vìnode_modules
trên server trống, liên kết sẽ ánh xạ một folder trống đến containers , overrides foldernode_modules
mới và ngăn ứng dụng của ta khởi động. Dung lượngnode_modules
được đặt tên giải quyết vấn đề này bằng cách duy trì nội dung của folder/home/node/app/node_modules
và gắn nó vào containers , ẩn ràng buộc.
Hãy ghi nhớ những điểm sau khi sử dụng phương pháp này :
- Liên kết của bạn sẽ mount nội dung của folder
node_modules
trên containers vào server lưu trữ và folder này sẽ thuộc quyền sở hữu củaroot
, vì ổ đĩa được đặt tên được tạo bởi Docker. - Nếu bạn có folder
node_modules
từ trước trên server , nó sẽ overrides foldernode_modules
được tạo trên containers . Cài đặt mà ta đang xây dựng trong hướng dẫn này giả định bạn không có foldernode_modules
từ trước và bạn sẽ không làm việc vớinpm
trên server của bạn .Điều này phù hợp với cách tiếp cận mười hai yếu tố để phát triển ứng dụng , giúp giảm thiểu dependencies giữa các môi trường thực thi.
- Đầu tiên là một liên kết mount gắn mã ứng dụng của ta trên server lưu trữ vào folder
networks
: Điều này chỉ định rằng dịch vụ ứng dụng của ta sẽ tham giaapp-network
mạng, mà ta sẽ xác định ở cuối file .command
: Tùy chọn này cho phép bạn đặt lệnh sẽ được thực hiện khi Compose chạy hình ảnh. Lưu ý điều này sẽ overrides hướng dẫnCMD
mà ta đặt trongDockerfile
ứng dụng của ta . Ở đây, ta đang chạy ứng dụng bằng tập lệnhwait-for
tập lệnh này sẽ thăm dò dịch vụdb
trên cổng27017
để kiểm tra xem dịch vụ database đã sẵn sàng hay chưa. Sau khi kiểm tra mức độ sẵn sàng thành công, tập lệnh sẽ thực thi lệnh mà ta đã đặt,/home/node/app/node_modules/.bin/nodemon app.js
, để khởi động ứng dụng vớinodemon
. Điều này sẽ đảm bảo bất kỳ thay đổi nào trong tương lai mà ta thực hiện đối với mã của bạn đều được reload mà ta không phải khởi động lại ứng dụng.
Tiếp theo, tạo dịch vụ db
bằng cách thêm mã sau vào bên dưới định nghĩa dịch vụ ứng dụng:
... db: image: mongo:4.1.8-xenial container_name: db restart: unless-stopped env_file: .env environment: - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD volumes: - dbdata:/data/db networks: - app-network
Một số cài đặt ta đã xác định cho dịch vụ nodejs
vẫn giữ nguyên, nhưng ta cũng đã áp dụng các thay đổi sau đối với định nghĩa image
, environment
và volumes
:
-
image
: Để tạo dịch vụ này, Compose sẽ lấy hình ảnh4.1.8-xenial
Mongo từ Docker Hub. Ta đang ghim một version cụ thể để tránh xung đột có thể xảy ra trong tương lai khi hình ảnh Mongo thay đổi. Để biết thêm thông tin về ghim version , vui lòng xem tài liệu Docker về các phương pháp hay nhất của Dockerfile . -
MONGO_INITDB_ROOT_USERNAME
,MONGO_INITDB_ROOT_PASSWORD
: Hình ảnhmongo
cung cấp các biến môi trường này để bạn có thể sửa đổi quá trình khởi tạo version database của bạn .MONGO_INITDB_ROOT_USERNAME
vàMONGO_INITDB_ROOT_PASSWORD
cùng nhau tạo userroot
trong database xác thựcadmin
và đảm bảo xác thực được bật khi containers khởi động. Ta đã đặtMONGO_INITDB_ROOT_USERNAME
vàMONGO_INITDB_ROOT_PASSWORD
bằng cách sử dụng các giá trị từ file.env
của ta , ta chuyển sang dịch vụdb
bằng tùy chọnenv_file
. Làm điều này nghĩa là user ứng dụngsammy
của ta sẽ là userroot
trên cá thể database , có quyền truy cập vào tất cả các quyền quản trị và hoạt động của role đó. Khi làm việc trong production , bạn cần tạo một user ứng dụng chuyên dụng với các quyền trong phạm vi phù hợp. Lưu ý: Lưu ý các biến này sẽ không có hiệu lực nếu bạn khởi động containers với một folder dữ liệu hiện có tại chỗ. -
dbdata:/data/db
:dbdata
ổ đĩa được đặt tên sẽ giữ nguyên dữ liệu được lưu trữ trong thư mục dữ liệu mặc định của Mongo,/data/db
. Điều này sẽ đảm bảo bạn không bị mất dữ liệu trong trường hợp bạn dừng hoặc xóa containers .
Ta cũng đã thêm dịch vụ db
vào app-network
mạng với tùy chọn networks
.
Bước cuối cùng, hãy thêm định nghĩa dung lượng và mạng vào cuối file :
... networks: app-network: driver: bridge volumes: dbdata: node_modules:
Mạng app-network
mạng cầu nối do user xác định cho phép giao tiếp giữa các containers của ta vì chúng nằm trên cùng một server Docker daemon. Điều này hợp lý hóa lưu lượng truy cập và giao tiếp trong ứng dụng, vì nó mở tất cả các cổng giữa các container trên cùng một mạng cầu, đồng thời không để lộ cổng nào ra thế giới bên ngoài. Do đó, các containers db
và nodejs
của ta có thể giao tiếp với nhau và ta chỉ cần để lộ cổng 80
để truy cập front-end vào ứng dụng.
Cấp cao nhất của ta volumes
định nghĩa then chốt dung lượng dbdata
và node_modules
. Khi Docker tạo tập, nội dung của tập được lưu trữ trong một phần của hệ thống file server , /var/lib/docker/volumes/
, được Docker quản lý. Nội dung của mỗi tập được lưu trữ trong một folder dưới /var/lib/docker/volumes/
và được gắn vào bất kỳ containers nào sử dụng tập. Bằng cách này, dữ liệu thông tin cá mập mà user của ta sẽ tạo sẽ tồn tại trong ổ đĩa dbdata
ngay cả khi ta xóa và tạo lại containers db
.
Tệp docker-compose.yml
đã hoàn thành sẽ giống như sau:
version: '3' services: nodejs: build: context: . dockerfile: Dockerfile image: nodejs container_name: nodejs restart: unless-stopped env_file: .env environment: - MONGO_USERNAME=$MONGO_USERNAME - MONGO_PASSWORD=$MONGO_PASSWORD - MONGO_HOSTNAME=db - MONGO_PORT=$MONGO_PORT - MONGO_DB=$MONGO_DB ports: - "80:8080" volumes: - .:/home/node/app - node_modules:/home/node/app/node_modules networks: - app-network command: ./wait-for.sh db:27017 -- /home/node/app/node_modules/.bin/nodemon app.js db: image: mongo:4.1.8-xenial container_name: db restart: unless-stopped env_file: .env environment: - MONGO_INITDB_ROOT_USERNAME=$MONGO_USERNAME - MONGO_INITDB_ROOT_PASSWORD=$MONGO_PASSWORD volumes: - dbdata:/data/db networks: - app-network networks: app-network: driver: bridge volumes: dbdata: node_modules:
Lưu file khi bạn hoàn tất chỉnh sửa.
Với các định nghĩa dịch vụ của bạn, bạn đã sẵn sàng để bắt đầu ứng dụng.
Bước 5 - Kiểm tra ứng dụng
Với file docker-compose.yml
của bạn tại chỗ, bạn có thể tạo các dịch vụ của bạn bằng lệnh docker-compose up
. Bạn cũng có thể kiểm tra xem dữ liệu của bạn có tồn tại hay không bằng cách dừng và xóa containers bằng docker-compose down
.
Đầu tiên, xây dựng các containers images và tạo các dịch vụ bằng cách chạy docker-compose up
với cờ -d
, sau đó sẽ chạy các containers nodejs
và db
trong nền:
- docker-compose up -d
Bạn sẽ thấy kết quả xác nhận các dịch vụ của bạn đã được tạo:
Output... Creating db ... done Creating nodejs ... done
Bạn cũng có thể nhận được thông tin chi tiết hơn về các quy trình khởi động bằng cách hiển thị kết quả log từ các dịch vụ:
- docker-compose logs
Bạn sẽ thấy thông tin như thế này nếu mọi thứ đã bắt đầu đúng :
Output... nodejs | [nodemon] starting `node app.js` nodejs | Example app listening on 8080! nodejs | MongoDB is connected ... db | 2019-02-22T17:26:27.329+0000 I ACCESS [conn2] Successfully authenticated as principal sammy on admin
Bạn cũng có thể kiểm tra trạng thái của các containers của bạn bằng docker-compose ps
:
- docker-compose ps
Bạn sẽ thấy kết quả cho biết rằng các containers của bạn đang chạy:
Output Name Command State Ports ---------------------------------------------------------------------- db docker-entrypoint.sh mongod Up 27017/tcp nodejs ./wait-for.sh db:27017 -- ... Up 0.0.0.0:80->8080/tcp
Khi các dịch vụ của bạn đang chạy, bạn có thể truy cập http:// your_server_ip
trong trình duyệt. Bạn sẽ thấy một trang đích giống như sau:
Nhấp vào nút Nhận thông tin cá mập . Bạn sẽ thấy một trang có mẫu mục nhập, nơi bạn có thể nhập tên cá mập và mô tả về đặc điểm chung của con cá mập đó:
Trong biểu mẫu, hãy thêm một con cá mập mà bạn chọn. Với mục đích của phần trình diễn này, ta sẽ thêm Megalodon Shark
vào trường Shark Name và Ancient
vào trường Shark Character :
Bấm vào nút Gửi . Bạn sẽ thấy một trang với thông tin cá mập này được hiển thị lại cho bạn:
Bước cuối cùng, ta có thể kiểm tra xem dữ liệu bạn vừa nhập sẽ tồn tại nếu bạn xóa containers database của bạn .
Quay lại terminal của bạn, nhập lệnh sau để dừng và xóa containers và mạng của bạn:
- docker-compose down
Lưu ý ta không bao gồm tùy chọn --volumes
; do đó, dung lượng dbdata
của ta không bị xóa.
Kết quả sau xác nhận containers và mạng của bạn đã bị xóa:
OutputStopping nodejs ... done Stopping db ... done Removing nodejs ... done Removing db ... done Removing network node_project_app-network
Tạo lại các containers :
- docker-compose up -d
Bây giờ quay trở lại biểu mẫu thông tin cá mập:
Nhập một con cá mập mới mà bạn chọn. Ta sẽ đi với Whale Shark
và Large
:
Khi bạn nhấp vào Gửi , bạn sẽ thấy rằng con cá mập mới đã được thêm vào bộ sưu tập cá mập trong database của bạn mà không làm mất dữ liệu bạn đã nhập:
Ứng dụng của bạn hiện đang chạy trên containers Docker với tính năng ổn định dữ liệu và đồng bộ hóa mã được bật.
Kết luận
Theo hướng dẫn này, bạn đã tạo cài đặt phát triển cho ứng dụng Node của bạn bằng cách sử dụng containers Docker. Bạn đã làm cho dự án của bạn có tính module và di động hơn bằng cách extract thông tin nhạy cảm và tách trạng thái của ứng dụng khỏi mã ứng dụng của bạn. Bạn cũng đã cấu hình file docker-compose.yml
mà bạn có thể sửa đổi khi nhu cầu phát triển và yêu cầu của bạn thay đổi.
Khi bạn phát triển, bạn có thể quan tâm đến việc tìm hiểu thêm về cách thiết kế các ứng dụng cho quy trình làm việc được chứa trong container và Cloud Native . Vui lòng xemỨng dụng kiến trúc cho Kubernetes và Ứng dụng hiện đại hóa cho Kubernetes để biết thêm thông tin về các chủ đề này.
Để tìm hiểu thêm về mã được sử dụng trong hướng dẫn này, vui lòng xem Cách tạo ứng dụng Node.js với Docker và Cách tích hợp MongoDB với ứng dụng Node của bạn . Để biết thông tin về cách triển khai ứng dụng Node với Reverse Proxy Nginx bằng cách sử dụng containers , vui lòng xem Cách bảo mật ứng dụng Node.js bị chứa bằng Nginx, Let's Encrypt và Docker Compose .
Các tin liên quan
Cách cài đặt và sử dụng Docker Compose trên CentOS 72019-01-23
Cách sử dụng Traefik làm reverse-proxy cho container Docker trên Debian 9
2019-01-08
Cách thiết lập registry Docker riêng trên Ubuntu 18.04
2019-01-07
Cách thiết lập triển khai nhiều node với Rancher 2.1, Kubernetes và Docker Machine trên Ubuntu 18.04
2019-01-03
Cách tạo ứng dụng Node.js với Docker
2018-11-29
Cách quản lý triển khai nhiều node với Máy Rancher và Docker trên Ubuntu 16.04
2018-10-30
Cách cài đặt và sử dụng Docker trên Ubuntu 16.04
2018-10-19
Cách cung cấp và quản lý server Docker từ xa bằng Máy Docker trên Ubuntu 18.04
2018-10-02
Cách cài đặt và bảo mật OpenFaaS bằng Docker Swarm trên Ubuntu 16.04
2018-09-19
Cách cài đặt Docker Compose trên Debian 9
2018-09-06