OmniAICreator/Qiita-1.07M huggingface dataset:
ポ〇モン的対戦RPGゲームの作り方-3.簡単なAIの設計

暇つぶしがてらポ〇モン的対戦RPGゲームを開発したので、その中身の作り方の概要を記事にしたいと思います。Qiitaに投稿するの初めてで書くの慣れてないので読みにくかったらすいません。また、Unity触って1年の初心者が独学で作っただけなのであくまで方法の一つ程度として考えてください。

全部で何記事になるか分かりませんが、
1.ゲーム進行の管理
2.外部データのインポートとEditor拡張
3.簡単なAIの設計
くらいまで書けたらと思っています。

1.↓

https://qiita.com/evebursterrorR/items/033907d163db3dad135a

2.↓

https://qiita.com/evebursterrorR/items/86315925a85d48fe6adc


___
# ・評価値
敵AIが手を選ぶ際には基準となる評価値が必要です。今回はがりゅうさんの記事を参考にさせて頂きました。

https://shingaryu.hatenablog.com/entry/2016/02/03/200000

私が作った評価値の計算式は、

評価値=
Σ(AIのポケモンのHP割合)×(有効ランク補正)×(状態異常補正)×(その他の補正)
÷
Σ(プレイヤーのポケモンのHP割合)×(有効ランク補正)×(状態異常補正)×(その他の補正)

としました。

ランク補正については、例えば物理アタッカーで攻撃ランクが下がってたら有効だし、鈍足アタッカーのSが下がってるのは有効じゃないなどを考慮して計算しました。

状態異常補正についても同様で耐久ポケモンが猛毒になっていたら評価を下げ、高速アタッカーがマヒになっていたら評価を下げるなどです。

その他の補正の部分に関しては、例えば変化技を拘りトリックやアンコールなどでロックされているポケモンは評価値を下げたり、身代わりを残せていれば評価値を上げたりなどです。

これを残ポケモン毎に和を取ったものを自分と相手で比を取った値を評価基準として、この値がなるべく高くなるような状況を目指させました。
___
# ・実装の方法とそれらの課題
1.
最初の実装では、プレイヤー側は常に最高打点を出してくるものだと仮定して、それに対応してAIに4ターン後までの全手(4^4=256通り)を試させて、そのルートの中で目の前の相手に勝つ(またはHP割合が相手より多くなる)ようなルートを検索させてみました。

しかし、明らかにほぼ勝てない状況なのにAIがめちゃくちゃ強気に居座る選択をすることが多くありました。

どうやら、256回も試行している中で、例えばAI側の全部の技が急所に当たって楽に突破できたり、プレイヤー側が混乱でずっと動かなくて勝ったりと確率は低いが通れば強いといういわゆる「運ゲー」に勝利できたルートが見つかったらしく、AIはそのルートを辿ろうとしていたようです。(しかし勿論現実は1回きりの試行なのでそんなに上手くはいかない)
___

2.
次の実装では勝てるルートを見つけた場合に、そのルートが本当に安全なルートなのか(1回きりの試行である現実でそのルートを取っても大丈夫なのか)を検証するため、シミュレーション上で1度勝てたルートを後でもう何回か追加検証としてシミュレーションすることにしました。

例えば発生確率が1%未満のような低確率で勝てる場合は、追加検証を50回した時にせいぜい1,2回くらいしか勝てないと予想されます。そのような低い確率でしか勝てないルートを間引こうという考えです。

この追加検証で一定以上の確率で勝てるという結果が得られた場合、それらのルートの中で最も評価値の高い(最も良さそうな)選択肢を選ばせるようにしました。

しかし、今度は1回目のルート探索で勝てるルートが多く見つかった際に追加検証の計算回数が多すぎてAIの思考時間がとても長くなってしまい、画面がフリーズという問題が発生しました。
___

3.
そこで、重い計算処理を非同期にして画面をフリーズさせない方法を調べていて、カヤックさんの記事を参考にしました。

https://techblog.kayac.com/unity_advent_calendar_2016_8

これによって何度も処理される計算を一定時間ごとに割り振ることで画面のフリーズの確率は下がりました。しかし、計算の回数自体が減っている訳ではなく相変わらず特定の状況では(特に耐久型モンスターで頻発)AIの思考時間が非常に長くなってしまいました。

そこで計算量を減らすために、ルート探索を全手256パターン探させるのではなく、ある程度の所で打ち切らせることを考えました。

---

4.
ルート探索途中で、ある技を使って負けた場合、それ以降の探索ではその技を使うのは無駄と判断してその技を使うルートは探索しない事にしました。これによって無駄な計算(地面タイプに電気技を使うルートなど)を省いてより計算時間を短縮することに成功しました。

これによってある程度の精度でそこそこ短い計算時間のAIが出来ました。

しかし、何度か対戦してみると明らかにAI側が勝つ確率の方が高そうな状況なのに勝負を仕掛けてこない場合がありました。例えば、AI側が先制で眠り粉(命中75%)→蝶の舞を積めばSを逆転して全抜きが狙えそうなのに眠り粉を選択してこないなどです。

5.
どうやら、最初のルート探索中に眠り粉を何度も使う内に外してしまう場合が1回はあったようで、その外しによって結果的に打ち負けてしまったのでそれ以降は眠り粉を使うルートを探索することを諦めてしまったようでした。

計算量を減らすために探索するルートを削った結果として勝てるはずだったルートを見つけられなかったようです。

そのため、初回の256パターンのルート探索ではAI側の技の命中率を100%である、と上振れを仮定して考えさせて、最検証の際に命中を考慮して計算させることで本当に安定して勝てるのかを検証させることにしました。

これによって連続混乱や急所といったような確率のかなり低いルートを省きつつ、命中率がある程度ある場合は強気に勝負を挑んでくるというAIが出来ました。

しかし、これはあくまで相手が常に攻撃技を選び続けるという仮定の下であり、AI側が相手の手を読んで手を変えるという事は実現できていないため、これからの課題点であるといえます。

---
以上がAI設計初めての人間の試行錯誤の結果でした。何かの参考にしてもらえれば。
Docker DesktopでWindows環境(+Python)を利用する

## はじめに

部署内で使っていたアプリがPythonスクリプトだった(=Python環境がないと動かない)ので、Pyinstallerでexe化することにした。
が、Anaconda環境だったためエラー出まくり、できたファイルは500MBオーバーということに。  

[【悲報】PyInstallerさん、300MBのexeファイルを吐き出すようになる](https://lets-hack.tech/programming/languages/python/pyinstaller-issue/)

↑で紹介されている事象を一通り経験したので、クリーンなWindows環境を求めて、Docker DesktopでWindows環境を立てることにした。  

## 前提

- ホストOS：Windows10
- Docker Version：v20.10.6

## Docker Desktopをインストールする

[Docker Desktop](https://www.docker.com/products/docker-desktop)からインストールすればOK。  
  
参考：
[【入門】はじめての Docker Desktop for Windows のインストールと CentOS の仮想環境構築のセットアップ](https://qiita.com/gahoh/items/7b21377b5c9e3ffddf4a)

## Windows Containerへの切り替え
インストールしたままでDockerイメージを作成しようとすると、Linux環境が構築されることになるので、モードの切り替えが必要。  
タスクトレイのDocker Desktopアイコンを右クリックし、Windowsモードに切り替える。
![2021-06-28 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/b0eab336-522c-05b2-48c1-96c83d7bea4e.png)


## Dockerイメージを取得する

### Pythonイメージを取得
Pythonのイメージを入れて起動するだけで、空のWinsows環境にPyhtonが入った状態で起動できる。  
コマンドプロンプトで以下のコマンドを入力すればOK。

```
> docker pull python
```

## Windowsコンテナを起動
### Pullしたイメージを選択
Docker Desktopの「Images」を開き、取得したPythonイメージを選択し、「Run」を選択する。
![2021-07-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/be7c0ab2-a373-36d7-695c-56bfb454103d.png)
 

### コンテナ名、ボリュームを設定
設定ダイアログで、コンテナ名とボリュームの結び付けを行う。(Portの設定は不要)  
Host PathにホストOSの利用したいフォルダのパスを設定すると、Container Pathに設定したフォルダを開いたときにHost Pathの内容が見られるようになる。  
コンテナ上で既存ファイルを操作したい場合は必ず設定しておく。
![2021-06-28 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/4e9be051-239f-70cc-d0e9-69f650508f31.png)


### コンテナを起動
設定ダイアログでRunを押すと、Pythonが入った状態のWindowsコンテナが起動する。
![スクリーンショット 2021-07-07 171637.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1259842/221789be-e4e2-c988-3ccf-32fea600b8da.jpeg)

### pipをインストールしておく  
そのままだとPythonの最小限のパッケージしか入っていない状態。パッケージが必要になる都度pipでインストールしていくことになるので、pipをインストールしておく。  
  
参考：
[pipの使い方とインストール](https://www.python-izm.com/tips/pip/)  

## Windowsイメージの取得・起動も可能
以下のイメージを取得すると、Windowsイメージの起動ができる。  
何も入っていない(=諸々インストールが大変)のにサイズが14GBもあるが…  

```
> docker pull mcr.microsoft.com/windows:2004
```

### Tagの指定

Dockerイメージを取得する際にTagの指定をしない(「:2004」をつけない)と、latestを取りに行って取得できないので注意。

```
> docker pull mcr.microsoft.com/windows
Using default tag: latest
Error response from daemon: manifest for mcr.microsoft.com/windows:latest not found: manifest unknown: manifest tagged by "latest" is not found
```

参考：
[Windows コンテナを動かしてみる](https://blog.techlab-xe.net/windows-container-and-directx/)
#3　webアプリケーション（しくみ編）

#webアプリケーションを表示するしくみ
大前提として、インターネットがなければ、webアプリケーションは表示できません。

つまり、インターネットの送受信が大切にアプリケーションを動かす大事な鍵になってくるわけです。
その送受信の方法として**HTTP通信**というものがあります。
###HTTP通信とは
ユーザーののもつPCとwebアプリケーションの架け橋となる通信方法です。
ユーザーが「このページ見たい！」と**リクエスト**。
webアプリケーションが「はいどうぞ！」と**レスポンス**。
ここでHTTP通信が使われています。
####リクエスト
データのやり取りの中でも、「情報の要求」のことを指します。💻→
####レスポンス
データのやり取りの中でも、「情報の返却」のことを指します。💻←


####◆脱線しましたが…
リクエストとレスポンスのHTTP通信でのやり取りのときなのですが！
このときwebアプリケーションは**URL**と呼ばれる**インターネット上の住所**を持っているので、行きたいURLにアクセスすることで通信ができるという仕組みです。

ちなみに、URLには**IPアドレス**と**ドメイン**というものがあります。
####IPアドレス
webアプリケーションの住所を表すもの（**数字**）
例）
142.250.206.227
数字だけではありますがちゃんとしたサイトの情報をもち、URLです。

####ドメイン
webアプリケーションの住所を表すもの（**英数字**）
例）
www.google.co.jp
みなさんおなじみグーグルのドメインです。

ちなみに上のIPアドレスもGoogleのものなので　**142.250.206.227 == www.google.co.jp**　ということができます。

###まとめ
リクエストの際はURLで通信の行き先を決めています！
既存ユーザーへのUUIDの割り当てで恥をかいた話

## やりたかったこと

`users` テーブルの各々のレコードに、人工キーとは別にある程度の乱雑さでIDを割り当てたいという話になった。
そこで、`unique_id` というカラムを追加して既存のレコード全てに対してUUIDを割り当てることにした。

## 行った実装

以下のような使い捨ての rake タスクを実装した。

```ruby:lib/tasks/onetime/set_unique_id_to_persisted_users.rake
# NOTE:
# これは、既存のUserモデルで実装されているコールバックなどを回避するための一時モデルとして使用
# 
# TODO:
# ここに定義していると全スコープでロードされるのでタスク実行後はこのファイルごと消す
class DummyUser < ActiveRecord::Base
  self.table_name = 'users'

  def generate_unique_id
    self.unique_id = SecureRandom.uuid
  end
end

namespace :onetime do
  desc "既存ユーザーにUUIDを割り振る"
  task :set_unique_id_to_persisted_users :environment do
    DummyUser.where(unique_id: nil).find_in_batch(batch_size: 1_000) do |users|
      users.each do |user|
        user.generate_uid
        user.save!
        sleep 1 if (user.id % 100).zero?
      end
    end
  end
end

```

※　実際は進捗を表示するような処理もあったがここでは割愛した

## 結果

やりたいこと自体は実現できたが、環境（本番やステージング）によっては処理対象のレコード数が結構な量だったため、このタスクが実行完了するまで数十分かかってしまった。
それで開発グループのSlackチャンネルで「実行に時間かかりすぎる（遅い）」「別のやり方の方が良かったのでは？」などの声があがった。

## 改善案

前章のSlackチャンネルにて、優秀なエンジニアの方が改善案を出してくれた。
曰く、「 `UPDATE users SET unique_id = UUID() WHERE unique_id is null` を実行すれば一瞬で完了する」

ぐうの音も出なかった。
今後、UUID関連の対応をする機会があればSQLのUUID関数を活用したい。
uniq -c のパディングやめて、区切り文字を変更する

**（備忘録な記事）**
Linuxコマンドで、テキストファイルの各行を一意（ユニーク）にして、被りが何行だったかカウントを表示する際のお話

```bash
cat a.tsv b.tsv | sort | uniq -c > result.txt
```

上記で処理すると、各行の先頭に、被りの行数が左パディング（半角スペースで桁揃え）されて、右に半角スペース1つで区切って、実際の各行の内容が出力されます。

```text
   1 a-text    01
 109 a-text    02
  12 a-text    03
   1 a-text    04
   1 b-text    01
1234 b-text    02
  45 b-text    03
```
（表示の都合上、タブを4スペースで表現してます）

これをパディングなしのタブ区切り（TSV）にします。
（エクセルに読み込むとか、元のファイルがtsvとか）

```bash
cat a.tsv b.tsv | sort | uniq -c | awk -F" " '{print $1"\t"$2"\t"$3}' > result.tsv
```
awkの使い方がダサいですが、手っ取り早く、これで。
AWS T系RDSのCredit Specificationはt2かt3かで決まる


# 経緯

- どこかでCredit Specificationを確認できたはずと RDSの項目を見てもみつからない
- 調べてみると、RDSの場合は、t2かt3によって決まるとのこと
- EC2については、マネジメントコンソールで確認でき、RDSでも同一と勘違いしていたのでメモ

# 内容

- Amazon RDS db.t2.*は、Standard Modeに相当
- Amazon RDS db.t3.*は、Unlimited Modeに相当


## RDSのマネジメントコンソール画面

![rds.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083912/d39464b4-6742-238b-24d9-3e9b21a8eb0a.jpeg)

## EC2のマネジメントコンソール画面

![ec2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083912/6b0e8428-296c-77d4-3ab5-9a53a01a53fb.jpeg)

## 引用

> Amazon EC2, Amazon ECS
> 
> クレジットの消費タイプは、Standard Modeと、Unlimited Modeが選択できます。Amazon EC2の場合、T2のデフォルトはStandard Mode、T3のデフォルトはUnlimited Modeです。

> Amazon RDS, Amazon Aurora
>
> CPUクレジットの計算方法は、基本はAmazon EC2と同じです。
> db.t2.*だとStandard Modeに相当する制限があり、
> 
> CPUクレジットが0になるとベースライン以下のCPU使用率に制限されます。
> db.t3.*ですと、Unlimited Modeに相当する動きとなり、
> CPUクレジットが0になると追加の課金が発生します。

# 参考

https://aws.amazon.com/jp/blogs/startup/burstable-performance-instances/
InfluxDB v2: Python Client の使い方

InfluxDB 2.0 の Python Client の使い方です。

Ubuntu での準備

```bash
sudo apt install python-is-python3
sudo apt install python3-pip
```

organization: ekzemplaro
bucket: tochigi
としたサンプルです。

ライブラリーのインストール

```bash
pip3 install influxdb-client
```

トークンを調べる

```bash
influx auth list
```

データの書き込み

```py:write_data.py
#! /usr/bin/python
#
#	write_data.py
#
#					Jul/07/2021
# ------------------------------------------------------------------
import influxdb_client
from influxdb_client.client.write_api import SYNCHRONOUS
# ------------------------------------------------------------------

bucket = "tochigi"
org = "ekzemplaro"
token = '1zIYs1taCm3pWQOBUNk0ssYccOF7YgJcdGyJDbiFYTBf7hcPvyl5nGyt71rNVkYtg2rBceUwQ1r5Xzm7Mabcde=='
#
url="http://localhost:8086"


client = influxdb_client.InfluxDBClient(
   url=url,
   token=token,
   org=org
)

write_api = client.write_api(write_options=SYNCHRONOUS)

pp = influxdb_client.Point("t0925") \
	.field("name", "日光") \
	.field("population", 23491) \
	.field("date_mod", "2004-6-14")
write_api.write(bucket=bucket, org=org, record=pp)
#
# ------------------------------------------------------------------
```

データの読み込み

```py:query_data.py
#! /usr/bin/python
#
#	query_data.py
#
#						Jul/07/2021
# ------------------------------------------------------------------
import influxdb_client
from influxdb_client.client.write_api import SYNCHRONOUS

# ------------------------------------------------------------------
bucket = "tochigi"
org = "ekzemplaro"
token = '1zIYs1taCm3pWQOBUNk0ssYccOF7YgJcdGyJDbiFYTBf7hcPvyl5nGyt71rNVkYtg2rBceUwQ1r5Xzm7Mabcde=='
#
url="http://localhost:8086"

client = influxdb_client.InfluxDBClient(
   url=url,
   token=token,
   org=org
)

query_api = client.query_api()
query = ' from(bucket:"tochigi")\
|> range(start: -12h)'
result = client.query_api().query(org=org, query=query)
results = []
for table in result:
    for record in table.records:
        results.append((record.get_field(), record.get_value()))

print(results)
# ------------------------------------------------------------------
```

実行結果

```text
$ ./query_data.py 
[('date_mod', '2004-6-14'), ('name', '日光'), ('population', 23491)]
```

次のバージョンで確認しました。

```text
$ influx version
Influx CLI 2.5.0 (git: 3285a03) build_date: 2022-11-01T16:32:06Z

$ python --version
Python 3.10.7
```

関連情報
[InfluxDB の cli の使い方 (2.0)](https://qiita.com/ekzemplaro/items/f3c8a58e337e8d974b24)
cpprestsdkとビルドについて


# cpprestsdk
cpprestsdkはマイクロソフトがオープンソースで公開しているC++のRestAPI用のSDKプロジェクトです。

**※現在では開発は終了？しているようで、新規プロジェクトでの利用は非推奨となっているみたいです。**

## ビルドエラー

ビルド時にnot foundエラーになってしまいました、```set(cpprestsdk_DIR /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/cmake/)```を追記することで解消しました。

```
cmake_minimum_required(VERSION 3.13)
add_library(main STATIC main.cc)

set(cpprestsdk_DIR /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/cmake/)
find_package(cpprestsdk REQUIRED)

target_link_libraries(main PRIVATE cpprestsdk::cpprest)
```

## 参考
https://github.com/microsoft/cpprestsdk/issues/686
InfluxDB v2: API の使い方

InfluxDB 2.0 の API の使い方です。

organization: ekzemplaro
bucket: tochigi
としたサンプルです。

トークンは次のコマンドで得られます。

```bash
influx auth list
```

## データの書き込み

参考ページ
[Write data with the InfluxDB API](https://docs.influxdata.com/influxdb/v2.6/write-data/developer-tools/api/)

```bash:write_data.sh
#
URL="http://localhost:8086/api/v2/write?org=ekzemplaro&bucket=tochigi&precision=s"
TOKEN='1zIYs1taCm3pWQOBUNk0ssYccOF7YgJcdGyJDbiFYTBf7hcPvyl5nGyt71rNVkYtg2rBceUwQ1r5Xzm7Mabcde=='
#
#
curl --request POST $URL \
  --header "Authorization: Token $TOKEN" \
  --data-raw '
t0921 name="宇都宮",population=93215,date_mod="2006-5-29"
t0922 name="小山",population=23719,date_mod="2006-7-6"
t0923 name="佐野",population=43817,date_mod="2006-8-12"
'
```

## データの読み込み

参考ページ
[Query with the InfluxDB API](https://docs.influxdata.com/influxdb/v2.6/query-data/execute-queries/influx-api/)

```bash:query_data.sh 
#
URL='http://localhost:8086/api/v2/query?org=ekzemplaro'
TOKEN='1zIYs1taCm3pWQOBUNk0ssYccOF7YgJcdGyJDbiFYTBf7hcPvyl5nGyt71rNVkYtg2rBceUwQ1r5Xzm7Mabcde=='
#
#
curl --request POST $URL \	
	--header "Authorization: Token $TOKEN" \
	--header 'Accept: application/csv' \
  --header 'Content-type: application/vnd.flux' \
  --data 'from(bucket:"tochigi") |> range(start: -12h)'
```

実行結果

```text
$ ./query_data.sh 
,result,table,_start,_stop,_time,_value,_field,_measurement
,_result,0,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,2006-5-29,date_mod,t0921
,_result,1,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,宇都宮,name,t0921

,result,table,_start,_stop,_time,_value,_field,_measurement
,_result,2,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,93215,population,t0921

,result,table,_start,_stop,_time,_value,_field,_measurement
,_result,3,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,2006-7-6,date_mod,t0922
,_result,4,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,小山,name,t0922

,result,table,_start,_stop,_time,_value,_field,_measurement
,_result,5,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,23719,population,t0922

,result,table,_start,_stop,_time,_value,_field,_measurement
,_result,6,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,2006-8-12,date_mod,t0923
,_result,7,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,佐野,name,t0923

,result,table,_start,_stop,_time,_value,_field,_measurement
,_result,8,2021-07-06T20:50:36.122730369Z,2021-07-07T08:50:36.122730369Z,2021-07-07T08:46:23Z,43817,population,t0923
```


## 関連情報
[InfluxDB v2: cli の使い方](https://qiita.com/ekzemplaro/items/f3c8a58e337e8d974b24)
開発用 Azure VM の自動停止

# 開発用 Azure VM の自動停止

個人開発用に使っている Azure Virtual Machines の使用料を節約するためにやっていることを紹介します。

## 背景

開発用 VM は **使いたいときに起動**し、 **使い終わったら停止** しています。
停止し忘れて無駄な課金が発生することがよくあったため、自動停止するようにしました。

## 方針

以下の方針で、自動停止を実装しました。また、起動するための処理の実装も必要だと分かりました。

* 定期的に停止
   * 「X 時間ごとに」停止することで止め忘れを防止します。
   * VM の自動シャットダウン機能だと、1 日 1 回しか停止できず不便なので、ほかの方法を検討しました。
* ディスク SKU の変更
   * 使わないときはもっとも安いディスク（Standard HDD）
      * 停止時に Standard HDD にする
   * 使うときは高性能なディスク (Premium SSD）
      * 起動時に Premium SSD にする

## 使用技術

Azure Automation の Runbook を作成することとしました。

* 停止 Runbook : スケジュールと紐づける
* 起動 Runbook : 手動実行

## Runbook 

### 起動 Runbook

```powershell
Param(
    [string]$ResourceGroupName,
    [string]$VMName
)

$TargetSku = 'Premium_LRS'

$Conn = Get-AutomationConnection -Name 'AzureRunAsConnection'
Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint

$VM = Get-AzVM -Name $VMName -ResourceGroupName $ResourceGroupName -Status
$VMState = $VM.Statuses[1].DisplayStatus

if (($VMState -eq "VM deallocated") -or ($VMState -eq "VM stopped")) {
    foreach ($VMDisk in $VM.Disks) {
        $Disk = Get-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $VMDisk.Name
        if ($Disk.Sku -eq $TargetSku) {
            continue
        }
        $Disk.Sku = [Microsoft.Azure.Management.Compute.Models.DiskSku]::new($TargetSku)
        $Disk | Update-AzDisk
    }
    Start-AzVM -Name $VMName -ResourceGroupName $ResourceGroupName
}
```

### 停止 Runbook

```powershell
Param(
    [string]$ResourceGroupName,
    [string]$VMName
)

$TargetSku = 'Standard_LRS'

$Conn = Get-AutomationConnection -Name 'AzureRunAsConnection'
Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint

$VM = Get-AzVM -Name $VMName -ResourceGroupName $ResourceGroupName -Status
$VMState = $VM.Statuses[1].DisplayStatus

if (($VMState -eq "VM deallocated") -or ($VMState -eq "VM stopped")) {
    Write-Output 'The VM already stopped.'
} else {
    Stop-AzVM -Name $VMName -ResourceGroupName $ResourceGroupName -Force
}

foreach ($VMDisk in $VM.Disks) {
    $Disk = Get-AzDisk -ResourceGroupName $ResourceGroupName -DiskName $VMDisk.Name
    if ($Disk.Sku -eq $TargetSku) {
        continue
    }
    $Disk.Sku = [Microsoft.Azure.Management.Compute.Models.DiskSku]::new($TargetSku)
    $Disk | Update-AzDisk
}
```

## 改善点

### 作業中の VM 停止の対処

現状だと、スケジュールどおりに自動停止されてしまいます。
「何時までは停止スケジュールを無効にしてね（何時から停止スケジュールを有効にしてね）」というスケジュールを手動設定できるようにしたいです。
Ubuntu20.04のRMagickで使用可能なフォントのインストール

###背景
Ubuntu20.04のrailsアプリケーションにRMagickを使用してQRコードに文字列を挿入して表示していますが、数字とアルファベットしか表示されなかったのでフォントをインストールしました。
今回使用したのはオープンソースの**Takaoフォント**です。

###Takaoフォントとは
Takaoフォントは、IPAフォントというやつの派生版。
IPAフォントとは独立行政法人情報処理推進機構（略称IPA）によって開発されている日本語アウトラインフォント。

「Takao」というのは,IPAフォントの元となったTBフォントのオリジナル・デザイナーである故・林隆男氏のファースト・ネームから取ったもので、開発にあたり林隆男氏の奥様から名前の使用許可はいただいているらしいです（どうでもいい）

### Takaoフォントをインストール
```terminal
$ sudo apt install fonts-takao
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
...中略...
この操作後に追加で 28.8 MB のディスク容量が消費されます。
続行しますか? [Y/n] # yでreturn

$ sudo fc-cache -fv #フォントキャッシュを更新
...中略...
fc-cache: succeeded
```
### インストールとパスを確認
```terminal
$ fc-list TakaoGothic
/usr/share/fonts/truetype/takao-gothic/TakaoGothic.ttf: Takaoゴシック,TakaoGothic:style=Regular
```
上の左側、`/usr/share/fonts/truetype/takao-gothic/TakaoGothic.ttf`までをコピーして環境変数に入れるなどして無事に表示できました
###参考記事
[Ubuntu Desktop (20.04/18.04) にフォントを追加インストールする！](https://lab4ict.com/system/archives/879)
swiftでDropbox API用にヘッダに日本語ファイル名があるデータをポストする

iPhoneからDropboxにファイルをアップロードしようとしてDropbox API を調べながらやってみた。

# 準備
ユーザ登録とかアプリの登録とかアクセストークンの発行とかあるけど、まあ[Dropox APIのページ](https://www.dropbox.com/developers/documentation/http/documentation)に書いてあるとおりやればいい。見たら、すでにアプリの登録までやってあった。昔にラズパイからアップロードしようとした形跡（そういうアプリ名になっている）がある(^^;)。全然覚えてないし、、。

#SwiftyDropboxで試す
swift用にSwiftyDropboxというSDK(?)があるけど、Tutorial通りやっても動かないので１時間くらいで諦める。

# 直接URLを叩く
URLを叩くだけなので、公式のページ（upload だと[この辺] (https://www.dropbox.com/developers/documentation/http/documentation#files-upload)）に結構親切に書いてある通りにやる。

##curlでやってみる
とりあえず curl の例が出ているのでそのままコピペすると動く。

```
curl -X POST https://content.dropboxapi.com/2/files/upload \
    --header "Authorization: Bearer <get access token>" \
    --header "Dropbox-API-Arg: {\"path\": \"/Homework/math/Matrices.txt\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}" \
    --header "Content-Type: application/octet-stream" \
    --data-binary @local_file.txt
```
get access token のところは、本物はクリックするとアクセストークンが入ってくれる。

##swiftで試しにやってみる

```
        // header
        request.addValue("Bearer <ここはアクセストークンが入る>", forHTTPHeaderField: "Authorization")
        request.addValue("{\"path\": \"/Homework/math/Matrices.txt\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}",forHTTPHeaderField: "Dropbox-API-Arg")
        request.addValue("application/octet-stream",forHTTPHeaderField: "Content-Type")
        // POSTするデータをBodyとして設定
        request.httpBody = "This is a pen.\n".data(using: .utf8)
        
        let session = URLSession.shared
        session.dataTask(with: request) { (data, response, error) in
            if error == nil, let data = data, let response = response as? HTTPURLResponse {
                // HTTPヘッダの取得
                print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
                // HTTPステータスコード
                print("statusCode: \(response.statusCode)")
                print(String(data: data, encoding: .utf8) ?? "")
            }
        }.resume()
```
（どこかのサイトからコピペしましたが、今探しても見つかりませんでした。すみません。）
これでOK。

## ちゃんとやってみる。
で、パス名とかをちゃんと治してみたら、パス名が英語だとうまくいくけど、**日本語だとNG。（400で返ってくる。）**
「そうか、encodeしないといけないのね。」と思って、URLエンコードしてみるけどうまく行かない。
「日本語」を「%E6%97%A5%E6%9C%AC%E8%AA%9E」とか書くと、そのままのファイルが出来てしまう。

（Macの）curlで試すと、日本語をUTF8（のファイル）で書いてもそのまま動く。
_「encode しなくていいのか？」_

だんだん分からなくなる。curlがどんなデータを渡しているのかわからないので、仕方ないので WireShark でパケットを見たら、UTF8 が直接流れている。

じゃあ、ということで swift のプログラムのパケットを見てみたら、日本語のパス名のところで切れている。どうも UTF8 のデータをうまく扱えていないみたいだ。で色々試してみたら、「日本語」を「\u65E5\u672C\u8A9E」って書いてあげると動くのが分かった。なので、

```
        // パス名に日本語が入っていると４００で失敗する
        var encodedfname: String = ""
        for c in fname.utf16 {
            encodedfname = encodedfname + String(format: "\\u%04X",c)
        }
```

みたいに変換してあげると動いた。

もっと上手に encode してあげると良いのかもしれないけれど、とりあえず動いたのでOKとする。
なお、ヘッダではなくてURLの引数にパス名を入れる方法があるとは書いてあるけど、シンタックスが見つからなかったので試してません。それならURLエンコードでうまくいくような気がする。
#2　webアプリケーションについて

# webアプリケーションとは
名前のとおり、**web**で使用される**アプリケーション**のことです。

### webとは
インターネット上で画像やテキストなどの情報を公開する仕組みのことです。

### アプリケーションとは
目的を達成するために開発されたプログラム（サービス）のことです。
例）　
- *世界中の素敵な写真を共有したい（Instagram）*　
- *どの場所からでも買い物ができるようにしたい（Amazon）*

# 動的コンテンツと静的コンテンツ
webアプリケーションにも種類があります。その種類は大きく分けて2つ、**動的**と**静的**です。
※　ちなみにサービスの中身のことを**コンテンツ**と呼ぶそうです。

###動的コンテンツとは
ユーザーごとに表示される内容が変わるような仕組みのことを指します。
というか、世の中のwebアプリケーションはほとんどが動的コンテンツですね。
例）
- *ユーザーごとにおすすめの動画が提案される（YouTube）*
- *ユーザーが投稿した順に一覧ページとして表示される（Twitter）*

###静的コンテンツとは
表示される内容が変わらない仕組みのことを指します。
管理者によって表示内容が変わることがありますが、**ユーザーによって変わるか**が決め手です。
例）
- *〇〇の紹介ページ（管理者のみが編集可能）*





```
今回は、ここで終わります。
```
Railsで使用するターミナル

##1.contentが「Hello world」であるPostインスタンスを作成してください
```ターミナル
$post=Post.new(content:"Hello world")
```
##2.作成したPostインスタンスをpostテーブルに保存してください
```ターミナル
$post.save
```
##3.postテーブルの最初のデータを取得してください
```ターミナル
$post=post.first
$post.content
```
##4.postテーブルに保存されている全てのデータを取得してください
```ターミナル
$posts=Post.all
```
##5.インデックス番号が0のデータを取得してください
```ターミナル
$Post.all[0]
```
##6.postテーブルの最初の投稿内容が取得してください
```ターミナル
$Post.all[0].content
```
Greengrass ランキング

Greengrassが気になった時に参考になった記事

#第1位
https://qiita.com/kojiisd/items/2c75ba1984caf3fcc43a
#第2位
https://qiita.com/yohachi/items/78f794ac665099c3a82a
#第3位
https://qiita.com/poruruba/items/d413477f210f3858a5b0
#その他
https://qiita.com/Yokogawa_Ishino/items/12426577573b2bbb47eb
https://qiita.com/morikaz/items/f23ac05ae5430ce4c986


いつもわかりやすい記事ありがとうございます。
Ruby のcsvについてお勉強（基本が理解できてませんがやってます） 

作っている最中のアプリがあるんだけど、どうもにつまったので、学んだ内容を包み隠さずアウトプットとして残すことにします。

今現在、行き詰まっているのはRUBY上でcsvを扱うことについて。
ヘッダーが日本語のcsvを取り込んでグラフ化したいっていうのがゴールなんだけど、これだけの事が中々理解出来ずにいます。

下記サイトを参考に色々といじってみたのですが、基本的なブロック変数（引数？）が理解できてない状態。

https://qiita.com/tyamagu2/items/950aad7e67de1592e9d3

###使っているcsv
```csv
測定器,シリアル番号,タイムスタンプ測定器,記録タイプ,過去のグルコース値 mg/dL,血糖値をスキャンする mg/dL,超速効型インスリン（数値入力なし）,超速効型インスリン値（単位）,食事（数値入力なし）,炭水化物（グラム）,炭水化物（回分）,持効型インスリン（数値入力なし）,持効型インスリン値（単位）,メモ,ストリップグルコース mg/dL,ケトン mmol/L,食事時インスリン（単位）,補正インスリン（単位）,使用者による変更インスリン（単位）
FreeStyle リブレ,JCGY177-T2765,2020-05-16 2:37,0,194,,,,,,,,,,,,,,
FreeStyle リブレ,JCGY177-T2765,2020-05-16 2:52,0,204,,,,,,,,,,,,,,
FreeStyle リブレ,JCGY177-T2765,2020-05-16 3:07,0,209,,,,,,,,,,,,,,
FreeStyle リブレ,JCGY177-T2765,2020-05-16 3:22,0,211,,,,,,,,,,,,,,
```
この中で扱いたいのが、日時とグルコース値だけなので、
参考サイトを見つつこんな感じで取り出してみた。

###参考サイトの実験
```ruby
require 'csv'

HEADER_TO_SYM_MAP = {
  'タイムスタンプ測定器' => :id,
  '過去のグルコース値 mg/dL' => :guru,
}

header_converter = lambda { |h| HEADER_TO_SYM_MAP[h] }
csv = CSV.read('test/RUBY/csv.csv', headers: :first_row, header_converters: header_converter, converters: :integer, skip_blanks: true)

csv.each do |row|
  HEADER_TO_SYM_MAP.values.each { |k| puts "#{k} : #{row[k]}" }
end
```
###結果
```RUBY
RUBY % ruby test.rb
id : 2020-05-16 2:37
guru : 194
id : 2020-05-16 2:52
guru : 204
id : 2020-05-16 3:07
guru : 209
id : 2020-05-16 3:22
guru : 211
```

この中で、グラフ化にする際に、どの変数を扱えばいいかわからないのと、
header_converter = lambda { |h| HEADER_TO_SYM_MAP[h] }
のうち|h|をなんで使っているかがわからないのです・・・。

そんな訳で、基本のキかもしれないけど|h|とか|k|の扱いを理解するためにブロック変数とか引数とか調べて、わかり次第追記します。
QNetworkRequest で https に対応させる

# はじめに

これも当たり前と思われ見つかられなかったので

QNetowrkRequest を OpenSSL のライブラリをリンクせずにプロジェクトをビルドし https でファイルをダウンロードするとエラーが発生する

> qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed

# OpenSSL 関連のライブラリをリンクする

確認に使用したプロジェクトは qtbase/examples/network/downloadmanager

downloadmanager.pro を開き 次のエントリーを追加

※Qtのインストール先が C:\Qt の場合

```
OPENSSL_DIR=C:/Qt/Tools/OpenSSL/Win_x64
LIBS += $${OPENSSL_DIR}/lib/libcrypto.lib
LIBS += $${OPENSSL_DIR}/lib/libssl.lib
```

これでリビルドすれば https も使用可能

配布する場合は C:/Qt/Tools/OpenSSL/Win_x64/bin/ 下の dll も忘れずに配布しましょう！

```:リンク前
> .\downloadmanager.exe https://www.google.com/

Downloading https://www.google.com/...
qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed
[###    ]      0.0 bytes/sec
Failed: TLS initialization failed
0/1 files downloaded successfully
```

```:リンク後
> .\downloadmanager.exe https://www.google.com/

Downloading https://www.google.com/...
[##################################################] 100% 157.3 kB/s
Succeeded.
1/1 files downloaded successfully
```
ファイル読み込み時　PHP の include と require の使い分け

参考サイト

https://uxmilk.jp/15560


###◎include文とrequire文の違い

「ファイル読み込み失敗したときにエラーで終了するか警告で継続するか」という点。

どちらも対象ファイルを読み込みますが、この時ファイルが存在しない、関数で構文エラーなどが起こった場合、それぞれ以下のような挙動となります。

* require：致命的なエラー（Fatal）となり処理を停止
* include：警告（Warning）となり処理は継続

###◎includeとrequire 使い分けの判断基準

どちらが良いかは、実装する内容によって適切な対応が異なると思います。以下に使い分けの例を挙げてみます。

####例：コア部分や後の処理に影響する場合はrequire

例えば、バッチ処理など致命傷なエラーにより後続処理の影響が発生する場合には、処理を中断させたいのでrequireを使用します。

#### 例：画面処理などの場合はinclude

画面表示などで表示処理のための計算処理でエラーとなるような場合は、requireだと画面が全く表示されなくなるのを避けるためにincludeを使用するなどの使い分けが考えられます。

実際のプロジェクトに合わせ、それぞれの特性を理解し異常終了させるべき、継続して処理を進めるべきなのか、プログラム全体を鳥瞰しながら見極めていくことが重要となります。
レプリケーションインスタンスをオートスケールさせる

## はじめに
AWS DMSの継続レプリケーションするタスクのインスタンスのスペックの調整をするのに、コンソール上でポチポチするのが面倒になったので、必要に応じてレプリケーションインスタンスを自動的にスケールして、より高い負荷 (スケールアップ) を処理し、負荷が低いときにコストを削減 (スケールダウン) する方法を模索しました。

##やりたいこと
対象のレプリケーションインスタンスのCPU使用率が閾値を超えた際は、スケールアップし、Eメール通知をもってトリガーを確認できるようにします。閾値を下回った際は、スケールダウンし、Eメール通知を行う。

## セットアップ
### **構成図**
![DMS AutoScaring.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/192019/5cea3c9f-1d4d-0aea-3423-5fa02bda5c35.png)

### **必要リソース** 
- インスタンスタイプの変更通知（SNS）
- Lambda関数を呼び出す通知（SNS）
- CPU監視用のアラーム（Cloudwatch）
- IAMロールの作成（IAM）
- Autoスケールスクリプト（Lambda）
- 毎分Lambdaを呼び出すスケジューリング（Cloudwatch）

#### **インスタンスタイプの変更通知** 
レプリケーションインスタンスのスケールアップした際に、Eメール通知します。
（トピック名: Replication_Instance_Update）

- SNSトピックを作成
- サブスクリプションでEメールでの通知設定

#### **Lambda関数を呼び出す通知** 
CPU使用率が閾値を跨いだ際に、Lambda関数を呼び出します。
トピックの作成（トピック名: dmsautoscaler）

- SNSトピックを作成
- サブスクリプションでlambda関数を設定

#### **CPU監視用のアラーム** 
レプリケーションインスタンスのCPU使用率を監視するようにアラームを作成します。
CPU使用率の閾値は、下記の参照ください。
 
**CPU使用率が閾値より超えた場合** 
（アラーム名：dms_cpu_high）
> CPU 使用率 CPUUtilization が次の場合は常に : >= 80: 5 データポイント 期間 = 1 MinuteStatistic = 平均

**CPU使用率が閾値より下回った場合** 
（アラーム名：dms_cpu_low）
> CPU 使用率 CPUUtilization が次の場合は常に: <= 40: 5 データポイント 期間 = 1 MinuteStatistic = 平均

#### **IAMロールの作成** 
Lambda関数に付与するロールを作成します。
（ロール名：dms-autoscaler）
#####権限一覧
- DMS レプリケーションインスタンスを変更する
- CloudWatch Logs に入力する
- CloudWatch イベントルールを作成して使用する
- SNS トピックに公開する

>※下記のポリシーを使用
>[lambda_policy.json](https://raw.githubusercontent.com/aws-samples/aws-dms-autoscaling/master/lambda_policy.json)

#### **Lambda関数の作成**
レプリケーションインスタンスのCPU使用率が閾値を跨いだ時に、Cloudwatchアラームにより、Lambdaに登録されるSNS通知がトリガーとなり、この関数で、レプリケーションインスタンスのスケールがされます。
自動スケーリングの設定は、S3バケットにjsonファイルを格納し、jsonファイルを介して管理しています。
（lambda関数：AutoscaleDMSReplicationInstance） 
（S3バケット名：dms-autoscaleinstances）

>自動スケーリング設定ファイル
>[instance_types.json](https://github.com/aws-samples/aws-dms-autoscaling/blob/master/instance_types.json)

>スケーリングlambda関数
>[dms.py](https://github.com/aws-samples/aws-dms-autoscaling/blob/master/dms.py)
SpringBoot Gradleで生成するjarとwarの切り替え設定方法

#はじめに
jarとwarの生成を切り替えるbuild.gradleの設定まとめです。

#環境
Gradle 7.0.1
SpringBoot 2.3.5.RELEASE
Java 11

#jar


**生成手順**
1.build.gradleに下記の設定を追加<br>※最小限の設定のみ記載しています。

```build.gradle
jar {
    archiveBaseName = '<任意の名前>'
    archiveVersion =  '<任意のバージョン>'
}
```

2.プロジェクトのディレクトリでgradlew buildコマンドを実行
3.<任意の名前><任意のバージョン>.jarが下記ディレクトリに生成される

```
プロジェクト/
　├ build/
　　　└ libs/
　　　　　└ <任意の名前><任意のバージョン>.jar
```


#war

**生成手順**
1.build.gradleとsettings.gradleに下記の設定を追加<br>※最小限の設定のみ記載しています

```build.gradle
plugins {
    id 'war'
}

version = '<任意のバージョン>'
```

```settings.gradle
rootProject.name = '<任意の名前>'
```
2.プロジェクトのディレクトリでgradlew buildコマンドを実行
3.<任意の名前><任意のバージョン>.warが下記ディレクトリに生成される

```
プロジェクト/
　├ build/
　　　└ libs/
　　　　　└ <任意の名前><任意のバージョン>.war
```


#注意
jarとwarは同時に生成できませんでした...
片方ずつお試しください。
AzureのルートテーブルとNSGの定義をまるっとコピーして使う方法


# 概要
AzureのNSGとかUDRでの通信制御の設定って細かいし数多いし、
設定がとても~~めんどくさい~~.....((+_+))
というわけで、少しでも楽できるように、
一度作成したルートテーブルの定義をコピーする方法について紹介します。

今回はルートテーブルの定義をコピーする方法ですが、
同じ要領でNSGの定義もコピーが可能です。

ルートテーブルとNSGの説明についてはこちらから↓
[ルート テーブルの作成、変更、削除](https://docs.microsoft.com/ja-jp/azure/virtual-network/manage-route-table)
[ネットワーク セキュリティ グループについて](https://docs.microsoft.com/ja-jp/azure/virtual-network/network-security-groups-overview)

## 手順
1.  コピー元となるルートテーブルを用意する  
※ 今回はコピーの手順なので、
事前に1つ以上のルートテーブルが作成されている必要があります。
2.  Azure PowerShellにログインする
3.  下記コマンドを実行する

```powershell
# コピー元ルートテーブルの定義をコピー
$rules = (Get-AzRouteTable -Name "コピー元のルートテーブル名" -ResourceGroupName "ルートテーブルが存在しているリソースグループ").Routes

# コピーした定義を使って新規ルートテーブルを作成する
New-AzRouteTable -Name "新規作成するルートテーブル名" -ResourceGroupName "新規ルートテーブルを作成するリソースグループ" -Location "ルートテーブルを作成するリージョン" -Route $rules

```
これで完了です。
Azure Portalから確認すると、
コピー元と同じ定義のルートテーブルがもう1つ作成されています。

東日本リージョンに作ったルートテーブルを他のリージョンに作るとか、
定義はちょっと違うけど、
一度コピーしてから変更した方が楽！なときなどはとても便利です。

ちなみに、NSGは以下のコマンドで定義コピーが可能です。

```powershell 
# コピー元NSGの定義コピー
$rules = (Get-AzNetworkSecurityGroup -Name "コピー元のNSG名" -ResourceGroupName "NSGが存在しているリソースグループ").SecurityRules

# コピーした定義を使って新規NSGを作成する
New-AzNetworkSecurityGroup -Name "新規作成するNSG名" -ResourceGroupName "新規NSGを作成するリソースグループ" -Location "NSGを作成するリージョン" -SecurityRules $rules -Force

```

以上です。
とても簡単なので、Azureのネットワーク設定で少しでも楽したい！
ってときにはぜひ使ってみてください。
最速で単語を調べる[ macbook ]

***環境***
・m1 macbook air
・google chrome

ブラウジング中に分からない単語を調べる時、

僕の従来のやり方⬇️⬇️⬇️⬇️⬇️⬇️

　　単語ドラッグ　➡️　cmd + c(コピ)　➡️　cmd + t(新規タブ)　➡️　cmd + v(ぺ)

新しいやり方⬇️⬇️⬇️⬇️⬇️⬇️


　　方法①　単語を二本指タップ(マウスなら右クリ)　➡️　¥ + Enter　＝　新規タブで検索

　　方法②　単語を二本指タップ(マウスなら右クリ)　➡️　[ + Enter　＝　mac内蔵辞書アプリの吹き出し
【Rails6.0】Active Storageを用いた画像投稿の実装手順

【Rails6+Docker】の開発環境下で画像投稿機能を実装する際、とても苦労したため備忘録に残しておきます。

### 前提

・各種モデル、コントローラー、ビューは作成済み
・画像だけが表示されない状態

### 失敗例　carrierwave、minimagickの利用
はじめのうちは、carrierwave、minimagickを使って画像投稿機能の実装を試みました。

Carrierwaveの導入には「ImageMagick」がインストールされている必要があるみたいでしたので
下記のとおり、DockerfileにImageMagickをインストールする記述を追記

```
# Dockerfile
FROM ruby:3.0.0

RUN apt-get update -qq && \
    apt-get install -y build-essential libpq-dev nodejs vim imagemagick     #←追記

RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
apt-get update && apt-get install -y yarn

RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \
        && apt-get install -y nodejs

RUN mkdir -p /var/www/remote-work-space

WORKDIR /var/www/remote-work-space

ADD Gemfile /var/www/remote-work-space/Gemfile
ADD Gemfile.lock /var/www/remote-work-space/Gemfile.lock

RUN gem install bundler
RUN bundle install

ADD . /var/www/remote-work-space

RUN mkdir -p tmp/sockets
RUN mkdir -p tmp/pids
```

追記をして保存したら、$ docker-compose build --no-cacheを実行。
すると・・・

```
executor failed running [/bin/sh -c apt-get update -qq &&     apt-get install -y build-essential libpq-dev nodejs yarn vim imagemagik]: exit code: 100
ERROR: Service 'app' failed to build : Build failed
```

とエラーが返ってきて上手く行きませんでした。

そこで違う方法として、Active Storageを用いた実装を試してみたところ、上手くいきました。

# 成功例　Active Storageの利用


▼参考記事▼

https://techtechmedia.com/active-storage-rails6/

### 重要コマンド　（Active Storageのインストール）
```
$ docker-compose exec app bundle exec rails active_storage:install
```

```
docker-compose exec app bundle exec rails db:migrate
```

### Gemfile

```
# Use Active Storage variant
# gem 'image_processing', '~> 1.2'

↓ コメントアウトを外します

# Use Active Storage variant
gem 'image_processing', '~> 1.2'
```

これによりImageProcessing経由で画像処理されます。

$ docker-compose buildでインストール完了後、「app/views/posts/index.html.erb」を確認してみると・・

今度こそ画像が表示されました！！！
Get-RDSDBInstanceコマンドレットでFilterを使う

# やりたいこと
PowerShellでRDSを検索して一覧を取得したい。
ググったけどやり方見つからなかったので記事にしてみる。

公式ドキュメントはここらへん。
https://docs.aws.amazon.com/powershell/latest/reference/items/Get-RDSDBInstance.html
https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/RDS/TFilter.html

# やりかた
こんな感じでできました。

```
$filter = New-Object Amazon.RDS.Model.Filter
$filter.Name = "engine"
$filter.Values = @("sqlserver-ee", "sqlserver-se")

Get-RDSDBInstance -Filter $filter 
```

# 補足
公式ドキュメントの`Parameters`セクションの`-Filter`のところに記載のある項目しか検索できないようです。
https://docs.aws.amazon.com/powershell/latest/reference/items/Get-RDSDBInstance.html#parameters
[HTML / CSS] 絶対位置と相対位置

##はじめに
Webアプリのフロントをいじっているときに、「絶対位置」と「相対位置」って、要素を自分の思った通りの配置に置きたい時にすごい大事だなと思いまして、自分になりにまとめてみました。

よろしくお願いします。

##positionプロパティ

positionプロパティは要素の配置を指定できるプロパティです。このプロパティを使いこなすのには絶対位置と相対位置の理解が必要不可欠です。

今回はposition: absolute;とposition: relative;　の使い方を見ていきながら絶対位置と相対位置の理解を深めていきたいと思います。

##絶対位置
まず、下記のようなHTMLとCSSがあるとします。

```html:index-position.html
<!DOCTYPE HTML>
<html>
  <head>
    <meta charset="UTF-8">
    <title>position</title>
    <link rel="stylesheet" href="style-position.css">
  </head>
  <body>
    <header></header>
    <div class="main">
      <div class="parent">
        <div class="child">
        </div>
      </div>
    </div>
    <footer></footer>
  </body>
</html>
```

```css:style-position.css
header{
  height: 80px;
  width: 100%;
  background-color: lightgray;
}

.main{
  color: white;
}

.parent{
  width :360px;
  height: 360px;
  background-color: pink;
  margin: 40px;
}

.child{
  width :200px;
  height: 200px;
  background-color: skyblue;
}

footer{
  height: 80px;
  width: 100%;
  background-color:lightgray;
}
```

これをブラウザで表示させてみるとこのようになります↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552346/12edae5b-ca58-d0a0-e7f6-d59abacde8e6.png)

そしたら、position: absolute;を使ってみましょう。
positionプロパティを使ったら、leftプロパティやtopプロパティを使って基準の位置からの距離も指定してあげます。
*※この指定がなければposition: absolute;を使っても反映されません*

childのcssを下記のように修正してください。

```css:style-position.css
.child{
  width :200px;
  height: 200px;
  background-color: skyblue;
  position: absolute;
  top: 40px;
  left: 40px;
}
```

そうするとこのようになるはずです↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552346/030a1340-3060-9cac-7907-ad3b97e1cecb.png)

基準が一番左上でtopとleftで40px指定しているので40pxの余白があります。
これが絶対位置です。例え、親要素の中に含まれている子要素でも、絶対位置に指定するとそこからはみ出て絶対位置に配置されます。

##相対位置
ある要素が他の要素との関係の上に成り立つ位置のことを相対位置と言います。

親要素のほうにposition: relative;を追加します。

```css:style-position.css
.parent{
  width :360px;
  height: 360px;
  background-color: pink;
  margin: 40px;
  position: relative;
}
```

そうすると子要素は親要素の左上を基準にするようになり、相対位置をとります。↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1552346/008d74d4-d362-1232-ac3f-9f2488305713.png)


親要素にposition:relative;を指定することで、position:absolute;を指定されている子要素の基準位置が、親要素になりました。これが相対位置です。

##最後に
要素を配置する時私は十中八九反映されなかったり、思った通りの場所へ動いてくれなかったりします。
それぞれのプロパティの意味などもしっかり理解して、フロントも強いエンジニアになりたいです。

そういえば、positionプロパティとは関係ないですが、プロパティの意味を勘違いしてた記事がありました、こちらの記事もよかったらどうぞ。
https://qiita.com/minhee/items/76e05c7537d2389a0ef6
InfluxDB v2: cli の使い方

InfluxDB 2.0 の cli の使い方です。

Arch Linux へのインストール

```bash
sudo pacman -S influxdb influx-cli
```

サーバーの起動

```bash
sudo systemctl start influxdb
```

バージョンの確認

```text
$ influx version
Influx CLI 2.6.1 (git: 61c5b4d) build_date: 2022-12-29T15:41:09Z
```

ユーザーの作成
user は uchida
organization ekzemplaro
で作成しました。

```bash
influx setup
```

ユーザーと　Token の確認

```bash
influx auth list
```

config の確認

```text
$ influx config
Active	Name		URL			Org
*	config_influx	http://localhost:8086	ekzemplaro
```

以下の操作は、
.influxdbv2/configs
が既に作成されている状況で行います。
organizatin や、 token は　configs から読み込まれます。

バケットの作成
tochigi というバケットを作成します。

```bash
influx bucket create -n tochigi -r 72h
```

バケットの表示

```bash
influx bucket list
```

バケットの削除

```bash
influx bucket delete -n tochigi
```

データの書き込み
tochigi という バケットが作成された状態で。

```bash
influx write --bucket tochigi --precision s \
  't0924 name="足利",population=6347900,date_mod="2004-3-22"'
```

データのリード

```bash
influx query --raw 'from(bucket:"tochigi") |> range(start:-12h)'
```
ElasticStackの歴史

## 目的
Elasticsearchを勉強しようと思い、案外に資料も少なく、ホームページの英文記事読むのも大変でした。ここで何回かを分けてElasticStackから勉強できたことを共有したいと思います。

## 歴史

ElasticStackが生まれる前にELKスタックというログ分析用のオープンソースフレームワークが使われました。
<pre>
Elasticsearch　→　オープンソース、APIです、さまざまなログ分析および検索に使われてる。
Logstash　→　オープンソース、さまざまなソースからデータを収集して変換し、希望する送信先に送信できます。
Kibana　→　オープンソース、Elasticsearchに保存されたデーターを可視化にするツール。
</pre>
![ELK.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755874/3060ee9f-e5da-dba1-98aa-ffc39bfaad11.png)
そこからBeatsというのもを加え、新しいブランドのElasticStackが誕生しました。

## ElasticStack とは
ELKより新しいメンバーのBeatsも含めて、構成は以下のようになってます。
![ELB.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755874/5c6b1037-88d0-9179-210a-1844091288d0.png)
流れの説明
<pre>
①Beatsから収集されたデーターはElasticsearchに送る
②Beatsから収集されたデーターはlogstashに送る
③logstashが一回送られたデーターを処理してElasticsearchに送る
④Elasticsearchがため込んだデーターをkibanaを通じて可視化にする
</pre>

## Beatsとは

Beats(ビーツ)とは、オープンソースデータ収集/転送プラットフォームです。つまり運び屋だと思ってください。<font color="Red">ここで注意</font>が必要なのは、Beatsは単独ではなくて運び屋の総合名前です。この中にログファイルの転送やってるFilebeat、CPUやメモリ、RedisやNGINXなど、システム統計情報を集計するMetricbeat、ネットワークパケットを集計するPacketbeatなどなどあります。
他にもいろいろあるので、[ここ](https://www.elastic.co/jp/beats/)にてご参照ください。

## Beatsとlogstashの違い

Beatsとlogstash全部データー収集仕事して、何が違う？
以前beatsがない時にlogstashがデーター収集の仕事をしてました。今、主にBeatsがデーター集計し、logstashがデーターの分割などの処理をするそうです。

## 次の記事

次回Elasticsearchについて解説します。
[PyTorch] GPUの動作確認セット

## はじめに
私は普段、PyTorchの環境構築をしたあとに確認用のコードを探して導入確認しています。ですが、いつも複数のサイトを探す時間がもったいないのと結局私好みの出力が欲しかったため、この場所に動作確認セットおよび備忘録として残しておきます。

## 動作確認セット
```python
import torch

print(torch.__version__)
print(f"cuda, {torch.cuda.is_available()}")
print(f"compute_{''.join(map(str,(torch.cuda.get_device_capability())))}")
device_num:int = torch.cuda.device_count()
print(f"find gpu devices, {device_num}")
for idx in range(device_num):
    print(f"cuda:{idx}, {torch.cuda.get_device_name(idx)}")

print("end")
```

出力例

```bash
1.8.0+cu101
cuda, True
compute_61
find gpu devices, 1
cuda:0, GeForce GTX 1080
end
```

## 最後に
これで自分があちこち検索しに回らなくて済むかも・・・
javascript 特定要素にmouseover（マウスが乗った時）とmouseleave（マウスが外れた時）

Every Qiita #5
のんびり独学初学者の毎日投稿チャレンジ 5日目
今回は・・・
自作コミュニティアプリ開発で追加した要素の親要素に対して操作したかった時の備忘録です。

##追加した要素にイベントを付与したい
javascriptで付与した要素にマウスが乗った時と外れた時のイベントを付与していきます。

```javascript:index.js
  xhr.onreadystatechange = function(){
    // xhrクライアントの状態がDONE=4　操作完了で発火
      if(this.readyState == 4 && this.status == 200){
       この中にイベントファンクションを記載
  }
```
今回は画像をs3に保存し、保存した画像を随時表示させたいので、上記のレスポンスが成功した時の関数の中に記述していきます。
readyState=4とstatus=200のif文の中に記載すると追加した要素にイベントを付与することができます。
###マウスが乗った時の処理
```javascript:index.js
img_element.addEventListener("mouseover", function(){
// マウスオーバーした親要素に削除テキスト、透過css判定id付与 
  this.parentNode.setAttribute("id","del_img");
  const del_btn = document.createElement("h1");
  del_btn.setAttribute("id","del_btn");
  del_btn.innerHTML = "×";
  this.parentNode.appendChild(del_btn);
});
```
####解説：
- img_elementは画像を表示する際に新しく追加したimg要素になります。反映後の構造は以下の通りになります。

```html:index.html
  <div id="del_img（新しく追加したid）">
    <h1 id="del_btn(新しく追加したid）">✖️</h1>
    <img 新しく追加した要素(img_element)>
  </div>
```
- this.parentNode: imgの親要素にidを付与することで、クリックアクションで選択要素を削除することが可能です。cssでホバー時のアニメーションを指定することも可能です。

###マウスが外れた時の処理
```javascript:index.js
  img_element.addEventListener("mouseleave", function(){
  // del_imgの削除(透過css)
  document.getElementById("del_img").removeAttribute("id");
  // del_btnの削除(削除テキスト)
  document.getElementById("del_btn").remove();
  });
```

####解説
- mouseleaveイベントで指定した要素がマウスを外れた時に発火します。
- mouseoverで付与した要素を取り除くことで、初期設定の状態に戻ります。

元々あるhtml要素の取得は他記事で多くありますが、javascriptで付与した要素に関しては付与した関数の中にイベントを同時に設定する必要がありますので、もし同じような境遇の方いらっしゃいましたら参考にして頂ければと思います。
Rust で画像処理？

# はじめに

最近、会社に就職して暇つぶしがてらRustの勉強をはじめてみました。
第一印象は難しい！って感じですね。さすがにシステムプログラミング言語ですので、そこまで簡単なわけはないのですが。

今回は標準ライブラリを使うことに慣れるため、簡単な画像処理を標準ライブラリのimageを用いて行いたいと思います。
題材としては、グレースケールです。

#結果

テストで使う画像はRUSTの公式サイトからダウンロードしてきたフェリスの写真です。
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328277/93920bb1-5178-377c-4870-fdd4841914fd.png)
グレースケール化した後の画像が以下の通りです。
![gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328277/3f54590b-d26c-bb2c-9de6-9adeb26fcf99.png)

#ソースコード

```cargo.toml
[dependencies]
image = "*"

```
設定ファイルにimage crateを追加する。
2021/7/7 現在ではversion 0.23.14がダウンロードされた。

``` rust:main.rs
extern crate image;

use std::env;

fn main() {
    // 引数解析
    let mut arg = env::args();
    // 実行コマンドをスキップ    
    arg.next();
    
    // 引数チェック
    let image_path = match arg.next() {
        None => {
            println!("error");
            return
        },
        Some(s) => s,
    };

    //引数を得られているかデバック
    println!("Image Path is {}",image_path);
    test(image_path);
}

fn test(path : String){
    // ファイル展開とエラーチェック(.unwrap())
    let     img      = image::open(path).unwrap();
    let     img      = img.to_rgb8();
    let     size_x   = img.width();
    let     size_y   = img.height();

    // グレースケール用のデータを作成
    let mut gray_img = image::GrayImage::new(size_x,size_y);
    let pix  = img.get_pixel(0,0);

    for y in 0 .. size_y {
        for x in 0 .. size_x {
            // ピクセルデータの取得
            let pix  = img.get_pixel(x,y);
            // GrayScale BT.709
            // V = 0.2126*R + 0.7152*G + 0.0722*B
            let val  = [((pix[0] as f32 * 0.2126) as u32 + 
                         (pix[1] as f32 * 0.7152) as u32 + 
                         (pix[2] as f32 * 0.0722) as u32) as u8;1];
            // ピクセルデータをあらかじめ作っておいたグレースケールデータに書き込む
            gray_img.put_pixel(x,y,image::Luma(val));
        }
    }

    // 画像をファイルとして保存する。エラーチェックも忘れずに
    gray_img.save("./gray.png").unwrap();
}
```
今回はあえて一つ一つのデータを取得し、加工し、保存するような書き方を行った。
image crateにはグレースケール化する関数も存在するので、そちらでもよかったのだが。。。

# おわりに

Rust はむずかしいな。
特に型が厳しすぎるので、慣れるまでコンパイラに怒られ続けるんだろうな。
次は何を題材に練習しようか悩みますね。

# 参考
https://kcs1959.jp/archives/4064/research/rust_image-crate_example
https://docs.rs/image/0.23.14/image/index.html
ActiveDirectoryのOUとセキュリティグループについて画像多めで解説してみる

Active Directoryを運用する中で、OUやセキュリティグループという単語はよく見聞きしても、実際に操作する画面と紐づかないという状態が過去の私の経験としてありました。そこで、Active Directoryユーザーとコンピューターを扱う上の頻出の、OUとセキュリティグループについて、画像多めでまとめてみました。

# OU(Organizational Unit、組織単位)
下記画像の赤枠で囲まれているものがOUです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/e5ee9957-73f8-53eb-7534-c2814b50e7d1.png)

実は、赤枠で囲んでいないもの（デフォルトで作成されているUsersなど）はOUではありません。見分け方としては、下記画像のようにフォルダマークの中に模様が描かれているか否かで判別できます。（Usersの中のオブジェクトに対してdsqueryすると、CN=Usersと表示され、OUではないことが分かります。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/0097c591-ad52-e012-383b-fc6c187ffa57.png)

OUを利用する場面としては、グループポリシーの配信管理が挙げられます。以下の画像では、ドメイン全体にDefault Domain Policyが適用されていて、Domain ControllersのOUにDefault Domnain Controllers Policyが適用されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/fa72b641-5a47-ae0c-dceb-36c079ccd2af.png)

### OUの新規作成
右クリック→新規作成→組織単位（OU）より、名前を入れてOKボタンを押してあげるだけです。カンタン。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/ff639a2c-cd3d-4bdb-4e98-63654dc1150d.png)

### OUの削除
OUの削除は面倒です。というのも、右クリックして削除しようとすると、以下のようなメッセージが表示されることがあります。これは、OUの削除を防止するための保護機能が働いているからです。OUの作成時に「間違って削除されないようコンテナーを保護する」にデフォルトでチェックが入る仕様となっており、この保護を解除しないことには、OUは削除できません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/a92d2d8b-746d-6f4f-66da-ba91f1de92a5.png)
この保護を解除するためには、以下の手順を踏む必要があります。

1. 表示メニューより、拡張機能にチェックを入れる
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/7182e95e-4826-2690-3d99-0e8706189fa0.png)
1. 削除したいOUのプロパティを開き、オブジェクトタブの「誤って削除されないようにオブジェクトを保護する」のチェックを外す
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/3a98207c-ac9a-5abb-137f-af3ca9cfe167.png)
1. OUを右クリックして削除をクリック（保護かかかっていない場合はこの手順のみでOK）
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/306c3abd-f61e-a0da-f633-6a14468abc88.png)
1. 表示メニューより、拡張機能のチェックを外す
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/d1f03abf-e9e4-d938-6123-7b06aa9e89e5.png)

# セキュリティグループ
下記画像の赤枠で囲まれているものがセキュリティグループです。種類の欄にセキュリティグループと記載があるのでOUよりは分かりやすいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/82a0e2fe-d549-25c9-9c68-fcb071df9b55.png)

セキュリティグループを利用する場面として、ファイルサーバーの権限管理が挙げられます。ユーザー単位でも権限を振ることはできますが、グループ単位で振ったほうが管理しやすいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/a2cebb57-a1cc-534b-243f-41167b6ceea5.png)

### セキュリティグループの新規作成
右クリック→新規作成→グループより、グループ名の入力とグループの種類で「セキュリティ」を選択してOKをクリックします。グループのスコープは3種類あり、単一フォレスト・単一ドメインのActive Directory なら使い分けるメリットはないです（詳しくはググってください）。他のセキュリティグループと合わせておくのが無難でしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/776880/34d97d8b-8038-a113-7207-553dced19ee9.png)

### セキュリティグループの削除
削除手順はOUと同じです。OUと異なりデフォルトでは保護は働いていないため、簡単に削除できます。
ルートドメイン（Apexドメイン）はWebページの正規URLにしてはいけない

## TL;DR

`example.com`というドメインを持っているとしたら、必ず`https://www.example.com/`を正式なURLにしましょう。`https://example.com/`へのアクセスは、ロードバランサを使用して`https://www.example.com/`へ転送してください。Google Chromeでは`www`は省略して表示され、Firefoxでは`www`は薄く表示されます。Google、AmazonやAppleはこの戦略を使用しています。

マーケティング上の理由で`www`を省略しようとするのは時代遅れです。

## Apexドメインの問題

ルートドメインにはドメインの正当性を示すためにSOA (Start Of Authority) リソースレコードと NS (Name Server) リソースレコードが登録されるようインターネット標準で決まっています。一方、ドメインの転送を行う際に指定するCNAME (Canonical NAME) リソースレコードは、他のリソースレコードと同じドメインに設定してはいけないとインターネット標準で決まっています。この制約のため、ルートドメインは別名としてApexドメインやネイキッドドメインと呼ばれて特別扱いされています。どうして特別扱いされているかというと、トラフィックをCDN (Content Delivery Network) に転送する際は、DNSに組み込まれている負荷分散の機構を十分に活用するために、通常はCNAMEリソースレコードを使用するのですが、Apexドメインでは、前述した制約によりCNAMEリソースレコードを設定することができないからです。

以上のことから、Apexドメインは正式なURLとして使用するのには適していないことが分かります。今はIPアドレスをAリソースレコードに指定していて問題なくても、将来CDNに移行しようとするときに必ず困ります。

## 回避策とその問題

このApexドメインの制約を回避するために、ALIAS擬似レコードをサポートしているDNSベンダーが存在します（https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/resource-record-sets-choosing-alias-non-alias.html ）。ALIAS擬似レコードは、DNSサーバーが転送先のIPアドレスを調べて、あたかもAリソースレコードが登録されているかのように振る舞います。結果的にCNAMEリソースレコードを使用しないので前節の問題は回避できるというわけです。

この説明を聞くとこれで問題解決じゃんと思うかもしれません。しかしALIAS擬似レコードには大きく2つの問題があります。
一つ目は、IETF　(Internet Engineering Task Force) による標準化がなされていないという点です。ドメインのシステムの中でALIAS擬似レコードが将来にわたって正しく動作することは誰も保証していないし、保証できないということです。過去にANAMEレコードが議論されていたようですが、今となっては誰も話題に挙げていないようなのでALIAS擬似レコードが標準に入る可能性は限りなく低いです。
二つ目は、DNSに組み込まれている負荷分散の機構をダメにしてしまう点です。ホストと権威ゾーンの間に余計なサーバーが介入するので、ロケーションに応じて最も近いホストのIPアドレスを返すといったことができなくなってしまいます。AWSの場合は、AWS内部のサービスをALIAS擬似レコードが指している場合はうまく負荷分散できるようですが、別のベンダーのサービスが入ってくると全てが狂ってしまいます。このALIAS擬似レコードの負荷分散の方式が標準化されていないことが原因でこのようなことが起こってしまいます。

## 結論

IETFが定めた標準の範囲でうまくやるべきです。マーケティングのために技術的標準から外れた設定は行うべきではありません。本当にマーケティング上で重要なら標準の方が変わるはずです。変わっていないということは、そこには価値はないということです。
vi vimコマンド

##はじめに
Macのviで個人的によく使用するものを備忘録として記録します。

##起動
|key|action|
|:--|:--|
|vi ファイル名|ファイルをvimで開く|
|vi ファイル名　ファイル名|複数ファイルをvimで開く|

##モード
|key|action|
|:--|:--|
|[esc]|コマンドモードへ切り替え|
|i|入力(インサート)モードへ切り替え|

##表示
|key|action|
|:--|:--|
|:set nu|行番号を表示|
|:set nonu|行番号を非表示|

##カーソル
|key|action|
|:--|:--|
|$|行の先頭へ移動|
|0|行の末尾へ移動|

##入力
|key|action|
|:--|:--|
|o|現在の行の下に1行挿入後、入力モードへ切り替え|
|O|現在の行の上に1行挿入後、入力モードへ切り替え|

##削除
|key|action|
|:--|:--|
|dd|現在の行を削除|
|:行番号1,行番号2d|指定範囲内の行を削除|

##検索
|key|action|
|:--|:--|
|/文字列|文字列を検索|
|n|順方向へ検索|
|N|逆方向へ検索|

##操作の修正
|key|action|
|:--|:--|
|u|直前の操作を取りやめ|
|.|直前の操作を繰り返し|
|[Ctrl]+r|取り消しの取り消し|

##終了
|key|action|
|:--|:--|
|:wq|ファイルを保存し終了|
|:wq!|ファイルを保存し強制終了|
|:q|ファイルを保存せず終了（変更がある場合警告あり）|
|:q|ファイルを保存せず強制終了（警告なし）|



#参考
[viコマンド（vimコマンド）リファレンス](https://qiita.com/pe-ta/items/0510bee10bcfd88afeee)