Dockerfile là gì? Hướng dẫn tạo Docker Image đầu tiên

 

Dockerfile là gì? Hướng dẫn tạo Docker Image đầu tiên

Ở bài trước, chúng ta đã tìm hiểu Docker là gì, sự khác nhau giữa Docker Image và Docker Container, cũng như cách chạy một container Nginx đơn giản.

Trong bài này, chúng ta sẽ đi thêm một bước: tự đóng gói ứng dụng của mình thành Docker Image bằng Dockerfile.

Nếu bạn đang phát triển ứng dụng bằng Node.js, NestJS, Angular hoặc các công nghệ web khác thì Dockerfile là kiến thức rất quan trọng.




1. Dockerfile là gì?

Dockerfile là một file văn bản chứa các câu lệnh hướng dẫn Docker cách tạo ra một Docker Image.

Có thể hiểu đơn giản theo quy trình:

Source Code
    ↓
Dockerfile
    ↓
docker build
    ↓
Docker Image
    ↓
docker run
    ↓
Docker Container

Ví dụ chúng ta có một ứng dụng Node.js.

Thay vì phải cài Node.js, npm và các thư viện thủ công trên server, chúng ta có thể mô tả toàn bộ môi trường đó trong Dockerfile.

Docker sẽ dựa vào Dockerfile để tạo ra một Image hoàn chỉnh.

Hiểu đơn giản: Dockerfile giống như một “công thức”, còn Docker Image là “sản phẩm” được tạo ra từ công thức đó.



2. Cấu trúc Dockerfile cơ bản

Một Dockerfile đơn giản cho ứng dụng Node.js có thể như sau:

FROM node:22

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

Nhìn qua có vẻ hơi nhiều câu lệnh, nhưng ý nghĩa của từng dòng khá đơn giản.


3. FROM – Chọn môi trường chạy

Dòng đầu tiên:

FROM node:22

có nghĩa là chúng ta muốn sử dụng Docker Image có sẵn của Node.js phiên bản 22 làm nền tảng.

Có thể hình dung:

Node.js Image
      ↓
Ứng dụng của chúng ta
      ↓
Docker Image mới

Ngoài Node.js, chúng ta cũng có thể sử dụng các image khác:

FROM nginx

hoặc:

FROM ubuntu:24.04

hoặc:

FROM python:3.12

Tùy vào công nghệ mà ứng dụng đang sử dụng.


4. WORKDIR – Chọn thư mục làm việc

Tiếp theo:

WORKDIR /app

Docker sẽ tạo và sử dụng thư mục:

/app

bên trong container.

Các câu lệnh sau đó sẽ được thực hiện trong thư mục này.

Ví dụ:

/app
 ├── package.json
 ├── node_modules
 ├── src
 └── main.js

5. COPY – Copy source code vào Image

Dòng:

COPY package*.json ./

sẽ copy:

package.json
package-lock.json

từ máy của chúng ta vào thư mục /app trong Docker Image.

Sau đó:

COPY . .

sẽ copy toàn bộ source code vào container.

Có thể hiểu như sau:

Máy Developer

project/
 ├── src/
 ├── package.json
 └── Dockerfile

        ↓ COPY

Docker Image

/app/
 ├── src/
 ├── package.json
 └── ...

6. RUN – Chạy lệnh khi build Image

Dòng:

RUN npm install

yêu cầu Docker chạy:

npm install

trong quá trình build image.

Docker sẽ cài đặt các dependency được khai báo trong package.json.

Ví dụ:

express
nestjs
rxjs
typeorm

sẽ được cài vào:

/app/node_modules

Một điều quan trọng cần nhớ:

RUN

được thực hiện khi build Docker Image.

Trong khi:

CMD

được thực hiện khi Container bắt đầu chạy.


7. EXPOSE – Khai báo port của ứng dụng

Nếu ứng dụng Node.js chạy ở port:

3000

chúng ta có thể khai báo:

EXPOSE 3000

Điều này giúp mô tả rằng ứng dụng bên trong container sử dụng port 3000.

Tuy nhiên, để truy cập ứng dụng từ máy bên ngoài, chúng ta vẫn cần map port khi chạy container.

Ví dụ:

docker run -p 3000:3000 my-app

Có nghĩa là:

Máy tính           Container

Port 3000   →      Port 3000

8. CMD – Lệnh chạy ứng dụng

Dòng cuối cùng:

CMD ["npm", "start"]

là lệnh sẽ chạy khi container được khởi động.

Nó tương đương với:

npm start

Nếu đang sử dụng NestJS production, chúng ta có thể dùng:

CMD ["node", "dist/main.js"]

9. Build Docker Image

Sau khi tạo Dockerfile, cấu trúc project có thể như sau:

my-app/
 ├── src/
 ├── package.json
 ├── package-lock.json
 └── Dockerfile

Mở Terminal tại thư mục project và chạy:

docker build -t my-app .

Trong đó:

docker build

yêu cầu Docker tạo một Image.

-t my-app

đặt tên image là:

my-app

Dấu:

.

có nghĩa là sử dụng thư mục hiện tại làm build context.

Sau khi build thành công, kiểm tra:

docker images

Bạn có thể thấy:

REPOSITORY    TAG       IMAGE ID
my-app        latest    xxxxxxxxx

10. Chạy Container từ Image vừa tạo

Sau khi có Image:

my-app

chúng ta có thể chạy:

docker run -d -p 3000:3000 --name my-app-container my-app

Giải thích:

-d

Chạy container ở background.

-p 3000:3000

Map port máy tính vào port container.

--name my-app-container

Đặt tên container.

my-app

Là image được sử dụng.

Sau đó kiểm tra:

docker ps

Nếu container xuất hiện trong danh sách thì ứng dụng đã chạy thành công.

[Hình minh họa: Docker Image my-app → docker run → Container → localhost:3000]


11. .dockerignore là gì?

Khi sử dụng Dockerfile, chúng ta thường nên tạo thêm file:

.dockerignore

File này hoạt động khá giống .gitignore.

Ví dụ:

node_modules
.git
.env
dist
README.md

Docker sẽ không copy những file hoặc thư mục này vào build context.

Điều này giúp:

  • Docker Image nhỏ hơn.

  • Build nhanh hơn.

  • Tránh copy file không cần thiết.

  • Hạn chế đưa thông tin nhạy cảm vào Image.

Ví dụ bạn không nên copy .env chứa password hoặc API key vào Docker Image.


12. Một Dockerfile tốt hơn cho Node.js

Thay vì:

RUN npm install

trong môi trường CI/CD hoặc production, chúng ta thường dùng:

RUN npm ci

Dockerfile có thể viết như sau:

FROM node:22

WORKDIR /app

COPY package*.json ./

RUN npm ci

COPY . .

EXPOSE 3000

CMD ["npm", "start"]

npm ci sử dụng chính xác dependency trong package-lock.json, giúp môi trường build ổn định hơn.


13. Dockerfile trong dự án thực tế

Ví dụ chúng ta có hệ thống:

Angular
   ↓
NestJS
   ↓
MySQL
   ↓
Redis

Chúng ta có thể tạo Docker Image riêng cho từng ứng dụng.

Ví dụ:

Angular
   ↓
frontend-image

NestJS
   ↓
backend-image

Sau đó chạy:

frontend-container
backend-container
mysql-container
redis-container

Các container này có thể được kết nối với nhau bằng Docker Network.

Khi số lượng container tăng lên, việc chạy từng container bằng docker run sẽ khá bất tiện.

Đây chính là lúc chúng ta sử dụng:

Docker Compose

để quản lý toàn bộ hệ thống chỉ bằng một file cấu hình.


14. Các lệnh cần nhớ

Build Image:

docker build -t my-app .

Xem danh sách Image:

docker images

Chạy Container:

docker run -d -p 3000:3000 my-app

Xem Container đang chạy:

docker ps

Xem log:

docker logs <container-name>

Theo dõi log realtime:

docker logs -f <container-name>

Dừng Container:

docker stop <container-name>

Xóa Container:

docker rm <container-name>

Xóa Image:

docker rmi <image-name>

15. Tổng kết

Trong bài này, chúng ta đã đi từ source code đến một container hoàn chỉnh:

Source Code
     ↓
Dockerfile
     ↓
docker build
     ↓
Docker Image
     ↓
docker run
     ↓
Docker Container
     ↓
Application Running

Nếu mới học Docker, bạn chỉ cần nhớ ba bước quan trọng:

1. Viết Dockerfile

Mô tả môi trường và cách chạy ứng dụng.

2. Build Image

docker build

3. Chạy Container

docker run

Sau khi hiểu được Dockerfile, Docker Image và Container, bước tiếp theo rất phù hợp là tìm hiểu Docker Compose.

Docker Compose sẽ giúp chúng ta chạy nhiều service như Angular, NestJS, MySQL và Redis cùng lúc chỉ bằng một câu lệnh.

Đăng nhận xét

Mới hơn Cũ hơn