アプリからドライバまで 現役エンジニアの濃い話
gitその2 rebaseとrebase -iを理解してgit-flowをやりやすくする :サイエンスパーク
author: 須藤さん
www.slideshare.net
前回、git
と svn
の違いから始まって、git-flow
のモデルまで説明されました。
今回は git-flow
の流れの続き。
rebase
+ rebase -i
についての話です。
ざっくりまとめると以下の機能があります。
rebase
指定したバージョンを元にコミット差分を乗せ直す機能です。
コミット履歴を見れば非常にわかりやすいですが、ブランチマージと異なっていて、コミットや修正の履歴が直列になります。rebase -i
はバージョンを指定して、履歴を操作する…という意味合いまでは一緒ですが、こちらはコミット履歴の統廃合することができます。
これによって、任意のタイミングでコミットしても後から整形すればいいという使い方ができます。
rebase -i (コミット範囲)
をすると、コミットログの一覧が出る。
これを編集して、git rebase continue
すると、コミットログが綺麗にまとまる。
なぜこれらが必要なのかというと、github
でプルリクエストをする時に、マージだと更新履歴がまともに見えないので論外。rebase は必須としても、履歴が汚れている場合は rebase -i
をした方がいいというお話です。
内容的には初級向けの印象でした。
rebase -i
久々に見ました…自分一人でやってるとコミット履歴気にしないもんなぁ(汗
Scikit-learnで機械学習系の何か
author: 安原
機械学習とAI の言葉の範囲が異なるというところから始まり、機械学習にも様々な種類があるというお話から始まりました。
そこから、決定木と、ランダムフォレストの説明につながりました。
- 決定木: 与えられた情報を「XX が YY 以上なら…」という様にしてデータを判断するツリーを決定木と言います。
- ランダムフォレスト: 決定木を学習データをランダム振り分けで学習させ、しかもそれを複数本用意するアルゴリズム。
こうした複数の学習機を利用して多数決で決定する様なアルゴリズムをアンサンブル学習と言います。
こちらも機械学習初級向けの内容とは思います。
決定木が学習する過程で条件を学習選択するアルゴリズムなどに関して突っ込んだら 絶対深かった 多分収集つかなくなった(汗
マインドフルネス
www.slideshare.net
今この瞬間に意識を傾けて物を考えるという方法。
集中力アップのほか、ストレス軽減の効果がある。
マインドフルネス瞑想!
- 力を抜いて楽にする
- 目を閉じます
- 鼻から深呼吸します
- 呼吸を数回繰り返します
- キリがいいくらい(5分位)その状態を維持します
変に何か感じたら、呼吸に意識を傾けることで頭をリセットします。
これを知ってると気分のリセットなどには実に有用です。
唯一のライフハック系のお話!
これ系は勉強会で出てくることは滅多にないので貴重です(^_^;)
Kubernetesの勉強がてら勉強会
参加してきたのは以下。
SpringBootでマイクロサービスを作ってKubernetesにデプロイしてみる【実践編】 dreamarts.connpass.com
尚、当日資料は下記。
K8S とは?
コンテナオーケストレーション技術。
コンテナオーケストレーションは、
- コンテナが落ちた時に自動でコンテナを用意してアップしてくれる
- 新しいバージョンリリースの時には「新しいコンテナを作って新版デプロイ」「接続設定を新版に切り替え」「旧版コンテナの削除」を順次行ってくれる
要するにバックのサービス云々を意識せすに、サービス維持を自動化してもらうツールと言える。
特徴として
- Declarative 宣言的
- Immutable 固定化
- AutoHealing 自己修復的
この特徴のおかげで、「致命的エラーが出た?新しいコンテナ作ればいいやん」という状況が生まれる。
ただし、このアーキテクチャを採用する場合
- サーバサイドにセッション情報を保持する作りは崩壊する。
- つまりステートレスサーバを作らなければならない(破棄前提)
個人的な意見だと Playframework でやればおk!
K8s の構成
基本的に
- kubectl
コントロールサーバ。ここから K8s の - kube-apiserver
kubectl からリクエストを受ける部分。
こいつが kube-scheduler に処理を移譲する - kube-scheduler
apiserver で受けた内容を元に、Pod を操作する部分。 - Kubelet
Podを起動し管理するエージェント - Pod
コンテナ本体
という構成とのこと。
Docker
カーネルレベルの機能としてプロセスを分離し、そこで Ubuntuなどの様なコマンドセットを用意することで、それぞれのディストリビューションとして振る舞う。
という様なお話。
すみません、自分で調べてたので聞き流してしまいました(汗
Docker 周りはこの辺聞けばいいと思う。
Flask 1.0.2 入門してみた
見通しよく、REST API を書きたいと思ったのだけど、Flask-restful がなんともしっかりした REST 特化しすぎてて微妙だったので、Flask 単品でちょっと実験したメモ
Install といっても…
Python だとこれだけ。
$ pip install Flask
ルーティング機構
と言っても非常にみたまま素直な感じ。
とてもわかりやすくてよい。
from flask import Flask, request app = Flask(__name__) @app.route('/seminers', methods=['GET', 'POST']) def seminers(): print(request) print(request.data) print(request.form) print(request.args) return 'Example message' app.run(debug = True, port = 3000)
リクエスト等の受け取り
で、どうなるのか気になったのがここなので、色々上記に流し込んで試してみた。
まずは単純リクエスト
$ curl http://localhost:3000/seminers
実行結果…まぁそうね。
null になってないあたり個人的に好き
<Request 'http://localhost:3000/seminers' [GET]> b'' ImmutableMultiDict([]) ImmutableMultiDict([])
URL パラメータ突っ込むと
$ curl http://localhost:3000/seminers?welcome=data
args が反応する
<Request 'http://localhost:3000/seminers?welcome=data' [GET]> b'' ImmutableMultiDict([]) ImmutableMultiDict([('welcome', 'data')])
今度は Form POST
$ curl -F "key=value" -F "msg=ひゃっはー" http://localhost:3000/seminers
request.form
が反応
<Request 'http://localhost:3000/seminers' [POST]> b'' ImmutableMultiDict([('key', 'value'), ('msg', 'ひゃっはー')]) ImmutableMultiDict([])
ファイルを送信してみる。適当に ss.png ファイルをキャプチャで作って食わせた
$ curl -F "key=value" -F "file=@ss.png" http://localhost:3000/seminers
えーっと(汗
<Request 'http://localhost:3000/seminers' [POST]> b'' ImmutableMultiDict([('key', 'value')]) ImmutableMultiDict([])
調べてみたら files
フィールドに格納されるらしい
@app.route('/seminers', methods=['GET', 'POST']) def seminers(): print(request) print(request.data) print(request.files) print(request.form) print(request.args) return 'Example message'
書き換えたリトライ。
でたでた。form/maltipart-formdata の区別は自動でやってくれてるらしい。
<Request 'http://localhost:3000/seminers' [POST]> b'' ImmutableMultiDict([('file', <FileStorage: 'ss.png' ('application/octet-stream')>)]) ImmutableMultiDict([('key', 'value')]) ImmutableMultiDict([])
なら、JSON はどうだろう?
$ curl -H 'Content-Type:application/json' -d "{"key":"val","key2":",val2"}" http://localhost:3000/seminers
ここにきて data
が反応。
application/json
にはデフォルトでは対応していない模様。
<Request 'http://localhost:3000/seminers' [POST]> b'{key:val,key2:,val2}' ImmutableMultiDict([]) ImmutableMultiDict([]) ImmutableMultiDict([])
json
パッケージとか使えばどうとでもなりそう。
import json @app.route('/seminers', methods=['GET', 'POST']) def seminers(): print(request) print(request.data) print(request.files) print(request.form) print(request.args) if request.headers['Content-Type'] == 'application/json': json_value = json.loads(request.data) print(json_value) return 'Example message'
そして投げ直す
$ curl -H 'Content-Type:application/json' -d "{\"key\":\"val\",\"key2\":\"val2\"}" http://localhost:3000/seminers
うまく行ったけど、あまりにダサい。
if とか腹立たしいのだけど…
<Request 'http://localhost:3000/seminers' [POST]> b'{"key":"val","key2":"val2"}' ImmutableMultiDict([]) ImmutableMultiDict([]) ImmutableMultiDict([]) {'key': 'val', 'key2': 'val2'}
application/json
にしか公開しないとかねーのかと思ったのだけど、
まぁこういう振り分けこそコントローラの仕事だし仕方ないのか(汗
Pandas の練習がてら、勉強会のトレンドを探ってみた
この辺の続きです。
4月の勉強会の開催状況と、事前申し込みの状況を眺めてみたが正解。
勉強会情報を拾ってくる
前回の流れですが、2サイト以上でやるので、少しだけ汎用化を考えてみた。
調べてみたら、ATND と COMPASS のAPI仕様がほぼ同一だった。
まずは共通の定義を用意して
import requests import sys class ClassRoom: def __init__(self, title, limit, accepted, waiting): def or_zero(v): if isinstance(v, int): return v return 0 self.title = title self.limit = or_zero(limit) self.joins = or_zero(accepted) + or_zero(waiting) def __str__(self): return f'ClassRoom({self.title}, {self.limit}, {self.joins})' class ClassRoomLoader: def load_class_rooms(self, month: str) -> list: pass class AbstractCompassAtnd(ClassRoomLoader): def __init__(self, api_url: str, max_event_num: int): self.api_url = api_url self.max_event_num = max_event_num def create_request_parameter(self, target_month: str, top: int): return {"ym": target_month, "count": self.max_event_num, "format": "json", "start": top} def request_to_compass(self, target_month: str, top: int) -> requests.Response: params = self.create_request_parameter(target_month, top) query = '&'.join([f'{key}={params[key]}' for key in params.keys()]) print(f'Request: {self.api_url}?{query}') return requests.get(self.api_url + '?' + query) def convert(self, event) -> ClassRoom: try: return ClassRoom(event['title'], event['limit'], event['accepted'], event['waiting']) except: print(f'Error: {sys.exc_info()[0]}') print(f'Value: {event}') raise Exception('Cannot convert') def load_request(self, target_month: str, top: int): class_rooms = [] r_get = self.request_to_compass(target_month, top) print(r_get.status_code) respond_json = r_get.json() for ev in respond_json['events']: class_rooms.append(self.convert(ev)) return (respond_json['results_returned'], class_rooms) def load_class_rooms(self, month: str) -> list: getted = self.max_event_num current_top = 1 class_rooms = [] request = 0 while getted == self.max_event_num: # 最大数以下が返ってきている == まだ次がある request += 1 getted, tmp_rooms = self.load_request(month, current_top) current_top += self.max_event_num class_rooms.extend(tmp_rooms) print( f'Request: {request}, Return: {getted}, currentTop: {current_top}') return class_rooms
単純に読み込む。
尚、読み取った勉強会情報は、pickle でダンプしておく。
class AtndLoader(AbstractCompassAtnd): COMPASS_API = 'http://api.atnd.org/events/' WANT_EVENT_NUM = 100 def __init__(self): super().__init__(AtndLoader.COMPASS_API, AtndLoader.WANT_EVENT_NUM) def convert(self, event) -> ClassRoom: return super().convert(event['event']) def create_request_parameter(self, target_month: str, top: int): return {"ym": target_month, "count": self.max_event_num, "format": "json", "start": top} class CompassLoader(AbstractCompassAtnd): COMPASS_API = 'https://connpass.com/api/v1/event/' WANT_EVENT_NUM = 100 def __init__(self): super().__init__(CompassLoader.COMPASS_API, CompassLoader.WANT_EVENT_NUM) def create_request_parameter(self, target_month: str, top: int): return {"ym": target_month, "count": self.max_event_num, "order": 1, "start": top} # import pickle for backup dump. import pickle if __name__ == "__main__": class_rooms = AtndLoader().load_class_rooms("201904") class_rooms.extend(CompassLoader().load_class_rooms("201904")) with open('backup.pickle', 'wb') as f: pickle.dump(class_rooms, f) print("\n".join([str(s) for s in class_rooms]))続きを読む
SQLAlchemy のさわりだけ
SQLAlchemy?
Python の O/R マッパーです。
スクリプト言語だし、たかが数行試すだけならSQL 文書いても…とは思ったのだけど、せっかくなので
検証は
- python : 3.6.8 (anaconda)
- sqlite3 インストール済み
- SQLAlchemy : 1.3
インストール
$ pip install SQLAlchemy
スキーマ作成
まずはインストールして
# Importing SQLAlchemy from sqlalchemy import create_engine, Column, Integer, String, Float from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy.orm.exc import NoResultFound
どうもテーブルがなければ作るという挙動らしい。
# Create Schema. Base = declarative_base() class ClassRoomInfo(Base): __tablename__ = 'class_room_info' id = Column(Integer, primary_key = True, autoincrement = True) title = Column(String(512)) limit = Column(Integer) joins = Column(Integer) def __repr__(self): return "<ClassRoomInfo(id='%s', title='%s', limit='%s', joins='%s')>" % (self.id, self.title, self.limit, self.joins) Base.metadata.create_all(engine)
セッションを作って操作
コネクションの類は全部セッションがどうにかしてくれるご様子。
# Database session start. Session = sessionmaker(bind=engine) session = Session() # ここに何かやりたいこと # End of session. session.close()
INSERT / SELECT
さすが Python 見たままやなー
# Isertion to database. session.add(ClassRoomInfo(title='ひゃっはー', limit=20, joins=10)) # 中略(500 件くらい挿入) # commit session.commit() # Selection id50 = session.query(ClassRoomInfo).filter_by(id = 50).one() print(f'ID 50 = {id50}')
Anaconda パッケージのアップデート
といっても、そんな大したコマンドではないので、あくまでメモ
$ sudo conda update --all
Python 3.6.8 までが今日時点で出てたのでサクッと入れた。
問題は pandas とかアップデート入ってたから、前に書いたサンプルとか動かなくなってそうな点…
結構 Depricate あったからなぁ(汗
AmazonLinux2 に Java11 + Tomcat9 を Ansible2.7 で突っ込む
やろうと思ったきっかけは、そんなわりかし新しい構成でサーバ作ってと頼まれたので。
そして、やってみるとわかるのだけど、2019/03/21 時点でこれをリポジトリサポートしてるLinuxがそうそう無いと言うことに気づく。
強いて言えば Ubuntu18 が JDK11 だけサポートしてた。
でも Tomcat の Java11 対応って、Tomcat9 からなので、それなりに最新を入れざるを得なかったという問題があった。
前回もそんな流れで、JDK11 だけ先行して入ってりゃどうにかなんだろーと思ったためだ。
しかし甘かった…Tomcat9 の問題が待ち構えていたのだ。
なので、今回は前回をパワーアップチャレンジした。
インストールの段階を分離
Playbook は本来やりたいことの単位で区切っておいた方が、可読性の問題から言ってもいいので、全体として 4 つの playbook を作った。
- main.yml
メインの playbook。他の platbook を呼び出すハブ