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ác- servicetheo thứ tự phụ thuộc, ở đây sẽ là khởi động- mysqlvà- redistrước.
- docker-compose up app- tức là khi bạn chỉ khởi động 1 service đơn lẻ thì service- mysqlvà- 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
 
  
 