データエンジニア日記

データエンジニアの雑記

「データエンジニアの始まり」というエントリーを読んで

データエンジニアの始まり(翻訳) - satoshihirose

というエントリー(翻訳)を読んで考えたことをメモ。 ちなみに元エントリーは

https://medium.freecodecamp.org/the-rise-of-the-data-engineer-91be18f1e603

未だにデータエンジニアとデータサイエンティストの違いがわからない方はとりあえず読んでいただければと思います。

データエンジニアの領域って、広く考えていくといくらでも広く定義することが可能ですよね。 たとえば、世の中のデータエンジニアの雇用募集を眺めると HadoopRDBやインフラ周りの細かい設定を熟知していることを求めるJobdescriptionもあるでしょうし、業務寄りや「ビジネスとデータをつなげる」なんて方向だったりすることもあったり、特定のETLツールに習熟していることを求める場合もあったり、はたまたデータガバナンス寄りの調整役的なポストもあったりと、様々です。

ETLツールの変革について

これは僕も常々感じておりました。「ドラッグアンドドロップ型のETLツール」と「コードや設定ファイルドリブンのツール」の対比。 自分は前者ではTalendを使いますし、一方後者ではDigdagを利用しております。今手がけているシステムでは併用しています。

基本は後者のツールの方がバージョン管理もできるし、シンプルに記述できる要件であれば、こちらのほうがよろしいかと思います。 ただ、(人にもよりますが) 「入れるデータを選べないデータエンジニアは」 という雑用係的(笑)立場である場合、複雑な要件でデータを受け取ることもあります。 たとえば3つの違うテーブルのデータCSVファイルを一つ(!?)のファイルに圧縮したものを渡さえれたりとか。。。 こういうのをコード系のものだけで書くと、かなりスパゲッティな感じなって、どういう処理をしているのか後から追いにくくなります。

自分はこういう未整理な要件のものにはGUIドラッグアンドドロップ側のツール(Talend)を用いたほうが良いと考えます。 インプット・アウトプットのDBや入れ物が2,3種類だけであれば後者だけで結構洗練されたシステムも作れると思いますけど。

データエンジニアは図書館員

このたとえは面白いと思いました。たしかに図書がどこにあるのか?無ければ他の図書館から引っ張ってくるとか、ほんの読み方を教えてあげたりとか、、、たしかに図書館員っぽい仕事ですね!

Docker版のJupyter-notebookとRStudioのdocker-composeの例

(今回はデータエンジニアというより、分析基盤エンジニア的なエントリーです)

自分の担当システムでは解析者用にRStudioとJupyterNotebookをDockerコンテナで提供している。 (原始的にEC2にDockerとdocker-composeをインストールして、その上に都度コンテナを構築している)*1

人ごと(Containerごと)にdocker runしていると、整理がつかなくなったり、管理が大変になったり、一斉起動、停止をしたい時に大変なので、docker-compose.ymlにサービスごとに記述してコンテナを建てている。 命名規則を合わせたり、共通の設定を書けたりするので、個々にdocker runするのに比べて設定漏れが少なくなる。

以下、jupyter/tensorflow-notebookのイメージを使用したコンテナの立ち上げdocker-compose.ymlの例

version: '2.1' #最新バージョンは3だが、リソース制限の指定の表記が対応していないので、2.1にしている。 
services:
  jn_1: #サービス名
    build: 
      context: ./jn_1/ docker-composeの下にdockerfileを置くディレクトリ「jn_1」を配置 
      dockerfile: dockerfile #dockerfile名を指定
    image: jn_common_1_image:1.0 #イメージを指定(既に同じイメージができていたらそこからコンテナが作られる)
    container_name: jn_1 #コンテナ名をつける。(ここではサービス名と合わせている)
    cpu_quota: 50000 #CPUの利用率制限(全体が100000とした場合の数を指定。例えば50000なら50%に制限される)
    mem_reservation: 10000m #メモリの利用バイト数制限(この場合は1コンテナが使える最大サイズを10Gバイトに制限)
    logging: #ここ以下で、dockerのこのコンテナのログのファイルの最大サイズを指定する。
     driver: "json-file"  #形式をjsonにしないと、最大サイズ制限がかけられないのでこうしている。 
     options:
       max-size: "10m" #dockerのログファイルは際限なく大きくなることがあるので、制限を設けるようにしておく。
       max-file: "3" #適当な数でrotateするようにしておく。
    ports:
      - "8002:8888" #割り当てるホスト側のポート:Jupyterの場合は8888固定
    volumes:
      - /home/jupyter:/home/jovyan/work #ここの利用者が作成するプログラムやデータはworkというディレクトリを作りそこマウント
#コンテナ内にプログラムなどを置くと、コンテナを消した時に一緒に消えてしまうので注意
    tty: true
    stdin_open: true #これと上の行は、コマンドラインから入る時に必要
    command: [start-notebook.sh, --NotebookApp.token=''] #特にパスワードを設定しないで、Jupyterを利用する場合はこう。
    user: root #下のGRANT_SUDOを利用する場合は必要
    environment:
      - GRANT_SUDO=yes #ユーザにコンテナ内でのOS設定を操作する権限を与えて良い場合はこのように設定
      - NB_UID=1013 #Linux上の場合は、ホストのユーザアカウントをここに指定しておく
      - NB_GID=1001 #Linux上の場合は、ホストのユーザアカウントをここに指定しておく

RStudioの場合のdocker-compose.ymlの例は以下 (rockerのrocker/tidyverseのイメージを用いた場合)

version: '2.1' #最新バージョンは3だが、リソース制限の指定の表記が対応していないので、2.1にしている。 
services:
  rs_1:
    build: 
      context: ./rs_common_1/
      dockerfile: dockerfile
    image: rs_common_1_image:1.0
    container_name: rs_1  
    cpu_quota: 50000
    mem_reservation: 10000m
    logging:
     driver: "json-file"
     options:
       max-size: "10m"
       max-file: "3"
    ports:
      - "8051:8787" #割り当てるホスト側のポート:RStudioのrockerパッケージの場合は8787固定
    volumes:
      - /home/rstudio:/home/rstudio/work #個人ディレクトリをマウント
    tty: true
    stdin_open: true
    environment:
      - ROOT=TRUE #管理者権限を持たせる場合(コンテナ内のOSを弄らせる場合はTRUEにしておく)
      - USERID=1013
      - GROUPID=1001

こちらはcommand指定は不要。何も指定しなくても、RStudioが立ち上がっている。

留意点配下に箇条書き、

  • Mac版のDockerでは権限はよしなに変換されるが、Linux版だと指定をしないとバンバンrootとかホスト上に無いUIDでファイルが作られる事があるので注意。利用者のホスト上のアカウントと同じUIDを指定しておくのがベスト。
  • MemoryやCPUを制限するところは、docker-composer.ymlのバージョン3ではSwarm版のみに対応となっているので、無効になってしまう(未検証?)
  • コンテナ内はステートレスに保つため、マウントしたボリューム内でのみ、プログラムやデータファイルを置くようにする。さもないとコンテナを削除した際に同時に消えてしまう。*2
  • docker logsで見れるログファイルに制限をかけないと、際限無く大きくなることがあるので、この辺の制限を入れるようにすること。

RStudioとJupyterNotebbokのdockerfileの例とdockerfileの注意点は次エントリーにて。

*1:将来的にはEKSとかEFSを組み合わせてモダンな作りに出来ないか?とか考え中・・・

*2:難しい言葉で言うと「データの永続化領域を設定する」という表現になる

EC2(EBS)のサイズ拡張が簡単になっていた件

会社で使っているサーバ(EC2)の容量がFullになっていた。 ここで動いているRStudioServerにブラウザ経由でログインできなくなったことにより発覚。*1

そこでそのEC2にアタッチされているEBSの拡張をしようと思った、、、のだが、

  1. EC2を停止

  2. 該当EBSのスナップショット取得(1時間ぐらい)

  3. スナップショットからサイズを大きくしたEBSを取得

  4. EC2から元のEBSをデタッチ

  5. 拡張した方のEBSをEC2にアタッチ

  6. EC2を起動

としなくてはいけないのかと思っていた(実際に昔この手順をしたことがある)。これはEBSの大きさにもよるが、小一時間程度かかる。

当然この間サーバは使えない。

ところが、以下のような簡単な操作でEBSのサイズを変えれるようになっていた!

dev.classmethod.jp

aws.amazon.com

以下の手順で早速やってみた。

AWSコンソール上での作業

上記のクラスメソッドのページの手順の通り。

今回は500Gのst1のEBSを1000Gにしたが、一瞬で完了した。 EBSをデタッチ→アタッチする手順に比べると大違い。EC2の高負荷時は避けた方がいいらしいので、出来れば凪な時間帯にやるのがいいだろう。今回は利用者側から特に不具合報告はなかった。

注意としてはAWSのWeb管理コンソール上のEBSのブロックデバイス名とLinuxコンソール上の各種コマンド(dfとか)で見たデバイス名は微妙に異なるので注意(作業対象のEBSを間違えないように)

docs.aws.amazon.com

事後にLinuxコンソール上でやらないといけない操作

Linuxコンソールでログインした後、

lsblk

で見ると

/dev/xvda1         40188  13461     26629  34% /
devtmpfs           15325      1     15325   1% /dev
tmpfs              15334      0     15334   0% /dev/shm
/dev/xvdc        1007805 488568    473122  51% /home

df -h で見てもサイズは元のままである。

ここでは

sudo resize2fs /dev/xvdc

のように作業対象のFileSystemをリサイズしてあげる必要がある。*2

事後にdfで確認したら、たしかにサイズが変わっていた。

Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G   14G   27G  34% /
devtmpfs         15G   60K   15G   1% /dev
tmpfs            15G     0   15G   0% /dev/shm
/dev/xvdc       985G  478G  462G  51% /home

docs.aws.amazon.com

まとめ

今後は気軽(?)にEBSのサイズの拡張ができる。 注意点としては、縮小はできません。縮小するとなると、最初に述べた「デタッチ→アタッチ」の手順しか無さそう。

dev.classmethod.jp

*1:ちゃんと監視入れとけというツッコミがありそうですが、まったくもってその通り

*2:要root権限

docker-composeメモ

Docker-compose系のコマンドのまとめ

docker-composeファイルを置いたディレクトリで操作する。

全サービスのアップ

Webサービスなどは-dオプションをつけて

docker-compose up -d

とする。全てのサービスが立ちあがる。 (正確にはコンテナを【作成】して【起動】する)

全サービスの停止

docker-compose down

で全サービスが停止する。

サービスごとの動作

サービスごとに操作したい場合(例えば他の本番サービスが起動中に、docker-compose.ymlを編集追記して、新たなサービスを定義した後、それを立ち上げたい場合)

サービスを作成してから起動を行う。

サービス名を指定しての作成

docker-compose create [service-name]

(補足)create使用時は以下のようなWariningが出る。

WARNING: The create command is deprecated. Use the up command with the --no-start flag instead.

サービス名を指定しての起動

docker-compose start [service-name]

サービス名を指定しての停止

docker-compose stop [service-name]

サービス名を指定しての削除

docker-compose rm [service-name]

サービス名を指定しての再起動

docker-compose restart [service-name]

補足事項

全体の

docker-compose up

と 個々の

docker-compose create

docker-compose start

は完全にイコールではない。最初にupする時にネットワークなども立ち上がるため。 このため、一つもコンテナが動いていない状態の時は最初は

docker-compose up

で立ち上げたほうがよい。

設定ファイルの名前を変えたい時

docker-compose.ymlや配下のdockerfileの名前を変えたい時は以下のサイトの設定を参考にする。

qiita.com

IAMRoleを使ったアクセスの有効時間が変更可能になった

元ネタは以下

aws.amazon.com

日本語解説はクラスメソッドさんのところより

dev.classmethod.jp

むしろ、自分はここで衝撃の事実 【IAMRoleを通してのアクセス許可は1時間だった】 ということを知りました!

RedshiftでCOPYやUNLOADコマンドを打つ時にS3への認証情報が必要になりますが、二通りの設定方法があります。

キーベースのアクセスコントロール

https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/copy-usage_notes-access-permissions.html#copy-usage_notes-access-key-based

アクセスキーとアクセスシークレットというIAMで発行した2つの文字列の組み合わせで認証情報を指定します。 たとえば以下のように指定します。

CREDENTIALS 'aws_access_key_id=<access-key-id>;aws_secret_access_key=<secret-access-key>';

ロールベースのアクセスコントロール

https://docs.aws.amazon.com/ja_jp/redshift/latest/dg/copy-usage_notes-access-permissions.html#copy-usage_notes-access-role-based

IAMRoleを指定して、認証します。以下のようにRedshiftに付与したIAMRole(S3へのアクセス権限を持っているとする)の名前を指定します。

COPY文の中では

copy customer from 's3://mybucket/mydata'
iam_role 'arn:aws:iam::0123456789012:role/MyRedshiftRole';

copy customer from 's3://mybucket/mydata' credentials 'aws_iam_role=arn:aws:iam::0123456789012:role/MyRedshiftRole';

のように書きます。

こちら

一時的セキュリティ認証情報は、COPY および UNLOAD 操作の期間全体で有効にする必要があります。 一時的セキュリティ認証情報の期限が操作中に切れた場合、コマンドは失敗し、処理はロールバックされます。たとえば、一時的セキュリティ認証情報の期限が 15 分後に切れるときに COPY 操作に 1 時間かかる場合、COPY 操作は完了前に失敗します。ロールベースのアクセスを使用する場合、一時的なセキュリティ認証情報は操作が完了するまで自動的に更新されます。

のように書いてある。。が、Spectrum上のテーブルへの問い合わせが1時間を超えると、AccessDeniedエラーで止まることがある。

よって、今回、IAMRoleの有効時間を伸ばして変更して、試してみようと思う。(結果は別エントリーにて報告予定)

あるマシンから別のEC2へポートフォワード(トンネリング)したい場合のsshコマンド

以下のような要望があったとする。

  • あるEC2(Linux)上のあるポート番号で動いているWebアプリケーションにつなぎたい。(例として9001番ポートとする)
  • セキュリティ上、10022ポートでのSSH接続しか許可されていない。
  • このEC2へのSSHログインはできる。(アカウントと鍵ファイルは所有している)
  • 接続用鍵ファイルはローカルの /[ホームディレクトリ]/.ssh/ 以下に置いてある。(権限は所有者のみ読み書き可に設定してある)

この場合以下のSSHコマンドで接続を確立すると、この接続が繋がっている間、ポートフォワーディングで繋がる。

ssh -p 10022 -i ~/.ssh/[キーファイル] -L 19001:localhost:9001 ec2-user@[EC2のIPアドレス]

  • -pオプション SSH接続先のポート番号。この場合は10022。デフォルトで何も指定しないと22番。よって、22番がSSH接続のポートとなっている場合は、このオプションは必要無い。

  • -i オプション::接続鍵ファイルを指定

  • -L オプション:: ローカルポート:接続先ホスト:接続先のポート という表記で書く。たとえば、上記だと「ローカルマシン上のブラウザでlocalhost:19001に繋ぐ」という行為が、「ホストマシン上のブラウザで localhost:9001で繋ぐ」という行為と同等となる。(接続がワープするようなイメージ)

  • ec2-user@[EC2のIPアドレス]   ::通常のSSHコマンドと同じ、「接続先ホストへ繋ぐユーザアカウント@ホストIPアドレス」で接続先を指定

Incrementさんのリモートワークのガイドラインがいい感じだった

zine.qiita.com

こちらの真ん中あたりに 「リモートワークのガイドライン」 というのが実際に載っている。これ非常にいいですね。

議論のログを残すようにしましょう。特に、何らかの意思決定や問題解決が行われた場合などには、その過程を書き残しておくことで誰かの役に立ちます。

この辺とか頷ける。

  • コミュニケーション方法に気を配りましょう
    • ツールを使い分けましょう
    • すぐに応答してほしいかどうかという緊急度に基いて、チャット、Qiita:Team、メール、電話など、適切なツールを選びましょう。一口にチャットと言っても、チャンネルの適切さやメンションの有無などにも配慮しましょう。
    • なおチャットなどの通知は受け取り手が適切に制御してくれていると考え、必要であれば遠慮せずにメンションなどを使いましょう。仮にチャットでメンションしたとしても、即座に反応がもらえることは強くは期待しないようにしましょう。緊急時は緊急連絡(別途用意する)を参照してください。
    • 通知は受け取る側がSlackのDo Not Disturb機能などを利用し適切に制御することが前提ですが、GitHubやQiita:Team、Slackの#status_xxxチャンネルなどをうまく使ってあげると受け取り手に優しいですね(特にインフラ担当メンバーに対して)。
  • 同期的なやり取りはコアタイムに寄せましょう
    • 弊社では現状13時から17時をコアタイムとしているので、会議などはこの時間中にやりましょう。
  • きちんとレスポンスを返しましょう
    • メンションやカレンダーの招待にはきちんと返事しましょう。
    • Chatは口頭と違い、話しかけた時に相手に伝わっているかはわかりません。相手から何も反応がない場合、話しかけた人には相手が「気付いていない」のか「伝わったが回答に悩んでいる」のか「伝わったけれども反応していないだけ」なのかわかりません。一言のレスや絵文字リアクションなど、簡単にでもいいので反応しましょう。
    • またカレンダーの招待もきちんと参加可否を回答しましょう。ミーティングを開催する人は誰が参加するのかを把握しておく必要があります。参加するか未定であっても、Maybeと回答し、招待されたことは把握していることを招待者に伝えましょう。

こういうのを全ての社員が徹底すれば、生産性が上がるのに!と思いました。 Googleカレンダーの会議招待に返答しない人とか、チャットで何かしらの返事をしない人とか、周りの社員の生産性を著しく下げている事に気づいて欲しい。

チケット駆動、チャットベース、Googleカレンダーベースの行動の3つだけでも多くの企業が取り入れれば、日本の生産性は大きく改善すると思うのだけれど。