SaltCloudマップファイルを使用してアプリケーションサーバーとNginxリバースプロキシをデプロイする方法
序章
アプリを作成したら、それをデプロイする必要があります。 本番環境を作成してVMにアプリをセットアップすることもできますが、人気が出たときにどのようにスケーリングしますか? 新しいバージョンをどのように展開しますか? 負荷分散についてはどうですか? そして、最も重要なことは、構成が正しいことをどのように確認できるでしょうか。 これらすべてを自動化して、時間を大幅に節約できます。
このチュートリアルでは、カスタムSaltグレインを使用してサーバーに役割を割り当てたり、リバースプロキシを動的に構成したりするなど、SaltCloudマップファイルでアプリケーションを定義する方法を示します。
このチュートリアルの最後に、2つの基本的なアプリサーバー、動的に構築された構成を持つNginxリバースプロキシ、およびアプリケーションを数分でスケーリングする機能があります。
前提条件
このチュートリアルに従うには、次のものが必要です。
1 GBCentOS7ドロップレット1つ。 このチュートリアルのすべてのコマンドはrootとして実行されるため、sudoの非rootユーザーを作成する必要はありません。
root ユーザー(つまり、 ドロップレットで作成された新しいキーペア)。 このSSHキーをDigitalOceanコントロールパネルに追加して、マスタードロップレットから他のDigitalOceanドロップレットにログインできるようにします。 手順については、DigitalOceanDropletsでSSHキーを使用する方法チュートリアルを使用できます。
DigitalOceanコントロールパネルでキーに割り当てた名前をメモします。 このチュートリアルでは、salt-master-root-keyという名前を使用しています。 秘密鍵の場所もメモしておく必要があります。 デフォルトでは、
/root/.ssh/id_rsaです。DigitalOceanAPIv2の使用方法のこのステップの手順に従って作成できるパーソナルアクセストークン。 スコープは必ず読み取りと書き込みに設定してください。
ステップ1—SaltとSaltCloudのインストール
開始するには、サーバーにSaltCloudをインストールして構成する必要があります。 このチュートリアルでは、Saltブートストラップスクリプトを使用します。
まず、SaltブートストラップスクリプトをエッチングしてSaltをインストールします。
wget -O install_salt.sh https://bootstrap.saltstack.com
Saltブートストラップスクリプトを実行します。 -Mフラグを使用して、salt-masterもインストールします。
sh install_salt.sh -M
Salt CloudコードベースはコアSaltプロジェクトにマージされていますが、CentOS用に個別にパッケージ化されています。 幸い、install_saltスクリプトでリポジトリが構成されたので、yumを使用してsalt-cloudをインストールできます。
yum install salt-cloud
これで、Salt Cloudのバージョンをチェックして、インストールが成功したことを確認できます。
salt-cloud --version
次のような出力が表示されます。
salt-cloud--version出力
salt-cloud 2015.5.3 (Lithium)
Saltはローリングリリースサイクルであるため、バージョンが上記と若干異なる場合があることに注意してください。
ステップ2—ソルトクラウドの構成
このセクションでは、DigitalOceanに接続するようにSalt Cloudを構成し、ドロップレットのいくつかのプロファイルを定義します。
DigitalOceanプロバイダーファイルの構成
Salt Cloudでは、プロバイダーファイルは、新しいVMが作成される場所を定義する場所です。 プロバイダーは/etc/salt/cloud.providers.dディレクトリで定義されています
nanoまたはお気に入りのテキストエディタを使用して、DigitalOceanプロバイダーファイルを作成して開きます。
nano /etc/salt/cloud.providers.d/digital_ocean.conf
以下のテキストを挿入し、 red の変数を自分の変数、つまりサーバーIPとアクセストークン、およびカスタマイズした場合はSSHキー名とファイルに置き換えます。
/etc/salt/cloud.providers.d/digital_ocean.conf
### /etc/salt/cloud.providers.d/digital_ocean.conf ###
######################################################
do:
provider: digital_ocean
minion:
master: your_server_ip
# DigitalOcean Access Token
personal_access_token: your_access_token
# This is the name of your SSH key in your Digital Ocean account
# as it appears in the control panel.
ssh_key_name: salt-master-root-key
# This is the path on disk to the private key for your Digital Ocean account
ssh_key_file: /root/.ssh/id_rsa
SSHキーファイルのアクセス許可をロックダウンする必要があります。 それ以外の場合、SSHはそれを使用することを拒否します。 デフォルトの場所/root/.ssh/id_rsaにあると仮定すると、次の方法でこれを行うことができます。
chmod 600 /root/.ssh/id_rsa
デプロイ可能なサーバーのプロファイルの構成
Salt Cloudでは、プロファイルはプロバイダーに関連付けられた個々のVMの説明です(例: 「DigitalOcean上の512MBのUbuntuVM」)。 これらは/etc/salt/cloud.profiles.dディレクトリで定義されています。
プロファイルファイルを作成して開きます。
nano /etc/salt/cloud.profiles.d/digital_ocean.conf
以下をファイルに貼り付けます。 変更は必要ありません:
/etc/salt/cloud.profiles.d/digital_ocean.conf
### /etc/salt/cloud.profiles.d/digital_ocean.conf ### ##################################################### ubuntu_512MB_ny3: provider: do image: ubuntu-14-04-x64 size: 512MB location: nyc3 private_networking: True ubuntu_1GB_ny3: provider: do image: ubuntu-14-04-x64 size: 1GB location: nyc3 private_networking: True
ファイルを保存して閉じます。 このファイルは2つのプロファイルを定義します。
- ニューヨーク3リージョンにデプロイされた512MBのメモリを搭載したUbuntu14.04VM。
- NewYork3リージョンにデプロイされた1GBのメモリを搭載したUbuntu14.04VM。
Ubuntu 14.04以外のイメージを使用する場合は、Salt Cloudを使用して、次のコマンドでDigitalOceanで使用可能なすべてのイメージ名を一覧表示できます。
salt-cloud --list-images do
これにより、すべての標準DigitalOcean画像と、スナップショットツールを使用してアカウントに保存したカスタム画像が表示されます。 プロバイダーファイルで使用したイメージ名またはリージョンを、このリストとは別のイメージ名に置き換えることができます。 その場合は、プロファイルファイルのimage設定でこの出力のslugフィールドを使用してください。
クイッククエリを使用して構成をテストします。
salt-cloud -Q
次のようなものが表示されます。
salt-cloud-Q出力の例
[INFO ] salt-cloud starting
do:
----------
digital_ocean:
----------
centos-salt:
----------
id:
2806501
image_id:
6372108
public_ips:
192.241.247.229
size_id:
63
state:
active
これは、Salt CloudがDigitalOceanアカウントと通信しており、2つの基本プロファイルが構成されていることを意味します。
ステップ3—簡単な地図ファイルを書く
マップファイルは、作成するサーバーのプロファイルと数を一覧表示するYAMLファイルです。 簡単なマップファイルから始めて、次のセクションでそれを基に構築します。
上記のプロファイルを使用して、2つの1GBアプリサーバーの前に1つの512MBリバースプロキシが必要だとします。 /etc/salt/cloud.maps.d/do-app-with-rproxy.mapという名前のマップファイルを作成し、アプリを定義します。
まず、ファイルを作成します。
nano /etc/salt/cloud.maps.d/do-app-with-rproxy.map
次のテキストを挿入します。 変更は必要ありません:
/etc/salt/cloud.maps.d/do-app-with-rproxy.map
### /etc/salt/cloud.maps.d/do-app-with-rproxy.map #### ###################################################### ubuntu_512MB_ny3: - nginx-rproxy ubuntu_1GB_ny3: - appserver-01 - appserver-02
それでおしまい! これは、マップファイルが取得するのと同じくらい簡単です。 先に進み、これらのサーバーを次のように展開します。
salt-cloud -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map
コマンドが終了したら、すばやくpingを実行して成功を確認します。
salt '*' test.ping
次のように表示されます。
[label salt '*' test.ping
appserver-01:
True
appserver-02:
True
nginx-rproxy:
True
マップファイルにVMを正常に作成したら、それらを削除するのも同じくらい簡単です。
salt-cloud -d -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map
ただし、そのコマンドは注意して使用してください。 そのマップファイルで指定されたすべてのVMを削除します。
ステップ4—より現実的な地図ファイルを書く
そのマップファイルは正常に機能しましたが、シェルスクリプトでさえVMのセットを起動する可能性があります。 必要なのは、アプリケーションのフットプリントを定義することです。 マップファイルに戻って、さらにいくつか追加してみましょう。 マップファイルを再度開きます。
nano /etc/salt/cloud.maps.d/do-app-with-rproxy.map
ファイルの以前の内容を削除し、その中に次のものを配置します。 変更は必要ありません:
/etc/salt/cloud.maps.d/do-app-with-rproxy.map
### /etc/salt/cloud.maps.d/do-app-with-rproxy.map ###
#####################################################
ubuntu_512MB_ny3:
- nginx-rproxy:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: rproxy
ubuntu_1GB_ny3:
- appserver-01:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-02:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
今、私たちはどこかに到達しています! たくさんのように見えますが、追加したのは2つだけです。 mine_functionsセクションとgrainsセクションの2つの追加機能について見ていきましょう。
これらのVMのSaltMinion構成を変更し、カスタムグレインを追加するようにSaltCloudに指示しました。 具体的には、グレインはリバースプロキシVMにrproxyの役割を与え、アプリサーバーにappserverの役割を与えます。 これは、リバースプロキシを動的に構成する必要がある場合に役立ちます。
mine_functionsもSaltMinion構成に追加されます。 ミニオンに、 eth0 で見つかったIPアドレスをソルトマスターに送り返して、ソルトマインに保存するように指示します。 つまり、Salt Masterは、新しく作成されたDropletのIPを、構成しなくても自動的に認識します。 これは次のパートで使用します。
ステップ5—リバースプロキシの定義
現在、私たちの目の前に共通のタスクがあります。それは、リバースプロキシWebサーバーをインストールして構成することです。 このチュートリアルでは、リバースプロキシとしてNginxを使用します。
Nginxソルトステートの記述
手を汚して、いくつかのソルト状態を書く時が来ました。 まず、デフォルトのソルト状態ツリーの場所を作成します。
mkdir /srv/salt
そのディレクトリに移動し、nginx専用のディレクトリをもう1つ作成します。
cd /srv/salt mkdir /srv/salt/nginx
そのディレクトリに移動し、お気に入りのエディタを使用して、rproxy.slsという名前の新しいファイルを作成します。
cd /srv/salt/nginx nano /srv/salt/nginx/rproxy.sls
そのファイルに以下を配置します。 変更は必要ありません:
/srv/salt/nginx/rproxy.sls
### /srv/salt/nginx/rproxy.sls ###
##################################
### Install Nginx and configure it as a reverse proxy, pulling the IPs of
### the app servers from the Salt Mine.
nginx-rproxy:
# Install Nginx
pkg:
- installed
- name: nginx
# Place a customized Nginx config file
file:
- managed
- source: salt://nginx/files/awesome-app.conf.jin
- name: /etc/nginx/conf.d/awesome-app.conf
- template: jinja
- require:
- pkg: nginx-rproxy
# Ensure Nginx is always running.
# Restart Nginx if the config file changes.
service:
- running
- enable: True
- name: nginx
- require:
- pkg: nginx-rproxy
- watch:
- file: nginx-rproxy
# Restart Nginx for the initial installation.
cmd:
- run
- name: service nginx restart
- require:
- file: nginx-rproxy
この状態は次のことを行います。
- Nginxをインストールします。
- カスタム構成ファイルを
/etc/nginx/conf.d/awesome-app.confに配置します。 - Nginxが実行されていることを確認します。
私たちのSalt状態は、単にNginxをインストールし、構成ファイルをドロップします。 本当に興味深いコンテンツは設定にあります。
Nginxリバースプロキシ構成ファイルの書き込み
設定ファイル用にもう1つのディレクトリを作成しましょう。
mkdir /srv/salt/nginx/files cd /srv/salt/nginx/files
そして、設定ファイルを開きます。
nano /srv/salt/nginx/files/awesome-app.conf.jin
設定ファイルに以下を入れてください。 プライベートネットワークを使用していない場合を除き、変更は必要ありません。 その場合、インラインで注記されているように、1を0に変更します。
/srv/salt/nginx/files/awesome-app.conf.jin
### /srv/salt/nginx/files/awesome-app.conf.jin ###
##################################################
### Configuration file for Nginx to act as a
### reverse proxy for an app farm.
# Define the app servers that we're in front of.
upstream awesome-app {
{% for server, addrs in salt['mine.get']('roles:appserver', 'network.ip_addrs', expr_form='grain').items() %}
server {{ addrs[0] }}:1337;
{% endfor %}
}
# Forward all port 80 http traffic to our app farm, defined above as 'awesome-app'.
server {
listen 80;
server_name {{ salt['network.ip_addrs']()[1] }}; # <-- change the '1' to '0' if you're not using
# DigitalOcean's private networking.
access_log /var/log/nginx/awesome-app.access.log;
error_log /var/log/nginx/awesome-app.error.log;
## forward request to awesome-app ##
location / {
proxy_pass http://awesome-app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
.jin拡張子を使用して、ファイルに Jinjatemplatingが含まれていることを確認します。 Jinjaテンプレートを使用すると、テキストファイルに少量のロジックを追加できるため、構成の詳細を動的に生成できます。
この構成ファイルは、すべてのポート80 HTTPトラフィックを取得し、それをアプリファームに転送するようにNginxに指示します。 これには2つの部分があります。アップストリーム(アプリファーム)と、ユーザーとアプリファーム間のプロキシとして機能する構成です。
上流について話しましょう。 通常のテンプレート化されていないアップストリームは、IPのコレクションを指定します。 ただし、ミニオンのIPアドレスは、存在するまでわかりません。また、構成ファイルを手動で編集することもありません。 (そうでなければ、Saltを使用する理由はありません!)
マップファイルのmine_function行を覚えていますか? ミニオンは、そのような機会のためにそれらを保存するためにソルトマスターにIPを与えています。 そのジンジャラインをもう少し詳しく見てみましょう。
神社抜粋
{% for server, addrs in salt['mine.get']('roles:appserver', 'network.ip_addrs', expr_form='grain').items() %}
これはJinjaのforループであり、任意のSalt関数を実行します。 この場合、mine.getを実行しています。 パラメータは次のとおりです。
roles:appserver-これは、「appserver」の役割を持つミニオンからのみ詳細を取得することを意味します。network.ip_addrs-これは私たちが鉱山から取得したいデータです。 これはマップファイルでも指定しました。expr_form='grain'-これは、穀物に基づいてミニオンをターゲットにしていることをソルトに伝えます。 Saltstackターゲティングドキュメントでのグレインによるマッチングの詳細。
このループに続いて、変数テンプレート:AddrsにはIPアドレスのリストが含まれます(アドレスが1つしかない場合でも)。 これはリストなので、[0]で最初の要素を取得する必要があります。
それが上流です。 サーバー名について:
server_name {{ salt['network.ip_addrs']()[0] }};
これは、Salt mine呼び出し(JinjaでSalt関数を呼び出す)と同じトリックです。 簡単です。 network.ip_addrs を呼び出し、返されたリストの最初の要素を取得します。 これにより、ファイルを手動で編集する必要がなくなります。
ステップ6—アプリファームの定義
リバースプロキシは、背後にアプリがない場合はあまり意味がありません。 稼働しているサーバーのIPを報告するだけの小さなNode.jsアプリケーションを作成しましょう(両方のマシンに到達していることを確認できます)。
awesome-appという名前の新しいディレクトリを作成し、そこに移動します。
mkdir -p /srv/salt/awesome-app cd /srv/salt/awesome-app
app.slsという名前の新しいアプリ状態ファイルを作成します。
nano /srv/salt/awesome-app/app.sls
以下をファイルに入れます。 変更は必要ありません:
/srv/salt/awesome-app/app.sls
### /srv/salt/awesome-app/app.sls ###
#####################################
### Install Nodejs and start a simple
### web application that reports the server IP.
install-app:
# Install prerequisites
pkg:
- installed
- names:
- node
- npm
- nodejs-legacy # workaround for Debian systems
# Place our Node code
file:
- managed
- source: salt://awesome-app/files/app.js
- name: /root/app.js
# Install the package called 'forever'
cmd:
- run
- name: npm install forever -g
- require:
- pkg: install-app
run-app:
# Use 'forever' to start the server
cmd:
- run
- name: forever start app.js
- cwd: /root
この状態ファイルは次のことを行います。
nodejs、npm、およびnodejs-legacyパッケージをインストールします。- シンプルなアプリとなるJavaScriptファイルを追加します。
- NPMを使用してForeverをインストールします。
- アプリを実行します。
次に、(小さな)アプリコードを作成します。
mkdir /srv/salt/awesome-app/files cd /srv/salt/awesome-app/files
ファイルを作成します。
nano /srv/salt/awesome-app/files/app.js
その中に以下を入れてください。 変更は必要ありません:
/srv/salt/awesome-app/files/app.js
/* /srv/salt/awesome-app/files/app.js
A simple Node.js web application that
reports the server's IP.
Shamefully stolen from StackOverflow:
http://stackoverflow.com/questions/10750303/how-can-i-get-the-local-ip-address-in-node-js
*/
var os = require('os');
var http = require('http');
http.createServer(function (req, res) {
var interfaces = os.networkInterfaces();
var addresses = [];
for (k in interfaces) {
for (k2 in interfaces[k]) {
var address = interfaces[k][k2];
if (address.family == 'IPv4' && !address.internal) {
addresses.push(address.address)
}
}
}
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(JSON.stringify(addresses));
}).listen(1337, '0.0.0.0');
console.log('Server listening on port 1337');
これは、ポート1337でHTTPリクエストを受け入れ、サーバーのIPで応答するという1つのことを行う単純なNode.jsサーバーです。
この時点で、次のようなファイル構造になっているはずです。
ファイル構造
/srv/salt
├── awesome-app
│ ├── app.sls
│ └── files
│ └── app.js
└── nginx
├── rproxy.sls
└── files
└── awesome-app.conf.jin
ステップ7—アプリケーションのデプロイ
残っているのは、アプリケーションをデプロイすることだけです。
SaltCloudを使用してサーバーをデプロイする
以前と同じ展開コマンドを実行します。これにより、これまでに作成したすべての構成が使用されます。
salt-cloud -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map
ソルトクラウドが完了するのを待ちます。 これにはしばらく時間がかかります。 戻ったら、アプリサーバーにpingを実行して、デプロイが成功したことを確認します。
salt -G 'roles:appserver' test.ping
君は見るべきだ:
アプリサーバーのping出力
appserver-02:
True
appserver-01:
True
リバースプロキシにpingを実行します。
salt -G 'roles:rproxy' test.ping
君は見るべきだ:
リバースプロキシping出力
nginx-rproxy:
True
VMを入手したら、次にそれらを機能させます。
アプリケーションを構築する
次に、Saltコマンドを発行して、アプリファームとリバースプロキシを自動的に構築します。
アプリファームを構築します。
salt -G 'roles:appserver' state.sls awesome-app.app
かなりの量の出力がありますが、次のように終了する必要があります。
Summary ------------ Succeeded: 6 (changed=6) Failed: 0 ------------ Total states run: 6
リバースプロキシを構築します。
salt -G 'roles:rproxy' state.sls nginx.rproxy
繰り返しになりますが、次のように終わるかなりの量の出力があります。
Summary ------------ Succeeded: 4 (changed=4) Failed: 0 ------------ Total states run: 4
では、ここで何が起こったのでしょうか。
最初のコマンド(アプリサーバーを使用するコマンド)は、前に記述したソルト状態を取得し、2つのアプリサーバーで実行しました。 これにより、同じ構成の2台のマシンが同じバージョンのコードを実行することになりました。
2番目のコマンド(リバースプロキシ)は、Nginx用に作成したSalt状態を実行しました。 Nginxと構成ファイルをインストールし、構成ファイルにアプリファームのIPを動的に入力しました。
これらのSaltの実行が完了したら、テストしてデプロイが成功したことを確認できます。 リバースプロキシのIPを見つけます。
salt -G 'roles:rproxy' network.ip_addrs
ドロップレットでプライベートネットワークを使用している場合は、2つのIPが返される可能性があります。
パブリックIPをブラウザに接続して、Webページにアクセスしてください。 更新を数回押して、構築した2つのアプリサーバー間でNginxが実際にプロキシしていることを確認します。 IPが変化し、実際に複数のアプリサーバーに接続していることを確認する必要があります。
更新しても同じIPを取得する場合は、ブラウザのキャッシュが原因である可能性があります。 代わりにcurlを使用して、Nginxがアプリサーバー間でプロキシしていることを示すことができます。 このコマンドを数回実行し、出力を確認します。
curl http://ip-of-nginx-rproxy
これをさらに数ステップ進めて、完全に OverStateを介してアプリケーションの展開を自動化できます。 これにより、単一のコマンドをビルドして、たとえば、リバースプロキシのビルドに進む前に、最初にアプリサーバーをビルドするようにソルトに指示し、ビルドプロセスの順序を保証できます。
ステップ8—スケールアップ(オプション)
Saltを使用するポイントは、ビルドプロセスを自動化することです。 Salt Cloudとマップファイルを使用するポイントは、デプロイメントを簡単にスケーリングすることです。 デプロイメントにさらにアプリサーバー(たとえば、さらに2つ)を追加する場合は、マップファイルを次のように更新します。
/etc/salt/cloud.maps.d/do-app-with-rproxy.map
### /etc/salt/cloud.maps.d/do-app-with-rproxy.map ###
#####################################################
ubuntu_512MB_ny3:
- nginx-rproxy:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: rproxy
ubuntu_1GB_ny3:
- appserver-01:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-02:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-03:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
- appserver-04:
minion:
mine_functions:
network.ip_addrs:
interface: eth0
grains:
roles: appserver
その更新を行った後、ステップ6のsalt-cloudコマンドと2つのsaltコマンドを再実行します。
salt-cloud -m /etc/salt/cloud.maps.d/do-app-with-rproxy.map salt -G 'roles:appserver' state.sls awesome-app.app salt -G 'roles:rproxy' state.sls nginx.rproxy
既存のサーバーはSaltの繰り返し実行の影響を受けず、新しいサーバーは仕様に合わせて構築され、Nginx構成が更新されて、新しいアプリサーバーへのトラフィックのルーティングが開始されます。
結論
サーバーのIPを報告するだけのアプリをデプロイすることはあまり役に立ちません。 幸い、このアプローチはNode.jsアプリケーションに限定されていません。 Saltは、アプリがどの言語で書かれているかを気にしません。
このフレームワークを使用して独自のアプリをデプロイする場合は、サーバーにアプリをインストールするタスクを自動化し(スクリプトまたはSalt状態を使用)、awesome-appを置き換える必要があります。独自の自動化の例。
Saltがドロップレットのプロセスを自動化するのと同じように、SaltCloudはクラウドのプロセスを自動化します。 楽しみ!