Docker là một nền tảng mạnh mẽ giúp các nhà phát triển đóng gói và triển khai ứng dụng dưới dạng container. Một container là một tiến trình độc lập, chạy trên hệ điều hành chung, mang lại sự linh hoạt và nhẹ hơn nhiều so với việc sử dụng máy ảo.
Trong hướng dẫn này, bạn sẽ:
-
Tạo một image ứng dụng cho một trang web tĩnh sử dụng Express và Bootstrap.
-
Xây dựng container từ image này và đẩy nó lên Docker Hub để sử dụng sau này.
-
Tải xuống image từ Docker Hub và chạy lại container, cho thấy cách bạn có thể tái tạo và mở rộng ứng dụng của mình.
Yêu cầu trước khi bắt đầu
Bước 1 – Cài đặt các thư viện cần thiết
Đầu tiên, bạn cần tạo các tệp ứng dụng, sau đó sao chép chúng vào container. Những tệp này sẽ bao gồm nội dung tĩnh, mã nguồn ứng dụng và các thư viện phụ thuộc.
Tạo thư mục dự án
Tạo một thư mục mới trong thư mục home của người dùng:
mkdir node_project
Di chuyển vào thư mục này:
cd node_project
Tạo tệp package.json
để khai báo thông tin về ứng dụng:
nano package.json
Thêm nội dung sau vào file package.json
:
{ "name": "nodejs-image-demo", "version": "1.0.0", "description": "nodejs image demo", "author": "Sammy the Shark <sammy@example.com>", "license": "MIT", "main": "app.js", "keywords": [ "nodejs", "bootstrap", "express" ], "dependencies": { "express": "^4.16.4" } }
Chúng ta sẽ tạo một trang web cung cấp thông tin về cá mập cho người dùng.
Ứng dụng của chúng ta sẽ có tệp điểm vào chính (app.js
) và một thư mục views
chứa các tài nguyên tĩnh của dự án.
-
Trang chính (
index.html
) sẽ cung cấp thông tin sơ bộ cho người dùng và liên kết đến một trang chi tiết hơn về cá mập. -
Trang thông tin chi tiết (
sharks.html
) sẽ chứa nhiều nội dung hơn về các loài cá mập.
Chúng ta sẽ tạo cả index.html
và sharks.html
trong thư mục views
.
Tạo file app.js
để định nghĩa các route:
nano app.js
Thêm đoạn mã sau:
const express = require('express'); const app = express(); const router = express.Router(); const path = __dirname + '/views/'; const port = 8080;
Hàm require
sẽ tải module Express, sau đó chúng ta sử dụng nó để tạo các đối tượng app và router.
-
Đối tượng
router
sẽ chịu trách nhiệm điều hướng trong ứng dụng. -
Khi định nghĩa các tuyến HTTP (routes), chúng ta sẽ thêm chúng vào
router
để xác định cách ứng dụng xử lý các yêu cầu từ người dùng.
Phần này của tệp cũng thiết lập hai hằng số quan trọng:
path
: Xác định thư mục gốc chứa các tệp giao diện, chính là thư mụcviews
trong thư mục dự án.port
: Định nghĩa cổng mà ứng dụng sẽ lắng nghe, ở đây là8080
.
Tệp Dockerfile xác định những thành phần nào sẽ được đưa vào container ứng dụng khi nó chạy.
Việc sử dụng Dockerfile giúp bạn định nghĩa môi trường container và tránh sự không tương thích về thư viện phụ thuộc hoặc phiên bản runtime.
Theo các hướng dẫn xây dựng container tối ưu, chúng ta sẽ tạo một image hiệu quả bằng cách:
-
Giảm thiểu số lượng lớp (layers) của image.
-
Giới hạn chức năng của image chỉ để tái tạo các tệp ứng dụng và nội dung tĩnh.
Trong thư mục gốc của dự án, tạo tệp Dockerfile:
nano Dockerfile
Thêm nội dung sau vào Dockerfile:
FROM node:10-alpine
Image này bao gồm Node.js và npm. Mỗi Dockerfile phải bắt đầu với một lệnh FROM.
Mặc định, image Docker Node đã tích hợp sẵn một người dùng non-root có tên node, cho phép bạn tránh chạy container ứng dụng với quyền root. Đây là một biện pháp bảo mật được khuyến cáo, nhằm tránh việc chạy container dưới quyền root và giới hạn các khả năng bên trong container chỉ những gì cần thiết để chạy các tiến trình của nó. Vì vậy, chúng ta sẽ sử dụng thư mục home của người dùng node làm thư mục làm việc cho ứng dụng và thiết lập người dùng này trong container. Để biết thêm thông tin về các thực hành tốt nhất khi làm việc với image Docker Node, hãy xem hướng dẫn thực hành tốt nhất.
Để tinh chỉnh quyền truy cập trên mã nguồn ứng dụng trong container, hãy tạo thư mục con node_modules trong /home/node cùng với thư mục app. Việc tạo các thư mục này sẽ đảm bảo rằng chúng có các quyền mà chúng ta mong muốn, điều này rất quan trọng khi cài đặt các module node cục bộ trong container bằng lệnh npm install. Bên cạnh đó, chúng ta cũng sẽ thiết lập quyền sở hữu của các thư mục này cho người dùng node.
... RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
Để hiểu thêm về lợi ích của việc hợp nhất các lệnh RUN
, bạn có thể tham khảo các thảo luận về cách quản lý các lớp (layers) của container.
Tiếp theo, thiết lập thư mục làm việc của ứng dụng thành /home/node/app
:
... WORKDIR /home/node/app
Nếu không thiết lập WORKDIR
, Docker sẽ tự động tạo một thư mục, vì vậy việc chỉ định rõ ràng sẽ là một ý kiến hay.
Tiếp theo, sao chép các tệp package.json
và package-lock.json
(cho npm 5+) vào container:
... COPY package*.json ./
Việc thêm lệnh COPY
này trước khi chạy npm install
hoặc sao chép mã nguồn ứng dụng cho phép chúng ta tận dụng cơ chế cache của Docker. Ở mỗi giai đoạn của quá trình build, Docker sẽ kiểm tra xem có layer nào được cache cho lệnh đó không. Nếu tệp package.json
thay đổi, layer này sẽ được xây dựng lại; nếu không, Docker sẽ sử dụng layer đã có sẵn và bỏ qua việc cài đặt lại các module của node.
Để đảm bảo rằng tất cả các tệp ứng dụng (bao gồm cả nội dung của thư mục node_modules
) đều thuộc về người dùng non-root node, hãy chuyển đổi người dùng sang node trước khi chạy npm install
:
... USER node
Sau khi sao chép các gói phụ thuộc của dự án và chuyển đổi người dùng, chạy lệnh:
... RUN npm install
Tiếp theo, sao chép mã nguồn ứng dụng của bạn vào thư mục ứng dụng trên container với các quyền phù hợp.
... EXPOSE 8080 CMD [ "node", "app.js" ]
Lệnh EXPOSE không thực sự mở cổng, mà chỉ nhằm mục đích ghi chú các cổng trên container sẽ được công bố khi chạy ở thời điểm runtime. Lệnh CMD chạy lệnh khởi động ứng dụng – trong trường hợp này là node app.js
. Lưu ý rằng trong mỗi Dockerfile chỉ nên có duy nhất một lệnh CMD; nếu có nhiều hơn, chỉ lệnh CMD cuối cùng sẽ có hiệu lực.
Có rất nhiều thứ bạn có thể làm với Dockerfile. Để xem danh sách đầy đủ các lệnh, hãy tham khảo tài liệu tham khảo về Dockerfile của Docker.
Tệp Dockerfile hoàn chỉnh sẽ trông như sau:
FROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install COPY --chown=node:node . . EXPOSE 8080 CMD [ "node", "app.js" ]
Lưu và đóng tệp sau khi bạn hoàn tất chỉnh sửa.
Trước khi xây dựng image ứng dụng, hãy thêm tệp .dockerignore. Tệp .dockerignore hoạt động tương tự như tệp .gitignore, chỉ định những tệp và thư mục trong thư mục dự án mà không cần sao chép vào container.
Mở tệp .dockerignore:
nano .dockerignore
Thêm nội dung sau:
~/node_project/.dockerignore node_modules npm-debug.log Dockerfile .dockerignore
Nếu bạn đang sử dụng Git, bạn cũng nên thêm thư mục .git
và tệp .gitignore
vào tệp .dockerignore
.
Lưu và đóng tệp sau khi hoàn tất chỉnh sửa.
Bây giờ, bạn đã sẵn sàng để xây dựng image ứng dụng bằng lệnh docker build
. Sử dụng tham số -t
với docker build
cho phép bạn gán nhãn cho image với một tên dễ nhớ. Vì chúng ta sẽ đẩy image lên Docker Hub, hãy bao gồm tên người dùng Docker Hub của bạn trong nhãn. Chúng ta sẽ gán nhãn image là nodejs-image-demo
, nhưng bạn có thể thay thế bằng tên khác nếu muốn. Hãy nhớ thay your_dockerhub_username
bằng tên người dùng Docker Hub của bạn.
sudo docker build -t your_dockerhub_username/nodejs-image-demo .
Dấu chấm (.
) xác định build context là thư mục hiện tại.
Kiểm tra image đã tạo:
sudo docker images
Bạn sẽ thấy một output tương tự:
Output REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 8 seconds ago 73MB node 10-alpine f09e7c96b6de 3 weeks ago 70.7MB
Bây giờ, bạn có thể tạo một container từ image này bằng lệnh docker run. Chúng ta sẽ sử dụng ba tham số với lệnh này:
-
-p: Tham số này công bố cổng của container và ánh xạ nó đến một cổng trên host. Chúng ta sẽ sử dụng cổng 80 trên host, tuy nhiên bạn có thể thay đổi nếu có tiến trình khác đang sử dụng cổng đó. Để biết thêm chi tiết về cơ chế ánh xạ cổng, hãy tham khảo tài liệu Docker về port binding.
-
-d: Tham số này chạy container ở chế độ nền.
-
–name: Tham số này cho phép bạn đặt tên dễ nhớ cho container.
Kiểm tra container đang chạy:
sudo docker ps
Output sẽ hiển thị thông tin container đang chạy, ví dụ:
Output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "node app.js" 8 seconds ago Up 7 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo
Mở trình duyệt và truy cập:
http://your_server_ip
Bạn sẽ thấy lại trang chủ của ứng dụng.
Bước 4 – Sử dụng Repository để làm việc với Image
Bằng cách đẩy image ứng dụng lên một registry như Docker Hub (ở đây, bạn có thể thay thành DataOnline Hub nếu cần), bạn có thể dễ dàng sử dụng lại image này khi cần mở rộng container.
Đăng nhập vào Docker Hub:
sudo docker login -u your_dockerhub_username
Khi được nhắc, hãy nhập mật khẩu tài khoản Docker Hub của bạn. Việc đăng nhập theo cách này sẽ tạo tệp ~/.docker/config.json
trong thư mục home của người dùng chứa thông tin xác thực của Docker Hub.
Bây giờ, bạn có thể đẩy image ứng dụng lên Docker Hub bằng cách sử dụng nhãn mà bạn đã tạo trước đó, ví dụ: your_dockerhub_username/nodejs-image-demo
.
sudo docker push your_dockerhub_username/nodejs-image-demo
Kiểm tra thử tính năng của Registry:
Dừng container đang chạy:
sudo docker ps
Bạn sẽ nhận được output sau:
Output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e50ad27074a7 your_dockerhub_username/nodejs-image-demo "node app.js" 3 minutes ago Up 3 minutes 0.0.0.0:80->8080/tcp nodejs-image-demo
Sử dụng CONTAINER ID
hiển thị ở trên, hãy dừng container ứng dụng đang chạy bằng lệnh sau (đảm bảo thay thế ID được đánh dấu bằng ID container của bạn):
sudo docker stop e50ad27074a7
Sau đó, liệt kê tất cả các image của bạn với flag -a
:
docker images -a
Bạn sẽ nhận được output sau với tên image của bạn, your_dockerhub_username/nodejs-image-dem
o, cùng với image của node và các image khác từ quá trình build:
Output REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 7 minutes ago 73MB <none> <none> 2e3267d9ac02 4 minutes ago 72.9MB <none> <none> 8352b41730b9 4 minutes ago 73MB <none> <none> 5d58b92823cb 4 minutes ago 73MB <none> <none> 3f1e35d7062a 4 minutes ago 73MB <none> <none> 02176311e4d0 4 minutes ago 73MB <none> <none> 8e84b33edcda 4 minutes ago 70.7MB <none> <none> 6a5ed70f86f2 4 minutes ago 70.7MB <none> <none> 776b2637d3c1 4 minutes ago 70.7MB node 10-alpine f09e7c96b6de 3 weeks ago 70.7MB
Để xóa container đã dừng và tất cả các image, bao gồm cả các image không được sử dụng hoặc bị treo, hãy chạy lệnh sau:
docker system prune -a
Khi được nhắc, nhập y
để xác nhận rằng bạn muốn xóa container đã dừng và các image. Lưu ý rằng lệnh này cũng sẽ xóa cache build của bạn.
Bây giờ, bạn đã xóa cả container đang chạy image ứng dụng và chính image đó.
Với việc xóa hết tất cả các image và container của bạn, bây giờ bạn có thể tải image ứng dụng từ Docker Hub:
docker pull your_dockerhub_username/nodejs-image-demo
Sau đó, liệt kê lại các image của bạn:
docker images
Output sẽ hiển thị image ứng dụng của bạn.
Output REPOSITORY TAG IMAGE ID CREATED SIZE your_dockerhub_username/nodejs-image-demo latest 1c723fb2ef12 11 minutes ago 73MB
Bạn có thể tái tạo container của mình bằng lệnh đã được sử dụng ở Bước 3.
docker run --name nodejs-image-demo -p 80:8080 -d your_dockerhub_username/nodejs-image-demo
Truy cập lại ứng dụng: Mở trình duyệt và truy cập:
docker ps
Output CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f6bc2f50dff6 your_dockerhub_username/nodejs-image-demo "node app.js" 4 seconds ago Up 3 seconds 0.0.0.0:80->8080/tcp nodejs-image-demo
Kết luận
Trong hướng dẫn này, bạn đã xây dựng một ứng dụng web tĩnh bằng Express và Bootstrap, đồng thời tạo ra một Docker image cho ứng dụng đó. Bạn cũng đã sử dụng image này để khởi tạo một container và đẩy image lên Docker Hub để chia sẻ và triển khai dễ dàng.
Khi xây dựng ứng dụng Node.js với Docker, một VPS chất lượng là yếu tố then chốt. Mua VPS từ DataOnline đảm bảo hiệu suất cao, dễ dàng quản lý container. Xem ngay danh mục mua VPS để sở hữu giải pháp lưu trữ tối ưu cho dự án của bạn!
Tiếp theo, bạn đã xóa container và image cũ, rồi tái tạo chúng từ repository trên Docker Hub.
Các bước chính bao gồm:
-
Bước 1: Cài đặt các gói phụ thuộc của ứng dụng.
-
Bước 2: Tạo các tệp ứng dụng (
app.js
,index.html
,sharks.html
, CSS tùy chỉnh). -
Bước 3: Viết Dockerfile để đóng gói ứng dụng vào container.
-
Bước 4: Đẩy image lên Docker Hub và tái tạo container từ image đó.
Chúc bạn thành công với ứng dụng Node.js và Docker!