ksaitoの日記

日々試したことの覚え書き

dockerのクラスタとスケジューラ

AWS ECS、AWS Fargate、Docker Swarm、Kubernetesの違いについての覚書です。 Kubernetesの人たちがAWS ECSよりKubernetesのほうが賢いといっていたり、Docker純正のSwarmよりKubernetesが注目されている理由が何となく納得できました。

AWS ECS

ECSは、Auto Scaleで複数のEC2インスタンスクラスタを構成できます。 サーバリソースの状況によって、Auto Scaleがサーバを増減させてくれるので負荷のピークとコストは最適に調整することができます。コンテナもAuto Scaleで増減され、ELBの動的ポートマッピングは、利用者からシームレスにバックエンドのコンテナの入れ替えを可能にしてくれる素晴らしい機能です。

ECSでネットワークモードがdefaultの場合、TaskDefinitionで定義された複数のコンテナは、1つのインスタンスでセットで起動されるためクラスタのスケールアウトの恩恵を受けることはできません。また、TaskDefinitionの中の特定のコンテナだけスケールアウトすることもできません。Linksは、相互参照をサポートしないので、コンテナ間で双方向の通信がある場合、DNSやconsulといった、何らかのサービスディスカバリが必要になります。

ECSでネットワークモードにawsvpcを使う場合、コンテナがVPC上のホストのように見えます。コンテナレベルでスケールアウトするには良さそうですが、実装は、EC2インスタンスにENIを追加してコンテナのNICに割り当てているため、EC2インスタンスに追加できるENIの数に制約を受けます。

ECSは、素晴らしいサービスですが、上記のような制約を管理する必要があり、なかなか大変です。

AWS Fargate

Fargateは、ECSのPaaSでEC2インスタンスの管理をなくても良く、課金モデルもEC2インスタンスではなく、コンテナの稼働に対する課金となります。ECSのEC2でAuto Scale、コンテナでもAuto Scaleの二段階のスケールアウトに違和感を感じていたので、リリースされたときには、すごく魅力的に感じました。

実際に動かしてみて、docker exec できないと分かって、使う気持ちがなくなりました。コンテナを使うのに、CloudWatch経由でログを見るしかないのは、ちょっとしんどい... そのためにsshサーバをたてると言うのは本末転倒

Docker Swarm

Docker純正のクラスタでDocker環境に設定すれば利用できるので非常に手軽です。スタックの設定は、docker-composeが、ほぼそのまま使えます。 現時点では、コンテナのスケールを動的に行うスケジューラがないので非常に残念です。 swarm modeは、cap_addをサポートしていないので権限を必要とするコンテナを動かすことはできません。(concourse.ciのworkerは残念ながら動きませんでした。)

kubernetes

Kubernates環境を作成するのが大変そうで、なかなか踏み出せません。 使っていないので、なんともいませんが、ECSやSwarmの制約がないのかな。 GCPやEKSで試す前に、一度、やってみなければ...

シェルでファイルの差分があった場合に処理を実行したい。

シェルでファイルの差分があった場合のみ処理を実行したいユースケースがあります。 diffで差分は、検出できますが終了ステータスは0以外が返されます。 このステータスは、異常終了ではなく、差分があるか・ないかを判定したいだけなので無視して欲しいです。

いろいろな実装方法はあると思いますが、下記で意図する実装をすることができました。 シェルの||の使い方を理解する必要があるため、コードを見て意図を理解することが難しい実装なので、もっと良い方法があるような気もします。

sts=0
diff -uwB file1 file2 || sts=$?
echo Status=$sts

実行例は、以下の通りです。

$ cat sample.sh 
#! /bin/bash

sts=0
diff -uwB file1 file2 || sts=$?
echo Status=$sts
$ ./sample.sh 
Status=0
$ echo bbb > file2
$ ./sample.sh 
--- file1       2018-02-12 21:24:46.161302088 +0900
+++ file2       2018-02-12 21:26:03.922402102 +0900
@@ -1 +1 @@
-aaa
+bbb
Status=1
$ 

下記の2行目で||は、diffが失敗(終了ステータスが0以外)を返した時に実行されます。sts=$?は、必ず成功するので4行目全体として失敗することはありません。

diffが成功(終了ステータスが0)を返すとsts=$?は実行されません。 2行目のsts=0とすることで結果的にstsが未定義とならず、正常終了の0が設定されます。

ちょっと、トリッキーなので、もっと良い方法があるはずなのですが...

$ cat -n sample.sh 
     1  #! /bin/bash
     2
     3  sts=0
     4  diff -uwB file1 file2 || sts=$?
     5  echo Status=$sts
$ 

Dockerのお掃除

Dockerの使っていないオブジェクトを掃除するための手順です。

イメージ

タグのついていないイメージを削除します。

docker rmi `docker images -qf dangling=true`

コンテナ

停止しているコンテナを削除します。

docker rm `docker ps -a -q`

ボリューム

コンテナに紐付いていないボリュームを削除します。 必要なコンテナが立ち上がっていないと削除対象となってしまうので注意が必要です。

docker volume rm `docker volume ls -qf dangling=true`

bosh-cliのインストール

bosh-cliのインストール手順です。

bosh.ioからプラットフォームに応じたバイナリのURLを確認して、下記の一連のコマンドでインストールできます。

sudo curl -o /usr/local/bin/bosh-cli-2.0.45-linux-amd64 https://s3.amazonaws.com/bosh-cli-artifacts/bosh-cli-2.0.45-linux-amd64
sudo chmod +x /usr/local/bin/bosh-cli-2.0.45-linux-amd64
sudo ln -s /usr/local/bin/bosh-cli-2.0.45-linux-amd64 /usr/local/bin/bosh

バージョンを確認したらインストール完了です。

$ bosh --version
version 2.0.45-d208799-2017-10-28T00:31:53Z

Succeeded
$

bashのretry

下記のコードを$HOME/.bashrcに追加するとコマンドが失敗した場合に、一定時間待って、指定した回数リトライするretry関数を追加できます。

オリジナルは、ここのソースを参考に改良しました。(コメントしようと思いましたが、50以上の評価がないとコメントできませんでした。)

aws cliでリソースを作成した場合、awsコマンドは、リソース作成の終了を待たずに終わってしまいます。リソース作成が終わってから後続の処理をしたい場合に、適切なタイムアウトを設けてリトライするのに便利です。

コード

function retry() {
    if [ $# -lt 3 ]; then
        echo 'usage: retry <num retries> <wait retry secs> "<command>"'
        return 1
    fi

    retries=$1
    wait_retry=$2
    shift 2
    command=$@

    for i in `seq 1 $retries`; do
        echo "$command"
        $command
        ret_value=$?
        [ $ret_value -eq 0 ] && break
        echo "> failed with $ret_value, $wait_retry sec waiting to retry..."
        sleep $wait_retry
    done

    return $ret_value
}

使い方

成功する場合

$ retry 5 1 test 0 -eq 0
test 0 -eq 0
$ echo $?
0
$ 

失敗する場合

$ retry 5 1 test 0 -eq 1
test 0 -eq 1
> failed with 1, 1 sec waiting to retry...
test 0 -eq 1
> failed with 1, 1 sec waiting to retry...
test 0 -eq 1
> failed with 1, 1 sec waiting to retry...
test 0 -eq 1
> failed with 1, 1 sec waiting to retry...
test 0 -eq 1
> failed with 1, 1 sec waiting to retry...
$ echo $?
1
$