「ソフトウェアエンジニアならば1時間以内に解けなければいけない5つの問題」をRubyで

ちょっと、というか結構古いけど 「ソフトウェアエンジニアならば1時間以内に解けなければいけない5つの問題」 というのを見つけた。

 cf. 1時間以内に解けなければプログラマ失格となってしまう5つの問題が話題に – SOFTANTENNA BLOG

大元のページはもうなくなっているようだけど、上のページに問題が載っているので Ruby でやってみた。

問題1

forループ、whileループ、および再帰を使用して、リスト内の数字の合計を計算する3つの関数を記述せよ。

def solv1(ary)
  result = 0
  for i in ary
    result += i
  end
  result
end

def solv2(ary)
  result = 0
  while ary.size > 0
    result += ary.shift
  end
  result
end

def solv3(ary)
  if ary.empty?
    0
  else
    x = ary.shift
    x + solv3(ary)
  end
end

puts solv1([1,2,3,4,5])
puts solv2([1,2,3,4,5])
puts solv3([1,2,3,4,5])

問題2


交互に要素を取ることで、2つのリストを結合する関数を記述せよ。例えば [a, b, c]と[1, 2, 3]という2つのリストを与えると、関数は [a, 1, b, 2, c, 3]を返す。

def solv(a1, a2)
  result = []
  until a1.empty?
    result << a1.shift
    result << a2.shift
  end
  result
end

p solv(['a', 'b', 'c'], [1, 2, 3])

問題3


最初の100個のフィボナッチ数のリストを計算する関数を記述せよ。定義では、フィボナッチ数列の最初の2つの数字は0と1で、次の数は前の2つの合計となる。例えば最初の10個のフィボナッチ数列は、0, 1, 1, 2, 3, 5, 8, 13, 21, 34となる。

require 'pp'

def fib(a, b, acc, n)
  if n == 0
    acc
  else
    acc << a
    fib(b, a + b, acc, n - 1)
  end
end

def solv
  fib(0, 1, [], 100)
end

pp solv

問題4


正の整数のリストを与えられたとき、数を並び替えて可能な最大数を返す関数を記述せよ。例えば、[50, 2, 1, 9]が与えられた時、95021が答えとなる。

def solv(ary)
  ary.map{|x| x.to_s }.sort.reverse.join("").to_i
end

puts solv([50, 2, 1, 9])

問題5


1,2,…,9の数をこの順序で、”+”、”-“、またはななにもせず結果が100となるあらゆる組合せを出力するプログラムを記述せよ。例えば、1 + 2 + 34 – 5 + 67 – 8 + 9 = 100となる 。

def combi(ary)
  if ary.size == 1
    ary
  else
    result = []
    x = ary.shift
    c = combi(ary)
    ["+", "-", ""].each do |op|
      result += c.map{|e| x + op + e }
    end
    result
  end
end

def solv
  a = [1,2,3,4,5,6,7,8,9].map{|n| n.to_s }
  result = []
  combi(a).each do |e|
    if eval(e) == 100
      result << e
    end
  end
  result
end

solv.each do |e|
  puts e + " = 100"
end

俺はソフトウェアエンジニアじゃないけど、どうにか1時間以内に5問ともできた。一番難しかったのは問題5だけど、意外にてこずったのは問題1。Ruby の for の使い方(というか for があること自体)を忘れていて、結局ここだけググった。

sshで接続できるようにする

bigswifty を WiMAX に接続したので、他のマシンとはネットワーク的に切り離されてしまった。まぁ、これは意図した通りなんだけど、ssh で接続できないのも不便なので、インターネット経由で接続できるように設定する。

ここからは bitswifty 上での作業。

sshdの設定

ポートを変更。

Port 12345

root でのログインを禁止。

PermitRootLogin no

設定を反映するために sshd を再起動。

[lcstorage@bigswifty ~]$ sudo systemctl restart sshd

ファイアウォールの設定

ポートを開ける。

[lcstorage@bigswifty ~]$ sudo firewall-cmd --add-port=12345/tcp --zone=public --permanent
success

設定をリロード。

[lcstorage@bigswifty ~]$ sudo firewall-cmd --reload
success

WiMAX端末のポートマッピング

192.168.100.1 にブラウザで接続して、設定ページから、ファイアウォール設定→ポートマッピングと進んで、WAN ポートの12345 を bitswifty の12345 番ポートに接続するようにする。と、ここで気が付いたんだけど bigswifty のポートは変更しなくても良かったんじゃないか?まぁいいか。

接続テスト

別のマシンからインターネット経由で ssh 接続してみると、無事接続できた。

公開鍵での認証

認証方法をパスワード認証から公開鍵認証に切り替える。公開鍵は GitHub からもってきた。

[lcstorage@bigswifty ~]$ mkdir .ssh
mkdir: ディレクトリ `.ssh' を作成できません: ファイルが存在します
[lcstorage@bigswifty ~]$ cd .ssh
[lcstorage@bigswifty .ssh]$ wget -O authorized_keys https://github.com/takatoh.keys
--2018-12-15 05:32:26-- https://github.com/takatoh.keys
github.com (github.com) をDNSに問いあわせています... 192.30.255.112, 192.30.255.113
github.com (github.com)|192.30.255.112|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 特定できません [text/plain]
`authorized_keys' に保存中

[ <=> ] 381 --.-K/s 時間 0s

2018-12-15 05:32:27 (2.28 MB/s) - `authorized_keys' へ保存終了 [381]

[lcstorage@bigswifty .ssh]$ chmod 600 authorized_keys[lcstorage@bigswifty .ssh]$ ls -al
合計 12
drwx------. 2 lcstorage lcstorage 48 12月 15 05:32 .
drwx------. 19 lcstorage lcstorage 4096 12月 15 04:34 ..
-rw------- 1 lcstorage lcstorage 381 12月 15 05:32 authorized_keys
-rw-r--r--. 1 lcstorage lcstorage 185 12月 4 03:56 known_hosts

接続テスト

再び、別のマシンから接続テスト。ちゃんと公開鍵認証で接続できた。

パスワード接続を禁止

最後に、もう一度 /etc/ssh/sshd_config ファイルを編集して、パスワード接続を禁止する。

PasswordAuthentication no

sshd を再起動。

[lcstorage@bigswifty .ssh]$ sudo systemctl restart sshd

これで完了。

参考ページ

MyDNS.JPを利用してDDNSを設定した

昨日はグローバル IP での接続に成功した。今日はDDNS(ダイナミック DNS)を使ってドメイン名で接続することに挑戦する。

無料の DDNS にはいくつか選択肢があるけど、日本語であること、更新に専用のクライアントソフトが不要であること、が決め手となって MyDNS.JP を利用することにした。

ユーザ登録(マスターIDの発行)

まずはユーザ登録。↓このページの JOIN US から登録ページに入る。

 cf. https://www.mydns.jp/

名前とかメールアドレスとかを入力して CHECK をクリックすると、確認画面になる。登録するドメイン名は1つなので子 ID の数は 0 のまま OK をクリック。すると登録したメールアドレスにマスター ID とパスワードが送られてくる。

ドメインの登録

送られてきたマスター ID とパスワードでログイン。DOMAIN INFO からドメインの登録ページに進んで、登録する。Domain 欄には希望するドメイン名(FQDN)、MX 欄は空欄、その下にはホスト名、A、マスター IDを設定。ドメインは無料で使える mydns.jp のサブドメインにした。CHECK →OK とクリックして登録完了。

IPアドレスの通知

ドメインの登録はできたはずなので、使用している IP アドレスの通知をする。通知の方法はいくつかあるけど、HTTP-BASIC を利用することにした。

[takatoh@bigswifty ~]$ wget -O - --http-user マスターID --http-password パスワード http://www.mydns.jp/login.html

↓こんな感じのレスポンスが返ってくれば OK。

<html>
<head>
<title>Free Dynamic DNS (DDNS) for Home Server and VPS etc  | MyDNS.JP</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<LINK href="./site.css" rel=stylesheet type=text/css>
</head>
<BODY BGCOLOR="#FFFFFF"
      TEXT="#304040"
      leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">

Login and IP address notify OK.<BR>
login_status = 1.<BR>
<BR>
<DT>MASTERID :</DT><DD>mydnsxxxxxx</DD>
<DT>REMOTE ADDRESS:</DT><DD>xxx.xxx.xxx.xxx</DD>
<DT>ACCESS DAYTIME:</DT><DD>2018/12/12 19:42:26 UTC</DD>
<DT>SERVER ADDRESS:</DT><DD>168.235.75.38</DD>
<BR>

</body>
</html>

確認

他のマシンから、ドメイン名でアクセスできるか確認したところ、ちゃんとアクセスできた。

cronで自動通知

最後に、cron を使って自動でアドレスを通知するようにする。とりあえず↓こんなスクリプトをかいて、

#!/usr/bin/sh

wget -O - --http-user マスターID --http-password パスワード http://www.mydns.jp/login.html > /dev/null 2> /dev/null

実行権限を付与、毎時 0 分と 30 分に実行するように cron を設定した。

[takatoh@bigswifty ~]$ crontab -l
0,30 * * * * /home/takatoh/bin/mydns_update.sh

これで完了。

WiMAX端末が届いたのでセットアップした

接続

クレードルも一緒に届いたので、クレードルに差して、bigswifty を有線で接続。クレードルには有線 LAN のポートがあるのだ。bigswifty を再起動してやると、自動的に IP アドレスが割り振られて接続できた。ちゃんとインターネットも閲覧できる。カンタン。

グローバルIPの設定

WiMAX 端末にグローバル IP を割り当てる設定をする。WiMAX では、通常は IP アドレス節約のためにプライベートアドレスが割り当てられるようだ。だけど今回はサーバを公開しようとしているので、グローバルなアドレスを割り当ててもらう必要がある。これはそのための設定。

まずは、ブラウザで 192.168.100.1 にアクセス。端末の設定ツールのページになるので、ユーザー名とパスワードを入力してログイン。ユーザー名は「admin」、パスワードは端末本体の背面に書いてある IMEI という番号の下5桁だ。

ログインしたら、設定→WAN設定→プロファイル設定と進んで、新しいプロファイルを Global という名前で作成する。設定の内容は↓このページの通りにしたらうまくいった。

 cf. https://www.uqwimax.jp/service/price/files/GIPOP_manual_HW.pdf

ここで一つ注意。上の設定でグローバル IP を割り当てられるようになるけど、固定ではないということ。これについては後でダイナミック DNS をつかう。

ポートマッピング

bitswifty を WEB サーバとして公開するので、引き続きポートマッピングの設定。設定→ファイアウォール設定→ポートマッピングと進んで、HTTP(80 番ポート)を bitswifty (のアドレス)に転送するように設定する。これで完了のはず。

試してみよう。実際にどんなアドレスが割り当てられているかは、↓このページでわかる。

 cf. https://www.cman.jp/network/support/go_access.cgi

上のページで確かめたアドレスに、他のマシンからブラウザでアクセスしたところ、無事ページが表示された。成功だ!

というわけで、今日のところはここまで。

CentOS: マシンを再起動するとサービスが動かない件

一昨日のエントリでは書かなかったけど、マシンを再起動したら uWSGI のサービスが起動しなかった。仕方がないので手動で起動してたんだけど、今日、問題が解決した。
原因は、ソケットファイルを作る /run/uwsgi ディレクトリがマシンを再起動すると消えてしまうこと。何故なら、/run は tmpfs というタイプのファイルシステムだかららしい。

[lcstorage@bigswifty ~]$ df -h
ファイルシス                      サイズ  使用  残り 使用% マウント位置
/dev/mapper/centos_bigswifty-root    50G  4.3G   46G    9% /
devtmpfs                            1.8G     0  1.8G    0% /dev
tmpfs                               1.8G  8.7M  1.8G    1% /dev/shm
tmpfs                               1.8G  9.8M  1.8G    1% /run
tmpfs                               1.8G     0  1.8G    0% /sys/fs/cgroup
/dev/sdb1                           917G  3.4G  867G    1% /mnt/wiwaxia
/dev/sdc1                           917G   77M  871G    1% /mnt/ottoia
/dev/sda1                          1014M  286M  729M   29% /boot
/dev/mapper/centos_bigswifty-home   179G  507M  178G    1% /home
tmpfs                               359M   32K  359M    1% /run/user/1001

詳しくは分からないけど、↓このページに解決策があった。

 cf.  /var/run の中身が再起動すると消えてしまうので・・・ – cles::blog

要するに、次のような内容で /etc/tmpfiles.d/uwsgi.conf  ファイルを作ってやればいいようだ。

d /run/uwsgi 0777 root root

これで無事、マシンを再起動しても大丈夫になった。

bottleアプリにNginx経由で接続できるようにする

1日空いてしまった。
uWSGI でデーモンとして動かすことに成功したので、今日は Nginx をリバースプロキシにして lcstorage というホスト名で接続できるようにする。

Nginx の前に、uWSGI の設定をちょっと変更。リクエストを待ち受けるのにポートじゃなくて UNIX ソケットを利用するようにする。

[uwsgi]
uid = lcstorage
gid = lcstorage
#http = :8080
socket = /run/uwsgi/lcstorage.sock
venv = /home/lcstorage/lcstorage/env
wsgi-file = /home/lcstorage/lcstorage/index.py
master = true
pidfile = /home/lcstorage/lcstorage/lcstorage.pid
daemonize = /home/lcstorage/lcstorage/lcstorage.log

http の設定をコメントアウトして socket を設定している。
でもって、sochekt のディレクトリをつくる。

[lcstorage@bigswifty ~]$ sudo mkdir /run/uwsgi
[lcstorage@bigswifty ~]$ sudo chmod 777 /run/uwsgi

つぎに、Nginx のバーチャルホストの設定。
/etc/nginx/conf.d/lcstorage.conf ファイルを作る。

upstream uwsgi-lcstorage {
    server unix:/run/uwsgi/lcstorage.sock;
}

server {
    # port
    listen      80;

    # server name
    server_name lcstorage;

    # document root
    #root        /home/lcstorage/lcstorage;

    # index
    #index       index.php index.html index.htm;

    client_max_body_size    4G;

    # log files
    access_log /var/log/nginx/lcstorage/access.log main;
    error_log  /var/log/nginx/lcstorage/error.log warn;

    keepalive_timeout     60;
    proxy_connect_timeout 60;
    proxy_read_timeout    60;
    proxy_send_timeout    60;

    location / {
        #root       /home/lcstorage/lcstorage;

        include uwsgi_params;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        uwsgi_pass unix:/run/uwsgi/lcstorage.sock;
    }
}

ログファイル用のディレクトリをつくる。

[lcstorage@bigswifty ~]$ sudo mkdir /var/log/nginx/lcstorage

そして、SELinux を無効化。コレ重要。

最後に、マシンをリブートして終了。
無事にバーチャルホストにアクセスできることを確認した。

CentOS 7上でbottleアプリをuWSGIで動かす(2)

一昨日の続き。今日は、アプリをデーモンとして動かす。
まず最初に、uWSGI の設定ファイルをちょっと書き換える。

最後の行を変更した。logger を daemonize に変えると何故かデーモンとしてバックグラウンドで動くようになるという、uWSGI のよくわからない仕様。

無事バックグラウンドで動くようになったら、systemd のサービスになるように設定ファイル /etc/systemd/system/lcstorage.service を書く。

これでいいはず。試してみよう。

[lcstorage@bigswifty system]$ sudo systemctl start lcstorage
[sudo] lcstorage のパスワード:
[lcstorage@bigswifty system]$ ps ax | grep uwsgi
28937 ?        S      0:00 /usr/bin/uwsgi /home/lcstorage/lcstorage/lcstorage.ini
28946 ?        S      0:00 /usr/bin/uwsgi /home/lcstorage/lcstorage/lcstorage.ini
28947 ?        S      0:00 /usr/bin/uwsgi /home/lcstorage/lcstorage/lcstorage.ini
28949 pts/1    S+     0:00 grep --color=auto uwsgi

成功のようだ。他のマシンのブラウザからも確認できた。

最後に、マシンの起動にあわせて自動的に起動するようにする。

[lcstorage@bigswifty system]$ sudo systemctl enable lcstorage
Created symlink from /etc/systemd/system/multi-user.target.wants/lcstorage.service to /etc/systemd/system/lcstorage.service.

さあ、あしたは Nginx 経由でアクセスできるようにしよう。時間があったらだけど。

Railsアプリのログをローテーションした話

Web を見ていてログローテーションの記事を見かけたので、そういえばさくらの VPS で動かしている Rails アプリのログはどうなってるんだろう、と思った。で、調べてみるとつぎの通り。

[takatoh@tk2-254-36564 log]$ ls -lh
合計 1.2G
-rw-r--r-- 1 root root  25K  5月  5 09:53 2017 development.log
-rw-r--r-- 1 root root 1.2G 12月  5 03:56 2018 production.log

うわあああああ、production.log が 1.2GB もあるううう!!!!!

こりゃ、ぜひともログローテーションしなきゃ、と思って調べてみると、Rails にはログのローテーションをする機能があるらしい。アプリのディレクトリ以下の config/environments/production.rb ファイルで、つぎのようにすればいいようだ。

ところが、実際に動いているアプリのファイル(の該当部分)を見てみるとつぎのようになっている。

コメントアウトしてあるのはいいとしても、Rails のバージョンのせいなのか何なのか、全然違う。はっきりいってこういうのはウカツに弄りたくない。

というわけで、logrotate でやってみることにした。さくらの VPS では logrotate がデフォルトで動いている(らしい)ので、/etc/logrotate.d 以下に設定ファイルを書いて置いてやればいい。

参考にしたページ:
cf. さくらのVPSの設定 – logrotateの設定
テストしてみる。

[takatoh@tk2-254-36564 logrotate.d]$ logrotate -dv unicorn_lathercraft
reading config file lathercraft
reading config info for /var/www/lathercraft/log/production.log 

Handling 1 logs

rotating pattern: /var/www/lathercraft/log/production.log  after 1 days (14 rotations)
empty log files are not rotated, old logs are removed
considering log /var/www/lathercraft/log/production.log
  log does not need rotating
not running postrotate script, since no logs were rotated

エラーらしきものは出てないので、いいのかな。
とりあえずこれで様子を見てみよう。

CentOS 7上でbottleアプリをuWSGIで動かす

専用ユーザーの作成

[takatoh@bigswifty ~]$ sudo useradd lcstorage
[sudo] takatoh のパスワード:
[takatoh@bigswifty ~]$ sudo passwd lcstorage
ユーザー lcstorage のパスワードを変更。
新しいパスワード:
よくないパスワード: このパスワードには一部に何らかの形でユーザー名が含まれています。
新しいパスワードを再入力してください:
passwd: すべての認証トークンが正しく更新できました。

よくないパスワードとか言われてるけど無視。sudo する権利をつける。

[takatoh@bigswifty ~]$ sudo usermod -G wheel lcstorage

ここからは新しいユーザーでの作業になるので、ログインし直し。

bottleアプリの配置

bitbucket.org から clone。

[lcstorage@bigswifty ~]$ git clone https://takatoh@bitbucket.org/takatoh/lathercraft-storage-py.git lcstorage
Cloning into 'lcstorage'...
Password for 'https://takatoh@bitbucket.org': 
remote: Counting objects: 127, done.
remote: Total 127 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (127/127), 13.63 KiB | 0 bytes/s, done.
Resolving deltas: 100% (55/55), done.

中に入って virtualenv を作成。

[lcstorage@bigswifty ~]$ cd lcstorage
[lcstorage@bigswifty lcstorage]$ virtualenv env
New python executable in /home/lcstorage/lcstorage/env/bin/python2
Also creating executable in /home/lcstorage/lcstorage/env/bin/python
Installing setuptools, pip, wheel...
done.
[lcstorage@bigswifty lcstorage]$ source env/bin/activate
(env) [lcstorage@bigswifty lcstorage]$

依存ライブラリのインストール。

(env) [lcstorage@bigswifty lcstorage]$ pip install -r requirements.txt
Collecting PyYAML==3.12 (from -r requirements.txt (line 1))
  Downloading https://files.pythonhosted.org/packages/4a/85/db5a2df477072b2902b0eb892feb37d88ac635d36245a72a6a69b23b383a/PyYAML-3.12.tar.gz (253kB)
    100% |████████████████████████████████| 256kB 6.9MB/s 
Collecting bottle==0.12.10 (from -r requirements.txt (line 2))
  Downloading https://files.pythonhosted.org/packages/5c/a9/f157af11b37834db992b14e43ade37a1bdc141485657cfa36dc1d99420a6/bottle-0.12.10-py2-none-any.whl (88kB)
    100% |████████████████████████████████| 92kB 8.9MB/s 
Collecting requests==2.12.3 (from -r requirements.txt (line 3))
  Downloading https://files.pythonhosted.org/packages/84/68/f0acceafe80354aa9ff4ae49de0572d27929b6d262f0c55196424eb86b2f/requests-2.12.3-py2.py3-none-any.whl (575kB)
    100% |████████████████████████████████| 583kB 8.1MB/s 
Building wheels for collected packages: PyYAML
  Running setup.py bdist_wheel for PyYAML ... done
  Stored in directory: /home/lcstorage/.cache/pip/wheels/03/05/65/bdc14f2c6e09e82ae3e0f13d021e1b6b2481437ea2f207df3f
Successfully built PyYAML
Installing collected packages: PyYAML, bottle, requests
Successfully installed PyYAML-3.12 bottle-0.12.10 requests-2.12.3

アプリの設定ファイルを作って、起動確認。設定はとりあえずはデフォルトのままで。

(env) [lcstorage@bigswifty lcstorage]$ cp config.yaml.example config.yaml
(env) [lcstorage@bigswifty lcstorage]$ python index.py
Bottle v0.12.10 server starting up (using WSGIRefServer())...
Listening on http://0.0.0.0:8080/
Hit Ctrl-C to quit.

よし、大丈夫そうだ。じゃ、virtualenv をぬけて uWSGI に行くぞ。

uWSGIで起動

まずは uWSGI のインストール。

[lcstorage@bigswifty lcstorage]$ sudo pip install uwsgi
Collecting uwsgi
  Using cached https://files.pythonhosted.org/packages/a2/c9/a2d5737f63cd9df4317a4acc15d1ddf4952e28398601d8d7d706c16381e0/uwsgi-2.0.17.1.tar.gz
Installing collected packages: uwsgi
  Running setup.py install for uwsgi ... done
Successfully installed uwsgi-2.0.17.1
You are using pip version 8.1.2, however version 18.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

上では省略しちゃったけど、最初のインストールの時にエラーが出て、sudo yum install gcc python2-devel しなきゃならなかった。
ともかくインストールできたので、コマンドラインから起動してみる。

[lcstorage@bigswifty lcstorage]$ uwsgi --http :8080 --wsgi-file index.py -H env

他のマシンからアクセスしたら、ちゃんと動いていたのでOK。

設定ファイルを書く。

起動確認。

[lcstorage@bigswifty lcstorage]$ uwsgi lcstorage.ini
[uWSGI] getting INI configuration from lcstorage.ini

OK。大丈夫そうだ。

とりあえずここまで。