Archive for the ‘Container’ Category

Triển khai stack đến Docker Swarm

Posted: Tháng Mười Hai 8, 2018 in Container, Docker
Thẻ:,

Ở bài trước chúng ta giới thiệu tổng quan về docker swarm mà nói về các tính năng, cũng như chạy một service đơn giản trong docker swarm. Trong phần này chúng ta đi vào phần Deploy một stack (gồm các service/container liên kết với nhau) trong docker swarm mà được gọi là “Docker Stack

Reference: Deploy a stack to a swarm

1. Về Docker Stack

  • Docker Swarm đơn thuần điều phối các container chạy trên nhiều các docker host. Nhưng giữa các container đấy không có sự phụ thuộc lẫn nhau. Chính vì vậy, để liên kết các container ấy thành một ứng dụng chúng ta cần phải thao tác thủ công cho phép kết nối giữa chúng.
  • Docker Compose là công cụ mà cho phép liên kết các container với nhau để chạy ứng dụng. Nhưng các container đấy lại chạy trên cùng một docker host. Vì vậy, nó sẽ có mặt hạn chế khi muốn high availability và horizontal scaling.
  • Với Docker Stack, nó cho phép chúng ta kết nối 2 công nghệ trên với nhau. Nó sử dụng tệp tin docker compose (mặc định docker-compose.yml) để định nghĩa, liên kết các container với nhau và chạy trong một cluster docker host (docker swarm). Hình dưới minh họa về Docker Stack

docker stack - docker swarm

2. Triển khai một stack

Chúng ta thực hiện deploy “LEMP” stack đến docker swarm với Docker Stack.

LEMP là stack gồm: Nginx, MySQL/MariaDB và PHP trên Linux

Thực hiện một số công việc sau:

  • Thiết lập docker swarm
  • Định nghĩa các services cho stack
  • Triển khai docker stack

2.1 Thiết lập docker swarm

Chúng ta tham khảo bài viết trước để thiết lập docker swarm

Xác nhận docker swarm đã tạo xong, với một số node

show docker nodes

2.2 Định nghĩa các services cho stack

Chúng ta thực hiện tạo tệp tin docker-compose.yml để định nghĩa các services cho stack, để cho phép triển khai trên docker swarm.

Tệp docker-compose.yml trong docker swarm không hỗ trợ  tùy chọn “build”, vì vậy mà chúng ta sẽ khai báo các image name trực tiếp ở mỗi service. Để sử dụng image name, chúng ta cần thực hiện build image trước với Dockerfile và push lên registry (private hoặc public).

Step1: Dựng private docker registry

Trong bài viết này tôi sử dụng private registry chứa các image, vì vậy chúng ta cần dựng private docker registry. Tham khảo qua bài viết dựng private docker registry

Nếu sử dụng một số public registry (như hub.docker.com), chúng ta có thể bỏ qua bước này.

Step2: Build các docker image

– Build php7-fpm image

Tạo tệp tin Dockerfile để build image cho php7-fpm với nội dung sau

FROM php:7.2-fpm

RUN apt-get update
RUN apt-get install libxml2-dev unzip libmcrypt-dev zlib1g-dev -y
#Using docker-php-ext-install to install php modules
RUN docker-php-ext-install pdo_mysql mysqli mbstring xml opcache zip

EXPOSE 9000
CMD ["php-fpm", "-F"]

Chúng ta sử dụng image gốc là php:7.2-fpm và thực hiện cài một số thư viện và một số module cho php

Thực hiện build image với tên hub.example.local/php7-fpm

 docker build -t hub.example.local/php7-fpm .

build image php7-fpm

– Build nginx image

Nội dung tệp tệp Dockerfile có nội dung như sau:

FROM nginx:latest

MAINTAINER KeepWalking86

ADD ./default.conf /etc/nginx/conf.d/
RUN mkdir -p /var/www/example/
RUN chown -R nginx:nginx /var/www/example

EXPOSE 80

Trong đó, chúng ta build lại từ image gốc nginx:latest, và thực hiện một số tùy như:

Add tệp ‘default.conf’ để thay nội dung tệp cấu hình chính mặc định của nginx. Nội dung tệp như sau:

server {
        listen 80;
        listen [::]:80 ipv6only=on;

    # Log files for Debugging
        access_log /var/log/nginx/laravel-access.log;
        error_log /var/log/nginx/laravel-error.log;

    # Webroot Directory for Laravel project
        root /var/www/example/public;
        index index.php index.html index.htm;

        # Your Domain Name
        server_name example.local;
        location ~* \.(jpg|jpeg|gif|css|png|js|ico|txt|srt|swf|zip|rar|html|htm|pdf)$ {
                access_log        off;
                log_not_found     off;
                expires 30d; # caching, expire after 30 days
        }

        location / {
                try_files $uri $uri/ /index.php?$query_string;
        }

    # PHP-FPM Configuration Nginx
        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                #fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                include fastcgi_params;
                fastcgi_pass web-phpfpm:9000;
        }
}

Ở đây, tôi khai báo một host với tên site “example.local”; vị trí thư mục root của website là “/var/www/example/public”. Website chạy php và sử dụng bộ dịch là FastCGI. Trong đó sử dụng chỉ thị “fastcgi_pass” để thiết lập kết nối đến server FastCGI, mà trong trường  hợp này là kết nối đến server web-phpfpm:9000. Ở đây web-phpfpm là tên service mà ta sẽ định nghĩa ở trong tệp docker-compose.yml trong Step tiếp theo.

Thực hiện build image với tên hub.example.local/nginx

docker build -t hub.example.local/nginx .

build nginx nginx

Step3: Tạo tệp tin docker-compose.yml

Chúng ta định nghĩa 03 service: db, web-phpfpm và web-nginx.

Một số tùy chọn cho mỗi service ở tệp docker compose như sau:

– image: xác định tên image để tạo container

– deploy: tùy chọn cấu hình cho việc triển khai

+ replicas: Số lượng bản sao container

+ constraints: [node.role == worker/manager] → Xác định container sẽ được triển khai ở host nào trong docker swarm. Ở đây, chúng ta xác định vị trí docker host theo vai trò manager/worker trong docker swarm.

– volumes: để mount source code giữa docker host và container. Mục đích của tôi là muốn mount source code tại thư mục ./src trên node manager vào web-phpfpm và web-nginx.

– Ở service “db”, tôi khai báo một số biến ENV để tạo thông tin database như: user, password, … Sau khi tạo và chạy stack, chúng ta có thể kiểm tra kết nối web và db

Tham khảo thêm về cấu hình tệp docker-compose cho docker stack từ:

https://docs.docker.com/compose/compose-file/#deploy

Nội dung tệp docker-compose.yml như sau:

version: "3"

services:
    db:
      image: mariadb:latest
      deploy:
        replicas: 2
        placement:
          constraints: [node.role == worker]
      networks:
        - mynet
      environment:
        MYSQL_ROOT_PASSWORD: P@ssw0rd
        MYSQL_DATABASE: laravel
        MYSQL_USER: keepwalking
        MYSQL_PASSWORD: P@ssw0rd

    web-phpfpm:
      image: hub.example.local/php7-fpm
      deploy:
        replicas: 2
        placement:
          constraints: [node.role == manager]
      volumes:
        - ./src:/var/www/example
      networks:
        - mynet

    web-nginx:
      image: hub.example.local/nginx
      deploy:
        replicas: 2
        placement:
          constraints: [node.role == manager]
      depends_on:
        - db
        - web-phpfpm
      ports:
        - 8080:80
      networks:
        - mynet
      volumes:
        - ./src:/var/www/example

networks:
  mynet:

2.3 Thực hiện deploy docker stack

Chúng ta thực hiện lệnh sau để triển khai stack với tên “lemp”

​docker stack deploy --compose-file docker-compose.yml lemp

Thực hiện một số lệnh sau để kiểm tra stack

Check docker stack lemp

Như vậy, chúng ta đã tạo lemp stack với 03 services. Mỗi service đều nhân bản với 02 replicas. Trong đó service: web-nginx và web-phpfpm được phân bố ở node vnsys (là node manager); service db được phân bố vào node02 và node03 là các node worker.

Tổng quan về Docker Swarm

Posted: Tháng Mười Hai 6, 2018 in Container, Docker
Thẻ:,

Khi số lượng docker container, docker host tăng lên với số lượng lớn, việc triển khai, mở rộng và quản lý riêng lẻ từng container, docker host gây ra khó khăn. Ngoài ra, khi triển khai các ứng dụng bằng docker, chúng ta cũng có nhu cầu chạy cân bằng tải. Để giải quyết những vấn đề trên, chúng ta cần một số hệ thống giúp điều phối container để cho phép triển khai, mở rộng và quản lý được tự động theo cụm docker host.

Hiện tại có một số công cụ điều phối container phổ biến được sử dụng là: Swarm, Kubernetes và Amazon ECS

Trong phạm vi bài này chúng ta sẽ nói trước về Docker Swarm (Công cụ được tích hợp sẵn trong Docker)

1. Về Docker Swarm?

Docker swarm là công cụ điều phối docker container giúp nhóm các docker host riêng lẻ lại với nhau thành cluster.

Mỗi docker host kết nối đến tập hợp swarm đóng vai trò là manager hoặc worker và được gọi là một node.

  • Manager là node có nhiệm vụ quản lý và phân cấp các member. Nó là node được phép thực hiện các lệnh trong một swarm

  • Worker là node mà chạy chế độ swarm. Nó chỉ nhận và thực hiện các lệnh từ manager.

Một docker host có thể là manager, worker hoặc cả 2 chức năng. Khi docker chạy trong chế độ swarm, trên docker host đó vẫn có thể chạy các container độc lập

Một số cải tiến chính của Docker Swarm đó là:

  • Có thể sửa cấu hình service mà bao gồm network, volumes mà nó kết nối mà không cần restart service thủ công.
  • Docker sẽ tự động update cấu hình, dừng các task của service mà có cấu hình và tạo mới serice với cấu hình mong muốn.

Cũng như docker host chạy độc lập, docker swarm cũng có thể sử dụng docker compose để định nghĩa và chạy các container.

Service

Để triển khai một application trong chế độ Swarm, chúng ta tạo một service. Một service là định nghĩa các tasks để thực thi trên các node manager và worker.

Một service áp dụng một trong 2 mode sau:

– replicated mode: khi đó node manager sẽ phân phối lượng các tasks đến các node mà trong thiết lập cấu hình replica.

– global mode: Khi đó swarm chạy một task cho service trên mỗi node có sẵn trong cluster.

Task

Một task giữ thông tin về một docker container và các lệnh để chạy bên trong container đó. Khi một task được gán vào một node, nó không thể chuyển đến node khác.

2. Thiết lập docker swarm

Yêu cầu: docker phiên bản 1.12+ (từ version 1.12 tích hợp sẵn docker swarm)

Một swarm được tạo thành bởi nhiều node, có thể là vật lý hoặc ảo hóa (nhưng thực tế môi trường product là máy vật lý hoặc cloud).

Chúng ta thử tạo cluster với 01 node manager (192.168.10.111) và 02 node worker (192.168.10.112-113)

Step1: Cài đặt Docker trên các host

curl -sSL https://get.docker.com/ | sudo sh
sudo usermod -aG docker `whoami`
systemctl start docker.service
systemctl enable docker.service

Hoặc sử dụng script sau cho cài đặt: install_docker.sh

Step2: Mở ports trên firewall

Trước khi thực khởi tạo và thêm các node đến swarm chúng ta cần thực hiện mở port trên firewall để các docker host có thể giao tiếp với nhau

Trên manager node thực hiện mở các port sau:

  • TCP 2376/2377: Giao tiếp giữa các manager node
  • TCP/UDP 7946: Giao tiếp giữa các node
  • UDP 4789: Cho phép các node giao tiếp qua VXLAN

Nếu sử dụng firewalld thì thực mở như sau:

firewall-cmd --add-port=2376/tcp --permanent 
firewall-cmd --add-port=2377/tcp --permanent 
firewall-cmd --add-port=7946/tcp --permanent 
firewall-cmd --add-port=7946/udp --permanent 
firewall-cmd --add-port=4789/udp --permanent

Trên worker node thực hiện mở port sau:

  • TCP/UDP 7946
  • UDP 4789

Nếu sử dụng firewalld thực hiện mở như sau:

firewall-cmd --add-port=7946/tcp --permanent
firewall-cmd --add-port=7946/udp --permanent
firewall-cmd --add-port=4789/udp --permanent

Step3: Khởi tạo một swarm trên manager node

Trên docker host mà chúng ta dự định sẽ thành manager node, thực hiện lệnh khởi tạo sau:

docker swarm init

docker swarm init 
Swarm initialized: current node (18haxn9fy9d05foo48ysjw345) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5xx36y3voshavvpd1n2j5zecjjg9m4e8gi9oqk99en9zd6vl4w-4n8pqcqal03mkfvce74c36rwz 192.168.10.186:2377

Sau khi khởi tạo thì docker host sẽ là manager node

Để docker host join vào một swarm thì chúng ta cần biết thông tin tokens trên manager node

– Show token cho các node managers

docker swarm join-token manager

docker swarm join –token SWMTKN-1-5xx36y3voshavvpd1n2j5zecjjg9m4e8gi9oqk99en9zd6vl4w-1ifnenemswb7zs4qa9ol59eft 192.168.10.186:2377

– Show token cho các node workers

docker swarm join-token worker
docker swarm join --token SWMTKN-1-5xx36y3voshavvpd1n2j5zecjjg9m4e8gi9oqk99en9zd6vl4w-4n8pqcqal03mkfvce74c36rwz 192.168.10.186:2377

Step4: Thêm các node đến swarm

Để thêm node đến swarm, cú pháp sẽ là:

​​docker swarm join [OPTIONS] HOST:PORT

Phần OPTIONS trong trường hợp này, chúng ta sẽ sử dụng token đã show ở trên

– Thêm node là worker

Trên docker hosts, chúng ta thực hiện lần lượt join đến swarm như sau:

docker swarm join \
--token SWMTKN-1-5xx36y3voshavvpd1n2j5zecjjg9m4e8gi9oqk99en9zd6vl4w-4n8pqcqal03mkfvce74c36rwz \
192.168.10.186:2377

This node joined a swarm as a worker.

Step5: Kiểm tra các nodes trong cluster

– Kiểm tra trạng thái Docker Swarm

docker info

– Show thông tin các node trong swarm

docker node ls

docker node ls

Chúng ta thấy có 01 node làm manager với status là Leader. 02 node còn lại là workers.

3. Deploy service

Để chạy một image trong chế độ swarm, chúng ta không sử dụng lệnh `docker run`, thay vào đó chúng ta sử dụng lệnh `docker service` và lệnh chỉ được thực hiện trên node manager.

3.1 Tạo một service đơn giản

Tạo một service có tên web_nginx, replica với 03 bản, publish với port 8080, mount ra ngoài

docker service create --name web_nginx --replicas 3 --publish 8080:80 \
--mount src=web_nginx,dst=/usr/share/nginx/html nginx

Xác nhận service “web_nginx” đang chạy

docker service ls

Show tasks của web_nginx service

docker service ps web_nginx

Ứng với mỗi replica, thì mỗi node sẽ tạo 01 container.

3.2 Update service

Trong khi service vẫn running, chúng ta có thể thực hiện update cấu hình service mà không cần phải stop hay tạo lại service

Syntax: `docker service update web_nginx [options]`

Sử dụng: `docker service update web_nginx –help` để xem các tùy chọn cần sử dụng

– Scaling service

Chúng ta scale service để mở rộng hoặc giảm bớt số lượng replicas khi service đang running

Ví dụ giảm replicas=02 cho web_nginx service ở trên như sau:

docker service update web_nginx --replicas 2

Hoặc sử dụng

docker service scale web_nginx=2

Note: 2 dòng lệnh trên là tương đương nhau, tuy nhiên có một sự khác biệt giữa sử dụng tham số –replicas và scale.

  • –replicas update số replicas trên một service
  • scale có thể update số lượng replicas trên nhiều service đồng thời

ví dụ:

docker service scale web_nginx=3 web_apache2=3

– Update mount

web_nginx service đã tạo ở trên, chúng ta đang sử dụng “mount” với type mặc định là “volume”. Để sử dụng mount với đường dẫn tuyệt đối (absolute path), chúng ta dùng type với “bind”.

Example:

docker service update web_nginx \
--mount-add type=bind,src=/web_nginx,dst=/usr/share/nginx/html

4. Roll back

Docker swarm cho phép chúng ta thực hiện hiện roll back khi update xảy ra lỗi. Chúng ta có thể cấu hình để thực hiện roll back thủ công hoặc tự động.

4.1 Thực hiện roll back thủ công

Cú pháp thực hiện roll back: `docker service rollback service_name`

Thực hiện roll back web_nginx service

docker service rollback web_nginx

 

docker service rollback web_nginx

4.2 Thực hiện roll back tự động

Một số tùy chọn khi thực hiện roll back

–rollback-delay 0s Thiết lập khoảng time mà task sau thực hiện roll back khi task trước thực hiện xong.
–rollback-failure-action pause Khi một task lỗi, whi đó nó sẽ pause/continue/rollback task tiếp theo
–rollback-max-failure-ratio 0 Thiết lập giá trị chấp nhận/ không chấp nhận lỗi với 0/1 khi có tỷ lệ lỗi trong quá trình roll back.
–rollback-monitor 5s Khoảng thời gian để monitor xem task roll back có ổn định không. Nếu ổn thì thì thực roll back task tiếp, nếu xảy ra lỗi trong khoảng time này thì quá trình roll back sẽ dừng
–rollback-parallelism 1 Số task thực hiện roll back đồng thời, mặc định là 1 task, thiết lập giá trị 0 thì all task thực hiện roll back đồng thời.

Chúng ta thực hiện tạo mới service web_apache, với các tùy chọn cho thực hiện roll back. Sau đó thực hiện quá trình update service để kiểm tra có xảy ra roll back tự động

Step1: Thực hiện tạo service web_apache

Thực hiện tạo service web_apache, và 2 task thực hiện roll back đồng thời, thời gian monitor là 20s cho service web_apache

docker service create --name web_apache --replicas 3 \
--update-failure-action rollback \
--mount type=volume,src=DocumentRoot_apache,dst=/usr/local/apache2/htdocs \
--rollback-parallelism 2 --rollback-monitor 20s httpd

Note: Mặc định tùy chọn “–update-failure-action rollback” có giá trị “pause”, vì vậy mà khi update service có lỗi thì nó sẽ dừng ở task đầu tiên và giữ nguyên trạng thái của các task còn lại. Để thực hiện roll back khi update xảy ra lỗi thì cần thiết lập tùy chọn chế độ roll back với “rollback”

Step2: Thực hiện update service

Thực hiện update service web_apache mà xảy ra lỗi, chẳng hạn update mount với thư mục không tồn tại với đường dẫn /DocumentRoot

docker service update web_apache \
--mount-add type=bind,src=/DocumentRoot,dst=/usr/local/apache2/htdocs

 

automatic rollback

Chúng ta thấy khi quá trình update xảy ra lỗi với service web_apache, khi đó quá trình rollback về phiên bản trước được thực hiện.

5. Rolling update

Tượng tượng rằng, khi triển khai một version mới của ứng dụng, chúng ta cần update ứng dụng đến toàn bộ task trong service. Cách thức đơn giản, là chúng ta sẽ stop service đó và tạo mới service từ một docker image được update. Khi đó, chúng ta thấy rằng cần downtime một lượng thời gian theo phương pháp update này.

Thay vì để downtime trong quá trình update, docker swarm cung cấp tính năng “rolling update”, mà cho phép update từng replica (task) trong khi service vẫn đang hoạt động.

Docker swarm sử dụng tính năng rolling update mặc định và cung cấp tùy chọn với 2 tham số:

  • update-delay: Thiết lập khoảng time ( mặc định là 0 giây; tùy chọn time có thể là giây Ts, phút Tm và giờ là Th) để thực hiện update task tiếp theo nếu quá trình update task trước đã xong và Running

  • update-parallelism: chỉ định số tasks tối đa (mặc định là 1) mà sẽ thực hiện update đồng thời

Qui trình để áp dụng rolling update mặc định như sau:

  • Stop task đầu tiên
  • Thực hiện update task vừa stop
  • Start container cho task vừa update
  • Nếu update cho một task mà trả lại trạng thái là “Running”, khi đó cho một khoảng time (dựa vào tham số update-delay) để thực hiện start task tiếp theo
  • Nếu trong quá trình update, một task trả lại trạng thái “Failed”, quá trình update sẽ được tạm dừng (pause) để đảm không xảy ra lỗi cho các task còn lại và duy trì trạng thái working cho service.
  • Chúng ta thực hiện update mà xảy ra trường hợp lỗi để kiểm tra
docker service update \
--mount-add src=/web_nginx,dst=/usr/share/nginx/html web_nginx

 

update service failed

Khi đó, chúng ta thấy ngay khi update task đầu tiên lỗi, quá trình update service tạm dừng

  • Thực hiện quá trình update service thành công
docker service update \
--mount-add src=web_nginx,dst=/usr/share/nginx/html \
--image nginx:latest web_nginx --update-delay 30s

 

update service success

Ta thấy task sau thực hiện update sau khoảng thời gian 30s khi task trước nó đã Running.

6. Draining nodes

Với các tùy chọn cấu hình mặc định, node manager sẽ thực hiện gán các task đến các node mà ACTIVE trong một swarm. Trong trường hợp, chẳng hạn khi cần dừng node nào đó để bảo trì, hay chúng ta remove một số node từ cluster. Khi đó, chúng ta sử dụng tính năng “drain” trong swarm để thực hiện.

Drain được sử dụng để ngăn một node nhận task mới từ manager và nó cũng dừng task mà đang chạy trên node đó.

Chúng ta kiểm tra các task trên các node trong swarm khi chưa sử dụng drain

before drain node

Khi đó chúng ta thấy các task được phân phối trên tất cả các node.

Cú pháp thực hiện drain node là: `docker node update –availability drain <NODE-ID>`

Chúng ta thực hiện drain “node02” trong swarm như sau:

docker node update --availability drain node02

Sau đó thực hiện check các task trong các service

after drain

Chúng ta thấy các task không chạy trên node02 và cũng thực hiện Shutdown các task đã chạy trước đó, đồng thời các task cũng được chuyển sang node khác.

Kiểm tra trạng thái của các node trong swarm

show node drain

node02 hiện tại STATUS vẫn là ready, nhưng thuộc tính “AVAIBILITY” đang là “Drain”, vì vậy nó không thể thực hiện nhận task từ manager.

Để đưa node02 trở lại thuộc tính “Active”, thêm thuộc tính “availibity” đến active

docker node update --availability active node02

Note: Nếu manager node ở thuộc tính “Drain” thì nó sẽ không nhận các task nhưng vẫn đóng vai trò là manager với nhiệm vụ quản lý, phân cấp các member và thực hiện các lệnh trong swarm.

7. Lập kế hoạch điều phối

Mặc định, các task được gán tự động đến các node mà có trạng thái Ready và Active trong swarm. Task sẽ được phân bố theo thứ tự node mà được tải ít nhất.

Trong nhiều trường hợp, chúng ta muốn tùy chọn phân bố số lượng replicas với node được chỉ định. Chẳng hạn chúng ta chỉ phân phối các task trên các node worker. Hoặc tùy thuộc hiệu năng của docker host, chúng ta điều phối task trên node được chỉ định. Khi đó, chúng ta sử dụng tùy chọn “constraint” trong docker swarm cho lập kế hoạch điều phối các node.

Một số trường hợp thực hành:

– Phân phối các task của service web_apache chỉ trên node worker

docker service create --name web_apache --replicas 3 \
--update-failure-action rollback \
--mount type=volume,src=DocumentRoot_apache,dst=/usr/local/apache2/htdocs \
--rollback-parallelism 2 --rollback-monitor 20s \
--constraint 'node.role == worker' httpd

hoặc nếu update

docker service update web_apache \
--constraint-add 'node.role == worker'

 

constraint with node role

–  Phân phối các task của service web_apache chỉ trên node03

docker service update web_apache \
--constraint-add 'node.hostname == node03'

 

Bài viết đã kết thúc phần giới thiệu tổng quan về docker swarm và các tính năng của nó. Trong bài tiếp sẽ thực hiện triển khai stack (như LEMP) trong docker swarm.

Tham khảo thêm về docker swarm tại: https://docs.docker.com/engine/swarm/

Docker compose

Posted: Tháng Chín 24, 2018 in Container, Docker
Thẻ:, ,

Trong phần trước “Cơ bản Docker“, chúng ta sử dụng Dockerfile để build image và sau đó chạy từng container. Phần này, chúng ta sử dụng Docker Compose.

Docker Compse là công cụ dùng để định nghĩa và chạy nhiều containers cùng lúc. Ví dụ để chạy stack LEMP, thay vì chạy từng container như nginx, mysql và php, chúng ta sử dụng Docker Compose để tạo và chạy toàn bộ stack này cùng lúc.

Docker Compose sử dụng tệp tin YAML để cấu hình các service và kết hợp với nhau để chạy ứng dụng. Trong đó, mỗi service gồm các thông tin cấu hình mà sẽ được áp dụng cho mỗi container, như thông tin về: image, network, volume, …

1. Cấu trúc tệp tin Docker Compose

Một số chỉ thị dùng trong tệp tin Docker Compose

  • version: chỉ ra phiên bản docker-composer sẽ sử dụng. Tùy thuộc Docker version mà khai báo version phù hợp trong tệp tin docker compose.

  • services: Định nghĩa các service sẽ chạy. Mỗi service gồm thông tin cấu hình mà sẽ được áp dụng cho mỗi container.

  • build: sử dụng để build image từ Dockerfile

  • image: Nếu không sử dụng tham số “build” thì sử dụng tham số image để build image nếu image từ local hoặc nếu chưa có sẵn ở local thì nó sẽ pull về.

  • volumes: Mount thư mục giữa host machine và container

  • ports: nat port từ host machine vào container

  • environment: sử dụng các môi trường được định nghĩa trong image

  • depends_on: được dùng để chỉ ra sự phụ thuộc service này vào service khác

Chúng ta có thể tham khảo thêm các tham số dùng cho tệp tin docker compose tại: https://docs.docker.com/compose/compose-file/

2. Cài đặt Docker-compose

Truy cập: https://github.com/docker/compose/releases để download version docker-compose phù hợp

sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

3. Tạo project

Quá trình tạo và chạy một project với Docker composer qua 4 bước:

  • Setup môi trường cho project: như thư mục chứa project, thư mục chứa sourcecode, database, ..

  • Tạo các tệp Dockerfile để build Docker images

  • Định nghĩa các services dùng để chạy App trong tệp tin docker compose

  • Build và chạy App với Docker Compose

3.1 wordpress project

Trong phần hướng dẫn  wordpres project trong phần doc trên trang chủ Docker, có hướng chúng ta cách tạo docker compose cho wordpress project, sử dụng 2 images có sẵn là: mysql:5.7 và wordpress.

Để hiểu rõ hơn quá trình start mỗi service và tạo các containers, chúng ta tự cấu hình và build lại các images và tạo các Dockerfile có cấu trúc rõ ràng.

Các bước tạo wordpress project như sau:

– Tạo thư mục chứa project

mkdir ~/wordpress
cd ~/wordpress
mkdir -p {wordpress,mariadb} #Thư mục chứa tệp tin cấu hình Dockerfile
mkdir -p {web_app,db_data} #Thư mục chứa source code và database

– Tạo các Dockerfile

Chúng ta sẽ tạo 02 tệp Dockerfile dùng cho build 02 images từ base image: mariadb và wordpress.

cd ~/wordpress/wordpress
cat >Dockerfile <<EOF
FROM wordpress
CMD ["apache2-foreground"]
EXPOSE 80
EOF
cd ~/wordpress/mariadb
cat >Dockerfile <<EOF
FROM mariadb
CMD ["mysqld"]
EXPOSE 3306
EOF

– Tạo tệp tin docker-compose.yml

cd ~/wordpress
cat >docker-compose.yml << EOF
version: "3"
services:
    webapp:
        depends_on:
          - db
        build: ./wordpress
        ports:
          - 8080:80
        restart: always
        volumes:
          - ./web_code:/var/www/html
        environment:
          WORDPRESS_DB_NAME: wordpress
          WORDPRESS_DB_HOST: db:3306
          WORDPRESS_DB_USER: wordpress
          WORDPRESS_DB_PASSWORD: P@ssw0rd
          WORDPRESS_TABLE_PREFIX: wp_
    db:
        build: ./mysql
        volumes:
          - ./db_data:/var/lib/mysql
        restart: always
        environment:
          MYSQL_ROOT_PASSWORD: P@ssw0rd
          MYSQL_DATABASE: wordpress
          MYSQL_USER: wordpress
          MYSQL_PASSWORD: P@ssw0rd   
EOF

Tệp cấu hình docker-compose.yml ở trên gồm 02 services: db và webapp

Một số tham số chính trong tệp cấu hình:

  • depends_on:  xác định webapp service sẽ phụ thuộc vào db service. Tham số “depends_on” xác định thứ tự ưu tiên service sẽ bắt đầu trước. Trong tệp tin docker compose ở trên thì db service sẽ start trước , tiếp mới đến webapp service. Để thấy rõ quá trình này chúng ta có thể xem quá trình chạy docker compose ở hình dưới

–  build: để build images từ thư mục chứa Dockerfile thành container.  Chúng ta chỉ nat port 8080 từ host vào 80 của container.

  • volumes: Để mount sourcecode và database giữa docker host và container

  • environment: Gọi các biến mà được định nghĩa ở các images gốc mariadb và wordpress

– Khởi tạo và chạy

docker-compose up -d

Quá trình chạy docker compose như sau:

build by docker-compose

Liệt kê containers vừa build và up, sử dụng “docker ps

docker ps

  • Kiểm tra ứng dụng wordpress

Sử dụng web browser và kiểm tra ứng dụng wordpress đã chạy chưa

http://your-ip-address:8080/

Nếu wordpress chạy thì nó sẽ redirect sang: http://your-ip-address:8080/wp-admin/install.php để bắt đầu quá trình tạo cấu hình wordpress như hình dưới

wordpress

3.2 Nodejs MongoDB

Chúng ta tiếp tục một project nữa, sử dụng docker compose tạo các container để chạy app sử dụng nodejs và cơ sở dữ liệu mongodb

– Step1: Tạo thư mục chứa project

mkdir nodejs-mongodb/{app,database,mongo,node}

Thư mục app,database dùng chứa dữ liệu sourcecode,database. Copy source code vào thư mục “nodejs-mongodb/app”

Thư mục mongo,node chứa tệp tin Dockerfile dùng để build images nodejs và mongodb

–  Step2: Tạo các tệp tin Dockerfile

Tạo tệp tin Dockerfile để build image cho nodejs

cd nodejs-mongodb/node
cat >Dockerfile <<EOF
FROM node:latest
WORKDIR /home/node/app
#update latest npm to fix vulnerabilities
RUN npm i npm@latest -g
RUN npm install -g express-generator
COPY app/package.json /home/node
RUN cd /home/node && npm install
CMD ["npm", "start"]
EXPOSE 3000
EOF

Tạo tệp Dockerfile để build image cho mongodb

cd nodejs-mongodb/mongo
cat >Dockerfile <<EOF
FROM mongo:3.6
WORKDIR /data/db
CMD ["mongod"]
EXPOSE 27017
EOF

– Step3: Tạo tệp tin docker-compose.yml

Bây giờ chúng ta sẽ tạo tệp tin docker-compose.yml để build và run các containers

cd nodejs-mongodb
cat >docker-compose.yml <<EOF
version: "3"
services:
  web:
    depends_on:
      - db
    build: ./node
    restart: always
    ports:
      - 3030:3000
    volumes:
      - ./app:/home/node/app
    hostname: node01
    domainname: example.local
  db:
    build: ./mongo
    restart: always
    ports:
      - 27017:27017
    volumes:
      - ./database:/data/db
    hostname: mongo01
    domainname: example.local
EOF

Tệp docker-compose.yml ở trên gồm 02 service: web và db

service web gồm một số directive sau:

  • depends_on: kết nối và sử dụng database là service “db”

  • build: chúng ta sẽ build một image mới, với đường dẫn chứa Dockerfile trong ./node và sử dụng image này để tạo nodejs container

  • ports: Chúng ta NAT port 3030 vào trong port 3000 của nodejs

  • volumes: dùng mount sourcecode giữa docker host và container

service db: chúng ta sẽ build một image mới, với đường dẫn chứa Dockerfile trong ./mongo và sử dụng image này để tạo mongodb container

Note: để app nodejs có thể kết nối đến mongodb container thì chúng ta sẽ kết nối thông qua tên của service, trong trường hợp này là “db”. Ví dụ:

var db = monk('db:27017/nodejs');

– Step4: Khởi tạo và chạy

docker-compose up -d

Xem thêm về project tại: https://github.com/keepwalking86/nodejs-mongodb

3.3 Chat App Project

Ở project này chúng ta sẽ sử dụng docker compose để build và run 03 containers: node, mongodb và redis

Ở project này chúng ta đã có web_app với dữ liệu có sẵn. Vì thế trong cấu hình chúng ta sẽ mount thư mục chứa web_app vào container

Thông tin sử dụng docker compose để tạo “Chat App” này, mình có đẩy lên github

https://github.com/keepwalking86/chat_app

Cơ bản về Docker

Posted: Tháng Tám 20, 2018 in Container, Docker
Thẻ:,

Công ty chỉ có 02 người dùng Docker =)) ( Mình và 1 thằng Dev). Thỉnh thoáng dùng đến, nên thỉnh thoáng lại quên à :D. Giờ đành viết mấy bài note tìm lại kiến thức cho dễ. Các bài viết về Docker chủ yếu là ở mức basic, làm nền tảng cho các CICD sau này

1. Một số khái niệm

Trước khi dùng thì biết nó là cái gì tí nhỉ?

Docker là platform mà sử dụng các container để phát triển, triển khai và chạy các ứng dụng một cách nhanh chóng mà không làm tác động tới môi trường hiện tại của máy.

Container sử dụng để triển khai các ứng dụng thì được gọi là containerization.

Container được tạo thành khi chạy một image. Image  gồm các thành phần cần thiết để chạy một ứng dụng – code, runtime, các biến, các tệp cấu hình

docker

2. Cài đặt Docker

Cài đặt trên CentOS/Ubuntu, thực hiện đơn giản với sh script

curl -sSL https://get.docker.com/ | sudo sh

Tạo tài khoản người dùng để sử dụng docker

sudo usermod -aG docker `whoami`

Start docker và cho phép chạy cùng OS

systemctl start docker.service
systemctl enable docker.service

Kiểm tra phiên bản Docker

docker version
Client:
Version: 18.06.0-ce
API version: 1.38
Go version: go1.10.3
Git commit: 0ffa825
Built: Wed Jul 18 19:11:02 2018
OS/Arch: linux/amd64
Experimental: false

Server:
Engine:
Version: 18.06.0-ce
API version: 1.38 (minimum version 1.12)
Go version: go1.10.3
Git commit: 0ffa825
Built: Wed Jul 18 19:09:05 2018
OS/Arch: linux/amd64
Experimental: false

3. Các thao tác với image

  • Tải image từ docker hub về máy
$docker pull <image_name>
  • Liệt kê các images trên local
$docker images
  • Xóa một image
$docker rmi <image_name>
$docker rmi <image_id>
  • Xóa all images
$docker rmi -f $(docker images -qa)`
  • Tìm images trong docker hub
$docker search <image_name>
  • Xem thông tin chi tiết về image
$docker image inspect <image_name>
  • Xem lịch sử các commit của image
$docker history <image_name>
  • Đánh lại ​tag của một image
$docker tag <image_name:version> <image_name:new_version>
$docker tag centos:lastest centos:7
  • Upload image
    Sử dụng tạo image mới ở local host
$docker tag <image_name:version> <ten_repo>/<image_name:new_version>
$docker tag nginx keepwalking/nginx:1.13

-Push Images To Docker Hub (giả sử push image keepwalking/docker-leamp)

  • Tạo tài khoản trên docker hub : https://hub.docker.com
  • Tạo repository (Nếu không tạo, thì khi push nó sẽ tự tạo)

vd: https://hub.docker.com/r/keepwalking/docker-leamp/

#Login
docker login
#Show images
docker images
#Tag keepwalking/docker-leamp image
docker tag keepwalking/docker-leamp keepwalking/docker-leamp
#push image
docker push keepwalking/docker-leamp

4. Các thao tác với container

– Tạo một container
Contain được tạo từ image. Nếu image chưa có sẵn trên host thì docker sẽ pull image về từ docker hub

docker run <image_name>
docker run centos

– Thao tác với một container với chế độ tương tác
Sử dụng tùy chọn ‘-it‘ (-i: interactive, -t: tty)

docker run -it <image_name> /bin/bash

– Chạy container trong chế độ background như một daemon
Thông thường, khi tạo một container với các tùy chọn trước thì sau khi tạo xong hoặc thoát container thì ngay lập tức container đó sẽ dừng hoạt động. Trong một số trường hợp ta sẽ cần các container chạy ngầm, trong trường hợp này ta sử dụng tùy chọn ‘-d’

docker run -d <image_name>
docker run -d centos

– Thao tác với một container đã tồn tại
Khi đã một container đang ở trạng thái UP (sử dụng lệnh ‘docker ps‘ để kiểm tra) thì ta có thể truy cập vào để thao tác, chứ không cần tạo mới container và chạy lại từ đầu
Để truy cập vào trong container đã UP ta dùng lệnh:

docker exec -it <container_id> /bin/bash
docker exec -it eb6a2df60f20 /bin/bash

– Chỉ định lượng RAM và CPU cho một container
Một container có thể chỉ định lượng RAM và CPU khi tạo

docker run -d -p 4000:80 --name webserver --memory 400m --cpus 0.5 httpd

– Đặt tên cho một container khi khởi tạo
Thay vì để sinh tự động container name khi khởi tạo, chúng ta tự đặt container name với tùy chọn tham số –name

docker run --name <new_container> <image_name>
docker run --name lemp_centos centos

– Nat port từ host machine vào container (sử dụng -p)
Để NAT port vào container ta sử dụng tham số -p

docker run -p host_port:container_port container_id
$docker run -d -p 8080:80 keepwalking/nginx

– Cũng sử dụng tham số -p để NAT nhiều ports

docker run -p 8080:80 3307:3306 keepwalking/lamp

– Liệt kê các container đang có trên host (gồm đang chạy và đã dừng)

docker ps -a

– Liệt kê các container đang up

docker ps

– Xóa một container

docker rm <container_id>

– Xóa toàn bộ container
Trước khi xóa all containers, chúng ta cần stop all container trước khi thực hiện lệnh xóa

#Stop all containers
docker stop `docker ps -aq`
#Delete all containers
docker rm docker ps -aq
#Có thể sử dụng tham số -f (force) thay vì phải stop container trước khi xóa
docker rm -f `docker ps -aq`
  • Xem thông tin chi tiết về một container
docker inspect <container_id>
  • Kiểm tra port của một container
docker port <container_id>
  • Để xem log của một container ta sử dụng lệnh:
docker logs -f <name_or_container_id>
  • Commit một container thành image (sử dụng trên localhost)
docker commit <container_name> <repo_name>/<image_name:version>
docker start/stop/restart <container_id>

5. Tạo một Dockerfile

5.1 Khái quát Dockerfile

Dockerfile là một tệp tin gồm tập hợp các chỉ thị, mà khi docker gọi tệp tin đó, nó có thể tự động tạo thành các image.
Một số chỉ thị cần biết:
FROM
Là base image để chúng ta tiến hành build một image mới. Chỉ thị này phải được đặt trên cùng của Dockerfile
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]
MAINTAINER
Chứa thông tin của tác giả tiến hành build image (Chỉ thị này là tùy chọn, có thể có hoặc không)
RUN
Chỉ thị này được dùng khi muốn cài đặt cái gói bổ sung trong quá trình build image
RUN <command>
RUN [“executable”, “param1”, “param2”]
COPY
Chỉ thị này dùng để copy tệp tin hoặc thư mục mới từ source (src) và chuyển đến  destination (dest) trong hệ thống tệp tin của container.
Source ở đây có thể là đường dẫn local host hoặc URL.
COPY có 2 forms:
COPY [–chown=<user>:<group>] <src>… <dest>
COPY [–chown=<user>:<group>] [“<src>”,… “<dest>”] (yêu cầu đường dẫn chứa khoảng trắng)
ENV
Định nghĩa các biến môi trường
ENV <key> <value>
ENV <key>=<value> …
EXPOSE
Chỉ thị này dùng khai báo các listen port của container
EXPOSE <port> [<port>/<protocol>…]
Kết hợp với tham số -p để expose port của container ra bên ngoài (NAT port)
CMD
Là chỉ thị cho biết lệnh nào được thực hiện mỗi khi khởi tạo container. Trong Dockerfile chỉ có duy nhất một chỉ thị CMD
CMD [“executable”,”param1″,”param2″] (exec form, this is the preferred form)
CMD [“param1″,”param2”] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell form)
ENTRYPOINT
ENTRYPOINT [“executable”, “param1”, “param2”] (exec form, preferred)
ENTRYPOINT command param1 param2 (shell form)
Chỉ thị ENTRYPOINT cho phép cấu hình một container mà chạy như một lệnh thực thi. ENTRYPOINT sẽ ghi đè lên các phần tử mà được sử dụng bởi chỉ thị CMD.
vd: docker run -it centos top -H
Tương tác CMD và ENTRYPOINT
Cả 2 chỉ thị đều xác định lệnh nào được thực hiện khi chạy một container.
Có vài quy tắc trong sự tương tác giữa 2 chỉ thị:
– Tối thiểu sẽ có 1 chỉ thị được chỉ định trong Dockerfile (tất nhiên là có thể 2 chỉ thị đều xuất hiện trong Dockerfile)
– ENTRYPOINT được định nghĩa khi sử dụng container như một lệnh thực thi
– CMD được sử dụng như một cách để xác định các tham số mặc định cho chỉ thị ENTRYPOINT hoặc thực thi một lệnh ad-hoc trong một container
– CMD sẽ bị ghi đè khi chạy container với các tham số xen kẽ.
VD về CMD & ENTRYPOINT trong Dockerfile:
ENTRYPOINT [“/usr/sbin/apache2ctl”]
CMD [“-D”, “FOREGROUND”]
ADD
Chỉ thị ADD dùng để copy tệp tin, thư mục hoặc tệp tin với URL từ source (src) và thêm chúng đến đường dẫn đích (dest) trong hệ thống tệp tin của image.
ADD có 2 forms:
ADD [–chown=<user>:<group>] <src>… <dest>
ADD [–chown=<user>:<group>] [“<src>”,… “<dest>”] (this form is required for paths containing whitespace)
Note: Để ý là sự khác nhau giữa COPY/ADD. COPY hỗ trợ copy chỉ đường dẫn local; còn ADD có thể hỗ trợ copy được tệp tin nén .tar và hỗ trợ đường dẫn URL.
WORKDIR
Là chỉ thị dùng để thiết lập thư mục làm việc. Nó giống home directory, trong trường hợp này là home directory của container. Khi gọi WORKDIR nó sẽ tạo ra thư mục ngay lần gọi đầu và truy cập đến nó như home directory.
Nó có thể được dùng nhiều lần trong một Dockerfile.
`WORKDIR /path/to/workdir`
###USER
Được sử dụng để thiết lập user để  sử dụng khi chạy image and cho một số chỉ thị: RUN, CMD & ENTRYPOINT trong Dockerfile.
VOLUME
Dùng để  mount file/directories giữa host và container. Mục đích của VOLUME là:
  • Giữ được dữ liệu khi container bị remove
  • Chia sẻ dữ liệu giữa host và container
  • Chia sẻ dữ liệu giữa các container
Tham khảo thêm về các chỉ thị: https://docs.docker.com/engine/reference/builder/

5.2 Thực hiện build image với Dockerfile

Chúng ta build thử gói cài đặt nginx trên centos:7 base image (CentOS7:nginx)
Tạo tệp tin Dockerfile với nội dung sau:
FROM centos:7
MAINTAINER KeepWalking
RUN yum -y install epel-release && yum -y install nginx
#Running with FOREGROUND
CMD ["nginx", "-g", "daemon off;"]
Chạy Nginx ở chế độ FOREGROUND ( mặc định thì nginx chạy ở chế độ BACKGROUND – daemon on)
– Build image
docker build -t keepwalking/CentOS7-Nginx .
– Run container
docker run -it -p 8080:80 KeepWalking/CentOS7-Nginx

6. export/save/load container

Export container thành image
docker export wordpress1 > wordpress.tar
Import image từ tệp đã xuất
docker import wordpress.tar wordpress:xxx
Save image như tệp nén tar
docker save -o mongodb.tar mongodb:xxx
Load image từ tệp tar
docker load < mongodb.tar​