diadia

興味があることをやってみる。自分のメモを残しておきます。

Promiseを理解するために役に立った資料

Promiseという概念を理解するに当たり以下の記事が大いに役に立った。こちらを読めば理解できる。

levelup.gitconnected.com

levelup.gitconnected.com

以下は自分がPromiseについて整理するために記録しておく。

そもそも自分はaxiosを使っていて、どうやらそれをthenを使うかasync awaitを使えばやり過ごせるって認識でいたのでasync awaitをノリで使いつづけ同期、非同期の話から解説する話がよく分かっていなかった。

そもそもなんでPromise説明を探しているのにコールバックが話題に出てくるのか?

A . 非同期処理はcallbackなしでは語れない概念で非同期処理のPromiseにはcallbackの説明がでてきてしまう。

const callbackFuncA = function () {
    console.log("this is callback a");
};

setTimeout(callbackFuncA, 3000);
console.log("setTimeoutは実行している");

// 結果
setTimeoutは実行している
this is callback a

setTimeoutでcallbackFuncAを呼ぶ文がまさしく非同期なのだ。同期処理ならば、setTimeoutの文を実行すると3秒間コードが止まった後に、callbackFuncAを呼び、実行する。callbackFuncAが終了したら次の行に行ってconsole.log("setTimeoutは実行している");が実行されるはずだ。しかしながら結果は異なる。最初にconsole.log("setTimeoutは実行している");の結果が表示されて、その後callbackFuncAの結果が表示されている。これはsetTimeoutをまず実行して、待ち時間の間に次の行を実行しているからこのような結果になる。

javascriptは処理に時間がかかる場合にはそこで処理を止めることはなく、次のコードを実行する仕様になっているらしい。時間がかかる処理が終わったあとに続く処理をつなげたいときにコールバックは使われる。

JavaScriptのエコシステムは伝統的にはスレッドを使わない計算モデルを使い、それの効率をあげる方向で進化してきました。 例えば、スリープのような、実行を行の途中で止めるような処理は基本的に持っていませんでした。 10秒間待つ、というタスクがあった場合には、10秒後に実行される関数を登録する、といった具合の処理が提供され、その場で「10秒止める」という処理を書く機能は提供されませんでした

非同期処理 — 仕事ですぐに使えるTypeScript ドキュメント

JavaScriptでは時間のかかる処理を実行する場合、完了した後に呼び出す処理を処理系に渡すことはあっても、そこで処理を止めることはありません。 タイマーを設定する setTimeout() 関数の実行自体は即座に完了し、その次の行がすぐ呼ばれます。 そして時間のかかるタイマーの待ちが完了すると、渡してあった関数が実行されます。 処理が終わるのをじっくり待つ(同期)のではなく、完了したら後から連絡してもらう(非同期)のがJavaScriptのスタイルです。 昔のJavaScriptのコードでは、時間のかかる処理を行う関数は、かならず引数の最後がコールバック関数でした。 このコールバック関数の中にコードを書くことで、時間のかかる処理が終わったあとに実行する、というのが表現できました。

非同期処理 — 仕事ですぐに使えるTypeScript ドキュメント

コールバック難を克服するためにPromise

以前はJavaScriptで数多くの非同期処理を実装しようとすると、多数のコールバック関数を扱う必要があり、以前はコールバック地獄と揶揄されていました。 その後、Promiseが登場し、ネストが1段になり、書きやすく、読みやすくなりました。 Promiseはその名の通り「重たい仕事が終わったら、あとで呼びに来るからね」という約束です。 これにより、上記のような、深いネストがされたコードを触れる必要が減ってきました。 何階層もの待ちが発生しても、1段階のネストで済むようになりました。 この Promise の実装は、文法の進化に頼ることなく、既存のJavaScriptの文法の上で実装されたトリックで実現できました。 コミュニティベースで実現されたソリューションです。 この Promise は現在も生き続けている方法です。直接書く機会は減ると思いますが、 Promise について学んだことは無駄にはなりません。

非同期処理 — 仕事ですぐに使えるTypeScript ドキュメント

関連記事

thenとasync awaitを比較してみた - diadia

以下おまけ(axiosを使わない方法を調べてみた)

同期処理のコード例

var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

var xhr = new XMLHttpRequest();
var method = "GET";
var url = new URL("https://api.coindesk.com/v1/bpi/currentprice.json");
var responseObj = null;
// 第3引数をfalseすると同期処理になる
xhr.open(method, url, false);

console.log('send前のコンソールログ')
xhr.send();
if (xhr.status != 200) {
    console.warn(xhr.status)
    responseObj = xhr.responseText
} else {
    console.warn(xhr.status)
    console.warn(xhr.responseText)
}
console.log('send後のコンソールログ')
// 上記スクリプト実行結果

send前のコンソールログ
200
{"time":{"updated":"May 12, 2021 02:59:00 UTC","updatedISO":"2021-05-12T02:59:00+00:00","updateduk":"May 12, 2021 at 03:59 BST"},"disclaimer":"This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org","chartName":"Bitcoin","bpi":{"USD":{"code":"USD","symbol":"$","rate":"57,175.3478","description":"United States Dollar","rate_float":57175.3478},"GBP":{"code":"GBP","symbol":"£","rate":"40,487.4075","description":"British Pound Sterling","rate_float":40487.4075},"EUR":{"code":"EUR","symbol":"€","rate":"47,114.4877","description":"Euro","rate_float":47114.4877}}}
send後のコンソールログ

非同期処理のコード

// https://ja.javascript.info/xmlhttprequest
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

var xhr = new XMLHttpRequest();
var method = "GET";
var url = new URL("https://api.coindesk.com/v1/bpi/currentprice.json");
var responseObj = null;
// 第3引数をtrueすると同期処理になる
xhr.open(method, url, true);

xhr.onload = function() {
  console.log("ON_LOADを実行")
};

xhr.onerror = function() { 
  console.log("ON_ERRORを実行")
};

console.log('send前のコンソールログ')
xhr.send();
console.log('send後のコンソールログ')
// 結果

send前のコンソールログ
send後のコンソールログ
ON_LOADを実行

thenとasync awaitを比較してみた

javascriptで非同期処理を行う場合にcallbackを使用してきた歴史がある。しかし非同期処理を使う際にcallbackを多用する場合がありコードが読みにくくなる問題が生じた。
こうした背景のもと問題解決のためPromiseという概念を発明した。

then, async awaitはこのPromiseオブジェクトに対して非同期処理の細かな制御を実現するために使うツールだと今のところ認識している。

どちらの方法でも非同期処理を制御できるけれども両者を比較したことがなかったが今回比較した。 自分の進歩を残すという趣旨で記録しているのでこの件もメモとしてのこす。

thenを使うケース

const axios = require('axios');

function createClient() {
    const client = axios.create({
            baseURL: 'https://api.coindesk.com',
            responseType: 'json'
    });
    console.log("clientを作成")
    return client
}

function getData() {
    const client = createClient();
    console.log("getData()内のrequest前のコンソールログ")
    // https://jp.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html
    let chartName = null;
    client.get(url = '/v1/bpi/currentprice.json').then((response) => {
        chartName = response.data["chartName"]
        console.log(chartName)
        console.log("getData()内にあるthen内のchartName取得後のコンソールログ")
    })
    console.log("getData()内のrequest後のコンソールログ", chartName)
}


// // コードを実行
console.log("getData()前のコンソールログ")
getData();
console.log("getData()後のコンソールログ")
// 結果
getData()前のコンソールログ
clientを作成
getData()内のrequest前のコンソールログ
getData()内のrequest後のコンソールログ null
getData()後のコンソールログ
Bitcoin
getData()内にあるthen内のchartName取得後のコンソールログ

thenを使うケースにおける非同期処理のポイント

非同期処理、例えば通信後のデータを使って何かロジックを組み立てる処理には、必ずthenのスコープ内でものを書いていく必要がある。

thenを使うケースにおける同期処理の優先順位

同期処理の優先順位つまり非同期処理を除いたロジックの流れは、以下の流れになると推測する。

非同期処理を含む関数(getData)では非同期処理以外(thenスコープ外)の命令が上から順番に実行される。これでgetDataが終わったとみなされ、その後に続く関数console.log("getData()後のコンソールログ")が実行される。getData内の非同期処理は同期処理とは無関係に、(マイペースに、)非同期処理の原因となる遅い処理が終わると、その後に続く処理console.log(chartName)console.log("getData()内にあるthen内のchartName取得後のコンソールログ")が実行されていく。

async, awaitのケース

const axios = require('axios');


function createClient() {
    const client = axios.create({
            baseURL: 'https://api.coindesk.com',
            responseType: 'json'
    });
    console.log("clientを作成")
    return client
}
 
async function getData() {
    const client = createClient();
    console.log("getData()内のrequest前のコンソールログ")
    // https://jp.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html
    const response = await client.get(url = '/v1/bpi/currentprice.json')
    const chartName = response.data["chartName"]
    console.log(chartName)
    console.log("getData()内のrequest後のコンソールログ")
};

// コードを実行
console.log("getData()前のコンソールログ")
getData();
console.log("getData()後のコンソールログ")
// 結果
getData()前のコンソールログ
clientを作成
getData()内のrequest前のコンソールログ
getData()後のコンソールログ
Bitcoin
getData()内のrequest後のコンソールログ

async, awitを使うケースにおける非同期処理のポイント

thenの場合ではthen内の処理がすべて非同期になるのに対し、async, awaitのケースの場合asyncがついた関数の処理すべてが非同期になるわけではない感じがする。async内のawait以降に書かれる処理がすべて非同期になり、await以前は同期処理扱いになるのではないかと推定する。

Inside the async function we can use await. JavaScript will know to execute the rest of the code only after the awaited function is resolved.

Promises in JavaScript: Explained for Kids | by ALEXANDRU TAPIRDEA | Apr, 2021 | Level Up Coding

nodejs expressでhttps通信を開発環境でできるようにするう

参考資料:

djangoの開発環境でhttps通信させるのが面倒そうだったのでnodejsでhttps通信できる環境を調べたところ案外かんたんにできそうなのでこちらを試してみた。 これにはcognitoを使う場合https通信をしないと認証できないことを知った背景がある。。。

手順

1.開発準備

// projectディレクトリを作成する
mkdir nodejs_https_sample
cd nodejs_https_sample

// package.json等作成
npm init

// expressをインストール
npm install express

2. http通信対応するスクリプトをとりあえず作る

// ファイル名:index.js

const express = require('express')
const app = express()
const port = 3000

app.get('/', function(req, res){ 
       res.send('Hello World!')
   }
)

app.listen(port, () => console.log(`Listening on port ${port}!`))

この状態だとhttp://localhost:3000/にアクセスすればHello Worldが表示されるが、 https://localhost:3000/にアクセスすると表示できない。これをできるようにするには以下の手順を実行する。

3. httpsに対応させる

1.証明書を作成する

// プロジェクトルート以下にcertディレクトリを作成する
mkdir cert
cd cert

certディレクトリ以下に privatekey.pemとcert.pemを作成する。

openssl req -x509 -newkey rsa:2048 -keyout privatekey.pem -out cert.pem -nodes -days 365

このコマンドについてはこちらを参照するとよい。 https://reffect.co.jp/node-js/node-js-https#i

2.index.jsを修正する

// index.js 
// 以下のように書き換える

const express = require('express')
const app = express()
const port = 3000
const fs = require('fs');
const option = {
    key: fs.readFileSync('./cert/privatekey.pem'),
    cert: fs.readFileSync('./cert/cert.pem'),    
}
const server = require('https').createServer(option, app)

app.get('/', (req, res) => res.send('Hello World!'))

server.listen(port, () => console.log(`Listening on port ${port}!`))

不動産業界の本を読んでまとめる

不動産業は取引業と賃貸業・管理業に分けられる。

不動産業に大きな影響を与える法律は、宅建業法、建築基準法、マンション管理適正化法、都市計画法

2018年のデータでは名目GDP538兆円の11.4%に当たる61兆円を占める。

全産業の売上高1535兆円のうち46兆円が不動産がシェアしている。これは全体の3%。

不動産業は従業員一人あたりの付加価値額が高い。他の産業より高い。 付加価値額が高い理由は

  1. 不動産という商品の単価が高い
  2. 売上に対する原価が低い
  3. 企画によって商品価格をあげることができる

売上に対する原価が低いの補足:売買の場合は不動産を仕入れるので原価がかかるが、高価な設備や機材などは必要なく、製造業や建築業、サービス業と比べると原価がかからないと言える。

地価公示とは、国土交通省が毎年1回標準地として定める不動産の価格を調査して公的に発表すること。

宅地建物取引業は免許制度となっているが、賃貸業・管理業は免許制度ではないので参入ハードルが高くない。

nodejs をすこし使えるようにするためのメモ

参考: Node.js入門 - とほほのWWW入門

今これを使うのは、cognitoで認証できた結果をdjangoにつなぐことができるか確認する事が必要。 記事通りやれば認証までは行けるからそこからdjangoにつなげさえすればかなりOKな感じになるんだと思う。

ハローワールドをやってみる

var http = require("http");
var server = http.createServer(function (req, res) {
    res.write("HELLO WORLD");
    res.end();
}).listen(8080);

このファイルをsample1.jsとして以下を実行する

node sample1

そしてlocalhost:8080/ にアクセスするとちゃんとハローワールドできている。

expressを利用するケース

expressはフレームワークのことらしい。

Express は、Web アプリケーションとモバイル・アプリケーション向けの一連の堅固な機能を提供する最小限で柔軟な Node.js Web アプリケーション・フレームワークです。

Express - Node.js Web アプリケーション・フレームワーク

手順

mkdir express_test
cd express_test

npm init
# package.jsonにexpress を登録
npm install express

以下のファイルをindex.jsと命名し作成する

var express = require('express');
var app = express();
app.listen(8080);

app.get('/test1', function(req, res) {
  res.send('TEST1\n');
});

app.post('/test2', function(req, res) {
  res.send('TEST2\n');
});

以下のコマンドでサーバーを立てる

node index

開発時expressをホットリロードにする

nodemonを使えばホットリロードができる。

nodemon - npm

// nodemonをインストール
npm install -g nodemon

// ホットリロードで起動
nodemon index
// npm startでnodemonを起動させる
// package.jsonのscriptsのところで以下のように書いておく
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon index"
  },

$ npm start

nodejsのでバッグ

デバッグの種類が多くてなにを使えばよいのか分からなかった。

とりあえずtyprにはnodeじゃなくてpwa-nodeとかpwa-chromeがあることが分かった。 pwa-chromeの方は最初にnodeを起動しておいてからじゃないとデバッグを起動しても動かないことだけは確認した。この辺なかなか難しい感じがする。。。pwa-chromeだとcognitoのテストが使えないし。

デバッグについて

デバッグの方法論

デバッグ方法のやりかたをまとめておくというより、統合開発環境デバッグモードを使ってフェーズごとに効率よく作業する方法を整理したい。

フェーズ

  • 開発時
  • テスト
  • プロダクトがエラーを吐き出したとき
  • なんかよく分からんけどアプリが落ちるとき

開発時

コードを実行する  
 ↓                
エラーが出る  
 ↓                
出たところを見る  
 ↓                
エラー原因を特定する 
 ↓                
原因を排除する   -> 再び'コードを実行する'へ

このフローだとエラーが出た際に正確にエラー原因を理解してエラーに対応したコードを書かなければ何度もコードを実行して時間がかかってしまう。 この場合には、コードを実行する際にデバッグモードで実行する。エラーが出る手前でbreakpointを設置し、エラーが出るスコープ内で、修正したコードをコンソールで実行する。こうすることで修正したコードは確実にエラーが出ないので修正したのにも関わらずエラーが出て時間をロスしてしまうことを回避する事ができる。

テストフェーズ

テストフェーズでは自動テストコードを走らせることが良いと思うが、それがない場合にはデバッグモードを実行しコードの品質を確認する。

  • 意図した条件分岐を通るか
  • 条件分岐先にある関数が適切に動くか

意図した条件分岐を通るか

ここをチェックしたい場合、以前まではデータベースのデータを調整してそのデータを流してその条件分岐を通るかどうか試していた。この場合データの構造がシンプルな場合にはDBのデータをいじれば済むが、それがコストがかかる場合がある。このケースでは、適切なデータを呼び出し、そのデータに条件分岐が通るべき修正を加え、コードを走らせる。そしてブレクポイントを適当に複数セットし、条件分岐が機能しているか確認すると良いだろう。

条件分岐先にある関数が適切に動くか

これに関しても単体テストを実行すればそれで済む話なのだけれども、職場環境によってはない場合がある。この場合には通らせたい条件分岐はTrueに変更、その他はFalseにすれば関数を実行することができる。関数実行時に適切なデータがほしければ、上で書いたようにコード中で修正すればその問題もないだろう。

プロダクトがエラーを吐き出したとき

web系に関しての対処法ではあるが、chromeのdevelopperツールのコンソールログを確認する。こちらでエラーが出ていなければサーバー側で問題があると考えられるのでツールのネットワークを開く。APIの結果で500が出ているものを見つけ、当該APIに対応するロジックの部分をデバッグモードで実行する。エラーメッセージもよく確認し、コンソールで通るコードを作成、修正でよい。

なんかよく分からんけどプロダクトのアプリが落ちるとき

具体的な対策が今は見つけられていない。IDEをつかうというよりはログを詳細にとりおかしいところを見つけることで対応している。

VSCODE pipenvの環境からデバッグを実行したいときには

.vscode/settings.jsonにpythonPathの項目を追加する。

{
    "python.autoComplete.extraPaths": [
        "/Users/chiaki/.local/share/virtualenvs/cognito_sample-4m7Vb3k3/lib/python3.9/site-packages/",
    ],
    "python.pythonPath": "/Users/chiaki/.local/share/virtualenvs/cognito_sample-4m7Vb3k3/bin/python"

}

pythonPathの値は、仮想環境に入ったあとのpythonパスを参照すること。

手順;

# `仮想環境に入る`
pipenv shell

# which python

settings.jsonにpythonPathを追加すると仮想環境に入っていなくてもデバッグを起動すると仮想環境でデバッグが実行される。

javascriptファイルを実行する方法

javascriptで書かれたファイルを実行する方法を今更ながら知ったので記録しておく。

ブラウザで開いて実行する方法

今まで.jsファイルは該当の ファイルを選択してブラウザで開いてconsole.logとかを確認していた。

nodejsを使って実行する方法

それとは別にnode hoge.jsとすれば、javascriptのコードを走らせ、ターミナルにconsole.logの結果を表示させることができる。

この場合のnode は

python hoge.py

pythonと同じ役割なのだろうと思う。

visual studio codeのドキュメントの中ではnode.jsをruntimeだと 定義している。 ランタイムとはある言語で書かれたファイルを実行するものに必要なものとのことだった。 ここにきてruntimeという概念を認知した。

Node.js is a platform for building fast and scalable server applications using JavaScript. Node.js is the runtime and npm is the Package Manager for Node.js modules.

Build Node.js Apps with Visual Studio Code

runtimeについての解説資料

https://wa3.i-3-i.info/word13464.html

mac ショートカット

目的

横になったり、リラックスした姿勢でPCを操作するためにはマウスを使わないほうが都合が良い。マウスをできるだけ排除した操作ができるような方法を調べメモしていく。

ショートカットキーカテゴリ

  1. macosによるOS系のもの
  2. ブラウザ(chrome)
  3. visual studio code

ウィンドウの切替ショートカット

Mac - ウィンドウの切替ショートカット - 覚えたら書く

command + tab

Chrome タブを切り替える

【Mac版Chrome】タブを切り替えるショートカットキー【隣のタブへ移動】 | Tipstour

Option + Command + 左右キー

CHROMEタブを閉じる

command + W

ショートカット|タブを閉じる・開く・タブの切り替え(Windows/Mac) | BIGLOBEハンジョー

CHROME閉じたタブを再度開く

command + Shift + T

CHROMEの戻る、進む

# 戻る
command + [

# 進む
command + ]

CHROME クリック可能な項目間の移動

順方向 tab 
逆方向 tab  + shift

visual studio code ターミナルを開く

ctrl + `

visual studio code breakpointの切り替え

F9

https://code.visualstudio.com/docs/nodejs/nodejs-tutorial#_debugging-hello-world

visual studio code デバッグで実行

command + shift + d 

https://code.visualstudio.com/docs/editor/debugging#_run-view

visual studio code デバック中次のbreakpointまで実行

F5

DjangoをDockerのドキュメントを見て環境構築

Docker自体はqiitaの記事を見て環境構築したことがあるが、ドキュメントが存在し、それ通りにやったら案外かんたんにできてしまったのでメモ。 引っかかったところはsettings.pyのデータベースのセッティングだったのでそこを中心にメモしておく。

ドキュメント;

クィックスタート: Compose と Django — Docker-docs-ja 19.03 ドキュメント

これ通りやると、動かない。データベースの接続にはpassword等の値とsettings.pyの設定値を対応させるためにdocker-compose.ymlにenviriionmentを定める。

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgres',
        'USER': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
        'PASSWORD': 'postgres',
    }
}

DATABASEの名前はpostgres, userはpostgres, passwordはpostgres。これを起動するコンテナに設定しないとdbに接続できなくてエラーが出てしまう。

// dokcer-compose.yml
version: "3"

services:
  db:
    image: postgres
    ports:
      - "5432"
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres

  web:
    build: .
    command: python3 manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

drf エラーハンドリング

リソースに変更を加えるapiを作る際に、以下の要望があった。 apiを叩いたら成功したか失敗したかの結果をTrue, Falseで欲しい。この場合drfでResponseオブジェクトを返せば良い。

return Response({"success":True})
# または
return Response({"success":False})

こんな感じで返せば良い。こうするとフロントエンドでsuccessの結果から処理を分岐させることができるようになり、統一するとフロントエンド側が書きやすくなる。
これを実装するにはエラーハンドリングについて少し考える必要があり、それに関する良い記事がある。

https://medium.com/@ceoroman9/django-rest-framework-apiview-class-advance-usage-permissions-response-and-exception-handling-d4321a08a83f

関連するソースコードはこちら:

django-rest-framework/exceptions.py at master · encode/django-rest-framework · GitHub

具体例な解決案

クライアントの操作に誤りがある場合には、djangoが自動的にエラーをすくいとり、エラーがあった旨のレスポンスを自動的に返す。それを使っている以上responseにはsuccessキーは使えない。

エラーをキャッチしてなおかつsuccessキーを返すには以下のようにすると良い。 一言添えるならば、drf上ではエラーが発生したらexception_handlerが呼び出され、handle_exceptionが作動する。こいつがdrfのエラーをどう処理するか定めているものなので、そこを自分好みにカスタマイしてみてはどうか?ってことだ。

from rest_framework.views import APIView
from rest_framework.exceptions import APIException, NotFound

class BookTagView(APIView):

    def get_exception_handler(self):
        default_handler = super().get_exception_handler()

        def handle_exception(exc, context):
            if isinstance(exc, KeyError):
                return Response({'success': False, 
                                 'error': {
                                     'message': exc.detail,
                                     }
                                 }, status_code=exc.status_code)
            elif isinstance(exc, NotFound):
                return Response({'success': False, 
                                 'error': {
                                     'message': exc.detail,
                                     }
                                 }, status_code=exc.status_code)
            else:
                # unknown exception
                return default_handler(exc, context)
    return handle_exception


    def post(self, request):
        data = request.data
        
        try:
            book_title = data["book_title"]
            tag = data['tag']
        except KeyError:
            raise KeyError()

        try:
            book = Book.objects.get(title=book_title)
        except NotFound:
            raise NotFound()

        book.tags.add(tag)
        book.save()
        return Response({"success": True})


class KeyError(APIException):
    status_code = status.HTTP_400_BAD_REQUEST
    default_detail = _('送信データのkeyにはbook_title, tagがないのでエラーとなります')
    default_code = 'key_error'

Amazon ES

Fine-Grained Access Control in Amazon Elasticsearch Service - Amazon Elasticsearch Service

Amazon ES Security

ESのセキュリティは3つから構成されている。

Network -> Public accessVPC accessで設定できる

Domain access policy -> resource-based access policy

Fine-grained access control -> fine-grained access control evaluates the user credentials and either authenticates the user or denies the request. If fine-grained access control authenticates the user, it fetches all roles mapped to that user and uses the complete set of permissions to determine how to handle the request.

Remote Reindex in Amazon Elasticsearch Service - Amazon Elasticsearch Service

指定のURLからのみのアクセスを行う

パブリックアクセスで以下をJSON内で指定すると指定のURLのみからアクセスできる設定になる

IPベースのポリシー

https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/es-ac.html#es-ac-types-ip

"Condition": {
        "IpAddress": {
          "aws:SourceIp": "[アクセス元IPアドレス]/32"
        }
      }

ただ、これをIPを調べて登録してもうまく行かなかった。

https://checkip.amazonaws.com/のipを使うとうまく行った。理由はわからない。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/anonymous-not-authorized-elasticsearch/

elasticsearch alias

インデックスにはaliasをつけられるらしい。

aliasの確認

GET sample1/_alias

GET <index_name>/_alias

aliasがない場合には空が表示される

// 結果

{
  "sample1" : {
    "aliases" : { }
  }
}

aliasの追加、更新

Create or update index alias API | Elasticsearch Reference [7.12] | Elastic

PUT sample1/_alias/my_alias

PUT <index_name>/_alias/<alias_name>

実行結果

{
  "sample1" : {
    "aliases" : {
      "my_alias" : { }
    }
  }
}

aliasの複数インデックスへの追加の注意点

ある名前のaliasは一つのインデックスのみに使えるわけではなくて、複数のインデックスに設定できる。 つまりalias Xをもつindex Aとindex Bが存在することを許容する。

しかしながら書き込み時は注意すること。書き込めるインデックスは複数設定できないっぽいので複数のインデックスにalias経由でデータを格納しようとするとエラーが出てしまう。

Is it possible to write to multiple indexes with an ElasticSearch alias? - Stack Overflow

Update index alias API | Elasticsearch Reference [7.10] | Elastic

searchをした場合当aliasをもつ複数のインデックスから結果を表示することはできる。

同一エイリアスを設定し、そのエイリアスを使ってインデックスに書き込みを実現したい場合は以下のように "is_write_index"を設定する必要があるようだ。当然true に設定するのは一つのインデックスのみ。

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "sample1",
        "alias": "my_alias",
        "is_write_index": false
      }
    },
    {
      "add": {
        "index": "sample2",
        "alias": "my_alias",
        "is_write_index": true
      }
    }
  ]
}