[AWS ALB] EC2で最初の接続だけ異常に重い時

EC2で最初の接続だけ異常に重く、一度接続すると、サクサク動く。
何故だ?curlで計測したところ、以下の様に最初の接続に130秒もかかっている。

$ curl ‘https://*.com/login’ -H ‘Accept-Encoding: gzip, deflate, sdch’ ept-Language: en-US,en;q=0.8,ja;q=0.6′ -H ‘Upgrade-Insecure-Requests: 1’ -H ‘User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36’ -H ‘Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8’ -H ‘Connection: keep-alive’ –compressed -o /dev/null -w “%{time_starttransfer}\n” -s
130.982826

最初、メモリが問題かと思って、増設し、さらにswapもつけたが、それでも変わらない。

$ df
ファイルシス 1K-ブロック 使用 使用可 使用% マウント位置
devtmpfs 492676 0 492676 0% /dev
tmpfs 503444 0 503444 0% /dev/shm
tmpfs 503444 524 502920 1% /run
tmpfs 503444 0 503444 0% /sys/fs/cgroup
/dev/xvda1 18862060 13096136 5765924 70% /
tmpfs 100692 0 100692 0% /run/user/1000

$ free
total used free shared buff/cache available
Mem: 1006892 713376 72396 352 221120 154016
Swap: 1048572 768 1047804

明らかにおかしいと思ったら、2つのsubnetのうち1つがinternet gatewayにアタッチされていなかったのが原因。

AWS Elastic Load Balancing: Seeing extremely long initial connection time
https://stackoverflow.com/questions/35523421/aws-elastic-load-balancing-seeing-extremely-long-initial-connection-time

ALBだとAZで2つのsubnetの設定が必要だが、今回はインスタンスは1つしか使わないので、internet gatewayには1つしかアタッチしてなかった。もう一つのpublic subnetをigwにアタッチしたところ、0.63msに改善

$ curl ‘https://*.com/login’ -H ‘Accept-Encoding: gzip, deflate, sdch’ -H ‘Accept-Language: en-US,en;q=0.8,ja;q=0.6’ -H ‘Upgrade-Insecure-Requests: 1’ -H ‘User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36’ -H ‘Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8’ -H ‘Connection: keep-alive’ –compressed -o /dev/null -w “%{time_starttransfer}\n” -s
0.638270

メモリの問題じゃなかったああああああああああああああああああ 
なんてこったい

[Laravel x Python] exec($command, $output)のトラブルシューティング

execコマンドがvagrantのamazon linux2では問題なく動作するのに、ec2にデプロイして実行すると反応しない。
execコマンドだとダメそうなので、SymphonyのProcessでやってみたが、それでもダメ。
NLPは最も肝となる処理なので諦めるわけにはいかん、とトラブルシューティングを丸一日。Webの記事を探しまくるも一向に解決せず。

該当のcontroller

            $input = $request->input;
            $replace_input = str_replace(" ", "\ ", $input);
            $path = app_path() . "/python/main.py";
            // $command = "python3 " . $path . " ".$replace_input;
            // exec($command, $output);
            $process = new Process(["python3", $path, $replace_input]);
            $process->run();
            $output = $process->getOutput();
            dd($output);

結論から言うと、ProcessFailedExceptionでエラー原因を表示させたらわかった。sudu pip3でpandas, sklearn, MeCabなどをインストールすればよかった。

use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
// 省略

            $process = new Process(["python3", $path, $replace_input]);
            $process->run();
            if (!$process->isSuccessful()) {
                throw new ProcessFailedException($process);
            }

エラーが起きたら、まず、Exceptionで原因を調べるのが早いですね。
laravelでpythonの処理は変則的なので、かなり焦りました。
もっと上達してええええええええええええ

[AWS EC2] mecab-ipadic-neologd で std::bad_alloc

EC2にMecabを入れます。

ssh ec2-user@18.179.118.214 -i ~/.ssh/aws-dev.pem

$ sudo yum update -y
$ sudo yum groupinstall -y “Development Tools”

$ wget ‘https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7cENtOXlicTFaRUE’ -O mecab-0.996.tar.gz
$ tar xzf mecab-0.996.tar.gz
$ cd mecab-0.996
$ ./configure
$ make
$ make check
$ sudo make install
$ cd –
$ rm -rf mecab-0.996*

$ git clone –depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
$ ./mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n -a -y
terminate called after throwing an instance of ‘std::bad_alloc’
what(): std::bad_alloc
/home/ec2-user/mecab-ipadic-neologd/bin/../libexec/make-mecab-ipadic-neologd.sh: 525 行: 24919 Aborted ${MECAB_LIBEXEC_DIR}/mecab-dict-index -f UTF8 -t UTF8

8.0G中、7.6G使用しており、メモリが足りなくなっています。
$ df -hT
ファイルシス タイプ サイズ 使用 残り 使用% マウント位置
devtmpfs devtmpfs 482M 0 482M 0% /dev
tmpfs tmpfs 492M 0 492M 0% /dev/shm
tmpfs tmpfs 492M 460K 492M 1% /run
tmpfs tmpfs 492M 0 492M 0% /sys/fs/cgroup
/dev/xvda1 xfs 8.0G 7.6G 455M 95% /
tmpfs tmpfs 99M 0 99M 0% /run/user/1000

memoryが足りないのでebsのボリュームを8Gから15Gにあげます。

$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda 202:0 0 15G 0 disk
└─xvda1 202:1 0 8G 0 part /

$ sudo xfs_growfs /dev/xvda1
data blocks changed from 2096635 to 3931643

$ df -h
ファイルシス サイズ 使用 残り 使用% マウント位置
devtmpfs 482M 0 482M 0% /dev
tmpfs 492M 0 492M 0% /dev/shm
tmpfs 492M 460K 492M 1% /run
tmpfs 492M 0 492M 0% /sys/fs/cgroup
/dev/xvda1 15G 7.6G 7.5G 51% /
tmpfs 99M 0 99M 0% /run/user/1000

$ ./mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -n -a -y
[install-mecab-ipadic-NEologd] : Finish..

$ echo すもももももももものうち | mecab
すもももももももものうち 名詞,固有名詞,一般,*,*,*,すもももももももものうち,スモモモモモモモモノウチ,スモモモモモモモモノウチ
EOS

>>> import MeCab
>>> tagger = MeCab.Tagger(‘-Ochasen -d/usr/local/lib/mecab/dic/mecab-ipadic-neologd’)
>>> print(tagger.parse(‘すもももももももものうち’))
すもももももももものうち スモモモモモモモモノウチ すもももももももものうち 名詞-固有名詞-一般
EOS

MeCabが使えないと色々と前提が崩れるところやったわ

[AWS] Lambdaの基本機能

tutorialの”Introduction to AWS Lambda”で学んでいきます。

S3でbucketを作ります。
images-1357-4563
images-1357-4563-resized

HappyFace.jpeg

bucketのimages-1357-4563にuploadします。
Lambdaのcreate function

function name: Create-Thumbnail
runtime: Python 3.7 *tutorialはpython3.8で実行するとエラーになります。
Permissions: use an existing role
L lambda execution role

Add Trigger
S3
bucket: images-1357-4563
event type: All object create events

upload file
https://s3-us-west-2.amazonaws.com/us-west-2-aws-training/awsu-spl/spl-88/2.3.15.prod/scripts/CreateThumbnail.zip

Runtime setting: CreateThumbnail.handler

Test event: New event
template: s3-put

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-west-2",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "images-1357-4563",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::images-1357-4563"
        },
        "object": {
          "key": "HappyFace.jpeg",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}

Execution result: succeeded
S3で確認するとリサイズされていることがわかります。

monitor, cloudwatchで確認できます。

add triggerは基本的にAWSでのサービスなんですね。

一周回って動きの流れは理解したが、アプリケーション側のイベントとは概念が異なるようです。
インフラで何か起きた時に、サーバレスでtriggerから実行するってことか。
数年前にも同じことやったが過去と比べて理解度が違ってる。

[Laravel8.46.0] LaravelCollectiveのCheckboxでEditの時

{!! Form::checkbox(‘${column}’, null, false !!} だと、初期値はnullですが、editの場合は、{!! Form::checkbox(‘${column}’, null, null !!} にします。

### create
{!! Form::checkbox(‘${column}’, null, false !!}

                    <div class="custom-control custom-checkbox">
                      {!! Form::checkbox('new_release', null, false, ['class'=>'custom-control-input', 'id'=>'custom-check-1']) !!}
                      <label class="custom-control-label" for="custom-check-1">新商品</label>
                    </div>

### Edit
{!! Form::checkbox(‘${column}’, null, null !!}

                    <div class="custom-control custom-checkbox">
                      {!! Form::checkbox('new_release', null, null, ['class'=>'custom-control-input', 'id'=>'custom-check-1']) !!}
                      <label class="custom-control-label" for="custom-check-1">新商品</label>
                    </div>

チェックボックスってあまり使わないので、ドキュメントも少ない印象です。

[V3 Vue.js] dataとcomputedの書き方

為替計算をサーバーサイド側ではなく、フロント側で実装しようと、Vue.jsを書いていたら、Vue.jsっていつの間にか2系から3系にバージョンアップしてたのね。知らなかった。。orz…
※為替レートはAPIで取得してサーバーサイドから流し込みます。

– new Vueではなく、Vue.createApp({})として、app.mount(‘#app’)とする。

             <div class="card-body" width="" height="">
                  <label for="yen">日本円を入力(単位円)</span></label>
                  <input type="number" v-model.number="yen" class="form-control col-md-2">
                  <br>
                  <label for="dollar">ドル換算</span></label>
                  <input type="string" v-model="dollar" id="dollar" class="form-control col-md-2">
                  <a href="javascript:OnLinkClick();">コピー</a>
              </div>

// 省略

  <script src="https://unpkg.com/vue@next"></script>
  <script>
    const app = Vue.createApp({
      data() {
          return {yen: 10000}
      },
      computed:{
          dollar(){
           return Number(this.yen / 110.05).toLocaleString('en-US', {style:'currency', currency:'USD'});
         }
      },
    });
    app.mount('#app')
  </script>

焦るな、というかtype scriptちゃんと勉強しないとついていけなくなりそう

[python3.x] 空白を含めたコマンドライン引数を送る

英文をコマンドライン引数で送って実行させたいが、英文の場合半角スペースが含まれる為、引数が分かれてしまう。
例えば、「The official site of the Boston Celtics. Includes news, scores, schedules, statistics, photos and video」を引数で渡そうとして実行すると、第一引数は「The」のみになってしまう。

L 半角スペース(” “)の文頭に”\”があると、エスケープされて処理される。

            $input = $request->input;
            $replace_input = str_replace(" ", "\ ", $input);
            $path = app_path() . "/python/en_ja.py";
            $command = "python3 " . $path . " ".$replace_input;
            exec($command, $output);
            $output = $output[0];

上記のようにエスケープすると、英文でも引数として渡すことができる。

なるほど、中々面白いね。

[JavaScript]aタグでテキストエリアの内容をコピー

Output(翻訳結果)をボタン一つでコピーしたい。
L aタグでjsの関数を実行させる
  L getElementsByTagName(“textarea”)[0]でtextareaのtextを取得し、*.select でコピーします。

<div class="card-footer ">
                {!! Form::label('dest', '英語(翻訳結果)') !!}
                {!! Form::textarea('dest', $output, ['class' => 'form-control' ]) !!}
                <a href="javascript:OnLinkClick();">コピー</a>
              </div>

<script>
function OnLinkClick(){
    var text = document.getElementsByTagName("textarea")[1];
    text.select();
}
</script>

おおおおおおおおおおおおお
ええやんか

[SnowNLP] Python3で中国語の自然言語処理

$ pip3 install snownlp

tokenization

from snownlp import SnowNLP

s = SnowNLP(u'今天是周六。')
print(s.words)

$ python3 snow.py
[‘今天’, ‘是’, ‘周六’, ‘。’]

speech tagにするとnoun, adverb, verb, adjectiveなどを表現できます。

print(list(s.tags))

$ python3 snow.py
[(‘今天’, ‘t’), (‘是’, ‘v’), (‘周六’, ‘t’), (‘。’, ‘w’)]

pinyin

print(s.pinyin)

$ python3 snow.py
[‘jin’, ‘tian’, ‘shi’, ‘zhou’, ‘liu’, ‘。’]

sentences

s = SnowNLP(u'在茂密的大森林里,一只饥饿的老虎逮住了一只狐狸。老虎张开大嘴就要把狐狸吃掉。"慢着"!狐狸虽然很害怕但还是装出一副很神气的样子说,"你知道我是谁吗?我可是玉皇大帝派来管理百兽的兽王,你要是吃了我,玉皇大帝是决不会放过你的"。')
print(s.sentences)

[‘在茂密的大森林里’, ‘一只饥饿的老虎逮住了一只狐狸’, ‘老虎张开大嘴就要把狐狸吃掉’, ‘”慢着”‘, ‘狐狸虽然很害怕但还是装出一副很神气的样子说’, ‘”你知道我是谁吗’, ‘我可是玉皇大帝派来管理百兽的兽王’, ‘你要是吃了我’, ‘玉皇大帝是决不会放过你的”‘]

keyword

print(s.keywords(5))

$ python3 snow.py
[‘狐狸’, ‘大’, ‘老虎’, ‘大帝’, ‘皇’]

summary

print(s.summary(3))

[‘老虎张开大嘴就要把狐狸吃掉’, ‘我可是玉皇大帝派来管理百兽的兽王’, ‘玉皇大帝是决不会放过你的”‘]

sentiment analysis

text = SnowNLP(u'这个产品很好用,这个产品不好用,这个产品是垃圾,这个也太贵了吧,超级垃圾,是个垃圾中的垃圾')
sent = text.sentences
for sen in sent:
	s = SnowNLP(sen)
	print(s.sentiments)

$ python3 snow.py
0.7853504415636449
0.5098208142944668
0.13082804652201174
0.5
0.0954842128485538
0.04125325276132508

0から1の値を取り、1に近づくほどポジティブ、0に近いほどネガティブとなります。

[NLTK] customize sentiment analysis

unwanted = nltk.corpus.stopwords.words("english")
unwanted.extend([w.lower() for w in nltk.corpus.names.words()])

def skip_unwanted(pos_tuple):
	word, tag = pos_tuple
	if not word.isalpha() or word in unwanted:
		return False
	if tag.startswith("NN"):
		return False
	return True

positive_words = [word for word, tag in filter(
	skip_unwanted,
	nltk.pos_tag(nltk.corpus.movie_reviews.words(categories=["pos"]))
)]
negative_words = [word for word, tag in filter(
	skip_unwanted,
	nltk.pos_tag(nltk.corpus.movie_reviews.words(categories=["neg"]))
)]

positive_fd = nltk.FreqDist(positive_words)
negative_fd = nltk.FreqDist(negative_words)

common_set = set(positive_fd).intersection(negative_fd)

for word in common_set:
	del positive_fd[word]
	del negative_fd[word]

top_100_positive = {word for word, count in positive_fd.most_common(100)}
top_100_negative = {word for word, count in negative_fd.most_common(100)}

unwanted = nltk.corpus.stopwords.words("english")
unwanted.extend([w.lower() for w in nltk.corpus.names.words()])

positive_bigram_finder = nltk.collocations.BigramCollocationFinder.from_words([
	w for w in nltk.corpus.movie_reviews.words(categories=["pos"])
	if w.isalpha() and w not in unwanted
])

negative_bigram_finder = nltk.collocations.BigramCollocationFinder.from_words([
	w for w in nltk.corpus.movie_reviews.words(categories=["neg"])