Như các bạn biết, khi làm docker thì việt tối ưu hóa cho file docker thật sự cần thiết và quan trọng. File ảnh cho docker nhỏ sẽ giúp cho việt deploy trên k8s diễn ra nhanh chóng, rollback cũng dễ. Ấy vậy mà việc làm ra môt file image cho docker dưới 100Mb thì thật cũng hok dễ. Mình viết bài này để chia sẻ một vài kinh nghiệm để làm cho file docker image bạn nhỏ hơn, sạch hơn.

Một vài tips mà mình thường áp dụng khi làm file ảnh cho docker như sau.

Hãy sử dụng alpine nhiều hơn!

Base image nên sử dụng alpine, hạn chế sử dụng các base image từ các nhánh khác như ubuntu hay debian. Dù rằng tạo file ảnh từ ubuntu dễ và sướng hơn vì mình có thể gõ các lênh quen thuộc như apt-get update, apt-get install. Nói chung muốn tốt thì phải chịu khổ thui anh em à.

Sử dụng multi-stage build.

Một vài thư viện trên python như bscypt hay mysqlclient cần phải compile lại khi cài đặt, do dó trước khi chúng ta sử dụng lệnh pip install mình phải cài một mớ tùm lum như libffi-dev mariadb-dev libc-dev py-gevent autoconf automake g++. Mà cài xong rồi thì file docker mình  sẽ tăng lên một cách vô tội vạ. Mà muốn cleanup sau khi cài đặt thì cũng rất mất công. Bạn nào rành linux thì còn mò mò ra được để dọn rác, chứ mình thì không rảnh, cũng như khá lười để tìm dọn mấy file tạm, rối mấy files nó tải xuống bỏ   vào mấy chỗ như  /tmp hay đâu đâu đó...nói chúng là có trời mới biết lúc gõ các lệnh apt-get install, apk add chúng nó làm cái quái gì bên trong. Do đó ta nên nhớ câu thần chú là chúng ta sẽ thực  hiện các việc cài thư viện c/c++ trên môt stage rồi copy kết quả stage đó sang stage cuối cùng là được.

Hãy xem mình áp dụng 2 cách trên vào việt mình triển khai cho cái Flask API của mình thế nào nhé.

Trước tiên là mình làm một file Dockerfile.builder có nội dụng như sau:

FROM python:3.6-alpine

RUN apk add python3-dev openssl-dev libffi-dev mariadb-dev libc-dev py-gevent autoconf automake g++ make && pip install --upgrade pip

RUN python -m venv /opt/venv

ENV PATH="/opt/venv/bin:$PATH"

COPY requirements.txt /requirements.txt

RUN pip install -r /requirements.txt

Nói chung là file này không cần optimize gì cả, không cần chạy nhiều command trên 1 RUN. Thích cài gì thì cài, copy paste nhanh, gọn lẹ..

Sau đó là tới file Dockerfile chính của mình.

FROM python:3.6-alpine

COPY --from=registry.gitlab.com/rickynguyen/algotraderviet/api:builder /opt/venv /opt/venv

ENV PATH="/opt/venv/bin:$PATH"

COPY . /algov/api/

RUN addgroup -g 1000 -S fa-group && adduser -u 1000 -S fa -G fa-group && chown -Rf fa /algov

WORKDIR /algov/api/

CMD ["gunicorn", "-w", "2", "-b", "0.0.0.0:5000", "rest:app"]

Vậy mình build cái này trên CI thì làm thế nào. Mình thường hay làm bằng docker-compose. Mình cũng khuyên anh em nên xài docker-compose trong CI nhiều hơn. Mình sẽ làm 2 files docker-compose.build.yml và docker-compose.yml. Một file để build cái builder, còn một file để build cái Dockerfile chính. Tại sao phải phức tạp như vậy nhỉ? Tại vì nếu không tách ra mà để chung 1 file thì tại thời điểm mình viết bài này docker hơi ngu, nó sẽ không cache cái builder của mình. Mỗi lần build rất tốn thời gian và công sức!

version: '3.4'
services:
  builder:
    image: registry.gitlab.com/rickynguyen/algotraderviet/api:builder
    build:
      context: module/api
      dockerfile: Dockerfile.builder
      cache_from:
        - registry.gitlab.com/rickynguyen/algotraderviet/api:builder
        
docker-compose.build.yml
version: '3.4'
services:
  api:
    image: registry.gitlab.com/rickynguyen/algotraderviet/api:${IMG_TAG:-stable}
    hostname: "api"
    build:
      context: module/api
      dockerfile: Dockerfile
      cache_from:
        - registry.gitlab.com/rickynguyen/algotraderviet/api
        - registry.gitlab.com/rickynguyen/algotraderviet/api:builder
        - registry.gitlab.com/rickynguyen/algotraderviet/api:stable
docker-compose.yml

Trên CI mình chỉ cần thực hiện theo các bước sau là được nà.

    - docker-compose -f docker-compose.build.yml pull --ignore-pull-failures
    - docker-compose -f docker-compose.build.yml build --parallel
    - docker-compose -f docker-compose.build.yml push
    - docker-compose pull --ignore-pull-failures
    - docker-compose build --parallel
    - docker-compose push

Thành quả của mình trên Gitlab như sau:

Chỉ 67Mb cho 1 file ảnh. Thật ra mình có thể làm cho nó nhỏ hơn nữa. Chúc vui anh em!

via GIPHY