開拓馬の厩

いろいろやる

研究と鬱と休学の話

これはEEICアドベントカレンダー2日目の記事です。

研究する気のない人が大学院に入って精神状態がボロボロなった話とそこから這い上がった話をします。

TL;DR

  • 人は心が荒むとスピリチュアルに頼りがち。でも認知行動療法に頼ったほうがいい
  • つらくなったら学生相談室にGO
  • 「勉強をしたい」と「研究をしたい」は別。大学院は後者のための場所

研究室と研究生活について

学部4年(去年)の4月から、電子情報工学科のIIS-Lab(矢谷研究室)に配属されました。 その後、大学院に進学した後も在籍し続け、1年半ほどこの研で過ごしました。

IIS-Labの研究内容は公式サイトを見ればわかるので、研究室の雰囲気や学生の過ごし方を軽く説明すると、

  • 毎週水曜日に全体ミーティング、また週一で先生と1対1でミーティング(コアタイムはこれだけ)。
  • 一人1プロジェクト。解決すべき問題を探すところから実験計画まで、全部自分で決める。
  • 研究の進捗管理GitHubのissue、業務連絡はSlack。
  • 論文はOverleafで書く。共有リンクを渡せば、先生も上級生もわりとガッツリ添削してくれる。*1
  • 発表練習が手厚い。卒論発表や輪講の直前はミーティングで発表練習をやる。
  • 学生居室がキレイ。(ベンチャー企業のオフィス感)
  • 修士1年の夏に企業のインターンに行くことが推奨されている。
  • 1/3ぐらいが留学生。*2
  • 計算資源はMicrosoft Azure。研究室でパートナー契約的なものをしてるらしく、高いインスタンスを借りても大丈夫っぽい。(書き忘れていたので2019/01/15追記)

自分で考えたテーマで研究したい人、研究のモチベーションが高い人にはとてもいい環境だと思います。 逆にそうでない人をきっぱりと休学・退学させてくれるのもいいところなのかな……(ごまかしで卒業できてしまうのよりは間違いなく良い)

気の乗らない研究生活。鬱の傾向が現れる

そんな素晴らしい環境に身を置きながらも、研究生活を送るにつれて私の精神はすり減っていきました。

その原因は研究の進捗が芳しくなかったことです。 まず、研究テーマを見つけるという段階から躓きました。 どのような研究をやりたいかというビジョンを明確に持たないまま4年生になってしまったため、やりたい研究が思いつかないという状態に陥りました。 IIS-Labでは、研究テーマは各人が自分で決めるという方針だったため、テーマが降ってくる*3こときも期待できず、ただ途方に暮れる状態でした。 結局、なんとかひねり出した微妙なアイデアを元に研究っぽい何かをでっち上げるというような酷い卒業研究でした。 こんな具合で卒業研究は散々な結果だったのですが、大学院入試には受かっていたためそのまま大学院に進学し、修士の研究でもテーマが見つからないという問題に直面し……

毎週のミーティングで先生に報告できるような進捗を作らなければという重圧や、修論発表の期日までに成果を出せないかもしれないという漠然とした不安と戦う毎日でした。 こうした不安は大学にいる時間だけでなく、買い物している間やご飯を食べている間、寝る前の布団に潜っているときも、趣味の登山の最中ですら、常にこうした不安からは逃れられない状態でした。 自由な時間はたくさんありましたが、心から休める時間は1秒たりともありませんでした。

そんな生活を送っていたら不眠症になり、保健センター精神科*4で診てもらったところ「鬱の傾向*5がある」と言われました。

認知行動療法、マインドフルネス、スピリチュアル

こうして鬱の傾向にあることが発覚したのですが、そこから抜け出すために色々な「あがき」をしました。

まず認知行動療法に取り組みました。 認知行動療法というのは、出来事に対するネガティブな思い込み(自動思考)を言語化し、合理的な思考に修正することで精神を安定させる手法で、鬱やパニック障害の治療方法のひとつです。 精神科医デイビット・D・バーンズの名著「いやな気分よさようなら」や「フィーリングGoodハンドブック*6を読み込みました。 そこに記載されているノートテイキング手法を試したり、日々認知の歪みを意識して生活したりすることで、ネガティブな自動思考を徐々に和らげていきました。 他にも認知行動療法の亜種であるAcceptance Commitment Therapy(ACT)についても調べ、できる範囲で実践しました。

他にもマインドフルネス(瞑想)にも取り組みました。 マインドフルネスとは「何も考えない」状態を作ることでストレスを和らげる手法です。 基本的に道具を使わなくてもマインドフルネスは可能なのですが、ガイドがある方がやりやすいと考えたので、HeadSpacePauseといったアプリを使いました。 ちなみにHeadSpaceのようなMindfulness-based Applicationは学術研究の対象として注目されてきているようです。 コンピュータサイエンスを用いてWell-beingを追求するという目標の元、HCIと精神医学を組み合わせた学際的な研究は最近流行ってきているみたいです(マインドフルネスは決してスピリチュアルやオカルトのたぐいではないぞ)。 「ウェルビーイングの設計論*7」等の書籍に詳しく書いてあるので興味を持った方はご一読してみることをおすすめします。

他にも、南米のスピリチュアルな儀式*8を試したり(今思い返すと相当キテた)、 ちょっとした一人旅に行ったり*9、 半ばヤケになって色々試しました。

認知の歪みを意識すると気分が落ち込みにくくなるという発見は今でも役立ってます。

東大の学生相談ネットワーク

学内の学生相談ネットワークもフル活用しました。 大学生活を送るうちに心を病む人というのはわりと多いらしく、ほとんどの大学には学生相談室という施設が設置されています。 東大の場合、学生相談室の他に、精神保健支援室(保健センター精神科)、コミュニケーション・サポートルーム、なんでも相談室、ピアサポートルームという施設が存在し、まとめて学生相談ネットワークと呼ばれています。 私はピア・サポートルーム以外は全部利用して精神の安定させていきました。 せっかくなので使い方とかどんな雰囲気かというのを書いておきます。

  • 学生相談室
    • スクールカウンセラーの方と相談できる場所
    • 基本的には毎週 or 隔週ぐらいのペースで通院(通室?)する
    • 予約制
      • ネット予約だと1~2週間後になる場合が多い
      • 実は電話予約だと融通が効く場合がある
  • 精神保健支援室(保健センター精神科)
    • 精神科医臨床心理士に診察してもらえる
    • 初回はスクールカウンセラーの方と面談 and アンケート
    • 基本的に薬を処方してくれるだけ。カウンセリングはあまりやらない
    • 診察料は無料、薬代は自費。健康保険が使えず全額負担
    • 初回は窓口に直接行く。二回目以降は予約制
  • コミュニケーション・サポートルーム(コミュサポ)
    • 発達障害の支援 and 自己理解を深めるという位置付けの相談室
    • 基本は学生相談室とほぼ同じ
    • 会話の練習をしたり、知能試験を受けたりすることができる
      • WAIS 3という1回2万円の試験が無料*10
    • 発達障害の医学的な診断を受けることも可能
      • 診断を受ける場合、臨床心理士が両親に電話して子供の頃の様子を聞くというフェイズが挟まる
      • ちなみに私も受けてみたが、発達障害というよりは社交不安症の傾向らしい
    • 隔週ぐらいのペースで通院(通室?)、予約制なのは学生相談室と同じ
  • なんでも相談室
    • なんでも相談できるらしい
    • 一度行ったが、学生相談室を紹介されただけだった
    • 精神状態の悩みは「学生相談室 or コミュサポ行きましょうね」になるような気がする
  • 工学部学生相談室
    • 学生相談ネットワークの管轄ではなく、工学部が独自に設置している学生相談室
    • カウンセラーじゃなくて事務員的な人が常駐してる?
    • 1回行ったけど正直微妙だった

まとめると、保健センター精神科でおくすりをもらいつつ、学生相談室とコミュサポのどちらか or 両方でカウンセリングを受けるのが良いんじゃないかな。 悩みを言語化したり、自分の状態をより深く理解したりすることはとても大事です。

どうやら自分は研究をしたいわけではないらしい

そんなもろもろの「あがき」や学生相談ネットワークの支援によって現在の私の精神状態はかなり安定しています。 そして今は何をしているかというと、大学院を休学してスタートアップ企業でエンジニアをしています。 今やってることについては他の記事*11で語るとして休学に至った心境についてお話します。

鬱の傾向が晴れて今の自分の状況が冷静に捉えられるようになった私は極めて単純な心理に気付きました。 つまるところ、自分は研究をやりたいわけではなかったのです。 大学に入ったときは講義を受けて色々なことを学びたいと考えていましたし、大学院入試をした動機の中には他の研究者が書いた論文を読むのが好きという要素も多少ありました。 しかし、自分が論文を書いて他の研究者に読ませることには全く興味がないということに気付きました。

本当にやりたいことは研究ではなかった、他にやりたいことがあるはず、仮にやりたいことがまったくなく自分の人生に意味がないとしても研究するよりはマシな時間の潰し方はある。。。 と、あれこれ考えるうちに、嫌々ながら研究を続けるより休学してエンジニアとして働いたほうが良いのではないかという結論に至りました。

  • 自分はお金とスキルを得られてうれしい
  • 先生は私の指導に当てていた時間を自由に使えてうれしい
  • 企業は私という労働力を得られてうれしい

などなど、私が休学して働くことであらゆるリソースが正しく使われるようになるわけです。*12 もちろん修士卒の肩書が遠のくというデメリットもありますが、研究に対するモチベーションが低い自分が、なんちゃってMaster of Engineeringで不当に高く評価されるのは、今後の人生において良い結果を産むはずがありません。

そんなことを考えた私は、次の日には休学届を完成させ、次の日には現在のインターン先に面接に行き(あっさり採用してくれた弊社に感謝)、あっという間にエンジニアに転生していました。 こうして研究する気がない学生が一人消え、人手不足のIT業界に働き手が一人増え、世界は少し良くなったとさ、めでたし。めでたし。

*1:研究室見学では論文執筆環境はあんまり語られることがないが、それなりに重要な要素だと思う

*2:今年秋入学で3人入った。彼らが秋入学したのと入れ違いで休学したので、どんな人なのかあまり知らない

*3:教授や院生が研究テーマを考え下級生にやらせる現象

*4:学内の病院的な施設

*5:精神科医による診察結果であるが、診断書が発行されるレベルの本格的な診断ではないため「傾向」というファジーな表現がされている

*6:この2冊は鬱の状態で読むには内容的にも物理的にもやや重いのでそれっぽい入門書を図書館で借りたほうがいいかも

*7:この本は私が発注した(ドヤ顔)ので工学部2号館図書館に蔵書されている

*8:瞑想をして精霊を呼ぶ的なやつ

*9:レンタカーで四万温泉にドライブして事故る(駐車場で隣の車を擦った)。レンタカー屋の保険2100円で全部解決できた上に警察に通報して事故の後処理をするという貴重な体験ができたのは良い買い物だった

*10:バラマキではなく医学研究の一貫である。念の為

*11:退学アドベントカレンダー13日のエントリ

*12:このあたりやたら大げさに書いているが、休学決めた日は鬱の傾向が裏返って軽い躁状態になっていたのか「俺は休学して世界を正しくするぞおおおおおぉ」と本気で考えていた

クックパッドインターンでちょっぴり成長した

10 day 技術インターンシップに参加してちょっぴり*1成長してきました。かなり楽しかったので体験記を書きます。

internship.cookpad.com

前半5日間

前半の5日は講義形式でwebアプリケーションの開発について学びました。

初日はオフィス見学と環境構築で、2~5日目で以下のようなことを学びました。

  • 事業開発
  • サーバサイド(Ruby on Rails)
  • フロントエンド(React Native、Typescript)
  • クラウドインフラ(AWS、Docker 内製デプロイツールの itamaehako のハンズオン*2 )

個人的にはクラウドインフラの回が一番楽しかったです。 EC2やRDSのインスタンスを立てたり、ロードバランサを置いて負荷分散をしたりといったことを体験しました。 これらは学生が個人でやるには限界があり、ここで体験できたのはとても良かったと思います。 サーバの設定内容をコードの形式で保存しChefツールで環境構築を自動化することで、サーバ環境構築の冪等性を確保するという概念を知ることもできました。

また、実習として、ISUCON形式でアプリケーションの処理速度をインターン参加者同士で競うということも行われました。 私は2位に入賞することができ、賞品としてレシピ本をもらいました🥈

後半5日間

後半はOJT*3コースとPBL*4コースに分かれて参加者ごとに異なる活動をしました。 OJTは実際にクックパッドの開発現場に参加するコースで、PBLは与えられた課題を解決するようなアプリケーションをチームで開発するコースです(やや期間が長いハッカソンのようなもの)。 私はPBLコースに配属され、「食材の購入を支援するアプリを作る」という課題に3人のグループで挑戦しました。

我々のチームは「休日に1週間分の食材を買い込んで、平日はそれらを使って料理する」というシナリオを想定し、1週間分の献立を決めると必要な食材の総量を計算してくれるアプリを作成しました。 自分は主にクライアントサイドの開発担当としてひたすらReact Nativeを書きました。 React Navigationで画面遷移を実装したり、Native BaseでUIを設計したり、コンポーネントはできる限りSFC*5になるように書いて使いまわしやすくしたり実践的なReact Nativeの開発ができました。

ただ、ライブラリの選定や設計方針を予め相談した方が良かったのかもしれません。今回のアプリではヘッダ部分をReact Navigationが生成するものを使いましたが、個人的には使いづらいように感じる部分もありました。 React Native Navigation等の他のライブラリを使用する、ヘッダ部分もNative Baseで実装するという選択肢をあまり考慮せず、なんとなくで決めてしまったのは良くなかったと思います。

仮に今回作ったものを実際に事業として動かすのであれば、デザイン面*6と技術面*7での課題を解決するためにユーザインタビューや機械学習モデルの構築などが必要で、事業開発のほんの一部分しか体験できていないようにも思いました(インターンの期間中にユーザインタビューまでやるのは無理そうだけど)。

参加前はOJTコースで現場の業務環境を体験したいと考えており、PBLコースに配属されたのを残念に思っていました*8。 しかし、アプリの設計から実装まで技術レベルが同じぐらいのメンバーでグループ開発するという体験はそんなにできることではないため、それはそれで良い体験になったのではと思います。

感想

いろいろなことが体験できた10日間でした。 Ruby on RailsやReact Nativeといったフレームワークを初体験したり、インフラエンジニアリングという分野のことを知ったりすることができました。

正直、今回のインターンそれ自身による成長はそこまで大きくは無いと思います。本当に成長するのは今回触れた技術をアルバイトなどで活用したときになるのではと思います。 しかしながら、今回のインターンを通じてweb系エンジニアの方々の働き方を垣間見たり、同期の学生達が様々なことに挑戦していることを知ったりして、勉強していくモチベーションが高まりました。

このインターンで成長したというよりは、今後成長する足がかりができた感じがしました。(植物に例えるなら、幹が伸びるというよりは新しい枝が生えたのかな)

学んだこと

ほぼ初見でしたが、2週間で多少使えるレベルにはなったかなと思います。 特にReact NativeはPBLコースの5日で沢山書いたので、その気になれば自力でアプリひとつ作れる程度にはなったかな? Typescriptやweb版のReact.jsの理解も深まりました。

  • 新規事業開発

企業がどのようにして新規事業を立案したり、ユーザのフィードバックを得たりしているのかを体験できました。大学では得られないものだったと思います。 研究のテーマ探しやユーザスタディなどでも役に立ちそうだと思いました。

新しく興味を持ったこと

  • インフラの最適化 ISUCON

AWSへのデプロイや、クラウドインフラの最適化は独学では体験しにくい分野なので体験できてよかったと思います。 インフラエンジニアという職種を知ることもできました。 また、社内ISUCONを体験して本家ISUCONにも興味が湧きました。

  • Rust

インターンの内容とは無関係ですが、参加者にRustaceanがいて布教されました。 Web Assemblyと組み合わせて使ってみたいです。

その他

クックパッドはオフィスにキッチンがある変わった会社です。 そのキッチンを使って、インターン参加者や社員の皆さんとごはんを作りました。 大勢で料理をしてみんなで食べるというのはとても楽しかったです。それに料理も美味しかったです。餃子とか唐揚げとか中華スープとか🥟 f:id:pf_siedler:20180913154158j:plain 3日目の餃子パーティー f:id:pf_siedler:20180913154204j:plain 最終日の懇親会

*1:圧倒的成長というが界隈で使われていますが、「圧倒的」という部分に嘘っぽさとブラック労働的な苦労自慢のニュアンスが感じられて好きではないので「ちょっぴり成長」したことにします

*2:どちらもオープンソースとして公開されている

*3:On-the-Job Training

*4:Problem Based Learning

*5:Stateless Functional Component

*6:献立を立てる→必要な食材を計算というコンセプトなので、献立を立てるという体験を楽しくさせるデザイン上の工夫が必要だと考えられる。また、世間一般の消費者は休日にまとめ買いや献立を予め決めるといった行動をどの程度行っているのかを分析し、このアプリが本当に必要とされているか見極めなければならない

*7:クックパッドのレシピに記載されている材料欄をもとに買うべき食料を算出しているため、食材を1人分に正規化したり、「適量」とか「お好み」とかいった表現をいい感じの数量に変換したり等で、提示する食材量の妥当性を確保する必要がある

*8:OJTコースのみ時給が支払われるという待遇の違いもあったが、その点について不満はなかった

Python & Serverless FrameworkでLambdaファンクションをサクッと作ってデプロイする

Serverless FrameworkAmazon AWSの開発&管理を補助するフレームワークです。 最近アルバイト先でserverlessを用いたアプリケーションに関わり、その際に使いかたを軽く勉強したので、ブログに書き残しておきます。

環境

  • Node.js 10.6.0
  • Python 2.7.15
  • serverless 1.29.2

Installation

ServerlessはNode.jsのアプリケーションとして配布されており、npmコマンドからインストールできます。

npm install -g serverless

# 確認
serverless --help
sls --help # slsという短縮コマンドが自動でエイリアスされるらしい

サービスを作ってデプロイ

インストールが完了したので、簡易的なLambdaのファンクションを作成してAWSにデプロイするところまでやってみましょう。 今回は毎朝8時にLINE Notificationを送ってくれる目覚ましファンクションを作ってみます。

サービスの新規作成

sls createコマンドでサービスを新規作成できます。 このとき-tオプションでアプリのテンプレートを、-pオプションでサービスを保存するパスを指定できます。 テンプレートはaws-python*1、保存先は適当な作業ディレクトリ上のlambdaMezamashiというディレクトリにします。

cd /path/to/working/dir #適当な作業ディレクトリに移動
sls create -t aws-python -p lambdaMezamashi
cd lambdaMezamashi
ls
# => handler.py   serverless.yml

コマンドを打つと、lambdaMezamashiというディレクトリが作成され、handler.pyserverless.ymlという2つのファイルが自動生成されます。 handler.pyはLambdaのファンクションを書くファイルで、serverless.ymlは設定ファイルになります。

とりあえず動かす

アプリが完成したのでとりあえず動かしてみます。 テンプレートにはhelloという関数名で簡単なメッセージを返す関数が定義されています。 sls invoke local -f {関数名}で指定した関数名の関数をローカル環境で実行できます。

sls invoke local -f hello
# => {
# =>     "body": "{\"input\": {}, \"message\": \"Go Serverless v1.0! Your function executed successfully!\"}", "statusCode": 200
# => }

LINE Notificationを送る関数を記述

LINE Notificationの使いかたはこちらの記事でも解説しています。

まず、LINE Notifyのページからアクセストークンを取得し、環境変数LINE_NOTIFY_TOKENに書き入れます。

export LINE_NOTIFY_TOKEN=XXXXXXXXX # bash zsh等の場合
set -x LINE_NOTIFY_TOKEN XXXXXXXXX # fishの場合

次に以下のようにhandler.pyを書き換え、LINE NotifyのAPIを叩くコードを追加します。

# -*- coding: utf-8 -*-

import os
import json
import requests

def mezamashi(event, context):
    output_url = "https://notify-api.line.me/api/notify"
    token = os.getenv("LINE_NOTIFY_TOKEN", "")

    headers = {"Authorization" : "Bearer "+ token}
    payload = {"message" :  "こんにちは"}

    response = requests.post(output_url ,headers = headers ,params=payload)

    return response.json()

def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

次にpipコマンドでrequestsをインストールします。

pip install requests

次にserverless.ymlを書き換え、mezamashi関数に関数名を割り当てます。

service: lambdaMezamashi

provider:
  name: aws
  runtime: python2.7

  environment:
    # LINE Notifyのアクセストークンを記入
    # invoke localで実行する場合はこれを記述しなくても動作するが、デプロイ時に必要となる
    LINE_NOTIFY_TOKEN: XXXXXXXXXXXXXXXX

functions:
  hello:
    handler: handler.hello
  mezamashi:
    handler: handler.mezamashi

そうしたら、serverless invoke localを用いて動作確認を行います。

sls invoke local -f mezamashi
# => {
# =>     "status": 200,
# =>     "message": "ok"
# => }
# (LINEに通知が来る)

スケジュール実行の設定

Lambdaファンクションの実装が完成したので、これをスケジュール実行するように設定を書きます。 serverless.ymlfunctionsの部分にイベント設定を追記するだけです。

functions:

    #<中略>

mezamashi:
    handler: handler.mezamashi
    events: # イベントの設定を記述
      - schedule: cron(0 23 * * ? *) # cron式で日本時間の朝8時を指定

ついでにAPI Gatewayも使う

LambdaはAPI Gatewayと組み合わせることでwebアプリのバックエンドとして使用するケースもよくあります。 少し脱線しますが、ymlを用いてLambdaファンクションをAPI Gatewayと結びつける方法も説明しておきます。

eventsの項目に- http: {メソッド} {APIのURI}の形式で記述します。 こうすることでデプロイした際に自動でAPI Gatewayが作成されます。

functions:
  hello:
    handler: handler.hello
    events:
      - http: GET hello

オフラインでテストする

ファンクションが書けたので早速デプロイと行きたいところですが、その前にローカルホストでLambdaファンクションをホストして、APIのテストを行う方法を解説します。 これは、業務でデバッグAPIのテストコードを走らせる場合にも必要なことなので覚えておいて損はありません。

まず、必要なプラグインをインストールします。インストールはsls plugin install -n {プラグイン名}で行います。どうやらこのコマンドはnpm i --save-devをラップしてnode_moduleをインストールしつつ、serverless.ymlプラグイン情報を追記してくれるものらしいです。

sls plugin install -n serverless-offline
npm install serverless-offline-python --save-dev # slsコマンドからインストールできないためnpmを直接使う

次にserverless.ymlの末尾に以下のように書き込んでserverlessとプラグインを紐付けます*2

plugins:
  - serverless-offline
  - serverless-offline-python

serverless-offlineserverless-offline-pythonをインストールしたことで、serverless offlineというコマンドが使えるようになります。これによりローカル環境でcurlコマンド等を用いてAPIデバッグが可能になります。

sls offline
#=> Serverless: Starting Offline: dev/ap-northeast-1.
#=>  (中略)
#=> Serverless: Offline listening on http://localhost:3000

# =====他のterminal窓から以下を実行=====

curl localhost:3000/hello
#=> helloファンクションが動く

ちなみにserverless-offline-schedulerというオフライン環境でスケジュール実行のデバッグができるプラグインがありますが、Node.jsで書いたものしか動かないみたいです*3

デプロイする

最後にデプロイの方法を説明します*4

AWSアカウントの設定

最初にAWSアカウントの設定をします。

AWSのマネジメントコンソールにログインし、IAMから新規ユーザーを追加します。 f:id:pf_siedler:20180822104021p:plain f:id:pf_siedler:20180822104717p:plain

ユーザー名を適当に設定し、「プログラムによるアクセス」にチェックを入れます。 f:id:pf_siedler:20180822104040p:plain

「既存のポリシーを直接アタッチ」から「AdministratorAccess」を選択します*5 *6f:id:pf_siedler:20180822104044p:plain

ユーザーの作成が完了したら、「アクセスキーID」と「シークレットアクセスキー」を控えておきます。シークレットアクセスキーはこのときにしか確認できないため、忘れた場合には再度アカウントを作成する必要があります。 f:id:pf_siedler:20180822104034p:plain

これを、sls config credentialsコマンドを用いて設定します。XXXXXの箇所は適宜アクセスキー、アクセスキーシークレットに書き換えてください。

sls config credentials --provider aws --key XXXXX --secret XXXXX

デプロイ、実行、削除

アカウントの設定が済んだら遂にデプロイです。

Serverless Frameworkはsls deployコマンドを実行するだけで簡単にデプロイできるようになっています。 しかし、今回のようにPythonで実装している場合は、自分で書いたコードだけでなくサードパーティ製のライブラリも一緒にデプロイする必要があります。

serverless-python-requirementsというプラグインをインストールするとデプロイ時に自動でこの作業をやってくれるため、今回はこのプラグインを利用することにします。

sls plugin install -n serverless-python-requirements # プラグインのインストール
pip freeze > requirements.txt # 依存関係をrequirements.txtというファイルに書き出す

上記のコマンドを実行したら、デプロイします。

sls deploy
#=> Serverless: Installing requirements of requirements.txt in .serverless...
#=> Serverless: Packaging service...
#=> (中略)
#=> service: lambdaMezamashi
#=> stage: dev
#=> region: ap-northeast-1
#=> stack: lambdaMezamashi-dev
#=> api keys:
#=>   None
#=> endpoints:
#=>   GET - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello
#=> functions:
#=>   hello: lambdaMezamashi-dev-hello
#=>   mezamashi: lambdaMezamashi-dev-mezamashi

これで完成です。毎朝8時にLINEに通知が来るようになりますし、https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/helloURIcurlコマンドやAjax通信でアクセスするとhelloファンクションをWeb APIとして叩くことができます。

デプロイしたアプリの削除も簡単に行うことができ、sls removeコマンドを実行するだけです。

sls remove
#=> Serverless: Getting all objects in S3 bucket...
#=> Serverless: Removing objects in S3 bucket...
#=> Serverless: Removing Stack...
#=> Serverless: Checking Stack removal progress...
#=> .............................
#=> Serverless: Stack removal finished...

さいごに

今回はServerless Frameworkの使い方を軽く説明しました。 Serverless Frameworkは他にもDynamoDBやS3といったサービスを操作することができ、その名の通りサーバレスなwebアプリケーションを構築することができます。

S3でwebページをホストし、Lambda & API Gatewayでバックエンドの処理を行い、DynamoDBやCognitoでユーザ情報を管理するという構築にすればどんなWebアプリでも作れそうですね。

参考資料

*1:was-python3だとローカルで動かす際にpyenv等を用いてver 3.6のPython環境を用意する必要があります。今回は面倒なので2.x系で我慢

*2:# sls plugin ~~ コマンドを走らせた際に自動で追記してくれるため、serverless-offline-python以外は不要かも

*3:実際にソースコードを確認したところ、Lambdaファンクションが記述されたjsファイルをrequireで読み込んで実行するという実装がされているようだった https://github.com/ajmath/serverless-offline-scheduler

*4:これ以降の部分で解説するコマンドを実行するとLambdaファンクションがAPI Gatewayを通して全世界から実行可能な状態になります。Lambdaファンクションに脆弱性が存在した場合には思わぬ被害を被る可能性があることを心に止めておいてください。

*5:公式ドキュメントでもAdmin権限を設定する(https://serverless.com/framework/docs/providers/aws/guide/credentials/)という解説されています。

*6:Serverless Frameworkは開発中でやれることがどんどん増えています。そのためAdmin権限でないと機能に制限がかかり、開発が面倒になる可能性があります。Amdin権限で開発しつつ、製品を公開する際に権限を必要最低限に絞るような運用が望ましいと思われます。

ほとんどのエンジニアには解けるらしいパズルを解いてみた

ジャバ・ザ・ハットリ氏作成の「ほとんどのエンジニアには解けるパズル」*1シリーズを全問解いたので感想とかwrite upとかを書きます。

出題者のブログはこちら

問題一覧はこちら(全8問)

目次

感想

問題はマイルドなCTFみたいでした。CTFはakictfksnctfの問題をちまちま解いたり、SECCONの過去問を漁ったりしていたので、こうした形式のパズルには多少慣れていました。ここに上げたサイトの問題と比較すれば「ほとんどのエンジニアには解けるパズル」は簡単な部類なのかなと思います。

解くにあたってcurlコマンドの基礎を学ぶことができたのは収穫でした。もともとcurlコマンドに対する理解は「-Oコマンド付けてファイルを落とすやつでしょ」ぐらいだったので、一般的なサーバにリクエストを投げるという使い方を確認することができました。

その意味では、最初の3問は有意義でしたが、第4問以降は単なる数学パズルの色合いが濃く、学習という観点ではそこまで得るものはなかったのかもしれません。ただ、数学パズルやCTFの練習問題としては結構楽しかったと感じています。

Write up(ネタバレ注意)

復習、忘備録を兼ねてここに解法を残して置こうと思います。ここから先はネタバレが含まれているため、自力で解きたい方は読まないようにお願いします。

実のところ問題作成者はネタバレ非推奨と発言しているため、ここに解法を書くのはあまり褒められた行為ではないのかもしれません。しかし、curlコマンドの使い方等の技術的な知識は、

  1. 「未知の知識が必要な場面が生じる」
  2. 「使い方を調査する≒答えを見る」
  3. 「実際に使う」
  4. 「次回以降マニュアルを見ずに使えるようになる」

というプロセスを踏んで覚えるものであると私は思います。3の後に後戻りせず4に到達できるのであれば、2で閲覧する答えが解説書であっても、info curlの出力であっても、本記事のネタバレであっても本日的には変わらないのでは?むしろ、答えが参照しやすくなっている方が2の段階でつまずくことがなくなっていいのでは?という考えからここに解法を載せることにしました(一応ボタンを押さないと表示されないようにはしておきます)。

解説はHTTP通信などをほとんど知らない人が読んでもふんわりと理解できる程度には詳しく書くように心がけました。解いているうちに詰まってしまったら読んでみるのもいいのかもしれません。curlの使用例を知りたいという人も解説を見ながら第1問を解いてみるのもいいかもしれません。

第1問

第2問

第2問以降は暇があったら書きます(第1問程度の文量で書くと結構長くなりそうなので)。

*1:○○なら誰でもできるっていう釣りタイトル個人的にはきらい

AWS Lambdaを使ってAmazonコインを安く買う

先日AWS Lambdaを初めて使ったので忘備録を兼ねて書きます

今回書いたコードはここで見れます

Amazonコインの価格変動

1年ぐらい前からHearthstoneというデジタルカードゲームにハマっています。 Hearthstoneは課金でカードパックを買えるのですが、Amazonアプリストア版のAndroidアプリを使用すると、Amazonコインでの支払いが可能で、クレジットカードで直接買うよりより少し安くなります。

そんなAmazonコインですが、実は日々値段が変動しているみたいです。

Amazonコインの価格の変動・推移を調べてみる2018-2 | JJゃもの部屋 \(^o^)/ 破滅に向かって2011

↑の記事を見た感じ、10000コインが8100円のときにまとめ買いしておくとおトクなようです。

Amazonコインが安い日を見逃さないために、今回はpythonでwebスクレイピングを行いコインの価格を自動で追跡するスクリプトを作成しました。また、そのスクリプトAWS Lambdaで自動化しました。

Amazonコインの価格を自動で取得する

PythonAmazonコインの価格を追うスクリプトを書きました。

まず、venvを用いて仮想環境を作成し、必要となるライブラリをインストールします。

$ python3 -m venv amazoncoin
$ cd amazoncoin
$ source bin/activate # bash zsh等の場合
$ source bin/activate.fish # fish shellの場合
(amazoncoin)$ pip install requests beautifulsoup4
(amazoncoin)$ pip freeze > requirements.txt #必要なライブラリを書き出しておく(あとで使う)

10000Amazonコインの販売ページは https://www.amazon.co.jp/dp/B00KQVXDW0/ であり、価格はid=priceblock_ourpricespanタグに記載されているので、以下のようなスクリプトで価格を取得できます。*1

import requests
from bs4 import BeautifulSoup

def find_price():
    coin_url = "https://www.amazon.co.jp/dp/B00KQVXDW0/"

    resp = requests.get(coin_url, timeout=1)
    soup = BeautifulSoup(resp.text, 'html.parser')
    target = soup.findAll('span',{'id':"priceblock_ourprice"})
    return int(target[0].text[2:].replace(',', ''))

print(find_price())#=> 8100など

価格をLine Notifyで通知する

次に、取得した価格をLine Notifyを使って自分のLINEアカウントに通知することにしました。 [参考] https://qiita.com/hiro-abe/items/e42f857bd6b40bc178a3

下準備として、LINE Notifyのwebページでアクセストークンを取得し、環境変数LINE_NOTIFY_TOKENに値を書き入れておきます。

$ export LINE_NOTIFY_TOKEN=XXXXXXXXX # bash zsh等の場合
$ set -x LINE_NOTIFY_TOKEN XXXXXXXXX # fishの場合

そして、以下のスクリプトを実行するとLINEに通知が届きます。

import requests
from bs4 import BeautifulSoup
import os

def lambda_handler(event, context):

    output_url = "https://notify-api.line.me/api/notify"
    token = os.getenv("LINE_NOTIFY_TOKEN", "")
    
    price = find_price()
    headers = {"Authorization" : "Bearer "+ token}
    message =  '現在10000 Amazonコインは' + str(price) + '円です'
    payload = {"message" :  message}
    
    r = requests.post(output_url ,headers = headers ,params=payload)
    
    
    return 'Notify Amazon Coin Price is running'

lambda_handler(None, None) # => LINEの指定したトークルームに通知が来る

これで、script.lambda_handler関数を実行することで、Amazonコインの価格がLINEに届くようになりました。

AWS Lambdaで毎日自動で動かす

上記のスクリプトをASW Lambdaを用いて自動化します。 Lambdaを用いて自動化します。今回は毎朝8時に実行させるようにします。

スクリプトのパッケージ化

最初に作成したスクリプトをzipにまとめます。Lambdaは使用するスクリプトをzip形式でアップロードする方式になっているためです。*2 このとき注意しなければならないのは、標準ライブラリ以外のライブラリも一緒にzipにまとめる必要がある点です。

デプロイ用のディレクトリを作成し、そこに今回のスクリプトとライブラリをすべて設置し、zipにまとめることにします。

mkdir deploy #デプロイ用ディレクトリを作成
cp script.py deploy #今回作ったスクリプトをデプロイ用ディレクトリにコピー
pip3 install -r requirements.txt -t deploy/ #-tオプションでデプロイ用ディレクトリにライブラリをインストール
cd deploy/
zip -r package.zip * #パッケージを作成
mv package.zip ../

これで、作業ディレクトリにpackage.zipが作成されます。 動作確認として、以下のようなコマンドを実行したらスクリプトが正しく動作することを確認してください。

(amazoncoin) $ deactivate #仮想環境が有効になっている場合のみ
$ cd deploy/
$ python3 -c "import script;script.lambda_handler(None, None)"
# > Notify Amazon Coin Price is running
# (LINEに通知が来る)

AWS Lambdaの設定

パッケージが完成したので、早速Lambdaのセッティングをしてみます。

まず、Lambdaの管理ページから新しい関数を作成します。 ロールは「テンプレートから新しいロールを作成」を選択し、「Basic Edge Lambdaアクセス権限」を使用します。他は適当に入力してかまいません。 f:id:pf_siedler:20180415133028p:plain

次に、先程作成したpackage.zipをLambdaにアップロードします。 このとき、ハンドラには[pythonのファイル名].lambda_handlerを指定します。 f:id:pf_siedler:20180415133042p:plain

そして、環境変数の欄にLINE Notifyのアクセストークンを入力します。 f:id:pf_siedler:20180415133023p:plain

最後にタイムアウトの値を少し大きめの値に変更しておきます。*3メモリは最小の128mbで足りるのでデフォルトのままでかまいません。 f:id:pf_siedler:20180415133036p:plain

イベントタイミングの指定

次に通知が来るタイミングを指定します。 トリガーにCloudWatch Eventsを指定します。 ルール名と説明は適当に埋め、ルールタイプを「スケジュール式」、 スケジュール式をcron(0 23 * * ? *)を指定します。

f:id:pf_siedler:20180415133315p:plain

これで毎日グリニッジ標準時の23時、つまり日本時間の8時にスクリプトが実行されるようになりました。

おわりに

今回はpythonを用いてwebスクレイピングと、AWS Lambdaによる自動化を行いました。

今回、個人的につまずいた点は以下のとおりです。

  • ライブラリをzipに含めないとライブラリの関数が使えない点*4
  • イベントのトリガーに何を選べばいいのかわからない点

今後の課題として、価格がn円以下のときのみ通知を流すようにしたり、TwitterやDiscordに自動投稿させたりなんかをやりたいと思っています。

今回のプロジェクトはこちらのリポジトリで管理してあります。よかったらforkしてみてください。また、改善点などあればissueに書いてくださると助かります。

2019/01/07 追記

このアプリはしばらく前から放置しており、コードが古くなっています。 urllib3 v.1.23 及び requests v2.20.0 は脆弱性が発見されているようなので、requirements.txtをそのままコピペしないようご注意ください。

*1:後々、価格がn円以下なら通知するといった条件分岐をしやすくするために結果をint型で返すようにした

*2:一応、webページで直接コードを書く機能もある。zipでうpしたファイルをweb上で微修正するということも可能。

*3:今回のスクリプト実行には4秒ぐらいかかる場合があり、デフォルトの3秒ではタイムアウトが生じる場合がある

*4:もしかしたら、これを自動でやってくれるソフトがあるのかもしれないが見つからなかった

東京大学eeic3年後期実験「大規模ソフトウェアを手探る」2016年度まとめ

東大生がOSS開発やってみた…みたいな記事

「大規模ソフトウェアを手探る」って?

大規模ソフトウェアを手探る

「大規模ソフトウェアを手探る」は、東京大学工学部電気電子/電子情報工学科(eeic)で開講されている、学部3年生向けの実験科目です。

ネット上に公開されているOSSオープンソース・ソフトウェア)をひとつ選んで、それを改造するという内容の講義です。

2~3人でチームを組んで、数万〜数十万行のプログラムに体当たりで改造を試みることで、以下のようなことを体験するのがこの講義の狙いです。

  • 全容を把握できない程度に大きなプログラムを扱う
  • 他人が書いたコードに触れる
  • デバッガの扱い方を学ぶ
  • 先生やTA、他の学生からフィードバックをもらい、それを開発に活かす
  • チームでの開発を体験する

また、この実験では、成果物をブログ形式で公開することで、レポートの代替として認められています。 ネット上には「インストールしてみた」「ライブラリを使ってみた」系の記事がたくさんあり、後を追う誰かの助けになっています。 我々が実験を通して得た知見も、誰かの役に立つのではないか?ということで、ブログ形式のレポートを認めているようです。

この記事では、そんなレポートの数々を一覧にしてみました。

OSS開発に興味がある方、学科選びに悩んでいる一二年生、そしてどの実験を受講しようか迷っている未来のeeic民の一助となれば幸いです。

目次


連載形式の記事については、初回あるいは目次のページを取り上げています。

コンパイラ・言語

gcc

結局なにをどう変更したのか(まとめ) - 27++'s Report

内容

  • 生成ファイルに自動で名前を付ける
  • 末尾の;を補完する

Python その1

Python3.x系でprint命令文を追加する話. - Qiita

Pythonにプライベート変数を実装しようと試みた話。 - Qiita

内容

  • Python3.x系でもprint文を使えるようにする
  • private変数を実装する

Python その2

Pythonを改造してみた はじめに - 開拓馬’s blog

内容

テキストエディタ

Vim

Vimにnanoみたいなコマンドチートシートを表示した話 - チョコの包み紙の裏

内容

nano

nanoを手探ってみる#1: ビルドする - meloidae’s blog

内容

  • tabキーでファイル名を補完する
  • 起動後でもnanorcのリロードをできるようにする
  • 雪を降らせる

ブラウザ

FireFox

大規模ソフトウェアを手探る Firefox - Qiita

内容

  • url欄の文字を隠す
  • 右クリックからタブの複製を行う
  • 新規タブを開くとクリップボードの内容を自動で検索する
  • タブを複数選択して一気に閉じる機能を付ける

Chromium

Chromiumを手探った#1 - Chromiumをビルドしよう - しゅわしゅわdedede。

内容

  • 新規タブで表示されるページをユーザが設定できるようにする

OpenTween

OpenTweenを手探ってみた#1 - Qiita

OpenTween 目次 : トルネード一門計算機室

内容

  • お気に入り一覧を登録順にする
  • 同内容のつぶやき(パクツイ)を検索する
  • フォロー/フォロワーの変化を可視化する
  • 時間制限付きのミュート機能を追加する

ユーティリティ

Libre Office

LibreOfficeを手探る - libsoft’s blog

内容

  • spaceを入力すると下付き文字モードが解除されるようにする

Rhythmbox

Rhythmboxで1曲ループを実現する −0− - かるぅあみるく(仮)

内容

  • 一曲ループ機能を追加する

GIMP

GIMPの履歴機能に制限を加えてみた - team-cの日記

内容

  • 変更履歴の数が増えすぎてフリーズする問題を防ぐ

まとめは以上になります。

昨年度にこの講義を受講されたid:SWIMATH2さんが、15年度のまとめ記事を作成してくださっています。

東京大学eeic3年後期実験「大規模ソフトウェアを手探る」2015年度まとめ - クフでダローバルな日記

こちら記事に「来年以降のeeicの後輩たちがまたまとめてくれるのを楽しみにしています。」とあったので、大変僭越ながら便乗させていただきました。

17年度以降も、後輩たちがまとめてくれるのを楽しみにしています。

Mac bookにBasicTeXを導入してみた

先日Mac BookのOSを再インストールしたのでTeXを入れ直し、ついでにLuaLaTeXに乗り換えた話をします。

BasicTeXを入れる

Mac用のTeX環境として、MacTeXというものがあります。 とりあえずこれをインストールすればMacTeXが使えるようになるのですが、インストーラのサイズが2.8Gもあり、ダウンロードに1時間程度かかって面倒です。 そのうえあまり使わないGUIアプリがついてきてLaunchpadが汚染されます。 *1

私の場合、Emacsのorg-modeやPandocを使ってMarkdownTeXに変換するのが主な利用法なので最低限のコマンドが使えれば十分です。

そこで最小限の機能だけ持ったBasicTeXを入れることにしました。

MacTeXのサイトからBasicTeX.pkgをダウンロードします。 BasicTeX.pkgを起動しインストーラの指示に従ってインストールします。

インストールが完了すると/usr/local/texlive/2016bacis(数字はバージョンによって異なる)というディレクトリが作成されます。 *2

必要なパッケージを入れる

BasicTeXは本当に最小限で日本語環境が使えないので必要なパッケージを入れます。

ターミナルで以下のコマンドを実行します。

% sudo tlmgr update --self —all
% sudo tlmgr install collection-langjapanese

ちなみにtlmgrはTeXLive managerの略だそうです。 *3

collection-langjapaneseにはluatex-jaというパッケージが入っており、LuaTeXを日本語で使えるようになります。

ghostscriptImageMagickを入れる

次にghostscriptImageMagickをインストールします。これらは画像処理やpdf関連の処理を行うプログラムで、TeXをpdfに出力する際に使われるそうです。

brew install ghostscript
brew install imagemagick

インストールにはhomebrewを使用しました。「homebrewって何?」「brewコマンドが使えない」って人は"homebrew"で検索してみてください。

LuaLaTeXで書いてみた

LuaTeXの詳細はTeX Wikiの説明に丸投げします。ざっくりまとめるとLuaというプログラミング言語LaTeXを混ぜたシステムです。

フォントの指定がわりと楽にできたり、Unicode文字に対応していたりとLaTeXより便利な点がいくつかあるのですが、 一番の利点はdviファイルを経由せずにpdf出力ができる点です。 *4

% platex report.tex
% dvipdf report.dvi
    #LaTeXではこんな感じにやっていたのが

% lualatex report
    #LuaTeXだとこれでOK

とりあえずluatex-ja Wikiにあるサンプルを出力してみました。

\documentclass{ltjsarticle}
\usepackage{luatexja} % ltjclasses, ltjsclasses を使うときはこの行不要
\begin{document}
\section{はじめてのLua\TeX-ja}
ちゃんと日本語が出るかな?
\subsection{出たかな?}
長い文章を入力するとちゃんと右端のところで折り返されるかな?
大丈夫そうな気がするけど.ちょっと不安だけど何事も挑戦だよね.
\end{document}

上記をtest.texの名称で保存しpdf出力してみます。

% lualatex test.tex
  :
  :
! LaTex Error: File `filehook.sty’ not found.

どうやらfilehook.styが必要なのにインストールされていないのでエラーが発生したようです。tlmgrを使ってfilehookをインストールします。

% sudo tlmgr install filehook

これに限らず、hoge.sty not found.というエラーが発生した場合、sudo tlmgr install hogeでスタイルファイルをインストールする必要があります。

何度か「実行」→「エラー」→「スタイルファイルを入れる」を繰り返すと出力が成功します。

f:id:pf_siedler:20161123141357p:plain

画像が表示されない場合

以降はLuaTeXを使い始めたばかりのときに躓いた部分を紹介します。同じような失敗をしている人の参考になれば幸いです。

LaTeXで画像を使用する場合、ヘッダ部分に

\usepackage[dvipdfmx]{graphicx}

と記述します。

一方でLuaLaTeXで画像を使用する場合、ドライバ指定オプションは不要です。

\usepackage{graphicx} %LuaTeXではこう

ここでドライバ指定を付けたまま出力すると、画像の部分が白抜き状態になります。

\documentclass{ltjsarticle}
\usepackage{luatexja}
\usepackage{graphicx} %[dvipdfmx]は不要
\begin{document}
\section{はじめてのLua\TeX-ja}
ちゃんと画像が出るかな?

\begin{figure}[h]
  \includegraphics[width=10cm]{test.jpg}
  \caption{Lenna}
\end{figure}
\end{document}

f:id:pf_siedler:20161123141531p:plain

オプションを付けたままだとこんな感じ

f:id:pf_siedler:20161123141509p:plain

筆者はネットから拾ってきたLaTeXのサンプルコードを何も考えずにコピペした結果、この症状に見舞われ数時間無駄にしました。

脳死コピペ、ダメゼッタイ

*1:個人の感想です。TeXShopは人によっては便利かも

*2:フルパッケージのMacTeXの場合、ディレクトリがtexlive/2016のように数字のみになる。MacTeX関連の記事を読む場合、2016→2016bacisと読み替える必要がある

*3:こういう略語の元の言葉を覚えておくとコマンドを暗記しやすい気がする

*4:pdf直接出力はLuaLaTeXの派生元であるpdfTeXで実装された機能です。pdfTeXは国内ではマイナーですが、海外ではよく使われているそうです。