上場したてのベンチャー企業に中途入社して5ヶ月で辞めました

タイトルの通りの退職エントリーになります。
2018年3月1日から株式会社ZUUでお世話になっていましたが、2018年8月末をもって退職する事になり、29日が最終出社でした。

21歳の時からIT業界に身を置いてきましたが、たった5ヶ月という最短記録を更新する状況になり、決断が早すぎたかもしれないと少し感じます。

辞めた経緯を以下に記載しますが、会社が悪いと批判するつもりは無く、事業自体はとても面白い事をやろうとしている会社だと思います。

ただ、私がベンチャー企業に求めていた事と、会社の考えの違いがあまりにも大きく、それについて幾度となく改善の指摘をしても変わる気配が無かった。
それだけの事です。

ベンチャー企業に求めていた事
”一緒に”作り上げていく面白さ

前職は韓国の大手ゲーム会社の子会社で働いており、何故か日本法人の仕事も見るポジションでした。
そこで感じたのは、セクショナリズムがゴリゴリに強く、検討についての議論が一切出来ない。というものでした。
(申請ベースで作業依頼が届き、必要性や用途のヒアリングして構築する。作業レイヤーもOSのチューニングまでとシンプルかつ面白味も無い)

ベンチャーと言う言葉に夢を見ていたと言われればそうかも知れませんが、ZUUの開発部門は正社員、契約社員、業務委託の合計で8名程度の小規模のチームにも拘らず適切な情報共有もない状態で、来週・来月に何をやらなければならないのか?がチーム内で共有されず、不明瞭なまま動いている状態でした。

企画者や中間層の会議で「○○やりたい!」「いいねぇ!」などが話され、そのまま開発に降ってくる。という状況でしょうか。
私がやりたかったのは、その会議の中で一緒に決めていく事でしたが、在籍していた5ヶ月間の間で他部門と会議を行った事は1度もありません。

bigquery事件
一番ビックリしたのは、DMPツールとしては他の製品を使っているにも拘らず、「ドヤりたいからbigquery使いたい!」でした。

bigqueryを使って何を分析するのか?が目的になっていないので、要件なんてありません。
とりあえず、既に一部をDMPツールに送信しているアプリケーションのログをbigqueryにも送り始め、re:dashで可視化させたり集計してスプレットシートに吐いたりとやりましたが、意味があるのか無いのか未だに分かりません。

cockroachDB事件
仕様決め、共有が無いのが1番辛かったです。
百歩譲って、構成や使い方に変動がないケースで共有が無かったり遅れる場合は、まだ理解出来ます。
開発環境が各個人のDockerの為、インフラに頼らなくても開発には支障がないでしょうが、
開発のプルリクエストでチラ見して気づいたcockroachDBの導入についても「インフラとして調査すらしてないよ?」とかもありました。

その時は、さすがに頭に来てしまい、

と、口汚くslackで上司に話を投げましたが、この回答があり非常にガッカリした記憶があります。

他部門との会議は基本的に室長が行なっており、そこで決まる仕様や要件などフィードバックが無い。
なぜ数名しかいない開発チームなのにコミュニケーションロスが生じるのか?
とても理解に苦しみます。

これらのやり方が変わらない限り、ZUUにエンジニアが定着する事は無いと思います。
リファラル採用も募集していましたが、こんな劣悪な体制に知り合いエンジニアを売るような事は出来ません。

開発を外注するにしても、社内で仕様や要件を決められない為、それも難しいでしょう。
(高額なSIerにお願いすれば別ですが)

次は何するの?

またベンチャー企業と呼ばれる環境に身を置きたいと思っているので、次の転職先もZUUと同じ規模の会社になります。

次こそ”一緒に作り上げて行ける”ように、頑張りたいと思います。

「さんあ~る」からゴミ捨てカレンダーをスクレイピングする

4月に千葉県柏市のゴミ捨てアプリ「さんあ〜る」が、Web版としてリリースされました。

ごみ分別アプリ「さんあ~る」がインターネットでも利用できます!

さんあ〜るのごみカレンダーをiframeとしてWPに引っ張ってこようと思ったけど拡縮の問題があり挫折。
あと、画像がいらないな、と思ったのでpython3でスクレイピングしてみた。

# coding: UTF-8
import datetime
import urllib.request
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
from bs4 import BeautifulSoup

today = datetime.date.today()
year = today.strftime("%Y")
month = today.strftime("%m")

# アクセスするURL
# XXXXXは地区番号らしい
url = 'https://manage.delight-system.com/threeR/web/calendar?jichitaiId=kashiwashi&areaId=22125&year={year}&month={month}'.format(year=year, month=month)

html = urllib.request.urlopen(url)
soup = BeautifulSoup(html, "html.parser")

table = soup.find_all("table")
cal = []
for tag in table :
    try:
        #import pdb; pdb.set_trace()
        days = tag.find_all("td")  # カレンダーのセル数(7日×週)
        for day in days:
            span = day.find_all('span')
            tmp = []
            if len(span) > 0  :
                for num in range(len(span)):
                    try:
                        if span[num].get("class")[0] in ('common','sat','sun'): 
                            tmp.append(span[0].string)           # 日
                            tmp.append(span[0].get('class')[0])  # 曜

                        elif span[num].get("class")[0] in 'trash_kind_name': # ゴミ種類
                            tmp.append(span[num].string)
                        cal.append(tmp)
                    except:
                        pass
            else : # 空白セル
                cal.append([''])
    except:
        pass

print(cal)

後で、オリジナルのカレンダーの情報に充てがう予定なのでlistに入れる。

結果

[[''], [''], [''], [''], [''], ['1', 'common'], ['2', 'sat'], ['3', 'sun'], ['4', 'common', '可燃ごみ'], ['4', 'common', '可燃ごみ'], ['5', 'common', '不燃ごみ'], ['5', 'common', '不燃ごみ'], ['6', 'common', '容器包装プラスチック類'], ['6', 'common', '容器包装プラスチック類'], ['7', 'common', '可燃ごみ'], ['7', 'common', '可燃ごみ'], ['8', 'common'], ['9', 'sat'], ['10', 'sun'], ['11', 'common', '可燃ごみ'], ['11', 'common', '可燃ごみ'], ['12', 'common'], ['13', 'common', '資源品', '容器包装プラスチック類'], ['13', 'common', '資源品', '容器包装プラスチック類'], ['13', 'common', '資源品', '容器包装プラスチック類'], ['14', 'common', '可燃ごみ'], ['14', 'common', '可燃ごみ'], ['15', 'common'], ['16', 'sat'], ['17', 'sun'], ['18', 'common', '可燃ごみ'], ['18', 'common', '可燃ごみ'], ['19', 'common', '不燃ごみ'], ['19', 'common', '不燃ごみ'], ['20', 'common', '容器包装プラスチック類'], ['20', 'common', '容器包装プラスチック類'], ['21', 'common', '可燃ごみ'], ['21', 'common', '可燃ごみ'], ['22', 'common'], ['23', 'sat'], ['24', 'sun'], ['25', 'common', '可燃ごみ'], ['25', 'common', '可燃ごみ'], ['26', 'common'], ['27', 'common', '資源品', '容器包装プラスチック類'], ['27', 'common', '資源品', '容器包装プラスチック類'], ['27', 'common', '資源品', '容器包装プラスチック類'], ['28', 'common', '可燃ごみ'], ['28', 'common', '可燃ごみ'], ['29', 'common'], ['30', 'sat']]

cal[n][1]の値は、
sat /土曜
sun / 祝祭日
common / 平日
らしい。

fluentdでMySQLにデータを入れる

https://github.com/tagomoris/fluent-plugin-mysqlを使って、fluentd-3.xでmysql 5.7にログを入れる方法のメモ

同時にbigqueryに対してもログを入れているので、@type copyを使う。

<match xxx.yyyy.accesslog>
  @type copy

  # bigquery用
  <store>
    @type             bigquery
    auth_method       json_key
    json_key          PATH/TO/FILE
    project           GCP PROJECT
    dataset           ${tag[0]}
    table             ${tag[1]}_${tag[2]}_%Y%m%d
    auto_create_table true
    schema_path       /etc/td-agent/schema.json

    <buffer tag,time>
      @type file
      path        /var/log/td-agent/buffer/papillon_accesslog
      timekey 1d
      chunk_limit_size 1000000
      queue_limit_length 128
      flush_interval 1
      retry_max_times 17
      retry_wait 1s
    </buffer>
    <inject>
      time_key time
      time_format %Y-%m-%d %H:%M:%S
    </inject>
  </store>

  # MySQL用にTimeをISO8061からDATETIMEに変換する。
  <store>
    @type record_reformer
    output_tag mysql.${tag_suffix[0]}   # tag名に「mysql」を追加
    enable_ruby true     # ruby有効化
    auto_typecast true
    <record>
      time ${require 'time'; Time.parse(record["time"]).strftime("%Y/%m/%d %H:%M:%S")} # TimeをISO8061からDATETIME
    </record>
  </store>
</match>


<match mysql.xxx.yyyy.accesslog>
    @type mysql_bulk
    host 10.254.0.xx
    database TABLE
    username USER
    password PASSWORD
    column_names time,user_id,uri,referer,remote_ip
    key_names time,user_id,uri,referer,remote_ip
    table log
    transaction_isolation_level read_committed    # 2018/5から、デフォルト値がnulになったので、指定しないとトランザクション貼れない。
    flush_interval 1s
</match>

ハマったのは、

transaction_isolation_level read_committed  

の記述の部分。

ここ以外の記述で、td-agentはちゃんと動くが、

2018-06-01 20:30:30 +0900 [warn]: #0 failed to flush the buffer. retry_time=4 next_retry_seconds=2018-06-01 20:30:30 +0900 chunk="56d92e8467c4fab0440db16ee36f0d34" error_class=Mysql2::Error error="You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1"

こんなエラーが出る。

mysql側でgeneral_logを有効にするも、

2018-06-01T20:30:46.022528+09:00           14 Connect   logger@fluentd01 on accesslog using TCP/IP
2018-06-01T20:30:46.023467+09:00           14 Query     SHOW COLUMNS FROM log
2018-06-01T20:30:46.024207+09:00           14 Quit

と、全然有用なログじゃないし。

結局、tcpdumpを取得して見たら、分離レベルを指定せずに 「SET SESSION TRANSACTION ISOLATION LEVEL」を投げている事が原因だった。

解決してよかったー

ansibleでタスクをスキップしても同名のregisterが設定される

ansibleでデプロイするタスクを書いていて遭遇した仕様の話。

やりたいことは、gitのtagやbranch名を引っ張ってきてslackに通知したい。
ステージング環境でのデプロイでも使っているタスクなので、「masterブランチのtag」が入るか、「develop/test」などのブランチが入ってきても適切に通知したい。

最初は、

# branchがmasterの場合、gittagにtag名を入れる
  - name: git status
    shell: git status | head -1 | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: branch_name
    changed_when: false

  - name: check tag version
    shell: git status | head -1 | awk '{print $5}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gittag
    when: branch_name.stdout == "HEAD"
    changed_when: false


# branchがmasterではない場合、gittagにブランチ名を入れる
  - name: check branch
    shell: git status --short --branch | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gittag
    when: branch_name.stdout != "HEAD"
    changed_when: false

# slack送信
  - name : send tag (finish)
    slack:
      token: '{{ slack_token }}'
      msg: "デプロイが開始します\n
            ``` 
            project: {{project}}\n
            env: {{env}}\n
            version: {{ gittag.stdout }}\n
            date: {{ lookup(\"pipe\",\"date +%Y/%m/%d-%H:%M:%S\") }}
            ```"
      channel: '{{ post_channel }}'
      color: good
    run_once: true

と書いたが、branchがmasterで、check branchタスクがskipされているにも関わらず、register: gittagの中身が空っぽになる。
公式を確認すると、

If a task fails or is skipped, the variable still is registered with a failure or skipped status, the only way to avoid registering a variable is using tags.

タスクが失敗したり、スキップしたりした場合も変数は設定される。
らしい。

結局、以下のように修正。

  vars_files:
    - ../vars/vars.yml
  tasks:

# branchがmasterの場合、gittagにtag名を入れる
  - name: git status
    shell: git status | head -1 | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: branch_name
    changed_when: false

  - name: check tag version
    shell: git status | head -1 | awk '{print $5}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gittag
    when: branch_name.stdout == "HEAD"
    changed_when: false


# branchがdevelopではない場合、gittagにブランチ名を入れる
  - name: check branch
    shell: git status --short --branch | awk '{print $2}'
    args:
      chdir: "{{ workdir }}/{{ repobase }}/{{ project }}"
    register: gitbranch
    when: branch_name.stdout != "HEAD"
    changed_when: false

# slack送信
  - name : send tag (finish)
    slack:
      token: '{{ slack_token }}'
      msg: "デプロイを開始します\n
            ``` 
            project: {{project}}\n
            env: {{env}}\n
            version: {{ gittag.stdout }}\n
            date: {{ lookup(\"pipe\",\"date +%Y/%m/%d-%H:%M:%S\") }}
            ```"
      channel: '{{ post_channel }}'
      color: good
    when: branch_name.stdout == "HEAD"
    run_once: true


  - name : send branch (finish)
    slack:
      token: '{{ slack_token }}'
      msg: "デプロイを開始します\n
            ``` 
            project: {{project}}\n
            env: {{env}}\n
            version: {{ gitbranch.stdout }}\n
            date: {{ lookup(\"pipe\",\"date +%Y/%m/%d-%H:%M:%S\") }}
            ```"
      channel: '{{ post_channel }}'
      color: good
    when: branch_name.stdout != "HEAD"
    run_once: true

上手い書き方無いかなぁ

CentOS7のファイルディスクリプタの設定

確認方法

cat /proc/`pgrep -f サービス名`/limits

Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             29222                29222                processes 
Max open files            1024                1024                files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       29222                29222                signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us        

変更方法

/usr/lib/systemd/system/サービス管理ファイル に、

LimitNOFILE=65536

を追加する。

追加した跡は、

systemctl daemon-reload
systemctl restart daemon

として、サービスを再起動する。

pecoで遊んでみる

Twitterでやり取りさせて貰っているShu1さんの記事が面白かったので、GCEで再現してみた
http://blog.jicoman.info/2018/04/ec2-ssh-using-peco/

※ gcloudの設定が終わっている事前提

pecoインストール

wget https://github.com/peco/peco/releases/download/v0.5.3/peco_linux_amd64.tar.gz

tar zxvf peco_linux_amd64.tar.gz
mv peco_linux_amd64/peco /usr/local/bin/
rm -rf peco_linux_amd64*

関数設定

vim ~/.bash_profile

# ssh簡単にするやつ
function ssh-gce() {
  local user="root"
  local host=$(gcloud compute instances list | grep stg | awk '{print $1,$3,$4}' | column -t -s" " | /usr/local/bin/peco | awk '{print $3}')
  ssh "$user@$host"
}

修正後に、

source ~/.bash_profile

出来たー!

pop2imapを使ってメールデータを移行する

お客さんの環境(さくらレンタルサーバー)から、自前で構築したIMAPSの環境にメールデータを移行したいと話があった。

色々探して、perlのpop2imapってツールを見つけた。
これならサーバーにSSHログイン出来ない環境でも、IMAPかPOP3が動いていればデータの移行が簡単に出来る。

wget http://www.linux-france.org/prj/pop2imap/dist/pop2imap-1.27.tgz

tar zxvf pop2imap-1.27.tgz

cd pop2imap-1.27/

INSTALLを見ると、必要がモジュールが書かれている。

You need : 
- Perl 
  try : perl -v
  try : perl -c pop2imap

- Mail::IMAPClient module 
  try : perl -mMail::IMAPClient -e ''

- Mail::POP3Client module 
  try : perl -mMail::POP3Client -e ''

- Email::Simple module
  try : perl -mEmail::Simple -e ''

- Date::Manip module 
  try : perl -mDate::Manip -e ''

- IO::Socket::SSL  module (optional needed with --ssl1 or --ssl2)
  try : perl -mIO::Socket::SSL -e ''

Any software packager should add all the perl modules dependencies by default
since users can use any option (--ssl*).

これらは入っていなければyumなりCPAMなりでインストールする。
yumの場合は

yum -y install \
perl-Mail-POP3Client.noarch \
perl-Mail-IMAPClient.noarch \
perl-Mail-POP3Client.noarch \
perl-Date-Manip.noarch \
perl-IO-Socket-SSL.noarch

モジュールを入れたら、makeする。

make
make install  # /usr/bin/pop2imap にインストールされる

オプションは直感的に分かるけど、パスワードを平文で打つので、作業後はhistory消しておいた方が良さそう。

pop2imap --help

usage: /usr/bin/pop2imap [options]

Several options are mandatory. See the example below.

--from        <string> : parsed as <user1>@<host1>[:<port1>]
--host1       <string> : "from" POP server. Mandatory.
--port1       <int>    : port to connect. Default is 110 (ssl:995).
--user1       <string> : user to login.   Mandatory.
--password1   <string> : password for the user1. Dangerous, use --passfile1
--passfile1   <string> : password file for the user1. Contains the password.
--ssl1                 : enable ssl on POP connect
--to          <string> : parsed as <user2>@<host2>[:<port2>][/<folder>]
--host2       <string> : "destination" IMAP server. Mandatory.
--port2       <int>    : port to connect. Default is 143 (ssl:993).
--user2       <string> : user to login.   Mandatory.
--password2   <string> : password for the user2. Dangerous, use --passfile2
--passfile2   <string> : password file for the user2. Contains the password.
--ssl2                 : enable ssl on IMAP connect
--starttls2            : use starttls on IMAP connect instead of SSL
--timeout2    <int>    : Connections timeout in seconds. Default is 240.
--folder      <string> : sync to this IMAP folder.
--delete               : delete messages in "from" POP server after
                         a successful transfer. useful in case you
                         want to migrate from one server to another one.
                         They are really deleted when a QUIT command
                         is send.
--idatefromheader      : sets the internal dates on host2 same as the 
                         "Date:" headers from host1. Turned on by default.
--dry                  : do nothing, just print what would be done.
--debug                : debug mode.
--debugimap            : IMAP debug mode.
--debugpop             : POP debug mode.
--tests                : Run non-regression tests
--quiet                : Only print error messages
--version              : print sotfware version.
--help                 : print this message.

Example: to synchronise pop  account "foo" on "pop3.truc.org"
                     to imap account "bar" on "imap.trac.org"

/usr/bin/pop2imap \
   --host1 pop3.troc.org --user1 foo --passfile1 /etc/secret1 \
   --host2 imap.trac.org --user2 bar --passfile2 /etc/secret2


Branched by Phil Carmody <phil.carmody@partner.samsung.com> from:
 $Id: pop2imap,v 1.27 2015/11/03 23:34:02 gilles Exp gilles $ 
      pop2imap copyleft is the GNU General Public License.

使い方

pop2imap --host1 移行元サーバー --user1 移行元ユーザー名 --password1 移行元パスワード --host2 移行先サーバー --user2 移行先ユーザー名 --password2 移行先パスワード -ssl2(IMAPSで接続)

おまけ
historyを削除する

history -c

EC2にswapを増やす

訳あってEC2のt2.nano(メモリ 0.5GB)でCPAMを使いたいけど、CPAMだけで300MB近くメモリを食うので、isntall処理がAbortしてしまう。

#  perl -MCPAN -e shell
Terminal does not support AddHistory.

cpan shell -- CPAN exploration and modules installation (v2.16)
Enter 'h' for help.

cpan[1]> install Mail::IMAPClient
Reading '/root/.cpan/sources/authors/01mailrc.txt.gz'
............................................................................DONE
Reading '/root/.cpan/sources/modules/02packages.details.txt.gz'
  Database was generated on Tue, 10 Apr 2018 14:54:48 GMT
....................................................Killed

遅くてもinstallが出来ればいいので、SWAP付けて逃げる。

# free -m
              total        used        free      shared  buff/cache   available
Mem:            483         174         222          32          86         245
Swap:             0           0           0  ← SWAPがない

/var/swapfileを作成して、SWAPに当てる

# dd if=/dev/zero of=/var/swapfile bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 15.0153 s, 71.5 MB/s

# mkswap /var/swapfile
mkswap: /var/swapfile: insecure permissions 0644, 0600 suggested.
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=2311e423-13a4-4451-b081-05ac4074c8bf

# swapon /var/swapfile
swapon: /var/swapfile: insecure permissions 0644, 0600 suggested.

# free -m
              total        used        free      shared  buff/cache   available
Mem:            483         175           5          32         302         244
Swap:          1023           0        1023  ← SWAP増えた!

nginxでのリダイレクト

nginxが.htaccess使えないの忘れていたので、HTTPで来たアクセスをHTTPSにリダイレクトする設定を今更書いた

server {
    listen       80;
    server_name  www.jhhk-family.net;
    return 301 http://www.jhhk-family.net/$request_uri;
}