Rancher2.0 Tech Preview2 ~Create a RKE Cluster~

Rancher2.0 Tech Preview2の主要機能として追加された「Create a RKE Cluster」は、RKE(Rancher Kubernetes Engine)を利用して、AWS、Azure、DigitalOcean、Packet、vSphereに対してKubernetesクラスタを作成することができます。(※2018年2月現在)

Rancher2.0 Tech Preview2からAWS上にKubernetesクラスタを作成してみましょう。

Tech Preview2のインストールはRancher2.0 Tech Preview2についての「Get Started with Rancher 2.0」を参考にしてください。

Rancher2.0 Tech Preview2でAWS上にKubernetesクラスタを作成

1.Rancher2.0 Tech Preview2 Serverにログイン後に、「Add Cluster」ボタンをクリックします。

image.png

2.「Create a RKE Cluster」の「Select」ボタンをクリックします。

image.png

3.nameに任意名を入力(今回はaws-k8s-clusterとします。)して、「Add a new cluster」をクリックします。

image.png

4.「Configure」をクリックします。

image.png

5.「Amazon EC2」を選択し、nameに任意名を入力(今回はaws-k8s-clusterとします。)し、Countは「3」、Regionは「ap-northeast-1」、Access keyとSecret keyはAWSのIMA Management Consoleでグループとユーザを作成し、そのユーザーのものを入力します。そして、「Next:Authenticate & select a network」ボタンをクリックします。

image.png

6.vpcを選択して、「Next:Select a Security Group」ボタンをクリックします。

image.png

7.「Next:Set Instance options」ボタンをクリックします。

image.png

8.「Create」ボタンをクリックします。

image.png

9.kubernetesの構成を作成します。最後に「Create」ボタンをクリックします。

image.png

10.しばらくするとkubernetesクラスタ構築が完了します。

screencapture-35-198-222-0-g-clusters-1517843931311.png

クラスタ名をクリックすると全体のリソース状況が可視化されます。

screencapture-35-198-222-0-c-cluster-8p8bq-1517844232326.png

11.上部メニュー「Nodes」を選択します。

AWS上に構築されたkubernetesクラスタの状況を確認できます。各クラスタ名をクリックするとリソース状況が可視化されます。

image.png

12.「KubeConfig File」ボタンをクリックします。

image.png

kubectlコマンドのconfigファイルをクリップボードにコピーできます。

image.png

kubectlコマンドを実行できるクライアントをローカルまたはサーバで構築します。今回はGCP上にGCEのインスタンス(Ubuntu 16.04 LTS)を1台用意します。

コマンド
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl

$ chmod +x ./kubectl

$ sudo mv ./kubectl /usr/local/bin/kubectl

$ mkdir .kube

$ vim .kube/config
#クリップボードにコピーした内容をペーストして保存します。
:wq

$ kubectl get nodes
NAME               STATUS    ROLES         AGE       VERSION
aws-k8s-cluster1   Ready     etcd,master   26m       v1.8.5-rancher1
aws-k8s-cluster2   Ready     worker        23m       v1.8.5-rancher1
aws-k8s-cluster3   Ready     worker        25m       v1.8.5-rancher1

これで、kubectlコマンドでアプリケーションをデプロイすることが可能となります。

13.AWSのコンソールを確認します。

インスタンスが3台で来ていることが確認できます。

image.png

以上となりますが、AWS以外のAzure、DigitalOcean、Packet、vSphereも同様にRKEでkubernetesクラスタを構築できると思います。

参考資料

RKE(Rancher Kubernetes Engine)

続きを読む

AWS Lambda から ElastiCache に接続するメモ

やりたいこと

  • AWS Lambda から ElastiCache に接続する
  • boto3 は get/set はできないらしいので他の方法を使う

必要な設定

  • AWS Lambda の実行ロールに ElastiCache へのアクセス権限追加
  • AWS Lambda, ElastiCache を同じVPCにあわせる

redis (クラスターモードが無効) の場合

EC2 にて redis-py をダウンロード

$ pip3 install redis -t ./
  • 生成されるディレクトリを開発環境の index.py と同じディレクトリにコピーする

index.py 作成

index.py
import redis

def handler(event, context):

    r = redis.StrictRedis(host='****.****.0001.apne1.cache.amazonaws.com', port="6379", db=0)

    r.set("key1", "value1")
    print(r.get("key1"))

    return "OK"

以上。

redis (クラスターモードが有効) の場合

EC2 にて redis-py-cluster をダウンロード

$ pip3 install redis-py-cluster -t ./
  • redis-py も依存してるので一緒にダウンロードされる
  • 生成されるディレクトリを開発環境の index.py と同じディレクトリにコピーする

index.py 作成

index.py
from rediscluster import StrictRedisCluster

def handler(event, context):

    startup_nodes = [{"host": "****.****.clustercfg.apne1.cache.amazonaws.com", "port": "6379"}]
    rc = StrictRedisCluster(startup_nodes=startup_nodes, decode_responses=True, skip_full_coverage_check=True)

    rc.set("key1", "value")
    print(rc.get("key1"))

    return "OK"

  • クラスターモードが無効の redis には繋がらないのでテスト環境などでは上記実装を切り替える

以上。

続きを読む

AWS ARNまとめ

資格勉強向けにARNの構文をメモ
Amazon ARN

基本形式

arn:partition:service:region:account-id:resource
arn:partition:service:region:account-id:resourcetype/resource
arn:partition:service:region:account-id:resourcetype:resource
  • パーティション: 基本的にAWS
  • サービス: AWS製品名
  • リージョン: リソースがあるリージョン。リージョン関係ないものは省かれている
  • リソース,リソースタイプ: サービスによって異なる。スラッシュやコロンなどで書いてある

覚え方
アランとパーティでサービス受けたらリケジョにアカンと(リ)ソースかけられた。

EC2構文例

arn:aws:ec2:region:account-id:instance/instance-id
arn:aws:ec2:region:account-id:volume/volume-id

arn:aws:ec2:us-east-1::image/ami-1a2b3c4d
アカウントが省略されて::となる。
arn:aws:ec2:us-east-1:123456789012:instance/*

S3構文例

arn:aws:s3:::bucket_name
arn:aws:s3:::bucket_name/key_name
s3はリージョン、アカウントは不要。故にバケット名が一意である必要があるのか

S3例

arn:aws:s3:::my_corporate_bucket/*
ポリシーのARNを指定する場合は、ワイルドカード「*」文字を使用できる。

RDS構文例

arn:aws:rds:region:account-id:db:db-instance-name
arn:aws:rds:region:account-id:cluster:db-cluster-name

AWSCLI,RDS APIを使用する時に使う。その際にはタグと共に使用する。

AWS サービスの名前空間
AWSサービスの識別をNamespaceを使用して AWSのサービスを識別する。
IAMポリシーを作る時などにアクションとリソースを識別するときに使用する。

そういえば関係ないけどサービス名のプレフィックスでAmazonとAWSの違いは

  • Amazon: 単体のサービスとして利用出来る。
  • AWS: 他のAWSのサービスと組み合わせて利用する。

と,どこかで聞いた気がする。公式のどこかに書いてあったけな?

続きを読む

Auroraのメンテナンスイベントについて

Auroraのメンテナンスイベントについて知らなければ!という衝動に突き動かされたわけではなく
意図しないアップグレードで少しやられてしまったので必要に迫られた故、メモ書きする。

メンテナンスの種類

RDSのマネージメントコンソールのクラスターのメンテナンス列で確認できる模様。
確認時に気づいたけれど大幅にUIが変更されたなー。Lambdaは頻繁に変わっているイメージだけど。。。

image.png

利用可能(Available)と必須(Required)がある。

Availableなイベントは延期が可能で、Requiredなイベントはメンテナンスウィウンドウで自動実行される。

Requiredなイベントについて、事前に知る必要がある。

describe-pending-maintenance-actions

Auroraのメンテナンス通知はメールで届いたりしないので、自分でAPIを叩いてメンテナンス情報を取得する必要がある。

describe-pending-maintenance-actions

上記APIを叩くと、以下のようなレスポンスが取得できる。

{
    "PendingMaintenanceActions": [
        {
            "PendingMaintenanceActionDetails": [
                {
                    "Action": "system-update", 
                    "Description": "Aurora 1.16 release"
                }
            ], 
            "ResourceIdentifier": "arn:aws:rds:ap-northeast-1:XXXXXXXX:cluster:XXXXXXXXXXXXXX"
        }
    ]
}

Requiredや、Available等表示されない。
どのように知るか?

API Output

Action

Auroraクラスタのメンテナンスの場合、基本的には”os-upgrade”あるいは”system-update”が返される。

  • os-upgrade:
    AuroraクラスタのOS更新
  • system-update:
    AuroraクラスタのDBエンジン更新
  • db-upgrade:
    主にRDS DBインスタンスにおけるDBエンジンの更新

AutoAppliedAfterDate, ForcedApplyDate

  • AutoAppliedAfterDate:
    更新が適用されるメンテナンスウィンドウの指定。この項目で指定された日付以降の最初のメンテナンスウィンドウでアクションを実行される。
  • ForcedApplyDate:
    指定された場合、メンテナンスウィンドウ外でも指定された日時でメンテナンスが実行される。

「メンテナンスウィンドウ外でも」 

これは絶対チェックやな。

OptInStatus

AutoAppliedAfterDate, ForcedApplyDateが指定されない場合や、指定された日時よりも早くメンテナンスを実行したい場合はapply-pending-maintenance-actionを–opt-in-typeオプションを指定して実行することができる。
メンテナンスに対する選択(opt-in)状態を示す。
opt-in とは「事前許可を求めるやりかた」というようなことらしい。
この項目は明示的にapply-pending-maintenance-actionを実行した場合にのみ追加される。

apply-pending-maintenance-action
ステータスは以下。

  • next-maintenance:
    次のメンテナンスウィンドウ
  • immediate: 即座
  • undo-opt-in: next-maintenanceの指定がキャンセルされた

CurrentApplyDate

CurrentApplyDateは、AutoAppliedAfterDate, ForcedApplyDate, OptInStatusのすべてを考慮したうえで、「現時点で実際にメンテナンスの自動適用が予定されている日時」

結論

AutoAppliedAfterDate 、ForcedApplyDateが設定されているものがRequiredなイベントなので、事前にちゃんと知るようにする。

参考サイト

続きを読む

kube-awsで既存のvpc、subnetに入れるときにハマったのでメモ

kube-awsとは

10分でわかる「kube-aws」を参照ください。
kube-awsをメンテされている方にて記載されています。

既存のvpcに入れる時にハマった

kube-awsを使い始めて、既存のvpcになかなか入れられなかったのでメモ
試行錯誤した結果なので正しく無いかもしれないですが、このyamlで動きました。

cluster.yaml
clusterName: oreno-k8s
sshAccessAllowedSourceCIDRs:
- 0.0.0.0/0
apiEndpoints:
-
  name: default
  dnsName: oreno.k8s.dev.examlpe.com
  loadBalancer:
    subnets:
    - name: ExistingPublicSubnet1
    - name: ExistingPublicSubnet2
    hostedZone:
      id: ABCDEFGAAAAAA
keyName: oreno-keyname
region: us-west-2
kmsKeyArn: "arn:aws:kms:us-west-2:1234567890123:key/aaaaaaa-aaaaa-aaaaa-aaa-aaaaaaa"
controller:
  instanceType: t2.medium
  rootVolume:
    size: 30
    type: gp2
  autoScalingGroup:
    minSize: 2
    maxSize: 3
    rollingUpdateMinInstancesInService: 2
  subnets:
  - name: ExistingPublicSubnet1
  - name: ExistingPublicSubnet2
worker:
  nodePools:
    - name: nodepool1
      count: 2
      instanceType: t2.medium
      rootVolume:
        size: 30
        type: gp2
etcd:
  count: 3
  instanceType: t2.medium
  rootVolume:
    size: 30
    type: gp2
  subnets:
    - name: ExistingPublicSubnet1
    - name: ExistingPublicSubnet2
vpc:
  id: vpc-123456789
subnets:
  - name: ExistingPublicSubnet1
    availabilityZone: us-west-2a
    id: "subnet-1234567a"
  - name: ExistingPublicSubnet2
    availabilityZone: us-west-2c
    id: "subnet-9876543e"
containerRuntime: docker
kubernetesDashboard:
  adminPrivileges: true
  insecureLogin: false
addons:
  省略
experimental:
  省略

ポイント

vpc

vpcはvpc.id、vpc.routeTableId、vpc.vpcCIDRは既存VPCの値を入れる

vpc:
  id: vpc-123456789

subnets

subnetsのnameは任意、subnet.availabilityZone、subnet.idは既存のsubnetの値を入れる

subnets:
  - name: ExistingPublicSubnet1
    availabilityZone: us-west-2a
    id: "subnet-1234567a"
  - name: ExistingPublicSubnet2
    availabilityZone: us-west-2c
    id: "subnet-9876543e"

controller

controllerのsubnetsでsubnets.nameの値を指定する
複数指定することで複数AZで展開できる

controller:
  instanceType: t2.medium
  rootVolume:
    size: 30
    type: gp2
  autoScalingGroup:
    minSize: 2
    maxSize: 3
    rollingUpdateMinInstancesInService: 2
  subnets:
  - name: ExistingPublicSubnet1
  - name: ExistingPublicSubnet2

etcd

controllerと同じくsubnetsでsubnets.nameの値を指定する
複数指定することで複数AZで展開できる

etcd:
  count: 3
  instanceType: t2.medium
  rootVolume:
    size: 30
    type: gp2
  subnets:
    - name: ExistingPublicSubnet1
    - name: ExistingPublicSubnet2

worker

ここでハマったのですが、workerにもworker.nodePools.subnetsがありますが、subnetsを指定するとエラーが出てデプロイ出来なかったです。
subnetsを指定しないとデプロイが出来て、複数台を複数AZにデプロイしてくれました。
指定しなかったらデフォルトでvpc.subnetを見るのかな??教えてエラい人・・

worker:
  nodePools:
    - name: nodepool1
      count: 2
      instanceType: t2.medium
      rootVolume:
        size: 30
        type: gp2

PS.

kopsよりAWS寄りに実装されているので、AWSでk8sを扱う方はkube-awsはオススメです。

続きを読む

AWS Batchで固定されたEC2インスタンスのGPUを使う

背景

AWS Batchでは、Jobの定義とAMIの指定を行うことで、
1. 自動でEC2インスタンスを立ち上げ
2. Dockerコンテナを起動
3. コンテナ終了時にEC2インスタンスの自動削除
を行ってくれます。

しかし、EC2インスタンスの料金体系では、毎回インスタンスを立ち上げ・削除するのはコスパが悪いため、Jobが実行されるEC2インスタンスを固定して、実行中以外は削除ではなく、「停止」にしたい!

ということで、その手順をまとめました。
AWS Batch、あるいはAmazon ECS(Elastic Container Service)でGPUを使うための設定も含まれていますので、インスタンスの固定はいいや、という方にも参考になれば、と思います。

AWS Batchの設定

Compute environments(=ECS Cluster)

AWS Batch > Compute environments > Create environment から、AWS BatchのJobを実行する計算環境を作成します。この計算環境は、ECSのClusterとして保存されます。(ECS Clusterが自動で生成され、Compute environmentsに紐付けされます。)

  • Managed:
    AMIを指定しておくことで、Jobの実行時にそのAMIで新たにEC2インスタンスを立ち上げてくれます。
  • Unmanaged:
    Compute environmentsに紐付いたECS Clusterを生成します。EC2インスタンスは、自分でそのClusterに登録する必要があります。

作成されたClusterをECSコンソールから確認しましょう。後ほど使います。
<EnvironmentName>_Batch_<ランダムっぽい文字列> のような名前担っているはずです。

Job Queue

Job Queueは、上記で作成したEnvironmentを選択してください。

Job Definition

Volumes

Name Source path
nvidia /var/lib/nvidia-docker/volumes/nvidia_driver/latest

Mount points

Container path Source volume Read only
/usr/local/nvidia nvidia false

ECSで使える(Dockerが動かせる)EC2インスタンスを用意する

ECS用に最適化されたAMIはAWSから提供されていますが、GPUが載ったものでECS用に最適化されたAMIは提供されていません。

用いるAMIを決める

AWS Batchのドキュメント GPU ワークロードの AMI の作成 にある例では Deep Learning AMI with Source Code (CUDA 9, Amazon Linux) を使用していますが、今回は Deep Learning Base AMI (Amazon Linux) を使用することにしました。
(AWS Batchを用いる = Dockerを用いるので、Hostマシンに機械学習ライブラリは不要。 & Cuda, CuDNNのバージョンが複数あるので、動かすDockerImageが変わった時に助かりそう。)
ここは、必要に応じてaws marketplaceでいろいろ探してみるとよいかと思います。

インスタンスを起動する

IAM

インスタンスからECSに向けたIAM Roleが必要です。Roleの設定がない場合は、EC2コンソールから設定をしましょう。

UserData

UserData
#!/bin/bash
echo "ECS_CLUSTER=<ClusterName>" >> /etc/ecs/ecs.config

Dockerが使えるようにする

上記のAMIのままでは、Dockerが使えないので、ドキュメントを参考に、ECSで使える状態のインスタンスを作成していきます。

configure-gpu.sh
#!/bin/bash
# Install ecs-init, start docker, and install nvidia-docker
sudo yum install -y ecs-init
sudo service docker start
wget https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker-1.0.1-1.x86_64.rpm
sudo rpm -ivh --nodeps nvidia-docker-1.0.1-1.x86_64.rpm

# Validate installation
rpm -ql nvidia-docker
rm nvidia-docker-1.0.1-1.x86_64.rpm

# Make sure the NVIDIA kernel modules and driver files are bootstraped
# Otherwise running a GPU job inside a container will fail with "cuda: unknown exception"
echo '#!/bin/bash' | sudo tee /var/lib/cloud/scripts/per-boot/00_nvidia-modprobe > /dev/null
echo 'nvidia-modprobe -u -c=0' | sudo tee --append /var/lib/cloud/scripts/per-boot/00_nvidia-modprobe > /dev/null
sudo chmod +x /var/lib/cloud/scripts/per-boot/00_nvidia-modprobe
sudo /var/lib/cloud/scripts/per-boot/00_nvidia-modprobe

# Start the nvidia-docker-plugin and run a container with 
# nvidia-docker (retry up to 4 times if it fails initially)
sudo -b nohup nvidia-docker-plugin > /tmp/nvidia-docker.log
sudo docker pull nvidia/cuda:9.0-cudnn7-devel
COMMAND="sudo nvidia-docker run nvidia/cuda:9.0-cudnn7-devel nvidia-smi"
for i in {1..5}; do $COMMAND && break || sleep 15; done

# Create symlink to latest nvidia-driver version
nvidia_base=/var/lib/nvidia-docker/volumes/nvidia_driver
sudo ln -s $nvidia_base/$(ls $nvidia_base | sort -n  | tail -1) $nvidia_base/latest

スクリプトの実行(Step 4.)

bash ./configure-gpu.sh

これで環境は完成していますが、念のため動作確認(Step 5. )

sudo docker run –privileged -v /var/lib/nvidia-docker/volumes/nvidia_driver/latest:/usr/local/nvidia nvidia/cuda:9.0-cudnn7-devel nvidia-smi

DockerからGPUが使えることを確認できたら(以下のような $ nvidia-smi の結果が見られれば) OKです。

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 384.81                 Driver Version: 384.81                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla V100-SXM2...  Off  | 00000000:00:17.0 Off |                    0 |
| N/A   43C    P0    42W / 300W |     10MiB / 16152MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

ECS Clusterにインスタンスを登録する

Check 1
インスタンスからECSに向けたIAM Roleが必要です。Roleの設定がない場合は、EC2コンソールから設定をしましょう。

Check 2
Clusterにインスタンスを登録するには、インスタンス側での設定が必要です。インスタンス起動時のユーザデータで下記ファイルがきちんと作られていることを確認。できていなければ作りましょう。

/etc/ecs/ecs.config
ECS_CLUSTER=<ClusterName>

ドキュメント のStep 6., 7.的なことをしてあげると、Clusterにインスタンスが登録されます。(しばらく時間がかかります)

sudo docker rm $(sudo docker ps -aq)

sudo docker rmi $(sudo docker images -q)

sudo restart ecs

Submit Job

お好きなフックでどうぞ

EC2インスタンスを停止する

Managedであれば自動でインスタンスを削除してくれますが、Unmanagedの場合、インスタンスの停止も自分で行わなければなりません。
今回は、Jobの成功、失敗にかかわらず停止したいので、以下のpythonスクリプトを自作し、AWS Lambda x CloudWatchEventsで走らせることにしました。

  • [‘SUBMITTED’, ‘PENDING’, ‘RUNNABLE’, ‘STARTING’, ‘RUNNING’] のいずれかの状態のJobがあればそのまま。
  • いずれの状態のJobもなければ、停止。
import os
import boto3

JOB_NAME = os.environ['JOB_NAME']
JOB_QUEUE = os.environ['JOB_QUEUE']
INSTANCE_ID = os.environ['INSTANCE_ID']


def is_running():
    batch = boto3.client('batch')

    check_status = ['SUBMITTED', 'PENDING', 'RUNNABLE', 'STARTING', 'RUNNING']
    for s in check_status:
        list = batch.list_jobs(
            jobQueue=JOB_QUEUE,
            jobStatus=s
        )['jobSummaryList']
        if list:
            print(f"{[j['jobId'] for j in list]} are still {s}.")
            return True
    else:
        return False


def ec2_stop():
    ec2 = boto3.client('ec2')

    response = ec2.describe_instances(
        InstanceIds=[INSTANCE_ID]
    )
    if response['Reservations'] and response['Reservations'][0]['Instances']:
        state = response['Reservations'][0]['Instances'][0]['State']['Name']

        if state in {'pending', 'running'}:
            print(f"Start to stop the instance: {INSTANCE_ID}")
            response = ec2.stop_instances(
                InstanceIds=[INSTANCE_ID]
            )
        else:
            print(f"The instance {INSTANCE_ID} is not running.")
    else:
        raise ValueError(f'No Such Instances: [{INSTANCE_ID}]')

def lambda_handler(event, context):
    if not is_running():
        ec2_stop()

最後に

こんなことするくらいなら、最初からBatchじゃなくて、ECSにしとけばよかったんちゃうか、と思いながら。
ただ新しいものに触れて、上記以外の学びも得たので、それはまた別の時に。

続きを読む

NixOpsを使ってAWS EC2にGPUクラスタを作ってChainerMNを動かしてみた

使用するもの

NixOS

  • Linuxディストリビューションの1つ

    • 関数型パッケージ管理ツールNixを使用している
    • パッケージ自体はnixpkgsのリポジトリで管理・開発されている(個々のパッケージはhttps://nixos.org/nixos/packages.html# で検索できる)
  • 環境の構築が楽にできる
    • 1つのファイルでシステム全体の設定ができる -> 管理が楽
    • システムのアップデート -> システムのロールバック ということが楽にできる
    • sudoとかしなくてもパッケージを用意できる(たとえばpythonでnumpyとmatplotlibを使いたい時は以下のようにする)
$ nix-shell -p python3 -p pythonPackages.numpy -p pythonPackages.matplotlib

NixOps

  • NixOSの環境をリモートマシンにdeployするためのツール
  • 基本的な使い方は以下のような感じ
登録
$ nixops create [deployする環境を書いた.nixファイル(複数可)] -d [環境の名前]

インスタンスの作成&deploy
$ nixops deploy -d [環境の名前]

インスタンスの破棄
$ nixops destroy -d [環境の名前]

Chainer

  • ニューラルネットワーク記述用のpythonフレームワーク

ChainerMN

  • Chainerで複数のGPU上での分散処理を行うための追加パッケージ

はじめに

NixOpsでは、Nixのコードを書くことで、Nixパッケージマネージャを利用した環境をリモートにdeployすることができます。
これを使ってGPUクラスタをデプロイできないかということでやってみました。

利用環境

AWSのEC2のp2.xlargeのインスタンスを使用しました。
GPUはTesla K80になります。

CUDAはCUDA 9.0、cudnnはv7.0、NCCLはv2.1.2を使用します。
MPIはOpenMPIのv1.10です。
これらはすべて利用したnixpkgsに含まれているものを使用します。
(これを書いている時点ではnixpkgsは自前で用意しています)

コード

NixOpsで使用するコード
https://github.com/hyphon81/ec2-gpu-cluster-with-NixOps

./ec2-gpu-cluster-with-NixOps というディレクトリがある想定で書きます。

nixpkgs
https://github.com/hyphon81/nixpkgs/tree/add/chainermn

これについても、 ./nixpkgs が存在する前提で書きます。

事前準備

AWSの準備

  • 当然、アカウントを作ります。
  • NixOpsのマニュアルで説明されてるように、AWS IAMのアクセスキーIDとアクセスキーを入手します。
  • AWS EC2インスタンスで使用するセキュリティグループの設定をします。これは以下の設定を含む必要があります。
    • sshポートの許可
    • 同じセキュリティグループ内でのパケットの許可
  • AWS EC2で使用するsshキーペアを入手します。
  • p2.xlargeインスタンスを使用する場合、インスタンス作成の上限数が1に設定されていると思うので、上限規制緩和の申請を行います。

※これらの手順等についてはここでは説明しません。

ファイルの準備

~/.ec2-keys
<アクセスキーID> <アクセスキー> <IAMユーザ名>
~/.aws/credentials
[<IAMユーザ名>]
aws_access_key_id = <アクセスキーID>
aws_secret_access_key = <アクセスキー>
  • ./ec2-gpu-cluster-with-NixOps/ec2-info.nix を次のように用意します。
./ec2-gpu-cluster-with-NixOps/ec2-info.nix
{ config, pkgs, ... }:

with pkgs.lib;

{
  deployment.ec2.accessKeyId = "<アクセスキーID>";
  deployment.ec2.keyPair = "<AWS EC2のsshキーペア名>";
  deployment.ec2.privateKey = "<ssh秘密鍵ファイルのパス>";
}
  • インスタンス間のssh接続に使用するsshキーペア(ed25519)を用意します。
$ ssh-keygen -t ed25519
...
$ cp ~/.ssh/id_ed25519* ./ec2-gpu-cluster-with-NixOps/

NixOpsによるデプロイ

①. nixops create コマンドを実行し、設定を作ります。

$ nixops create ./ec2-gpu-cluster-with-NixOps/ec2gpu-cluster.nix -d ec2-gpu-cluster

②. 早速 nixops deploy コマンドでAWS EC2インスタンスの作成→環境のデプロイを行います。

$ nixops deploy -d ec2-gpu-cluster -I nixpkgs=./nixpkgs

以上です。
コマンドは2行とあっけないですが、デプロイに結構時間がかかります。
私の場合は1〜2時間ぐらい待ちました。
NVIDIAのドライバやCUDAなどを含むため、デプロイするファイルサイズの合計が5GBぐらいになるようです。

デプロイする環境の設定について

今回用意したファイルは以下のようになっています。

./ec2-gpu-cluster-with-NixOps/ec2gpu-cluster-conf.nix
{ region, ami, instanceType, securityGroups, publicKey, secretKey, hosts }:

{ config, pkgs, resources, ...}:
let
  ## この辺は使用するパッケージの設定です。
  python = pkgs.python3;
  cudatoolkit = pkgs.cudatoolkit;
  mpi = pkgs.openmpi.overrideDerivation (attrs: {
    configureFlags = [ "--with-cuda=${pkgs.cudatoolkit}" ];
  });
  gpu-test1 = pkgs.callPackage ./gpu-test1.nix {
    mpi = mpi;
  };
  gpu-test2 = pkgs.callPackage ./gpu-test2.nix {
    python = python;
    cython = python.pkgs.cython;
    mpi = mpi;
    cudnnSupport = true;
    cudatoolkit = cudatoolkit;
    cudnn = pkgs.cudnn;
    nccl = pkgs.nccl;
  };
in
{ imports = [ ./ec2-info.nix ];
  deployment.targetEnv = "ec2";
  deployment.ec2.ami = ami;
  deployment.ec2.region = region;
  deployment.ec2.instanceType = instanceType;
  deployment.ec2.securityGroups = securityGroups;
  deployment.ec2.ebsInitialRootDiskSize = 20;

  nixpkgs.config.allowUnfree = true;

  environment.systemPackages = [
    python
    mpi
    cudatoolkit
    gpu-test1
    gpu-test2
  ];

  ## 以下が環境設定です。
  services.xserver = {
    enable = true;
    videoDrivers = [
      "nvidia"
    ];
  };

  services.openssh = {
    enable = true;
    hostKeys = [
      { bits = 4096; path = "/etc/ssh/ssh_host_rsa_key"; type = "rsa"; }
      { path = "/etc/ssh/ssh_host_ed25519_key"; type = "ed25519"; }
      { path = "/etc/root/ssh/id_ed25519"; type = "ed25519"; }
    ];
    knownHosts = hosts;
    passwordAuthentication = false;
    challengeResponseAuthentication = false;
  };

  environment.etc = {
    "/root/ssh/id_ed25519" = {
      enable = true;
      user = "root";
      group = "root";
      mode = "0600";
      source = secretKey;
    };
  };

  ## LD_LIBRARY_PATHに必要なライブラリを含めるためにこんなことやってしまっています。
  ## もっとかっこいい方法をご存じの方がいたらぜひ教えてください。
  hardware.opengl = {
    extraPackages = [
      mpi
      pkgs.linuxPackages.nvidia_x11
      cudatoolkit
      cudatoolkit.lib
    ];
  };

  networking.firewall.enable = false;

  users.users.root = {
    openssh.authorizedKeys.keyFiles = [ publicKey ];
  };

  programs.bash.shellInit = ''
    if [ ! -e ~/.ssh/id_ed25519 ] ; then
      if [ -f /etc/root/ssh/id_ed25519 ] ; then
        /run/current-system/sw/bin/ln -s /etc/root/ssh/id_ed25519 .ssh/id_ed25519
      fi
    fi
  '';
}
./ec2-gpu-cluster-with-NixOps/ec2gpu-cluster.nix
let
  ## ここでデプロイする先のAWS EC2の設定を記述しています
  region = "ap-northeast-1"; ## 東京リージョン
  ami = "ami-89b921ef"; ## 東京リージョンのNixOSのAMI https://nixos.org/nixos/download.html
  instanceType = "p2.xlarge"; ## インスタンスタイプ
  ec2 = import ./ec2gpu-cluster-conf.nix;

  securityGroups = [ "allow-ssh" ]; ## インスタンスが所属するセキュリティグループ名を入れてください
  secretKey = ./id_ed25519;
  publicKey = ./id_ed25519.pub;

  hosts = [
    { hostNames = [ "node1" ]; publicKeyFile = publicKey; }
    { hostNames = [ "node2" ]; publicKeyFile = publicKey; }
    { hostNames = [ "node3" ]; publicKeyFile = publicKey; }
    { hostNames = [ "node4" ]; publicKeyFile = publicKey; }
  ];
in
{
  network.description = "An ec2 gpu cluster create test";

  node1 = ec2 {
    inherit region ami instanceType securityGroups publicKey secretKey hosts;
  };
  node2 = ec2 {
    inherit region ami instanceType securityGroups publicKey secretKey hosts;
  };
  node3 = ec2 {
    inherit region ami instanceType securityGroups publicKey secretKey hosts;
  };
  node4 = ec2 {
    inherit region ami instanceType securityGroups publicKey secretKey hosts;
  };
  ## 4ノード作成します
}

デプロイ完了後の操作(動作確認)

①. NVIDIAドライバインストールがあったため、ドライバの読み込み等必要になるので、一度全ノードをrebootしたほうが良いです。

$ nixops ssh-for-each -d ec2-gpu-cluster -p reboot

②. その後少し待ってnode1に接続します。

$ nixops ssh -d ec2-gpu-cluster node1
...
[root@node1]#

③. 今回用意したnixコードでは以下のパッケージを含み、テストができます。
(こちらに記載されているテストコードをそのまま使用しています。)

// gpu-mpi-test
// mpiパッケージがCUDA-Awareに対応しているか確認するプログラムです。
// 問題なければOKと出るだけです。
[root@node1]# gpu-mpi-test
OK.

// check_mpi4py
// mpi4pyが動作するかの確認です。
// node1〜node4が表示されれば問題ありません。
[root@node1]# mpiexec --allow-run-as-root -n 4 -host node1,node2,node3,node4 check_mpi4py
node1 0
node2 1
node3 2
node4 3

まぁ、Nixなので一度動作を確認できた環境なら同じコード使えば同じように動くはずなんですが・・・

ChainerMNを使用したMNISTの学習の動作

コード自体はここに従って用意しています。

実際に動作させてみたときの動画がこちらです
※ 6分程度と長いのでBGMつけました。音が出ます。

ビデオが開けなかった場合に表示されます

一応、動作していました。

余談

ChainerMNでMNISTの学習が動作できたのですが、
ノードを1つだけ使ったときのほうが計算が早く終わりました。

ビデオが開けなかった場合に表示されます

ChainerMNの論文を見る限り、
ノード増やすとほぼリニアにスピードアップするグラフが乗っているので、
何か間違ったかと思っていました。
https://arxiv.org/abs/1710.11351

ですが、こちらのスライドではそもそもMNISTの学習は
ChainerMNの速度の測定に使用するには向かない旨の注意が書いてありました。
https://www.slideshare.net/pfi/20171128chainermn

というわけで、ノード増やしてスピードアップすることを確認するには、
ちゃんとしたベンチマークになりうるプログラムを用意する必要がありそうです。

とりあえず、NixOpsでChainerMNが動作する環境の構築はできたということで、
今回はここまでです。

続きを読む

AWS Fargate

AWS ECS Fargate とは Docker コンテナを動かすための仕組み。Docker コンテナを起動して外から URL でアクセス出来るようになるまでやってみた。

  • コンテナは 8001 (WebSocket) と 8002 (HTTP) の2つのポートを受け付ける。
  • 外から両方のポートを使いたい。

AWS ECS には Clusters, Task Definitions, Repository の3つの設定項目がある

  • Clusters:

    • 複数の Service をまとめて動作させる箱。
    • Service:
      • 複数の Task をまとめて動作させる箱。
  • Task Definitions:
    • Task とは、Docker コンテナの動作単位。使う Docker image は AWS の Repositories から取ってきても良いし、他の Docker repository を使っても良い。
  • Repositories:
    • AWS 用の Docker Repository. Docker image を保存する場所。

作業手順は次のようになる。

  1. Repositories に Docker image を登録
  2. Task Definition に Docker repository を指定
  3. Cluster を作成
  4. Application Load Balancer を作成
  5. Cluster 内に Service と Task を作成
  6. Security Group の設定

AWS Fargate First run

  • https://qiita.com/riywo/items/b223bdad2b3ae3bebf55 の通りに First runチュートリアル試した。
  • 次へ次へと押すだけ
  • Unable to assume the service linked role. Please verify that the ECS service linked role exists というエラーが出る。
  • 二回目同じ事をやると何故か問題なく完了した。
  • Cluster : default > sample-app-service
  • AWS Console > Elastic Container Service > Clusters : default > Tasks > Task をクリック
  • IP が割り当てられている事を確認。

AWS Cluster を削除する時の注意点

  • The vpc ‘vpc-xxx’ has dependencies and cannot be deleted. のエラーが出て消せなかった。
  • EC2 security group が参照していたので消したら OK。

AWS ECS Repositories に Docker image を登録

AWS FargateでFlaskを動かしてみる を参考に作業。

  • Console で Region N. Virginia を選択
  • AWS Console > Elastic Container Service (ECS) > Repositories
  • Create repository
  • Repository name: hoge-websocket-server
  • Repository URL: xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server

画面に表示されるコマンドを実行してログイン

$ aws ecr get-login --no-include-email --region us-east-1
docker login -u AWS -p eyJwYXlsb2...
$ docker login -u AWS -p eyJwYXlsb2... (表示された長いコマンドをそのまま実行)

先程出来た Docker image にタグを追加

$ docker tag hoge-websocket-server:latest xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server:latest

Docker image を Registory に追加

$ docker push xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server:latest
file integrity checksum failed for "usr/lib/x86_64-linux-gnu/libjpeg.a"

Docker image が大きいと上のようなエラーが出た。小さい node のイメージを使ってイメージをダイエットすると成功。

Task Definition に Docker repository を指定

ここで Docker image の場所や使いたいリソースを定義する。

  • AWS Console > ECS > Task Definitions > Create a new definition

    • 1: Select launch type compatibility

      • FARGATE
    • 2: Configure task and container definitions
      • Task Definition Name: hoge-websocket-server
      • Task memory (GB): 0.5GB
      • Task CPU (vCPU): 0.25 vCPU
      • Add container
        • Container name: hoge-websocket-server
        • Image: xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server:latest
        • Port mappings
          • 8001
          • 8002
      • Create

Cluster を作成

  • AWS Console > ECS > Clusters > Create Cluster

    • 1: Select cluster template

      • Networking only
    • 2: Configure cluster
      • Culster name: hoge-cluster
      • Create VPC: チェック (意味はよくわからない)
        • 作成されるネットワーク情報に目を通しておく。
        • デフォルトで2つのサブネットが定義されるようだが、そのままにしておいた。
    • View Cluster

これで Cluster 本体の他、色々な Cluster Resources が作成される。これらの大事な情報を後から参照する方法が分からなかったので必ずメモっておく。

Load Balancer の作成

このままだと起動停止のたびに IP アドレスが変わってしまうので、Application Load Balanceer (ALB) を使ってドメイン名を割り当てる。

  • AWS Console > EC2 > Load Balancers
  • Application Load Balancer > Create
  • 1: Configure Load Balancer
    • Name: hoge
    • Scheme: internet-facing
    • IP address type: ipv4
    • Listeners
      • HTTP: 8001 (Websocket も HTTP で良い)
      • HTTP: 8002 (HTTP 用の Listener は ECS Service 設定時に出来るのでここで作らなくて良い)
    • Availability Zones
      • VPC (Create Cluster で設定した VPC)
      • 下の Subnet は2つとも選択する
    • Tag
      • project : hoge
  • 2: Configure Security Settings
    • 設定なし
  • 3: Security Groups
    • Create a new security group
    • Security group name: hoge-security-group
    • 使いたいポート番号を設定
    • あとで AWS Console > ECS > Clusters > hoge-cluster > Tasks > Task : a44f… から編集出来る。
  • 4: Configure Routing
    • ヘルスチェックの設定らしい
    • Name: hoge-8002
    • Port: 8002
  • 5: Register Targets
    • 何も設定しない

Cluster 内に Service を作成

  • AWS Console > ECS > Clusters > hoge-cluster > Services > Create

    • 1: Configure service

      • Launch type: FARGATE
      • Task Definition: (作成した Task)
      • Service name: hoge-service
      • Number of taksk: 1
    • 2: Configure network
      • Cluster VPC: (Create Cluster で設定した VPC)
      • Subnets: (Create Cluster で設定した subnet)
      • Security groups > Edit
        • Assigned security groups: Select existing security group
        • Load Balancer で作った hoge-security-group を選択
      • Load balancing
        • Load balancer type: Application Load Balancer
        • Load balancer name: hoge
      • Container to load balance
        • ここで設定すると、コンテナの IP が変わっても自動的に一つのポートだけ Load Balancer に割り当てられる。
        • hoge-websocket-server:8002 > Add to load balancer
          • Listner port: 8002:HTTP
          • Path pattern: /*
          • Evaluation order: 1
          • Health check path: /
        • hoge-websocket-server:8002
    • 3: Set Auto Scaling
      • デフォルトのまま
    • 4: Review
      • 特に VPC Id と Subnets に気をつける。間違うとややこしい事になる。
    • Create Service

コンテナに割り当てられた IP アドレスの確認

  • AWS Console > ECS > Clusters > hoge-cluster > Tasks > Task : a44f… を選択
  • Private IP と Public IP が表示される。

もう一つのポート Port 8001 の設定

ECS Service の作成時に Load Balancer を割り当てると、IP が変化しても Load Balancer が勝手に面倒を見てくれる。ただしポート番号は一つしか設定出来ない!!!!この例のように2つ目のポートを Load Balancer に見せるには、手動で IP を指定した Target Groups を作る必要があった。なので、更新の際にはこの IP をわざわざ再入力する必要がある。

  • AWS Console > EC2 > LOAD BALANCING Target Groups

    • Target group name: hoge-8001
    • Protocol: HTTP
    • Port: 8001
    • Target type: ip
    • VPC: Cluster の VPC
    • Create
  • AWS Console > EC2 > LOAD BALANCING Target Groups > hoge-8001
    • Targets > Edit
    • Task の IP を登録
  • AWS Console > EC2 > LOAD BALANCING Load Balancers > hoge > Listeners
    • HTTP:8001 > View/edit rules
    • THEN に hoge-8001 を登録

Docker image の更新

  • AWS Console > ECS > Clusters > hoge-cluster > hoge-service > Update

    • Force new deployment: Check
    • 次へ次へ。。。
  • しばらく待つと新しいコンテナが起動して古いコンテナが停止する。
  • IP アドレスが変わっているので Load Balancer の Target Group も更新する。

Task 削除

Task は service が管理するので、直接止められないらしい。Service の Update で Number of tasks を 0 にすると止められた。

  • AWS Console > ECS > Clusters > hoge-cluster > hoge-service > Update
  • Number of tasks: 0
  • 次へ次へ

やりたかった事は node 一つの単純なサーバをテストしたいだけだったんだけど、随分大げさな構成になってしまった。

続きを読む

BlockSci環境構築(ローカル編)

こちらの記事のローカル編です。
自分のPCがOS X High Sierraですが、gcc g++周りがLinuxと違う影響やその他諸々でうまくいっていません。
もし環境構築に成功された方がいらしたら情報提供、もしくはQiitaに記事をあげていただけると幸いです。
データ解析についてはBlockSciデータ解析編に書いてあります。

インストール

推奨環境のUbuntu 16.04を使ってやっていきます。
公式ドキュメントとほぼ同じですが公式にはまだ載っていない内容も多少混ざってますのでご注意ください。

sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
sudo apt-get update
sudo apt install build-essential cmake libssl-dev libboost-all-dev libsqlite3-dev autogen \
autoconf libcurl4-openssl-dev libjsoncpp-dev libjsonrpccpp-dev libjsonrpccpp-tools \
python3-dev python3-pip liblmdb-dev libsparsehash-dev libargtable2-dev libmicrohttpd-dev \
libhiredis-dev libjsoncpp-dev catch gcc-7 g++-7
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7

git clone https://github.com/bitcoin-core/secp256k1
cd secp256k1
./autogen.sh
./configure --enable-module-recovery
make
sudo make install

cd ~
wget https://cmake.org/files/v3.10/cmake-3.10.0.tar.gz
tar xzf cmake-3.10.0.tar.gz
cd cmake-3.10.0/
cmake .
make
sudo make install
exec bash

cd ~
git clone https://github.com/rescrv/HyperLevelDB
cd HyperLevelDB
autoreconf -i
./configure
make
sudo make install
sudo ldconfig

cd ~
git clone https://github.com/citp/BlockSci.git
cd BlockSci
git submodule init
git submodule update --recursive
sudo cp -r libs/range-v3/include/meta /usr/local/include
sudo cp -r libs/range-v3/include/range /usr/local/include

cd libs/bitcoin-api-cpp
mkdir release
cd release
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install

cd ../../..
mkdir release
cd release
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install

# 公式に載っていないインストールコマンド
cd clustering
mkdir release #ディレクトリがない場合に実行
cd release
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install
# ここまで

sudo -H pip3 install --upgrade pip
sudo -H pip3 install --upgrade multiprocess psutil jupyter pycrypto matplotlib pandas dateparser

clusterを生成するコマンドのインストールがReadmeに載っていないのですが2017/12/21 時点でプルリクエストが出されているので時期に公式に載るかもしれません。

bitcoindのインストール

インストールディレクトリは各自で決めてください。

sudo add-apt-repository ppa:bitcoin/bitcoin
sudo apt-get update
sudo apt-get install libdb4.8-dev libdb4.8++-dev

git clone https://github.com/bitcoin/bitcoin.git
cd bitcoin
./autogen.sh
./configure
make
sudo make install

以上でインストールは終了です。

参考URL

続きを読む

Dynamic DNS creation for core nodes in case of Autoscaling – AWS

パイソン & Linux Projects for $30 – $250. We wanted to implement the Dynamic creation/Deletion of DNS for nodes, when the AWS EMR cluster Autoscales up and down for Route53 using Autoscaling cloudwatch events that triggers a lambda function… 続きを読む