Ubuntu 18.04 で KVM + OpenvSwitch(NAPT)
いくつかのサイトを参考に作成.
環境
OS
Hypervisor
やりたいこと
正しい書き方かわからないけど概要はこんな感じ.
KVMの設定
基本は省略する.defaultネットワークを停止させ,自動起動を無効化する.
以下のコマンドでLinux標準ブリッジを無効化する.
rmmod bridge
OpenVSwitchの設定
インストール
apt install openvswitch-switch apt install openvswitch-common apt install python-openvswitch apt install python-netifaces
Ubuntu 18.04はnetplanでネットワークされるが,OVSに非対応なので従来の設定ファイルを行う. (netplanの設定でmatchというキーを使えばIPアドレスを付加できたが接続は未検証) まずは,/etc/netplan/に存在するファイルの中身を全てコメントアウトする.
次に以下のパッケージをインストールする.
apt install ifupdown
/etc/network/interfacesを編集.ブリッジの設定をここに書く.
auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto br0 iface br0 inet static address 192.168.0.1 netmask 255.255.255.0 network 192.168.0.0 ovs_type OVSBridge
保存したら設定を適用.
ifdown br0 ifup br0
ブリッジ設定ファイルを作成.
<network> <name>br0</name> <forward mode='bridge'/> <bridge name='br0'/> <virtualport type='openvswitch'/> </network>
設定ファイルを適用.
virsh net-define br0.xml virsh net-start br0 virsh net-autostart br0
作成したブリッジを仮想マシンに設定. この際にもともとあるmacvtapを選ばないように注意.
ufwとフォワーディング設定
/etc/ufw/before.rules
*filterと書かれている行の前に以下を記述.
*nat :PREROUTING ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] -F -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE COMMIT
/etc/ufw/sysctl.conf
net/ipv4/ip_forward=1
/etc/default/ufw
DEFAULT_FORWARD_POLICY="ACCEPT"
3つの設定を弄ったら,ufwを適用. ufwはデフォルトで全通信を遮断しインターネットに繋がらなくなるので注意(許可する設定をする必要があるけど省略).
ufw disable ufw enable
うまく行っているか確認.
iptables -L
dnsmasqの設定
インストールとSystemdで起動.
apt install dnsmasq systemctl enable dnsmasq systemctl start dnsmasq
/etc/dnsmasq.conf
listen-address=192.168.0.1 dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
ゲストOS側の設定
ゲストはnetplanで大丈夫.うまく行けばインターネットに繋がる.
network: version2: ethernet: ens3: dhcp4: true addresses: []
設定適用.
netplan apply
KVMでハマったところ
KVMでのハマった点をメモする. 以下の2つの現象が起きた.
- クローン後にホスト名を変更できない
- クローン後にDHCPが同じIPを排出する
環境
OS
仮想環境
- Compiled against library: libvirt 4.0.0
- Using library: libvirt 4.0.0
- Using API: QEMU 4.0.0
- Running hypervisor: QEMU 2.11.1
- virsh 4.0.0
前提
Ubuntu Server 18.04をインストール済みのイメージをvirt-cloneを用いてクローンした.
1. クローン後にホスト名を変更できない
問題
ホスト名を変更するhostnamectl
を用いても再起動すると元のホスト名に戻ってしまう.
解決
cloud-initの設定(/etc/cloud/cloud.cfg)を変更することで解決した.
# 前: preserve_hostname: false # 後: preserve_hostname: true
変更後hostnamectl
によりホスト名を変更して再起動.
2. クローン後にDHCPが同じIPを排出する
問題
virsh edit
でMACアドレスやUUIDが重複していないことを確認したにも関わらず,
DHCPでリースされるIPアドレスが重複してしまう.
解決
根本的な原因がわかっていないが,設定をリセットするコマンドであるvirt-sysprep
を利用することで,
別々のIPアドレスが振られるようになった.
virt-sysprep
を実行するために
ホスト側でlibguestfs-tools
をインストールする必要がある.
$ sudo apt install libguestfs-tools
インストール後,特定の設定をリセットする. もしかしたら無駄な設定もリセットしているかも.
$ sudo virt-sysprep -d [クローンしたマシン名] \ --enable dhcp-client, machine-id, net-hwaddr
Linux上のFirefoxでタイトルバーを隠す
LinuxでFirefoxを使う上で、タイトルバーが無駄に伸びているのが気になる。 Chromeでは、タブとタイトルバーが一緒になっているので、それが決めてでChromeを使っていた。
環境
Firefoxのバージョンによって、方法が違うかもしれない。
- Ubuntu: 16:04
- Desktop: gnome-shell
- Firefox: Mozilla Firefox 60.0.1
操作
- アドレスバーに
about:config
と入力し、設定画面に遷移。 browser.tabs.drawInTitlebar
と検索する。false
になっているので、true
にする。
結果
もともとの画面。タイトルバーが無駄にある
タイトルバーがなくなりスッキリした。
Railsデプロイで困ったことメモ
調べながら解決していったので、自分の詰まったところとそのリンクのまとめに近い。
やっていること
環境
- nginx
- Ruby on Rails 5
- puma
Rails側設定
/config/environments/production.rb
SSLの強制、/publicの公開、サイトのトップページをサブディレクトリに。
config.force_ssl = true config.public_file_server.enabled = true ENV["RAILS_RELATIVE_URL_ROOT"] = "/siteA" Rails.application.config.relative_url_root = "/siteA"
/config.ru
サブディレクトリで起動するように変更。
require_relative 'config/environment' map ActionController::Base.config.relative_url_root || "/" do run Rails.application end
/config/puma.rb
ここで、nginxで振り分けるsockを生成する設定。 ポート番号もアプリごとに分ける。
_app_path = "#{File.expand_path("../..", __FILE__)}" _app_name = File.basename(_app_path) _home = ENV.fetch("HOME") { "/home/アプリケーションのディレクトリ" } pidfile "#{_home}/run/#{_app_name}.pid" bind "unix://#{_home}/run/#{_app_name}.sock" daemonize true directory _app_path port ENV.fetch("PORT") { ポート番号をアプリごとに設定 }
nginx側設定
設定ファイルをいじり、サブディレクトリをRailsのアプリケーションのポートに振り分ける。 Rails側のタイムアウトの時間設定はproxyのタイムアウトで設定するのが注意どころ。
upstream siteA { server unix:///home/~/run/~.sock fail_timeout=0; } upstream siteB { server unix:///home/~/run/~.sock fail_timeout=0; } server { listen 80; server_name www.example.com; return 301 https://$host$request_uri; } server { server_name www.example.com; listen 443 default ssl; ssl on; ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; location /pll { proxy_pass http://siteA; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto https; proxy_redirect off; } location /hateconcat { proxy_pass http://siteB; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Proto https; proxy_redirect off; } location / { root /var/www/html; index index.html; } error_page 500 502 503 504 /500.html; client_max_body_size 4G; proxy_connect_timeout 605; proxy_send_timeout 605; proxy_read_timeout 605; send_timeout 605; keepalive_timeout 605; }
問題と解決のリンク
サブディレクトリへの展開
An unhandled lowlevel error occurred.
このようなエラーはSECRET_KEY_BASEが無いときに出ることが多いらしい。 起動時に環境変数を渡してあげる。
puma_error.logで確認できるのでわかりやすい。
Assetsをプリコンパイルしても、404エラーが出る。
プロダクション環境ではデフォルトでは、public/を公開しない。おそらくnginxなどに委ねることもできるからだと思う。
個人的に分かる範囲での正規表現
正規表現についての基本の基本を自分用にまとめる。
できるだけシンプルにまとめたかった。
他にもいろいろできるみたいだが、とりあえずはこれだけでもいろいろできそう。
ところどころ「・・・」とあるが、これは何か文字が入るという意味。
1文字の表し方
文字 | 概要 |
---|---|
[・・・] | 括弧内の文字の集合から1つ |
[^・・・] | 括弧内の文字以外の集合から1つ |
[a-z] | 小文字、大文字、数値など範囲した集合から1つ |
. | 任意の1文字 |
^ | 行頭 |
$ | 行末 |
バックスラッシュによる特殊な1文字
文字 | 概要 |
---|---|
\d | 数字(digit?)。[0-9]と等価。 |
\w | 英数字(word?) |
\s | スペースまたはタブ |
\t | タブ |
\n | 改行 |
[メタ文字] | メタ文字そのものを文字として利用したいときに\でエスケープ |
繰り返し系
文字 | 概要 |
---|---|
{n} | n回繰り返す |
{m,n} | mからn回繰り返す |
+ | 1回以上繰り返す |
* | 0回以上繰り返す |
? | 0回or1回。言い換えると省略可能な文字 |
+?, *? | 最小マッチ。マッチする中で+や*の繰り返しが最小限のもの。 |
文字をまとめる括弧
文字 | 概要 |
---|---|
(・・・) | 文字をまとめる。言い換えると文字列。 キャプチャしたい時にまとまりとして使用。 キャプチャは後述。 |
(?:・・・) | ()でまとめたいが、キャプチャしなくていいものを記録しない。 |
| | 〜または〜。(abc|def)など。 |
キャプチャ
一致するものの中の一部を取り出すことができる。()を使って実現する。
# 言語100本ノックの22問目より。 import sys import re lines = ["[[Category:イギリス|*]]", "[[Category:英連邦王国|*]]", "[[Category:G8加盟国]]", "[[Category:欧州連合加盟国]]", "[[Category:海洋国家]]", "[[Category:君主国]]", "[[Category:島国|くれいとふりてん]]", "[[Category:1801年に設立された州・地域]]"] pattern = r'\[\[Category:(.*?)(\|.*)?\]\]' for line in lines: for match in re.finditer(pattern, line): print(match.group(1))
()を2回使っているが、1つ目の括弧の中身がほしいので、group(1)で取り出している。
取り出し方は言語によって異なると思うが、大体は、1つ目、2つ目という感じで取れそう。
メモ化とline_profilerによる計測
アルゴリズムの勉強などをあまりしていなかったので,メモ化という言葉は就活なんかでの技術試験で知るのが初めてだった.
メモ化自体はそこまで難しいことではなく,何度も同じ計算をしないで,一度計算したものをうまく保存することで計算結果を再利用するというものである.
例として,フィボナッチ数列がよく使われる.
フィボナッチ数列は,fib(n) = fib(n-1) + fib(n-2)で定義されるため,fib(k)が何度も呼び出されることになる.
ここで,メモ化を用いることで,一度計算したfib(k)を保存し,再利用する.
これを,時間計測ツールであるline_profilerを用いて,計測した.
# 通常のフィボナッチ数列 def normal_fib(n): if n == 0 or n == 1 : return n else: return normal_fib(n-1) + normal_fib(n-2) # メモするフィボナッチ数列 memo = [0 for x in range(100)] def memo_fib(n): if n < 2 : return n elif memo[n] != 0: return memo[n] else: memo[n] = memo_fib(n-1) + memo_fib(n-2) return memo[n] @profile def main(): print(memo_fib(20)) print(normal_fib(20)) if __name__ == '__main__': print("Memoization!!!") main() print(memo)
fib(20)で比較した出力結果は以下のよう.
Memoization!!! 6765 6765 [0, 0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] Wrote profile results to memo.py.lprof Timer unit: 1e-06 s Total time: 0.015669 s File: memo.py Function: main at line 19 Line # Hits Time Per Hit % Time Line Contents ============================================================== 19 @profile 20 def main(): 21 1 51.0 51.0 0.3 print(memo_fib(20)) 22 1 15618.0 15618.0 99.7 print(normal_fib(20))
当確率ではないランダム
サイコロのように当確率で目がでるのではなく、おみくじのように出現確率が変化するようなプログラムが必要になったので作成した。
実際にうまく言っているかを確認するため、ヒストグラムを表示させた。
weighted_random関数
- 入力:Numpyの配列に重み付けをした数値を入れる(合計が1である必要はない)
- 出力:ランダムな値(0~配列長-1)を返す
import numpy as np import matplotlib.pyplot as plt # 入力データ(それぞれ、10%,30%,60%) data = np.array([0.1, 0.3, 0.6]) # 関数 def weighted_random(data): # 累積 culm = [0] * data.shape[0] total = np.sum(data) for i in range(data.shape[0]): cuml[i] = np.sum(data[:i+1]) / total # 乱数から選択 s = np.random.rand() m = 0 for i in cuml: if s < i: break m += 1 return m # 表示 x = np.array([weighted_random(data) for _ in range(1000)]) plt.hist(x, rwidth=1.0, bins=3)
1000個の結果から見て大体100個、300個、600個程度の出力を得ている。 なんかもっと綺麗に書けないんだろうか。