マイクロサービスとは何ですか?
マイクロサービスは、大規模なアプリケーションを構築するためのますます人気のあるアーキテクチャです。アプリケーションは、単一のモノリシックコードベースを使用するのではなく、マイクロサービスと呼ばれる小さなコンポーネントのコレクションに分割されます。このアプローチには、個々のマイクロサービスをスケーリングしたり、コードベースを理解してテストしやすくしたり、マイクロサービスごとに異なるプログラミング言語、データベース、その他のツールを使用できるようにするなど、いくつかの利点があります。
Dockerは、マイクロサービスを管理およびデプロイするための優れたツールです。各マイクロサービスは、DockerfilesおよびDockerCompose構成ファイルで指定できる個別のDockerコンテナーで実行されるプロセスにさらに分割できます。 Kubernetesなどのプロビジョニングツールと組み合わせると、各マイクロサービスを開発者チームが簡単にデプロイ、スケーリング、コラボレーションできます。このように環境を指定すると、マイクロサービスを簡単にリンクして、より大きなアプリケーションを形成することもできます。
このガイドでは、DockerとDockerComposeを使用してサンプルのマイクロサービスを構築およびデプロイする方法を示します。
始める前に
-
まだ行っていない場合は、Linodeアカウントとコンピュートインスタンスを作成します。 Linode入門とコンピューティングインスタンスの作成ガイドをご覧ください。
-
コンピューティングインスタンスのセットアップと保護ガイドに従って、システムを更新してください。また、タイムゾーンの設定、ホスト名の構成、制限付きユーザーアカウントの作成、SSHアクセスの強化を行うこともできます。
注 このガイドは、root以外のユーザーを対象としています。昇格された特権を必要とするコマンドには、接頭辞としてsudo
が付けられます 。 sudo
に慣れていない場合 コマンドを使用すると、ユーザーとグループのガイドを確認できます。
Dockerをインストールする
Docker CE(Community Edition)をインストールするには、以下のいずれかのガイドの手順に従ってください。
さらに多くのLinuxディストリビューションの詳細については、Dockerの公式ドキュメントの「Dockerエンジンのインストール」セクションを参照してください。
DockerComposeのインストール
-
DockerComposeの最新バージョンをダウンロードします。リリースページを確認し、1.25.4
を置き換えます 以下のコマンドで、バージョンが最新リリースとしてタグ付けされています :
sudo curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
-
ファイルのアクセス許可を設定する:
sudo chmod +x /usr/local/bin/docker-compose
環境の準備
このセクションでは、Dockerfilesを使用してDockerイメージを構成します。 Dockerfileの構文とベストプラクティスの詳細については、Dockerfilesの使用方法ガイドとDockerのDockerfileのベストプラクティスガイドを参照してください。
-
マイクロサービス用のディレクトリを作成します:
mkdir flask-microservice
-
新しいディレクトリ内にマイクロサービスコンポーネントのディレクトリ構造を作成します:
cd flask-microservice
mkdir nginx postgres web
NGINX
-
新しいnginx
内 サブディレクトリで、NGINXイメージのDockerfileを作成します:
- ファイル:nginx / Dockerfile
-
1
2
| from nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf |
-
nginx.conf
を作成します Dockerfileで参照されている:
- ファイル:/ nginx / nginx.conf
-
1
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
| 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イメージは、公式の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
\. |
注意 init.sql
の22行目 、テキストエディタがタブをスペースに変換しないことを確認してください。この行のエントリの間にタブがないと、アプリは動作しません。
ウェブ
web
画像にはFlaskアプリの例が含まれます。次のファイルをweb
に追加します アプリを準備するためのディレクトリ:
-
.python-version
を作成します Python 3.6の使用を指定するファイル:
echo "3.6.0" >> web/.python-version
-
web
のDockerfileを作成します 画像:
- ファイル:web / Dockerfile
-
1
2
3
4
5
6
7
8
9
10
| 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
-
1
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
| 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依存関係を持つファイル:
- ファイル:web /requirements.txt
-
1
2
3
4
| flask
gunicorn
psycopg2-binary
redis |
Docker Compose
Docker Composeは、コンテナとその構成設定の間の接続を定義するために使用されます。
docker-compose.yml
を作成します flask-microservice
内のファイル ディレクトリを作成し、以下を追加します:
- ファイル:docker -compose.yml
-
1
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 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" |
マイクロサービスをテストする
-
Docker Composeを使用してすべてのイメージをビルドし、マイクロサービスを開始します:
cd flask-microservice/ && docker-compose up
ターミナルですべてのサービスが開始されていることを確認する必要があります。
-
新しいターミナルウィンドウを開き、サンプルアプリケーションにリクエストを送信します。
curl localhost
Hello Linode! This page has been viewed 1 time(s).
-
ページヒットカウンターをリセットします:
curl localhost/resetcounter
Successfully deleted redis and postgres counters
-
Docker Composeが開始されたターミナルウィンドウに戻り、標準の出力ログを表示します。
flaskapp_1 | DEBUG in linode [/home/flask/app/web/linode.py:56]:
flaskapp_1 | reset visitor count
本番環境でのコンテナの使用:ベストプラクティス
サンプルのマイクロサービスで使用されているコンテナーは、本番環境でコンテナーを使用するための次のベストプラクティスを示すことを目的としています。
コンテナは次のようになります:
-
一時的 :最小限のセットアップと構成で、コンテナーの停止、破棄、再構築、および再デプロイが簡単にできる必要があります。
Flaskマイクロサービスはこの理想的な例です。マイクロサービス全体は、DockerComposeを使用して起動または停止できます。コンテナの実行後に追加の構成は必要ないため、アプリケーションを簡単に変更できます。
-
使い捨て :理想的には、より大きなアプリケーション内の単一のコンテナーは、アプリケーションのパフォーマンスに影響を与えることなく失敗できる必要があります。 restart: on-failure
の使用 docker-compose.yml
のオプション ファイルは、レプリカカウントを持っているだけでなく、サンプルのマイクロサービスの一部のコンテナが、エンドユーザーに悪影響を与えることなく、Webアプリケーションを提供しながら正常に失敗することを可能にします。
注 レプリカカウントディレクティブは、この構成がこのガイドでカバーされていないaDockerSwarmの一部として展開されている場合にのみ有効になります。
-
すぐに開始 :Dockerファイルでの追加のインストール手順の回避、不要な依存関係の削除、再利用可能なターゲットイメージの構築は、Docker内で初期化時間が短いWebアプリケーションを作成する上で最も重要な3つの手順です。サンプルアプリケーションでは、初期化時間を最小限に抑えるために、短く簡潔なビルド済みのDockerfileを使用しています。
-
すばやく停止 :docker kill --signal=SIGINT {APPNAME}
であることを検証します アプリケーションを正常に停止します。これにより、再起動条件とレプリカ条件とともに、コンテナに障害が発生した場合に、コンテナを効率的にオンラインに戻すことができます。
-
軽量 :アプリケーションの構築と実行に必要なすべてのユーティリティを提供する最小のベースコンテナを使用します。多くのDockerイメージは、Dockerイメージでわずか5MBを占める軽量でシンプルなLinuxディストリビューションであるAlpineLinuxに基づいています。小さなディストロを使用すると、ネットワークと運用のオーバーヘッドが節約され、コンテナのパフォーマンスが大幅に向上します。サンプルアプリケーションでは、該当する場合はアルパインイメージ(NGINX、Redis、PostgreSQL)を使用し、Gunicorn/Flaskアプリケーションにはpython-slimベースイメージを使用します。
-
ステートレス :一時的なものであるため、コンテナは通常、状態を維持するべきではありません。マイクロサービスのPostgreSQLデータストアの場合と同様に、アプリケーションの状態は別の永続的なデータボリュームに保存する必要があります。 Redis Key-Valueストアはコンテナー内のデータを維持しますが、このデータはアプリケーションクリティカルではありません。コンテナが応答できない場合、Redisストアはデータベースに正常にフェールバックします。
-
ポータブル :コンテナランタイムに必要なアプリの依存関係はすべて、ローカルで利用可能である必要があります。サンプルのマイクロサービスの依存関係と起動スクリプトはすべて、各コンポーネントのディレクトリに保存されています。これらはバージョン管理にチェックインできるため、アプリケーションの共有と展開が簡単になります。
-
モジュラー :各コンテナには、1つの責任と1つのプロセスが必要です。このマイクロサービスでは、主要なプロセス(NGINX、Python、Redis、PostgreSQL)のそれぞれが個別のコンテナーにデプロイされます。
-
ロギング :すべてのコンテナはSTDOUT
にログを記録する必要があります 。この均一性により、単一のストリームですべてのプロセスのログを簡単に表示できます。
-
回復力 :サンプルアプリケーションは、何らかの理由でコンテナが終了した場合にコンテナを再起動します。これにより、メンテナンス期間中であっても、Dockerizedアプリケーションに高可用性とパフォーマンスを提供できます。
このトピックの詳細については、次のリソースを参照してください。これらは有用であることを期待して提供されていますが、外部でホストされている資料の正確性や適時性を保証することはできません。
- マイクロサービスの例のGithubリポジトリ
- コンテナを使用したマイクロサービスアーキテクチャの構築