Bạn có biết mì tôm là gì không?
Nếu bạn không biết mì tôm là gì, mình bán tín bán nghi rằng mục đích đi làm hay tìm hiểu về công nghệ trong DevOps của bạn đi làm là gì? Món ăn nhanh của thời sinh viên, của dân IT thâu đêm, hay bữa sáng âm ấm là những từ để nói về mì tôm.
Bạn đã từng ăn mì tôm chưa?
Nếu bạn đã ăn, chúng ta hãy cùng nhau ăn bát mì tôm có Docker qua bài viết này. Tại sao lại là mì tôm có Docker? Bạn thắc mắc, tôi trả lời, chúng ta sẽ cùng nhau tìm hiểu Docker theo cách mà chúng ta ăn mì tôm.
Để tránh có những hiểu lầm trong nội dung của bài viết, mình có 2 quy ước về Docker và Mì tôm như sau:
- Trong bài viết sẽ nhắc tới Docker đại diện công nghệ containerization, do từ khóa Docker là phổ biến hơn.
- Trong bài viết này cũng có đề cập tới mì tôm, được sử dụng quen miệng hơn mì ăn liền, nhưng từ khóa mì tôm được nhắc tới sẽ bao hàm nghĩa rộng hơn, gồm các loại mì, phở, bún ăn liền cho đa dạng.
No Docker = Không mì tôm
Thời kì mà Docker chưa xuất hiện, cũng như mì tôm chưa xuất hiện trên thế giới. Bạn đói, bạn cần ăn, bạn hết tiền, chỉ còn đồ ăn trong tủ , bạn đi nấu ăn, và công sức, thời gian bạn tiêu tốn là bao nhiêu. Tương tự, trước khi có Docker, thời kỳ mà triển khai, vận hành ứng dụng là những thao tác thủ công, truy cập tới máy chủ, cập nhật source code, build source code, triển khai source code. Nhắc tới thôi là đã có thể hình dung kha khá các tác vụ, đắn đo kỹ lưỡng trong từng câu lệnh và duy trì tập trung trong thời gian dài của công việc.
Dù cho mì tôm và Docker không ra đời cùng thời, cũng không nguyện ra đi cùng năm, nhưng những gì mà chúng mang lại là sự tiện lợi, hiệu quả, tiết kiệm thời gian và dễ dùng. Kể về tiện ích mà mì tôm mang lại hẳn là dư thừa, nên chúng ta sẽ đi tới ngay tới phần thông tin của Docker. Docker mang tới làn gió mới cho việc triển khai và vận hành, Docker mở bát, kéo theo hàng loạt các cải tiến, kỹ thuật, phương pháp cho tới ngày nay. Bạn sẽ nghe về DevOps, DevSecOps, GitOps, AIOps, MLOps,… ít nhiều chúng đều có liên quan hoặc có sử dụng Docker, hoặc dạng tiến hóa của Docker là Container Orchestration. Dưới đây là một tài lợi ích chính mà Docker đem lại:
- Tính đóng gói - Packaging: khả năng đóng gói all-in-one, đẩy đủ môi trường và thành phần cho ứng dụng hoạt động.
- Tính di động - Portability: ứng dụng trong Docker có thể triển khai trên nhiều môi trường khác nhau: máy vật lý, máy ảo, dịch vụ cloud,…
- Sự cô lập - Isolation: mô hình cô lập tài nguyên, đảm bảo an toàn và tránh xung đột tài nguyên khi chạy nhiều ứng dụng trên cùng một máy chủ.
- Hiệu suất tài nguyên - Efficiency: khi so sánh với các máy ảo truyền thống, Docker sử dụng tài nguyên hiệu quả hơn vì nó tận dụng sử dụng chung kernel với máy chủ mà vẫn đảm bảo tính cô lập.
Docker Hub = Siêu thị mì tôm
Bạn thấy đấy, có biết bao loại mì tôm khác nhau, từ nguyên liệu tới hương vị khác nhau, chúng cũng được bày bán ở đa dạng các loại hình: siêu thị, siêu thị mini, cửa hàng tạp hóa, cửa hàng tiện lợi. Với Docker, bạn cũng có một cái “chợ”, thực ra là rất nhiều cái “chợ” khác nhau có cung cấp “món” Docker Image. Chắc chắn là bạn mất tiền khi mua mì gói, nhưng với Docker bạn lựa chọn, sử dụng thoải mái trừ trường hợp Docker Image đó là enterprise. Cùng nhau điểm qua một vài “chợ” Docker với đầy úp các “mì Docker”.
Public Docker Registry
Là những kho lưu trữ Docker Image cho cộng đồng, mọi người có thể tải xuống bất cứ đâu, bất kỳ lúc nào. Khi vô đây xem, bạn cần lưu ý về một số thông tin của nó độ uy tín (trusted content), kiến trúc hỗ trợ(supported architect), dung lượng lưu trữ (size), an toàn thông tin (security),… Danh sách một vài Registry phổ biến:
- Docker Hub
- RedHat Quay
- GCR (lưu ý, hiện tại GCR đã ngừng cập nhật và sử dụng)
- …
Private Docker Registry
Là những kho lưu trữ do cá nhân, tổ chức, doanh nghiệp đăng ký dịch vụ có sẵn hoặc tự cài đặt. Docker Registry sẽ cần đăng nhập và phân quyền để sử dụng, thường chỉ dùng nội bộ cho tổ chức, doanh nghiệp sở hữu nó. Sẽ có hình thức trả phí (pricing) hoặc tự cài đặt và vận hành(self-hosted) cho các Registry này:
- Docker Hub - Pricing
- JFrog Artifactory - Pricing
- JFrog Container Registry - Pricing or Self-Hosted
- AWS ECR - Pricing
- GCP Artifact Registry - Pricing
- Harbor - Self-Hosted
- Docker Registry - Self-hosted
- …
Source Code = Nguyên liệu
Để cấu tạo lên mì, chúng ta cần có nguyên liệu làm vắt mì. Còn nguyên liệu của ứng dụng, chắc chắn rồi đó là source code.
Dưới đây là hình ảnh về các thành phần chung cho một gói mì.
Có thể nó quá chi tiết và quá nhiều thông tin chúng ta chưa cần biết về thành phần của mì ăn liền. Bởi vì hẳn là rất nhiều người, trong đó có tôi là người sử dụng mì gói, chúng ta không phải là người sáng tạo hay sản xuất các gói mì. Chúng ta chỉ quan tâm đến chức năng, đến hương vị và vệ sinh an toàn của nó.
Tương tự như với ứng dụng, hàng ngày, chúng ta sử dụng kha khá các ứng dụng cho đa dạng các mục đích khác nhau như công việc, giải trí, tin tức,… Liệu chúng ta có cần biết về hết “nguyên liệu” của ứng dụng không. Câu trả lời là ở trong các bạn, còn câu trả lời của mình là không, mình không phải là cái máy tính mà nạp hết cả hàng triệu dòng code như vậy. Nhưng khi mà tham gia vào quá trình sản xuất Docker Image, chúng ta nên nắm được các thông tin cơ bản của source code, chúng sẽ giúp bạn có thể định hình nên Dockerfile. Sẽ có một vài thông tin chúng ta nên nắm bắt:
- Ngôn ngữ hay framework sử dụng trong source code là gì?
- Ví dụ: ngôn ngữ Java, ngôn ngữ Golang, framework Spring Boot, framework ReactJS,…
- Thông tin này để bạn lựa chọn base image hoặc tự tạo riêng base image khả thi cho ứng dụng hoạt động trên nó.
- Trên máy của developer, có sử dụng tools, script gì ngoài source code khi chạy ứng dụng không?
- Nếu thiếu các công cụ hay script này, môi trường sẽ không đồng nhất giữa máy tính local của dev và Docker Container, dẫn tới ứng dụng không hoạt động như kỳ vọng.
- Danh sách về các thư viện, package, module cần tải về mà mã nguồn có phụ thuộc(dependencies)
- Ví dụ: Java sẽ định nghĩa các thư viện trong tệp tin pom.xml, Golang có tệp tin go.mod, framework của JavaScript thì có package.json,…
- Thiếu thư viện, hiển nhiên, khi dòng lệnh yêu cầu thư viện thiếu đó, kết quả hẳn mọi người đều đoán được là lỗi.
- Câu lệnh để khởi động ứng dụng là gì?
- Có mã nguồn, có môi trường mà không biết khởi động ứng dụng như nào thì công sức của các bạn developer như công cốc rồi. Hãy tìm hiểu về câu lệnh để khởi chạy ứng dụng, vì trong Dockerfile, chúng ta không có giao diện đồ họa, không có IDE với button Play.
- Khi hoạt động có tệp tin dữ liệu nào quan trọng, cần lưu trữ dài lâu không?
- Ví dụ: các tệp tin được kết xuất báo cáo hàng ngày và lưu trữ trực tiếp trên môi trường chạy, …
- Đặc tính của Container sẽ không duy trì dữ liệu trên container writable layer, nên nếu có dữ liệu, tệp tin quan trọng phát sinh trong quá trình ứng dụng hoạt động, chúng sẽ cần được mount ra filesystem hoặc ở nơi lưu trữ khác.
- Cổng (port) mà ứng dụng hoạt động cho phép người dùng hay dịch vụ khác truy cập là bao nhiêu?
- Ví dụ: cổng mặc định của NodeJS là 3000, Java Spring Boot là 8080, hoặc một cổng bất kỳ mà cấu hình quy định.
- Ứng dụng trong Docker có sự cô lập về tài nguyên, bao gồm cả network, nên sẽ có cơ chế khai báo riêng để cho phép bên ngoài truy cập vào ứng dụng tại cổng hoạt động.
- Khi khởi động ứng dụng, sẽ có câu lệnh healthcheck không? Nếu có, câu lệnh healthcheck là gì?
- Điều này có thể không được quan tâm trong quá trình phát triển, nhưng khi ứng dụng khởi động, chúng ta cũng nên biết và nên kiểm tra liệu ứng dụng có thực sự hoạt động hay không thông qua câu lệnh healthcheck.
Các nhà máy luôn kỹ càng trong khâu chuẩn bị nguyên liệu, còn chúng ta sử dụng Docker, cần thu thập rõ ràng các thông tin theo source code để tiến hành biến đổi về chất, từ source code cấu thành Docker Image.
Dockerfile = Công thức làm mì gói
Nguyên liệu đã sẵn sàng, ốp công thức là có thành phẩm. Mọi người có mong chờ mình tiết lộ công thức sản xuất mì gói không? Không, mình đâu có biết bí kíp kinh doanh của họ, hoặc nếu có biết mình có thể đã có nhãn hiệu mì riêng mà không ở đây để hoàn thành bài viết này. Nhưng để trả lời câu hỏi, sản xuất hàng nghìn gói mì hàng ngày, làm sao để tiết kiệm và đạt hiệu quả kinh tế cao? Mình tin chắc sẽ có rất rất nhiều người có cùng suy nghĩ với mình, đó là tự động hóa, xây dựng nên một quy trình tự động hóa, dây chuyền sản xuất, máy móc tự động hóa nhiều nhất có thể. Mình xin phép đặt thêm câu hỏi cho bạn, nhưng đầu vào của tự động hóa là gì? Bạn nghĩ đúng rồi đấy, là con người hoặc AI hỗ trợ sẽ thiết lập thứ tự các bước hoạt động trong dây chuyền.
Chúng ta sẽ cần đưa ra danh sách mệnh lệnh cho các công cụ, máy móc theo một logic nhất định để chúng có thể làm việc, lấy đầu vào và xuất ở đầu ra. Trong Docker, chúng ta định nghĩa các mệnh lệnh trong tệp tin Dockerfile và tuân thủ theo quy tắc của Dockerfile. Dưới đây là một ví dụ đơn giản ví von cho Dockerfile đóng gói ứng dụng NodeJS.
# dây chuyền nào được sử dụng
FROM node:14
# đưa nguyên liệu vào dây chuyền
COPY . .
# thêm chất hóa học, phụ gia
RUN npm install
# chạy ứng dụng
CMD ["npm", "start"]
Với project thực sự sẽ có nhiều điều cần làm trong Dockerfile. Bạn có thể tham khảo một số các Dockerfile templates mà cộng đồng cùng nhau xây dựng, trong đó, có những best practice đáng học hỏi, ví dụ như kỹ thuật multi stage, layer caching, clean install, … Hoặc sử dụng các project đi cùng template này như những bài thực hành luyện tập khi bắt đầu tiếp xúc với Docker.
Docker Build = Dây chuyền nhà máy
Sắp có thành quả rồi, sản phẩm ở cuối dây chuyền.
Docker cũng là một công cụ tự động hóa giúp chúng ta đóng gói mã nguồn thành Dockerfile, giống như cách nhà máy sử dụng dây chuyển tự động hóa quá trình sản xuất và đóng gói gói mì. Nguyên liệu, quy trình chuẩn bị sẵn sàng, thật hay nếu chúng ta bấm một nút cho dây chuyền thực thi việc mà nói giỏi nhất, tạo ra các gói mì. Tạo ra Docker Image cũng đơn giản vậy đấy, 1 nút “enter” là xong. À thì là có một câu lệnh trước khi ấn nút “enter”:
docker build -t <image_name>:<image_tag> -f <path_to_Dockerfile> <build_context>
Trên đây là một câu lệnh cơ bản để yêu cầu Docker tạo ra Docker Image, trong đó:
<image_name>:<image_tag>
là cái tên và phiên bản mà chúng ta muốn đặt cho Docker Image, ví dụ:hao_hao:chua_cay
<path_to_Dockerfile>
là đường dẫn trỏ tới tệp tin tới Dockerfile hay trong ngữ cảnh gói mì, thì đây là công thức, bít quyết làm mì gói. Ví dụ đường dẫn tới tệp tin Dockerfile làproject_a/Dockerfile
.<build_context>
là thành phần không thể thiếu, tương tự như nguyên liệu của gói mì thì build_context sẽ cho Docker biết mã nguồn dự án ở đâu. Ví dụ mã nguồn của bạn nằm trong thư mụcproject_a
Và câu lệnh hoàn chỉnh trước cái ấn “enter” của chúng ta sẽ là:
docker build -t hao_hao:chua_cay -f project_a/Dockerfile project_a
Tự động hóa bắt đầu diễn ra, bạn sẽ làm gì trong quá trình tự động hóa diễn ra? Đứng lên ngồi xuống cho đỡ đau lưng đi :D.
Entrypoint (CMD) = Cách người dùng chế biến
Bạn đã dần thấy hương vị của bát mì chưa? Hay bạn có công thức riêng làm bát mì trở lên khác biệt so với bát mì của mình?
Trong phần này, mình đề cập tới câu lệnh để khởi động ứng dụng đóng trong Docker Image. Nó sẽ giống như hướng dẫn sử dụng trên gói mì “Bỏ vắt mì vào bát, cho nước sôi ngập mì, đợi 5 phút rồi thưởng thức”. Và như “hướng dẫn sử dụng” viết trong Dockerfile ở chỉ thị ENTRYPOINT hoặc CMD, Docker sẽ tạo ra Docker Container và kích hoạt ứng dụng bằng câu lệnh trong ENTRYPOINT hoặc CMD.
Tuy nhiên, đâu phải ai cũng làm theo hướng dẫn sử dụng, có người sẽ thích ăn sống uống nguội, có người thì thích bỏ nước, ăn trộn, có người lại thích đem xào thêm rau, … Rất nhiều cách để chúng ta thưởng thức một gói mì. Rất giống nhau về khả năng tùy biến khi sử dụng, Docker Container được tạo ra cũng rất tùy biến về câu lệnh khởi động ứng dụng. Bạn có thể ghi đè chỉ thị CMD hoặc thậm chí cả chỉ thị ENTRYPOINT để thêm vào hay sửa đổi câu lệnh chạy ứng dụng trong Docker Image. Ví dụ, cho Dockerfile sau:
FROM ubuntu:latest
CMD ["Hello World"]
Chúng ta build Docker Image bằng lệnh docker build -t example:v1 -f Dockerfile .
. Hãy cùng xem cách chúng ta ghi đè lệnh CMD:
- Nếu chỉ đơn thuần chạy Docker Image bằng lệnh
docker run example:v1
bạn sẽ nhận về lời chào “Hello World”. - Với câu lệnh chạy Docker Image
docker run example echo "You are good"
, bạn sẽ nhận về lời chúc “You are good”. Bởi, câu lệnh CMD đã bị ghi đè bởi phần command trong câu lệnhdocker run
.
Có sự khác biệt giữa CMD và ENTRYPOINT không? Liệu có thể ghi đè chỉ thị ENTRYPOINT khi tạo ra Docker Container hay không? Nếu có thì sẽ có thêm câu hỏi như thế nào để ghi đè ENTRYPOINT? Mình rất mong chờ các bạn có những câu trả lời trong phần bình luận của bài viết.
Tổng kết
Không có gì tuyệt vời hơn một bát mì tôm xúc xích trứng khi ví dẹp lép. Giờ chúng ta sẽ thấy cả hương vị của Docker trong bát mì nữa.
Hi vọng qua bài viết có thể đem một chút tươi mát cho công việc IT của mọi người khi tìm hiểu mới hay xem lại kiến thức tổng quan về Docker. Biết và hiểu về Docker hay công nghệ containerization vẫn là một ưu điểm, lợi thế khi làm việc, phối hợp giữa các bên phát triển và vận hành. Docker hay công nghệ containerization đã quá phổ biến trong lĩnh vực vận hành, còn với lĩnh vực phát triển ứng dụng, dịch vụ, chúng cũng là công cụ hỗ trợ đắc lực khi cần.
Trong bài viết, cũng có những câu hỏi hướng tới các bạn, hãy cho mình biết suy nghĩ, câu trả lời của các bạn trong phần bình luận của bài viết.
Bình luận
Ha ha, làm bát mỳ tôm Docker có trứng