AnsibleでUbuntu 20.04のセットアップ

目標

Ansibleを使ってUbuntuの環境を最低限構築します. 構築する環境はだいたい下のような感じ.

  • Git
  • fish
  • Docker

Ubuntu20.04はインストール済みということにします. Ubuntu20.04では最初からPython3.8がインストール済みなので新たにインストールする必要はありません.

Ansibleとは

Python製の構成管理自動化ツールです.

f:id:ocucraqp:20200506051655p:plain

今回はローカルで個人利用のPCの初期設定に用いましたが,本来は多数のサーバを一括でセットアップしたりするのが目的のツールです. セットアップしたいPCにPythonさえ入れておけば管理サーバからセットアップすることができます. (Linuxはだいたい最初からPythonが入っている.)

また,複数回実行しても結果が同じになるという冪等性が担保されるように作られているそうです.

冪等性についてはこちらが分かりやすいです.
Ansibleの冪等性について深く考えたことはありますか? - ぽよメモ

インストール

# Ubuntu
$ sudo apt update
$ sudo apt install ansible
# Mac
$ brew install ansible

バージョン確認.

$ ansible --version
ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/username/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.2 (default, Apr 27 2020, 15:53:34) [GCC 9.3.0]

ここからは失敗談なので興味がない人は飛ばしてください.

Ansibleのインストール— Ansibleのドキュメントの通り下の方法でインストールすると途中でエラーが出ました.

$ sudo apt update
$ sudo apt install software-properties-common
$ sudo apt-add-repository --yes --update ppa:ansible/ansible
$ sudo apt install ansible

エラー箇所

$ sudo apt-add-repository --yes --update ppa:ansible/ansible

# エラー文
Err:12 http://ppa.launchpad.net/ansible/ansible/ubuntu focal Release                         
  404  Not Found [IP: 2001:67c:1560:8008::15 80]
Reading package lists... Done
E: The repository 'http://ppa.launchpad.net/ansible/ansible/ubuntu focal Release' does not have a Release file.
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
N: See apt-secure(8) manpage for repository creation and user configuration details.

なので,追加したPPAを削除して,普通にapt installしたらインストールできました.

$ sudo rm /etc/apt/sources.list.d/ansible-ubuntu-ansible-focal.list
$ sudo apt install ansible

Ubnutu20.04ではデフォルトでAnsibleのリポジトリを含むPPAを持っているため,必要ないという感じでしょうか.

Ansibleの構成

今回はシンプルに以下のような構成にしました.

$ tree
.
├── ansible.cfg
├── hosts
├── roles
│   ├── apt_packages
│   │   └── tasks
│   │       └── main.yml
│   └── fish
│       └── tasks
│            └── main.yml
└── ubuntu-provisioning.yml

Ansible用のディレクトリを作成しそこで作業しました.

$ mkdir ansible
$ cd ansible

設定ファイル(ansible.cfg)の作成

ansible.cfgというファイルに書き込みます.ansible.cfgには優先順位があるので,優先順位が高いカレントディレクトリに作成しました.

GitHubに上がっている公式の設定例を落としてきて置きました.

wget https://raw.githubusercontent.com/ansible/ansible/devel/examples/ansible.cfg

必要に応じてコメントアウトを外せばいいと思います(僕は設定については変更していないのでデフォルトのままですが...…).

インベントリ(hosts)の作成

インベントリにはAnsible実行する対象を記述します. 今回は自身に対するので127.0.0.1を指定しています.

[localhost]
127.0.0.1

プレイブック(ubuntu-provisioning.yml)の作成

プレイブックはどこに何を実行するか記述します.

---
- hosts: localhost
  connection: local
  gather_facts: no
  roles:
    - apt_packages
    - fish

hostsのlocalhostはインベントリで設定したもの,ローカルのセットアップなのでconnectionはlocalです. gather_factsはyesにするとシステム変数がとってこれるそうです. またrolesには次に記述するtaskを持つディレクトリ名を記述します.

Roleの作成

roles以下の部分を作成します. まずはこのようにディレクトリを作成します.

$ tree
.
├── ansible.cfg
├── hosts
├── roles
│   ├── apt_packages
│   │   └── tasks
│   │       └── main.yml
│   └── fish
│       └── tasks
│            └── main.yml
└── ubuntu-provisioning.yml

必要なパッケージのインストール

./roles/apt_packages/tasks/main.yamlに記述します.

---
- name: Upgrade all package
  apt:
    update_cache: yes
    upgrade: yes

- name: Install apt packages
  apt:
    name:
      - vim
      - curl
      - git
      - docker-compose
      - fonts-powerline
      - tree
      - tmux
    state: latest

nameは自由につけることができます. aptなどはモジュールと呼ばれるもので,公式のモジュールを利用することで冪等性が担保されるそうです. モジュールはかなり種類が多いらしいので必要なモジュールを調べるのが大変そうだなと思いました.

fishの設定

./roles/fish/tasks/main.yamlに記述します.

---
- name: Upgrade all package
  apt:
    update_cache: yes

- name: Install packages
  apt:
    name:
      - fish
    state: latest

- name: Exec fish after bash
  lineinfile:
    dest: /home/username/.bashrc
    line: exec fish

- name: Make function directory
  file:
    path: /home/username/.config/fish/functions
    state: directory

- name: Install fisher
  get_url:
    url: https://git.io/fisher
    dest: /home/username/.config/fish/functions/fisher.fish

- name: Install fisher theme
  shell: fish -lc "fisher add {{ item }}"
  args:
    creates: /home/username/.config/fisher/github.com/{{ item }}
  with_items:
    - oh-my-fish/theme-bobthefish

実行

この記事のために再実行した結果なので,初回の実行とは表示が異なりますが下のような出力が出ました.

$ sudo ansible-playbook -i hosts ubuntu-provisioning.yml --ask-become-pass
BECOME password: 

PLAY [localhost] *****************************************************************************

TASK [apt_packages : Upgrade all package] ****************************************************
[WARNING]: The value True (type bool) in a string field was converted to 'True' (type
string). If this does not look like what you expect, quote the entire value to ensure it does
not change.
ok: [127.0.0.1]

TASK [apt_packages : Install apt packages] ***************************************************
ok: [127.0.0.1]

TASK [fish : Upgrade all package] ************************************************************
changed: [127.0.0.1]

TASK [fish : Install packages] ***************************************************************
ok: [127.0.0.1]

TASK [fish : Exec fish after bash] ***********************************************************
ok: [127.0.0.1]

TASK [fish : Make function directory] ********************************************************
ok: [127.0.0.1]

TASK [fish : Install fisher] *****************************************************************
ok: [127.0.0.1]

TASK [fish : Install fisher theme] ***********************************************************
ok: [127.0.0.1] => (item=oh-my-fish/theme-bobthefish)

PLAY RECAP ***********************************************************************************
127.0.0.1                  : ok=8    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

最後に

本当はサーバをセットアップする感じでMacからansibleを実行したかったんですが,接続がうまく行きませんでした. SlackとかVSCodeもsnapでインストールしたかったのですが,相変わらず日本語に対応していなさそうなので諦めました. 今回はAnsibleのことを色々調べていて疲れてしまったので,シンプルなことだけしましたが分かったような気になれたので良かったです.

今後何かでUbuntuを入れ直したりしたときに,ちゃんと使えればいいなぁという感じです. 正直Ansibleに詳しい方からするとツッコミどころ満載な気がしますが最後まで読んでいただきありがとうざいます.

参考

めざせGitHubマスター💪💪💪

リポジトリGETだぜ!(???)

はじめに

この記事はあくあたん工房 GWアドベントカレンダー 2020,2日目の記事です.ぜひ他の記事も見てみて下さい! gw-advent.9wick.com

GitHub便利だけど多機能すぎて把握しきれてないなぁと思ったので勉強も兼ねてまとめてみました.

基本的にリポジトリの管理者が使える便利機能をGitHubのタブの順に紹介していきます. 詳しい使い方とかまでは書けなかったので概要のみ紹介します.

f:id:ocucraqp:20200430194124p:plain

Code

概要

リポジトリの概要を書いたりリンクを貼ったりできます.

f:id:ocucraqp:20200430193512p:plain
f:id:ocucraqp:20200430193607p:plain

Topic

リポジトリにTopicを付けることができます. Topicをつけることで他の人が検索しやすくなったり,GitHubを使って研究している人がデータを集めやすくなったりするのでぜひ付けましょう

f:id:ocucraqp:20200430193849p:plain
f:id:ocucraqp:20200430193903p:plain

Issues

issueには様々なものを紐付けて管理できます.

f:id:ocucraqp:20200430195511p:plain

Assignees

誰がこのissueを担当するかを任命することができます. 任命できるのは自分か後述するCollaborator(協力者)のみです.

Labels

issueの内容が分かりやすくなるようにissueにラベルをつけることができます. デフォルトで9種類のラベルが用意されていて自分で新しく作ることもできます.

f:id:ocucraqp:20200430195424p:plain

Milestone

開発の目標や期日を設定することができます.

f:id:ocucraqp:20200430195657p:plain

issueへの紐付け

コミットメッセージissue番号を含めるとissueと紐付けることができます. issueの画面で関連するコミットが見えるので非常に分かりやすいです.

git commit -m "Update README.md #1"

ちなみにclose issue番号のようにするとissueを閉じてくれます.

git commit -m "Update README.md close #1"

Pull requests

こちらもIssueと同じくAssignees, Labels, Milestoneなどを設定できます. 追加で設定できるReviewers, Linked issuesについて説明します.

f:id:ocucraqp:20200430202015p:plain

Reviewers

名前の通りレビュー担当者を設定することができます.

Linked issues

issue と pull requestを紐付けることができます. 直接Linked issuesを選択してもいいんですが,先程のコミットメッセージのようにCommentに「Close #1」というふうにするとマージされたときにissueを自動で閉じたりしてくれます.

f:id:ocucraqp:20200430200958p:plain

File changed

そのプルリクでの差分を確認できます.

Actions

テストは自動化する時代がもうそこまできている! っていうかもう来ていますね. 今まで使ったことなかったので今回初挑戦しました.

f:id:ocucraqp:20200430202307p:plain
おすすめのworkflowみたいなのがでてきたのでTopicsとかから自動で判別してくれてるのかな? 分からないですが今回はDjangoリポジトリなのでDjangoのWorkflowを選択.
f:id:ocucraqp:20200430203025p:plain
ここで自由にテストを記述することができるっぽいです.

ほとんど使ったことがないため詳しく説明できないのですが,こちらの記事を見れば結構簡単に設定できそうです.

GitHubの新機能「GitHub Actions」で試すCI/CD | さくらのナレッジ

Projects

f:id:ocucraqp:20200430204007p:plain
この機能も初めて使いました. TrelloやPlannerのようなタスク管理機能ですね.
f:id:ocucraqp:20200430204152p:plain

GitHubでは基本的にはIssueなどで作業を管理すると思っていたのですが,Issueなどにはしないコードと関係ないような内容などを管理するのに使えそうです. あとここで作成したタスクをIssueにするなどの使い方もできます.

僕は普段Trelloを利用してるんですが,GitHubならではの機能もありますが,Trelloのほうが機能が充実してて使いやすいなと感じました. まだ機能を把握しきれていないだけかもしれないですが.

Wiki

簡易なWiki機能が利用できます.

f:id:ocucraqp:20200430205058p:plain
Markdownなどの形式で簡単な記事を書いていくことができます. チームのWikiがない場合や,プロジェクトに紐づけて管理する場合は便利そうですね.

Security

チームの参加者に見せるためのセキュリティポリシーを書いたり,依存関係のアラートを設定したりできます.

f:id:ocucraqp:20200430205823p:plain
セキュリティポリシーは有名なリポジトリを見ても書かれていない場合もあったので必ずしも設定してるわけではなさそうですし,READMEなど他の場所でセキュリティについて書いていたりしそうです.

依存関係のアラートはデフォルトで有効になっています. Dependabotというのが動いていて問題のあるライブラリなどを使用していると自動で更新してくれるプルリクを生成してくれて便利です. 現在サポートしているのは以下の言語です.

f:id:ocucraqp:20200430210946p:plain

Insights

誰がどれくらい貢献しているかや,このリポジトリを訪問した回数やcloneした回数などを確認できます.

Settings

リポジトリに関する設定が色々できます. リポジトリ名の変更や公開・非公開の設定などはここでします. 一部設定を紹介します.

Manage access

Collaboratorはここで招待できます. CollaboratorはReviewerやAssigneeに設定できる他,Wikiを書く権限などを付与できます.

Branch protection rules

Branchesから設定できます.

f:id:ocucraqp:20200430200709p:plain
設定したブランチにマージするプルリクに対し,レビューを必須にするなどの条件を設定できます.

Webhooks

ここを設定することでマージ時に自動でデプロイするなどを設定できるそうです.

その他

GitHub Pages

f:id:ocucraqp:20200430212641p:plain
github-pages
GitHubにpushしたリポジトリをサイトとして公開できます. 静的なサイトしか公開できませんが,HPとして使うには十分です. あくあたん工房のHPもこの機能で公開しています. 自分でもっているドメインを設定することもできます.

GitHub Pages を使ってみる - GitHub ヘルプ

最後に

改めて今まで全然GitHubを使いこなせてなかったなと思いました. 実際1人で開発する分には必要ないというか,使っていたら余計な労力が掛かりそうな部分もありました.

Actionsはすごく便利だと感じたので,今後は積極的に使っていきたいと思います.

Projects,Wikiなどは複数人で開発するときに有効活用したいです. もしかするとここらをきちんと書いておけば,外部からプロジェクトに参入してもらえるのかもしれません.

これであなたもGitHubマスター!
みんなもリポジトリGETじゃぞ〜〜!

grubのバグ?を修正した

Ubuntu20.04インストールの後,ubuntuを起動すると黒い画面に下のような文字だけ表示されるようになった.

GNU GRUB  version 2.02
Minimal BASH-Like line editing is supported. For the first word, TAB lists possible command completion. Anywhere else TAB lists possible device or file completions.

grub> 

ubuntu - Ubuntuが起動できません。minimal bash like line editing is supported - スタック・オーバーフローを見て,grubの画面から起動する場合は次のコマンドでなんとか起動できた.

configfile (hd0,msdos5)/boot/grub/grub.cfg

人によって違うと思うがubuntuをインストールしたストレージのどこかの/grub/grub.cfgを探してconfigfileコマンドを実行するとubuntuに入れるらしい.

毎回こんなのを打つのも面倒なので直したいと思って色々調べるとboot-repairなるものがあった.

boot-起動時にgrubの最小限のbashのようにgrubメニューを表示する方法は? -Ubuntuに尋ねる

さっきの方法でUbuntuに入ってからターミナルを開いて,boot-repiarをインストール,起動した.

sudo add-apt-repository ppa:yannubuntu/boot-repair
sudo apt-get update
sudo apt-get install -y boot-repair && boot-repair

起動したら色々設定できるが,何も設定せずにRecommend repairを選択して修復を開始. 開始すると指示を出されたのでそれを実行していった. まず,下のコマンドを打てと指示された.

sudo dpkg --configure -a
sudo dpkg --configure -a
sudo apt-get install -fy
sudo apt-get purge -y grub*-common shim-signed

これを実行するとGRUB2を消すかと聞かれるのでYesと答えろと指示される. 次に下のコマンドを入力した.

sudo apt-get install -y grub-efi-amd64-signed os-prober shim-signed linux-headers-generic install gawk package

入力するとしばらくboot-repairが頑張って「Boot Successfully repaired.」と表示され無事解決した.

Ubuntu20.04インストールしてみた

環境

最低限必要な環境

  • ストレージ:25GB(最小インストールの場合は5GB)
  • ブータブルUSB作成用OS:Ubuntu14.04
  • USBメモリ:4GB

自分の環境

普段はWindowsが入っているストレージもついているが,Windows環境に影響を与えたくなかったので外してインストールした.

isoファイルのダウンロード

Ubuntu 20.04 LTS (Focal Fossa)から64-bit PC (AMD) desktop imageをクリックしてダウンロード. f:id:ocucraqp:20200426113601p:plain

ブータブルUSB作成

Create a bootable USB stick on Ubuntu | Ubuntuを見ながらブータブルUSBを作成した. まず,Startup Disk Creator(ブータブルUSBの作成)が入ってなかったので次のコマンドでインストールして起動.

sudo apt update
sudo apt upgrade -y
sudo apt install usb-creator-gtk
usb-creator-gtk

起動したらさっきダウンロードしたisoファイルと使用するUSBメモリを選択してブータブルUSBの作成をクリック.

f:id:ocucraqp:20200426115748p:plain
ブータブルUSBの作成画面
ディスク消去の確認やパスワードの入力をするとディスクイメージの書き込みが始まる. 書き込み終了したら再起動.

インストール

Install Ubuntu desktop | Ubuntuを見ながらインストールを行った. F2やF11,F12,ESPなどを連打しながら再起動しブートメニューからUSBメモリを選択.

起動すると「Try Ubuntu」と「Install Ubuntu」が選べるので,PCにインストールせずにUbuntuを体験する場合は「Try Ubuntu」,インストールする場合は「Install Ubuntu」を選択する. 「Install Ubuntu」を選択すると各種設定が始まる.

まずは,キーボードと言語を設定する.

次に,色々なソフトが最初から入っているが通常インストールを行うか,できるだけコンパクトな最小インストールを行うか選ぶ. 自分は不要なものを入れたくなかったので最小インストールとしたが,容量に余裕があるのならどちらでもいいと思う. 「Download updates」とは「Install third-party software」は両方チェックした.(ここでグラボのドライバが入っている?)

次はインストール方法について. ストレージが空の場合は特に選択肢は出ないらしいが,元々18.04が入っていたので,「20.04と共存させる」,「一部を残して20.04にする」,「全部消して20.04」にするなどがあった. 自分は必要なデータもないので全部消すを選択した.

後は,タイムゾーン,ユーザー名,パスワードなどを設定するとインストールが始まる. 結構早くて数分で終わった. 再起動しろといわれるので再起動したらgrubの画面が出てきたが適当にexitすればubuntuが起動した.

f:id:ocucraqp:20200426115817p:plain
Ubuntu20.04デスクトップ画面

grubの画面はその後も出てきて困ったのでgrubのバグ?を修正したに記述した.

感想

18.04だとちょっと面倒だったRTX機でも問題なくインストールできて感動した. 18.04のときは魔改造してた上にどうやってそれをしてか全く覚えていないので,今回はUbuntuの素材の味を活かしたカスタマイズをしつつAnsibleを使ってうまいことできないかなって考えている.

Djangoで作るLIFFアプリ

はじめに

この記事はあくあたん工房 Advent Calendar 2019,10日目の記事です.Advent Calendarにはなんでもありな面白い記事が載ってるのでぜひ.

adventar.org

この記事ではLINE Front-end Framework(LIFF)の紹介を兼ねてLINE上で動く簡単なアプリを作っているのでそれを紹介したいと思います.

LIFFって?

LINE Front-end Framework(LIFF)は、LINEが提供するウェブアプリのプラットフォームです。このプラットフォームで動作するウェブアプリを、LIFFアプリと呼びます。

LIFFアプリを使うと、LINEのユーザーIDなどをLINEプラットフォームから取得できます。LIFFアプリではこれらを利用して、ユーザー情報を活用した機能を提供したり、ユーザーの代わりにメッセージを送信したりできます。

developers.line.biz

LINE Botなどと合わせて使えたりするのとプロフィールやルーム情報を利用できる点が利点なのかなと思います.また従来はLINE内のブラウザでしか利用できなかったそうなんですが,バージョン2から外部のブラウザでも使えるようになりできることの幅が広がりました.

作っているもの

友人と行っている筋トレを今日は何時に行く?とか誰が行く?とかを毎回LINEで決めているんですがもうちょっといい感じに連絡できないかなということで筋トレのスケジュールを管理するアプリを作ろうかなと思いました.どうせなんでやった筋トレのメニューとかも保存する機能とかほしいなぁ……

構成

  • LIFF 2.1
  • Django 3.0
  • Bootstrap 4.3

アプリはPythonAnyware上にデプロイしました.

仕組みをざっくりと図にしました.認証するとliffを使ってユーザ情報を取得したりメッセージを送信したりできるようになります.

f:id:ocucraqp:20191210213342j:plain

したこと

  1. LINEチャネル作成
  2. Djangoでアプリ作成
  3. デプロイ

LINEチャネル作成

LINEにはAPIやclova用のスキルの作成時に利用するLINE Developersというものがあります.ここにMessaging APIを登録することでLIFFを利用できるようになります.

developers.line.biz

Djangoでアプリ作成

普通にWebアプリを作ればいいのですがLIFFを組み込む必要があります.javascriptでLIFFを利用して取得したID等の情報をpostしてサーバで管理することにしました.

一応コードをおいておきます.

github.com

デプロイ

PythonAnywareにデプロイしました.Django Girls Tutorialを見ながらしたのですがすごく簡単にデプロイできてびっくりしました.

tutorial.djangogirls.org

おわりに

一応デモのようなものは動いてはいるんですが機能もほとんど実装できてない上セキュリティなどがガバガバのガバなので公開するのは辞めておきます.一応テストで出した画面をおいておきます.(これのために何年かぶりにプロフィールを設定しました.)

f:id:ocucraqp:20191210221931p:plain
プロフィールを表示するデモ

また,LINE内のブラウザで動かすのが前提みたいなところがあるのでもっと軽いフレームワークで作りたかった(LIFFを使ってる記事とか見るとVueとかで実装してるのが多い気がする)んですが他のフレームワークを勉強する余裕がなかったんで使用経験のあるDjangoになっちゃいました.

明日以降もAdvent Calendarは続いていくので見ていただければ幸いです.

LINE Dev Day参加レポート

はじめに

LINE 株式会社様より学生への交通費支援を利用させて頂き LINE DEVELOPER DAY に初めて参加してきました.楽しくてとても勉強になるイベントに招待頂きありがとうございました.自分が聞いたセッションや参加したハンズオンについてのまとめと感想を紹介できたらと思います.間違えている部分などがあれば優しく指摘していただけると嬉しいです.

入り口からスマホを何台も使ったロゴがあってびっくりしました.私は事前に顔認証での登録をしておいたのでスムーズに入場できました.受付も顔認証が認証端末に近づいたときには既に認証されているという速さでびっくりしっぱなしでした.

f:id:ocucraqp:20191125150015j:plain
豪華な入り口

keynote

speakerdeck.com

これまでの Dev Day や今年の事業の話と力を入れている 3 つの分野についての話です.最初の Keynote については Developer アカウントが詳しくツイートされていますので是非御覧ください.

twitter.com

3 つの分野は下の 3 つです.

  • AI
  • Data Platform & Infrastructure
  • Security & Privacy

AI

Smart Channel や LINE BRAIN の話がありました.Smart Channel とは,最近よくトーク一覧の上に表示されているあれです.なんとユーザの行動を基にした 2000 万次元もある特徴量を分析して,表示するコンテンツを決めているそうです.LINE BRAIN は,LINE で培われた AI を技術を外販するサービスです.受付でも使われていた顔認証システムや OCR 技術,自然言語処理,STT/TTS,動画解析など多くの技術が開発されています.自分は LINE の AI 関連技術は主に LINE Clova しか知らなかったので想像以上に力を入れているのが分かりました.会場で OCRAPI を無料体験できるコードを頂いたので時間があれば使ってみたいと思います.

Data Platform & Infrastructure

それぞれの欲しいデータがすぐ手に入る環境(Self-Service Data Platform)を目指しているそうです.LINE では毎日 1 兆件ものレコード(390TB)を処理しており,次の課題の解決が必要でした.

  1. Accessibility
  2. Multi-tenancy
  3. Data Quality

これらに対して,次のような解決策を考案したそうです.

  1. オンラインでのプラットフォームの統合
  2. スケーラブルなオンデマンド分析環境の実装
  3. 自動でデータ分類・メタデータの構築

プラットフォームの統合によってデータガバナンスが向上したそうです.また,プライベートクラウド Verda の紹介がありました.自社でクラウドを運用することで,不具合が発生しても迅速に対応することが可能になっています.ソフトウェアでインフラ機能を実装することでインフラのライフサイクルを開発のライフサイクルに近づけることができたそうです.

Security & Privacy

LINE では法律の専門家を加えたチームがいて,エンジニアと設計段階からセキュリティーとプライバシーについて話あって開発を行っている程プライバシーを大事にしているそうです.私もネット上のデータや OSS を利用したりする際法的に問題ないか心配になるときがあるので,ユーザだけでなくエンジニアも安心できる仕組みだと思います.またアカウント乗っ取りについて,昨年の乗っ取り件数が 0 件だったのは驚きました.乗っ取り対策についても機械学習が使われていて,フィルタリングルールを自動的に更新している手動で更新するコストを削減していました.他にもいち早く FIDO の導入が行われていたり,透明性レポートを発行しているなどの話がありました.

LINE が開発した時系列データベース‘Flash’の紹介

speakerdeck.com

眠いときは DB の話を聞くと元気がでるらしいです.

既存の DB は何かしらの欠点があったため新しい DB を作る必要があったそうです.新しい DB を作るにあたって必要だったものは次の 3 つ.

  • ユーザがどのプロトコルを使ってもサポートしたい
  • 簡単にスケールアウトでき読み込みも書き込みも高速
  • メンテンナンスコストが低い

これらを目標に実際に完成した DB は以下のようなものでした.

  • 秒間 400 万の Datapoints を書き込める
  • 秒間 1000 クエリを処理できる
  • Write P99 < 100ms
  • Read P99 < 100ms

時系列 DB とは,CPU などの情報を Label,時間毎の Label を Datapoint,一連の Datapoint を Serie として定義された DB です.Flash の実現には,基本的に利用することが多い最新の 28 時間分データは DRAM 保存したり,Delta-Delta Xor Algorithm というものを利用したそうです.

プロトタイプとベータ版はたったの 2 ヶ月で完成したが,製品としてリリースするには 1 年かかかったらしいです(通常は 5 年はかかるらしいのでめちゃくちゃ早い).現在のスペックにはまだ満足していなそうで,これから秒間に書き込める Datapoints を 100 倍,クエリの数を 10 倍にすることを目指しているとのことでした.

LINT (LINE Improvement for Next Ten years)

speakerdeck.com

2011 年から溜まっている過去の技術的負債を消すための活動について聞くことができました.LINE は「安定したメッセージングサービスを目指す段階」から,「より信頼でき,より柔軟なプラットフォームを目指す段階」に移行しており,多くの部分で改善が必要になっているそうです.これらの改善のために,様々な分野の専門家が集まった LINT チームが誕生しました.現在主に取り組んでいる課題は以下の 4 つです.

  1. HTTP/2 and Push
  2. Event delivery mechanism
  3. Authentication Token renewal
  4. General setting storage for client/server

セッションでは 1 と 2 について語られていて,1 については,Google がサポートを終了している SPDY をベースにした LEGY という内製プロトコルを使っており,それが無駄なリクエストをしているため,HTTP/2 ベースに変えていきたいという内容でした.2 についてはクライアントとサーバ間でのデータの不整合を分かりやすくしたり,久しぶりに LINE を起動すると使ってなかった期間のデータを一気にフェッチしようとして重くなるのを改善しようという取り組みです.データ整合性に関してはデータのダイジェストをクライアント・サーバ間で交換することで整合性を保つ Auto Repair という仕組みを導入しているそうです.フェッチの部分には,メッセージが 100 件以下程度なら従来どおりフェッチし,それ以上であれば FullSync という API を叩きデータを得る仕組みとなっていました.

他にも自分がずっと改善を望んでいるマルチアカウント機能やマルチデバイス機能も今後の課題として挙げられており改善が楽しみです.

Kubernetes の利用・普及,その先は何か?

このセッションのスライドは見つけられなかったです.最初は Docker や k8s(Kubernetes)などに関して軽い説明がありました.世界ではすでに k8s を使ったプロダクトは当たり前で,日本は出遅れている状態だそうです.また Google はすべての業務がコンテナで実行されているというのはさすが Google という感じでした.

なぜ k8s を選んだかについて

登壇者の河さんが所属するZ Labのミッションは「イケてるモダンなインフラを作る」というもの.Portability & Reproducibility の観点からコンテナ技術に注目したが,当時は k8s の他に docker swarm や mesos もありました.k8s を選んだのはスポンサー企業の数やセキュリティ対応などから将来性が高いと判断したから.とりあえず k8s を構築してみるとすぐに運用がハードであることが分かったそうです.VM と同時に k8s を運用するのは保守コストが非常に高いのでソフトウェアで解決しようということで Caas を立ち上げ.

CaaS (Container as a Service)

CaaS はいろいろ操作が 1 コマンドでできるように作られました.k8s は Declarative object configuration モデルをサポートしており,この設定を読み込み,処理するプログラムを Controller と呼ぶ.理想の状態を定義し,Controller がその状態になるように稼働し,この機構を Reconciliation Loop と呼ぶ.CaaS 自体が SPOF(単一障害点)になることを防ぐために,自身が所属する K8s クラスタも CaaS が生成する.CaaS が壊れても k8s が復旧し,k8sVM に障害が起きても CaaS が復旧する.

CaaS と k8s がそれぞれ補い合っているというシステムが面白いと思いました.このセッションを聞いて国内外の同年代に負けないように自分も頑張って行きたいと気持ちを改めました.

Gatebox の内と外 - AR でも VR でもない新しいデバイスプラットフォーム

www.gatebox.ai developer.gatebox.biz

Gatebox の仕組みをガッツリ紹介していただきました.こちらもスライドは見つけられなかったです.

Gatebox の癒しの花嫁である逢妻ヒカリは人らしい会話や能動的アクション,動きにこだわっているそうです.システムは Unity, STT/TTS, Gatebox SDK, CIC SDK などで構築されており,クライアント側のことを Hikari App,サーバ側のことを Hikari Brain としていました.ユーザの時間分布や特徴的な発話傾向などを分析することでクオリティが対応力が上げています.キャラクターの動きをフレーム単位で制御し,どんなときでも不自然にならないように状態を変更する技術がすごいと思いました.例えば飲み物を飲んでいるときに話すのはおかしいので,そういうことにならないように設定されているのはこだわりを感じました.今後は Gatebox 上で動くアプリ・サービスの開発がオープンでできるようになるというのは夢もあるし,サービスの数が増えるのはユーザにはかなり嬉しいことなんじゃないかと思います.

LINE Pay ハンズオン ~ あなたの Bot に LINE Pay 決済機能を追加してみよう

LINE Pay v3 ハンズオン資料 [Version. 2019.11.22]

最初に LINE Pay について簡単なまとめがありました.個人でも(法人でなくても)個人事業主であれば加盟店になれて,sandbox(開発環境)の利用のみなら加盟店になる必要もないそうです.LINE なのでユーザ行動と連動したサービスを作りやすいのも魅力です.

ハンズオンでは次の流れで行いました.

  1. LINE Pay Sandbox の設定
  2. ngrok の実行
  3. LINE Developers 上でチャネルの作成
  4. チャネルの設定(アクセストークンの発行や Webhook URL の設定など)
  5. サンプルプログラムのダウンロード
  6. リッチメニューの作成
  7. チャネル ID 等を設定して実行
  8. コードの解説

実際に動いた次のように画面が動いて感動しました.想像以上に簡単にできて感動しました(コードは書いてませんが).

f:id:ocucraqp:20191125152801p:plain
LINE Pay 商品画面

f:id:ocucraqp:20191125152828p:plain
LINE Pay カート画面

私は法人でも個人事業主でもないため,実際に活用ができないのが残念ですが,もしナイスなサービスが思いついたらまたチャレンジしたいです.

Journey of Feature Flag development in LINE Android

speakerdeck.com

LINE での Android アプリ開発の中で生まれた Feature Flag についてメリットから課題まで説明していただきました.

まずは Feature Branch についての説明がありましたが,これは Git を使って開発するときに機能ごとに切るブランチのことです.これの問題点ですが,大規模開発においては Feature Branch が多くなり,複雑に絡み合って管理やコンフリクトの修正が大変になるということでした.

Feature Flag は,従来 Feature Branch に対して送っていたプルリクを直接 Main Branch へ送るようにし,開発途中の機能についてはフラグをつけることで,本番環境では実行されないようにするというものです.これにより,コンフリクトサイズの縮小,リリース計画のスムーズ化,機能の有効/無効の切り替えの簡易化,全員が開発中のコードを確認できるなどのメリットがありました.すごくわかりやすいメリットで数人での開発でも開発の内容によっては有効に利用できそうだと思いました.

次に Feature Flag の限界についてです.従来のライブラリのアップデート等に適用するのが難しかったり,プロトタイプなどを作成する際には過剰な管理になってしまったなどです.社内で実際に Feature Flag を使っていた際のトラブルにも触れていて,flag 生成のコードがコピーされ参照関係で問題が起こったことなどが語られていました.

なんとこのセッションのタイミングで LINE のアンドロイド開発用の Feature Flag を用いる開発ツールが公開されました.以下の URL から利用することができます.

github.com

LINE Developer Community の歩み

LINE の開発者のコミュニティだと思ったら社外のコミュニティでした.こういうコミュニティがあったのは知らなかったので知ることができてよかったです.LINEDC から生まれた関連ツールもあり,C# SDK LINE Messaging API や LIFF CLI のサーバ API をいい感じに CLI から実行できるツールなどがそうらしいです.このコミュニティでは Bot を公開できたり,API Expert の方に質問できます.実際に会場でも Q&A が和気あいあいと行われて,コミュニティの熱が伝わってきました.自分も LIFF を使ってアプリ作りたいと思っているので,疑問があれば質問したいなと思いました.

おわりに

多くの新しい知識を得られたとともに同世代の学生にも出会うことができ,非常に刺激的な2日間となりました.ほとんど理解できなかったセッションもあり,もっと勉強しないとと思いました.また,もっと質問したりすればよかったというのが心残りなので,今後もこのようなイベントに参加して見聞を広めればと思います.改めて LINE 株式会社様,ありがとうございました.

f:id:ocucraqp:20191125151105j:plain
1日目の後で大井町で食べた美味しい味噌バターラーメン

f:id:ocucraqp:20191125151209j:plain
会場近くのUCガンダム デストロイモード

乱雑に保存された画像たちを供養!

はじめに

これはあくあたん工房お盆休みアドベントカレンダー6日目 (8月15日)の記事です.

僕は今まで,外で撮った写真やネットで集めてきた可愛いイラストなどを,スマホに適当に保存して,スマホの買い替え時にPCに移していました.そのため,PCの画像フォルダは訳の分からないフォルダ名に全然別の種類の画像が乱雑に保存されている状況でした(まさに画像の墓場).このままでは画像たちが浮ばれませんが手動でフォルダ分けは辛い……

f:id:ocucraqp:20190815132425p:plain
無意味なフォルダ名たち

ということで機械学習の勉強も兼ねて乱雑な画像フォルダを分類してくれる分類機を作成したいと思います.(機械学習全く理解できてないので解説だとはお思わず,ツッコミを入れながら読んでください.)

↓こちらの記事を非常に参考にさせていただきました. qiita.com github.com

環境

  • Google Colaboratory
  • Chainer 5.4.0

手順

  1. 画像の準備
  2. 標準化
  3. 学習
  4. 分類・評価

僕の画像フォルダはほとんどが風景・犬(ラブラドール・レトリバー)・萌え画像のどれかなのでこの3パターンに分類したいと思います.

画像の準備

学習用の画像と評価用の画像を用意します.

学習用画像

「風景」,「ラブラドール 茶色」,「イラスト」をGoogleで検索してでてきた画像を使うことにしました.ガバガバ検索ワードですが全然違うの大丈夫だと信じました.スクレイピングの知識もないので参考記事にあるこちらのdownload.pyを使い,各画像96枚ずつ用意しました.これらの画像はGoogle Driveのtraining-dataフォルダ内に0~2のフォルダを作り,0から順に「風景」・「犬」・「萌え画像」のフォルダとして保存しました.

qiita.com

f:id:ocucraqp:20190815162842p:plain
風景の画像
f:id:ocucraqp:20190815162858p:plain
ラブラドールの画像
f:id:ocucraqp:20190815162913p:plain
イラストの画像

評価用画像

最終的には自分の画像全てを分類したいのですが,時間がかかるので僕の画像中から3つの種類をそれぞれ20枚を用意して分類させ,だいたいの正答率を調べたいと思います.こちらの画像はGoogle Drive内のtest-dataフォルダ内に学習用画像と同じように保存しました.

ちなみに画像フォルダには少なくとも2万5千枚の画像があるっぽいです.中学生の頃にくだらない面白画像とかをダウンロードしてたのを死ぬほど後悔してます(消したいけど他の画像と混ざってて面倒).

標準化

まずは,GoogleDriveをColaboratoryにマウントします.

from google.colab import drive
drive.mount('/content/drive')

次に,データを読み込んで,そのままでは解像度もバラバラなので64x64にリサイズしました.最初はChainerのLabeledImageDatasetクラスを使ってリサイズしようとしてたんですが,上手くいかなかったため,参考記事の通りOpenCVで行いました.

import numpy as np
import cv2
import os

# 分類
words = ["風景", "犬", "萌え画像"]

# データフォルダ名
training_folder_name = "drive/My Drive/training-data/"
test_folder_name = "drive/My Drive/test-data/"

# wordsに対する辞書を作成
detection_words = {}
for i in range(len(words)):
    detection_words[words[i]] = i

# 学習用データ
x_train = []
# 学習用ラベル
t_train = []
# テスト用データ
x_test = []
# テスト用ラベル
t_test = []

# 学習用データとラベルを準備
for word in detection_words:
    path = training_folder_name + '/' + str(detection_words[word])
    imgList = os.listdir(path)
    img_num = len(imgList)
    for j in range(img_num):
        imgSrc = cv2.imread(path + "/" + imgList[j])
        if imgSrc is None: continue

        # 画像を64x64にリサイズ
        imgSrc = cv2.resize(imgSrc, (64, 64))
        x_train.append(imgSrc)
        t_train.append(detection_words[word])

# テスト用データとラベルを準備
for word in detection_words:
    path = test_folder_name + '/' + str(detection_words[word])
    imgList = os.listdir(path)
    img_num = len(imgList)
    for j in range(img_num):
        imgSrc = cv2.imread(path + "/" + imgList[j])
        if imgSrc is None: continue

        # 画像を64x64にリサイズ
        imgSrc = cv2.resize(imgSrc, (64, 64))
        x_test.append(imgSrc)
        t_test.append(detection_words[word])

# データは1/255して値を0~1にする
x_train = np.array(x_train).astype(np.float32).reshape((len(x_train), 3, 64, 64)) / 255
t_train = np.array(t_train).astype(np.int32)
x_test = np.array(x_test).astype(np.float32).reshape((len(x_test), 3, 64, 64)) / 255
t_test = np.array(t_test).astype(np.int32)

学習用ネットワーク

2層の畳み込み層+3層の全結合層です.画像には畳み込みが効くそうです.

import chainer.links as L
import chainer.functions as F
from chainer import Chain


# モデル
class CNN(Chain):
    def __init__(self):
        super(CNN, self).__init__(
            # 畳み込み層
            conv1=L.Convolution2D(None, 20, 5), 
            conv2=L.Convolution2D(None, 50, 5), 
            # 全結合層
            l1=L.Linear(None, 500),
            l2=L.Linear(None, 500),
            l3=L.Linear(None, len(detection_words), initialW=np.zeros((len(detection_words), 500), dtype=np.float32))
        )

    def forward(self, x):
        h = F.max_pooling_2d(F.relu(self.conv1(x)), 2)
        h = F.max_pooling_2d(F.relu(self.conv2(h)), 2)
        h = F.relu(self.l1(h))
        h = F.relu(self.l2(h))
        h = self.l3(h)
        return h

学習

最適化関数にAdamというのを使い,エポック数を30,バッチサイズを15とし

from chainer import optimizers

model = CNN()
# 最適化関数
optimizer = optimizers.Adam()
optimizer.setup(model)

n_epoch = 30
batch_size = 15
N = len(t_train)

# accuracyとlossの推移をみるために用意
accuracy_histry = []
loss_histry = []

各種設定を終えたら次のコードで学習が始まります.

from chainer import Variable

for epoch in range(n_epoch):
    # train
    sum_loss = 0
    sum_accuracy = 0

    perm = np.random.permutation(N)
    for i in range(0, N, batch_size):
        x = Variable(x_train[perm[i:i + batch_size]])
        t = Variable(t_train[perm[i:i + batch_size]])
        y = model.forward(x)
        model.zerograds()
        loss = F.softmax_cross_entropy(y, t)
        acc = F.accuracy(y, t)
        loss.backward()
        optimizer.update()
        sum_loss += loss.data * batch_size
        sum_accuracy += acc.data * batch_size
    accuracy_histry.append(sum_accuracy / N)
    loss_histry.append(sum_loss / N)
    print("epoch: {}, mean loss: {}, mean accuracy: {}".format(epoch, sum_loss / N, sum_accuracy / N))

分類・評価

import matplotlib.pylab as plt

# グラフ化
X_axis = np.arange(n_epoch)
plt.plot(X_axis, accuracy_histry, label="mean-accuracy")
plt.plot(X_axis, loss_histry, label="mean-loss")
plt.xlabel("epoch")
plt.title("accuracy,loss-epoch")
plt.legend()
plt.show()

cnt = 0
testsize = len(t_test)
count = {}
for i in range(testsize):
    x = Variable(np.array([x_test[i]], dtype=np.float32))
    t = t_test[i]
    y = model.forward(x)
    y = np.argmax(y.data[0])

    try:
        count[t]
    except:
        count[t] = [0, 0]
    if t == y:
        cnt += 1
        # それぞれのラベルの正解数をカウント
        count[t][0] += 1
    # それぞれのラベルの合計数をカウント
    count[t][1] += 1
    # print(t,y)
print("accuracy: {}".format(cnt / testsize))
# それぞれのラベルの正解数
for i in count:
    for word in detection_words:
        if detection_words[word] == i:
            print(
                "{}の正当数:{}、誤答数{}、正解率{}".format(word, count[i][0], count[i][1] - count[i][0], count[i][0] / count[i][1]))

出力は次のようになりました. f:id:ocucraqp:20190815203436p:plain

accuracy: 0.6333333333333333
風景の正当数:10、誤答数10、正解率0.5
犬の正当数:16、誤答数4、正解率0.8
萌え画像の正当数:12、誤答数8、正解率0.6

感想

こんな精度では画像たちが報われない!今後も地縛霊のごとくHDDに残っていくことでしょう.

いろいろ頑張ろうと思ってたのですが,最終的に分からないところが多すぎて参考の記事からコードをそのまま使わせてもらうことが多くなってしまいました.

やはり機械学習は難しい……

これからもっと精進して来年には悪霊,もとい画像フォルダを供養したいと思います.