Docker & Dockerization trong kiến trúc Microservices
Làm thế nào để tự động scale các service khi workload của service đó tăng và giảm số lượng instance khi workload giảm?
Giới thiệu
Trong kiến trúc microservices, mỗi service được thiết kế để đảm nhận một chức năng cụ thể. Khi hệ thống gặp tải đột biến, service đó có thể mở rộng bằng cách nhân bản thành nhiều instance để đáp ứng nhu cầu xử lý; ngược lại, khi tải giảm, số instance cũng được thu hẹp nhằm tối ưu chi phí. Để làm được điều này, mỗi instance cần tuân thủ nguyên tắc stateless, tức không lưu trữ trạng thái bên trong, giúp việc nhân bản hay loại bỏ instance không ảnh hưởng đến các request đang được xử lý.
Đáp ứng các yêu cầu đó, việc đóng gói service thành image là điều tất yếu. Image đóng vai trò như bản định nghĩa các bước cần thiết để triển khai một service thành một instance có thể chạy độc lập. Công cụ phổ biến nhất để thực hiện việc này là Docker, và quá trình đóng gói dịch vụ thành image được gọi là dockerization.
Docker là gì?
Docker là một nền tảng mã nguồn mở, cho phép phát triển, phân phối và chạy các ứng dụng. Docker cho phép bạn tách biệt ứng dụng của bạn và hệ thống cơ sở hạ tầng (infrastructure), và bạn chỉ cần quan tâm tới việc phát triển logic business của ứng dụng.
Docker cho phép đóng gói ứng dụng của bạn và chạy trong một môi trường biệt lập mà được gọi là các container. Mỗi container có cơ chế cô lập và bảo mật (isolation and security) cho phép bạn chạy đồng thời nhiều container trên cùng một host.
Container có kích thước nhẹ và chứa đầy đủ mọi thành phần cần thiết để ứng dụng hoạt động, nên không phụ thuộc vào những gì được cài đặt sẵn trên host. Bạn cũng có thể dễ dàng chia sẻ container trong quá trình làm việc, và chắc chắn rằng mọi người đều nhận được cùng một container, chạy theo cách hoàn toàn giống nhau.
Cách thành phần chính của Docker
Docker sử dụng kiến trúc client-server. Docker client gửi Docker API request tới cho Docker deamon. Docker deamon lắng nghe các request từ Docker client, quản lý các images, run các container và pull images từ Docker Registry.
Docker registries
Là nơi chứa các docker images, Docker Hub là registry public cho phép mọi người chia sẻ các docker images.
Bạn có thể cấu hình sử dụng Docker Hub hoặc tự setup 1 private hub. Khi bạn chạy `docker pull``docker sẽ pull image từ docker hub. Ngược lại khi bạn chạy ``docker push` Docker sẽ đẩy image của bạn lên docker hub
Images
Image là một mẫu (template) chỉ đọc, chứa hướ
ng dẫn để tạo ra một Docker container. Thông thường, một image có thể dựa trên một image khác và bổ sung thêm một số tùy chỉnh. Ví dụ: bạn có thể tạo một image dựa trên ubuntu
, sau đó cài đặt Apache, ứng dụng của bạn và các cấu hình cần thiết để ứng dụng chạy được.
Containers
Container là một instance chạy được của image. Bạn có thể tạo, khởi động, dừng, di chuyển hoặc xóa container bằng Docker CLI hoặc API. Container có thể kết nối với một hay nhiều mạng, gắn thêm storage, hoặc tạo thành image mới dựa trên trạng thái hiện tại của nó.
Tìm hiểu thêm về Docker tại: https://docs.docker.com/get-started/docker-overview/
Dockerization trong microservices
Sau khi đã tìm hiểu về Docker, chúng ta sẽ tiếp tục với việc làm thế nào để đóng gói các service.
Tạo Dockerfile
Dockerfile là một script file chưa các lệnh để build Docker image. Các lệnh đó bao gồm: base images, application source code, các thư viện liên quan, và các cấu hình cần thiết để chạy.
# Sử dụng base image chính thức, nhẹ
FROM python:3.10-slim
# Đặt thư mục làm việc trong container
WORKDIR /app
# Copy file requirements trước để tận dụng cache layer
COPY requirements.txt .
# Cài đặt dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy toàn bộ source code vào container
COPY . .
# Mở cổng ứng dụng
EXPOSE 8000
# Lệnh chạy service
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Đây là một Dockerfile đơn giản cho service FastAPI. File này sử dụng Python base image, cài đặt dependencies từ requirements.txt
, copy source code vào container, mở cổng 8000 và chạy ứng dụng bằng Uvicorn.
Sau khi có docker file trên, chúng ta có thể build và chạy bằng lệnh sau:
# Build image (tên my-service, tag latest)
docker build -t my-service:latest .
# Run container
docker run -d -p 8000:8000 my-service:latest
Chạy nhiều service cùng lúc bằng docker-compose
Trong một project microservices thường có nhiều service, và mỗi service đều cần một Dockerfile riêng. Nếu phải chạy thủ công từng service bằng lệnh docker run
thì sẽ rất bất tiện. Thay vào đó, chúng ta có thể dùng Docker Compose để gom các service lại và khởi chạy tất cả chỉ với một lệnh duy nhất.
Ví dụ chúng ta có một project với structure như sau:
microservices-project/
│
├── service-auth/
│ ├── Dockerfile
│ ├── requirements.txt
│ └── main.py
│
├── service-orders/
│ ├── Dockerfile
│ ├── package.json
│ └── app.js
│
├── service-db/
│ └── init.sql
│
└── docker-compose.yml
Khi đó chúng ta có thể tạo một file docker-compose.yml như sau để chạy các service một lần bằng lệnh `docker-compose up --build` . Khí đó tất cả các service: Auth, order, db sẽ được start cùng lúc.
version: '3.9'
services:
auth-service:
build: ./service-auth
container_name: auth-service
ports:
- "8000:8000"
depends_on:
- db
orders-service:
build: ./service-orders
container_name: orders-service
ports:
- "8001:8001"
depends_on:
- db
db:
image: postgres:14
container_name: db
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: microservices_db
ports:
- "5432:5432"
volumes:
- db_data:/var/lib/postgresql/data
- ./service-db/init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
db_data:
Kết luận
Lợi ích của Dockerization trong microservices
Giả sử bạn tham gia nhiều project khác nhau, trong đó mỗi project lại yêu cầu một phiên bản MySQL riêng. Khi đó, việc tách biệt môi trường bằng container riêng cho từng project sẽ giúp quá trình phát triển trở nên dễ dàng và linh hoạt hơn.
Ngày nay, với sự phổ biến của điện toán đám mây, các service có thể được triển khai nhanh chóng trên những nền tảng như AWS hay GCP. Việc đóng gói service bằng Docker mang lại cách tiếp cận mạnh mẽ và hiệu quả để triển khai trên các nền tảng này một cách nhất quán và nhanh chóng.
Hạn chế
Việc quản lý sẽ trở nên phức tạp khi số lượng container lớn, dễ tiêu tốn tài nguyên và gây overhead nếu không tối ưu. Bên cạnh đó, container chia sẻ kernel với host nên có rủi ro bảo mật, việc lưu trữ dữ liệu lâu dài cũng phức tạp hơn do container mặc định là stateless. Khi muốn lưu trữ thông tin gì đó, chúng ta phải lưu vào database. Cuối cùng, để vận hành hiệu quả, đội ngũ phát triển cần nắm vững Dockerfile, networking và các công cụ orchestration như Kubernetes.
Đọc thêm
https://docs.docker.com/get-started/get-docker/
https://aws.amazon.com/free/containers
Cảm ơn bạn đã đọc bài viết này, nếu bạn có bất kỳ câu hỏi nào, đừng ngần ngại để lại comment ở bên dưới. Và đừng quên subscribe để luôn nhận được các bài viết mới nhất