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等を付加して使えるサービスなので、ココら辺をちゃんと理解してやるとやらないでは進みがだいぶ違うという印象を受ける。
裏で何が行われてるのかなという道理を理解することは大事。

続きを読む

AWS ECSを使用する前にDockerを理解しなきゃ

◎はじめに

・Amazon EC2 Container Service (ECS)ってなんじゃらほい?
というわけでざっくり調べてみたところ、Dockerコンテナを管理するものとのこと。
『えっ、、Dockerってクジラさんのアイコンのヤツでしょ。VMの一つの。。』
とまたまたITリテラシーの低さを露呈した私、
ECSを触る前にまずDockerを理解する必要が出てきました。

1. Dockerとは

docker.png

Wikipediaより

Docker(ドッカー)はソフトウェアコンテナ内のアプリケーションのデプロイメントを自動化するオープンソースソフトウェアである。
Linuxカーネルにおける「libcontainer」と呼ばれるLinuxコンテナ技術とaufsのような特殊なファイルシステムを利用してコンテナ型の仮想化を行う。

はい。まだよくわかりません。。コンテナってなんなのさ。。。

1-1. コンテナとは

■目的
・デプロイ作業を簡易にする
→ 開発環境をそのまま本番環境として利用できる
→ アプリケーション開発環境(ライブラリ、環境変数など)をまるごとパッケージングし本番環境にデプロイすることで環境に依存することがなくなる。

■仮想化との差異

タイプ    概要
仮想化 複数のオペレーティングシステムが単一のシステム(ゲストOS)で同時に実行できるようになる
コンテナ 同じオペレーティング・システム・カーネルを共有し、アプリケーションプロセスをシステムの他の部分から独立させる

▽補足
・Hypervisor上に複数のOSを実行する仮想化は負荷が大きい。
一方、コンテナはホストOS、ライブラリを共有している。
そのため少ディスクかつ少メモリでビルドもデプロイも高速、オーバーヘッドも少ない。
それでいて仮想化のためプラットフォームやハードウェアからは隔離された環境となっている。

・1コンテナ=1プロセス

▽参考サイト:
今からでも間に合うDockerの基礎。コンテナとは何か、Dockerfileとは何か。Docker Meetup Tokyo #2

1-2. 取り敢えず触ってみよう

■参考サイト
・公式サイト
https://docs.docker.com/

・Docker ドキュメント日本語化プロジェクト
http://docs.docker.jp/index.html

■環境
・最新のAmazon linux AMIからlaunchしたインスタンスに導入してみました。

[ec2-user@mitzi_dev02 ~]$ uname -r
4.9.27-14.31.amzn1.x86_64

■余談
・amzn linuxへのインストール方法が公式サイト上で見つからなかったため、CentOSの手順でやってみたところ
ライブラリが足りないなどのエラーが多発し撃沈。。そこで上述の日本語化されたサイトを見たところAWS公式DocumentationのECSの項にDockerの説明・導入方法の記載がありました。親切ね

Docker の基本

■インストールそしてHello World

・パッケージとパッケージキャッシュを更新
$ sudo yum update -y

・Dockerをインストール
$ sudo yum install -y docker

・Dockerサービスを開始する
$ sudo service docker start
Starting cgconfig service:                                 [  OK  ]
Starting docker:  .                                  [  OK  ]

・ Dokerの環境情報表示でコマンド実行できることを確認
$ sudo docker info
・Hell world コンテナを実行

[ec2-user@mitzi_dev02 ~]$ docker run ubuntu:14.04 /bin/echo 'Hello world'

docker: Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post http://%2Fvar%2Frun%2Fdocker.sock/v1.27/containers/create: dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.
[ec2-user@mitzi_dev02 ~]$ sudo docker run ubuntu:14.04 /bin/echo 'Hello world'
Unable to find image 'ubuntu:14.04' locally
14.04: Pulling from library/ubuntu
1d8592394ba1: Pull complete
01aa7f61ccd1: Pull complete
5dd2552a960e: Pull complete
7cbe941c5e3e: Pull complete
2549ecfb14c6: Pull complete
Digest: sha256:30204139c6ab96ebd75d72f34db390f28c4decd5e563488b4e485bf979397b67
Status: Downloaded newer image for ubuntu:14.04
Hello world

公式サイトより、ここでやっていることの説明を引用

  • docker run でコンテナを実行。
  • ubuntu は実行するイメージです。この例では Ubuntu オペレーティング・システムのイメージです。イメージを指定したら、Docker はまずホスト上にイメージがあるかどうか確認します。イメージがローカルに存在していなければ、パブリック・イメージ・レジストリである Docker Hub からイメージを取得(pull)します。
  • /bin/echo は新しいコンテナ内で実行するコマンドです。

コンテナを起動するとは、Docker が新しい Ubuntu 環境を作成し、その中で /bin/echo コマンドを実行し、その結果を出力します。

なんとなくイメージが湧いてきました。
ここでは簡単なコマンドを実行しただけですが、これを応用していくことで前述の目的を達成するんだろうな。という何となくなイメージが

とりあえず今回はココまでになります。
引き続きDockerを触ってゆき、その先にあるECSを使いこなせるようにしていきたく思います。

続きを読む

略称がややこしいサービス in AWS

随時更新予定

改めて、Elasticって何?

AWSを使ってるとやたらと目にするので改めてElasticの意味を

  • 形容詞

    1. 弾力のある、しなやかな
    2. 〈人・感情など〉不幸があってもすぐ立ち直る,容易に屈しない; 屈託のない.
    3. 融通性のある
  • 名詞
    1. ゴムひも,ゴム入り生地.
    2. 可算名詞 《主に米国で用いられる》 輪ゴム.

Weblioより

恐らくAWSとしては「融通性のある」という意味合いで使ってるっぽい

EC2とECSとECR

EC2

  • 正式名: Elastic Compute Cloud
  • 何の変哲もない仮想サーバ
  • サーバレス界隈では「EC2を使っていいのは小学生まで」と言われているらしい

ECS

  • 正式名: EC2 Container Service
  • 略称であるEC2の頭文字をとるという荒業
  • Dockerをよしなに扱えるEC2と思えばだいたい合ってる

ECR

  • 正式名: EC2 Container Registry
  • AWS内に作れるプライベートなDockerイメージリポジトリ
  • 位置づけ的にはECSの諸々の機能の中にECRが存在する
  • なのでAWSコンソールのサービス名検索では現れない

EBとEBS

EB

  • 正式名: Elastic Beanstalk
  • EC2やロードバランサをまとめて管理するツール
  • 設定ファイルを使いまわせばすぐに環境を構築できる
  • あまり使われてないっぽいけど意外と便利

EBS

  • 正式名: Elastic Block Store
  • EC2にマウントするストレージボリューム
  • 後から拡張しようとすると地味にめんどくさい

RDSとRDB

RDS

  • 正式名称: Relational Database Service
  • (オンプレよりは)運用しやすいデータベース
  • とはいえフルマネージドではないのでサーバレス界隈では嫌われている
    • Lambdaからつなぎに行くと同時接続数制限で死ぬのが不評

RDB

  • 正式名称: Relational DataBase
  • サービスでなく一般名詞
  • RDSと字面も意味もダダ被りなので混ざりやすい
    • しかも言い間違えたりしても問題ない場面が多かったりする

SQS、SNS、SES、SMS

SQS

  • 正式名称: Simple Queue Service
  • キューを放り込むためのもの
  • 複数サービス連携しつつ、処理が確実に行われる保証が欲しいってときにお世話になる

SNS

  • 正式名称: Simple Notification Service
  • なんらかの方法で通知を送るためのサービス
  • 「SNS」だけだとSocial Networking Serviceのことだと誤解されるかもしれないので注意
  • 「SNSの機能でSMSを使える」とだけ言うとまるでfacebookからメッセージを送れるという話のようだ
    • 翻訳すると「Simple Notification Serviceの機能で携帯電話にショートメッセージを送れる」

SES

  • 正式名称: Simple Email Service
  • メールの送受信サービス
  • 初期状態ではサンドボックスに閉じられてるので、サポートに「がっつり使わせて」という申請が必要です。

SMS

  • 正式名称: Server Migration Service
  • オンプレのサーバをAWSに移行するサービス
  • ここでもう一度「SNSの機能でSMSを使える」というフレーズを思い出してほしい
    • もはや何を言っているかわからない

最近の動向

サービスの種類が増えてきたからか最近は略称を使わない方向に行ってるっぽい
とはいえ、略称を使うことは多いのでちゃんと相手と文脈を共有したうえで適宜補足をしましょう

続きを読む

Mastodonインスタンス構築(鯖:AWS EC2、ドメイン:お名前.com、SSL:Let’s Encrypt)

まずは

QiitaはROM専(死語?)だったので初投稿。
こちらの記事に勇気付けられた。

「僕が書いたことはみんな書いている、ハマっていることは共有しなくてもいい」という考えも浮かぶと思うが…
・情報鮮度の観点で出す価値あり

http://qiita.com/hinom77/items/dfa9e0c734e47271edb7

たしかにググって記事がたくさん出てくると学んでて損はない技術なんだろうと思えてくる。
ビッグウェーブに乗りたいというのもある。
というわけで何番煎じかわからないがマストドンのインスタンス構築記録を書く。
見せ方、引用の仕方など作法があればご容赦。
Qiitaのマークダウンすらままならず…。

基本的な軸

基本的には↓↓を参考にさせていただきました。
私の環境で違ったところだけ横道に反れたりしながら追記してます。
基本は参考URLを見ていただき、たまにこっちに戻ってくるという感じがよいかと。
※以降【】でかこっている中項目は下記参考先の中項目タイトルに準じています。

■マストドンAWS構築チュートリアル完全版|初心者から大規模運用まで 5.お手軽な手順
http://webfood.info/mastodon-aws-tutorial/#section-5

【EC2インスタンスの作成】

インスタンススペックはt2.microを選択。
無料枠で選択できたので。

【Route53でHosted Zoneを作る】

丸々飛ばし。
DNSはお名前.comに任せる。

AWS EC2のインスタンスに固定グローバルIPを付与

AWSでは固定グローバルIP=Elastic IPと呼ばれている。
↓↓に書かれている通りに沿って進める。

■AWS EC2インスタンスにElastic IP(固定グローバルIPアドレス)を割り当てる
https://ac-5.net/aws/aws_elasticip_allocation

AWSのElastic IPを独自ドメインと関連付ける

DNSの設定。関連付ける、という言葉が正しいのかどうか。

■(お名前.com)ネームサーバーのAレコード設定
http://rensrv.com/domain/onamae-com/a_record-setting-onamae-com/

【SSHでログインする】

そのまま。

【Let’s EncryptでSSL証明書を取得する】

$ ./certbot-autoの箇所で途中、エラーで正常終了しなかった。

Requesting root privileges to run certbot...
/home/ubuntu/.local/share/letsencrypt/bin/letsencrypt
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Failed to find executable apache2ctl in PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
Certbot doesn't know how to automatically configure the web server on this system. However, it can still get a certificate for you. Please run "certbot-auto certonly" to do so. You'll need to manually configure your web server to use the resulting certificate.

apache2の起動コマンド(?)が見つからないようだ。
そもそもデフォルトでインストールされていないぽい。
↓↓
●対応:apache2インストール→再度実行

$ sudo apt-get update
$ sudo apt-get install apache2
$ ./certbot-auto

結果としてこれで成功したが、完了までにいろいろ聞かれたので参考までに記載。

●ドメイン名を入力してくれー
No names were found in your configuration files. Please enter in your domain
name(s) (comma and/or space separated) (Enter ‘c’ to cancel):

自前のドメイン名を入力して、エンター。

●HTTPSのみの接続にする?
Please choose whether HTTPS access is required or optional.
——————————————————————————-
1: Easy – Allow both HTTP and HTTPS access to these sites
2: Secure – Make all requests redirect to secure HTTPS access
——————————————————————————-
Select the appropriate number [1-2] then enter:

2の方が無難かと考え、2を入力してエンター。

●成功の確認
——————————————————————————-
Congratulations! You have successfully enabled https://(設定したドメイン)

You should test your configuration at:
https://www.ssllabs.com/ssltest/analyze.html?d=(設定したドメイン)
——————————————————————————-

一応言われている通りにアクセスしたら、A評価(多少時間かかる)。

【ミドルウェアの設定】

t2.microなのでスワップ設定は飛ばし。

と思ったら後ほど出てくるdocker-composeでひっかかった。
処理が途中で止まってしまうのだが、どうやらメモリ不足が原因らしい…。

$docker-compose run --rm web rails assets:precompile

http://uyamazak.hatenablog.com/entry/2017/05/22/151210

たしかにスワップの設定後、再トライしたら処理が完了できた。

あと、念のためnginxの編集前の設定ファイルをコピーして残しておく。

cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.ort

【マストドンのセットアップ】

ここでだいぶ時間を食った…。

Docker動いてない?

ERROR: Couldn’t connect to Docker daemon at http+docker://localunixsocket – is it running?

●いろいろ対応:
・鯖再起動←たぶん関係ない。
・pip3のupgrade←アップグレードは成功したがたぶん関係ない。
 ただ、一応やったことなので残しておく。

$sudo pip3 install --upgrade pip

・インストール済かの確認

$pip3 list
docker-compose (1.13.0)

結局よくわからず↓↓を参考に入れ直す。

■今何かと話題のマストドン(mastodon)鯖を自分用に無料で立てる方法
【必要なものをインストールする】のセクション
http://jtwp470.hatenablog.jp/entry/2017/04/15/174036

その後、再度実行→成功!

$ sudo docker-compose build

以降、

$ sudo docker-compose run --rm web rake secret

から続行。
以降、assetsのdocker-composeで躓くも何とかAbout画面表示までこぎ着けた…。
まずは一旦の達成感。

【メールの設定】

今回はとりあえず自分のテスト用鯖なのでSESの制限は解除しない。
他はそのまま。

【cronの設定】

今回参照させていただいているところはマストドンのインストールディレクトリを指定しているのでcron内のパスを修正。
/home/ubuntu/mastodon→/home/ubuntu/live
他はそのまま。

ログインと管理画面

最後に、今回はとりあえず個人用インスタンスということにしているのでユーザ認証は手動で実行。
ということで↓↓を参考にさせていただきました。

■今何かと話題のマストドン(mastodon)鯖を自分用に無料で立てる方法
【初期登録と管理画面をだす方法】【管理画面の出し方】
http://jtwp470.hatenablog.jp/entry/2017/04/15/174036

なぜか手動認証のコマンドを打っても自前のgmailアカウントが見つからないと言われたので、適当な捨てアドを作り、登録し、管理画面から認証するという手順を踏みました。

さいごに

最初は探り探りだったので時間としては朝から晩までかかりました。
2回目はこの手順を残しておいたので2時間強くらいでできました。
1回目構築終わったあと、疲れなのか何なのかAWSのインスタンを消去してしまい、朝起きたときは絶望しましたが逆にこの手順が間違ってなかったことを自分で証明できてよかったよかった(?)
インスタンス構築にあたり最近の技術も勉強できたのでそれもよかった。
Dockerのことがまだはっきりと理解しきれていないので引き続き勉強ですね。

最後になりましたが、参考にさせていただいた先人たちには多大なる感謝を。
またこの記事がこれからの誰かの役に立てれば幸いです。

参考サイトまとめ

■マストドンAWS構築チュートリアル完全版|初心者から大規模運用まで
http://webfood.info/mastodon-aws-tutorial/
■AWS EC2インスタンスにElastic IP(固定グローバルIPアドレス)を割り当てる
https://ac-5.net/aws/aws_elasticip_allocation
■(お名前.com)ネームサーバーのAレコード設定
http://rensrv.com/domain/onamae-com/a_record-setting-onamae-com/
■今何かと話題のマストドン(mastodon)鯖を自分用に無料で立てる方法
http://jtwp470.hatenablog.jp/entry/2017/04/15/174036

続きを読む

AmazonLinuxでAWSのLocalStackインストール手順

リンク: https://github.com/atlassian/localstack

Dockerインストール

確認する:

$ sudo yum search docker
Loaded plugins: priorities, update-motd, upgrade-helper
========================================================================================= N/S matched: docker ==========================================================================================
docker-storage-setup.noarch : A simple service to setup docker storage devices
docker.x86_64 : Automates deployment of containerized applications
docker-devel.noarch : A golang registry for global request variables (source libraries)
docker-pkg-devel.noarch : A golang registry for global request variables (source libraries)

  Name and summary matches only, use "search all" for everything.


$ sudo yum info docker

Loaded plugins: priorities, update-motd, upgrade-helper
Available Packages
Name        : docker
Arch        : x86_64
Version     : 17.03.1ce
Release     : 1.50.amzn1
Size        : 22 M
Repo        : amzn-updates/latest
Summary     : Automates deployment of containerized applications
URL         : http://www.docker.com
License     : ASL 2.0 and MIT and BSD and MPLv2.0 and WTFPL
Description : Docker is an open-source engine that automates the deployment of any
            : application as a lightweight, portable, self-sufficient container that will
            : run virtually anywhere.
            :
            : Docker containers can encapsulate any payload, and will run consistently on
            : and between virtually any server. The same container that a developer builds
            : and tests on a laptop will run at scale, in production*, on VMs, bare-metal
            : servers, OpenStack clusters, public instances, or combinations of the above.

インストール:

$ sudo yum install -y docker

サービス起動

$ service docker start

LocalStackインストール

プロジェクトをクローンする

$ git clone https://github.com/atlassian/localstack.git

起動する:

make docker-run

# Or using docker-compose:

docker-compose up

続きを読む

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

続きを読む