如何使用Docker部署微服务

什么是微服务?

微服务是用于构建大规模应用程序的越来越流行的体系结构。应用程序不是使用单一的单一代码库,而是分解为一组称为微服务的较小组件。这种方法提供了多种好处,包括扩展单个微服务的能力,使代码库更易于理解和测试,以及为每个微服务使用不同的编程语言,数据库和其他工具。

Docker是管理和部署微服务的绝佳工具。每个微服务可以进一步细分为在单独的Docker容器中运行的进程,可以使用Dockerfiles和Docker Compose配置文件指定。结合Kubernetes等配置工具,每个微服务都可以由开发团队轻松部署,扩展和协作。以这种方式指定环境还可以轻松地将微服务链接在一起以形成更大的应用程序。

本指南介绍如何使用Docker和Docker Compose构建和部署示例微服务。

准备

您需要安装带有Docker和Docker Compose的Linode才能完成本指南。

安装Docker

这些步骤使用官方Ubuntu存储库安装Docker Community Edition(CE)。要在其他发行版上安装,请参阅官方安装页面

  1. 删除系统上可能存在的旧版Docker: sudo apt remove docker docker-engine docker.io
  2. 确保您拥有必要的软件包以允许使用Docker的存储库: sudo apt install apt-transport-https ca-certificates curl software-properties-common
  3. 添加Docker的GPG密钥: curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  4. 验证GPG密钥的指纹: sudo apt-key fingerprint 0EBFCD88 您应该看到类似于以下内容的输出: pub 4096R/0EBFCD88 2017-02-22 Key fingerprint = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 uid Docker Release (CE deb) <docker@docker.com> sub 4096R/F273FCD8 2017-02-22
  5. 添加stableDocker存储库: sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
  6. 更新软件包索引并安装Docker CE: sudo apt update sudo apt install docker-ce
  7. 将有限的Linux用户帐户添加到该docker组: sudo usermod -aG docker exampleuser 您需要重新启动shell会话才能使此更改生效。
  8. 通过运行内置的“Hello World”程序检查安装是否成功: docker run hello-world

安装Docker Compose

  1. 下载最新版本的Docker Compose。检查版本页面并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
  2. 设置文件权限: sudo chmod +x /usr/local/bin/docker-compose

准备环境

本节使用Dockerfiles配置Docker镜像。有关Dockerfile语法和最佳实践的更多信息,请参阅我们的如何使用Dockerfiles指南和Docker的Dockerfile最佳实践指南

  1. 为微服务创建一个目录: mkdir flask-microservice
  2. 在新目录中为微服务组件创建目录结构: cd flask-microservice mkdir nginx postgres web

NGINX

  • 在新nginx子目录中,为NGINX映像创建一个Dockerfile: nginx的/ Dockerfile1 2 from nginx:alpine

COPY nginx.conf /etc/nginx/nginx.conf

  • nginx.conf在Dockerfile中创建引用: /nginx/nginx.conf

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

这个微服务的PostgreSQL映像将使用postgresqlDocker 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

web图片将包含一个示例Flask应用程序。将以下文件添加到web目录以准备应用程序:

  • 创建一个.python-version文件以指定Python 3.6的使用: echo "3.6.0" >> web/.python-version
  • web映像创建Dockerfile : web/ Dockerfile
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"]

  • 创建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 redis

Docker Compose

Docker Compose将用于定义容器及其配置设置之间的连接。

docker-compose.ymlflask-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"

测试微服务

  1. 使用Docker Compose构建所有图像并启动微服务: cd flask-microservice/ && docker-compose up 您应该看到终端中的所有服务都已启动。
  2. 打开一个新的终端窗口并向示例应用程序发出请求: curl localhost Hello Linode! This page has been viewed 1 time(s).
  3. 重置页面点击计数器: curl localhost/resetcounter Successfully deleted redis and postgres counters
  4. 返回到启动Docker Compose的终端窗口以查看标准输出日志:

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

flaskapp_1 | reset visitor count

在生产中使用容器:最佳实践

示例微服务中使用的容器旨在演示在生产中使用容器的以下最佳实践:

容器应该是:

  1. 短暂的:用最少的设置和配置来容易地停止,销毁,重建和重新部署容器。 Flask微服务就是一个理想的例子。使用Docker Compose可以启动或关闭整个微服务。容器运行后无需其他配置,这样可以轻松修改应用程序。
  2. 一次性:理想情况下,较大应用程序中的任何单个容器都应该能够在不影响应用程序性能的情况下失败。使用文件中的restart: on-failure选项docker-compose.yml以及具有副本计数,可以使示例微服务中的某些容器在仍然为Web应用程序提供服务的同时优雅地失败,而不会降低最终用户的性能。 注意: 只有当此配置作为Docker Swarm的一部分进行部署时,副本计数指令才有效,本指南未对此进行介绍。
  3. 快速启动:避免在泊坞文件额外的安装步骤,删除不需要的依赖关系,并建立可重复使用的目标图像有三个在制造具有内码头工人快速初始化时间Web应用程序中最重要的步骤。示例应用程序使用简短,简洁的预构建Dockerfiles,以最大限度地缩短初始化时间。
  4. 快速停止:验证a docker kill --signal=SIGINT {APPNAME}正常停止应用程序。这与重启条件和复制条件一起将确保当容器发生故障时,它们将有效地恢复在线状态。
  5. 轻量级:使用最小的基本容器,它提供构建和运行应用程序所需的所有实用程序。许多Docker镜像都基于Alpine Linux,这是一种轻巧简单的Linux发行版,在Docker镜像中仅占用5MB。使用小型发行版可以节省网络和操作开销,并大大提高容器性能。示例应用程序使用适用的高山图像(NGINX,Redis和PostgreSQL),并为Gunicorn / Flask应用程序使用python-slim基本图像。
  6. 无国籍:由于它们是短暂的,容器通常不应该保持状态。应用程序的状态应存储在单独的持久数据卷中,就像微服务的PostgreSQL数据存储一样。Redis键值存储区确实在容器内维护数据,但这些数据不是应用程序关键的; 如果容器无法响应,Redis存储将正常故障回复到数据库。
  7. 可移植:容器运行时所需的所有应用程序依赖项都应在本地可用。所有示例微服务的依赖项和启动脚本都存储在每个组件的目录中。这些可以检入版本控制,从而可以轻松共享和部署应用程序。
  8. 模块化:每个容器应该有一个责任和一个过程。在这个微服务中,每个主要进程(NGINX,Python,Redis和PostgreSQL)都部署在一个单独的容器中。
  9. 记录:所有容器都应该登录STDOUT。这种一致性使得在单个流中查看所有进程的日志变得容易。
  10. 弹性:如果出于任何原因退出容器,示例应用程序将重新启动其容器。这有助于为Dockerized应用程序提供高可用性和高性能,即使在维护期间也是如此。

更多信息

有关此主题的其他信息,您可能需要参考以下资源。虽然提供这些是希望它们有用,但请注意,我们无法保证外部托管材料的准确性或及时性。

更多教程请前往腾讯云+社区学习更多知识。


参考文献:《https://www.linode.com/docs/applications/containers/deploying-microservices-with-docker/

本文的版权归 阿小庆 所有,如需转载请联系作者。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏实战docker

Docker下,pinpoint环境搭建

在上一章《Docker下,极速体验pinpoint1.6.3》中,我们快速体验了pinpoint的监控和调用链跟踪的服务,本章我们一起来了解pinpoint环境...

4659
来自专栏沃趣科技

ASM 翻译系列第十三弹:ASM 高级知识 - Forcing the issue

原作者:Bane Radulovic 译者: 庄培培 审核: 魏兴华 DBGeeK社群联合出品 Forcing the issue ASM中有部分的...

3855
来自专栏技术翻译

在K8s群集中构建容器映像

了解如何从Kubernetes集群内的Dockerfile构建容器映像源,并将映像推送到IBM Cloud Container Registry; 所有这一切都...

2241
来自专栏刺客博客

(转载)灰常有趣的Docker镜像

1.7K3
来自专栏乐沙弥的世界

再说 Oracle RAC services

        应用程序工作负载在Oracle 10g中可以被定为services,也称之为服务,能够在单实例中使用,也能够在RAC中单独使用和管理。因此整个数...

1194
来自专栏张善友的专栏

数据库厂商提供的 Providers for ASP.NET

ASP.NET一堆内建服务,只提供SqlProvider,至于Oracle或MySQL怎么办?现在Oracle或MySQL也应该是因为微软的不支持,而自己开发相...

1898
来自专栏Netkiller

《Netkiller Virtualization 手札》Docker 卷管理

本文节选择《Netkiller Virtualization 手札》Docker 卷管理 1.5. 卷管理 1.5.1. 列出卷 docker volume l...

3929
来自专栏云计算教程系列

在你的电脑上运行Kubernetes

Kubernetes 是编配平台的首选。在开发过程中,您不妨在个人电脑上运行 Kubernetes,以便在本地启动和调试应用程序。本文提供了两种在 Mac OS...

1932
来自专栏康怀帅的专栏

使用 Docker 安装 Gogs

使用 Docker Compose 安装 Gogs。 GitHub:https://github.com/khs1994-docker/ci docker-co...

6885
来自专栏码匠的流水账

docker化360的pika

1651

扫码关注云+社区