Bài 39.5: Bài tập thực hành containerize ứng dụng

Bài 39.5: Bài tập thực hành containerize ứng dụng
Chào mừng bạn đến với bài thực hành cực kỳ quan trọng trong chuỗi bài của chúng ta! Sau khi đã nắm vững các khái niệm về lập trình web front-end và thậm chí chạm đến các công nghệ hiện đại như React hay NextJs, việc đưa ứng dụng của bạn từ môi trường phát triển lên môi trường thực tế là một bước đi không thể thiếu. Và container hóa chính là công cụ mạnh mẽ nhất hiện nay để làm điều đó một cách hiệu quả, đáng tin cậy.
Hôm nay, chúng ta sẽ không chỉ học lý thuyết mà sẽ "xắn tay áo" để tự tay đóng gói một ứng dụng web đơn giản vào container sử dụng Docker - nền tảng container phổ biến nhất thế giới.
Tại sao cần Containerize ứng dụng web?
Trước khi bắt đầu, hãy điểm qua nhanh lý do tại sao việc này lại quan trọng đến vậy:
- Tính nhất quán: "Nó chạy tốt trên máy tôi!" - Câu nói kinh điển này sẽ đi vào dĩ vãng. Container đóng gói ứng dụng cùng với tất cả môi trường phụ thuộc (thư viện, cấu hình, runtime) vào một đơn vị duy nhất. Điều này đảm bảo ứng dụng chạy giống hệt nhau trên mọi môi trường có Docker, từ máy local của bạn đến server sản phẩm.
- Tính cô lập: Mỗi container hoạt động độc lập, giống như một máy ảo mini nhưng nhẹ hơn rất nhiều. Nếu một container gặp sự cố, nó không ảnh hưởng đến các container khác đang chạy trên cùng một server.
- Tính di động: Container rất dễ dàng di chuyển giữa các môi trường khác nhau (cloud, on-premise, local machine) mà không cần cấu hình lại phức tạp.
- Khả năng mở rộng: Với container, việc nhân bản ứng dụng để xử lý lượng truy cập lớn trở nên cực kỳ đơn giản và nhanh chóng.
Docker cung cấp cho chúng ta "con thuyền" để chở ứng dụng và môi trường của nó đi khắp nơi.
Các khái niệm cốt lõi của Docker cần biết cho bài này
- Dockerfile: Là một file văn bản chứa các chỉ thị (instructions) để xây dựng một Docker Image. Nó giống như công thức nấu ăn.
- Docker Image: Là một bản mẫu chỉ đọc (read-only template) dùng để tạo ra các Container. Nó chứa code ứng dụng, thư viện, công cụ, runtime, và tất cả những gì cần thiết để ứng dụng chạy. Giống như gói nguyên liệu và hướng dẫn đã chuẩn bị sẵn.
- Docker Container: Là một phiên bản chạy được (runtime instance) của một Docker Image. Bạn có thể tạo, bắt đầu, dừng, di chuyển, hoặc xóa một container. Giống như món ăn đã được nấu xong và sẵn sàng để dùng.
Bài Tập Thực Hành: Containerize một ứng dụng Node.js đơn giản
Chúng ta sẽ containerize một ứng dụng Node.js rất đơn giản: một server HTTP phục vụ một file HTML tĩnh.
Bước 1: Tạo ứng dụng Node.js đơn giản
Tạo một thư mục mới cho dự án của bạn, ví dụ my-web-app-docker
.
Trong thư mục này, tạo hai file: index.html
và app.js
.
File: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chào mừng đến với Container!</title>
</head>
<body>
<h1>Xin chào từ ứng dụng Web trong Docker Container!</h1>
<p>Đây là một ví dụ đơn giản về cách đóng gói ứng dụng web.</p>
</body>
</html>
Đây là một file HTML cơ bản, nội dung sẽ được hiển thị khi truy cập vào server của chúng ta.
File: app.js
const http = require('http');
const fs = require('fs');
const path = require('path');
const port = 3000; // Port mà ứng dụng sẽ lắng nghe bên trong container
const server = http.createServer((req, res) => {
// Chỉ phục vụ file index.html cho mọi request
const filePath = path.join(__dirname, 'index.html');
fs.readFile(filePath, (err, data) => {
if (err) {
res.writeHead(500, {'Content-Type': 'text/plain'});
res.end('Internal Server Error');
return;
}
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data);
});
});
server.listen(port, () => {
console.log(`Ứng dụng đang lắng nghe trên port ${port}`);
console.log('Containerize thành công!');
});
Đây là một server HTTP rất cơ bản sử dụng module http
và fs
có sẵn trong Node.js.
- Nó đọc file
index.html
. - Nó thiết lập server lắng nghe trên port 3000 bên trong container.
- Khi có request đến, nó trả về nội dung của file
index.html
.
Bước 2: Tạo Dockerfile
Trong cùng thư mục my-web-app-docker
, tạo một file mới không có phần mở rộng, đặt tên là Dockerfile
.
File: Dockerfile
# Bước 1: Chỉ định image cơ sở
# Chúng ta sẽ sử dụng một image Node.js chính thức.
# Sử dụng phiên bản cụ thể (ví dụ: 18-alpine) thay vì 'latest' là một best practice
# để đảm bảo tính nhất quán. 'alpine' là một phiên bản rất nhẹ.
FROM node:18-alpine
# Bước 2: Thiết lập thư mục làm việc bên trong container
# Tất cả các lệnh tiếp theo (COPY, CMD) sẽ chạy trong thư mục này
WORKDIR /app
# Bước 3: Sao chép các file ứng dụng vào thư mục làm việc
# Dấu . đầu tiên là nguồn (thư mục hiện tại trên máy host, nơi có Dockerfile)
# Dấu . thứ hai là đích (thư mục làm việc bên trong container, tức là /app)
COPY . .
# Bước 4: Mở port mà ứng dụng bên trong container đang lắng nghe
# Điều này chỉ mang tính tài liệu hóa và cho phép Docker biết port nào cần public.
# Việc ánh xạ port từ host sang container sẽ thực hiện khi chạy container.
EXPOSE 3000
# Bước 5: Lệnh mặc định để chạy ứng dụng khi container khởi động
# CMD chỉ định lệnh sẽ được thực thi khi container bắt đầu.
# Đây là entrypoint của ứng dụng của bạn trong container.
CMD ["node", "app.js"]
Hãy giải thích chi tiết các dòng trong Dockerfile
:
FROM node:18-alpine
: Chỉ thị này chọn một Docker Image làm nền tảng cho image của chúng ta. Chúng ta chọn image Node.js phiên bản 18 dựa trên Alpine Linux (một bản phân phối Linux rất nhẹ). Điều này cung cấp môi trường Node.js sẵn sàng để chạyapp.js
.WORKDIR /app
: Chỉ thị này thiết lập/app
làm thư mục làm việc mặc định cho các chỉ thị tiếp theo. Thay vì phải gõ/app/...
mỗi lần, chúng ta chỉ cần dùng tên file hoặc thư mục tương đối.COPY . .
: Chỉ thị này sao chép tất cả file và thư mục từ thư mục hiện tại trên máy tính của bạn (nơi chứaDockerfile
) vào thư mục làm việc (/app
) bên trong image.EXPOSE 3000
: Chỉ thị này thông báo rằng container sẽ lắng nghe trên port 3000 tại runtime. Điều này giúp những người khác hoặc các công cụ orchestration biết container này sử dụng port nào. Lưu ý: Chỉ thị này không thực sự xuất bản port ra bên ngoài host; việc đó được làm khi chạy container.CMD ["node", "app.js"]
: Chỉ thị này thiết lập lệnh mặc định sẽ chạy khi một container được tạo từ image này và khởi động. Lệnh này bắt đầu server Node.js của chúng ta.
Bước 3: Xây dựng (Build) Docker Image
Mở terminal hoặc command prompt, điều hướng đến thư mục my-web-app-docker
chứa Dockerfile
và các file ứng dụng.
Chạy lệnh sau:
docker build -t my-simple-web-app:latest .
docker build
: Lệnh để xây dựng một Docker Image.-t my-simple-web-app:latest
: Gắn tag (tên và phiên bản) cho image.my-simple-web-app
là tên image,:latest
là tag phiên bản. Bạn có thể dùng tag khác như:1.0
. Việc đặt tên giúp bạn dễ dàng quản lý các image của mình..
: Dấu chấm ở cuối chỉ định context của quá trình build. Nó nói với Docker rằng hãy tìmDockerfile
và các file cần thiết trong thư mục hiện tại. Docker sẽ gửi toàn bộ context này (các file trong thư mục) đến Docker Daemon để xử lý.
Quan sát output của lệnh docker build
. Bạn sẽ thấy Docker thực hiện từng bước theo các chỉ thị trong Dockerfile
. Mỗi chỉ thị thành công sẽ tạo ra một layer trung gian và hiển thị ID của layer đó.
Sau khi build xong, bạn có thể kiểm tra danh sách các image trên máy mình bằng lệnh:
docker images
Bạn sẽ thấy image my-simple-web-app
với tag latest
(hoặc tag bạn đã đặt).
Bước 4: Chạy (Run) Docker Container
Bây giờ chúng ta đã có image, sẵn sàng để tạo và chạy một container từ image đó.
Chạy lệnh sau:
docker run -p 8080:3000 my-simple-web-app:latest
docker run
: Lệnh để tạo và chạy một container mới từ một image.-p 8080:3000
: Đây là phần quan trọng nhất để truy cập ứng dụng từ bên ngoài container. Nó ánh xạ port 8080 trên máy tính host (máy bạn) tới port 3000 bên trong container (port mà ứng dụng Node.js của chúng ta đang lắng nghe). Bạn có thể chọn port bất kỳ trên máy host, miễn là nó chưa bị sử dụng.my-simple-web-app:latest
: Tên và tag của image mà chúng ta muốn chạy.
Khi bạn chạy lệnh này, Docker sẽ tìm image my-simple-web-app:latest
trên máy bạn (nếu chưa có sẽ tải về), tạo một container từ image đó, và thực thi lệnh CMD ["node", "app.js"]
.
Bạn sẽ thấy output Ứng dụng đang lắng nghe trên port 3000
và Containerize thành công!
xuất hiện trong terminal. Điều này có nghĩa là ứng dụng Node.js đang chạy bên trong container.
Bước 5: Truy cập ứng dụng
Mở trình duyệt web của bạn và truy cập địa chỉ:
http://localhost:8080
Bạn sẽ thấy nội dung của file index.html
được hiển thị! Bạn đang truy cập ứng dụng web đang chạy bên trong một Docker Container thông qua port 8080 trên máy host của bạn, được ánh xạ tới port 3000 bên trong container.
Để dừng container, quay lại terminal và nhấn Ctrl + C
.
Nếu bạn muốn chạy container ở chế độ ngắt kết nối (detached mode) để terminal không bị khóa, bạn có thể thêm flag -d
:
docker run -d -p 8080:3000 my-simple-web-app:latest
Lệnh này sẽ chạy container ở chế độ nền và trả về ID của container. Để dừng container chạy ở chế độ detached, bạn cần tìm ID của nó (docker ps
) rồi dùng lệnh docker stop <container_id>
.
Tóm tắt quy trình
- Chuẩn bị ứng dụng (file code).
- Viết
Dockerfile
để mô tả cách xây dựng image. docker build
để tạo image từDockerfile
.docker run
để tạo và chạy container từ image, ánh xạ port.- Truy cập ứng dụng qua port đã ánh xạ trên máy host.
Comments