Shiopon Labo

しおぽん(@shiopon01)の個人ブログです

Minikubeを無料枠のAWS EC2 t2.microで実行しよう

Kubernetesクラウドサービスで扱おうとするとどうしても課金が発生してしまうので、無料枠の t2.micro で実行できないかと思ってやってみた記事。つまり、 Minikube を AWS EC2 t2.micro で実行する記事 です。

EC2 t2.micro

今回使用するインスタンスのスペックは次の通り…

  • AMI:Ubuntu Server 18.04 LTS (HVM), SSD Volume Type - ami-09c81ecf1c2b2ef70
  • インスタンスタイプ:t2.micro
  • ストレージ:汎用SSD(gp2) 30GiB
  • vCPU:1
  • Mem:1GiB

※ コンソールでの作業時、もしsudoで sudo: unable to resolve host ip- ... みたいな文言が出力される場合は、 /etc/hosts にホスト名を追記すれば消せます。

$ sudo sh -c 'echo 127.0.1.1 $(hostname) >> /etc/hosts'

インストール作業

Docker

Kubernetesはホスト上で実行するので、 Docker をインストールしておきます。

sudo apt-get update
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
$ docker -v
Docker version 17.03.2-ce, build f5ec1e2

Get Docker CE for Ubuntu | Docker Documentation

kubectl

Kubernetesを操作するための kubectl をインストールしておきます。

sudo apt-get update && sudo apt-get install -y apt-transport-https
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubectl
$ sudo kubectl version
Client Version: version.Info{ ,,,

https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-on-linux

Minikube

Minikube をインストールします。(コマンドのバイナリをダウンロードするだけです)

curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 && chmod +x minikube && sudo cp minikube /usr/local/bin/ && rm minikube
$ minikube version
minikube version: v1.1.1

Install Minikube - Kubernetes

Minikubeの実行

管理者になって、 minikube start からMinikubeを起動します。Kubernetesはホスト上で実行するためVMドライバーは不要で、オプションでは none になっています。

$ sudo -i
# minikube start --vm-driver=none

コマンドの実行後、1回目の起動はCPU数が問題で apiserver が起動しないかもしれません。

しかし諦めずに2回目の起動を行うと(何故か)起動できるので、2回目の実行を行ってムリヤリMinikubeを起動してあげてください。

# minikube start --vm-driver=none
...
[ERROR NumCPU]: the number of available CPUs 1 is less than the required 2
...
Sorry that minikube crashed.

# minikube start --vm-driver=none
...
Done! kubectl is now configured to use "minikube"

Minikubeが動作すると minikube statuskubectl から状態を確認することが出来ます。

# minikube status
host: Running
kubelet: Running
apiserver: Running
kubectl: Correctly Configured: pointing to minikube-vm at ...

# kubectl get nodes
NAME       STATUS   ROLES    AGE    VERSION
minikube   Ready    <none>   150m   v1.14.3

おっと、この時点でもうメモリが悲鳴を上げている気が…。

f:id:shiopon01:20190621151705p:plain
htopの結果

いろいろ動かしてみよう

ひとまずMinikubeが動いたので、その上でいろいろ動かしてみましょう。

EchoServer

動作確認を兼ねて、 gcr.io/google_containers/echoserver を使って簡易のWebサーバーを立ち上げてみます。

kubectl run hello-minikube --image=gcr.io/google_containers/echoserver:1.4 --port=8080
kubectl expose deployment hello-minikube --type=NodePort

ここで作成したサービスのポートにcurlでアクセスすると情報が返ってきます。セキュリティグループでこのポートを開放してあげれば http://IPv4パブリックIP:ポート でブラウザからもアクセスできますね。

# kubectl get svc
NAME             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
hello-minikube   NodePort    10.108.62.7   <none>        8080:30732/TCP   24h

# curl localhost:30732
CLIENT VALUES:
client_address= ...

EchoServerの削除は次のコマンドで行えます。

kubectl delete services hello-minikube
kubectl delete deployment hello-minikube

Jenkins

次はEchoServerよりちょっとだけ大きな Jenkins を動かしてみましょうか。

とりあえずボリュームの共有とかデータの保存とかは考えず、動作の確認をしてみましょう。JenkinsのReplicationControllerとServiceを起動するために次のYAMLテンプレートを用意しました。

kubernetes-jenkins.yaml · GitHub

applyします。

# kubectl apply -f kubernetes-jenkins.yaml
replicationcontroller/jenkins created
service/jenkins-svc created

お、ちゃんと動いているらしい…。

# kubectl get pods,svc
NAME                READY   STATUS    RESTARTS   AGE
pod/jenkins-hnz4k   1/1     Running   0          27s

NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/jenkins-svc   NodePort    10.105.128.151   <none>        8080:30080/TCP   27s

でもメモリは辛そう…。

f:id:shiopon01:20190621153735p:plain
htopの結果2

※ Jenkinsを2分くらい起動していると apiserver が死にます。(kubectlが使えない!!)

# # kubectl get pods,svc
Unable to connect to the server: net/http: TLS handshake timeout

# minikube status
host: Running
kubelet: Running
apiserver: Stopped

こうなるともう minikube stop しか…。

apiserverは死にましたが他は生きてるので、一応Jenkinsにはアクセスできます。セキュリティグループでTCP 30080を開放すれば http://IPv4パブリックIP:30080 でブラウザからアクセスも可能。(性能は足りてないので運用はできないと思いますが…)

f:id:shiopon01:20190621154501p:plain
Jenkins

結果

MinikubeはEC2のt2.microでも一応動きますが、使うとしても定期タスクの実行くらいでしか使えなさそうです。Kubernetesの操作を学ぶくらいには良さそう。

GitLabとかJenkinsみたいなWebアプリの運用は性能がショボいので厳しいですね。

Erlang/Elixir のプロセスを5歳児でも分かるくらい丁寧に説明する記事(draft)

Erlang を始めて最初によく分からないと感じたのはあの癖の強い構文でしたが、一番良くわからなかったのは Erlang の目玉機能と言っても良い並列指向プログラミングをかじるようになった辺りでした。

突然現れた Erlang プロセス、メッセージパッシング、エラー処理、etc。この記事では、自分より後に Erlang を学び始めた人が少しでもこれら並列実行を構成する要素に惑わされないようにいくつかの要素を丁寧に説明していきたいと思います。

実行環境

まず大前提ですが、 Erlangコンパイル言語 です。

コンパイルされたソースコード中間言語.beam)に変換され、Erlang の実行環境 BEAMErlang Virtual Machine)で動作します。BEAM 自体は C 言語で作成されていて、 GitHub で公開されている ので誰でもソースコードを覗くことができます。(蛇足ですが、BEAM は Java で言うところの JVMRuby で言うところの YARV です)

さて、Erlang が注目されている点として、Erlang は並列指向プログラミングが可能な言語であるという点が挙げられます。この部分を理解しようと思うなら、BEAM がどうやって生成された中間言語を実行しているかという点を探ることが近道でしょう。

BEAM の仕事は単純にコンパイルされた中間言語を読み解いて実行することですが、Erlang の並列性を実現するため実行にはいくつかの要素を取り入れています。例えば、プロセス、メールボックス、スケジューリングなど。そして、この要素のいくつかを丁寧に説明することがこの記事の本題です。

Erlang プロセス

Erlang の並列性を実現するための要素としてまず現れるのは Erlang プロセス です。

BEAM は並列性を実現するため、Erlang を実行する仕組みに OS のプロセスと同じような仕組みを取り入れています。概念的には、BEAM という OS の上で動作する Erlang プロセスという認識で間違いはありません。Erlang プロセスは OS のプロセスのようにメモリやストレージまで完全に分離されています。(Golang の goroutine に近いものがあると感じています)

ただこの Erlang プロセス、OS のプロセスほど重くも複雑でもありません。実際の所、BEAM の中での Erlang プロセスの実体は process という名前の C の小さな構造体です。大変軽量なため、ErlangRuby で1つのオブジェクトを作るくらいの感覚でプロセスを生成することができます。そして多数生成したプロセスがプロセス間通信を行いながらプログラムが実行される部分が Erlang の醍醐味でもあります。

Erlang プロセスには次のようなデータが格納されています。

  • プロセス ID
  • ステート(状態保存用)
  • メールボックス(Queue)
  • 関数(メッセージリスナー)
  • etc

完全に分離されているので外部からステートの情報を操作することは出来ません。また、プロセス ID は分離されたプロセス同士がプロセス間通信を行うための住所のようなものです。

例えば、Erlang には erl で起動できる対話形式で実行できる Erlang エミュレーターがありますが、このエミュレーター自体も実体は BEAM で動作するプロセスの1つです。実行中プロセスの ID はビルドイン関数 erlang:self/0 で確認することができます。

1> self().
<0.79.0>

(この <0.79.0> みたいなのがプロセス ID です)

他の要素であるメールボックスやメッセージリスナーなどの機能はプロセス間通信を行うための仕組みであり、各プロセスに実装されているものです。

さて、ここまででもう Erlang が BEAM のプロセスを駆使して実行されていることと、Erlang プロセスがどういうものなのかを少しは分かってもらえたと思います。次からはプロセス同士が通信するための仕組みを見ていきましょう。

補足:ステート

プロセスには状態管理のために確保された専用ストレージ(ステート)が存在しています。 そのプロセスによってのみ変更可能で、基本的には関数とやり取りを行って状態を変化させていきます。

プロセスを生成するために BEAM はヒープ領域を確保しますが、初期のヒープ領域が小さめに設定されている理由は何十万ものプロセスを同時に実行するためだとかなんとか。

補足:メールボックス

他プロセスから自プロセスに送信されたメッセージを保存するためのキューを メールボックス と呼びます。プロセス間通信の基本は、送信側が相手のメールボックスにメッセージを push し、受信側が自分のメールボックスからメッセージを pop することにあります。

(途中)

Emacs で再起動せずに設定ファイルを反映させる

Emacs で、変更した設定ファイルを再起動せずに反映させたい時は M-x load-file を使える。

ここで .emacs.d/init.el を指定すると全設定ファイルの読み直しが行われる。

M-x load-file RET ~/.emacs.d/init.el

flex.phys.tohoku.ac.jp

定期的に忘れるのでここにメモ。

VSCodeの設定・拡張機能を環境間で共有する

追記

こんなものがあるらしい。この記事いらんやんけ!

github.com

本題

VSCodeの設定を環境間で共有するのはまあまあ面倒くさい(自分はMac/Ubuntuで設定を共有している)。この記事ではそのあたりについて言述する。

共有したい設定

VSCodeで共有しておきたい設定は次の2つ。

settings.json はOSによって保存場所が違うみたいで、例えば UbuntuLinux) だと .config の下とかに保存されているが、 Mac だともっと深いところに保存されている。

  • Windows %APPDATA%\Code\User\settings.json
  • Mac $HOME/Library/Application Support/Code/User/settings.json
  • Linux $HOME/.config/Code/User/settings.json

setting.json は bashrc のように dotfiles のリポジトリ等でファイルを管理して、上記の場所にシンボリックリンク等を張れば共有することができるので、難しくはない。

ln -s $HOME/rc/settings.json $HOME/.config/Code/User/settings.json

code.visualstudio.com

曲者なのが拡張機能で、拡張機能本体の容量がまあまあ大きいのでそのままリポジトリ等で管理するわけにはいかない。(できないこともないが、 push や clone にめっちゃ時間がかかったりする)

そのため、拡張機能拡張機能の名前のリストのみ管理することにする。これには code --list-extensions コマンドが利用でき、これでインストールされている拡張機能のリストを出力することができる。(Maccode コマンドを使うのに少し設定が必要らしい https://code.visualstudio.com/docs/setup/mac

$ code --list-extensions
bungcip.better-toml
codezombiech.gitignore
Compulim.vscode-clock
dbaeumer.vscode-eslint
...

この出力結果をファイルに保存してリポジトリとかで管理して共有する。

code --list-extensions > $HOME/rc/extensions

このファイルから1発でインストールできたらいいけど、ドキュメント見たかんじ欲しいコマンドは無さそう…。

code.visualstudio.com

出力した拡張機能リストからのダウンロードには code --install-extension を使う。ただし、ドキュメントにあるように1つづつしかダウンロードできないため、ファイルの行をループして1つづつインストールしていく。このスクリプトも extensions のファイルと一緒に管理しておこう。

while read ext; do
  code --install-extension $ext
done <$HOME/rc/extensions

一応、実行したらこんなかんじ。

$ sh install.sh 
Found 'bungcip.better-toml' in the marketplace.
Installing...
Extension 'bungcip.better-toml' v0.3.2 was successfully installed!
Found 'codezombiech.gitignore' in the marketplace.
Installing...
Extension 'codezombiech.gitignore' v0.6.0 was successfully installed!
Found 'compulim.vscode-clock' in the marketplace.
Installing...
Extension 'compulim.vscode-clock' v0.0.1 was successfully installed!
Found 'dbaeumer.vscode-eslint' in the marketplace.
Installing...
...

まとめ

環境の共有には次の手順が必要。

$HOME/rc/vscode/settings.json$HOME/rc/vscode/extensions が存在している状態なら次のスクリプトでなんとかなる。(Windowsなし)

# setting.json

if [ "$(uname)" == 'Darwin' ]; then
  SETTINGS_PATH="$HOME/Library/Application Support/Code/User/settings.json"
elif [ "$(expr substr $(uname -s) 1 5)" == 'Linux' ]; then
  SETTINGS_PATH="$HOME/.config/Code/User/settings.json"
elif [ "$(expr substr $(uname -s) 1 10)" == 'MINGW32_NT' ]; then                                                                                           
  echo "Windows is not supported."
  exit 1
else
  echo "Your platform ($(uname -a)) is not supported."
  exit 1
fi

ln -s $HOME/rc/vscode/settings.json $SETTINGS_PATH

# install extensions

while read ext; do
  code --install-extension $ext
done <$HOME/rc/vscode/extensions

echo 'done.'

Homebrew を使うなら brew bundle を使っていくべきだ

Homebrew はMacユーザーなら知らぬものはいないレベルで有名なパッケージマネージャーだ。
きのこたけのこのように MacPorts と戦わされがちなポジション。僕はたけのこ派

Homebrew でパッケージをインストールするときは通常 brew install を使うが、実は Brewfile というものを使って複数パッケージをまとめてインストールすることもできる。RubyでのGemfile、Nodeでのpackage.jsonみたいなもので、使ってない人はとりあえず使ったほうが良い。

とりあえずHomebrewのインストール。公式のやつをコピペしよう。

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

brew.sh

この後すぐBrewfileを用意して brew bundle を行う。

自分が普段使っているBrewfileはこちら。こう見るとぜんぜん使ってないものばっかりだ。(人のこういうファイルを見るのが好きなのでみんな晒してくれ)

gist.github.com

追加で brew install したくなった時は気持ちをぐっとこらえ、このファイルに追記して brew bundle を行う。

使い方としては、このファイルをローカルに落として brew bundle を実行するだけ。App Storeのアプリを落としてくれる mas では初回の警告が出るかもしれない。また、 ## mas kakin 以下の行は課金アプリで購入していないと普通にエラーを吐くと思うので、そのまま使う場合はコメントアウトするなりしてもらいたい。

$ curl https://gist.githubusercontent.com/shiopon01/a584cc20552cf3eed3012865c0b020c5/raw/298d9ff2a48783ac64ac4f2bd5c40177213b47c3/Brewfile > Brewfile
$ brew bundle

インストール後はインストールした Alfred 3Karabiner-Elements などいくつかの常駐アプリの起動と設定、 nodenvgoenv でのプログラミング言語インストールを行う。

ショートカット

SkitchのスクショのショートカットがMacOSのものと被るので注意。自分はMacOSの方をOFFにしている。

f:id:shiopon01:20190102024539p:plain

明けましておめでとうございます。抱負など

明けましておめでとうございます!

1995年生まれ男の自分は今年は猪年の年男でもあり、本厄の年でもあります。厄年でもあるので慎重に、しかし猪年らしく 一意専心 に何事にも取り組んでいける年にできたらなと思います。

ちなみにFGO福袋は酒呑童子が出ました。はじめての★5アサシンありがとう!

f:id:shiopon01:20190101013631j:plain

新年の抱負

抱負なのであんまり公言するものでもありませんが、2019年は何かまじめなサービスをリリースすることを考えています。ユーザー○人獲得とかではなくて、単純にリリースすることが目標です。

これは実は2018年後半から考えていて、せっかく個人のサービスなら流行りのアーキテクチャとか技術をもりもり積んでいこうということで、そういうインプットをなんやかんや去年からやっています。(勉強すればするほど学ばないといけないことが増える辛さ)

具体的に自分がずっと学生時代から作りたいともんもんと考えていたアプリは次の2つ。

どちらも設計の段階で他のアプリの劣化のように思えてきて何度か挫折してきたものです。

学生の頃はOICのメディアフロンティアのためにRailsとかでこれらのプロトタイプを作成したりしましたが、こういう挫折もあって方向性を見失い、結局お蔵入りとなったり…。

どちらも大きくシェアを取っているアプリがすでに存在している分野でもあるので、今更同じ土俵にあがるのも…と気負いするところでもありますが、まあ個人アプリなんで、そのあたりは気にせず良い所はパクる精神で今年はゆるゆる作っていけたらなと思います。

他の抱負で言うと、最近練習し始めた絵描きでマシな絵を書けるようになるとか、結局学ぶだけ学んで大したものを生み出せてないAfter Effectsで映像作品を作るなど。文字だけでなく動画や音声でのアウトプットを行ってみたいという気持ちもあるので、そういうのもあります。

あ、あとIIDXで十段を取りたい!(PEN以来まともにやってないブランクもあって現在悲しみの1曲目落ち)

f:id:shiopon01:20190101021149j:plain

以下まとめです。2019年の shiopon01 の活躍にご期待ください。

  • サービスをリリース
  • After Effectsで映像作品を作る
  • マシな絵を書けるようになる
  • 映像や音声でのアウトプット手段の模索
  • IIDX十段取得
  • TOEIC 500点
  • etc(考え中)

(絵の練習はPixivのSenseiがまじで良いです)

sensei.pixiv.net

2018/1/1のステータス

  • 身長:174cm
  • 体重:77.2kg
  • TOEIC:310点くらい

今年は体重を減らすか、キープか増えるにしても筋肉で……が目標…。

Node.jsの初見殺し (x) => (y) => { ... }

引数定義する部分が2つある関数定義、まさに初見殺し。

const a = (x) => (y) => {
 console.log(x * y)
}

ただでさえ function (x) { ... }シンタックスシュガーみたいな (x) => { ... } (アロー関数)があったりするのに、アロー関数には引数定義が2つ付いた (x) => (y) => { ... } みたいな定義方法もある。調べても情報が多くないように思うし、そもそも調べ方もよく分からないかんじがある。この記事はこれの話。( this とかには触れない)

カリー化

さて、結論から言うとこれは カリー化 と呼ばれるもので、先述した関数定義は次の構文のシンタックスシュガーみたいなものである。カリー化についてはいろいろ記事があると思うので 部分適応カリー化 で検索してもらえたらと思う。

const a = function (x) {
  return function (y) {
    console.log(x * y)
  }
}

これは見たとおり、定義した a は引数 x を受け取って新しい関数を生成する関数だ。ここで生成される関数は最初に渡される x を保持しているので、 function (y) の中でも x を利用できる。

実際に 2 を渡して実行してみても、関数が生成されていることが分かる。この関数はもちろん 2 を保持している。

$ node
> const a = (x) => (y) => {
... console.log(x * y)
... }
undefined
> a (2)
[Function]

生成された関数は引数 y を受け取って console.log するだけのものだ。計算に使う x は最初の a (2) で渡したものが引き継がれている。

次の2つは呼び出し方は違うが、結果は同じものである。これは生成された関数に 3 を渡してそのまま実行するパターン。

> a (2)(3)
6

そしてこれが、関数を一旦変数にいれてから 3 を渡して実行するパターン。そのまま実行するパターンではカリー化するメリットが無いので、ふつうはこうやって使う。

> const b = a(2)
[Function]
> b (3)
6

ちなみに関数定義ではいくらでも引数の定義をアローで繋げることができて、つなげた分だけ階層を深くすることができる。好きなだけ繋げるべし。

> const c = (x) => (y) => (z) => {
... console.log (x + y + z)
... }
undefined
> c (1)(2)(3)
6

使い方

この構文を使うタイミングについて少し言述する。

カリー化とはつまり、新たな関数を生み出して処理の共通化を行うためのアプローチだ。関数を生成する際に様々な設定値などを渡すことで、最適な新たな関数を生み出すことができる。この結果、何度も呼び出すが決め打ちの引数が入る煩わしい関数のをスマートに定義することができるようになる。

ありがちだが、例えば次のプログラムは消費税を計算するものだ。ここでは calcJPTax という日本の消費税を計算する新しい関数を生成している。この関数を生成する際に渡す数値を変えて複数定義すれば、国に対応した消費税計算関数を簡単に作成することができる。

const product = (tax) => (value) => {
  return (value * tax)
}

const calcJPTax = product (1.08)

console.log(calcJPTax(100)) //=> 108

少し難しいが、カリー化する際に関数を渡すこともできる。これを使うことで好きな処理を関数に差し込むことができる。

これはコンソールに値を出力する関数で、最初に渡す関数の返り値によって内容を変更できる。

const say = (func) => (value) => {
  return console.log(func(value))
}

const sayHello = say ((name) => {
  return 'Hello, ' + name
})

sayHello('Mario') //=> 'Hello, Mario'

などなど。様々な場面で使いみちがある。