woshidan's blog

そんなことよりコードにダイブ。

AWS DevDay Tokyo 2018 LT大会に「AWSを用いたWebサービスの負荷試験のTips」というテーマで参加してきました

9月にLTのCFPの公募があり、これはいい勉強になるだろうということで応募したら通ったのでわたわたしながら準備していました。

speakerdeck.com

以下、とっちらかっていますが、自分用のメモ兼補足です。

  • JMeterが同時に1000スレッドを立ち上げたらフリーズした件
    • JMeterJava製の負荷試験用ツールなんですが、2~3GHzの1つのクライアントだと同時に300~600くらいまででWebサーバの場合は例外*1とのことですが、約3GHzの仕事用MBPだとこの辺で止まりました。メモリじゃなくてクロック数書いた方がよかったかもです
    • 実際のテストケースでは、一度に立ち上げるスレッド数を制限して、かわりに短いテストシナリオを短時間でたくさんループして1分あたりのスレッド数*2を増やしています
  • 負荷用クラスタの構成あたりの話について
    • 3/10の量のリクエストが送れるかどうかのテストをしていた際に心配していたこと
      • これで3/10のリクエスト数が送れなかったらAWSアカウントで利用できるEC2のインスタンスタイプごとの制限台数*3にひっかかるからインスタンスタイプあげなきゃ...
      • マスターとスレーブの間の通信でマスターが詰まらないか
        • JMeterのスレーブはテストの最中、制御に必要な情報(グラフリスナーなどが必要とする情報も含むっぽい)をマスターに送っているので、それによってマスター/スレーブ間が詰まらないか*4
        • 結論からいうとCUIモードならだいたい大丈夫ではと思います
      • スレーブから対象クラスタへの通信でネットワーク帯域が詰まらないか
    • なぜEC2を利用しているのか
      • 並列数が欲しいだけならLambdaでもいいのでは、という感じなのですが、負荷試験で調べたいことにElastiCache, RDSとEC2のクラスタとの間のネットワーク転送量があり、ある程度キャッシュが効いた状態の様子を見たかったので
        • キャッシュが効くまでに5分くらい暖機数分をとりたかったことと
        • 上記の暖機をした上で本番に近い形でキャッシュが効くようなデータセットを続けて利用することを考えると、5分の稼働時間のLamdbaで設定やテスト用データの用意をするのはややこしそうです。EC2なら動かしっぱなしでいいので
        • ランダムだったり固定データだったらLambdaでもよさそう
  • 負荷試験用環境のセットアップについて*5
    • ストレージ容量が4桁GiBになってくるとバックトラック(弊社の試験時にうまくいかず。。)がうまくいかないこともあるみたいですね
    • 書いていない工夫としてはバッチの結果が入っているRedisがあって、そのデータを用意するため、バッチの実行時間のAuroraのスナップショットとバッチ実行後のRedisのスナップショットを用意していました
      • 試験当時はAuroraが停止できなかったのでクローンでインスタンス立ち上げた後停止させるわけにはいかなかったので。。

現場からは以上です。

*1:https://jmeter.apache.org/usermanual/jmeter_distributed_testing_step_by_step.pdf

*2:JMeter(に限らないかもですが)での同時に動かすスレッド数はだいたい同時にアクセスしているユーザ数みたいな感じ

*3:https://aws.amazon.com/jp/ec2/faqs/#How_many_instances_can_I_run_in_Amazon_EC2

*4:https://jmeter.apache.org/usermanual/jmeter_distributed_testing_step_by_step.pdf

*5:質問されたのですが、試験環境のセットアップも自分でやってました。だいなみっく!

JAWS-UG 初心者支部#14「AWS Night school & LT」 LT大会 に「 IAMロールの使用と必要なポリシー」というテーマで参加してきました

結構前なのですが、仕事ともう一個のLTでバタバタしていていまさらになりますががが。

speakerdeck.com

自分がはじめてIAMさわったとき、IAMのポリシーってたくさんあってどれがどれだかわからないぞって気持ちが強くて。。

IAMのポリシーにつく形容詞には

  • ロールの利用に関するもの(信頼ポリシーなど、スライド参照)
  • 誰が作成しているか(管理ポリシーとか)
  • 何にアタッチするか(サービスのリソース vs プリンシパルエンティティ)

の軸があって、そのうち1つだけでもさらっておくと一度に覚える事が減って後でIAMのドキュメント見るとき読みやすくなりそう、と思って今回のLTで話す範囲を決めました。

なお、スライド中で言っているSTSが出てくるドキュメントは以下となります。

docs.aws.amazon.com

かんたんですが、他に参考になりそうなドキュメントをまとめて現場からは以上とします。

参考

Hadoop3.1.1でs3a://ファイルシステムを利用する場合の環境構築をしてみる

今日はHadoopファイルシステム(特に、s3を利用するもの)についてすこしおさらいしてから表題の環境構築をしてみます。

Hadoopファイルシステム

Hadoopは抽象化されたファイルシステムの概念を持っていて、その実装に

など、さまざまなストレージをバックエンドとした実装があり、今日扱うS3をバックエンドとするファイルシステムもこの中に含まれます。

ただ、Hadoopで扱えるファイルシステムのうち、S3をバックエンドとするものにはいくつか種類があり、ややこしいので動かすための設定を見ていく前にかんたんに整理しておきます。

Hadoopのs3をバックエンドとしたファイルシステム

詳しい歴史的経緯はこちらのドキュメントの冒頭やこちらの記事が参考になりますが、Hadoopのs3用のファイルシステムを表にすると以下のようになります。

ファイルシステムURIスキーマ*1 備考
s3 最初に作成されたs3をバックエンドとするファイルシステム。このファイルシステムで作成されたファイルはHadoopのs3ファイルシステムを通してしか読み書きできなかった。現在はもうApacheによる開発は終了しており、ソースコードにも含まれていない。下の方で述べるEMRのs3ファイルシステムとは別物。
s3n 上述のs3ファイルシステムの問題点を解決し、アプリケーションのコードとHadoopの間でS3のオブジェクトを共用できるようにしたもの。Hadoopの2.x系で利用することができた
s3a s3nより、より大きいファイルをより高いパフォーマンスで扱えることを目的に開発された第三世代。改善内容は、たとえば、部分的なシークの追加など。Hadoop 3.x系で利用できる
s3(Amazon版) s3nが開発されている頃からAmazonが独自にEMR用に改良したファイルシステム。現在s3のファイルシステムで開発されているものはこちらであり、Hadoopコミュニティが開発していたs3ファイルシステムとは別物

それでは、今日はs3aプロトコルを利用する場合の環境構築をしていきます。

Hadoopでs3a://ファイルシステムをPseudo-Distributed Modeで動かしてみる場合の環境構築について

s3にHadoop用のファイルを置くバケットを作成

f:id:woshidan:20180920082701p:plain

なお、バケットには管理者のみがアクセスできるようにします。

バケットを作成したら、テスト用のCSVもアップロードしておきます。

そのCSVの中身は以下です。

dog
dog
dog
cat
cat
dog
dog
cat

Hadoopの設定

Hadoopの認証情報とファイルシステムの実装クラスを設定する

次にHadoop側の設定をしていきます。まず、 core-site.xml の変更内容ですが以下になります。

<!-- etc/hadoop/core-site.xml -->
<property>
  <name>fs.s3a.access.key</name>
  <value>MY_ACCESS_KEY</value>
</property>

<property>
  <name>fs.s3a.secret.key</name>
  <value>MY_SECRET_KEY</value>
</property>

etc/hadoop/core-site.xml の変更はずいぶん少ないですが、これは今回s3a系のファイルシステムを利用する際の設定のうち、

についてHadoopのデフォルトの設定を利用しているためです。

まず、Hadoopのデフォルトの設定については、 https://hadoop.apache.org/docs/r3.1.1/hadoop-project-dist/hadoop-common/core-default.xml に記載されていて、fs.s3a.impl の値は org.apache.hadoop.fs.s3a.S3AFileSystem になっています。

また、 fs.s3a.aws.credentials.provider の値の設定に関しては https://hadoop.apache.org/docs/r3.1.1/hadoop-project-dist/hadoop-common/core-default.xml にも記載されていますが、s3のファイルシステムの設定について説明しているHadoop-AWS module: Integration with Amazon Web Servicesのページの方がわかりやすいです。

Hadoop-AWS module: Integration with Amazon Web Servicesによると、s3a://ファイルシステムを利用する場合、

  • 認証方法の指定は credential provider クラスの指定という形で行い、複数指定することもできる
  • 特に認証方法を指定しない場合、デフォルトで指定されている認証方法が用いられる
    • デフォルトで指定されている credential provider は以下の順で試される
      • org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider ... core-site.xml に記載した fs.s3a.access.keyfs.s3a.secret.key を利用して認証を行う
      • com.amazonaws.auth.EnvironmentVariableCredentialsProvider ... 環境変数 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY を利用
      • com.amazonaws.auth.InstanceProfileCredentialsProvider ... EC2のインスタンスプロファイルの認証情報を利用

となっています。

そして、今回はデフォルトの認証方法のうち、org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider を利用する前提の設定となっているので、 core-site.xmlfs.s3a.access.keyfs.s3a.secret.key だけ記載しています。

HadoopからAWSを利用するためのクラスのパスをクラスパスに追加

hadoop から s3 を利用するためには $HADOOP_HOME/share/hadoop/tools/lib/ 以下にある hadoop-aws-3.1.1.jar が必要なので、 etc/hadoop/hadoop-env.sh に以下を追記します。

# etc/hadoop/hadoop-env.sh
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HADOOP_HOME/share/hadoop/tools/lib/*

その他

また、 s3a:// プロトコルを利用することとは関係ないですが、以下の設定もしていました。

<!-- etc/hadoop/hdfs-site.xml -->
<!-- 今回もおためしなのでレプリケーションは作りません -->
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>

コマンドを実行してみる

$ $HADOOP_HOME/bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar wordcount s3a://woshidan-hadoop-test/word_count_test.csv wordcount-output-s3
$ $HADOOP_HOME/bin/hdfs dfs -cat wordcount-output-s3/*
cat 3
dog 5

よさそうです。

トラブルシューティング

bin/hadoop fs -ls s3a://bucket_name を実行したら ls: `s3a://bucket_name': No such file or directory というエラーメッセージが返ってきた

https://hortonworks.github.io/hdp-aws/s3-s3aclient/index.html のコマンドを眺めていたら s3a://bucket_name/ のように末尾に / が必要なようでした。

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop fs -ls s3a://woshidan-hadoop-test
2018-09-20 23:14:46,289 INFO impl.MetricsConfig: loaded properties from hadoop-metrics2.properties
2018-09-20 23:14:46,384 INFO impl.MetricsSystemImpl: Scheduled Metric snapshot period at 10 second(s).
2018-09-20 23:14:46,384 INFO impl.MetricsSystemImpl: s3a-file-system metrics system started
2018-09-20 23:14:48,278 INFO Configuration.deprecation: fs.s3a.server-side-encryption-key is deprecated. Instead, use fs.s3a.server-side-encryption.key
ls: `s3a://woshidan-hadoop-test': No such file or directory
2018-09-20 23:14:48,543 INFO impl.MetricsSystemImpl: Stopping s3a-file-system metrics system...
2018-09-20 23:14:48,544 INFO impl.MetricsSystemImpl: s3a-file-system metrics system stopped.
2018-09-20 23:14:48,544 INFO impl.MetricsSystemImpl: s3a-file-system metrics system shutdown complete.

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop fs -ls s3a://woshidan-hadoop-test/
2018-09-20 23:20:24,680 INFO impl.MetricsConfig: loaded properties from hadoop-metrics2.properties
2018-09-20 23:20:24,798 INFO impl.MetricsSystemImpl: Scheduled Metric snapshot period at 10 second(s).
2018-09-20 23:20:24,798 INFO impl.MetricsSystemImpl: s3a-file-system metrics system started
2018-09-20 23:20:26,691 INFO Configuration.deprecation: fs.s3a.server-side-encryption-key is deprecated. Instead, use fs.s3a.server-side-encryption.key
Found 1 items
-rw-rw-rw-   1 ec2-user ec2-user         31 2018-09-19 23:26 s3a://woshidan-hadoop-test/word_count_test.csv
2018-09-20 23:20:26,924 INFO impl.MetricsSystemImpl: Stopping s3a-file-system metrics system...
2018-09-20 23:20:26,925 INFO impl.MetricsSystemImpl: s3a-file-system metrics system stopped.
2018-09-20 23:20:26,925 INFO impl.MetricsSystemImpl: s3a-file-system metrics system shutdown complete.

org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider を fs.s3a.aws.credentials.provider に指定して fs -ls したらエラーになった

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop fs -ls s3a://woshidan-hadoop-test
2018-09-20 23:05:52,547 INFO impl.MetricsConfig: loaded properties from hadoop-metrics2.properties
2018-09-20 23:05:52,670 INFO impl.MetricsSystemImpl: Scheduled Metric snapshot period at 10 second(s).
2018-09-20 23:05:52,670 INFO impl.MetricsSystemImpl: s3a-file-system metrics system started
ls: org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider constructor exception.  A class specified in fs.s3a.aws.credentials.provider must provide a public constructor accepting Configuration, or a public factory method named getInstance that accepts no arguments, or a public default constructor.

fs.s3a.aws.credentials.provider に指定できる credential provider のクラスは

  • org.apache.hadoop.conf.Configurationインスタンスを引数にとるpublicコンストラクタを持つ
  • 何も引数を取らないgetInstance()メソッドを持つ
  • publicなデフォルトコンストラクタを持つ

という条件を満たす必要があります。

たとえば、他の credential provider のクラスを見てみると TemporaryAWSCredentialsProvider では

https://github.com/apache/hadoop/blob/da9a39eed138210de29b59b90c449b28da1c04f9/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/TemporaryAWSCredentialsProvider.java#L55-L57

  public TemporaryAWSCredentialsProvider(Configuration conf) {
    this(null, conf);
  }

のように、 Configurationインスタンスを引数にとるpublicコンストラクタを持っており、 AnonymousAWSCredentialsProvider では

https://github.com/apache/hadoop/blob/da9a39eed138210de29b59b90c449b28da1c04f9/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/AnonymousAWSCredentialsProvider.java#L40-L56

public class AnonymousAWSCredentialsProvider implements AWSCredentialsProvider {

  public static final String NAME
      = "org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider";

  public AWSCredentials getCredentials() {
    return new AnonymousAWSCredentials();
  }

  public void refresh() {}

  @Override
  public String toString() {
    return getClass().getSimpleName();
  }

コンストラクタが宣言されていないので、publicなデフォルトコンストラクタを使えます。一方、 BasicAWSCredentialsProvider クラスは

https://github.com/apache/hadoop/blob/da9a39eed138210de29b59b90c449b28da1c04f9/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/BasicAWSCredentialsProvider.java#L42-L45

  public BasicAWSCredentialsProvider(String accessKey, String secretKey) {
    this.accessKey = accessKey;
    this.secretKey = secretKey;
  }

のようにコンストラクタが定義されていますが、これは fs.s3a.aws.credentials.provider に指定できる credential provider のクラスの条件

  • org.apache.hadoop.conf.Configurationインスタンスを引数にとるpublicコンストラクタを持つ
  • 何も引数を取らないgetInstance()メソッドを持つ
  • publicなデフォルトコンストラクタを持つ

を満たしていないので、Basic と接頭辞がついているので試しに使ってみたい感じなのですが、 fs.s3a.aws.credentials.provider に指定することはできません。ですが、 fs.s3a.aws.credentials.provider に何も指定しなければ、デフォルトで利用されるため、今回はそうしています。

現場からは以上です。

*1:HadoopではUIスキーマで利用するファイルシステムの実装を指定する

Hadoop 3.1.1をインストールしてPseudo-Distributed Modeで動かしてみる

Java環境変数のセットアップ

$ sudo yum install java-1.8.0-openjdk

[ec2-user@ip-172-31-16-244 ~]$ sudo update-alternatives --config java

There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
   2           /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java

$ java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH
export PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/usr/java/latest/bin
# sudo update-alternatives --config java で出てきたパスに合わせてみる
export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk.x86_64
export HADOOP_HOME=/home/ec2-user/hadoop

Hadoopのインストール

$ wget http://ftp.jaist.ac.jp/pub/apache/hadoop/common/hadoop-3.1.1/hadoop-3.1.1.tar.gz
$ tar xvfz hadoop-3.1.1.tar.gz
$ ln -s hadoop-3.1.1 hadoop

$ cd hadoop
[ec2-user@ip-172-31-16-244 hadoop]$  ls
bin  etc  include  lib  libexec  LICENSE.txt  NOTICE.txt  README.txt  sbin  share
[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop
Usage: hadoop [OPTIONS] SUBCOMMAND [SUBCOMMAND OPTIONS]
 or    hadoop [OPTIONS] CLASSNAME [CLASSNAME OPTIONS]
  where CLASSNAME is a user-provided Java class

Pseudo-Distributed モードで動かすための設定

<!-- etc/hadoop/core-site.xml -->
<!-- fs.defaultFS には マスターノードの設定を行う
<!-- マスターノードがどのファイルシステムを使うのかをスキーマで指定したり、データノードがハートビートを送る先をIP + portで指定したりするらしい
<!-- https://stackoverflow.com/questions/36072890/what-is-the-exactly-use-of-defaultfs https://community.hortonworks.com/questions/185804/difference-between-fsdefaultfs-and-dfsnamenodehttp.html
<configuration>
+  <property>
+    <name>fs.defaultFS</name>
+    <value>hdfs://localhost:9000</value>
+  </property>
</configuration>
<!-- etc/hadoop/hdfs-site.xml -->
<!-- HadoopでHDFSを利用する場合、一つのファイル中のシークに時間がかからないようにデータをそれなりに分けて保存していて、
<!-- そういうクラスタに投入されたデータを保存しておく単位をブロックという。
<!-- そして、HDFSは耐障害性を高めるために、ブロック単位でレプリケーションを行うんですが、今回はテスト用だしレプリケーションいらないよね、という設定 -->
<!-- https://hadoop.apache.org/docs/r3.1.1/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml -->
<configuration>
+  <property>
+    <name>dfs.replication</name>
+    <value>1</value>
+  </property>
</configuration>

ローカルでネームノード、データノードを立ち上げるので、これらのssh通信用の設定

$ ssh localhost # localhostにsshで通信できないことを確認
The authenticity of host 'localhost (127.0.0.1)' can't be established.
ECDSA key fingerprint is SHA256:0STL9JFnI81GzdikYqN2DLu4BkdtY1Bb3A7o2zzkbpw.
ECDSA key fingerprint is MD5:c5:c0:cc:69:96:23:27:90:fa:d6:6b:13:1d:cf:16:b9.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'localhost' (ECDSA) to the list of known hosts.
Permission denied (publickey).

$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

chmod 0600 ~/.ssh/authorized_keys

ためしにジョブを実行するための準備

$ bin/hdfs namenode -format

$ sbin/start-dfs.sh
Starting namenodes on [localhost]
Starting datanodes
Starting secondary namenodes [ip-172-31-16-244]
ip-172-31-16-244: Warning: Permanently added 'ip-172-31-16-244,172.31.16.244' (ECDSA) to the list of known hosts.

$ bin/hdfs dfs -mkdir /user
$ bin/hdfs dfs -mkdir /user/ec2-user
$ bin/hdfs dfs -put etc/hadoop input
2018-09-18 22:34:53,634 WARN hdfs.DataStreamer: Caught exception
java.lang.InterruptedException
    at java.lang.Object.wait(Native Method)
    at java.lang.Thread.join(Thread.java:1252)
    at java.lang.Thread.join(Thread.java:1326)
    at org.apache.hadoop.hdfs.DataStreamer.closeResponder(DataStreamer.java:986)
    at org.apache.hadoop.hdfs.DataStreamer.endBlock(DataStreamer.java:640)
    at org.apache.hadoop.hdfs.DataStreamer.run(DataStreamer.java:810)
    ... # きになる...

exampleのGrep Jobがうまくいかなかった

$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar grep input output 'dfs[a-z.]+'
$ bin/hdfs dfs -cat output/*
$ bin/hdfs dfs -ls output/*
-rw-r--r--   1 ec2-user supergroup          0 2018-09-18 22:36 output/_SUCCESS
-rw-r--r--   1 ec2-user supergroup          0 2018-09-18 22:36 output/part-r-00000
$ bin/hdfs dfs -cat output/_SUCCESS
$ bin/hdfs dfs -cat output/part-r-00000
# 空

Hadoopを操作するユーザーの設定がよくわかってないせいかもしれないです。

exampleの円周率を計算するジョブはうまくいった

$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar pi 2 100
Job Finished in 2.099 seconds
Estimated value of Pi is 3.12000000000000000000

もともとやりたかったことはS3をバックエンドにしてHiveの素振りなので深追いはやめます*1

bin/hdfs dfs コマンドについて

refs: https://hadoop.apache.org/docs/r3.1.1/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html

bin/hdfs スクリプトを利用して、HDFSコマンドを実行することができます。

bin/hdfs dfs -mkdir などで作成しているディレクトリはHDFS上のもので、通常のシェルコマンドでは検索したり開いたりできませんが、 HDFS コマンドで -ls みたいに打ち込むとHDFS上のファイルを操作することができます。

$ bin/hdfs dfs -ls output/*
-rw-r--r--   1 ec2-user supergroup          0 2018-09-18 22:36 output/_SUCCESS
-rw-r--r--   1 ec2-user supergroup          0 2018-09-18 22:36 output/part-r-00000

word-countのexampleは成功した

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar wordcount input/hadoop-env.sh wordcount-output

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hdfs dfs -ls wordcount-output
Found 2 items
-rw-r--r--   1 ec2-user supergroup          0 2018-09-18 23:10 wordcount-output/_SUCCESS
-rw-r--r--   1 ec2-user supergroup      10073 2018-09-18 23:10 wordcount-output/part-r-00000
[ec2-user@ip-172-31-16-244 hadoop]$ bin/hdfs dfs -cat wordcount-output/*
"  3
"AS  1
"License");    1
"log   1
# 310
##    12
###   28
#export   1
$USER 1
${HADOOP_HOME}/logs   1
${HADOOP_OS_TYPE} 1
${HOME}/.hadooprc 1
...
wants 2
way   1
when  13
where 1
which 3
who   1
why....   1
wildcards 1
will  29
with  6
work  1
workers.sh,   1
writing   1
writing,  1
xxx-env.sh.   1
yarn  1
yet/still,    1
you   2
{YARN_xyz|HDFS_xyz}   1
{yarn-env.sh|hdfs-env.sh} 1

動いているHadoop関連のプロセスを止める

$ sbin/stop-all.sh
WARNING: Stopping all Apache Hadoop daemons as ec2-user in 10 seconds.
WARNING: Use CTRL-C to abort.
Stopping namenodes on [localhost]
Stopping datanodes
Stopping secondary namenodes [ip-172-31-16-244]
Stopping nodemanagers
Stopping resourcemanager

現場からは以上です。

*1:2.5.0で Fully-Distributed モードでGrep Jobが動かなかったらしいのでそういうこともあるかもしれないです... http://hadoop.apache.org/docs/r3.1.1/hadoop-project-dist/hadoop-common/release/2.7.0/CHANGES.2.7.0.html

Hadoop 3.1.1をインストールしてLocal Modeで動かしてみる

今日は https://qiita.com/Esfahan/items/39fd1e2f8b755eacec65 http://www.atmarkit.co.jp/ait/articles/0902/27/news129_2.html http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/SingleCluster.html あたりを参考にしつつHadoopのインストール作業の素振りをしてみます。

Hadoopをインストール

Hadoopのインストールの事前準備

HadoopJavaで動作するのでJavaのOpen JDKを入れる。

$ sudo yum install java-1.8.0-openjdk

# http://www.yunabe.jp/tips/linux_default_java_version.html を参考に利用するJavaのバージョンを切り替える。
$ sudo update-alternatives --config java

There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
   2           /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java

Enter to keep the current selection[+], or type selection number: 2

$ java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)

Hadoopの起動に必要な環境変数を設定。

[ec2-user@ip-172-31-16-22 ~]$ vim ~/.bash_profile 

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH
export PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/usr/java/latest/bin
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre
export HADOOP_HOME=/home/ec2-user/hadoop

JAVA_HOMEのパスが若干どこから出てきた感ありますが、JAVA_HOMEにはインストールしてきたJavaのルートディレクトリを指定する必要があるので、yum install したあと下記のパッケージで調べています。

# yumでインストールしたパッケージのインストール場所を調べる http://d.hatena.ne.jp/muupan/20130311/1362939424
$ rpm -ql  java-1.8.0-openjdk
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/bin/policytool
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/amd64/libawt_xawt.so
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/amd64/libjawt.so
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/amd64/libjsoundalsa.so
/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181-8.b13.39.39.amzn1.x86_64/jre/lib/amd64/libsplashscreen.so

.bash_profile の設定を反映させる。

$ exec $SHELL -l

Hadoopのインストール

$ wget http://ftp.jaist.ac.jp/pub/apache/hadoop/common/hadoop-3.1.1/hadoop-3.1.1.tar.gz
$ tar xvfz hadoop-3.1.1.tar.gz
$ ln -s hadoop-3.1.1 hadoop

$ cd hadoop
$ ls
bin  etc  include  lib  libexec  LICENSE.txt  NOTICE.txt  README.txt  sbin  share

$ bin/hadoop
Usage: hadoop [OPTIONS] SUBCOMMAND [SUBCOMMAND OPTIONS]
 or    hadoop [OPTIONS] CLASSNAME [CLASSNAME OPTIONS]
  where CLASSNAME is a user-provided Java class

  OPTIONS is none or any of:
...

Hadoop 3.1.1の場合はこの時点で、

  • Local (Standalone) Mode
  • Pseudo-Distributed Mode
  • Fully-Distributed Mode

での動作が可能と書いてあるので、 Local (Standalone) Mode で動かしてみます。

動作確認をしてみる

Local Modeで動かしてみる

とはいえ、インストールしたまま設定変更不要で動かせるのはLocal Modeだけみたいです。

ということで、公式のGetting Startedにあわせて、Local Modeでのサンプルコマンドを動かしてみる。

# テスト処理用にconfigファイルをコピーしてきている
$ cp etc/hadoop/*.xml input
# inputディレクトリ以下にあるdfs..で始まる設定ファイルの名前をoutputディレクトリ以下に出力する
$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar grep input output 'dfs[a-z.]+'
# 今回みたいにめちゃくちゃ出力が少ないコマンドでも part-r-00000 みたいな形のファイルで出るのかー(独り言)
$ ls output/
part-r-00000  _SUCCESS
# grepの結果は part-r-00000 に出力されている
$ cat output/part-r-00000 
1  dfsadmin

これのあと設定をいじって Pseudo-Distributed Mode を動かそうとしたらうまく動かなかったので、一からインスタンス立て直して試した方が早そう、ということで今日はここまで。

Linuxにrbenvをインストールし、rbenvでRubyを入れ、mysql2のgemを入れるまでのメモ

ちょっとたくさんのインスタンスからMySQLにクエリを投げたいことってありますよね?

というわけで今日はRubyMySQLに接続するコードを書く前段階として表題のEC2上で環境構築の素振りをします。

TL;DR

  • rbenvおよび、rbenvのプラグイン ruby-buildはGitHubからcloneしてくるのでまずgitを入れる
  • exec $SHELL -l で新しく起動中のシェルを実行し、現在のプロセスと入れ替えることで ~/.bash_profile に書いた設定内容(シェルの起動時に読み込み)をその場で反映させられる
  • RubyのインストールにはCコンパイラが必要で、Cコンパイラとしてgccをインストールする
  • -devel がついたパッケージには開発環境で使うオブジェクトやヘッダが含まれていて、C言語で書かれたライブラリをコンパイルする際に必要になることがある
  • mysql2 のgem のインストールには mysql-devel が必要
    • ネイティブエクステンションの部分でgemのインストールが失敗すると長いメッセージが出てきてオプションなどの羅列に目が行きがちだが、まんなかあたりに具体的に何をやればいいか1行で書いてある

rbenvのインストール

https://qiita.com/inouet/items/478f4228dbbcd442bfe8 を参考に作業していきます。

gitをインストールする

$ sudo yum -y install git

rbenvはGitHubからDLしてきます。そのために、Gitクライアントがあると便利なのでgitのインストールを行います。

rbenvをホームディレクトリにDLし、rbenvの実行ファイルへのパスを通す

$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile

ついでに、シェルを立ち上げた時に rbenv が起動するようにも設定しておきます。

$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

~/.bash_profile に書いた設定は次以降にbashを開く時に読み込まれるので

$ exec $SHELL -l

を実行して先ほどまで ~/.bash_profile に追記していた設定をその場で反映させます。

exec $SHELL -l コマンドは、新しく ~/.bash_profile の設定変更が反映されているbash($SHELL の中身)のプロセスを実行して今動いているbashのプロセスと入れ替えます*1

rbenvのプラグインである ruby-build をインストールする

$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

rbenvはシステム全体や特定のディレクトリ下で利用するRubyのバージョンを管理してくれますが、Rubyのインストールは ruby-buildプラグインが必要です。

というわけで、 rbenv を入れたパスの /plugins/ 以下に ruby-build を、これもGitHubからDLしてきて入れます。

Ruby 2.5.1 のインストール

1回目のRubyのインストールをやってみるとCのコンパイラがないといって失敗する

それではrbenvでRubyのインストールをためしてみます。

$ rbenv install 2.5.1
Downloading ruby-2.5.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.bz2
Installing ruby-2.5.1...

BUILD FAILED (Amazon Linux AMI 2018.03 using ruby-build 20180822-8-g336584c)

Inspect or clean up the working tree at /tmp/ruby-build.20180916020903.3029
Results logged to /tmp/ruby-build.20180916020903.3029.log

Last 10 log lines:
tool/config.sub already exists
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking target system type... x86_64-pc-linux-gnu
checking for gcc... no
checking for cc... no
checking for cl.exe... no
configure: error: in `/tmp/ruby-build.20180916020903.3029/ruby-2.5.1':
configure: error: no acceptable C compiler found in $PATH
See `config.log' for more details

すると利用可能なCコンパイラがないといってインストールに失敗します。

Cのコンパイラとしてgccを入れる

sudo yum install gcc

gccC言語コンパイラ、およびコンパイル用実行ファイルの名称です*2

ところで、XCodeを入れたら/入れ直したらmysqlのgemがインストールできるようになった、という話がありますが*3XCodeはもともとC系の言語用のIDEなので、C言語コンパイラが一緒に入った/正常な状態のものに更新された、ということではないかと思います。

2回目のRubyのインストールをやってみるとopenssl, readline, zlibのextensionがないといって失敗する

では、気を取り直してもう一回インストールを試すと、今度は

[ec2-user@ip-172-31-19-189 ~]$ rbenv install 2.5.1
Downloading ruby-2.5.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.bz2
Installing ruby-2.5.1...

BUILD FAILED (Amazon Linux AMI 2018.03 using ruby-build 20180822-8-g336584c)

Inspect or clean up the working tree at /tmp/ruby-build.20180916021333.14684
Results logged to /tmp/ruby-build.20180916021333.14684.log

Last 10 log lines:
The Ruby openssl extension was not compiled.
The Ruby readline extension was not compiled.
The Ruby zlib extension was not compiled.
ERROR: Ruby install aborted due to missing extensions
Try running `yum install -y openssl-devel readline-devel zlib-devel` to fetch missing dependencies.

Configure options used:
  --prefix=/home/ec2-user/.rbenv/versions/2.5.1
  LDFLAGS=-L/home/ec2-user/.rbenv/versions/2.5.1/lib 
  CPPFLAGS=-I/home/ec2-user/.rbenv/versions/2.5.1/include 

のようなメッセージで失敗します。

openssl-devel readline-devel zlib-devel のインストール

openssl, readline, zlib はそれぞれSSL通信用のライブラリ、コマンド履歴管理用のライブラリ*4、データ圧縮、解凍のためのライブラリですが、これらの拡張が足りず -devel のsuffixがついたパッケージを入れるように言われています。

https://www.unknownengineer.net/entry/2017/04/07/162346 の記事によると、 -devel というsuffixはなにかというと開発に必要なライブラリオブジェクトやヘッダファイル(**.h とかそういう)が入っていて、(そのライブラリやコマンドを単体で利用する場合にはおそらくいらない場合もあると思われるんですが、)ライブラリを利用したプログラムをコンパイルする際に必要となることがあるそうで、

つまり、Rubyコンパイルするのに、openssl, readline, zlib の開発用のヘッダ、ライブラリの一式が必要ということみたいです。

というわけでいれます。

$ sudo yum install -y openssl-devel readline-devel zlib-devel

なお、 -devel のパッケージは -devel が入っていない方のパッケージに依存しているというか、openssl-develはopensslの内容+開発用オブジェクトやヘッダが入っている、という感じなので、 sudo yum install -y openssl-devel したあとは openssl コマンドが動かせます。

$ sudo yum install -y openssl-devel readline-devel zlib-devel

...


Installed:
  openssl-devel.x86_64 1:1.0.2k-12.110.amzn1                 readline-devel.x86_64 0:6.2-9.14.amzn1                 zlib-devel.x86_64 0:1.2.8-7.18.amzn1                

Dependency Installed:
  keyutils-libs-devel.x86_64 0:1.5.8-3.12.amzn1           krb5-devel.x86_64 0:1.15.1-19.43.amzn1                   libcom_err-devel.x86_64 0:1.42.12-4.40.amzn1          
  libkadm5.x86_64 0:1.15.1-19.43.amzn1                    libselinux-devel.x86_64 0:2.1.10-3.22.amzn1              libsepol-devel.x86_64 0:2.1.7-3.12.amzn1              
  libverto-devel.x86_64 0:0.2.5-4.9.amzn1                 ncurses-devel.x86_64 0:5.7-4.20090207.14.amzn1          

Dependency Updated:
  krb5-libs.x86_64 0:1.15.1-19.43.amzn1                                               openssl.x86_64 1:1.0.2k-12.110.amzn1                                              

Complete!
$ openssl
OpenSSL> exit

このAMIにはもともとopenssl入っていたみたいですが!

3回目の正直でRubyのインストールに成功するので、インストールしたバージョンを利用するようにする

$ rbenv install 2.5.1
Downloading ruby-2.5.1.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.bz2
Installing ruby-2.5.1...
Installed ruby-2.5.1 to /home/ec2-user/.rbenv/versions/2.5.1

$ ruby -v
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
$ rbenv global 2.5.1
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

mysql2のgemをインストールする

なにもせず mysql2 のgemを入れようとするとmysqlがインストールされていないので怒られる

$ gem install mysql2

Fetching: mysql2-0.5.2.gem (100%)
Building native extensions. This could take a while...
ERROR:  Error installing mysql2:
    ERROR: Failed to build gem native extension.

    current directory: /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2/ext/mysql2
/home/ec2-user/.rbenv/versions/2.5.1/bin/ruby -r ./siteconf20180916-10601-y92qux.rb extconf.rb
checking for rb_absint_size()... yes
checking for rb_absint_singlebit_p()... yes
checking for rb_wait_for_single_fd()... yes
checking for -lmysqlclient... no
-----
mysql client is missing. You may need to 'apt-get install libmysqlclient-dev' or 'yum install mysql-devel', and try again.
-----
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/home/ec2-user/.rbenv/versions/2.5.1/bin/$(RUBY_BASE_NAME)
    --with-mysql-dir
    --without-mysql-dir
    --with-mysql-include
    --without-mysql-include=${mysql-dir}/include
    --with-mysql-lib
    --without-mysql-lib=${mysql-dir}/lib
    --with-mysql-config
    --without-mysql-config
    --with-mysql-dir
    --without-mysql-dir
    --with-mysql-include
    --without-mysql-include=${mysql-dir}/include
    --with-mysql-lib
    --without-mysql-lib=${mysql-dir}/lib
    --with-mysqlclientlib
    --without-mysqlclientlib

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/extensions/x86_64-linux/2.5.0-static/mysql2-0.5.2/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/gems/mysql2-0.5.2 for inspection.
Results logged to /home/ec2-user/.rbenv/versions/2.5.1/lib/ruby/gems/2.5.0/extensions/x86_64-linux/2.5.0-static/mysql2-0.5.2/gem_make.out

MySQLをインストールしていないので、

mysql client is missing. You may need to 'apt-get install libmysqlclient-dev' or 'yum install mysql-devel', and try again.

と怒られています。それはそうですね...。ということで 'yum install mysql-devel' をします。

mysql-develをインストールする

$ sudo yum install mysql-devel
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main                                                                                                                               | 2.1 kB  00:00:00     
amzn-updates                                                                                                                            | 2.5 kB  00:00:00     
Resolving Dependencies
--> Running transaction check
---> Package mysql-devel.noarch 0:5.5-1.6.amzn1 will be installed
--> Processing Dependency: mysql55-devel >= 5.5 for package: mysql-devel-5.5-1.6.amzn1.noarch
--> Processing Dependency: /usr/bin/mysql_config for package: mysql-devel-5.5-1.6.amzn1.noarch
--> Running transaction check
---> Package mysql55.x86_64 0:5.5.61-1.22.amzn1 will be installed
--> Processing Dependency: real-mysql55-libs(x86-64) = 5.5.61-1.22.amzn1 for package: mysql55-5.5.61-1.22.amzn1.x86_64
--> Processing Dependency: mysql-config for package: mysql55-5.5.61-1.22.amzn1.x86_64
---> Package mysql55-devel.x86_64 0:5.5.61-1.22.amzn1 will be installed
--> Running transaction check
---> Package mysql-config.x86_64 0:5.5.61-1.22.amzn1 will be installed
---> Package mysql55-libs.x86_64 0:5.5.61-1.22.amzn1 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

===============================================================================================================================================================
 Package                                Arch                            Version                                    Repository                             Size
===============================================================================================================================================================
Installing:
 mysql-devel                            noarch                          5.5-1.6.amzn1                              amzn-main                             2.7 k
Installing for dependencies:
 mysql-config                           x86_64                          5.5.61-1.22.amzn1                          amzn-updates                           49 k
 mysql55                                x86_64                          5.5.61-1.22.amzn1                          amzn-updates                          7.5 M
 mysql55-devel                          x86_64                          5.5.61-1.22.amzn1                          amzn-updates                          201 k
 mysql55-libs                           x86_64                          5.5.61-1.22.amzn1                          amzn-updates                          816 k

Transaction Summary
===============================================================================================================================================================
Install  1 Package (+4 Dependent packages)

Total download size: 8.6 M
Installed size: 32 M
Is this ok [y/d/N]: y
Downloading packages:
(1/5): mysql-config-5.5.61-1.22.amzn1.x86_64.rpm                                                                                        |  49 kB  00:00:00     
(2/5): mysql-devel-5.5-1.6.amzn1.noarch.rpm                                                                                             | 2.7 kB  00:00:00     
(3/5): mysql55-5.5.61-1.22.amzn1.x86_64.rpm                                                                                             | 7.5 MB  00:00:00     
(4/5): mysql55-libs-5.5.61-1.22.amzn1.x86_64.rpm                                                                                        | 816 kB  00:00:00     
(5/5): mysql55-devel-5.5.61-1.22.amzn1.x86_64.rpm                                                                                       | 201 kB  00:00:00     
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Total                                                                                                                           15 MB/s | 8.6 MB  00:00:00     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : mysql55-libs-5.5.61-1.22.amzn1.x86_64                                                                                                       1/5 
  Installing : mysql-config-5.5.61-1.22.amzn1.x86_64                                                                                                       2/5 
  Installing : mysql55-5.5.61-1.22.amzn1.x86_64                                                                                                            3/5 
  Installing : mysql55-devel-5.5.61-1.22.amzn1.x86_64                                                                                                      4/5 
  Installing : mysql-devel-5.5-1.6.amzn1.noarch                                                                                                            5/5 
  Verifying  : mysql-config-5.5.61-1.22.amzn1.x86_64                                                                                                       1/5 
  Verifying  : mysql55-libs-5.5.61-1.22.amzn1.x86_64                                                                                                       2/5 
  Verifying  : mysql55-5.5.61-1.22.amzn1.x86_64                                                                                                            3/5 
  Verifying  : mysql55-devel-5.5.61-1.22.amzn1.x86_64                                                                                                      4/5 
  Verifying  : mysql-devel-5.5-1.6.amzn1.noarch                                                                                                            5/5 

Installed:
  mysql-devel.noarch 0:5.5-1.6.amzn1                                                                                                                           

Dependency Installed:
  mysql-config.x86_64 0:5.5.61-1.22.amzn1 mysql55.x86_64 0:5.5.61-1.22.amzn1 mysql55-devel.x86_64 0:5.5.61-1.22.amzn1 mysql55-libs.x86_64 0:5.5.61-1.22.amzn1

Complete!

yum installに指定したパッケージのは mysql-devel ですが、

Dependencies Resolved

===============================================================================================================================================================
 Package                                Arch                            Version                                    Repository                             Size
===============================================================================================================================================================
Installing:
 mysql-devel                            noarch                          5.5-1.6.amzn1                              amzn-main                             2.7 k
Installing for dependencies:
 mysql-config                           x86_64                          5.5.61-1.22.amzn1                          amzn-updates                           49 k
 mysql55                                x86_64                          5.5.61-1.22.amzn1                          amzn-updates                          7.5 M
 mysql55-devel                          x86_64                          5.5.61-1.22.amzn1                          amzn-updates                          201 k
 mysql55-libs                           x86_64                          5.5.61-1.22.amzn1                          amzn-updates                          816 k

Transaction Summary
===============================================================================================================================================================
Install  1 Package (+4 Dependent packages)

のように、mysql-develは mysql55 に依存しているのでMySQLのクライアント、およびサーバのプログラムも入ります*5

これだけ書くと yum install mysql でよさそうな気もしますが、別途試したところ、mysql2のgemのCの部分をコンパイルするために mysql-devel パッケージ内のヘッダが必要みたいです。

あらためてmysql2のgemをインストール

$ gem install mysql2
Building native extensions. This could take a while...
Successfully installed mysql2-0.5.2
Parsing documentation for mysql2-0.5.2
Installing ri documentation for mysql2-0.5.2
Done installing documentation for mysql2 after 0 seconds
1 gem installed

成功しました。

もうちょっとよくわからないトラブルが起きてそのトラブルシューティングで知見をためようとおもっていたらすんなり進んで若干がっかりしていますが、まあいいですね。

現場からは以上です。

*1:https://www.gnu.org/software/bash/manual/html_node/Bourne-Shell-Builtins.html#Bourne-Shell-Builtins

*2:http://e-words.jp/w/gcc.html

*3:https://qiita.com/unsoluble_sugar/items/1403ddf0ac9709b1aae6#xcode%E3%82%92%E6%9C%80%E6%96%B0%E3%81%AB%E3%82%A2%E3%83%83%E3%83%97%E3%83%87%E3%83%BC%E3%83%88 など

*4:https://ja.wikipedia.org/wiki/GNU_Readline irbとかで上キー押したら前のコマンド見れる、とかそういうところなんでしょうか...?

*5:mysql55だとサーバとクライアントの両方が入るんですが、一つのホストに必要なのは片方だけであることも多いので、サーバプログラム、クライアントプログラムをわけてインストールすることも可能 https://dev.mysql.com/doc/refman/5.6/ja/linux-installation-rpm.html

CSVからCSVを作るためのシェル芸のいくらかについてメモ

列の順番を入れ替えたい

awkを使えばよい。

aaa,ddd,fff,bbb,ccc,eee

のような行があったとき、

aaa,bbb,ccc,ddd,eee,fff

のように列を入れ替えたいとすると

$ echo 'aaa,ddd,fff,bbb,ccc,eee' | awk -F ',' '{ print $1 "," $4 "," $5 "," $2 "," $6 "," $3 }'
aaa,bbb,ccc,ddd,eee,fff

CSVにダブルクオーテーションがついている項目と付いていない項目が混じっている

aaa,"bbb",ccc

上の行をすべて、""がついているようにしたいとする。扱う列はまあなんとか手で処理できる個数だとすると

$ echo 'aaa,"bbb",ccc' | gawk -v FPAT='([^,]+)' '{print "\"" $1 "\"," "\""$2"\"," "\""$3"\""}' | sed -e 's/""/"/g'
"aaa","bbb","ccc"

gawk-v FPAT pattarn で1列分の要素として扱われるパターンを指定できるので、これで , 以外の文字列を指定。
余分についた "sedで簡単に取り除けるので、各列に " を追加する。

こんな感じ。念のため、もともと "bbb" の列の中に "" のような文字列がないか、

$ echo 'aaa,"bbb",ccc' | cut -d ',' -f 2 | grep '""'

のようにして探しておくとよい(なお、上はa列にはややこしい文字列が入ってこないことを仮定している)。

CSVの列の中に,が含まれている項目がある

これもgawk-v FPAT pattarn が使える。

$ echo '"aaa","b,b,b","ccc"' | gawk -v FPAT='(\"[^\"]+\")' '{ print $2 "  " $3 }'
"b,b,b"  "ccc"

上のパターンと組み合わさった条件のCSVの場合、上のパターンと組み合わせればよい。

$ echo 'aaa,"b,b,b",ccc' | gawk -v FPAT='([^,]+)|(\"[^\"]+\")' '{print "\"" $1 "\"," "\""$2"\"," "\""$3"\""}' | sed -e 's/""/"/g'
"aaa","b,b,b","ccc"

もっとややこしいパターンが入っていてうまく入れ替えられない場合

取り出したい列の近くの特徴的な列を利用して、tr + grep とかsedで頑張る。

aaa,bbb,,"cccc,ddddd","e,ff,g","State",hhh,ii

の場合、"State" の部分が何種類かの固定値であることがわかっているのであれば、

$ echo 'aaa,bbb,,"cccc,ddddd","e,ff,g","State",hhh,ii' | tr ',' '\n' | grep -n1 "State" | sed -n '3,3 p' | cut -d '-' -f 2
hhh

となる。

  • tr ',' "\n" で雑にセパレータで行を分けてしまう
  • 行単位でわかりやすい列をgrepで前後も出力するようにして検索する
  • わかりやすい列から目当ての列が何個かを踏まえて、sed -n '開始行,終了行 p' で抜き出す
  • grep -n数字 の影響で取り出した列は 数字- から始まっているので cut を使って最初から2番目の値を目当ての値として取り出す
$ echo 'aaa,bbb,,"cccc,ddddd","e,ff,g","State",hhh,ii' | tr ',' '\n'
aaa
bbb

"cccc
ddddd"
"e
ff
g"
"State"
hhh
ii

$ echo 'aaa,bbb,,"cccc,ddddd","e,ff,g","State",hhh,ii' | tr ',' '\n' | grep -n1 "State" 
8-g"
9:"State"
10-hhh

$ echo 'aaa,bbb,,"cccc,ddddd","e,ff,g","State",hhh,ii' | tr ',' '\n' | grep -n1 "State" |  sed -n '3,3 p'
10-hhh

$ echo 'aaa,bbb,,"cccc,ddddd","e,ff,g","State",hhh,ii' | tr ',' '\n' | grep -n1 "State" |  sed -n '3,3 p' | cut -d '-' -f 2
hhh

もしある列の値を見て別の値を割り振りたい

is_current_year="false"
target_date="2017/01/02"
echo $target_date | grep '2018' 1>/dev/null && is_current_year="true"
echo $is_current_year
# false

is_current_year="false"
target_date="2018/02/03"
echo $target_date | grep '2018' 1>/dev/null && is_current_year="true"
echo $is_current_year
# true

grepである列の値が特定のパターンに合致するか調べて、合致する場合はその列の値を表す変数を上書きする、みたいな感じ。

別のCSVのデータと結合したいが、別のCSVからデータを探してくる時間を少しでも短くしたい

世の中には別々のデータストアに入っているデータがそれぞれCSVでしか出力できないため、CSV同士でデータを結合しなければいけないという時がある(ないほうがよい)。
で、片方のCSVからデータを取り出すとき、必要な行を少しでも早く取り出したいとき、grep-m オプションをつけると必要な数だけ合致する行を見つけたらその場でreturnしてくれる。

# 社員データ.csv から誰でもいいので営業部の社員を1人だけ出力したい
grep -m 1 営業部 社員データ.csv

その他留意事項

  • 改行の扱いなどが環境によって異なる場合があるのでバッチ処理を行わせるサーバでコマンドの結果を改めて確認すること
  • 3つ以上特殊な値があったら1つずつ値を取り出して、sedで置換していくほうが正確性はたかそう
  • gawkは入っていない場合があるので、インストールすること

参考

現場からは以上です。