LoginSignup
3
0

More than 3 years have passed since last update.

自宅にHadoopクラスターをつくってみた(Hadoop、YARN)

Posted at

はじめに

自宅にHadoopクラスターを作ったときの備忘録です。
初めてHadoopを触るので、間違い等があれば、ご指摘ください。
だいぶ試行錯誤したので、余計なものが入ってしまっている気はします...

以下、参考したもの

環境

  • Ubuntu 18.04 3台(ここでは、わかりやすいようにそれぞれのホスト名をworker1, worker2, worker3としました)
  • Hadoop 3.1.3

1. 準備

1-1. IPアドレスとホスト名の紐付け
1-2. Hadoop専用のユーザ生成
1-3. Hadoop用ディレクトリを生成
1-4. Hadoopのダウンロード
1-5. sshの設定
1-6. ssh-agentの設定
1-7. 並列分散シェル - pdshの設定
1-8. Javaのインストール

1-1. IPアドレスとホスト名の紐付け

この作業の前に、自宅で使用しているルータの設定で、それぞれのPCに固定IPアドレスを払い出しておきます。
そして、PCを識別するのにわかりやすくするため、それぞれのPCでIPアドレスとホスト名の紐付けを行います。

sudo vim /etc/hosts
# エディタは何でもいいが、ファイルの上部を各PCで以下のように設定

以下、192.168.***.***には適切なIPアドレスを入れます。

/etc/hosts
#127.0.0.1       localhost
192.168.***.*** worker1
192.168.***.*** worker2
192.168.***.*** worker3

1-2. Hadoop専用のユーザ生成

全てのノード上でhadoopユーザ作成をする。
同じマシン上で動作している他のサービスからHadoopを分離しておくために、Hadoopのユーザーアカウントを生成しておくのがいいらしいです。

sudo adduser hadoop
# パスワード等ユーザ設定のプロンプトが出るので適切に打つ

1-3. Hadoop用ディレクトリを生成

全てのノード上でHadoop用ディレクトリを生成します。
Hadoop用のディレクトリ一式は、/usr/local等に置いてもいいが、今回は/optに入れます。
hadoopのホームディレクトリに置くのはあまり得策ではないらしいです。
(小規模なクラスタの場合、ホームがNFSの場合等もあるため、/optや/usr/localに置くのを習慣にするのがいいみたいです。)

sudo mkdir /opt/hadoop
# Hadoop関連のファイルの所有者はhadoopユーザ及びhadoopグループにしておく
sudo chown hadoop:hadoop /opt/hadoop
cd /opt/hadoop
# hadoopユーザに切り替え
su hadoop

1-4. Hadoopのダウンロード

ダウンロードは一旦worker1上のみで行う。他のノードには、あとでscpをします。
Hadoop公式ダウンロードページ(Apache Hadoop: Download)から欲しいバージョンをダウンロードします。

バージョン3.1.3の場合は、以下のコマンドで落ちてきます。

# /opt/hadoopディレクトリにいることを確認
pwd
# /opt/hadoop
wget https://archive.apache.org/dist/hadoop/common/hadoop-3.1.3/hadoop-3.1.3.tar.gz
tar xvzf hadoop-3.1.3.tar.gz
# パスの簡略化のため、シンボリックリンクを作成しておく
ln -sf hadoop-3.1.3 hadoop

1-5. sshの設定

Hadoopではクラスタ操作がSSHに依存しているため、SSHの公開鍵認証方式の設定が必要となります。

ssh-keygen -t rsa -f ~/.ssh/id_rsa -b 4096
# パスフレーズは適当なものをうっておく
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
ssh worker1
# yesの後、パスフレーズが聞かれるため先程設定したパスフレーズを打っておく
# ssh接続確認後
exit
# worker2に公開鍵を送る
scp ~/.ssh/id_rsa.pub worker1:~/.
# パスワードを打つ
# sshでworker2に入る
ssh worker2
# パスワードを打つ
mv ~/id_rsa.pub ~/.ssh/worker1.pub
cat ~/.ssh/worker1.pub >> ~/.ssh/authorized_keys
exit
# worker3に公開鍵を送る
scp ~/.ssh/id_rsa.pub worker1:~/.
# パスワードを打つ
# sshでworker3に入る
ssh worker3
# パスワードを打つ
mv ~/id_rsa.pub ~/.ssh/worker1.pub
cat ~/.ssh/worker1.pub >> ~/.ssh/authorized_keys
exit

注意
この設定のためSSHにパスワード認証が必要だったがこの設定後は公開鍵認証が可能となるため、hadoopのセットアップ後は、
セキュリティの観点から、sshのパスワード認証を禁止する設定にするといい気がします。

1-6. ssh-agentの設定

Hadoopではパスフレーズ入力なしにSSH接続できるようにしなければならないため、ssh-agentを使用します。

ssh-agentは、秘密鍵とパスフレーズはメモリ上にキャッシュすることにより、パスフレーズの入力を代わりにやってくれるアプリケーションです。
PCを再起動した場合やターミナルを新規に立ち上げた場合は、以下のパスフレーズ登録作業を毎度行うようにします。

eval `ssh-agent`
ssh-add ~/.ssh/id_rsa
# 先程設定したパスフレーズを打つ

# 情報が登録されているのか確認
ssh-add -l
# 再度sshしてみる
ssh worker1
# ssh接続にパスフレーズが聞かれなかったことを確認後
exit

ssh worker2
# ssh接続にパスフレーズが聞かれなかったことを確認後
exit

ssh worker3
# ssh接続にパスフレーズが聞かれなかったことを確認後
exit

注意
ssh-agentはssh接続を簡易化するが、セキュリティレベルは下がってしまう。そのため使用しないときは

ssh-agent -k

でプロセスをkillしておくか、~/.bash_logoutに上記のコマンドを登録して自動的にkillするのが得策らしいです。

参考 【SSH】ssh-agent の使い方

以下のように表示される場合は、ssh-agentの再設定が必要です。

worker1: hadoop@worker1: Permission denied (publickey,password).

1.7. 並列分散シェル - pdshの設定

クラスタ整備のため、複数のホスト上でコマンド実行が可能な並列シェルを用いることにします。
並列シェルはいくつかツールが存在するが、Hadoopではhdfsを開始するシェルスクリプト等の内部でpdshを用いているため、ここではpdshを選びました。

以下のコマンド実行には、sudo権限が必要なため、一旦sudo権限を持つユーザに切り替えます。
(一時的にhadoopユーザにsudo権限を与えるのもいい手な気がする。)

注意
pdshのインストールはworker1のみでいいです。

# 現在のユーザを表示
echo $USER
# hadoop
# 現在のユーザが属すグループを表示
groups
# hadoop #hadoopユーザはhadoopグループにしか属していないためsudo権限がない
exit
# 現在のユーザが属すグループを表示
groups
#***** adm cdrom sudo dip plugdev lpadmin sambashare kvm libvirt docker #sudo権限あり
# sudo権限を用いてrootユーザになる
sudo su -
apt update
apt install pdsh
apt install ssh-askpass
echo "ssh" >> /etc/pdsh/rcmd_default 
exit
# rootユーザから抜ける

pdshを使用してみます。

su hadoop
# hadoopユーザになる
pdsh -w worker[1,3] 'echo hello $HOSTNAME'
# 以下の様に表示されれば正常に動いている。
# worker1: hello worker1
# worker2: hello worker2
# worker3: hello worker3

pdshは以下の様に正規表現も可能となる。workerが複数ある場合は下記のようなコマンドを用いると便利です。

pdsh -w worker[1,3]

pdshをインタラクティブに用いることも可能です。

pdsh -w worker1,worker2,worker3

多くのマシンから構成されたクラスタをいじる場合、pdshを使用すると便利だが、少ないときはそれぞれで行ってもそこまで苦ではないです。
tmuxを使用しているものならばプレフィックスset synchronize on[off]などでシンクロ入力ができるので活用すると楽です。

1-8. Javaのインストール

pdshもしくはtmuxなどを用いて、以下のコマンドのようにjavaをインストールします。

sudo apt install openjdk-8-jdk

複数のJavaを入れている場合、update-alternativesなどを使用して設定するとよいみたいです。

update-alternatives --display java

2. Hadoopの設定

設定するファイルは以下です。

  • .profile
  • .bashrc
  • hadoop-env.sh
  • core-site.xml
  • hdfs-site.xml
  • mapred-site.xml
  • workers

Hadoopの設定は、hadoop/etc/hadoopディレクトリ下のファイルで主に行います。
(full path : /opt/hadoop/hadoop/etc/hadoop)

2-1. ユーザ空間での設定

.profile .bashrc等といったユーザディレクトリ以下での設定はなるべく少なくするのがよさそうなのですが...
なんだかんだありいっぱい書いてしまうことにしました。

.profile
...
PATH=/opt/hadoop/hadoop/bin:/opt/hadoop/hadoop/sbin:$PATH
.bashrc
...
export LANG=en_US.utf8
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
export HADOOP_HOME=/opt/hadoop/hadoop
export PATH=${PATH}:${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib/native"

以下のように並列シェルを実行すれば書き込めます。

pdsh -w worker[1:3]
echo "PATH=/opt/hadoop/hadoop/bin:/opt/hadoop/hadoop/sbin:\$PATH" >> ~/.profile
cat << EOF >> ~/.bashrc
export LANG=en_US.utf8
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
export HADOOP_HOME=/opt/hadoop/hadoop
export PATH=${PATH}:${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib/native"
EOF
exit

2-2. Hadoop環境変数の設定 - hadoop-env.sh

JAVA_HOMEのパスはaptでインストールした場合、以下のように設定します。
HADOOP_WORKERSの部分は、HADOOPのバージョンによっては、slavesとなるので要確認です。

/opt/hadoop/hadoop/etc/hadoop/hadoop-env.sh
export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
export HADOOP_HOME=/opt/hadoop/hadoop
export HADOOP_CONF_DIR=${HADOOP_CONF_DIR:-"${HADOOP_HOME}/etc/hadoop"}

以下のようにシェルを実行すれば書き込めます

echo "export JAVA_HOME=\"/usr/lib/jvm/java-8-openjdk-amd64\"" >> /opt/hadoop/hadoop/etc/hadoop/hadoop-env.sh
echo "export HADOOP_HOME=/opt/hadoop/hadoop" >> /opt/hadoop/hadoop/etc/hadoop/hadoop-env.sh
echo "export HADOOP_CONF_DIR=\${HADOOP_CONF_DIR:-\"\${HADOOP_HOME}/etc/hadoop\"}" >> /opt/hadoop/hadoop/etc/hadoop/hadoop-env.sh
# 確認
tail /opt/hadoop/hadoop/etc/hadoop/hadoop-env.sh

2-3. Hadoopのコア設定 - core-site.xml

Hadoop CoreのHDFSとMapReduceが共通に利用するI/Oなどの設定を行うファイルがcore-site.xmlです。

ここで設定を行わなければならないのは、fs.defaultFSというURIで、ここで設定した値がHDFSファイルシステムのURIとなります。
hdfs://ホスト名:ポートで指定し、ポートを省略すると8020番が使用されます。

hadoop core-site.xmlのドキュメント
https://hadoop.apache.org/docs/r3.1.3/hadoop-project-dist/hadoop-common/core-default.xml

上記のリンクをみるとバージョン3.1.3では、fs.default.nameはDeprecated. Use (fs.defaultFS) property insteadと書かれていたので、fs.defaultFSを用います。

注意 バージョンによってプロパティの変更があるため、上記のリンクの/.r3.1.3/を適切な値に変更し情報を見る必要があります。
注意 以下に書いた設定は、実稼働させるための最低限の定義を記したものです。

/opt/hadoop/hadoop/etc/hadoop/core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
    <configuration>
        <property>
            <name>fs.defaultFS</name>
            <value>hdfs://worker1:8020</value>
            <final>true</final>
        </property>
    </configuration>

以下のようにシェルを実行すれば書き込めます。

# </configuration>を削除
sed -i -e "s/<\/configuration>//" /opt/hadoop/hadoop/etc/hadoop/core-site.xml
# 設定追加
cat << EOF >> /opt/hadoop/hadoop/etc/hadoop/core-site.xml
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://worker1:8020</value>
        <final>true</final>
    </property>
<name>hadoop.tmp.dir</name>
<value>file:/var/lib/hadoop/tmp</value>
</property>
</configuration>
EOF

# 確認
cat /opt/hadoop/hadoop/etc/hadoop/core-site.xml

2-4. HDFSデーモンの設定 - hdfs-site.xml

Namenode, SecondaryNode, DataNodeなどのHDFSデーモンの設定を行うのがhdfs-site.xmlです。

ここでは、

  • dfs.namenode.name.dir
  • dfs.datanode.data.dir
  • dfs.replication

の3点のプロパティ設定を行います。
これは、NameNodeとDataNodeのストレージのディレクトリです。
また今回は、ノードが2つのため、データノードのレプリケーションを2つ行うことにします。

hadoop hdfs-site.xmlのドキュメント
https://hadoop.apache.org/docs/r3.1.3/hadoop-project-dist/hadoop-hdfs/hdfs-default.xml

注意 バージョンによってプロパティの変更があるため、上記のリンクの/.r3.1.3/を適切な値に変更し情報を見る必要があります。
注意 以下に書いた設定は、実稼働させるための最低限の定義を記したものです。

/opt/hadoop/hadoop/etc/hadoop/hdfs-site.xml
<configuration>
    <property>
            <name>dfs.namenode.name.dir</name>
            <value>file:/home/hadoop/data/nameNode</value>
    </property>
    <property>
            <name>dfs.datanode.data.dir</name>
            <value>file:/home/hadoop/data/dataNode</value>
    </property>
    <property>
            <name>dfs.replication</name>
            <value>2</value>
    </property>
</configuration>

以下のようにシェルを実行すれば書き込める。

# </configuration>を削除
sed -i -e "s/<\/configuration>//" /opt/hadoop/hadoop/etc/hadoop/hdfs-site.xml
# 設定追加
cat << EOF >> /opt/hadoop/hadoop/etc/hadoop/hdfs-site.xml
    <property>
            <name>dfs.namenode.name.dir</name>
            <value>file:/home/hadoop/data/nameNode</value>
    </property>
    <property>
            <name>dfs.datanode.data.dir</name>
            <value>file:/home/hadoop/data/dataNode</value>
    </property>
    <property>
            <name>dfs.replication</name>
            <value>1</value>
    </property>
</configuration>
EOF
# 確認
cat /opt/hadoop/hadoop/etc/hadoop/hdfs-site.xml
# ディレクトリを作成しておく
mkdir -p ~/data/nameNode
mkdir ~/data/dataNode

2-5. MapReduceデーモンの設定 - mapred-site.xml

jobtrackerとtasktrackerなどのMapReduceデーモンの設定を行うのがmared-site.xmlです。

hadoop mapred-site.xmlのドキュメント
https://hadoop.apache.org/docs/r3.1.3/hadoop-mapreduce-client/hadoop-mapreduce-client-core/mapred-default.xml

注意 バージョンによってプロパティの変更があるため、上記のリンクの/.r3.1.3/を適切な値に変更し情報を見る必要があります。
注意 以下に書いた設定は、実稼働させるための最低限の定義を記したものです。

/opt/hadoop/hadoop/etc/hadoop/mapred-site.xml
<configuration>
        <property>
                <name>mapreduce.framework.name</name>
                <value>yarn</value>
        </property>
        <property>
                <name>yarn.app.mapreduce.am.env</name>
                <value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
        </property>
        <property>
                <name>mapreduce.map.env</name>
                <value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
        </property>
        <property>
                <name>mapreduce.reduce.env</name>
                <value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
        </property>
        <property>
                <name>yarn.app.mapreduce.am.resource.mb</name>
                <value>512</value>
        </property>
        <property>
                <name>mapreduce.map.memory.mb</name>
                <value>256</value>
        </property>

        <property>
                <name>mapreduce.reduce.memory.mb</name>
                <value>256</value>
        </property>
</configuration>
tail /opt/hadoop/hadoop/etc/hadoop/mapred-site.xml
# バージョンによってはファイルが無い場合もある
# </configuration>を削除
sed -i -e "s/<\/configuration>//" /opt/hadoop/hadoop/etc/hadoop/mapred-site.xml
# 設定追加
cat << EOF >> /opt/hadoop/hadoop/etc/hadoop/mapred-site.xml
        <property>
                <name>mapreduce.framework.name</name>
                <value>yarn</value>
        </property>
        <property>
                <name>yarn.app.mapreduce.am.env</name>
                <value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
        </property>
        <property>
                <name>mapreduce.map.env</name>
                <value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
        </property>
        <property>
                <name>mapreduce.reduce.env</name>
                <value>HADOOP_MAPRED_HOME=$HADOOP_HOME</value>
        </property>
        <property>
                <name>yarn.app.mapreduce.am.resource.mb</name>
                <value>512</value>
        </property>
        <property>
                <name>mapreduce.map.memory.mb</name>
                <value>256</value>
        </property>

        <property>
                <name>mapreduce.reduce.memory.mb</name>
                <value>256</value>
        </property>
</configuration>
EOF
# 確認
cat /opt/hadoop/hadoop/etc/hadoop/mapred-site.xml

2-6. YARNデーモンの設定 - yarn-site.xml

hadoop yarn-site.xmlのドキュメント
https://hadoop.apache.org/docs/r3.1.3/hadoop-yarn/hadoop-yarn-common/yarn-default.xml

yarn.nodemanager.aux-servicesは、ノードマネージャが実行する外部サービスのリストです。
YARNには、mapの出力をreduceタスクに渡すtasktrackerがなく、この処理をシャッフルハンドラに任せています。
このシャッフルハンドラは外部のサービスでありノードマネージャ内で動作するため、yarn-site.xmlで明示的なプロパティ設定が必要となります。

注意 バージョンによってプロパティの変更があるため、上記のリンクの/.r3.1.3/を適切な値に変更し情報を見る必要があります。
注意 以下に書いた設定は、実稼働させるための最低限の定義を記したものです。

/opt/hadoop/hadoop/etc/hadoop/yarn-site.xml
<configuration>
    <property>
            <name>yarn.acl.enable</name>
            <value>0</value>
    </property>
    <property>
            <name>yarn.resourcemanager.hostname</name>
            <value>worker1</value>
    </property>
    <property>
            <name>yarn.nodemanager.aux-services</name>
            <value>mapreduce_shuffle</value>
    </property>
    <property>
            <name>yarn.nodemanager.resource.memory-mb</name>
            <value>1536</value>
    </property>

    <property>
            <name>yarn.scheduler.maximum-allocation-mb</name>
            <value>1536</value>
    </property>

    <property>
            <name>yarn.scheduler.minimum-allocation-mb</name>
            <value>128</value>
    </property>

    <property>
            <name>yarn.nodemanager.vmem-check-enabled</name>
            <value>false</value>
    </property>
</configuration>
tail /opt/hadoop/hadoop/etc/hadoop/yarn-site.xml
# </configuration>を削除
sed -i -e "s/<\/configuration>//" /opt/hadoop/hadoop/etc/hadoop/yarn-site.xml
# 設定追加
cat << EOF >> /opt/hadoop/hadoop/etc/hadoop/yarn-site.xml
    <property>
            <name>yarn.acl.enable</name>
            <value>0</value>
    </property>
    <property>
            <name>yarn.resourcemanager.hostname</name>
            <value>worker1</value>
    </property>
    <property>
            <name>yarn.nodemanager.aux-services</name>
            <value>mapreduce_shuffle</value>
    </property>
    <property>
            <name>yarn.nodemanager.resource.memory-mb</name>
            <value>1536</value>
    </property>

    <property>
            <name>yarn.scheduler.maximum-allocation-mb</name>
            <value>1536</value>
    </property>

    <property>
            <name>yarn.scheduler.minimum-allocation-mb</name>
            <value>128</value>
    </property>

    <property>
            <name>yarn.nodemanager.vmem-check-enabled</name>
            <value>false</value>
    </property>
</configuration>
EOF
# 確認
cat /opt/hadoop/hadoop/etc/hadoop/yarn-site.xml

2.7 workerの登録 - workers

バージョンによってはworkerはslaveとなっています。

/opt/hadoop/hadoop/etc/hadoop/workers
worker1
worker2
worker3
sed -i -e "s/localhost//" /opt/hadoop/hadoop/etc/hadoop/workers
cat << EOF >> /opt/hadoop/hadoop/etc/hadoop/workers
worker1
worker2
worker3
EOF
cat /opt/hadoop/hadoop/etc/hadoop/workers

2.8 workerのセットアップ

scp hadoop-3.1.3.tar.gz worker1:/opt/hadoop/.
ssh worker1
cd /opt/hadoop
tar xvzf hadoop-3.1.3.tar.gz
ln -sf hadoop-3.1.3 hadoop
mkdir -p ~/data/datanode

for node in worker2 worker3;
do
    scp /opt/hadoop/hadoop/etc/hadoop/* $node:/opt/hadoop/hadoop/etc/hadoop/;
done

3. Hadoop起動

hdfs namenode -format
start-dfs.sh
start-yarn.sh

jpsコマンドを打つと、PIDは違うはずですが、以下のようにでていればOKです。

worker1
jps
# output
#16208 NameNode
#17537 Jps
#16690 SecondaryNameNode
#16419 DataNode
worker2
jps
# output
#13815 DataNode
#13950 Jps

http://worker1:9870
http://worker1:8088
上記リンクにアクセスすると、webインターフェースが見れます。

yarn node -list

うまくクラスターが作れていると以下のように表示されます。

2020-06-29 16:15:07,898 INFO client.RMProxy: Connecting to ResourceManager at worker1/192.168.*.*:8032
Total Nodes:3
         Node-Id             Node-State Node-Http-Address       Number-of-Running-Containers
worker1:43791           RUNNING worker1:8042                               0
worker2:44499           RUNNING worker2:8042                               0
worker3:35063           RUNNING worker3:8042                               0

hdfsの動作確認

hdfsがうまく動くか確認してみます。

mkdir ~/test
cd ~/test
wget -O alice.txt https://www.gutenberg.org/files/11/11-0.txt
wget -O holmes.txt https://www.gutenberg.org/files/1661/1661-0.txt
wget -O frankenstein.txt https://www.gutenberg.org/files/84/84-0.txt
hdfs dfs -mkdir -p /user/hadoop
hdfs dfs -mkdir books
hdfs dfs -put alice.txt holmes.txt frankenstein.txt books
hdfs dfs -ls books
mkdir temp
cd temp
hdfs dfs -get books/alice.txt

yarnの動作確認

yarnがうまく動くか確認してみます。

yarn jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.3.jar wordcount "books/*" output
# 実行結果をgetする
hdfs dfs -get output
# 出力ファイルを見てみる
head output/part-r-00000

結果はこんな感じでした。

"Defects,"      3
"Information    3
"Plain  6
"Project        15
"Right  3
#11]    1
#1661]  1
#84]    1
$5,000) 3
&       5
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0