Docker: Chưa biết gì đến biết dùng ( Phần 4 - Một số trick tối ưu và lưu ý )
Bài đăng này đã không được cập nhật trong 6 năm
1. Mở đầu
- Chào các bạn, sau khi đi qua ba phần đầu của series:
Docker - Chưa biết gì đến biết dùng - Chúng ta đã tìm hiểu vai trò, cách sử dụng
Dockerfilke&&docker-compose.ymlđể ứng dụngDockervào trong dự án.

-
Hôm nay, chúng ta sẽ tiếp tục tìm hiểu một số cách để
tối ưucông cụ này nhé ! -
Bài viết tiếp tục dựa trên
Rails frameworkđể tìm hiểu, nhưng tác giả sẽ cố gắng để viết tổng quát vấn đề.
2. Docker-compose up
-
Sau khi chúng ta gõ docker-compose up thì
-
Câu lệnh này sẽ
pull/buildtất cả cả cácimagesmà chúng ta đã định nghĩa -
Trong trường hợp máy đã
có sẵnimage thì sẽ được skip bước này
-
Tiếp theo là
run imagevàcreate container.
-
Rồi đính kèm chúng với các
servicetương ứng (Luôn nhớ rằng mỗicontainerứng với một máy ảo) -
Câu lệnh này sẽ tổng hợp
logscủa tất cả cáccontainervà hiển thị lênTerminal -
Ở đây có
mysql servicevàapp servicenên cólogscủa 2 bác này, màu hiển thị cũng khác nhau cho chúng ta dễ phân biệt.
-
Khi muốn tắt
servicethì nhấn tổ hợp phímCtrl + C
-
Nếu như bạn dùng
docker-compose up -dthì cáccontainerssẽ chạy ngầm, màn hìnhTerminalsẽ không hiển thịlogs
-
-
Với cách triển khai này thì có chút nhược điểm như sau:
-
Như bạn thấy ở trên, khi
Ctrl + Cthì sẽ tắt tất cảservice, -
Khi
docker-compose upthì lại bật tất cả chúng lên. -
Cứ tắt bật, tắt bật tất cả
serviceliên tục như thế, trong project này chỉ có 2servicenên có vẻ chưa hề hấn gì nhưng nếu số lượngservicelớn hơn (ví dụmysql+redis+nginx....) thì cũng mất kha khá thời gian đấy. -
Hơn nữa, khi chúng ta debug code trên Terminal, kiểu như thế này

-
Thì
docker-compose upsẽ không dừng hiển thịlogsđể chúng takiểm tra giá trịcủa các biến chẳng hạn, -
Mà bỏ qua một cách lạnh lùng, như thế này.

-
Bởi vì
docker-compose uphiển thịlogstổng hợp của tất cảservice -
Không vì một
servicenào mà dừng lại cả, như vậy là chúng ta khôngdebugđược.
-
-> Giải pháp là gì ?
-
Thay vì bật tất cả
containerslên cùng một lúc -
Chúng ta sẽ bật các
background containerslên trước (ví dụ: mysql, redis ...) -
Sau đó mới bật
main container(ví dụ: app). -
Khi stop thì ta chỉ stop
main containerthôi, cácbackground containersvẫn chạy ngầm bên dưới. -
Câu lệnh
-
Start background container( chỉ 1 lần khi mới mở máy tính lên )
docker-compose up -d mysql docker-compose up -d redis docker-compose up -d woker -
Start main container
docker-compose run --rm -p 3000:3000 app rails s
-> Với cách này chúng ta sẽ luôn có
background service already running, thời gian khởi động ứng dụng sẽ giảm xuống.->
docker-compose runsẽ dừng để bạn có thể debug,--rmsẽ xóa chính container này đi khi bạn stop nó. -
3. Makefile
- Phải công nhận là gõ những dòng
docker-compose up -d ...nhiều như vậy thật là mệt.

-
Chưa kể
docker-compose build,uprồidown,run... -
Vậy thì hãy dùng Makefile, công dụng của nó thì có nhiều hơn nhưng ở đây mình chỉ ứng dụng nôm na giống như Git alias vậy, không cần phải ghi nhớ và gõ nhiều câu lệnh.
-
Thay vì phải gõ từng câu lệnh một
docker-compose up -d mysql docker-compose up -d redis docker-compose up -d workerhoặc ngắn hơn
docker-compose up -d mysql redis workerthì hãy viết vào trong Makefile:
up: docker-compose up -d mysql redis workervà
dev: docker-compose run --rm -p 3000:3000 app rails s -
Khi đó, trên Terminal gõ
make upđể start cácbackground containers, sau đó gõmake devđể startmain container -
Mình có push code mẫu lên GitHub
-
Hơn nữa, khi có một member mới vào dự án và chưa rõ về Docker (có thể là member của team front end chẳng hạn), khi support setup project, bạn chỉ cần hướng dẫn.
- Chạy
make upđể bật các tiến trình nền. - Chạy
make devđể start project. - Chạy
make testđể test code trước khi gửi pull request.
- Chạy
Mà bản thân họ không cần phải có quá nhiều kiến thức về Docker, khá là tiện lợi phải không 

3. Depends_on && links
- Ở một số hướng dẫn, chúng ta có thể nhìn thấy sử dụng depends_on & links, ví dụ:
app:
build: .
container_name: app
depends_on:
- mysql
- redis
hoặc
app:
build: .
container_name: app
links:
- mysql
- redis
-
depends_onvàlinksđược dùng để thể hiện sự phụ thuộc giữa cácservice, nó tạo ra các hành vi:docker-compose upsẽ khởi động cácservicetheo thứ tự phụ thuộc, ở đây sẽ là khởi độngmysqlvàredistrước.docker-compose up app- tức là khi bạn chỉ khởi động 1 service đơn lẻ thì servicemysqlvàredisvẫn sẽ được khởi động.
-
Sự khác nhau giữa hai đồng chí này, thậm chí links cuối cùng có thể được gỡ bỏ, thay vào đó các service nằm trong cùng một network thì có thể tự tìm thấy nhau.
-
Thì đấy, bạn có thể giải quyết vấn đề này bằng cách sử dụng
depends_onnhưng nhược điểm thì đã nên ở trên, cứ bật lên tắt xuống mất khá nhiều thời gian, trong thực tếservice mysqlcó thể mất tới gần 20s để khởi động. -
Thậm chí khi
mysql containerđã chạy sẵn mà bạnrun container applên thì Docker vẫn tạo mới mộtmysql containernữa chứ không sử dụngmysql containerđó.
4. CMD && entrypoint
5. Layers và images
-
Docker imageđược xây dựng dựa trên cáclayersxếp chồng, giống như việc bạn xếp nhiều viên gạch chồng lên nhau vậy. -
Cùng xem cách Docker build image

-
mysql uses an image, skipping: Container mysql sử dụng image có sẵn bên không cần build image nữa ->skipping -
Building app: Bắt đầu build image chocontainer app. -
Step 1/15,Step 2/15,Step 3/15... Từng câu lệnh trongDockerfilesẽ được thực thi và sẽ tạo ra cáclayerstương ứng.Nếu câu lệnh trước đó đã được thực thi và tạo
layerthì Docker sẽ sử dụnglayercũ đó chứ không tạolayermới nữa, giúpgiảm thời gianbuild image và nếu ở mộtlayercó sự thay đổi thì kể từ layer đó trở về sau, tất cả sẽ được build lại.
-
-
Dùng
docker images -ađể kiểm ra danh sách các images nhé.
Oài, sao nhiều
none imagevậy ? Chúng là gì, sao dung lượng của chúng lớn vậy ? Nó sẽ tiêu thụ nhiều không gian ổ cứng à ?
- Thực ra
none imagechính là nhữnglayers, hãy xem cách Docker pull images về như thế nào.

-
Cũng tương tự như khi chúng ta
build imagevậy, từnglayerđược xây dựng theo mô hình cha con, sinh sau đẻ muộn hơn thì làlayer con, kế thừa từlayer cha, tất cả đều được đặt tên là<none>như bức ảnh ở trên, đếnlayercuối cùng thì mới đầy đủimagecủa chúng ta và đặt tên chính xác. Cùng xem kích thước cáclayerstăng dần kìa.
-
Dòng
---> Using cachexuất hiện mỗi khi bạnbuild imagechính là tái sử dụng cáclayers. Nhữnglayersmà không được tái sử dụng nữa được gọi làdangling images, tạm dịch là nhữngimagelơ lửng -> nó không trỏ tớiimagesnào cả. -
Ủa lạ hây, phải gọi mà
dangling layersmới đúng chứ nhỉ ? Các tài liệu mà mình tham khảo chưa thấy có khái niệm dangling layers, chỉ có dangling images thôi. Mà câu lệnhdocker images -acũng trả về cả danh sáchimages&& danh sáchlayersnữa,-alà viết tắt củaall, thếlayersvớiimageslàsêm sêmnhau à. Cũng có thể hiểu như vậy, vì nếu trongDockerfile, ta xóa bỏ đi vài dòng cuối, thìlayersngoài cùng đó trở thànhimagescòn gì

-
6. Một vài lưu ý nhỏ
Có một chút lưu ý khi sử dụng Docker như sau:
-
Quy định
phiên bảncủa image chi tiết nhất có thể.-
Nếu bạn define
services: mysql: image: mysql container_name: mysqlthì mặc định, Docker sẽ pull
image mysql phiên bản mới nhất về, ở thời điểm bạn viếtDockerfilethì có thể mysql phiên bản mới nhất là5.7nhưng đến khi người khác settup project thì có thể nó đã lên tới phiên bản8.0 -
Và ở giữa phiên bản có những sự thay đổi nhất định, có thể một
hàm, phương thứcnào đó hoạt động tốt ởmysql 5.7nhưng không hoạt động tốt ởmysql 8.0, ví dụ vậy. Như thế sẽ dễ phát sinh bug tiềm ẩn và cũng vi phạm tính đồng nhất môi trường màDockerlấy làm tôn chỉ.
-
-
Ở Dockerfile, các phần dễ thay đổi thì thực hiện về sau.
-
Như lý thuyết về phần
layerđã nhắc tới, nếu ở mộtlayercó sự thay đổi thì kể từ layer đó trở về sau, tất cả sẽ được build lại -
Do vậy, những câu lệnh nào có khả năng thay đôỉ cao, bạn hãy đặt nó xuống dưới cùng.
-
-
Hãy xóa bỏ những
layerskhông cần thiết-
Những
dangling imageskhông còn hữu ích nữa, chúng cũng không được Docker tự động xóa, mà chúng ta sẽ xóa thủ công nó đi để giải phóng bộ nhớ. Cũng chưa rõ các phiên bản sau này, Docker có tự động xử lý việc này không -
Hôm nay là 24/03/2019, Dương lịch, Linux OS
docker -v -->> Docker version 18.09.0, build 4d60db4- Danh sách images:

- Chỉnh sửa nội dung 1 dòng lệnh gần cuối của
Dockerfile(giữ nguyên số lượng câu lệnh) và tiến hànhbuild imagethì thấy từ vị trí thay đổi trở đi không còn được sử dụngcachenữa

- Kiểm danh sách images thì thấy số lượng
none imagestăng lên -> Oh, đúng rồi ^_^

- Kiểm tra danh sách image "lơ lửng", xóa nó đi và kiểm tra lại

Danh sách
none imageschỉ hiển thị id củalayertại vị trí mà bạn thay đổi, khi xóa nó thì sẽ xóa tất cảlayers conđi kèm.Ngoài ra có thể tham khảo lệnh
docker system pruneđể dọn dẹp choDocker
-
5. Updating ...
- Trong phần 5 chúng ta sẽ cùng tìm hiểu về deploy with Docker nhé (bao gồm cấu hình nginx, sử dụng haproxy ....) Mời mọi người đón đọc.
All rights reserved