CentOS 7にApacheを導入しようとして挫折する物語

これは、CentOS 7.2をインストールしたRaspberry Pi 3にApacheを導入する物語である。
筆者は、CentOSを3年もいじっているので、Apacheを入れるなんて朝飯前。
サーバを何回も初期化して、幾度とApacheを入れ直してるんで
Apacheを入れるのなんかカップラーメンにお湯を注ぐぐらい簡単だよって思ってます。

やることを頭に描き

## Apacheのインストール
# yum install httpd

## Apacheの起動および自動起動の設定
# service httpd start
# chkconfig httpd on

## 80番ポートの開放
# vim /etc/sysconfig/iptables
-A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT // 追加
# service httpd restart

サラッと、これを実行して、http://__IP_Address__ にアクセスして下の画面を確認しよう!

f:id:usmysa:20160529172640p:plain

第一章 Apacheを起動させよ

一瞬でApacheをインストールし、いざApacheを起動させようとした時、
普段なら下みたいに表示されるのに、表示されない…
まぁ、いっか。
きっと起動してるはず!

# いつも表示される結果
# service httpd start
httpd を起動中: httpd: Could not reliably determine the server's fully qualified domain name, using localhost.localdomain for ServerName
                                                           [  OK  ]
# 今回、表示された結果
# service httpd start
Redirecting to /bin/systemctl start httpd.service

第二章 挫折への前触れ

じゃあ、次にポートの開放をやってみよう。
えっ…「/etc/sysconfig/」に「iptables」がない…!
うん、きっと80番ポートはデフォルトで空いてるはず…

第三章 大いなる挫折

さぁ、http://__IP_Address__にアクセスして、いつもの画面を確認しよう!

「ページを開けません。このページのあるサーバが応答しません。」

な、な、なぜだ…!!
いつもの段取りでやってるのに、なんで出来ない…
いつもiptablesあるのに、なんでない…
ラズパイ用のCentOSだとないのかな…

第四章 解決へのアプローチ

実は、解決へのヒントが表示されていました。
そうです、「Redirecting to /bin/systemctl start httpd.service」です。
実は、CentOS 7になってから自分が大好きだったserviceコマンドがなくなっていました。
ついでに、iptablesもなくなってました。
新しいやり方では、「systemctl」というコマンドを使います!

## Apacheの起動および自動起動の設定
# systemctl start httpd.service
# systemctl enable httpd.service

## 80番ポートの開放
# firewall-cmd --add-service=http --zone=public
# firewall-cmd --add-service=http --zone=public --permanent

参考:Apache httpd 2.4 を CentOS 7 に yum でインストールする手順 | WEB ARCH LABO

で、http://__IP_Address__にアクセスしてみると、Apacheの画面を確認することができるようになりました。
f:id:usmysa:20160529181908p:plain

あとがき

今回、初めて当たり前だと思っていたことに挫折を覚えました。
ググる時も、CentOSのバージョンを意識しないで、
普通に「CentOS Apache」というクエリーで調べていたので
違うバージョンのやり方が出て、解決するのに時間がかかってしまいました。
バージョンが上がったら、変更される部分があるということを実感したので、
今後は注意したいと思います!笑

CentOSでキーボードのレイアウトを変更

Raspberry Pi 3にCentOS 7.2を入れており、
設定をする時にSSHからじゃなくてキーボードを使ったんですが、
どうやっても「:(コロン)」が打てないんですよねぇ…
昔、Virtual BoxにCentOSを入れた時も同じ問題が発生して解決したので
次、同じ問題が発生しても忘れないようにメモしときたいと思います。

環境

CentOS 7.2

解決策

# localectl list-keymaps | grep jp106
jp106

とコマンドを入力して、「jp106」があることを確認してください。
ちなみに、localectlコマンドはCentOSロケールを確認および変更するために使うコマンドのようです。

ロケールとは、ソフトウェアに内蔵される、言語や国・地域ごとに異なる単位、記号、日付、通貨などの表記規則の集合。または単に、利用する言語や国・地域の指定。多くのソフトウェアやプログラミング言語は、使用する言語とともにロケールを設定し、ロケールで定められた方式に基づいてデータの表記や処理を行う。

引用:ロケールとは|locale|ロカール - 意味/定義 : IT用語辞典

なるほど、また賢くなってしまいました…笑
(初めて見た時、「ローカルじゃないの?」って思っちゃいました…)

$ localectl set-keymap jp106

これで完了です。
無事にコロンが打てるようになりました!


参考:centos7でキーボードレイアウト変更 - Qiita

wgetとcurlの違い

サーバ関係の作業をしていると、よくwgetを使っているサイトを見るんですが、
稀にcurlを使っているサイト見かけます。
どちらも、ダウンロードをするのに使うコマンドなんですが、
その違いを調べてみました。

wget

読み方は「ダブルゲット」です!
自分は、ずっと「ダブリューゲット」って読んでました…笑
みなさん、ご存知の通り、ファイルをダウンロードする時に使うコマンドです。
wgetには、2つの特徴があります。

  • 再帰的にファイルをダウンロードすることができる
  • 左手だけで入力することができる

curlと違って、再帰的にファイルをダウンロードすることができるのが最大の特徴です。
wgetで対応しているプロトコルは、HTTP・HTTPSFTPのみです。
実は、アップロードもできたんですね…

今まで、wgetコマンドにオプションを使ったことがなかったんですが、便利なオプションがあるんですね!
ここで、wgetコマンドの便利なオプションを紹介したいと思います。

# index.htmlファイルをダウンロード
wget http://xxxxx/yyyyy/zzzzz/index.html

# オプション「-r」
# ディレクトリにあるファイルを全部ダウンロード
wget -r http://xxxxx/yyyyy/zzzzz/

# オプション「-I」 
# 再帰的にダウンロードする場合に、階層の数を指定する
wget -r -I 3 http://xxxxx/yyyyy/zzzzz/

# オプション「-A」 
# 指定した文字列のあるファイルをダウンロードする
wget -r -A png http://xxxxx/yyyyy/zzzzz/

# オプション「-R」 
# 指定した文字列のあるファイルはダウンロードしない
wget -r -A jpg http://xxxxx/yyyyy/zzzzz/

curl

読み方は「カール」です。
curlの最大の特徴は、

  • サポートが充実している

です。
例えば、wgetと異なり、curlで対応しているプロトコルには、
FTP、FTPS、Gopher、HTTP、HTTPS、SCP、SFTP、TFTP、TELNET、DICT、LDAP、LDAPS、FILE、POP3IMAP、SMB/CIFS、SMTPRTMP、RTSP
があります。
他にも、wgetでは対応していないものが、curlでは対応されているという場合があります。

# オプション「-o(小文字)」
# ダウンロードしたファイルを指定したファイルに出力する
curl -o index2.html http://xxxxx/yyyyy/zzzzz/index.html

# オプション「-O(大文字)」
# index.htmlファイルをダウンロード
curl -O http://xxxxx/yyyyy/zzzzz/index.html

# 連番ファイルをダウンロードする
curl -O ftp://ftp.example.com/data[001-100].txt

curlコマンドは、SFTPプロトコルに対応しているので、
curlコマンドを使って、SFTPでアップロードできないのかな…?
どうやるのか、分からないので
教えていただけると嬉しいです!

参考:Linuxコマンド集 - 【 wget 】 ファイルをダウンロードする:ITpro
   Linuxコマンド集 - 【curl】ファイルのダウンロードやアップロードを行う:ITpro

Raspberry Pi 3の起動時に自動無線LAN接続(2)

前回の続きです!
前回:Raspberry Pi 3の起動時に自動無線LAN接続(1) - usmysaの開発メモ

前回、コマンドから無線LANに繋ぐ方法を紹介しました。
今回は、シェルスクリプトを使って、それを自動化するスクリプトを作成したいと思います。

シェルスクリプトとは

シェルスクリプトとは、複数の処理をまとめて行う(バッチ処理)ときに使われる、OSのシェルが直接解釈・処理できるスクリプト

引用:シェルスクリプトとは|shell script - 意味/定義 : IT用語辞典


そもそもスクリプトとは…

コンピュータプログラムの種類の一つで、機械語への変換や実行可能ファイルの作成などの過程を省略または自動化し、ソースコードを記述したら即座に実行できるようなプログラムのことをスクリプトという。

引用:スクリプトとは|script|scr - 意味/定義 : IT用語辞典

なるほど。
スクリプトとは、コンパイルが必要ない言語のことを言うんですね。
スクリプト言語の例としては、PythonRubyなどがあります。
つまり、シェルスクリプトとは、シェルのスクリプト(=コンパイルが必要ないプログラム)ってことですね!

起動時に自動で接続するための手順


f:id:usmysa:20160520171816p:plain


サーバ起動時に実行するコマンドとして、「chkconfig」があります。
これに作成したスクリプトを登録して、ラズパイが起動したときに実行させます。

いざ、スクリプトを書いてみる

スクリプト名は、「wifi-connect-script」となっています。
コードは以下のとおりです。

# !/bin/sh
# chkconfig: 2345 99 20
# description: wifi-connect-script

LoopConnectWiFi(){
  ssid=$1
  password=$2
  nmcli d wifi connect ${ssid} password ${password}
  result_connect='nmcli d wifi connect $ssid password $password'
  success_str="Device 'wlan0' successfully activated with"
  if [ ! 'echo ${result_connect} | grep ${success_str}' ]; then
    sleep 3
    LoopConnectionWiFi $ssid $password
  fi
}

readonly ssid_array=($(nmcli d wifi))
readonly wifi="SSID"
readonly wifi_pass="SSIDのパスワード"

case "$1" in
  start)
    for ssid in "${ssid_array[@]}"; do
      if [ $ssid = $wifi ]; then
        LoopConnectWiFi $wifi $wifi_pass
      fi
    done
    ;;
  stop)
    ;;
  *)
    ;;
esac

解説

# !/bin/sh

まず、これを書くことで、「このテキストはシェルスクリプトである」ということを宣言します。
なので、シェルスクリプトを書く場合は絶対必要な部分となります。

# chkconfig: 2345 99 20
# description: wifi-connect-script

これは、chkconfigの設定を記述しています。

2345 起動させるランレベル ランレベルを数字で入力。複数ある場合は、連続して2345のように各。- と書くと全てのランレベルでOFFとする。
99 起動時の優先度 数値(1~99までで記入するのが一般的)で記入。数字が大きくなるほど後で起動される。
20 停止時の優先度 数値(1~99までで記入するのが一般的)で記入。数字が大きくなるほど後で停止される。

ランレベルとは、Linux OSの動作モードを表します。

0:システムの停止
1:シングルユーザーモード、ネットワーク無し
2:マルチユーザーモードNFSマウントなし)
3:マルチユーザーモード(コンソール)、ネットワーク有り
4:未使用
5:マルチユーザーモードX Windows)、ネットワーク有り
6:システム再起動

引用:ランレベルとは(chkconfig) - Linux入門 - Webkaru

「description」はサービスの説明です。
適当に書いといて大丈夫です。

readonly ssid_array=($(nmcli d wifi))
readonly wifi="SSID"
readonly wifi_pass="SSIDのパスワード"

「nmcli d wifi」はWiFiの一覧を表示するコマンドで、その結果を「ssid_array」という配列に代入しています。
また、ここら辺の変数は書き換えることはないと思うので、一応「readonly」にして読み込み専用にしています。

case "$1" in
  start)
    for ssid in "${ssid_array[@]}"; do
      if [ $ssid = $wifi ]; then
        LoopConnectWiFi $wifi $wifi_pass
      fi
    done
    ;;
  stop)
    ;;
  *)
    ;;
esac

この部分は、switch文になってます。
「$1」っていうのは、シェルスクリプトでは第1引数を表します。
ここは覚えとく必要があります。
で、for文を使ってWiFi一覧の中から接続したいWiFiがあったら、LoopConnectWiFiメソッドを呼び出すという仕組みになっています。
この時、switch文で「start」の場合しか使ってないので、if文で書いた方が正しいのかなと思います。

nmcli d wifi connect ${ssid} password ${password}

前回で紹介しましたが、これでWiFiに接続しています。

result_connect='nmcli d wifi connect $ssid password $password'
success_str="Device 'wlan0' successfully activated with"
if [ ! 'echo ${result_connect} | grep ${success_str}' ]; then
  sleep 3
  LoopConnectionWiFi $ssid $password
fi

「result_connect」には、WiFiに接続した後に表示される文章を代入しています。
そして、result_connectが失敗していた場合、3秒ごとに再接続するような仕組みになっています。

chkconfigに登録する

$ sudo mv wifi-connect-script /etc/init.d/
$ sudo chkconfig --add wifi-connect-script
$ sudo chkconfig wifi-connect-script on


これで、Raspberry Pi 3が起動した時に自動で無線LANに接続します!
個人的に、ソースコードにパスワードを書くのは非常に嫌いなんですが、良い方法を知っている方がいれば教えて下さい!

参考:bash: コマンドの実行結果を配列や変数に代入する | deadwood
   (CentOS6まで)自作のサービスをchkconfigで登録する
   シェルスクリプトで関数を利用する - Qiita
   文字列Aに文字列Bが含まれるか - わからん
   CentOSの起動スクリプトを書き方(+注意点) | 本日も乙

Raspberry Pi 3の起動時に自動無線LAN接続(1)

Raspberry Pi 3には、2とは違い
無線LANのモジュールがデフォルトで付いているらしいです。
で、Raspberry Pi 3に無線経由でSSHをするには、以下のような手順が必要です!

1. ラズパイに有線ケーブルをつないで、SSHで入ったあと、無線LANに接続する
2. ラズパイに有線ケーブル・キーボード・ディスプレイをつないで、無線LANに接続する

無線経由でSSHしたくても、どっちにしろ有線で一度繋がなきゃいけないんです…
それって面倒くさくないですか!?
ってことで、今回はラズパイが起動した時点で、自動的に無線LANに接続するようなスクリプトを作成したいと思います。

環境

Raspberry Pi 3
CentOS 7

そもそも、どうやって無線LANに繋げるのか

ラズパイを無線LANに接続するには、「nmcli」というコマンドを使います。
「nmcli d」というコマンドを打って、ネットワーク機器の一覧を表示します。

# nmcli d
DEVICE  TYPE      STATE         CONNECTION 
eth0    ethernet  connected     eth0       
lo      loopback  unmanaged     -- 

しかし、無線LANであるwlan0が認識できておらず、見当たらりません。
そこで、wlan0を認識するには、Firmwareというソフトウェアを準備しなくちゃいけないっぽいです。

# yum -y install git
# git clone https://github.com/RPi-Distro/firmware-nonfree.git
# mv /lib/firmware/brcm{,.org}
# cp -R firmware-nonfree/brcm80211/brcm /lib/firmware/brcm
# curl -L --output /usr/bin/rpi-update https://raw.githubusercontent.com/Hexxeh/rpi-update/master/rpi-update
# chmod +x /usr/bin/rpi-update
# rpi-update
# reboot

で、リブートした後、もう一度ネットワーク機器の一覧を確認してみると

DEVICE  TYPE      STATE         CONNECTION 
eth0    ethernet  connected     eth0       
wlan0   wifi      disconnected  --         
lo      loopback  unmanaged     -- 

と、wlan0が認識されるようになります。
で、無線LANに接続するには、以下のような手順で接続することができます。

# nmcli d wifi connect ___ssid___ password ___password____
# nmcli d
DEVICE  TYPE      STATE      CONNECTION 
eth0    ethernet  connected  eth0       
wlan0   wifi      connected  ___ssid___    
lo      loopback  unmanaged  --  

ちなみに、無線LANの一覧を確認したい場合は、以下のようなコマンドを打てば確認できます

# nmcli d wifi

参考:Raspberry Pi 3 へCentOS7導入からWifi無線LAN接続まで設定メモ - YOMON8.NET

以上で、やっと無線LANに接続なるようになりました。
次回では、自動的に起動するようなスクリプトを書きたいと思います。

Raspberry Pi 3にCentOS 7をインストール

先日、Raspberry Pi 3を買ってみました!
お値段は5500円ぐらいですが、ケースや16GBのMicroSDやMicroUSBケーブルを買ったら合計1万円以上かかってしまいました…
友達から「ラズパイは安い!」って聞いてたのに、いざ買ってみると周辺機器を揃えるのにお金がかかってしまいます
バイトをしてない大学生には、ちょっと痛手です…
せっかく、買ったのでRaspberry Piで面白いものを作りたいと思います!

Raspberry Piは「Raspbian」というOSが推奨されてるっぽいんですが、
自分はCentOSが使い慣れているので今回、CentOS 7をインストールさせたいと思います。

準備

まず、以下のURLからイメージファイルをダウンロードします。
http://buildlogs.centos.org/centos/7/isos/armhfp/

このダウンロードしたファイルは「xz」という初めて見る拡張子になってます。

xz は、データ圧縮プログラムのひとつ、およびその圧縮データのフォーマットである。LZMA2圧縮アルゴリズムを利用している。先行するgzip, bzip2と比較すると、圧縮時にはこれら以上の時間とメモリを消費するが、圧縮率では概ね優位で、また伸張速度もgzipより多少遅いがbzip2よりは速い傾向にある。このことから、特にアーカイブ配布用途として、.tar.gzや.tar.bz2と並んで採用されるケースが増えている(.tar.bz2にとって代わる場合もある)。アーカイブ機能はない。

引用:xz (ファイルフォーマット) - Wikipedia

ふーん、よく分からないッス!笑
で、このファイルを解凍するために、コマンドをインストールする必要があります。
自分のPCはMacなので、Macでコマンドをインストールする方法を紹介したいと思います。

$ brew install xz
$ xz -d xxxx.xz #解凍する方法

MicroSDに書き込み

PCにMicroSDを差し込んで、MicroSDカードの場所を調べる必要があるそうです。

$ diskutil list
  #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *121.3 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:          Apple_CoreStorage Macintosh HD            120.5 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1 (internal, virtual):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:                  Apple_HFS Macintosh HD           +120.1 GB   disk1
                                 Logical Volume on disk0s2
                                 FD2938A5-EF87-4FFA-B79B-C2D6B41336E0
                                 Unlocked Encrypted
/dev/disk2 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *15.9 GB    disk2
   1:               Windows_NTFS SDCard                  15.9 GB    disk2s1

で、表示されたものを確認すると、MicroSDの場所は「disk2s1」ということが分りました。

次に、MicroSDをアンマウントします。

sudo diskutil umountDisk /dev/disk2
Unmount of all volumes on disk2 was successful


MicroSDの場所が「disk2s1」なのに、「disk2」をアンマウントするのはよく分からないです…
ここは、注意ですね!

$ sudo dd bs=1m if=xxxxx.img of=/dev/disk2
3072+0 records in
3072+0 records out
3221225472 bytes transferred in 1501.605507 secs (2145188 bytes/sec)


これで、MiroSDに書き込むことができました!
ちなみに、「dd」は書き込み(=コピー)をするコマンドっぽいです。
詳しくは、以下のURLを参照して下さい。
ddコマンド:Linuxコマンド集 - 【 dd 】 ファイルの変換とコピーを行う:ITpro

参考:Raspberry PiにRaspbianをインストールする for Mac OSX - Qiita

自分が犯した失敗

diskutilコマンドでMicroSDの場所が「disk2s1」と表示されていたので、
書き込みの時、「of=/dev/disk2s1」ってしたら、書き込みに失敗してました。
書き込みに失敗していたので、Raspberry Piが起動しなくて、泣いてました…
なので、みなさんご注意ください!

Rubyの勉強(2)

前回の続きです!
前回:Rubyの勉強(1) - usmysaの開発メモ

それでは、さっそく気温データのCSVファイルをRubyで読み込んで、データベースに追加したいと思います。

CSVファイルが読み込めない!

for csv_i in 1..15 do
  filename = "data-" + csv_i.to_s + ".csv"
  csvData = CSV.read(filename)
end

15個あるCSVファイルをfor文を使って、読み込もうとしたら

# ruby setDB_weatherData.rb 
/usr/local/rbenv/versions/2.3.1/lib/ruby/2.3.0/csv.rb:2016:in `=~': invalid byte sequence in UTF-8 (ArgumentError)
	from /usr/local/rbenv/versions/2.3.1/lib/ruby/2.3.0/csv.rb:2016:in `init_separators'
	from /usr/local/rbenv/versions/2.3.1/lib/ruby/2.3.0/csv.rb:1538:in `initialize'
	from /usr/local/rbenv/versions/2.3.1/lib/ruby/2.3.0/csv.rb:1273:in `new'
	from /usr/local/rbenv/versions/2.3.1/lib/ruby/2.3.0/csv.rb:1273:in `open'
	from /usr/local/rbenv/versions/2.3.1/lib/ruby/2.3.0/csv.rb:1339:in `read'
	from setDB_weatherData.rb:19:in `<main>'

というエラーが表示されました。
「invalid byte sequence in UTF-8 (ArgumentError)」っていうことは、utf-8じゃないから問題が発生してるのかな?

原因

このCSVファイルをどうやって作ったかは分からないですが、
Excelで作ったCSVSJISとして保存されるようです。
Excelで作った場合だと、終端文字が"\r\n"になるそうです。
その結果、エラーが発生してうまくいってないようです。

解決策

CSV.foreach('CSVファイル名', headers: true, row_sep: "\r\n", encoding: "SJIS") do |row|
  puts row[0] #rowの0番目を表示
end

ちなみに、「row_sep」とは行区切り文字列として使用する文字列を返すメソッドらしいです。

参考:Rails3でExcelで作ったSJISのCSVファイルをUTF-8で読み込む方法 - temitaの不思議空間
   ライブラリ:csv > クエリ:row_sep > バージョン:1.9.3 | るりまサーチ (Ruby 1.9.3)
  

何とか解決できたので、はやくデータベースに追加して
夜ご飯を食べたい…