微服务是用于构建大规模应用程序的越来越流行的体系结构。应用程序不是使用单一的单一代码库,而是分解为一组称为微服务的较小组件。这种方法提供了多种好处,包括扩展单个微服务的能力,使代码库更易于理解和测试,以及为每个微服务使用不同的编程语言,数据库和其他工具。
Docker是管理和部署微服务的绝佳工具。每个微服务可以进一步细分为在单独的Docker容器中运行的进程,可以使用Dockerfiles和Docker Compose配置文件指定。结合Kubernetes等配置工具,每个微服务都可以由开发团队轻松部署,扩展和协作。以这种方式指定环境还可以轻松地将微服务链接在一起以形成更大的应用程序。
本指南介绍如何使用Docker和Docker Compose构建和部署示例微服务。
您需要安装带有Docker和Docker Compose的Linode才能完成本指南。
这些步骤使用官方Ubuntu存储库安装Docker Community Edition(CE)。要在其他发行版上安装,请参阅官方安装页面。
stable
Docker存储库:
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"docker
组:
sudo usermod -aG docker exampleuser
您需要重新启动shell会话才能使此更改生效。1.21.2
在下面的命令中替换标记为最新版本的版本:
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本节使用Dockerfiles配置Docker镜像。有关Dockerfile语法和最佳实践的更多信息,请参阅我们的如何使用Dockerfiles指南和Docker的Dockerfile最佳实践指南。
nginx
子目录中,为NGINX映像创建一个Dockerfile:
nginx的/ Dockerfile1 2
from nginx:alpine COPY nginx.conf /etc/nginx/nginx.conf
nginx.conf
在Dockerfile中创建引用:
/nginx/nginx.confuser 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映像将使用postgresql
Docker Hub上的官方映像,因此不需要Dockerfile。
在postgres
子目录中,创建一个init.sql
文件:
Postgres的/ init.sql
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 \. |
---|
警告:在第22行中
init.sql
,确保您的文本编辑器不会将制表符转换为空格。如果此行中的条目之间没有选项卡,该应用程序将无法运行。
该web
图片将包含一个示例Flask应用程序。将以下文件添加到web
目录以准备应用程序:
.python-version
文件以指定Python 3.6的使用:
echo "3.6.0" >> web/.python-versionweb
映像创建Dockerfile :
web/ Dockerfilefrom 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"]
web/linode.py
并添加示例应用程序脚本:
web/ linode.py 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"
requirements.txt
具有所需Python依赖项的文件:
网络/ requirements.txt1 2 3 4
flask gunicorn psycopg2 redisDocker Compose将用于定义容器及其配置设置之间的连接。
docker-compose.yml
在flask-microservice
目录中创建一个文件并添加以下内容:
compose.yml
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | 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 that 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 3 replicas in the case of failure of one of the containers (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" |
---|
flaskapp_1 | DEBUG in linode [/home/flask/app/web/linode.py:56]:
flaskapp_1 | reset visitor count
示例微服务中使用的容器旨在演示在生产中使用容器的以下最佳实践:
容器应该是:
restart: on-failure
选项docker-compose.yml
以及具有副本计数,可以使示例微服务中的某些容器在仍然为Web应用程序提供服务的同时优雅地失败,而不会降低最终用户的性能。
注意: 只有当此配置作为Docker Swarm的一部分进行部署时,副本计数指令才有效,本指南未对此进行介绍。docker kill --signal=SIGINT {APPNAME}
正常停止应用程序。这与重启条件和复制条件一起将确保当容器发生故障时,它们将有效地恢复在线状态。STDOUT
。这种一致性使得在单个流中查看所有进程的日志变得容易。有关此主题的其他信息,您可能需要参考以下资源。虽然提供这些是希望它们有用,但请注意,我们无法保证外部托管材料的准确性或及时性。
更多教程请前往腾讯云+社区学习更多知识。
参考文献:《https://www.linode.com/docs/applications/containers/deploying-microservices-with-docker/》