Triển khai Microservices với Docker

Microservice là gì?

Microservices là một kiến ​​trúc ngày càng phổ biến để xây dựng các ứng dụng quy mô lớn. Thay vì sử dụng một cơ sở mã đơn khối, các ứng dụng được chia thành một tập hợp các thành phần nhỏ hơn được gọi là microservices. Cách tiếp cận này mang lại một số lợi ích, bao gồm khả năng mở rộng các microservices riêng lẻ, giúp cơ sở mã dễ hiểu và dễ kiểm tra hơn, và cho phép sử dụng các ngôn ngữ lập trình, cơ sở dữ liệu và các công cụ khác nhau cho mỗi microservices.

Docker là một công cụ tuyệt vời để quản lý và triển khai các dịch vụ siêu nhỏ. Mỗi dịch vụ siêu nhỏ có thể được chia nhỏ hơn nữa thành các quy trình chạy trong các vùng chứa Docker riêng biệt, có thể được chỉ định bằng Dockerfiles và các tệp cấu hình Docker Compose. Kết hợp với một công cụ cung cấp như Kubernetes, mỗi dịch vụ siêu nhỏ sau đó có thể dễ dàng được triển khai, mở rộng và cộng tác bởi một nhóm nhà phát triển. Việc chỉ định một môi trường theo cách này cũng giúp dễ dàng liên kết các dịch vụ siêu nhỏ với nhau để tạo thành một ứng dụng lớn hơn.

Hướng dẫn này sẽ chỉ cho bạn cách xây dựng và triển khai một dịch vụ siêu nhỏ mẫu bằng Docker và Docker Compose.

Trước khi bạn bắt đầu

  1. Nếu bạn chưa thực hiện, hãy tạo một tài khoản Linode và Compute Instance. Xem hướng dẫn Bắt đầu với Linode và Tạo Compute Instance của chúng tôi .
  2. Làm theo hướng dẫn Thiết lập và Bảo mật Phiên bản Compute của chúng tôi để cập nhật hệ thống của bạn. Bạn cũng có thể muốn đặt múi giờ, cấu hình tên máy chủ, tạo tài khoản người dùng giới hạn và tăng cường quyền truy cập SSH.

Ghi chú: Hướng dẫn này được viết cho người dùng không phải root. Các lệnh yêu cầu quyền nâng cao được thêm tiền tố sudo. Nếu bạn không quen với sudolệnh này, bạn có thể xem hướng dẫn Người dùng và Nhóm của chúng tôi .

Cài đặt Docker

Để cài đặt Docker CE (Phiên bản cộng đồng), hãy làm theo hướng dẫn trong một trong các hướng dẫn dưới đây:

Để xem hướng dẫn cài đặt cho các bản phân phối Linux hoặc hệ điều hành khác như Mac hoặc Windows, hãy tham khảo tài liệu chính thức của Docker tại đây: Cài đặt Docker Engine

Cài đặt Docker Compose

Docker Compose có sẵn trong các biến thể plugin và độc lập. Tuy nhiên, tài liệu chính thức của Docker ưu tiên plugin. Hơn nữa, plugin có cài đặt đơn giản và hoạt động tốt với các lệnh Docker Compose trước đây.

Các bước này cho thấy cách cài đặt plugin Docker Compose. Nếu bạn quan tâm đến việc cài đặt ứng dụng Docker Compose độc ​​lập, hãy làm theo hướng dẫn cài đặt chính thức của Docker .

Ghi chú

Nhiều hướng dẫn vẫn giữ nguyên định dạng lệnh độc lập Docker Compose, trông như sau:

docker-compose [command]

Hãy đảm bảo thay thế lệnh này bằng định dạng lệnh của plugin khi sử dụng phương pháp cài đặt này. Điều này thường chỉ có nghĩa là thay thế dấu gạch nối bằng một khoảng trắng, như trong:

docker compose [command]

1.Bật kho lưu trữ Docker cho trình quản lý gói của hệ thống. Kho lưu trữ thường đã được bật sau khi bạn cài đặt công cụ Docker. Hãy làm theo hướng dẫn liên quan của chúng tôi về cách cài đặt Docker để bật kho lưu trữ trên hệ thống của bạn.

2.Cập nhật trình quản lý gói của bạn và cài đặt plugin Docker Compose.

Trên hệ thống Debian và Ubuntu , hãy sử dụng các lệnh sau:

sudo apt update
sudo apt install docker-compose-plugin

Trên CentOS , Fedora và các bản phân phối dựa trên RPM khác, hãy sử dụng các lệnh sau:

sudo yum update
sudo yum install docker-compose-plugin

Chuẩn bị môi trường

Phần này sử dụng Dockerfiles để cấu hình Docker image. Để biết thêm thông tin về cú pháp Dockerfile và các phương pháp hay nhất, hãy xem hướng dẫn Cách sử dụng Dockerfiles và hướng dẫn Thực hành tốt nhất của Dockerfile của Docker .

1.Tạo thư mục cho dịch vụ vi mô:

mkdir flask-microservice

2.Tạo cấu trúc thư mục cho các thành phần dịch vụ vi mô trong thư mục mới:

cd flask-microservice
mkdir nginx postgres web

NGINX

1.Trong thư mục con mới nginx, hãy tạo Dockerfile cho hình ảnh NGINX:

from nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf

2.Tạo nginx.conftham chiếu trong Dockerfile:

user  nginx;
worker_processes 1;
error_log  /dev/stdout info;
error_log off;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
    use epoll;
    multi_accept on;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /dev/stdout main;
    access_log off;
    keepalive_timeout 65;
    keepalive_requests 100000;
    tcp_nopush on;
    tcp_nodelay on;

    server {
        listen 80;
        proxy_pass_header Server;

        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;

            # app comes from /etc/hosts, Docker added it for us!
            proxy_pass http://flaskapp:8000/;
        }
    }
}

PostgreSQL

Ảnh PostgreSQL cho dịch vụ siêu nhỏ này sẽ sử dụng postgresqlảnh chính thức trên Docker Hub, do đó không cần Dockerfile.

Trong postgresthư mục con, tạo một init.sqltệp:

SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET row_security = off;
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
SET search_path = public, pg_catalog;
SET default_tablespace = '';
SET default_with_oids = false;
CREATE TABLE visitors (
    site_id integer,
    site_name text,
    visitor_count integer
);

ALTER TABLE visitors OWNER TO postgres;
COPY visitors (site_id, site_name, visitor_count) FROM stdin;
1 	linodeexample.com  	0
\.

Quan trọng: Ở Dòng 22 của init.sql, hãy đảm bảo trình soạn thảo văn bản của bạn không chuyển đổi tab thành khoảng trắng. Ứng dụng sẽ không hoạt động nếu không có tab giữa các mục trong dòng này.

Trang web

Hình webảnh sẽ chứa một ứng dụng Flask mẫu. Thêm các tệp sau vào webthư mục để chuẩn bị ứng dụng:

1.Tạo một .python-versiontệp để chỉ định việc sử dụng Python 3.6:

echo "3.6.0" >> web/.python-version

2.Tạo Dockerfile cho webhình ảnh:

from python:3.6.2-slim
RUN groupadd flaskgroup && useradd -m -g flaskgroup -s /bin/bash flask
RUN echo "flask ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN mkdir -p /home/flask/app/web
WORKDIR /home/flask/app/web
COPY requirements.txt /home/flask/app/web
RUN pip install --no-cache-dir -r requirements.txt
RUN chown -R flask:flaskgroup /home/flask
USER flask
ENTRYPOINT ["/usr/local/bin/gunicorn", "--bind", ":8000", "linode:app", "--reload", "--workers", "16"]

3.Tạo web/linode.pyvà thêm tập lệnh ứng dụng mẫu:

from flask import Flask
import logging
import psycopg2
import redis
import sys

app = Flask(__name__)
cache = redis.StrictRedis(host='redis', port=6379)

# Configure Logging
app.logger.addHandler(logging.StreamHandler(sys.stdout))
app.logger.setLevel(logging.DEBUG)

def PgFetch(query, method):

    # Connect to an existing database
    conn = psycopg2.connect("host='postgres' dbname='linode' user='postgres' password='linode123'")

    # Open a cursor to perform database operations
    cur = conn.cursor()

    # Query the database and obtain data as Python objects
    dbquery = cur.execute(query)

    if method == 'GET':
        result = cur.fetchone()
    else:
        result = ""

    # Make the changes to the database persistent
    conn.commit()

    # Close communication with the database
    cur.close()
    conn.close()
    return result

@app.route('/')
def hello_world():
    if cache.exists('visitor_count'):
        cache.incr('visitor_count')
        count = (cache.get('visitor_count')).decode('utf-8')
        update = PgFetch("UPDATE visitors set visitor_count = " + count + " where site_id = 1;", "POST")
    else:
        cache_refresh = PgFetch("SELECT visitor_count FROM visitors where site_id = 1;", "GET")
        count = int(cache_refresh[0])
        cache.set('visitor_count', count)
        cache.incr('visitor_count')
        count = (cache.get('visitor_count')).decode('utf-8')
    return 'Hello Linode!  This page has been viewed %s time(s).' % count

@app.route('/resetcounter')
def resetcounter():
    cache.delete('visitor_count')
    PgFetch("UPDATE visitors set visitor_count = 0 where site_id = 1;", "POST")
    app.logger.debug("reset visitor count")
    return "Successfully deleted redis and postgres counters"

4.Thêm một requirements.txttệp có các phụ thuộc Python cần thiết:

flask
gunicorn
psycopg2-binary
redis

Docker Soạn thảo

Docker Compose sẽ được sử dụng để xác định kết nối giữa các container và cài đặt cấu hình của chúng.

Tạo một docker-compose.ymltệp trong flask-microservicethư mục và thêm nội dung sau:

version: '3'
services:
 # Define the Flask web application
 flaskapp:

   # Build the Dockerfile that is in the web directory
   build: ./web

   # Always restart the container regardless of the exit status; try and restart the container indefinitely
   restart: always

   # Expose port 8000 to other containers (not to the host of the machine)
   expose:
     - "8000"

   # Mount the web directory within the container at /home/flask/app/web
   volumes:
     - ./web:/home/flask/app/web

   # Don't create this container until the redis and postgres containers (below) have been created
   depends_on:
     - redis
     - postgres

   # Link the redis and postgres containers together so they can talk to one another
   links:
     - redis
     - postgres

   # Pass environment variables to the flask container (this debug level lets you see more useful information)
   environment:
     FLASK_DEBUG: 1

   # Deploy with three replicas in the case one of the containers fails (only in Docker Swarm)
   deploy:
     mode: replicated
     replicas: 3

 # Define the redis Docker container
 redis:

   # use the redis:alpine image: https://hub.docker.com/_/redis/
   image: redis:alpine
   restart: always
   deploy:
     mode: replicated
     replicas: 3

 # Define the redis NGINX forward proxy container
 nginx:

   # build the nginx Dockerfile: http://bit.ly/2kuYaIv
   build: nginx/
   restart: always

   # Expose port 80 to the host machine
   ports:
     - "80:80"
   deploy:
     mode: replicated
     replicas: 3

   # The Flask application needs to be available for NGINX to make successful proxy requests
   depends_on:
     - flaskapp

 # Define the postgres database
 postgres:
   restart: always
   # Use the postgres alpine image: https://hub.docker.com/_/postgres/
   image: postgres:alpine

   # Mount an initialization script and the persistent postgresql data volume
   volumes:
     - ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
     - ./postgres/data:/var/lib/postgresql/data

   # Pass postgres environment variables
   environment:
     POSTGRES_PASSWORD: linode123
     POSTGRES_DB: linode

   # Expose port 5432 to other Docker containers
   expose:
     - "5432"

Kiểm tra dịch vụ vi mô

1.Sử dụng Docker Compose để xây dựng tất cả các hình ảnh và khởi động dịch vụ siêu nhỏ:

cd flask-microservice/ && docker-compose up

Bạn sẽ thấy tất cả các dịch vụ đều khởi động trong thiết bị đầu cuối của mình.

2.Mở một cửa sổ terminal mới và gửi yêu cầu tới ứng dụng ví dụ:

curl localhost/resetcounter
Successfully deleted redis and postgres counters

4.Quay lại cửa sổ terminal nơi Docker Compose được khởi động để xem nhật ký đầu ra chuẩn:

flaskapp_1  | DEBUG in linode [/home/flask/app/web/linode.py:56]:
flaskapp_1  | reset visitor count

Sử dụng Container trong sản xuất: Thực hành tốt nhất

Các container được sử dụng trong ví dụ về dịch vụ vi mô nhằm mục đích chứng minh các biện pháp thực hành tốt nhất sau đây khi sử dụng container trong sản xuất:

Các thùng chứa phải:

  1. Tạm thời : Có thể dễ dàng dừng, phá hủy, xây dựng lại và triển khai lại container với thiết lập và cấu hình tối thiểu.Microservice Flask là một ví dụ lý tưởng về điều này. Toàn bộ microservice có thể được đưa lên hoặc hạ xuống bằng Docker Compose. Không cần cấu hình bổ sung nào sau khi các container đang chạy, giúp dễ dàng sửa đổi ứng dụng.
  2. Dùng một lần : Lý tưởng nhất là bất kỳ container đơn lẻ nào trong một ứng dụng lớn hơn đều có thể bị lỗi mà không ảnh hưởng đến hiệu suất của ứng dụng. Sử dụng restart: on-failuretùy chọn trong docker-compose.ymltệp, cũng như có số lượng bản sao, giúp một số container trong dịch vụ siêu nhỏ ví dụ có thể bị lỗi một cách bình thường trong khi vẫn phục vụ ứng dụng web – mà không làm giảm chất lượng của người dùng cuối.Ghi chúChỉ thị đếm bản sao chỉ có hiệu lực khi cấu hình này được triển khai như một phần của Docker Swarm , điều này không được đề cập trong hướng dẫn này.
  3. Bắt đầu nhanh : Tránh các bước cài đặt bổ sung trong tệp Docker, loại bỏ các phụ thuộc không cần thiết và xây dựng hình ảnh mục tiêu có thể tái sử dụng là ba bước quan trọng nhất trong việc tạo ứng dụng web có thời gian khởi tạo nhanh trong Docker. Ứng dụng ví dụ sử dụng Dockerfiles ngắn gọn, súc tích, được dựng sẵn để giảm thiểu thời gian khởi tạo.
  4. Dừng nhanh : Xác thực rằng a docker kill --signal=SIGINT {APPNAME}dừng ứng dụng một cách bình thường. Điều này, cùng với điều kiện khởi động lại và điều kiện sao chép, sẽ đảm bảo rằng khi container bị lỗi, chúng sẽ được đưa trở lại trực tuyến một cách hiệu quả.
  5. Nhẹ : Sử dụng container cơ sở nhỏ nhất cung cấp tất cả các tiện ích cần thiết để xây dựng và chạy ứng dụng của bạn. Nhiều hình ảnh Docker dựa trên Alpine Linux , một bản phân phối Linux nhẹ và đơn giản chỉ chiếm 5MB trong một hình ảnh Docker. Sử dụng một bản phân phối nhỏ giúp tiết kiệm chi phí mạng và hoạt động, đồng thời tăng đáng kể hiệu suất container. Ứng dụng ví dụ sử dụng hình ảnh alpine khi có thể (NGINX, Redis và PostgreSQL) và hình ảnh cơ sở python-slim cho ứng dụng Gunicorn / Flask.
  6. Không trạng thái : Vì chúng là tạm thời, các container thường không duy trì trạng thái. Trạng thái của ứng dụng phải được lưu trữ trong một khối dữ liệu riêng biệt, liên tục, như trường hợp kho dữ liệu PostgreSQL của microservice. Kho lưu trữ khóa-giá trị Redis duy trì dữ liệu trong một container, nhưng dữ liệu này không quan trọng đối với ứng dụng; kho lưu trữ Redis sẽ tự động chuyển ngược về cơ sở dữ liệu nếu container không thể phản hồi.
  7. Portable : Tất cả các dependency của ứng dụng cần thiết cho thời gian chạy container phải có sẵn cục bộ. Tất cả các dependency của microservice mẫu và các tập lệnh khởi động được lưu trữ trong thư mục cho từng thành phần. Những dependency này có thể được kiểm tra trong kiểm soát phiên bản, giúp dễ dàng chia sẻ và triển khai ứng dụng.
  8. Mô-đun : Mỗi container phải có một trách nhiệm và một quy trình. Trong microservice này, mỗi quy trình chính (NGINX, Python, Redis và PostgreSQL) được triển khai trong một container riêng biệt.
  9. Ghi nhật ký : Tất cả các container phải ghi vào STDOUT. Sự thống nhất này giúp dễ dàng xem nhật ký của tất cả các quy trình trong một luồng duy nhất.
  10. Resilient : Ứng dụng ví dụ sẽ khởi động lại các container của nó nếu chúng bị thoát vì bất kỳ lý do gì. Điều này giúp cung cấp cho ứng dụng Dockerized của bạn tính khả dụng và hiệu suất cao, ngay cả trong thời gian bảo trì.

Thông tin thêm

Bạn có thể muốn tham khảo các nguồn sau để biết thêm thông tin về chủ đề này. Mặc dù chúng tôi cung cấp với hy vọng rằng chúng sẽ hữu ích, nhưng xin lưu ý rằng chúng tôi không thể đảm bảo tính chính xác hoặc tính kịp thời của các tài liệu được lưu trữ bên ngoài.

Nguồn: https://www.linode.com/docs/guides/deploying-microservices-with-docker/