目次
概要
分析で使用する分析サーバー(pyspark-notebook)や分散基盤(EMR)の構築方法について、説明します。
はじめに
分析やモデルの作成をしていくための環境を作成していくフェーズになります。
分析をするために必要なライブラリやGPUの環境を整えていくことがメインになりますが、
VPNサーバーや踏み台サーバー、分散処理サーバーなどもクライアントのセキュリティ要件やデータに合わせて、必要なサーバーを構築をしていきます。
要件と対応方針
基盤を構築するにあたり、要件と対応方針の例を示します。
こうした要件は、設計前段階で必要な要件についてはクライアントにヒアリングしておきます。
クライアントから出てこなかった要件についても、セキュリティ面などが抜け落ちていることもあり得るので、
コストと納期を相談した上でこちらから提案してあげる必要があります。
- 分析データ、作成コードは、ローカルPC上にダウンロードができないこと
→ 踏み台サーバー上からのみ分析環境にアクセスを可能にする。 - アクセスは指定したネットワーク内に限ること
→ VPNサーバーを構築し、VPN経由して環境にアクセスをする - 分析環境から外部ネットワークへの接続ができないこと
→ オフラインでもライブラリの追加等の運用作業ができるようにする。 - 1ファイル数TBサイズのデータを分析できること
→ 分散基盤を作成し、ビッグデータ分析にも対応できる環境を作成する。
今回は、セキュリティに関する要件以外に、PoCでクイックに環境を作成する必要があるという前提条件を追加し、
Amazon Web Serviceを使用した分析環境の設計をします。
GCPやAzureでも良かったのですが、
構築でスタックしたときに、記事の件数の多さと一番慣れているからという理由でAWSを選択しました。
設計例
以下、要件をもとに作成した分析基盤の概要図とAWS構成図になります。
第三者がデータやコードに絶対にアクセスできないようにセキュリティを高めにし、
分析者は、分析用のコードを書く以外、裏側の環境のことは気にしなくてもいい設計にしました。
【構成図】
VPN,踏み台2つのサーバーを経由させることで、
分析サーバーおよび保存されたデータへのアクセスへのセキュリティを強化しています。
VPNサーバーへのアクセスに、GoogleAuthencatorを用いた多段階認証を導入することで、パスワードの共有などによる許可された分析者以外の第3者のログインを防ぐことが出来ます。
ローカルサーバー上にデータのダウンロードを防ぐためには、踏み台とローカルPCとの間でコピペができないようにWindowsServerの設定をする必要があります。
ちなみに、私が担当したプロジェクトは、さらに外部アクセスが必要な地図表示ライブラリを使用していたので、
別途マップタイルサーバーを構築して、完全にオフラインで地図表示ができるようにしました。
基盤の使い方
データ前処理の流れ
- 生データのアップロード
- AWSマネジメントコンソールまたは、AWSCLIを利用しS3バケットに生データを格納する。
- 前処理スタート
- CloudWatchでバケットへのアップロードを検知し、前処理開始用のlambdaをキックする。
- 前処理
- lambdaからEMRを呼び出し、Sparkでビッグデータの前処理をする。終了後、加工データ用のS3バケットに格納する。
Pandas等では、数TBのデータを扱うことが出来ないので、Spark+Hadoopの分散処理によって前処理をします。
AWSでは、分散基盤のマネージドサービスであるEMRがあります。
分析までの流れ
- ネットワークへの接続
- VPNサーバーの認証を通り、分析環境にVPN接続する。
- 踏み台サーバーへの接続
- VPN接続した状態で、作業用のWindowsがインストールされた踏み台サーバーにリモート接続する。
- 分析サーバーへの接続
- 踏み台サーバーから、ブラウザを経由してJupyterLabがインストールされた分析サーバーにアクセスします。
- 分析サーバーは、S3に保存されたコードとデータを参照し、保存することが出来ます。
- また、前処理用のコードをアドホックに作成するため、踏み台サーバーからJupyterがインストールされたEMRのマスターノードにアクセスをします。
EMRNotebookを利用すれば、もっと簡単に構築ができるかもしれませんが、今回の要件の一つに外部接続不可ということがあり、EMRにJupyterをプレインストールして、踏み台サーバー上からアクセスする手段をとりました。
今は、知識さえあれば、ポチポチと簡単にインフラ構築ができるようになったので、
エンジニアにしろデータサイエンティストにしろ、環境構築は必須のスキルであると思っています。
実装
ネットワーク
省略
VPNサーバー
VPNサーバーの構築方法については、
こちらの過去の記事が参考になると思います。
[OpenVPN]AWS上にVPNサーバーを構築してみる
踏み台サーバー
省略
分析サーバー
AmazonLinux2上にpyspark-notebookのDockerイメージを使って、Pysparkが使えるJupyterNotebookの環境を構築します。
- Dockerfile作成
jupyterlabを起動用のスクリプトになります。FROM jupyter/pyspark-notebook:latest # install additional python packages COPY requirements.txt /tmp/requirements.txt RUN pip install -U pip RUN pip install -r /tmp/requirements.txt # update conda RUN conda update -n base conda RUN conda install -y -c conda-forge nodejs # install Jupyterlab extensions RUN jupyter labextension install @jupyter-widgets/jupyterlab-manager RUN jupyter labextension install plotlywidget RUN jupyter labextension install jupyterlab-plotly RUN jupyter labextension install jupyterlab-chart-editor RUN jupyter labextension install @lckr/jupyterlab_variableinspector RUN jupyter labextension install @jupyterlab/toc
- docker-compose.ymlファイルの作成
awscliが使えるように環境変数にアクセスキーとシークレットキーを渡してあげています。version: "3.7" services: jupyterlab: build: /home/ec2-user/jupyterlab user: root container_name: jupyterlab environment: - GRANT_SUDO=yes - NB_UID=1000 - NB_GID=100 - TZ=Asia/Tokyo - JUPYTER_ENABLE_LAB=yes - AWS_ACCESS_KEY_ID=youraccesskey - AWS_SECRET_ACCESS_KEY=yoursecretkey ports: - "80:8888" volumes: - /home/ec2-user/jupyterlab/work:/home/jovyan/work command: start.sh jupyter lab --NotebookApp.token='' restart: always
- dockerとdocker-composeのインストール~起動
# update server programs sudo yum update -y # install docker fuse sudo yum install -y docker fuse # running docker sudo service docker start sudo systemctl enable docker # install docker-compose sudo curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-<code>uname -s</code>-<code>uname -m</code> -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose # execute docker-compose docker-compose up -d --build`
分散基盤
EMRを使って分散基盤を構築します。
分散基盤の設定用のファイルはS3に保存しておき、起動時にオプションで呼び出します。
- IPアドレスの固定(サブIPの付与)
assign_private_ip#!/usr/bin/python # #Copyright 2017-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. # #Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file #except in compliance with the License. A copy of the License is located at # # http://aws.amazon.com/apache2.0/ # #or in the "license" file accompanying this file. This file is distributed on an "AS IS" #BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the #License for the specific language governing permissions and limitations under the License. import sys, subprocess is_master = subprocess.check_output(['cat /emr/instance-controller/lib/info/instance.json | jq .isMaster'], shell=True).strip() if is_master == "true": private_ip = str(sys.argv[1]) instance_id = subprocess.check_output(['/usr/bin/curl -s http://169.254.169.254/latest/meta-data/instance-id'], shell=True) interface_id = subprocess.check_output(['aws ec2 describe-instances --instance-ids %s | jq .Reservations[].Instances[].NetworkInterfaces[].NetworkInterfaceId' % instance_id], shell=True).strip().strip('"') #Assign private IP to the master instance: subprocess.check_call(['aws ec2 assign-private-ip-addresses --network-interface-id %s --private-ip-addresses %s' % (interface_id, private_ip)], shell=True) subnet_id = subprocess.check_output(['aws ec2 describe-instances --instance-ids %s | jq .Reservations[].Instances[].NetworkInterfaces[].SubnetId' % instance_id], shell=True).strip().strip('"').strip().strip('"') subnet_cidr = subprocess.check_output(['aws ec2 describe-subnets --subnet-ids %s | jq .Subnets[].CidrBlock' % subnet_id], shell=True).strip().strip('"') cidr_prefix = subnet_cidr.split("/")[1] #Add the private IP address to the default network interface: subprocess.check_call(['sudo ip addr add dev eth0 %s/%s' % (private_ip, cidr_prefix)], shell=True) #Configure iptablles rules such that traffic is redirected from the secondary to the primary IP address: primary_ip = subprocess.check_output(['/sbin/ifconfig eth0 | grep \'inet addr:\' | cut -d: -f2 | awk \'{ print $1}\''], shell=True).strip() subprocess.check_call(['sudo iptables -t nat -A PREROUTING -d %s -j DNAT --to-destination %s' % (private_ip, primary_ip)], shell=True) else: print "Not the master node"
- 追加のPythonライブラリインストール
bootstrapping.sh#!/bin/bash sudo python3 -m pip install pandas scikit-learn seaborn
- S3とjupyterのマウント
config.json[ { "Classification": "jupyter-s3-conf", "Properties": { "s3.persistence.enabled": "true", "s3.persistence.bucket": "yours3backet" } } ]
- EMR起動
awscliを利用してEMRを起動します。aws emr create-cluster \ --name emr-cluster \ --applications Name=Hive Name=Hue Name=Hadoop Name=Spark Name=JupyterHub Name=Ganglia \ --configurations https://yours3backet/config.json \ --release-label emr-5.27.0 \ --ec2-attributes SubnetId=yoursubnet,KeyName=yourkeyname,InstanceProfile=yourInstanceProfile \ --service-role yourRole \ --instance-groups InstanceGroupType=MASTER,InstanceCount=1,InstanceType=r4.4xlarge InstanceGroupType=CORE,InstanceCount=10,InstanceType=r4.4xlarge \ --no-termination-protected \ --log-uri s3://yours3backet/logs/ \ --bootstrap-actions Path=s3://yours3backet/assign_private_ip.py,Name=assign_private_ip,Args=["10.0.0.100"] Path=s3://yours3backet/bootstrapping.sh,Name=install_jupyter_lib
最後に
システム開発の経験がないデータサイエンティストの方々は、
インフラ部分の環境構築を苦手とする方々が多いかと思います。
環境の構築は、エンジニアの方に任せてしまえば良いという考えの方もいるかもしれませんが、
実際に環境を使う人が構築までできた方が良いに越したことがありません。
人員のリソースが豊富な会社であれば、作業の分業化が可能であれるかもしれませんが、
ベンチャーであったり、フリーランスになるとそもそも人がいないため、
環境構築から全て一人で担当できるスキルも必要になります。
環境構築は、市場での価値を高めてく上でも
他のデータサイエンティストとの差別化ができる為、使えるスキルだと思っています。