Hành trình cùng cá voi xanh (Dockerize App)

1198
06-03-2018
Hành trình cùng cá voi xanh (Dockerize App)

Mục đích của Dockerize app

Docker hiện tại là một công nghệ không phải mới nhưng vẫn rất nổi hiện nay. Hôm nay, Bizfly Cloud sẽ thử Dockerize một ứng dụng nho nhỏ gồm Nginx làm Proxy và Python app.

Mô hình Dockerize app

Mô hình mình dựa theo Tutorial này của Digital Ocean:https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-uwsgi-and-nginx-on-ubuntu-14-04.

Ngắn gọn thì:

Nginx đóng vai trò Reverse Proxy thông qua Wsgi Protocol giao tiếp với Backend App là một Python Web viết bằng Flask. Python Web này chỉ làm mỗi nhiệm vụ trả lời mọi Request bằng một Response "Hello there".

Trong bài Tut của Digital Ocean sử dụng Virtualenv để tạo môi trường độc lâp giữa các Python App nhưng do mình áp dụng Docker vào nên sẽ không cần cài đặt Virtualenv nữa.

Theo Best Practice, mỗi Container của Docker chỉ nên chứa một Service. Do đó, chúng ta sẽ có hai Container: 1 cho Nginx Reverse Proxy, 2 cho Backend Python Flask app. Tất cả Container này đều chạy chung một Host.

Thư mục chứa code và các file config, mình host trên:

https://github.com/dungmanh88/system

trong thư mục app/simple_flask.

Chuẩn bị Dockerfile

Dockerfile sẽ chứa các chỉ thị mà từ đó chúng ta build ra một Docker image.

FROM centos:latest

RUN yum -y install epel-release \

&& yum -y install python34 python-pip python-devel gcc gcc-c \

&& pip install --upgrade pip \

&& pip install flask uwsgi

COPY ./ /simple_flask

WORKDIR /simple_flask

CMD ["uwsgi", "--ini", "wsgi.ini"]

FROM là keyword bắt buộc có, cho biết docker image này sẽ base trên mọt bản centos image. Tốt nhất bạn nên base trên official docker image. Nếu không đánh tag thì mặc định docker hiểu bạn dùng latest version. Nếu không có official docker image nào ưng ý trên docker hub thì bạn nên chọn các image có kèm Dockerfile để biết rõ tác gỉa cài đặt copy cái gì vào đâu trong image. Trong Dockerfile chỉ có duy nhất một chỉ thị FROM.

RUN là chỉ thị cho biết sẽ thực hiện lệnh nào khi build image. RUN thường dùng để chạy các command cài dependency packages. Vì mỗi một chỉ thị trong Dockerfile sẽ phát sinh một layer nên ở đây mình chain các command lại bằng && nhằm hạn chế bớt layer sẽ được tạo ra trong image khiến image nhỏ gọn hơn. Tuy vậy cách làm này lại không tận dụng được cơ chế cache của docker trong qúa trình build image. Mặc định, docker sẽ cache các layer lại nên khi re-build lại thì rất nhanh. Nhưng nếu chỉ có một command trong chuỗi command nối nhau kể trên bị thay đổi thì thì layer đó hiểu là bị thay đổi, docker sẽ chạy lại tất cả các command trong chỉ thị RUN và tất cả các chỉ thị ngay sau đó. Thực sự thì ít khi dependency packages của image thay đổi mà chỉ có phần file được copy vào mới hay thay đổi, nhưng phần này thì dung lượng rất nhỏ nên mình thấy các Dockerfile vẫn hay được viết theo kiểu chain command.

COPY là chỉ thị sẽ copy tất cả các file có trong thư mục hiện tại mà chứa Dockefile trên docker host ngoại trừ các file liệt kê trong .dockerignore Thư mục trên host chứa Dockerfile gọi là build context. Khi build toàn bộ các file/thư mục trong build context được send đến docker server. Nếu build context có một file nặng vài G mà không dùng đến trong image thì nó cũng bị send đến server khiến qúa trình build sẽ bị chậm lại ở bước send context. Đó là lý do một số người dùng .dockerignore để filter bớt các thư mục/file rác trong build context.

Ở đây, .dockerignore của mình gồm:

Dockerfile

README.md

simple_flask.conf

WORKDIR là chỉ thị cho biết khi chạy container thì thư mục nào là điểm khởi đầu

CMD là chỉ thị cho biết lệnh nào được chạy khi bật container. Do chỉ định WORKDIR rồi nên lệnh này sẽ được thực hiện tại /simple_flask trong container luôn. Lệnh này phải thực hiện ở chế độ foreground nếu không container sẽ bị thoát ra ngay lập tức. Để giữ cho container running thi có hai cách:

• Một là chạy process ở foreground

• Hai là chạy process ở background nhưng có tail -f log, dùng tail để giữ cho container tiếp tục running

Trong một Dockerfile chỉ có duy nhất một chỉ thị CMD

Chuẩn bị Docker Network

Mình sẽ cần một user-defined network để giúp các container nói chuyện với nhau dễ dàng.

Trên host đã cài sẵn docker engine, bạn chạy

docker network create my-net

Build image cho python app. Tại thư mục có chứa Dockerfile ( chính là build context ), bạn chạy:

docker build -t xavivn/simple_flask .

Dấu chấm cuối để chỉ thư mục hiện tại là build context. Tên image thường đặt theo chuẩn:

/:

Ở đây mình không khai báo version nên mặc định là latest

Run docker image xavivn/simple_flask

docker run -itd --net=my-net --name=simple_flask xavivn/simple_flask:latest

Lý do phải dùng --name và --net mình đã viết trong http://kipalog.com/posts/Cach-lien-ket-cac-container-lai-voi-nhau-trong-docker

Python app flask này dùng port uwsgi/8000 nhưng port này chỉ cần thiết truy cập từ web proxy nên bạn không cần map port ở đây

Run docker image nginx

docker run -itd --net=my-net --name=nginx -p 80:80 -v /app/simple_flask/nginx/simple_flask.conf:/etc/nginx/conf.d/simple_flask.conf nginx

Do container này sẽ trực tiếp phục vụ client nên cần phải map port 80 trên host với port 80 trong container qua tham số -p

Config của nginx thường hay thay đổi nên mình mount nó ra một file bên ngoài nằm trên host. File /etc/nginx/conf.d/simple_flask.conf trong container sẽ tham chiếu đến file /app/simple_flask/nginx/simple_flask.conf trên host.

Nói chung những file/thư mục nào thường thay đổi thì bạn nên mount ra ngoài. Trừ trường hợp code của web thì nên đóng gói luôn vào container để tiện vận chuyển và dễ dàng deploy ( áp dụng trong CI/CD, docker thường được dùng làm phương tiện vận chuyển môi trường do tính tinh gọn), dễ dàng scale. Khi cần thay đổi code ngay lập tức thì bạn có thể git pull về docker host rồi dùng docker cp ghi đè thư mục code trong container. Trong trường hợp backend db thì khó hơn chút vì datadir khá lớn, nhỏ cũng tầm 20-30G. Lượng data đó qúa lớn nhét vào container thì không được, build vừa lâu lại vừa khó vận chuyển phân phối. Cũng vì lý do này áp dụng docker ở tầng db rất hạn chế, việc scale db vẫn không khác gì so với trước đây. Nếu muốn áp dụng docker tầng db thì phần datadir cần được tách rời và host bằng công nghệ storage như glusterfs, ceph chẳng hạn nhưng giải pháp cho bọn này đòi hỏi nâng cấp hạ tầng network để đảm bảo performance. Cá nhân mình thấy docker áp dụng tốt để làm container cho tầng app, web thôi. Không có giải pháp nào bao trùm hết vấn đề.

Nội dung file này như sau:

upstream simple_flask_app {

server simple_flask:8000;

}

server {

listen 80;

server_name simple.flask.example.com;

location / {

include uwsgi_params;

uwsgi_pass simple_flask_app;

}

}

Chỉ là khai báo một vhost, container python app được tham chiếu qua tên container. Do file config tham chiếu đến container python app nên bạn phải chạy image xavivn/simple_flask trước.

VCCloud sưu tầm

Theo Digital Ocean

>> Tham khảo thêm: Sử dụng Docker thiết lập môi trường tạo Package RPM cho CentOS

TAGS: docker
SHARE