背景
ちょっとAWS上でサービスをデプロイしたいと思う時があります。本格的にデプロイするとするとCloudFormationでインフラ作ってCodeDeployなどででアプリをデプロイするという形になると思います。将来的に本番で使うならいざしらず、開発の初期や何かのテストという場合はやりたいことの割には大掛かりになりがちです。
そんな用途の時にはAWS Beanstalkがお手軽そうに感じたので、ちょっと調べてみることにしました。
AWS Beanstalkって?
公式ページ:AWS Elastic Beanstalkから引用します。
AWS Elastic Beanstalk は、Java、.NET、PHP、Node.js、Python、Ruby、Go および Docker を使用して開発されたウェブアプリケーションやサービスを、Apache、Nginx、Passenger、IIS など使い慣れたサーバーでデプロイおよびスケーリングするための、使いやすいサービスです。
テストなどのお手軽用途だけでなく、本格的なデプロイにも使えそうです。
Dockerfieが使える?
Docker プラットフォームの使用によると、Dockerfileを使う構成も、docker-compose.ymlを使う構成も出来るようです。
今回の目標
- docker-composeを使用して、nginxと単純なwebサーバーを構築する
- AWSコンソールではなく、ローカルからのコマンドで操作する
コマンドラインツール
公式ページ:Elastic Beanstalk コマンドラインインターフェイス (EB CLI) の使用にある通り、ブラウザでのAWSコンソール以外でも管理できるようです。今回の目的に必要なのでこちらを使います。
インストール
公式Github:EB CLI Installerに従って対応します。
対象OSはUbuntuです。
-
Gitがない場合はinstallする必要があるようです。ただ自分の場合は既にインストールしているのでスキップします。
-
必須パッケージをインストールします
apt install build-essential zlib1g-dev libssl-dev libncurses-dev libffi-dev libsqlite3-dev libreadline-dev libbz2-dev
- gitリポジトリをクローンします
git clone https://github.com/aws/aws-elastic-beanstalk-cli-setup.git
- インストーラーを実行します
./aws-elastic-beanstalk-cli-setup/scripts/bundled_installer
- 環境変数をセットします
インストーラーのメッセージの最後に、パスを通すようにメッセージが表示されます。.bash_profile
への追記は他の動作に影響しそうです。ebへのパスは良いとして、pythonの方は影響する確率高そうです。不安な場合は上手いこと分けたほうが良さそうです。今回は都度exportすることにします。
export PATH="/home/hogeuser/.ebcli-virtual-env/executables:$PATH"
export PATH=/home/hogeuser/.pyenv/versions/3.7.2/bin:$PATH
出てくるメッセージ
***************
5. Finishing up
***************
Success!
Note: To complete installation, ensure `eb` is in PATH. You can ensure this by executing:
1. Bash:
echo 'export PATH="/home/hogeuser/.ebcli-virtual-env/executables:$PATH"' >> ~/.bash_profile && source ~/.bash_profile
2. Zsh:
echo 'export PATH="/home/hogeuser/.ebcli-virtual-env/executables:$PATH"' >> ~/.zshenv && source ~/.zshenv
- NOTE: To complete installation, ensure `python` is in PATH. You can ensure this by executing:
1. Bash:
echo 'export PATH=/home/hogeuser/.pyenv/versions/3.7.2/bin:$PATH' >> /home/hogeuser/.bash_profile && source /home/hogeuser/.bash_profile
2. Zsh:
echo 'export PATH=/home/hogeuser/.pyenv/versions/3.7.2/bin:$PATH' >> /home/hogeuser/.zshrc && source /home/hogeuser/.zshrc
ElasticBeanstalkプロジェクト作成
ElasticBeanstalkのコマンドラインツールを使用するには、aws cli
が使える状態になっている必要があります。aws configure
などを実行し、デプロイしたいAWSアカウントに対してawscliが使用出来るように、状況に応じた対応を行っておきます。
プロジェクトを作りたいフォルダ以下に移動して初期化します。
eb init
- リージョンを聞かれるので選択します。自分は
ap-northeast-1
なので、9
を選択します。 - プロジェクト名を聞かれるので入力します。フォルダ名がデフォルトになってます。そのままにします。
- プラットフォームを選択します。PHPとかPythonとか選べます。今回はDockerを選択します。
3
を選択します。 - OSを選択します。AmazonLinux2以外が非推奨なので、
1
を選択します。 - CodeCommitとの連携が出来るようですが、今回は無視します。
- ssh用のキーペアの設定を選択します。既存のキーペアの番号を選択します。
docker-compose構成
以下のようなフォルダ構成になるようにファイルなどを準備します。
appフォルダ配下のファイルは、AWS公式ページ:チュートリアルおよびサンプルにあるdocker.zipから、アプリ用のファイルを抜き出しました。
.
├── .elasticbeanstalk/ # eb init により生成されます
│ └── config.yml
├── .gitignore # eb init により生成されます
├── Dockerrun.aws.json # 新規作成します(詳細は後述)
├── app/
│ ├── Dockerfile # サンプルからコピー
│ └── application.py # サンプルからコピー
├── docker-compose.yml # 新規作成します
└── nginx/
└── nginx.conf # 新規作成します
docker-compose.yml
version: '3.7'
services:
nginx:
image: nginx
ports:
- 80:80
volumes:
- ./log/nginx/:/var/log/nginx/
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
depends_on:
- app
container_name: beanstalk-nginx
app:
build:
context: ./app
dockerfile: Dockerfile
volumes:
- ./log/app:/tmp/sample-app
ports:
- 8000:8000
container_name: beanstalk-app
nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
gzip_disable "msie6";
include /etc/nginx/sites-enabled/*;
upstream app {
server app:8000;
}
server {
listen 80 default_server;
server_name localhost;
charset utf-8;
location / {
proxy_pass http://app/;
}
}
}
ElasticBeanstalk設定ファイル(Dockerrun.aws.json)作成
本来、ElasticBeanstalk特有の設定を行うものだと思いますが、内容はdocker-compose.ymlの中身と一緒です。
UrlからするとまだV2ですが、公式ページ:複数コンテナの Docker 設定のページを参考に作成します。
Dockerrun.aws.json
{
"AWSEBDockerrunVersion": "3",
"volumes": [
{
"name": "app-log-vol",
"host": {
"sourcePath": "/var/app/current/log/app"
}
},
{
"name": "nginx-log-vol",
"host": {
"sourcePath": "/var/app/current/log/nginx"
}
},
{
"name": "nginx-conf-vol",
"host": {
"sourcePath": "/var/app/current/nginx/nginx.conf"
}
}
],
"containerDefinitions": [
{
"name": "app",
"image": "app",
"essential": true,
"memory": 128,
"mountPoints": [
{
"sourceVolume": "app-log-vol",
"containerPath": "/tmp/sample-app"
}
]
},
{
"name": "nginx",
"image": "nginx",
"essential": true,
"memory": 128,
"portMappings": [
{
"hostPort": 80,
"containerPort": 80
}
],
"links": [
"app"
],
"mountPoints": [
{
"sourceVolume": "nginx-log-vol",
"containerPath": "/var/log/nginx"
},
{
"sourceVolume": "nginx-conf-vol",
"containerPath": "/etc/nginx/nginx.conf"
}
]
}
]
}
volumesブロック
ホスト側パスのエイリアスを指定するもののようです。後にマウントで使用します。
プロジェクトルートからのパスを/var/app/current/
に追記する形で記載します。
containerDefinitionsブロック
コンテナ毎の詳細を記載します。以下その配下のブロックです。
mountPointsブロック
先に指定したvolumesのエイリアスと、コンテナ内部のパスを結びつけます。docker-compose.ymlのvolumesブロックですね。
sourceVolume:ホスト側パスのエイリアス、containerPath:コンテナ内部のパス
portMappingsブロック
ホスト側とコンテナ内部のポート結びつけです。docker-compose.ymlのportsブロックですね。
hostPort:ホスト側ポート、containerPortコンテナ内部ポート
linksブロック
依存関係です。docker-compose.ymlのdepends_onブロックですね。
ふと思ったこと
docker-compose.ymlの設定とDockerrun.aws.jsonの設定で不整合があったらどうなるのだろう?(未実験)
ローカルで起動チェック
eb local run
しかし、以下エラーが出てきます。
ERROR: ValidationError - The AWSEBDockerrunVersion key in the Dockerrun.aws.json file is not valid or is not included.
別件で試した時、こんなエラーも出てきました。
ERROR Instance deployment: 'Dockerrun.aws.json' in your source bundle specifies an unsupported version. Elastic Beanstalk only supports version 1 for non compose app and version 3 for compose app. The deployment failed.
おそらくですが、docker-composeを使う時にはAWSEBDockerrunVersionを3
にしなくてはならず、その時にはローカル実行は出来ないのだと思います。docker-compose.ymlがあるのだからdocker-composeを使ってローカルテストせよという事でしょう。
という事で、普通にdocker-composeを使ってローカルで動作確認します。
AWS環境作成
必要なファイルをzip化してAWS上に環境を作成できます。以下コマンドを実行します。
eb create my-testenv
2021-09-25 01:45:24 INFO Application available at my-testenv.eba-XXXXXXXX.ap-northeast-1.elasticbeanstalk.com.
2021-09-25 01:45:25 INFO Successfully launched environment: my-testenv
というメッセージが出てきて、デプロイ成功したようです。
アクセスチェック
http://my-testenv.eba-XXXXXXXX.ap-northeast-1.elasticbeanstalk.com/
無事アクセスできました。
実際のリソース
起動している間にどんなリソースが生成されたかチェックします。
- EC2(t2.micro)※デフォルトVPCに作成されました。
- Auto Scaling グループ
- ELB(Classic)
手動でチェックしましたがCloudFormationみたく実際のリソースリストがAWSコンソールから見れるといいですね。
ドメイン名からすると、elsticbeanstalk用のR53からサブドメインが割り振られ、それがELBと結びついているという形と思われます。
また、docker-composeを使って2つのコンテナで構成しましたが、EC2は1つでした。AutoScalingの下で動いている以上、それぞれのインスタンスで分けて何か自動で判断するのはさすがに無理ですね。
本番で使用する際にはdocker-composeを使わず、処理内容に応じて1インスタンス1コンテナの構成にしてそれぞれの処理でAutoScalingするというのが望ましいと思います。今回のnginxなんて本来はCloudFrontに任せるべき処理ですし。
今回の使い方ではECRにイメージが作成されることも無い様でした。もちろんDockefile内の処理が多い場合には事前にイメージ作っておいたほうが良いですが、ちょっと試すぐらいなら手順が少なかったり変なゴミが発生しなかったりでよいと思います。
AWS環境破棄
ec2インスタンスなどが起動したままだとお金がかかるので、テスト終了したら環境破棄します。
eb terminate my-testenv
最後に
標準的なwebサービス構成、ELB+Autoscaling+EC2(webサーバー)+DBの形ならばかなり楽になりそうです。
また、今回の様にちょっとだけAWS内にサービス立てたいという時にも有効な選択肢かと思います。
もっとElasticBeanstalkで出来ることを追っておきたいと思えるサービスです。
更新履歴
2021-09-25:Dockerrun.aws.json
の記述を全然していなかったので追記しました。