AWS初心者が本を1冊やってみて学んだことメモ

AWSを使い始めて5日目。ググれどググれど大枠が見えないのでさっぱりわからない。
というわけで本を1冊ハンズオンでやってみました。
内容を忘れないようにメモしておきます。

やった本

『Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂版』 日経BP社

アプリ開発は勉強したけど、インフラはさっぱりわからない…そんな自分にぴったりでした。
インフラエンジニアの間では常識なのだろうな(TCP/IPとかHTTPメソッドとか)…ということも丁寧に繰り返し説明してくれています。

学んだこと

IPアドレスとは

TCP/IP通信において、通信先を特定するのに使われるのがIPアドレス。ネットワーク上で互いに重複しない唯一無二の番号、いわゆる「住所」に相当する。

パブリックIPアドレス

インターネットに接続する際に用いるIPアドレスのこと。グローバルIPアドレスとも言う。重複を避けるためICANNと言う団体が一括管理している。

プライベートIPアドレス

インターネットで使われないIPアドレス。10.0.0.0 ~ 10.255.255.255など、範囲が決められている。誰にも申請することなく使える。社内LAN構築時や、自前でネットワークの実験をするときはこれを使う。

ホスト

コンピュータやルーターなどのネットワーク機器など、IPアドレスをもつ通信機器の総称。

VPC領域

VPC = Virtual Private Cloud
つまり、プライベートなネットワーク空間。
作成したユーザーが自由に扱うことができる空間で、他のユーザーからは全く見えない。
IPアドレスをCIDR表記する場合、その範囲は「CIDRブロック」と呼ばれる。
このCIDRブロックをさらに小さなCIDRブロックに細分化したものをサブネットと呼ぶ。

同書を元に作成した図。
image

パブリックサブネット:インターネットからアクセスできる
プライベートサブネット:インターネットからはアクセスできない
→セキュリティを高める時によく用いられるネットワーク構成

サブネットをインターネットに接続するには、「インターネットゲートウェイ(Internet Gateway)」を用いる。自分のネットワークにインターネット回線を引き込むイメージ。

ルートテーブル

宛先IPアドレスの値がいくつのときに、どのネットワークに流すべきか、と言う設定。

「宛先アドレス」 「流すべきネットワークの入り口となるルーター」

という書式で設定。
宛先アドレス=ディスティネーション(destination)
流すべきネットワーク先=ネクストホップ(next hop)、ターゲット(target)

TCP/IP

ポート(Port):他のコンピュータと、データを送受信するためのデータの出入り口

ポートには、以下の2種類がある。
TCP(Transmission Control Protocol):相手にデータが届いたことを保証する。
UDP(User Datagram Protocol):確認せずに送信する(その代わりに高速)

ファイアウォール

「通してよいデータだけを通して、それ以外を遮断する機能」の総称。
そのもっとも簡単な構造のものがパケットフィルタリング(Packet Filtering)。

パケットフィルタリング

流れるパケットをみて、通過の可否を決める仕組み。パケットには、「IPアドレス」のほか「ポート番号」も含まれている。パケットフィルタリングは、「IPアドレス」と「ポート番号」など、パケットに付随する各種情報を見て、通過の可否を決める。
AWSでは、インスタンスに対して構成する「セキュリティグループ」がこの機能を担当する。

インバウンドとアウトバウンド

インバウンド:外から、このインスタンスに接続する向き(例 誰かが接続しようとしているのを排除する)
アウトバウンド:このインスタンスから外に出て行く向き

NAT

NAT=Network Address Translation
「プライベートサブネット→インターネット」の向きの通信だけを許可する。

例えば、DBサーバーはインターネットからは接続されたくない。しかし、サーバーのアップデートやソフトウェアのインストールのために、DBサーバーからインターネットへは接続できるようにしたい。そういうときは、DBサーバー(プライベートサブネット)→インターネットの一方向の通信のみを許可できる。

curlコマンド

「HTTPやFTPで、ファイルをダウンロードしたりアップロードしたりするコマンド」。Amazon Linuxにはtelnetコマンドがインストールされていないため、代わりにcurlコマンドを使う。

続きを読む

Fresh Install bitnami redmine stack on AWS, then migrate old data and database from old instance.

Fresh Install bitnami redmine stack on AWS, then migrate old data and database from old instance.

目的: Redmine のバージョンアップ と HTTPS化

ですが、新環境の構築手順のみやっていただければ新規構築手順としても使えます。

  • 前回書いた記事から1年ちょっと経過しました。
  • bitnami redmine stack AMI のバージョンも以下のように変化しました。(2017/06/21 現在)
    • Old version 3.2.1
    • New version 3.3.3
  • 前回は GUI でぽちぽち作成しましたが、今回は AWS CLI を中心に進めます。
  • ついでに前回書いてなかった HTTPS化 についても簡単に追記します。
  • 以下の AWS の機能を利用します。
    • Route53: ドメイン名取得、名前解決(DNS)
    • ACM(Amazon Certificate Manager): 証明書発行
    • ELB(Elastic Load Balancer): 本来は複数インスタンスをバランシングする用途に使うものですが今回は EC2 インスタンス 1台 をぶら下げ、ACM で取得した証明書を配布し外部との HTTPS通信 のために利用します。

注意と免責

  • 無料枠でない部分は料金が発生します。
  • データの正常な移行を保証するものではありません。

前提

  • 現環境が正常に動作していること。
  • 新環境を同一リージョン、同一VPC、同一サブネット内に新たにたてます。
    • インスタンス間のデータ転送を SCP で簡易に行いたいと思います。
    • 適宜セキュリティグループを解放してください。
  • aws cli version
% aws --version
aws-cli/1.11.47 Python/2.7.12 Darwin/16.6.0 botocore/1.5.10

段取り

  • 大まかに以下の順序で進めます
  1. Version 3.3.3 の AMI を使って EC2 インスタンスを起動
  2. Version 3.2.1 のデータバックアップ
    • Bitnami Redmine Stack の停止
    • MySQL Dump 取得
  3. Version 3.3.3 へのデータ復元
    • Bitnami Redmine Stack の停止
    • MySQL Dump 復元
    • Bitnami Redmine Stack の開始
  4. 動作確認

参考資料

作業手順

1. Newer Bitnami redmine stack インスタンス作成

以下の条件で作成します。

  • Common conditions

    • AMI: ami-15f98503
    • Type: t2.micro
    • Public IP: あり
  • User defined conditions
    • Region: N.Virginia
    • Subnet: subnet-bd809696
    • Security Group: sg-5b5b8f2a
    • Keypair: aws-n.virginia-default001
    • IAM Role: ec2-001
    • EBS: 20GB

WEB GUI から作るも良し、AWS CLI から作るも良し
以下コマンド実行例

set-env
KEY_NAME=aws-nvirginia-default001.pem
echo $KEY_NAME
check-ami
aws ec2 describe-images \
    --filters "Name=image-id,Values=ami-15f98503"
check-ami-name
aws ec2 describe-images \
    --filters "Name=image-id,Values=ami-15f98503" \
    | jq ".Images[].Name" \
    | grep --color redmine-3.3.3
create-instance
aws ec2 run-instances \
    --image-id ami-15f98503 \
    --count 1 \
    --instance-type t2.micro \
    --key-name aws-n.virginia-default001 \
    --security-group-ids sg-5b5b8f2a \
    --subnet-id subnet-bd809696 \
    --block-device-mappings "[{\"DeviceName\":\"/dev/sda1\",\"Ebs\":{\"VolumeSize\":20,\"DeleteOnTermination\":false}}]" \
    --iam-instance-profile Name=ec2-001 \
    --associate-public-ip-address
set-env
INSTANCE_ID=i-0f8d079eef9e5aeba
echo $INSTANCE_ID
add-name-tag-to-instance
aws ec2 create-tags --resources $INSTANCE_ID \
    --tags Key=Name,Value=redmine-3.3.3

注意書きにもありますが以下に表示される MySQL Database の root パスワードは初期起動時にしか表示されません。
このときに保管しておくか MySQL のお作法にしたがって変更しておいても良いでしょう

check-instance-created
aws ec2 describe-instances --filter "Name=instance-id,Values=$INSTANCE_ID"
wait-running-state
aws ec2 describe-instances \
    --filter "Name=instance-id,Values=$INSTANCE_ID" \
    | jq '.Reservations[].Instances[].State["Name"]'
get-redmine-password
aws ec2 get-console-output \
    --instance-id $INSTANCE_ID \
    | grep "Setting Bitnami application password to"
get-publicip
aws ec2 describe-instances \
    --filter "Name=instance-id,Values=$INSTANCE_ID" \
    | jq '.Reservations[].Instances[].NetworkInterfaces[].Association'
set-env
PUBLIC_IP=54.243.10.66
echo $PUBLIC_IP
site-check
curl -I http://$PUBLIC_IP/
HTTP/1.1 200 OK
(snip)
ssh-connect
ssh -i .ssh/$KEY_NAME bitnami@$PUBLIC_IP
first-login
bitnami@new-version-host:~$ sudo apt-get update -y
bitnami@new-version-host:~$ sudo apt-get upgrade -y
bitnami@new-version-host:~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.5 LTS"
bitnami@new-version-host:~$ sudo ./stack/ctlscript.sh status
subversion already running
php-fpm already running
apache already running
mysql already running
bitnami@new-version-host:~$ sudo ./stack/ctlscript.sh help
usage: ./stack/ctlscript.sh help
       ./stack/ctlscript.sh (start|stop|restart|status)
       ./stack/ctlscript.sh (start|stop|restart|status) mysql
       ./stack/ctlscript.sh (start|stop|restart|status) php-fpm
       ./stack/ctlscript.sh (start|stop|restart|status) apache
       ./stack/ctlscript.sh (start|stop|restart|status) subversion

help       - this screen
start      - start the service(s)
stop       - stop  the service(s)
restart    - restart or start the service(s)
status     - show the status of the service(s)

2. 旧バージョンのバックアップ取得

login_to_oldversion
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-74-generic x86_64)
       ___ _ _                   _
      | _ |_) |_ _ _  __ _ _ __ (_)
      | _ \ |  _| ' \/ _` | '  \| |
      |___/_|\__|_|_|\__,_|_|_|_|_|

  *** Welcome to the Bitnami Redmine 3.2.0-1         ***
  *** Bitnami Wiki:   https://wiki.bitnami.com/      ***
  *** Bitnami Forums: https://community.bitnami.com/ ***
Last login: Sun May 29 07:33:45 2016 from xxx.xxx.xxx.xxx
bitnami@old-version-host:~$
check-status
bitnami@old-version-host:~$ sudo stack/ctlscript.sh status
subversion already running
php-fpm already running
apache already running
mysql already running
stop
bitnami@old-version-host:~$ sudo stack/ctlscript.sh stop
/opt/bitnami/subversion/scripts/ctl.sh : subversion stopped
Syntax OK
/opt/bitnami/apache2/scripts/ctl.sh : httpd stopped
/opt/bitnami/php/scripts/ctl.sh : php-fpm stopped
/opt/bitnami/mysql/scripts/ctl.sh : mysql stopped
start-mysql
bitnami@old-version-host:~$ sudo stack/ctlscript.sh start mysql
170621 10:04:34 mysqld_safe Logging to '/opt/bitnami/mysql/data/mysqld.log'.
170621 10:04:34 mysqld_safe Starting mysqld.bin daemon with databases from /opt/bitnami/mysql/data
/opt/bitnami/mysql/scripts/ctl.sh : mysql  started at port 3306
check-available-filesystem-space
bitnami@old-version-host:~$ df -h /
Filesystem                                              Size  Used Avail Use% Mounted on
/dev/disk/by-uuid/6cdd25df-8610-4f60-9fed-ec03ed643ceb  9.8G  2.7G  6.6G  29% /
load-env-setting
bitnami@old-version-host:~$ . stack/use_redmine
bitnami@old-version-host:~$ echo $BITNAMI_ROOT
/opt/bitnami
dump-mysql
bitnami@old-version-host:~$ mysqldump -u root -p bitnami_redmine > redmine_backup.sql
Enter password:
bitnami@old-version-host:~$ ls -ltrh
  • scp 準備

手元の作業PCから新Redmine環境へssh接続するときに使用した証明書(pem)ファイルを旧Redmine環境にも作成します。

  • 今回は作業PC上で cat で表示させておいて旧環境のコンソール上にコピペしました。
example
bitnami@old-version-host:~$ vi .ssh/aws-nvirginia-default001.pem
bitnami@old-version-host:~$ chmod 600 .ssh/aws-nvirginia-default001.pem
  • ファイル転送
file_transfer
bitnami@old-version-host:~$ scp -i .ssh/aws-nvirginia-default001.pem redmine_backup.sql <new-version-host-ipaddr>:~
  • 新バージョン側にファイルが届いているか確認
check-transfered-files
bitnami@new-version-host:~$ ls -alh redmine*

3. 新バージョンへの復元

stop-stack
bitnami@new-version-host:~$ sudo stack/ctlscript.sh status
subversion already running
php-fpm already running
apache already running
mysql already running

bitnami@new-version-host:~$ sudo stack/ctlscript.sh stop

/opt/bitnami/subversion/scripts/ctl.sh : subversion stopped
Syntax OK
/opt/bitnami/apache2/scripts/ctl.sh : httpd stopped
/opt/bitnami/php/scripts/ctl.sh : php-fpm stopped
/opt/bitnami/mysql/scripts/ctl.sh : mysql stopped
start-mysql
bitnami@new-version-host:~$ sudo stack/ctlscript.sh start mysql
initial-database
bitnami@new-version-host:~$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.35 MySQL Community Server (GPL)

(snip)

mysql> drop database bitnami_redmine;

mysql> create database bitnami_redmine;

mysql> grant all privileges on bitnami_redmine.* to 'bn_redmine'@'localhost' identified by 'DATAB
ASE_PASSWORD';

mysql> quit
restore-dumpfile
bitnami@new-version-host:~$ mysql -u root -p bitnami_redmine < redmine_backup.sql
Enter password:
bitnami@new-version-host:~$
edit-line-18
bitnami@new-version-host:~$ vi /opt/bitnami/apps/redmine/htdocs/config/database.yml

    18    password: "DATABASE_PASSWORD"
db-migrate
bitnami@new-version-host:~$ cd /opt/bitnami/apps/redmine/htdocs/
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ ruby bin/rake db:migrate RAILS_ENV=production
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ ruby bin/rake tmp:cache:clear
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ ruby bin/rake tmp:sessions:clear
stack-restart
bitnami@new-version-host:/opt/bitnami/apps/redmine/htdocs$ cd
bitnami@new-version-host:~$ sudo stack/ctlscript.sh restart

bitnami@new-version-host:~$ exit
site-check
curl -I http://$PUBLIC_IP/
HTTP/1.1 200 OK
(snip)

ブラウザでも http://$PUBLIC_IP/ でアクセスして旧環境のユーザー名とパスワードでログイン出来ることを確認してください。

この時点で旧環境のインスタンスを停止させておいて良いでしょう。
いらないと判断した時に削除するなりしてください。

4. おまけ HTTPS化

  • 4-1. Route53 でドメイン名を取得してください

    • .net で 年額 11 USドル程度ですので実験用にひとつくらい維持しておくと便利
  • 4-2. Certificate Manager で証明書を取得します
    • コマンドでリクエストしてます
aws acm request-certificate --domain-name redmine.hogefuga.net
check-status-pending
aws acm describe-certificate \
    --certificate-arn "arn:aws:acm:us-east-1:942162428772:certificate/fdf099f9-ced7-4b97-a5dd-f85374d7d112" \
    | jq ".Certificate.Status"
"PENDING_VALIDATION"
  • 4-3. 承認する

    • ドメインに設定しているアドレスにメールが飛んできます
  • 4-4. ステータス確認
check-status-ISSUED
aws acm describe-certificate \
    --certificate-arn "arn:aws:acm:us-east-1:942162428772:certificate/fdf099f9-ced7-4b97-a5dd-f85374d7d112" \
    | jq ".Certificate.Status"
"ISSUED"
  • 4-5. Classic タイプの HTTPS ELB をつくる
example
aws elb create-load-balancer \
    --load-balancer-name redmine-elb1 \
    --listeners "Protocol=HTTPS,LoadBalancerPort=443,InstanceProtocol=HTTP,InstancePort=80,SSLCertificateId=arn:aws:acm:us-east-1:942162428772:certificate/fdf099f9-ced7-4b97-a5dd-f85374d7d112" \
    --availability-zones us-east-1a \
    --security-groups sg-3c90f343
  • 4-6. インスタンスをくっつける
example
aws elb register-instances-with-load-balancer \
    --load-balancer-name redmine-elb1 \
    --instances i-0f8d079eef9e5aeba
  • 4-7. State が InService になるまで待ちます
example
aws elb describe-instance-health \
    --load-balancer-name redmine-elb1
  • 4-8. DNS Name を確認する(4-10. で使います)
example
aws elb describe-load-balancers \
    --load-balancer-name redmine-elb1 \
  | jq ".LoadBalancerDescriptions[].DNSName"
  • 4-9. 今の設定を確認
example
aws route53 list-resource-record-sets \
    --hosted-zone-id Z3UG9LUEGNT0PE | jq .
  • 4-10. 投入用の JSON をつくる
example
vi change-resource-record-sets.json
example
{
  "Comment": "add CNAME for redmine.hogefuga.net",
  "Changes": [
    {
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "redmine.hogefuga.net",
        "Type":"CNAME",
        "TTL": 300,
        "ResourceRecords": [
          {
            "Value": <DNSName>
          }
        ]
      }
    }
  ]
}

4-11. 設定投入

example
aws route53 change-resource-record-sets \
    --hosted-zone-id Z3UG9LUEGNT0PE \
    --change-batch file://change-resource-record-sets.json

4-12. 設定確認

example
aws route53 list-resource-record-sets \
    --hosted-zone-id Z3UG9LUEGNT0PE

4-13. ブラウザ確認

https://redmine.hogefuga.net

4-14. EC2 インスタンスのセキュリティグループ再設定

  • グローバルの TCP:80 削除
  • サブネット内の TCP:80 許可
check
aws ec2 describe-security-groups --group-ids sg-b7d983c8
change
aws ec2 modify-instance-attribute \
    --instance-id i-0f8d079eef9e5aeba \
    --groups sg-b7d983c8

4-15. 再度ブラウザからアクセス可能か確認

続きを読む

AmazonECRとEC2を使って手元でビルドしたDockerイメージをAWS上でサクッと動かす

ECR(EC2 Container Registry)に登録したDockerイメージをEC2上でコンテナとして起動するまでの一通りの流れを書いてみた
ECSも一通り検証終わっていて、サービスではそちらを使う予定だが、基礎を振り返るという意味でのまとめ。

Docker

ここ一ヶ月ひたすらdockerを触っているが、やはり手元の開発環境で動いたものが、別の環境でそのまま動くというのは他にないメリット。
これまでだと、開発環境でOK→STでまた一から作る→本番でも同じくみたいなことしてたけど、ホスト側にDockerエンジン入れるだけで、実際のプロセスは開発環境のものをそのまま移植出来るというところはかなり熱い。といった印象。

やること

  • ECRを使う準備
  • ECRへのDockerイメージの登録
  • EC2作成
  • EC2上にECRからpullしたDockerコンテナを立てる

ECRとは

正式名称 EC2 Container Registry
Amazonが提供するフルマネージドのDockerコンテナレジストリ。
Dockerイメージを管理して、ECS(EC2 Container Service)やEB(Elastic Beanstalk)に簡単にデプロイすることが出来るソリューション

ECR使うと何がうれしい

  • EC2インスタンスにIAMroleを付与するだけ、EC2側で面倒な認証をせずにdockerイメージを使える
  • S3がバックエンドなので、可用性高い
  • 自動的に暗号化されたり、https通信されるのでセキュリティも安心

ECRを使う準備(ローカルマシンで実施)

AWS Command Line Interface のインストール を参考にAWS CLIを手元のマシンにインストールしておく。

基本的には、AWS CLIで操作する。

1.リポジトリの作成&確認

$aws ecr create-repository --repository-name tst-shnagai
{
    "repository": {
        "registryId": "xxxxx",
        "repositoryName": "tst-shnagai",
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:xxxxx:repository/tst-shnagai",
        "createdAt": 1496229520.0,
        "repositoryUri": "xxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai"
    }
}

リポジトリは、GUIから見ると、ECSサービスの中の[リポジトリ]に出来る

Amazon_EC2_Container_Service.png

2.ecrにログイン

セッションは12時間なので、感覚的に翌日には切れてる感じ。

## これで一発

$ $(aws ecr get-login --region ap-northeast-1)
Flag --email has been deprecated, will be removed in 17.06.
Login Succeeded

## $()の式展開を使わない場合

$ aws ecr get-login --region ap-northeast-1
docker login -u AWS -p eyJwYXlsb2FkIjoicURLTkxCTFhobUJuSTRxSDRNSUFBOEprc0txSnVuTVgrdzRzNkl4NU5rRDUxM0N...
### 標準出力の結果を貼り付けてログイン
$ docker login -u AWS -p eyJwYXlsb2FkIjoicURLTkxCTFhobUJuSTRxSDRNSUFBOEprc0txSnVuTVgrdzRzNkl4NU5rRDUxM0N...
Login Succeeded

ECRへのDockerイメージの登録(ローカルマシンで実施)

手元にある何かしらのDockerイメージをECRにpushする手順
手元で、Dockerイメージに対して、ECR用のタグづけを行ってから、ECRにpushする

1.docker tagコマンドでタグづけをする

今回は例として元々手元にある[apache_td]というdockerイメージに対して、ECRのルールに沿った名前でタグ付け(aliasつけるようなもの)する

## 元々のイメージ
$ docker image list |grep apache_td
apache_td                                                         latest              2c42dd3f5e5c        13 days ago         1.4GB

## タグ付けを実施
$ docker tag apache_td:latest  xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest

## imageIDは変わらないので、下記のような検索するとapache_tdがECRに対応したイメージとしてタグ付けされたことがわかる
$ docker image list |grep 2c42dd
xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai     latest              2c42dd3f5e5c        13 days ago         1.4GB
apache_td                                                         latest              2c42dd3f5e5c        13 days ago         1.4GB

2. 1でタグづけしたDockerImageをECRにpushする

$ docker push xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest
The push refers to a repository [xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai]
47d1cbb6b480: Layer already exists
...
latest: digest: sha256:14b7a5d491fa694c00f026bbc6c6cd09e0ddc63d0e586569a0de42a8ce7ec5d1 size: 2411

GUIで、タグ名とプッシュされた日時を確認して無事イメージがアップされていることを確認する

Amazon_EC2_Container_Service.png

ここまでで、ECRへのDockerイメージの登録は完了!!

EC2インスタンスの作成

1.通常通りEC2インスタンスを作成する(OSはデフォルトでawscliが入っているamazon linuxだと楽)

ポイントは、IAMRoleに[AmazonEC2ContainerRegistryReadOnly]ポリシを付与しておくことのみ

IAM_Management_Console.png

2. dockerのインストール

AWSの公式ドキュメントに沿ってやるだけなので、コマンドだけ羅列
Docker のインストール

ec2-userでdockerコマンドがsudoなしでうてるとこまでやっておく。

$ sudo yum update -y
$ sudo yum install -y docker
$ sudo service docker start
### ec2-userでsudoなしでdockerコマンドを打てるようにするため
$ sudo usermod -a -G docker ec2-user
###再ログイン
$ docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 17.03.1-ce

EC2上にECRからpullしたDockerコンテナを立てる(EC2上で実施)

1. ECRへのログイン

IAMRoleがついていない場合は、ログインで弾かれる

$ $(aws ecr get-login --region ap-northeast-1)
Login Succeeded

2. ECRからDockerイメージをpullする

## ECRにアップロードしたイメージをpull
$ docker pull xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest
latest: Pulling from tst-shnagai
996fe98f55d8: Pull complete
...
e6b377ddca6e: Pull complete
Digest: sha256:14b7a5d491fa694c00f026bbc6c6cd09e0ddc63d0e586569a0de42a8ce7ec5d1
Status: Downloaded newer image for xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest

## 手元のイメージとして登録されたことを確認
$ docker image ls
REPOSITORY                                                      TAG                 IMAGE ID            CREATED             SIZE
xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai   latest              2c42dd3f5e5c        13 days ago         1.4 GB

3. dockerコンテナを起動する

pullしてきたイメージからコンテナを起動する

## ホストの8080ポートにマッピングするtestという名前のコンテナを起動する
$ docker run -d --name test -p 8080:80 xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest
dbbb74b6ebe95666d356250de8310c19403078f53e020069e9a6d10e479b2873

## -lオプションで最後に起動したコンテナを表示
$ docker ps -l
CONTAINER ID        IMAGE                                                                  COMMAND                  CREATED             STATUS              PORTS                  NAMES
dbbb74b6ebe9        xxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/tst-shnagai:latest   "/bin/sh -c '/bin/..."   4 seconds ago       Up 4 seconds        0.0.0.0:8080->80/tcp   test

## 動作確認として、ホストの8080に対してcurlでリクエストしてみる
$ curl localhost:8080
version 1.2

まとめ

オーソドックスな、AWSでECRを使ってdockerコンテナを起動する一通りの流れをやってみた。dockerを手元で触ってる人だったら、特に躓くことなくやれる内容だと思う。
ECSは、基本オペレーション(この投稿でいうEC2以降の話)を抽象化して、クラスタというEC2集合体の上で、ELB,AutoScaling等を付加して使えるサービスなので、ココら辺をちゃんと理解してやるとやらないでは進みがだいぶ違うという印象を受ける。
裏で何が行われてるのかなという道理を理解することは大事。

続きを読む

EC2上にhubotを設置してslackと連携させる

最近ChatOpsに興味が出てきたので、とりあえずEC2上にhubotが動作する環境を作り、slackと連携出来るようにしました。

前提

以下の環境が既にあること。

  • EC2
  • slack

環境構築

  • nvm〜hubotの起動までは全てEC2上での作業です。

nvmのインストール

  • curlでインストールします
sudo curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
  • .bash_profileに以下を追記します
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
  • nvmコマンドが実行できることを確認します
nvm help

nodejsのインストール

  • nvmでnodejsのv6.11.0をインストールします
  • 公式サイトを見ると includes npm 3.10.10 と書いてあるのでnpmも一緒にインストールされます
nvm install 6.11.0
  • インストールされたことを確認します
node -v
npm -v

redisのインストール

  • elasticacheは使わずAmazon Linux上にredisをインストールします
  • epelだとバージョンが古いようなのでremiを利用します
sudo rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
sudo yum --enablerepo=remi install redis
  • redisを起動させます
sudo service redis start

hubotのインストール

  • hubotの環境を構築します
  • npmでhubot,coffescript,redis,yo,generator-hubotをインストールします
npm install -g hubot coffee-script redis yo generator-hubot
  • hubotをインストールします
mkdir -p ~/deploy-sushi
cd deploy-sushi
yo hubot
  • 対話式で色々聞かれるので自身の環境に合わせて入力してください
? ==========================================================================
We're constantly looking for ways to make yo better!
May we anonymously report usage statistics to improve the tool over time?
More info: https://github.com/yeoman/insight & http://yeoman.io
========================================================================== Y/no


省略


? Owner (User <user@example.com>)
? Bot name (deploy-sushi)
? Description (A simple helpful robot for your Company)
? Bot adapter (campfire)
  • 今回は?4つに以下のように答えました
? Owner {MY_EMAIL}
? Bot name deploy-sushi
? Description this is deploy boy
? Bot adapter slack
  • hubotを立ち上げて動作確認をします
bin/hubot
  • いろんなメッセージが出た後にEnterを押せば deploy-sushi> という対話が始まります
deploy-sushi> deploy-sushi ping
deploy-sushi> PONG
  • hubot-slackをnpmでインストールします
npm install hubot-slack

slackと連携させる

  • slack上の操作に変わります
  • slackのサイドメニューを開き Configure Apps を開きます

スクリーンショット 2017-06-17 14.12.32.png

  • 画面上部に Hubot を入力し、検索結果をクリックします

スクリーンショット 2017-06-17 14.16.11.png

  • Installボタンをクリックします

スクリーンショット 2017-06-17 14.17.17.png

  • hubotの名前を入力して Add Hubot Integrationをクリックします

スクリーンショット 2017-06-17 14.18.21.png

  • 作成が完了したらhubotの詳細画面に遷移するので画面上部の Setup Instructions のところに記載されている HUBOT_SLACK_TOKEN={YOUR_API_TOKEN} をコピーしておきます
  • 画像を変更する・・・など何か編集を加えた場合、画面下部の保存ボタンをクリックして更新してください

  • EC2に戻り以下の環境変数をexportします

echo 'export HUBOT_SLACK_TOKEN={YOUR_API_TOKEN}' >> ~/.bash_profile
source ~/.bash_profile
  • アダプタを指定してhubotを起動します
bin/hubot --adapter slack
  • slackのどこかのチャンネルにhubotをinviteして動作確認します

スクリーンショット 2017-06-17 14.29.14.png

  • 動作確認ができたらEC2起動時に自動でhubotが立ち上がるようにします

hubotの永続実行

  • foreverをnpmでインストールします
npm install -g forever
  • bin/hubot を書き換えます
以下を追記
forever start -w -c coffee node_modules/.bin/hubot --adapter slack


以下をコメントアウト
exec node_modules/.bin/hubot --name "deploy-sushi" "$@"

  • バックグラウンドで動くことを確認します
bin/hubot
ps aux | grep hubot

EC2起動時のスクリプト作成

sudo vi /etc/init.d/start_deploy_hubot
#!/bin/bash
#chkconfig: 2345 99 10
#descpriction: start deploy hubot
DIR="/home/ec2-user/deploy-sushi"
cd $DIR
bin/hubot
  • 実行権限を付与します
sudo chmod 755 /etc/init.d/start_deploy_hubot
  • サービスに追加します
sudo chkconfig --add start_deploy_hubot
  • EC2インスタンスを再起動してhubotが立ち上がっていることを確認してください

hubotが起動しなかった・・・

  • npm,foreverのパスがec2-userにしか通っていないかったのでrootでnpm,foreverするとコマンドが見つからない。
  • トークンもec2-userの .bash_profile に記述していたので見つからない。
  • とりいそぎ bin/hubot に以下を追記しました
    • npm install よりも前に追記してください
export PATH="/home/ec2-user/.nvm/versions/node/v6.11.0/bin:$PATH"
export HUBOT_SLACK_TOKEN={YOUR_API_TOKEN}
  • EC2を再起動して、hubotが立ち上がっていることを確認してください

    • プロセスの確認をします
ps aux | grep hubot
  • slackのチャンネルでもpingして返答があることを確認してください

おわり

hubotがサーバ上で動き続けるようになったので、次回はslackからgit操作を出来るようにしようと思います。

続きを読む

AWS API Gateway + Lambda Proxy で苦労した話

API GATEWAY PROXYって?

API GATEWAYから Lambdaなどにリンクする場合URIなどを具体的に設定しないといけないのだけど、 PROXYモードでまとめて全部Proxyされているサービスに投げることが可能なのだが。。。

例えば 
Link : https://<api ID>.execute-api.ap-southeast-2.amazonaws.com/<stage name>/`

API IDが abcdef, stage が development とすると。

Link : https://abcdef.execute-api.ap-southeast-2.amazonaws.com/development/

で、これに例えば IDとPhoneを追加したい場合、 普通は リソースに IDとPhoneを追加して

/{id}/{phone}
https://abcdef.execute-api.ap-southeast-2.amazonaws.com/development/1234/81-50-1234-1234

これを PROXYサービスに変えると

https://abcdef.execute-api.ap-southeast-2.amazonaws.com/development/user?id=1234&phone=81-50-1234-1234

すげーべんりじゃん?! ってのは、ふつーにだれでもわかるんだけど・・

テストでハマった

最初にCORSが面倒、LAMBDAにPROXYしてるんだけど、LAMBDAでも CORSの設定が必要とか 
下の例は、headers の アクセスコントロールとかを追加。

const returnvalue = {
                            statusCode: 200, 
                            headers: {
                                'Access-Control-Allow-Origin' : '*',
                                'Access-Control-Allow-Headers':'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token',
                                'Access-Control-Allow-Credentials' : true,
                                'Content-Type': 'application/json'
                            },
                            body: JSON.stringify(response) 
                    };
callback(null, returnvalue);

テストをしてみたけど えらーばかり リソースをヒットしていないのがテストが不可能な状態、 JqueryではHTTP OPTION, CORSではじかれ、CURLでも 403でおかしい。

なんで 403エラー?

パラメータのパスで問題だったのだが、なにせAWSのガイドを読んでもちんぷんかんぷん。ここでかなり無駄な時間。

jqueryで テストってことで ?ID=123  

$.ajax({ url: "https://abcdef.execute-api.ap-southeast-2.amazonaws.com/development/?id=123",
        type: "GET",
        dataType: 'json',
        crossDomain: true,
        contentType: 'application/json',
        success: function(data){
           console.log(data);
        }});

をなげてたのが403で弾かれまくり、 CURLでも同じく403、いろいろ検索したら リソースにヒットしてないのでは?ってことで、やっとわかったのが

https://abcdef.execute-api.ap-southeast-2.amazonaws.com/development/?id=1234

では だめで

https://abcdef.execute-api.ap-southeast-2.amazonaws.com/development/user?id=1234

USERってのが必要。。 (なんでやねんってかんじ)

これがちゃんと動くサンプル

$.ajax({ url: "https://abcdef.execute-api.ap-southeast-2.amazonaws.com/development/user?id="+id,
        type: "GET",
        dataType: 'json',
        crossDomain: true,
        contentType: 'application/json',
        success: function(data){
           console.log(data);
        }});

さいごに・・・

AWSさん・・ 文章をもっとちゃんとしてくれ。 なにもかんでも403ってなんだよ。

続きを読む

Amazon API Gateway にカスタムドメインを設定する

概要

Amazon API Gateway に独自ドメインを設定しアクセス可能にします。
SSL証明書は AWS Certificate Manager を利用します。

前提条件

この記事の内容を実践する為に必要な前提条件です。

  • 公開可能済のAPI Gatewayが存在している事
  • 利用可能なドメインを取得している事
  • AWS Certificate ManagerでSSL証明書が準備されている事

前提条件1 API Gatewayの準備

API Gatewayの準備ですが、Serverless Framework を使うと簡単に用意出来るのでオススメです。
以前、 Serverless Frameworkのインストールと初期設定 という記事を書きましたので参考になると幸いです。

前提条件2 利用可能なドメインを取得する

Route 53でドメインを購入すると全てがAWSのサービスで管理出来る事になるのでオススメです。
ドメインの購入方法等は下記の記事に載せてありますので参考にして頂けると幸いです。

前提条件3 AWS Certificate ManagerでSSL証明書を準備する

手順に関しては難しくありません、 AWSのサービスでドメインを取得しALBでSSLで接続出来るようにする に取得方法を記載してあります。

1点だけ注意点があります。それは証明書を配置する region は必ず us-east-1 米国東部(バージニア北部) にする事です。

2017-06-15 現在の時点では AWS Certificate Manager の証明書を利用する為には、証明書が us-east-1 米国東部(バージニア北部) に配置されている必要があります。

API Gatewayにカスタムドメインの設定を行う

マネジメントコンソール → Amazon API Gateway → +カスタムドメイン名の作成 より設定を行います。

以下の情報を入力します。

  • ドメイン名: 任意のドメイン名を入力します
  • ACM 証明書: 作成した証明書を選択します
  • ベースマッピング: 作成したどのAPI Gatewayにマッピングさせるかを選択します

custom-domain1.png

「保存」を押すと作成が開始されます。(かなり時間がかかります)

しばらくすると下記のような形になります。
この「ディストリビューションドメイン名」という箇所をコピーしましょう。

custom-domain2.png

ディストリビューションドメイン名をRoute 53に設定する

DNSレコードの設定を行っていきます。

私の場合はAPI Gatewayに設定したドメインは 元々持っていたドメインのサブドメインとして設定を行いました。

その為、元々のHosted zoneにAレコードとして登録を行います。

Name: 設定したドメイン名を入力
Alias Target: 先程コピーしたディストリビューションドメイン名

custom-domain3.png

このあたりの方法に関しては AWSのサービスでドメインを取得しALBでSSLで接続出来るようにする でも方法を紹介しています。

動作確認

最後に動作確認を行います。

自動で割り当てられるURLで接続
curl -v https://i99999999i.execute-api.ap-northeast-1.amazonaws.com/development/other
設定したカスタムドメインで接続
curl -v https://api-gateway.hoge.org/other

両方で同じ内容が返ってくれば成功になります。

以上になります。最後まで読んで頂きありがとうございました。

続きを読む

Run CommandとStep Functionsでサーバレス(?)にEC2のバッチを動かす

動機

cronやdigdag-serverは便利で使い勝手がいいですが、常時サーバを動作させておかないといけないのが難点です。一日一回の業務のために、インスタンスを立ち上げっぱなしにしたり冗長構成にするのはコスト的にやめたいなという場合もあると思います。
そこで本記事では、EC2 Run CommandとStep Functionsを使って、EC2インスタンス起動、Dockerを使ったバッチ起動、EC2シャットダウンまでの一連の処理の設定の仕方を説明していこうと思います。

処理実装

State Machine

State Machineの概要は下記になります。基本的には、処理開始→数秒待つ→状態を監視→状態がOKなら次へ進む、という構成になっています。

※ Run Command実行時にたまにエラー(InvalidInstanceId)になります。実行前のWaitの時間を長くするなど対策が必要かもしれないです。

スクリーンショット 2017-06-12 2.53.26.png

下記のJSONをStep Functionsへ登録します。

step-functions.json
{
  "Comment": "Invoke job",
  "StartAt": "StartInstance",
  "States": {
    "StartInstance": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:889476386829:function:qiita-sample-start-instance",
      "Next": "WaitInstanceState"
    },
    "WaitInstanceState": {
      "Type": "Wait",
      "Seconds": 60,
      "Next": "ConfirmInstanceState"
    },
    "ConfirmInstanceState": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:889476386829:function:qiita-sample-confirm-instance-state",
      "Next": "ChoiceInstanceState"
    },
    "ChoiceInstanceState": {
      "Type": "Choice",
      "Default": "FailInstanceState",
      "Choices": [
        {
          "Variable": "$.instance_state",
          "StringEquals": "pending",
          "Next": "WaitInstanceState"
        },
        {
          "Variable": "$.instance_state",
          "StringEquals": "running",
          "Next": "StartJob"
        }
      ]
    },
    "FailInstanceState": {
      "Type": "Fail",
      "Cause": "Failed to launch instance"
    },
    "StartJob": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:889476386829:function:qiita-sample-start-job",
      "Retry": [
        {
          "ErrorEquals": [
            "States.TaskFailed"
          ],
          "IntervalSeconds": 30,
          "MaxAttempts": 3,
          "BackoffRate": 2
        }
      ],
      "Next": "WaitJob"
    },
    "WaitJob": {
      "Type": "Wait",
      "Seconds": 10,
      "Next": "ConfirmJobStatus"
    },
    "ConfirmJobStatus": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:889476386829:function:qiita-sample-confirm-job-status",
      "Next": "ChoiceJobStatus"
    },
    "ChoiceJobStatus": {
      "Type": "Choice",
      "Default": "WaitJob",
      "Choices": [
        {
          "Or": [
            {
              "Variable": "$.job_status",
              "StringEquals": "Failed"
            },
            {
              "Variable": "$.job_status",
              "StringEquals": "TimedOut"
            },
            {
              "Variable": "$.job_status",
              "StringEquals": "Cancelled"
            },
            {
              "Variable": "$.job_status",
              "StringEquals": "Success"
            }
          ],
          "Next": "TerminateInstance"
        }
      ]
    },
    "TerminateInstance": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:us-east-1:889476386829:function:qiita-sample-terminate-instance",
      "End": true
    }
  }
}

Lambda関数

前準備

Lambda関数へ割り当てられているrole(大抵の場合はlambda-default-roleという名前になっている)へ、下記の権限を割り振ってください。

  • ec2:RunInstances
  • iam:PassRole
  • ec2:DescribeInstances
  • ssm:SendCommand
  • ec2:TerminateInstances
  • states:StartExecution

また、同じroleへ下記のポリシーもアタッチしてください。

  • AmazonSSMReadOnlyAccess

さらに、EC2インスタンスにアタッチするIAM roleを作成し、下記ポリシーをアタッチしておいてください。(本記事ではssm-roleと名前をつけます)

  • AmazonSSMFullAccess

EC2インスタンスを起動する

EC2インスタンスを起動します。取得したインスタンスIDを次のLambdaへ渡します。

start_instance.py
import boto3
import base64


def lambda_handler(event, context):
    client = boto3.client('ec2', region_name='us-east-1')
    resp = client.run_instances(
        ImageId='ami-898b1a9f',
        MinCount=1,
        MaxCount=1,
        KeyName='my-key',
        SecurityGroups=['default'],
        UserData=_make_user_data(),
        InstanceType='m3.medium',
        IamInstanceProfile={
            'Name': 'ssm-role',
        },
    )

    event['instance_id'] = resp['Instances'][0]['InstanceId']

    return event

def _make_user_data():
    with open('user-data.txt', 'rb') as f:
        return base64.b64encode(f.read())
user-data.txt
#!/bin/bash -xe

cd /tmp
curl https://amazon-ssm-us-east-1.s3.amazonaws.com/latest/linux_amd64/amazon-ssm-agent.rpm -o amazon-ssm-agent.rpm
yum install -y amazon-ssm-agent.rpm

EC2インスタンスの起動状況を確認する

confirm_instance_state.py
import boto3


def lambda_handler(event, context):
    ec2 = boto3.resource('ec2', region_name='us-east-1')
    instance = ec2.Instance(event['instance_id'])

    event['instance_state'] = instance.state['Name']

    return event

バッチを起動する

EC2 Run CommandでEC2インスタンスへコマンドを発行していきます。本記事では説明のためにpublicなrepositoryからイメージをpullしていますが、本番利用ではprivate repositoryからイメージを利用することが多いと思うので、そのための処理も入れています。
また、dockerの起動オプションで、標準出力をCloudWatch Logsへログ転送するように設定すると便利です(あらかじめLog Groupを作っておく必要があると思います)。

start_job.py
import boto3


def lambda_handler(event, context):
    client = boto3.client('ssm', region_name='us-east-1')

    command = "docker run --log-driver=awslogs" \
              " --log-opt awslogs-group=hello-world" \
              " --log-opt awslogs-region=us-east-1 --rm" \
              " -i alpine:latest /bin/echo 'hello, world'"

    resp = client.send_command(
        InstanceIds=[event['instance_id']],
        DocumentName='AWS-RunShellScript',
        MaxConcurrency='1',
        Parameters={
            'commands': [
                "yum install -y docker",
                "service docker start",
                "eval $(aws ecr get-login --no-include-email --region us-east-1)",
                command,
            ],
        },
        TimeoutSeconds=3600,
    )

    event['command_id'] = resp['Command']['CommandId']

    return event

バッチ処理の状態を確認する

confirm_job_status.py
import boto3


def lambda_handler(event, context):
    client = boto3.client('ssm', region_name='us-east-1')

    resp = client.get_command_invocation(
        CommandId=event['command_id'],
        InstanceId=event['instance_id'],
    )

    event['job_status'] = resp['Status']

    return event

EC2インスタンスをシャットダウンする

terminate_instance.py
import boto3


def lambda_handler(event, context):
    ec2 = boto3.resource('ec2', region_name='us-east-1')
    instance = ec2.Instance(event['instance_id'])
    resp = instance.terminate()

    return event

Step Functionsを呼び出す

下記Lambda関数を定期実行するよう設定します。State Machine名は変わりやすいので環境変数にしておくと便利です。

invoke-step-functions.py
import boto3
import uuid
import os


def lambda_handler(event, context):
    account_id = event.get('account')

    state_machine_arn = 'arn:aws:states:us-east-1:%s:stateMachine:%s'\
                        % (account_id, os.environ.get("STATE_MACHINE_NAME"))
    client = boto3.client('stepfunctions', region_name='us-east-1')
    resp = client.start_execution(
        stateMachineArn=state_machine_arn,
        name=str(uuid.uuid4()),
    )

    return 'invoked step functions'

まとめ

本記事では、EC2 Run CommandとStep Functionsを使って定期的にバッチ処理を起動するやり方を説明しました。今回は簡単な実装にしましたが、スポットインスタンスの価格を調べて10%上乗せして落札できたらそちらを利用するなど、工夫すれば複雑な処理も可能です。
従来のPaaSサービスでは待ち時間もCPU課金されますが、Step Functionsは状態遷移回数での課金ですので新たな使い方ができると思います。

追記

Githubへ公開しました
https://github.com/runtakun/qiita-sample-step-functions

続きを読む

Visual Studio Codeでリモートマシン上のNode.jsファイルを直接操作(編集、デバッグ実行)する

実現すること

Visual Studio Codeでリモートマシン上のNode.jsファイルを直接操作(編集、デバッグ実行)します

    debug.png
    ローカルマシン(Windows)でリモートマシン(Linux)のNode.jsファイルを直接操作

対象者

  • Visual Studio Codeを使ってNode.jsの開発をしている人
  • LinuxマシンのファイルをViとかEmacsで編集するが煩わしいと思っている人
  • ローカルで開発したコードをいちいちリモートのLinuxサーバに転送するのが面倒な人
  • リモートのLinuxサーバのソースを直接デバッグ実行したい人

検証環境

  • ローカルマシン

    • Windows 10
    • Visual Studio Code 1.13.0
  • リモートマシン

    • AWS EC2 (Amazon Linux AMI 2017.03.0.20170417 x86_64 HVM)

リモートマシン側の準備

Node.js

インストール

$ sudo su
# curl -sL https://rpm.nodesource.com/setup_8.x | bash -
# yum install -y gcc-c++ make
# yum install -y nodejs

動作確認

# curl -sL https://deb.nodesource.com/test | bash -
# npm --v
# node -v

参考URL

リンク:https://github.com/nodesource/distributions#enterprise-linux-based-distributions

jmate

インストール

# npm -g install jmate

参考URL

リンク:https://github.com/jrnewell/jmate

SSH

セキュリティグループの設定でインバウンド、アウトバウンドともに52698ポートを許可します。

  • タイプ:カスタムTCPルール
  • プロトコル:TCP
  • ポート範囲:52698
  • ソース:許可したいIPアドレス

クライアントマシン側の準備

Visual Studio Code, Node.js

Visual Studio Code, Node.jsをインストールしておきます。

Windows用のSSHクライアントのインストール

Windowsで実行可能はSSHクライアント(ssh.exe)をインストールしPATHを通します。
SSHクライアントは、 git for windows などに含まれています。

SSH

キーペア

インスタンス生成時に作成したキーペア(*.pemファイル)を ~.ssh に保存します。
ここでは、my-ec2.pem というファイル名で保存したものとして以降説明します。

configファイル

毎回長いsshコマンドをうたなくても大丈夫なようにconfigファイルに設定を書いておきます。

~.sshconfig
ServerAliveInterval 60

Host my-ec2
    HostName ※EC2のIPアドレス
    User ec2-user
    IdentityFile ~/.ssh/my-ec2.pem
    RemoteForward 52698 127.0.0.1:52698

テスト

sshコマンドでサーバにアクセスできることを確認します。

> ssh my-ec2

自動的にログインできればSSHの設定完了です。

Remote VSCode

インストール

Visual Studio Code > EXTENSIONS (Ctrl+Shift+X) > Remote VSCode > Install

settings.jsonの設定

Visual Studio Code > File > Preferences > Settings (Ctrl+Comma)

User Settingsに以下を追記

  //-------- Remote VSCode configuration --------

    // Port number to use for connection.
    "remote.port": 52698,

    // Launch the server on start up.
    "remote.onstartup": true

"remote.onstartup": false の設定にした場合は、F1でcommand palleteを表示し、Remote: Start server でRemote VSCodeのサーバーを起動します。

ローカルのVisual Studio Codeでリモートのファイルを操作

  1. Visual Studio Codeを起動します。
  2. Visual Studio CodeのTerminalでサーバにSSH接続します。

    > ssh my-ec2
    
  3. ファイルオープン
    Visual Studio Codeでリモートのファイルを開きます。

    $ jmate -p 52698 ファイル名.js
    
  4. 編集とデバッグ実行
    4.1. ファイルを編集します。
    4.2. ソースコードにブレークポイントをはります。
    4.3. デバッグウィンドウを開きます。(Ctrl+Shift+D)
    4.4. デバッグ実行します。(F5)

 これで、ファイルの編集、ステップ実行、変数の参照、ウォッチ式の設定、コールスタックの参照、ブレークポイントの設定がローカルファイルと同様に動作するようになりました。

続きを読む

Amazon Linux(2017.03)にdocker-composeでZabbix3.2をインストールするまで

Zabbixサーバーのインストールと起動

Zabbixは公式でdocker-composeの定義を用意してくれていますので、とっても簡単に起動までできます。

Amazon Linuxを立ち上げてSSHで接続したところから、下記のコマンドで起動できます。

sudo yum update -y
sudo yum install -y docker git

# Docker起動
sudo service docker start

# docker-composeのインストール
curl -L https://github.com/docker/compose/releases/download/1.13.0/docker-compose-`uname -s`-`uname -m` > /tmp/docker-compose
chmod 755 /tmp/docker-compose
sudo mv /tmp/docker-compose /usr/local/bin/

# zabbixのdocker-compose定義を取得
git clone --depth=1 https://github.com/zabbix/zabbix-docker.git

# zabbix起動
cd zabbix-docker
sudo /usr/local/bin/docker-compose -f docker-compose_v2_alpine_mysql_latest.yaml up -d

Webにアクセスするとログイン画面が表示されます。
初期状態では Admin/zabbix でログインできます。

初期状態でzabbix-server自体がホストとして登録されていますが、接続に失敗している状態になっています。
IPが127.0.0.1に設定されていますが、Dockerコンテナ内なのでDockerコンテナ内から接続可能なIPである必要があります。
sudo docker psなどでコンテナ名を確認し、コンテナ名をドメインとして登録することで接続できます。(Dockerネットワーク内はコンテナ名でIPを引けるため)

zabbix-agentのインストールと起動

RPMはRedHat7ではなく6用のものを使う必要があります。
7用のものを使うとsystemdが必要と言われてエラーになります。

インストールしてすぐに起動するとバインドアドレスが0.0.0.0/0になっているので油断しますが、設定ファイル(/etc/zabbix/zabbix_agentd.conf)を編集しないとローカルホスト以外からのアクセスはできないので注意が必要です。

sudo rpm -ivh http://repo.zabbix.com/zabbix/3.2/rhel/6/x86_64/zabbix-release-3.2-1.el6.noarch.rpm
sudo yum install zabbix-agent

# Server=127.0.0.1 の部分をZabbixサーバーのIPに変更する
sudo vim /etc/zabbix/zabbix_agentd.conf

sudo service zabbix-agent start

セキュリティグループ

下記のポートを開けるのを忘れないこと。
サーバー: 10051
エージェント: 10050

続きを読む

nginxで同一ホスト内にリバースプロキシしようとするとエラーが出る

nginxで同一ホスト内にリバースプロキシしようとするとエラーが出る

環境

  • Amazon EC2
  • Centos7
  • nginx/1.12.0 (80port)
  • puma (3000port)

状況

例えば、/etc/nginx/conf.d/default.confに以下のような設定をした時、

/etc/nginx/conf.d/default.conf
server {
    listen       80;
    server_name  localhost;

   location / {
    proxy_pass http://localhost:3000/;
    }
}

ブラウザで接続してもつながらない。3000ポート直打ちだとアクセスできる。また、なぜか別ホストに変えてみるとプロキシできる。  
curlでレスポンスを確認してみる。

$ curl -L -I xxx.com
HTTP/1.1 502 Bad Gateway
Server: nginx/1.12.0
Date: Thu, 01 Jun 2017 08:34:06 GMT
Content-Type: text/html
Content-Length: 537
Connection: keep-alive
ETag: "xxxxxxxxxx-xxx"

502が出ている。
また、nginxのログを確認すると、

/var/log/nginx/error.log
2017/06/01 08:38:16 [crit] 27144#27144: *1 connect() to 127.0.0.1:3000 failed (13: Permission denied) while connecting to upstream, client: xxx.xxx.xxx.xxx, server: localhost, request: "HEAD / HTTP/1.1", upstream: "http://127.0.0.1:3000/", host: xxx.com"
2017/06/01 08:38:16 [warn] 27144#27144: *1 upstream server temporarily disabled while connecting to upstream, client: xxx.xxx.xxx.xxx, server: localhost, request: "HEAD / HTTP/1.1", upstream: "http://127.0.0.1:3000/", host: "xxx.com"
2017/06/01 08:38:16 [crit] 27144#27144: *1 connect() to [::1]:3000 failed (13: Permission denied) while connecting to upstream, client: xxx.xxx.xxx.xxx, server: localhost, request: "HEAD / HTTP/1.1", upstream: "http://[::1]:3000/", host: "xxx.com"
2017/06/01 08:38:16 [warn] 27144#27144: *1 upstream server temporarily disabled while connecting to upstream, client: xxx.xxx.xxx.xxx, server: localhost, request: "HEAD / HTTP/1.1", upstream: "http://[::1]:3000/", host: "xxx.com"

どうやら権限の関係でうまくプロキシ出来ていないよう。しかしnginxの実行ユーザをrootにしてみても解消されない。

解決策

どうやらSELinuxが効いているせいらしい。EC2では初期は無効だった気がするが有効になっていたみたい。この辺はディストリビューションによって違うのかも?

httpd_can_network_connectを許可してやることで接続できるようになるはず。

$ sudo setsebool httpd_can_network_connect on -P

確認してみます。

$ curl -L -I xxx.com
HTTP/1.1 200 OK
Server: nginx/1.12.0
Date: Thu, 01 Jun 2017 08:40:36 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
~~~~~~~~~~~~~~~~~~~~

接続できました。

結論

私はSELinuxを切って解決しました。

続きを読む