OmniAICreator/Qiita-1.07M huggingface dataset:
CDKでCloudFront Functionが見つからないエラーの対処

## 問題

CDKでCloudFront Functionを作成した状態で、別のリソースを変更した際に下記のエラーが出た。

```sh
12:02:49 | UPDATE_FAILED        | AWS::CloudFront::Function                       | mycloudfrontfunction911B181C
Resource handler returned message: "Resource of type 'AWS::CloudFront::Function' with identifier 'ap-northeast-1MyCloudFrontFunction6AA8AF07' was not found." (RequestToken: 74d6dbad-01af-c5ca-985b-4091c1eddd4a, HandlerErrorCode: NotFound)
```

NotFound？CloudFront Functionの変更は行っていないのに何故だろうと思って調べると下記のissueを見つけた。


[(aws-cloudfront): Unexpected diffs caused by cloudfront.Function #15523](https://github.com/aws/aws-cdk/issues/15523)

どうやらCDKのバグで、CloudFront FunctionのID生成に問題があることが分かった。

## 回避策

回避策としては、CloudFront FunctionのID生成はCDKに任せず明示的に指定してやれば良い。

issueに記載されていたjsの実装方法を参考にしながらPythonで実装した。

```py
from aws_cdk import (
    Duration,
    Stack,
    aws_cloudfront as cloudfront,
)

class MyPythonCDKStack(Stack):
    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

cf_function_origin_redirect_id = f"originredirect{self.node.addr}"[:55]　# CDK　で生成するIDの長さが55文字だったため同様に指定した
cf_function_origin_redirect = cloudfront.Function(
    self,
    cf_function_origin_redirect_id, # 明示的に作成したIDを渡す
    code=cloudfront.FunctionCode.from_file(
        file_path="cloudfront_functions/origin_redirect/index.js"
    ),
    function_name=cf_function_origin_redirect_id,
)
```

生成するIDの文字数が長すぎると下記のエラーが発生する。上記の55文字がオススメ。

```py
Resource handler returned message: "Invalid request provided: 2 validation errors detected: Value 'origin_redirect_func_dev_xxxxxxxx
xxx_c8f3291eab8ea63782c49ef250e1f4f76e4c61ea94' at 'name' failed to satisfy constraint: Member must have length less than or equal t
o 64; Value 'origin_redirect_func_dev_xxxxxxxxxxx_c8f3291eab8ea63782c49ef250e1f4f76e4c61ea94' at 'name' failed to satisfy constraint
: Member must satisfy regular expression pattern: ^[a-zA-Z0-9-_]{1,64}$ (Service: CloudFront, Status Code: 400, Request ID: 01d2d320
-abb6-425e-bba8-78d77d3f9700, Extended Request ID: null)" (RequestToken: 3e13c565-4c8f-55e8-d4e2-086ff2791650, HandlerErrorCode: Inv
alidRequest)
```
[小ネタ] nginxで、スラッシュ(%2F)が入る可能性があるpathパラメーターの扱い

# この記事について

対応が解ればシンプルな問題だったんですが、何気にネット上にはそのままの情報が存在せず、自分と同じ事に悩んでる人の問題解決時間短縮になればよいかなと思って投稿します。

# 問題発生する条件

- nginxをリバースプロキシとして配置し、各種サーバーにアクセスを振り分けている
- 起動しているサーバーのアクセスパスが`/api/hoge/{paramfilename}`などpathパラメーターを使用している
- 前述パスに`/api/hoge/paramdir%2Fparam`の様にパラメーターをURIエンコードした状態でアクセスする可能性がある

# 発生する問題

nginxはアクセスuriを正規化したり、エンコードしたりした後に振り分けを行います。その為、`/api/hoge/paramdir%2Fparam`というアクセスがあると、まずは`/api/hoge/paramdir/param`と変換された後に振り分けされてしまいます。当然ながら意図せぬエンドポイントへ振り分けされます。

https://nginx.org/en/docs/http/ngx_http_core_module.html#location

ちなみに以下ページによるとwebサーバによって微妙に動作が違うようです。
> Apache は %2F をURL デコードしないという謎の仕様があるが、nginx にはこの仕様がないので微妙に互換でない。

https://blog.cybozu.io/entry/2015/11/20/080000

# 対応

前述のページにもヒントが記載されていますが、`$request_uri`を活用します。nginx.confのlocationディレクティブに追加します。
これで、`http://nginx/api/hoge/paramdir%2Fparam`のアクセスは`http://target-server/api/hoge/paramdir/param`でなく`http://target-server/api/hoge/paramdir%2Fparam`へ流れます。

```nginx:nginx.conf
    # 例
    location ~ /api/hoge/(.+)$ {
        proxy_pass http://target-server$request_uri;
    }
```

# 教訓

今回はAPI側のソースを修正できない状況だったのでこのような手段を取りましたが、特殊文字が入る可能性のある情報にpathパラメーターを使うのはやめましょう。クエリパラメータやbody使いましょう。

# 参考にさせて頂いたページ

https://blog.cybozu.io/entry/2015/11/20/080000

https://shimonoakio.hatenadiary.org/entry/20110810

https://stackoverflow.com/questions/8264239/nginx-unescapes-2f-to-a-forward-slash-how-can-i-stop-it

https://blog.utgw.net/entry/2020/03/12/121959
WordPressの固定ページでテンプレートが表示されない

WordPressの固定ページ投稿画面でテンプレートが表示されないという現象と出会いました。

# 解決方法
index.php、style.cssを設置する。
下記のように最低限だけでも動きます。
もちろん他のテンプレートファイルを削除してしまった時などのために
最低限の情報が表示されるようにしていれば尚良いと思います。

# ソース

```index.php
<?php
```

```style.css
/*
Theme Name: test-theme
*/
```

# 原因
テンプレートを構成するindex.phpとstyle.cssがテーマフォルダ内にないことが原因でした。
オリジナルテーマを作成していて最近ではこれらのファイルはほとんど表示に使われない
またファイルがなくてもWordPressがテーマを認識してくれるようになったといった背景もあり
削除した状態で開発を進めていたところ前述の不具合に遭遇しました。

今回原因を特定できたのはテーマ選択画面でWordPressがエラーを具体的に吐いてくれていたおかげです。

# 環境
docker公式コンテナのwordpress:5.9.3-php8.1-apacheで発生しました。
Kubernetes CRDのダウングレード

CRDのバージョンを変えたい場合、まず以下の2つのプランがある。

- 方法A: CRDとオブジェクトを全部消して再作成する
- 方法B: 再作成は行わずオブジェクトをダウングレードする

ここでは方法Bについて。

以下のように、`v1alpha1`と`v1beta1`が用意されていて、`v1beta1`から`v1alpha1`に戻す方法を例にする。なお例示のyamlは必要なところ以外省略するので注意。

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
spec:
  group: example.com
  names:
    kind: Example
    listKind: ExampleList
    plural: examples
    singular: example
  versions:
  - name: v1alpha1
    served: true
    storage: false
  - name: v1beta1
    served: true
    storage: true
```

## 1. CRDのストレージバージョンを対象に変える

- `stored: true`になるバージョンは1つだけであるように注意
- まだ新しいバージョンは削除しないように注意

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
spec:
  versions:
  - name: v1alpha1
    served: true
    storage: true
  - name: v1beta1
    served: true
    storage: false
```

## 2. kubectl getで明示的に変換リクエストを発生させる

ストレージバージョンを指定して明示的に変換リクエストを発生させる。ストレージバージョンでストレージされる。

```sh
kubectl get examples.v1alpha1.example.com -A
```

- Conversion Webhookがある場合
    - Webhookで適切に変換されることを確認
    - 変換できない場合は変換可能な状態にするか再作成しかない
- Conversion Webhookがない場合
    - バージョン間でフィールドに互換性がない場合は修正

いずれの場合も、必ずすべてのオブジェクトについてgetを行う。もともと`v1alpha1`で作成されていても、ストレージバージョンが`v1beta1`なら`v1beta1`で保存されているため。

## 3. バージョンを無効化

`served: false`で使用できなくなる。

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
spec:
  versions:
  - name: v1alpha1
    served: true
    storage: true
  - name: v1beta1
    served: false
    storage: false
```

## 4. コントローラの再起動

```sh
kubectl rollout restart deploy/example-contoller
```

まだバージョンは削除しないように注意。

## 5. バージョンの削除

コントローラなどが正常に動作することを確認してから、バージョンを削除する。

```yaml
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
spec:
  versions:
  - name: v1alpha1
    served: true
    storage: true
status:
  storedVersions:
  - v1alpha1
```

kubectlでバージョンを指定しない場合、しばらくエラーが起きる可能性がある。これはAPIサーバが最新バージョン名をキャッシュしているからで、しばらくすれば動くようになる。
【MT7】カテゴリセットの名前＆カテゴリ名を出力する

## はじめに
**MovableType7**
・カテゴリセットに登録したカテゴリを出力させる方法。
・その都度テンプレートのコードを書き換えなくていいようにしたい。


## 変更前のコード
```
<tr>
  <th>坂道グループ</th>
  <td>
    <ul class="list">
      <li class="item"><label><input type="checkbox" name="" value="乃木坂46" class=""><span>乃木坂46</span></label></li>
      <li class="item"><label><input type="checkbox" name="" value="櫻坂46" class=""><span>櫻坂46</span></label></li>
      <li class="item"><label><input type="checkbox" name="" value="日向坂46" class=""><span>日向坂46</span></label></li>
    </ul>
  </td>
</tr>
```
この書き方だと、新しいグループができるたびに「〇〇坂46」を追加する必要があります。


## 変更後のコード
```
<tr>
  <th>坂道グループ</th>
  <td>
    <mt:CategorySets name="坂道グループ">
      <mt:TopLevelCategories>
      <mt:SubCatIsFirst><ul class="list"></mt:SubCatIsFirst>
        <li class="item"><label><input type="checkbox" name="" value="<$mt:CategoryLabel$>" class=""><span><$mt:CategoryLabel$></span></label></li>
      <mt:SubCatIsLast></ul></mt:SubCatIsLast>
      </mt:TopLevelCategories>
    </mt:CategorySets>
  </td>
</tr>
```
カテゴリセットの変更がそのまま反映されるため、コードの修正が不要になります。


## 説明
・**CategorySets**：モディファイア「name="foo"」を指定すると、特定の名前のカテゴリセットを表示できる。
・**CategoryLabel**：カテゴリ名を表示。



## 管理画面　(カテゴリセットの管理)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1104075/c88ba68b-8648-e925-6f65-221295e56f39.png)
今回は「坂道グループ」と「48グループ」がカテゴリセットの名前となります。


## 管理画面　(カテゴリセットの編集)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1104075/3beb09ea-6ef1-dae5-b689-75f013fa26ca.png)
「坂道グループ」に登録されたカテゴリたち。
「乃木坂46」や「櫻坂46」がカテゴリ名になる。


## 出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1104075/13304f19-e887-1ba2-1355-2375054ef03c.png)


## 参考サイト
・[MTCategorySets | MovableType公式リファレンス](https://www.movabletype.jp/documentation/appendices/tags/categorysets.html)
・[MTTopLevelCategories | MovableType公式リファレンス](https://www.movabletype.jp/documentation/appendices/tags/toplevelcategories.html)
・[MTSubCategories | MovableType公式リファレンス](https://www.movabletype.jp/documentation/appendices/tags/subcategories.html)
・[MTSubCatIsFirst | MovableType公式リファレンス](https://www.movabletype.jp/documentation/appendices/tags/subcatisfirst.html)
・[MTSubCatIsLast | MovableType公式リファレンス](https://www.movabletype.jp/documentation/appendices/tags/subcatislast.html)
1on1ミーティングでの話題・テーマに困ったらどうする？ (エンジニアの自社メンター)

# 1on1の話題がないのではないか？という悩みがある

話題がないと気まずい。
もちろん無理に引き伸ばす必要はないし、話題がなければ早めに終われば良いのだけれど。

どうせ時間を作るなら話を引き出して、有益な時間にしたい。それがメンターの役割ではないだろうか。

# 1on1の話題を1on1で作る

たとえば1人のメンバーがプログラミングの学習方法で悩んでいるとする。
その時メンターからアドバイスを直接することも出来るが、他のメンティに学習方法について聞いてみる事もできる。

これで新しい話題ができ、メンティの役にもたつ、自分の知見も広まる。なんとも一石二鳥ではないか？

# テーマの種類をリストにしておく

思いついたことはリストにしておいて、誰にでも話せるようにしておく。

- 開発全般について、今のプロジェクトの状況など
- スキル戦略、今後、どんなスキルを伸ばしていきたいか、技術的な好き嫌いはあるか
- キャリアプラン、エンジニア1本でやっていきたいか、マネージャーなどを目指したいかなど
- プログラミングの学習方法
- 開発に直接関わらない集中法や休息法、ハックなど
- 他の開発メンバーをどんな人だと思うかと聞いてみる
- 他の開発メンバーとの関係性、コミュニケーションについて聞いてみる
- 前職があれば前職について聞いてみる
- 健康状態、疲れていないか、負荷が高すぎないかどうか、よく眠れているか。
- 開発ツールについて聞いてみる
- メンターが知らないことがあれば、聞いてみる。むしろ教えてもらうぐらいの気持ちで、自分自身の知見を広め、相手の理解も深めてみる。
- 給与に満足してるか、不満かどうか。

# 私はメンター初心者

今は自社の若手メンバーと月1の1on1ミーティングをしている。
だがまだ「相手の時間を浪費しているのではないか」「本当に価値を生み出せているのか」という悩みがある。

客観的に考えれば価値があることは明らかな気がするのだが、各人に本当にそれを実感してもらえるかどうか、自分がそう感じられるかどうかがネックだ。とにかく手探り状態。道なきところに道を探すような、なんとも落ち着かない感覚もある。

つまり僕は難しいことにチャレンジしている、ような気がする。
エンジニアリングもメンタリングも試行錯誤、挑戦だ！

<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
Zscalerをインストールしている端末から起動したiOSシミュレーターが、インターネットに通信できない場合の対処方法

ログに下記ようなZscaler関連のエラーが出ている場合は
端末側のZscalerの設定をオフにすれば通信できますが、めんどくさいorオフにできない状況の場合

```
Task <9C272777-1A81-41A9-8D93-014088720B0D>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, NSErrorPeerCertificateChainKey=(
    "<cert(0x7fc639047e00) s: qiita.com i: Zscaler Intermediate Root CA (zscaler.net) (t) >",
    "<cert(0x7fc639048c00) s: Zscaler Intermediate Root CA (zscaler.net) (t)  i: Zscaler Intermediate Root CA (zscaler.net)>",
    "<cert(0x7fc639042c00) s: Zscaler Intermediate Root CA (zscaler.net) i: Zscaler Root CA>"
```



1. Keychain Access.appを開きZscaler Root CAの証明書を選択
2. 右クリックしてメニューから書き出す
![スクリーンショット 2022-06-21 16.21.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1734199/b0af5407-3966-b043-6e8f-935d427816c9.png)

3. 書き出したZscaler Root CA.cerファイルを起動しているシミュレーターにドラッグアンドドロップ
![スクリーンショット 2022-06-21 16.24.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1734199/f8d8e0ac-9d82-2f21-927e-c4ec3f5a5d2c.png)

4. シミュレーターのSettingsアプリから General -> About -> Certificate Trust Settingsに "Zscaler Root CA"が追加されているのでオンにする
![Simulator Screen Shot - iPhone SE (3rd generation) - 2022-06-21 at 16.27.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1734199/bdff3bfe-9de2-b9e4-7ec8-05240c9ff725.png)


これで通信できるようになります。シミュレーターにインストールした証明書は基本的に消せないので注意してください。
【Rails】【いいね数】1人のuserの全いいね数を取得する方法

先日ポートフォリオを作成していた際に、少し悩んだことを復習がてら書いていきたいと思います。

### 【環境】
- Rails 6.1.5.1
- ruby 2.6.3

### 【前提】
- 投稿機能を実装済み（この記事では、Postモデル）
- いいね機能を実装済み（この記事では、Favoriteモデル）

### 【リレーション】
- UserとPost(１対多)
- UserとFavorite(１対多)
- PostとFavorite(１対多)

### 【実現したいこと】
　タイトル通り（少し分かりにくい日本語かも）ですが、ある1人のuserの全投稿に紐づく全てのいいね数を表示させたいと思います。
　userの詳細ページを作成している際に、この全いいね数を表示させたいなと思ったのがきっかけです。

### 【方法】

#### ①userの全投稿数を取得（関係ないですが、復習のため）
　これは簡単ですね！countメソッドを使って数えます。
```ruby
user.posts.count
```
#### ②次に、１つのpostに紐づくいいね数

```ruby
post.favorites.count
```

#### ③では、本題の取得方法です！
色々試した結果、下記のコードで全いいね数の取得ができました。

```ruby
user.posts.inject(0) { |sum, post| sum + post.favorites.count }
```

#### ④「inject」とは
injectは繰り返しの計算ができるメソッドです。使用例を見てみましょう！

```ruby
(1..6).inject(0) { |sum,num| p sum+=num }

実行結果
1
3
6
10
15
21
```
　1~6までの数字をどんどん足していっていることが分かります。injectの右にある「0」は、「sum」に代入される初期値です。なので、他の数字にすると実行結果も変わっていくると思います。「num」は(1..6)の一つ一つの要素になります。
　ちなみにeach文でも書くことができますが、スッキリしたコードにしたいと思った結果injectに辿り着きました。

```ruby
#eachバージョン（同じ結果になるはずです！）
array = 1..6

sum = 0
array.each do |num|
  sum += num
  p sum
end
```

#### ⑤最後にもう一度③のコードを見てみましょう！
```ruby
user.posts.inject(0) { |sum, post| sum + post.favorites.count }
```
1.user.posts　→ userの全ての投稿を用意する。
2.(0) → sumの初期値を0にする。
3.|sum, post| → sumは足した合計を入れていく為のもの。postはuser.postsの中の一つ一つのpost。
4.sum + post.favorites.count → 一つ一つのpostのいいね数を取得し、sumに足していく。
5.完成！！

### 終わりに

railsの勉強だけでなく、rubyのコードを書けるようにすることも大事だなあと感じました。
最後までご覧いただきありがとうございました！！
[Refactoring]多くなったPrivateメソッドはクラス化できる！！

どうも、「病院なび」の開発チームメンバー甘利です。
今日はリファクタリングについて書こうと思います。

うちのホープの@KON-ch(こん)が勉強会に参加して「多くなったPrivateメソッドはクラス化できる！！」というのをもたらしてくれました。
確かにそうだわ、、と納得したので早速使っていきたいと思います。

# そもそもなぜPrivateメソッド達をクラスにする必要があるのか
単体テストの範囲を細かくできます

# Privateメソッドがたくさんある実装
以前書いた　[自社サービス「病院なび」へ AWS Opensearch を導入したい](https://qiita.com/namarin/items/533e4297f05d23f198b3)を実装した際のクラスをリファクタリングしたいと思います。この`IndexingService`というクラスは社内での運用に合わせて　Opensearch　のインデックスを簡単に管理する目的で実装しています。簡単に管理する一環としてIndexを作成する際に、指定されたインデックス名に日付を表すサフィックスを付与し`index_name_20220624`の様な名前にして作成します。このサフィックスを生成するメソッドはプライベートメソッドとして実装されています。

```　indexing_service.rb
class IndexingService
  def create(index_prefix)
    @client.indices.create(
      index:  index_with_time(index_prefix),
      body:   schema_json(index_prefix)
    )
  end       

  private

  def time_suffix
    Time.now.strftime("%Y%m%d-%H%M%S")
  end

  def index_name(name_prefix)
    [name_prefix, time_suffix].join("_")
  end
end
```

よくよく考えなくても、この`index_name`　はただ名前を表す`String`を返すので　`IndexingService` になくて良いですよね。
ということで、Class化していきたいと思います。

# Class化してみ

無事 `IndexingService` の `index_name` メソッドが見事 `IndexingService::Name` のパブリックメソッドとなりました。
（テストコードなんていらない！！とは　t-wada　さんの前では言えないので、見つからないことを祈ります。）

```　indexing_service/name.rb
class IndexingService::Name
  def initialize(prefix)
    @prefix = prefix
  end

  def index_name
    [@prefix, time_suffix].join("_")
  end
  
　 private

  def time_suffix
    Time.now.strftime("%Y%m%d-%H%M%S")
  end
end
```

元々の呼び出し元であったIndexingService::Name は以下の様な感じで呼び出しています。
クラスからprivateメソッドをなくなりすっきりしましたね！

```　indexing_service.rb
class IndexingService
  def create(index_prefix)
    name = Name.new(index_prefix)　#Nameクラスを作るように変更
    @client.indices.create(
      index:  name.index_name,
      body:   schema_json(index_prefix)
    )
  end       
end
```

# Builder化
無事Privateメソッドをクラス化できました。しかし、まだまだ違和感を感じます。
*　.index_name　メソッドを呼び出した際に日付を取得しているため、同じメソッドを呼んでいても違う値が戻される
*　インスタンスを作成する必要がない（Builderパターンにする）
といった点が気になりまね。そこで以下みたいな感じに修正してみす。

```　indexing_service/name_builder.rb
class IndexingService::NameBuilder
  def build_with_prefix(prefix)
    [prefix, time_suffix].join("_") 
  end

  private

  def time_suffix
    Time.now.strftime("%Y%m%d-%H%M%S")
  end
end
```
```　indexing_service.rb
class IndexingService
  def create(index_prefix)
    @client.indices.create(
      index:  NameBuilder.build(index_prefix),
      body:   schema_json(index_prefix)
    )
  end       
end
```

すいっきりしましたね！！！

# まとめ
今回はいくつかの`Privateメソッドのクラス化することでリファクタリングを行いました。「Publicメソッドのみテストを書く」という方針で開発をおこなっているため、Privateメソッドが増えてくるとテストしにくいなぁ、、、なんて思っていたのですが、今後は悩まなくても済みそうです。

少しでも面白いと思っていただけたら　LGTM お願いします！
またよければフォローいただけると幸いです！！

異論は認めませんがアドバイスは受け付けてます、それでは。
WSL2インストール後に仮想マシンの起動に失敗する

Windows11 + WSL2によって仮想環境のインスストールが簡単になったとのことで、テストしてみたところ、「WslRegisterDistribution failed」と出て、動かなくてハマったのでメモです。

## 環境
- Windows11 Home (21H2)
- Ryzen7 5800HS

## WSLインストール

管理者モードで以下のコマンドでWSL環境をインストールします。
```
wsl --install
```
デフォルトでubuntuマシンがインストールされます。

## バージョンチェック

以下のコマンドでバージョンがわかります。
```
wsl -l -v
```

## 実行&エラー

再起動後に```ubuntu```と打って実行すると以下のエラーが出ます。
```
> ubuntu
Installing, this may take a few minutes....
WslRegisterDistribution failed with error 0x80370109
...
```
## チェックポイント
### BIOS(UFEI)設定

BIOS(UFEI)で「SVMモード」がEnableになっているの確認します
機種によって異なりますので、頑張って探しましょう。

### Windows機能の有効化

それでも起動しない場合は「Windows機能の有効化」メニューから「Windowsハイパーバイザープラットフォーム」のチェックがONになっているか確認してください。

![スクリーンショット 2022-06-21 160717.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14768/21882eb1-0a1b-fbc5-e490-a345de59708f.png)
GetとPostの違い

### Get? Post?
IT勉強を始めてから結構耳にしたGetメソッドとPostメソッド。

なんとなくはわかっていたがモヤモヤしているので頭の整理のため記録します。

今までGetはhttp://a.com?id=kj960&pass=001122　
みたいにURLの後ろに？とアドレスが付くが、Postはそれが付かないくらいの理解で、
どんな時Getを使い、またどんな時Postを使えばいいかは分かりませんでした。
***


### GetとPostの目的
英語の意味を考えると少しは簡単になります。

* Get
英語のまま、持ってくるという意味です。これをHTTPに適用してみると、GetはServerからあるデータを持ってきて見たり検索をかける時に使用することになります。
データの修正などはできません。修正をするためには修正したデータをServerに送ってそ変更させる作業が必要ですが、Getは何かを送ることができないからです。
なので、Getは掲示板の目録を見たり、投稿の内容を読むとき使います。

* Post
送るという意味です。これをHTTPに適用してみると、Postは新しいデータを作成したり、既存のデータを修正、アップデートしてServerに送るとき使用することになります。
なので、Postは掲示板に投稿したり、自分の書いた投稿の内容を修正するときに使います。

***
### まとめ
Getはデータを読むときと、検索するとき使うメソッド
Postはリソースを作成、アップデートするためServerにデータを送るとき使うメソッド
WKCに登録したデータをWatson Studioの分析プロジェクトに登録する

WKCに登録した「接続」や「接続された資産」（データ）をWatson Studioの分析プロジェクトに登録ができます。

この機能をつかうことで分析者は接続に必要なURLやユーザー、パスワードなどを知らなくても、データにアクセスが可能になります。

*テスト環境
*CP4D 4.0.9

# 1. カタログからデータを探す

まず、「すべてのカタログ」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/954ac89c-8f8a-aeaa-7e64-c42a651fde14.png)

カタログを開きます。ここでは「2022」という名前のカタログを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/71d03e16-5f9a-8c0a-f992-b2b480243999.png)


資産から検索などをして参照したい「データ」や「接続」を選びます。
ここでは「01TRIP」というデータを選んでいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1d9351d3-907c-65fb-8fb7-e58ff8792651.png)

このデータを参照したいプロジェクトを選び、「追加」を押します。この例ではTestProjectというプロジェクトに追加をしています。
選択された資産である「01TRIP」というデータに加えて、このデータが存在するRDBへの「接続」である「DB2WHOC_HDM」という接続も自動的に追加対象になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/eabcc93f-a353-03bf-e13f-95ea17bd9e5e.png)

追加をするとプロジェクトに移動するというボタンがでるのでこれをクリックすることでも追加先のプロジェクトに移ることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/51a8caab-749b-e5b6-03b8-7203dbfdca30.png)

ただ、このメッセージは追加したときにしか出ないので、今回はメニューからたどってプロジェクトに移動します。

# 2. 分析プロジェクトで追加されたデータを確認
メニューから「すべてのプロジェクト」を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e527cff0-51a3-f7d9-d1b3-38e0d5b7432b.png)

「データ」を追加したプロジェクトを選びます。この例では「TestProject」です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/16e5f124-652f-540b-5f28-f8ce7c9598a6.png)

プロジェクトに入って、「資産」タブを開くとWKCから追加した「01TRIP」というデータと「DB2WHOC_HDM」という接続がデータ資産として登録されていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/3e8a963a-3863-995b-4620-c4460b0a9b64.png)

クリックするとプレビューも見ることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/8a491439-c2f7-f372-1589-6b2f272647f0.png)


これで、データの位置やユーザー、パスワードを知らなくても、ModelerFlowやJupyterからデータの参照が可能になりました。
Mr.Nam 先生の講義要約 Chapter 2, 27-35

27番　例外処理(1)-実習

ExceptionController.java
メソッドの前に@ExceptionHandlerを付けてtry-catchのcatchのように使用可能。ただし、同じクラス内で使用。
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

28番　例外処理(2)-理論
@ExceptionHandlerと@ControllerAdvice
例外処理のためのメソッドを作成し、@ExceptionHandlerを貼り付ける。

@ControllerAdviceで全域例外処理クラスを作成可能(パッケージ指定可能)
例外処理メソッドが重複の場合、コントローラ内の例外処理メソッドが優先。

@ResonseStatus
応答メッセージのステータスコードを変更する際に使用

例外処理メソッド
例外処理をすると200(成功)が出る。-->405に変更

ExceptionResolver
ばねの例外処理基本戦略
handlerExceptionResolver
1. ExceptionHandlerExceptionResolver
2. ResponseStatusExceptionResolver
3. DefaultHandlerExceptionResolver

スプリングでの例外処理
コントローラメソッド内でtry-catchとして処理
コントローラに@ExceptionHandlerメソッドが処理
@ControllerAdviceクラスの@ExceptionHandlerメソッドが処理
例外種類別にビュー指定-SimpleMappingExceptionResolver
応答状態コード別にビュー指定-<error-page>-web.xml

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

29番　DispatcherServletパヘチギ
SpringMVCの要請処理過程
HandlerMapping, HandlerAdapter
HandlerMappingはMapの形で(key=URL,　value=メソッド)保存しておいて、
リクエスト("ch2/register/add)が来たらDispatcherServletが受け取り
HandlerMappingからURLに合ったメソッドを探し、メソッド情報をもらって
そのメソッドに合ったHandlerAdapterを探す。
そのHandlerAdapterと接続されたタブレットやコントローラで接続。
HandlerAdapterにより緩めの接続==>変更に有利

DispatcherServletのソース分析
DispatcherServlet.classはspring-webmvc-5.0.7.RELEASE.jarに含まれる。
ソースファイルの位置-org/springframework/web/servlet/DispatcherServlet.java
基本戦略-org/springframework/web/servlet/DispatcherServlet.properties
(基本戦略:基本的に使用するクラス)

主要メソッド
initStrategies()-基本戦略を初期化
doService()-doDispatch()呼び出し
doDispatch()-実際のリクエストを処理
processDispatchResult()-例外が発生しているかを確認、発生していなければrender()呼び出し
render()-応答結果を生成して送信

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

30番　データの変換と検証(1)
PropertyEditor

双方向タイプ変換(String->タイプ·タイプ->String)
特定タイプや名前のフィールドに適用可能
デフォルトPropertyEditor-スプリングが基本的に提供。
カスタムPropertyEditor-ユーザーが直接実装。 PropertyEditorSupportを継承すると便利。
すべてのコントローラ内での変換-WebBindingInitializerを実装し、登録
特定コントローラ内での変換-コントローラに@InitBinderが付いたメソッドを作成

Converter, ConversionService
Converter

単方向タイプ変換(タイプA→タイプB)、stateless(iv使用X)。
PropertyEditor(両方向)の短所を改善(stateful(iv使用)→stateless)
ConversionService()

タイプ変換サービスを提供。 複数のConverterを登録可能。
WebDataBinderにDefaultFormattingConversionServiceが基本登録
すべてのコントローラ内での変換-ConfigurableWebBindingInitializerを設定して使用
特定コントローラ内での変換-コントローラに@InitBinderが付いたメソッドを作成。
Formatter
双方向タイプ変換(String->タイプ·タイプ->String)
バインディングするフィールドに適用-@NumberFormat、@DateTimeFormat

PropertyEditor:双方向
Converter:単方向
Formatter:双方向、@NumberFormat、@DateTimeFormat

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

31番　データの変換と検証(2）
Validator
オブジェクトを検証するためのインタフェース。 オブジェクト検証器(validator)の実装に使用

Validatorを利用した検証-手動
コントローラメソッド->検証という関心事に分離
Validatorを利用した検証-自動
@Valid

グローバルValidator
一つのValidatorで複数のオブジェクトを検証する際、グローバルValidatorとして登録


ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

32-35番はもうおわったことです。
DatadogでEC2内部のログを監視する

## What's This
DatadogでEC2内部のログを監視対象にして、任意の文字列を検知したらアラート、という
仕組みを実装したのでメモです。

## EC2設定

```
# 適当なディレクトリを作成する
sudo mkdir /etc/datadog-agent/conf.d/error_log.d/

# そのディレクトリ配下にconf作成し、編集
sudo vi /etc/datadog-agent/conf.d/error_log.d/conf.yaml

init_config:

instances:

logs:
  - type: file
　　　　　　 # 拾いたいファイル対象を記載する。
    path: /var/www/html/error*
　　　　　　 # DD logs上で表示するサービス名を記載（定義）する。
    service: error_log
　　　　　　 # DD logs上で表示するサービス名を記載（定義）する。
    source: error_log
    log_processing_rules:
      - type: include_at_match
        name: include_datadog_messages
　　　　　　　　# 拾いたい文字列を記載する。
        pattern: error|Error|

# datadog.yamlを編集
sudo vi /etc/datadog-agent/datadog.yaml

# logs_enabled というパラメータを true にする
logs_enabled:true

# 再起動
sudo systemctl restart datadog-agent

# チェック
sudo datadog-agent status

```

```
Getting the status from the agent.

==========
Logs Agent
==========

    Reliable: Sending compressed logs in HTTPS to agent-http-intake.logs.datadoghq.com on port 443
    BytesSent: 0
    EncodedBytesSent: 0
    LogsProcessed: 0
    LogsSent: 0

  error_log
  -------------------
    - Type: file
      Path: /var/www/html/error*
      Status: OK
　　　　　　　　　　　　# ここの** filesで、対象のファイル数がいくつかわかる。
        52 files tailed out of 52 files matching
      Inputs:
        /var/www/html/error2022-XX-XX.log
　　　　　　　　　　　　〜省略〜（対象の52ファイルが羅列される）
      BytesRead: 0
      Average Latency (ms): 0
      24h Average Latency (ms): 0
      Peak Latency (ms): 0
      24h Peak Latency (ms): 0
```

## Monitor設定

算定式的には、以下の感じ。
上記で設定したSerivce名を、クエリに算入する。

```
Service:error_log error（検知したい文字列を明示的に書く）（書かなくてもOKかもしれませんが）
```

実際に、以下でログを流し込むと、検知しました。（モニタークエリは若干上記で設定したものとは異なっています）

```
vi /var/www/html/error*
error
```

![スクリーンショット 2022-03-23 18.27.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1740345/2186895c-efdd-5751-c0c8-cddc7dc50f7d.png)


## Reference

・https://docs.datadoghq.com/ja/getting_started/logs/
・社内有識者

参考になりました。ありがとうございます。
[備忘] Dockerのイメージビルドが失敗した(ls: cannot access '.' : Operation not permitted)

備忘のために。

## 起きたこと

Dockerコンテナのビルド時 `docker-php-ext-install pdo_mysql` をRUNしたら掲題のエラーが発生。
ベースイメージは`php-fpm:7.3`。
lsコマンドがエラーになっていた。

## 参考

[こちらのページ](https://www.creationline.com/lab/46861)とほぼ同様の事象が起きていたので丸々参考に。
ありがとうございます。

## 原因と対処

- ベースイメージ内のlsコマンドがxstatというシステムコールを利用していた。
  - lsのバージョンが8.32で、ちょうど切り替わったバージョンらしい。
- ホストマシンにインストールされていたDockerのバージョンが18.xxで、そのSeccompにxstatが許可されていなかった。
- さらにカーネルとDockerを仲介するlibseccompもxstatに対応していなかった。
- 両方のバージョンを上げることで対処。
Anaconda環境下にインストールしたPyMOL(Open Source) 起動時にエラー「qt.qpa.plugin: Could not find or load the Qt platform plugin "windows" in ""」

最終更新 22/07/08

## 環境
- Windows 11
- Anaconda
  - Python 3.9.12
  - Anaconda-Navigator 2.1.4
  - conda 4.13.0

## やったこと
すでにconda-forgeがチャンネルに入っている場合は省略
~~~
conda config --add channels conda-forge
~~~
ここから本題
~~~
conda install pymol-open-source
## Solving environment後 Pythonなどのバージョンに合ったものが選ばれる
> Proceed? ([y]/n) ## yと答えるか何も打たずにEnter
> done
~~~

PyMOLの起動
~~~
pymol
> "C:\Anaconda3\Scripts\\..\python.exe" "C:\Anaconda3\Scripts\\..\Lib\site-packages\pymol\__init__.py"
> could not find or load the Qt platform plugin "windows" in ""
~~~
というエラーが出た
どうやらPyQt5プラグインを探せないエラーっぽい

## 試したけどダメだったこと
Anacondaをインストールした際にデフォルトで付いてくるPyQt5は
Spyder等を起動するのに必須らしいが
conda listで確認するとpypiチャンネルから(pip install ~~)のものらしい

~~~
pip uninstall PyQt5 PyQt5-sip PyQtWebEngine
conda install pyqt
~~~
でcondaのチャンネルから新しく入れたがエラーは特に変わらず

## 解決法その1
別のパッケージでも同じエラーが出るらしい
https://qiita.com/sukoppu1102/items/f5c00b520da0fa5d2ff3

https://qiita.com/sukoppu1102/items/f5c00b520da0fa5d2ff3

ただし 記事の通りにシステム環境変数に「QT_PLUGIN_PATH」を追加すると
他のQtベースのUIを使用しているWindowsのアプリケーション(LINEなど)が
競合を起こして正常に機能しなくなる

そこで以下の記事を参照
https://zenn.dev/eqs/scraps/714b7889323109

https://zenn.dev/eqs/scraps/714b7889323109

この記事の通りにシステム環境変数に以下の値を追加

変数名： QT_QPA_PLATFORM_PLUGIN_PATH
変数値： C:\Anaconda3\Library\plugins　　(自身のAnacondaインストール先フォルダを参照)

これで再度ターミナルからPyMOLを実行したところ
無事に起動しGUIが表示されました

しかしこれでも一部のアプリケーションが不具合を起こす場合もあります
その場合は解決法その2を試してください

## 解決法その2
最初に実行されるファイル「\_\_init\_\_.py」を直接編集します

プロンプト上でpymolを実行したときに表示されるファイル
筆者の環境ではこれ → "C:\Anaconda3\Lib\site-packages\pymol\\_\_init\_\_.py"

適当なエディタで開くと
「setup_environ」
という名前で定義されている関数があると思います

その関数内の最後に以下を追加
~~~
def setup_environ():
    ~~
    省略
    'PYMOL_PATH'に色々パスを追加している内容が書かれている
    ~~
    plugin_path = 'C:/Anaconda3/Library/plugins' ## 自身のAnacondaインストール先フォルダを参照
    os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path
~~~
こうすることで
\_\_init\_\_.py を実行した時にのみ
一時的に環境変数に'QT_QPA_PLATFORM_PLUGIN_PATH'が追加されるため
他のアプリケーションと競合を起こさないはずです

(最初からpluginフォルダを探すようにデフォルトでなっていて欲しいですが…)

---
補足
~~~
conda install -c schrodinger pymol-bundle
~~~
の方はSolving environmentの結果がfailedになってインストール失敗

これはおそらくPythonが3.9だからだと思います
PyMOLをEXEでインストールする場合はPython3.7が付いてくるので
参考：https://pymol.org/2/


コメントより追加補足
conda create -n test
conda install -c schrodinger pymol-bundle -n test
として 新たに作成した仮想環境にpymol-bundleをインストールをしようとすると
Pythonのバージョンは3.8.13になるようです (2022/07/01 現在)
Pythonファイルを超簡単にexeファイル化するGUIソフト

**auto-py-to-exe**というpyinstallerをGUI化したソフトが便利です。書くのが煩わしいspecのadd-data等をマウス操作で簡単に追加できちゃいます。

---

操作のデモがこちら
![68747470733a2f2f6e6974726174696e652e6e65742f706f7374732f6175746f2d70792d746f2d6578652f6175746f2d70792d746f2d6578652d64656d6f2e676966.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282788/b53d6160-5efb-c4b0-ecdf-b7343a441f0b.gif)

---

auto-py-to-exeのレポジトリはこちら

https://github.com/brentvollebregt/auto-py-to-exe

pyinstallerのspecの書き方はこちら

https://pyinstaller.org/en/stable/spec-files.html

# インストール方法

```
$ pip install auto-py-to-exe
```

# 実行方法

```
$ auto-py-to-exe
```

# exe化する前に読んでおきたい

[良いコード悪いコードで学ぶ設計入門 保守しやすい 成長し続けるコードの書き方](https://amzn.to/3PLRnqz)


<a href="https://www.amazon.co.jp/%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89-%E6%82%AA%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%81%A7%E5%AD%A6%E3%81%B6%E8%A8%AD%E8%A8%88%E5%85%A5%E9%96%80-%E2%80%95%E4%BF%9D%E5%AE%88%E3%81%97%E3%82%84%E3%81%99%E3%81%84-%E6%88%90%E9%95%B7%E3%81%97%E7%B6%9A%E3%81%91%E3%82%8B%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9-%E4%BB%99%E5%A1%B2/dp/4297127830?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=1GD8D2NUA2H2O&keywords=python+%E3%82%AA%E3%83%A9%E3%82%A4%E3%83%AA%E3%83%BC&qid=1659221729&sprefix=python+%E3%82%AA%E3%82%A4%E3%83%AA%E3%83%BC%2Caps%2C217&sr=8-42&linkCode=li2&tag=hurryth30-22&linkId=51d484011547562ee3a7a9c4ef25b416&language=ja_JP&ref_=as_li_ss_il" target="_blank"><img border="0" src="//ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=4297127830&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=hurryth30-22&language=ja_JP" ></a><img src="https://ir-jp.amazon-adsystem.com/e/ir?t=hurryth30-22&language=ja_JP&l=li2&o=9&a=4297127830" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
オリジナルアプリ作成進捗（スクレイピング処理記述もあと少し）

オリジナルアプリ作成も終盤に入り、スクレイピング処理の記述ももう少しのところまで来ています。

# スクレイピング処理のコードはこんな感じです。
```ScrapingController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Goutte\Client;
use App\Models\Playerdata;


class ScrapingController extends Controller
{
    public function scraping1()
    {
        $client = new Client();
        $crawler = $client->request('GET', 'https://baseball.yahoo.co.jp/npb/teams/3/memberlist?kind=p');
        $info = $crawler->filter('.bb-playerTable__row')->each(function ($tr) {
        $data = $tr->text();
      
        return $data;
        });
        foreach($info as $data) {
            
            // $playerdata = new Playerdata;
            // $playerdata->playername = $data[1];
            $playerdata = explode(" ", $data);
            // print_r($playerdata);
            dump($playerdata[4]);
            
            $playerdata = Playerdata::select('playername', 'ining', 'balls', 'hit_by_a_pitch', 'by_homeruns', 'wins', 'loses', 'saves', 'resp_points', 'lost_points', 'saved_adv')->get();
            // dd($playerdata);
            // $playerdata->save();
        };
        return view('pitcher');
    }    
    
    
     public function scraping2()
     {
        $client = new Client();
        $crawler = $client->request('GET', 'https://baseball.yahoo.co.jp/npb/teams/3/memberlist?kind=b');
        $info = $crawler->filter('.bb-playerTable__row')->each(function ($tr) {
        $data = $tr->text();
      
        return $data;
        });
        foreach($info as $data) {
            $playerdata = explode(" ", $data);
            //print_r($playerdata) ;
            dump($playerdata[4]);
            
            $playerdata = Playerdata::select('playername', 'times_at_but', 'hit', 'hit_point', 'hit_adv')->get();
            
            // $playerdata->save();
            
        };
        return view('butter');
    }
    
}
```
未完ではありますが、ここまで書きました。

# ここから分からないこと
上のコードだと、データを持ってきたときに、すべてのデータを取得できずデータベースの追加・更新が出来ません。

データベースの追加・更新が出来るコードが書ければ、ほぼ完成にはなるんですが。（これがわからない）

みなさんはお分かりになるでしょうか？
Macに存在する動画や画像、音声ファイルをiPhoneで利用する

## はじめに

自宅の環境がAppleに支配された方向けの便利な利用方法の紹介です。

Macのディレクトリにファイルが入ってるけどPCのところに行くのがめんどくさいことってたくさんあると思います。

いや、絶対あります。

そんなときにサクッとiPhoneから見たり聴いたりできたら最高だなと。

当然自分にもそんなときがあって、ふとiPhoneに入っている普段使わない Apple の標準アプリを見ていたら存在しました。

**ファイル**という非常に安易なネーミングがつけられたアプリが！！

これは使うしかないということで利用したら非常に便利だったので紹介します。

## 1. ファイル App とは

[^1]
![貼り付けた画像_2022_06_21_15_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/20052243-7130-6793-5fab-e3735d4cfe0c.png)

iPhoneユーザーならどこかに紛れ込んでいると思います。
もしくはAppライブラリの検索で「**ファイル**」と入力すれば出てきます。

## 2. Mac側の設定

ファイル Appを利用する前にMac側で設定が必要になります。

![システム環境設定 - 共有](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/73b4ef38-991e-2e28-c549-64a6d157de7a.png)

![共有 - 共有フォルダ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/3bc9d67d-4434-dc05-14d1-f5c51403a984.png)

フォルダ共有を設定しましょう。

ここに `smb://〜` と書かれているものがローカルアドレスになります。
覚えておいてください。

## 3. ファイル App を使う

はじめにアプリを起動します。

![ファイル App 右上](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/9a14a008-2232-ad19-3c34-f4cb3fb5d713.png)

すると右上にメニューがあるのでそれを押下します。

![サーバへ接続](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/7884e94a-3194-0390-1c4a-21a775ba636a.png)

**サーバへ接続**というメニュー項目があるのでそれを押下します。

![サーバIP入力](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/fb084316-7532-81f0-2757-f350f73d8b4b.png)

先程のサーバアドレスの `smb://` 以下を入力して**接続**を押します。

![名前とパスワード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/ce8d008e-3166-62e3-659b-abb649a22401.png)

接続する際に**ユーザの種類**が問われます。
登録ユーザを選択してMacのアカウント情報を入力しましょう。

![接続完了](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30241/e3d9e4dc-2f07-5229-10b7-0c2064616f1d.png)

アカウント情報が認証されて、各ディレクトリが表示されたらほぼ完了です。

登録ユーザなので外付けHDDの中身とかも閲覧可能です。

ここから目的のファイルまで辿ることができれば完了です。

## おわりに

今までは**Google Drive**で同期して利用していたのですが、家にいるのに一度Googleにデータを預けるのもリスクしかないので今回の方法に至りました。

世界的にインターネットは規制まっしぐらなので余計なものは介さずにローカルで解決することをおすすめします。

たまに外付けHDDのファイルを認識してくれなくことがあるため解決策を模索中です。

[^1]: 下部のぼやけているアプリは戦車道大作戦とスパロボDDです。どちらも面白いのでプレイしてみてね。
Raspberry Pi 4 で 温湿度気圧センサー（BME280）の測定結果を出力する

RaspberryPi4に温湿度気圧センサーのBME280を接続、測定結果を出力した。

## はじめに
サンプルコードはSWITCH SCIENCEのサンプルプログラム（bme280_sample.py）を使う。
※[GitHubはこちら](https://github.com/SWITCHSCIENCE/samplecodes/blob/master/BME280/Python27/bme280_sample.py)

**wgetで取得**
```
$ wget https://raw.githubusercontent.com/SWITCHSCIENCE/BME280/master/Python27/bme280_sample.py
```

## 準備

**配線**
Raspberry Pi 4との配線は配線図の通りに実施。
具体的には、以下の通り。

```
SDI      (BME280)  -> GPIO2 P03 (Raspberry Pi SDA1)
SCK      (BME280)  -> GPIO3 P05 (Raspberry Pi SCL1)
GND,SDO  (BME280)  -> GND  P09 (Raspberry Pi)
Vio,CSB  (BME280)  -> 3.3v P01 (Raspberry Pi)
```

以下のコマンドで 0x76 が接続アドレスであることがわかる。

```
$ sudo i2cdetect -y 1
```
:::note warn
0x76じゃない場合はサンプルコード修正必要あり
:::

**モジュールインストール**

```
$ sudo apt-get update
$ sudo apt install -y python-smbus
$ sudo pip install smbus2
```

Raspberry Pi から I2CをPythonで制御するため、smbus2をインストール。
私はこのとき、赤外線アレイセンサ（AMG8833）を使った後だったため、smbus2が無いと以下のエラーになった。

```
File "/home/hogehoge/bme280_sample.py", line 3, in <module>
    from smbus2 import SMBus
ModuleNotFoundError: No module named 'smbus2'
```

**サンプルコード修正**

サンプルコードはPython2.7で作られており、Python3で実行すると以下のエラーになる。

```
  File "/home/hogehoge/bme280_sample.py", line 95
    print "pressure : %7.2f hPa" % (pressure/100)
          ^
SyntaxError: invalid syntax
```

Python3で実行するため、以下のように修正する。
修正箇所は、95行目、103行目、117行目。
```
修正前）　print "pressure : %7.2f hPa" % (pressure/100)
修正後）　print ("pressure : {:7.2f} hPa".format(pressure/100))
```

## 実行

```
$ sudo python3 bme280_sample.py 
temp :  24.04 ℃
pressure : 1007.90 hPa
hum :  50.68 ％
```
Modeler FlowでSQLプッシュバックを確認する

CP4DのModeler FlowではSQLプッシュバックの機能でノードの処理をSQLにしてRDBで実行することができます。
どんなSQLが実行されているかを確認する方法を紹介します。


* テスト環境
* CP4D 4.0.7

「フロー・プロパティ」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/9c9e956a-85d6-723d-4681-952423a03b66.png)

「ロギング」のタブを開き、「実行時にSQLをメッセージ・ログに表示」と「SQLを再フォーマットして読みやすくする」にチェックを入れ、保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/51c2f83c-4c23-83ee-0612-8efe178c8880.png)

そしてストリームを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/8a974298-1a2b-8f6c-ae51-73f8f89114b5.png)

再度「フロー・プロパティ」を開き、「メッセージ」を開きます。そうすると実行したSQLが記録されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/828fdfce-477e-dd3e-5573-0e4197490a89.png)
[jinja2]パラメータで渡したファイルをincludeする

以下の通りパラメータとして渡した値をそのままincludeの引数として渡して読み込もうとするとエラーになる

```
{% include "{{ INC_FILE }}" %}
```

エラーメッセージ
```
jinja2.exceptions.TemplateNotFound: {{ INC_FILE }}
```
## 解決策

以下のように書くことでエラーを回避できる

```
{% include '%s' % INC_FILE %}
```

## 参考

https://stackoverflow.com/q/12233971
サブドメインを使ってPHPを動作させる方法



|[../](https://qiita.com/taconana/items/d382d08a83a2a67f3b6a) |
|---|

既存のサーバー、既存のドメインで httpd + Tomcat が動作している環境で、テスト的にPHPを試してみたい場合の最も簡単な方法は、サブドメインを使う方法かなと思う。例えば、既に kankeri.com で httpd + Tomcatが動作している場合、Tomcatに干渉しないでPHPをテストしたいときは、php.kankeri.com のようなサブドメインを使うと非常に簡単である。

ただし、委任先のDNSサーバにサブドメインを追加する作業が必要であり、反映されるまで若干待つかもしれない。CNAMEを１行追加するだけである。以下の感じである。

```
php 	CNAME 	kankeri.com.
```

そして、httpdのVirtualHostに php.kankeri.com:80 を追加する。以下の感じである。例えば、vhost-php.conf を作成して記述する。phpのコードは、/opt/php74/webapps/php に置く場合で示している。
```:vhost-php.conf
$ vi /etc/httpd/conf.d/vhost-php.conf 
<VirtualHost php.kankeri.com:80>
	ServerName php.kankeri.com
	ServerAdmin webmaster@kankeri.com
	DocumentRoot "/opt/php74/webapps/php"
	DirectoryIndex index.php index.html index.htm

	<Directory "/opt/php74/webapps/php">
	    Options FollowSymLinks
	    AllowOverride None
	    Require all granted
	</Directory>
	ErrorLog  logs/php-error_log
	CustomLog logs/php-access_log combined
</VirtualHost>
```
既存の kankeri.com:80 に干渉しないので、構築しやすく、テストしやすいと思う。
サブドメインを使いたくない場合は「 [CentOS Stream 8でNginxとTomcatを共存させる手順](https://qiita.com/taconana/items/a7d34af9282de781cd76)」を参照のこと。

|[../](https://qiita.com/taconana/items/d382d08a83a2a67f3b6a) |
|---|
IBMCloud vSRX(HA)アップグレード時のステータスと機器監視について

## 内容サマリ
IBMCloudでvSRX(HA)を運用しており、アップグレードをする際の各機器に対する監視や管理アクセスの可否について記載する。

vSRXへの通信団はないが、ubuntuに対してはダウンが発生するので監視サーバを利用している場合は一時的い停止しておくと良い。

## 作業前の状態
[作業前の状態]
```
vSRX-Node0> show chassis cluster status
Redundancy group:
node0 100 primary no no None
node1 1 secondary no no None
```

Cluster: vSRX Ping/SSH OK
Node0: Ubuntu Ping/SSH OK
Node1: Ubuntu Ping/SSH OK

## アップグレード開始後の状態
Rediness Checkを実施後、ポータルからUpgrade versionを実施

ポータルステータス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703043/72d8af0f-0378-d4c3-ddae-c629474bed14.png)

```
vSRX-Node0> show chassis cluster status
Redundancy group:
node0 100 primary no no None
node1 0 lost n/a n/a n/a

Cluster: vSRX Ping/SSH OK
Node0: Ubuntu Ping/SSH OK
Node1: Ubuntu Ping/SSH OK .... -> NG
```

Node1のアップグレードが開始されると、疎通ができなくなる。
アップグレード開始後、おおよそ90秒で停止

## アップグレード途中

アップグレードが完了までまだかかるが、Node1の疎通が復旧する。
このあと、Node0のアップグレードをスタンバイするため、PrimaryとSecondaryが入れ替わる。

ポータルステータス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703043/72d8af0f-0378-d4c3-ddae-c629474bed14.png)


```
vSRX-Node0> show chassis cluster status
Redundancy group:
node0 100 primary no no None
node1 0 lost n/a n/a n/a

Cluster: vSRX Ping/SSH OK
Node0: Ubuntu Ping/SSH OK
Node1: Ubuntu Ping/SSH NG.... -> OK
```

## アップグレード完了(Node1)

ポータルステータス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703043/b049193c-06f2-bc4b-d8e5-52ba8096dbad.png)

```
vSRX-Node1> show chassis cluster status
Redundancy group:
node0 0 lost n/a n/a n/a
node1 1 primary no no None

Cluster: vSRX Ping OK
Node0: Ubuntu Ping OK
Node1: Ubuntu Ping OK
```

アップグレードが完了すると、Node0とNode1の役割が入れ替わっている

この後、Node0のアップグレードに進みます。
Node0のubuntuのアップグレードを開始すると、ubuntuへの通信も停止する。
[Nuxt.js]@nuxtjs/toastを利用してトーストを表示する

# はじめに
@nuxtjs/toastを利用してトーストを実装したので備忘録として残しておきます。

# 環境
- Vue：2.6.14
- Nuxt.js：2.15.8
- @nuxtjs/toast：3.3.1
# 目次
[1.モジュールの概要](#1-モジュールの概要)
[2.導入手順](#2-導入手順)
[3.サンプル](#3-サンプル)
[4.参考記事](#4-参考記事)

# 1. モジュールの概要
手軽にトーストを実装できるモジュールです。


https://www.npmjs.com/package/@nuxtjs/toast

# 2. 導入手順
### インストール
```
npm install @nuxtjs/toast --save
```

### モジュールを登録する
`nuxt.config.js`の`modules`に追記します。
```js:nuxt.config.js
//略
modules: [
   '@nuxtjs/toast',
],
toast: {
    position: 'top-center',
    duration: 4000
  }
],
//略
```

`position`、`duration`以外にもオプションがあるので、手軽にカスタマイズすることができます。

### 実装例
下記のようにして、表示します。
```js:成功時
this.$toast.success("成功！！！")
```
```js:失敗時
this.$toast.success("失敗！！！")
```

# 3. サンプルコード
以前作成したログインページにトーストを実装しました。

https://qiita.com/HuntingRathalos/items/b401ee865c40e94cef46

```vue:pages/login.vue
<template>
  <v-card max-width="500" class="mx-auto mt-5 full-width" flat outlined>
    <v-card-title class="text-center pa-8">
      <h1 class="text-h5 font-weight-bold full-width">ログイン情報入力</h1>
    </v-card-title>
    <v-divider> </v-divider>
    <div class="px-6 py-8">
      <div style="max-width: 336px" class="mx-auto">
        <v-card-text>
          <v-form ref="login_form" @submit.prevent="doLogin">
            <EmailInput :email.sync="form.email" />
            <PasswordInput :password.sync="form.password" />
            <v-card-actions>
              <v-row justify="end" class="pb-8 pt-4">
                <BaseButton :color="btnColor">ログイン</BaseButton>
              </v-row>
            </v-card-actions>
          </v-form>
        </v-card-text>
        <v-divider></v-divider>
        <div class="pt-8 pb-4">
          <span>パスワードをお忘れですか？</span>
          <nuxt-link to="/forgot-password">パスワードをリセットする</nuxt-link>
        </div>
      </div>
    </div>
  </v-card>
</template>
<script>
import BaseButton from '../../atoms/buttons/BaseButton.vue'
import EmailInput from '../../atoms/inputs/EmailInput.vue'
import PasswordInput from '../../atoms/inputs/PasswordInput.vue'
export default {
  components: { EmailInput, PasswordInput, BaseButton },
  data() {
    return {
      btnColor: 'indigo accent-2',
      form: {
        email: '',
        password: ''
      }
    }
  },
  methods: {
    async doLogin() {
      if (this.$refs.login_form.validate()) {
        try {
          await this.$auth.loginWith('laravelSanctum', {
            data: this.form
          })
          this.$router.push('/')
          this.$toast.success('ログインに成功しました。') //追加
        } catch {
          this.$toast.error('ログインに失敗しました。') //追加
        }
      }
    }
  }
}
</script>
<style scoped>
.full-width {
  width: 100%;
}
</style>
```
以上で実装完了です。

# 4. 参考文献

https://masublog.net/vue_nuxt_js/nuxt-js%E3%81%A7toast%E3%82%92%E4%BD%BF%E3%81%86

https://www.kabanoki.net/3356
特定の文字が含まれているファイルを１箇所にコピーしたい

# 特定のファイルを１箇所に移動したい

## 目的
特定の文字が含まれているファイルを１箇所にコピーしたい

## 方法
`find` コマンドの `exec`　オプションを使用する

## 使用例

```sh
$ find 検索パス -type f -name 検索したい名前 -ecec cp {} コピー先ディレクトリ　\;

# 例
$ find . -type f -name "*.txt" -exec cp {} ~/dest \;
```
## 解説
`find`コマンド
- `type`：ファイルタイプを指定する
    - `-type f`はファイルを対象として検索
    - `-type d`はディレクトリを対象として検索

- `name`：ファイルの名前を指定する
    - ワイルドカードを使用して、ファイルやディレクトリ名の一部のみを指定し、検索することができる
        - `"*.txt"`はすべての`text`ファイル
        - `"test*/*.txt"`はファイル名が`test`で始まるすべての`text`ファイル

- `-exec`：検索結果に対してコマンドを実行する
    - `{}`は対象となる全ファイルを表し、最後の`\;`は`-exec`の終わりを表す記号
iPhoneのアプリはどうやってできるのか？

スマートフォンで日々使えるアプリたち。
これらは一体どうやって作られているのか、まとめました。

## アプリとは？
アプリとは、「application software（アプリケーションソフトウェア）」の略で、目的にあった作業をする応用ソフトウェアのことです。
具体的には、スマホであれば、ゲームやメッセージアプリ、動画再生など
　　　　　　パソコンであれば。表計算やワープロなど

このアプリたちは、『OS(オペレーションシステム)』上にインストールして使用できるようになります。

#### OSとは？
OSとは私たちが使うすべてのコンピュータ（パソコン、スマホなど）を制御するのに必要な基本ソフトウェアのことです。簡単にいうと、パソコンやスマホを動かすためのソフトです。OSがないと電源を入れても何も表示されず、キーボードから文字も入力できなくなります。

一般的なOSとしては、下記があります。
| パソコン | スマホ |
|:-:|:-:|
| Windows , MacOS , Linux , ChromeOS  | iOS(iPhoneで使用)、Android|


## iOSとAndroid
では、スマホで使われるiOSとAndroidについて見ていたいと思います。
|  | iOS | Android |
|:-:|:-:|:-:|
| OS  | Appleが提供するOS  | Googleが提供するOS  |
| 端末 | iPhone,iPadなど |  NEXUS,HwaweiなどiOS以外のほとんど |
|  メーカー | Appleのみ  | Google以外にもある  |
|  インストール | App Store  | Google Play|
|  主な開発言語 | Swift・Objective-Cなど | Java・Kotlinなど  |

## iOSのアプリができるまで
そもそもiOSアプリは、Appleが提供するOS上でしか作ることができません。
そのため、アプリの開発には「Mac」のパソコンが必要になります。
#### ■MacPCでiOSアプリを開発する場合の推奨スペック
Macのスペックとしては下記が推奨されています。
・バージョン：最新
・コア数：Core i5（1.5GHz/2コア 以上)  |
・メモリ： 16GB以上  
・ストレージ：512GB
（ちなみにWindowsで生きてきた私は、MacBookProのメモリ8GBを買いました。）


iOSアプリは、『Swift』というプログラミングで使う言語を使って作ります。
そのSwiftを使用するためのツールとして、『Xcode』という開発ツールを用います。
#### ■iOSアプリに必要な開発環境
開発ツールは、「開発環境」とも呼ばれ、iOSでは適した開発環境として以下が挙げられます。

【Xcode】
XcodeはMac専用の統合開発環境で、Apple社が開発したもの。Swiftを用いた、iOSアプリの開発に使われる環境。少ないコードでアプリ開発ができるので、初心者にオススメ。

【Xamarin】
Xamarinは、C#を使ったiOSアプリ開発環境でオープンソースである分、誰でも利用できる。本来はiOSとAndroidのアプリで必要な環境や言語が異なるが、XamarinはiOSとAndroidのどちらのアプリも開発できので、両方のアプリを同時にリリースしたい方にオススメ。

【Flutter】
Flutterは、Google社が開発したアプリ開発環境で、Dartというプログラミング言語を使ったクロスプラットホーム開発（iOS、Android、Windows、macOSなど異なるプラットフォーム上で、同じ仕様のアプリケーションを動かせるプログラムの開発）ができる。ほかのアプリ開発環境と比べて、デザイン性に富んだアプリの開発がしやすく、高く評価されやすいUIの構築が可能です。しかし、Dartの普及率が低いため情報が少なく、初心者には難しめ。
#### ■iOSアプリの開発手順
iOSアプリの開発の大まかな開発手順は、以下の通りです。
　1.制作するアプリを企画する（コンセプト）
　2.アプリの要件を洗い出す
　　　∟どんなことができるアプリなのか箇条書きにする
　3.機能を洗い出す
　　　∟要件をもとに具体的な機能を挙げる
　4.データ設計
　　　∟アプリ内に必要なデータ構造を設計する
　5.ワイヤーフレームづくり
　　　∟アプリの骨組み。紙とペンやフォトショでもいい
　6.プログラミング開始

#### ■アプリ実行までの流れ
Swift 　→　Xcode　→　中間言語　→　iOS　→　iPhone
　　　　　　　　　　　　　　　　　　　
①開発ツールを用いてSwiftでプログラミングする
②プログラミングしたものをコンピュータが理解できるよう中間言語にコンパイル（変換）する
③iOSに読み込む
④端末上で実行する

#### ■アプリのストア公開
完成したアプリをApp Storeにリリースするまでの流れです。

　アプリ完成　　
　　　↓　申請　
　App Store Connect
　　　↓　審査
　Apple審査員による審査
　　　↓　承認
　アプリ公開

## 終わりに
　日々の生活に欠かせないスマートフォン。そのスマートフォン上で使える便利なアプリたちはどうやってできているのか？　そんなプログラミング初心者の私が最初に持った疑問を記事にしてみました。
POSTMANを使用したTwitter API使用

チュートリアルを見ればすぐ出来ると思いますが、しょうもないところで迷ったのでメモ
https://developer.twitter.com/en/docs/tutorials/postman-getting-started

1. POSTMANにユーザー登録して Twitter API v2 をフォーク
https://www.postman.com/twitter/workspace/twitter-s-public-workspace/collection/9956214-784efcda-ed4c-4491-a4c0-a26470a67400?ctx=documentation

2. WorkSpaceを移動して、Collectionsタブを選択、リストから Twitter API v2 を選択

3. Typeを Bearer Token に変更、Token欄に"{"を入力してbearer_tokenを選択
※Saveボタンを忘れずに

4. Variables タブを開いて、bearer_token 欄にtokenをコピー
※bearer_token は Developer Portal → Projects & Apps → 作成したアプリ → Keys and tokens から再生成可能

5. Collections から使用したいAPIの"フォルダー"を選択し、Type が Inherit auth from parent になってることを確認（ここが別の認証になってて時間を潰しました泣）

6. 適当なAPIを選択したらパラメータを入力して Send 、下のResponseに結果が表示される。401エラーが出たら、上記のTypeとTokenを再確認
例：Tweet Lookup → Single Tweet、Paramsの PathVariables の id Key のvalueに「1228393702244134912」を入力

↓Twitter Devのツイート
https://twitter.com/TwitterDev/status/<b>1228393702244134912</b>

※入力したパラメータはSaveを押すと保存される、Save横の矢印を押せば別名保存できる。

2～5
![スクリーンショット 2022-06-21 140709.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2713217/4378db98-3795-e184-59b3-89394ae3571a.png)

6
![スクリーンショット 2022-06-21 140927.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2713217/8cd66709-dd9f-bfc2-e5b4-c4b3082f5d5a.png)
DevOpsについて

# この記事は？
社内の研修用にDevOpsについて私なりにまとめました。DevOpsについては、どちらかといえばマネジメントに関わる内容で書かれることが多いと考えていますが、マネジメントのメンバーが考えるDevOpsを共に協力し考えることのできるメンバーとなることでプロジェクトを推進するための一助になればと思います。

# 一番伝えたいこと
メンバーとして作業しているとテストの自動化やCI/CDをすることがDevOpsであると考えてしまいがちです。
しかし、DevOpsとはもっと抽象的な考えであるためそこを理解することが重要だと考えます。
今日一番伝えたいのはテストの自動化やCI/CDを行うことがDevOpsの「**目的**」ではなく、「**手段**」であるという点を抑えてもらえればと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/0b93eff5-fb6a-3285-4d11-9d4eed8c6024.png)

# DevOpsとは？
DevOpsは「**開発チーム(Development)**」と「**運用チーム(Operations)**」がお互いに協調し合うことでより確実かつ迅速にエンドユーザーにシステムを届け続ける取り組み。

## 開発チームと運用チームが別々に仕事をする場合の問題点
開発チームは機能修正したものをどんどんリリースをしていきたいのに対して運用チームはサービスを安定稼働させていきたいと考えています。リリースが増えれば必然的にサービスを安定稼働させるのが難しくなってしまうため、この2つの考えは相反するものになってしまいビジネスを進める上での障害となってしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/524088/90d26b7c-063b-227c-b002-70bb2dcfc37d.png)

## どうやって解決するか？
開発・運用チームが互いに協力し、開発と運用のサイクルをスムーズに行うためにボトルネックとなる部分を解決していくことで問題点を解消していきます。
以下がすべてというわけではないですが、具体的な例を示します。

* リリースのたびにテストする内容を自動化する(CIツールの導入)
    * 人力のテストを行う場合、チェックが行き届かずデグレが発生する可能性があるため自動化することでデグレを防止する
* リリース作業を行うにあたって自動リリースが可能な状況を作る(CDツールの導入)
    * リリース作業の負担を軽減することで頻繁なリリース作業に対応可能な状況を作る
* ChatToolなどを用いて情報共有を行う
    * 開発チームや運用チームだけで問題を解消しようとせずお互いに情報共有が可能な状況を作る

## 開発メンバーとしてDevOpsにむけて重要だと考える点
### マインド
DevOpsを実現するにために開発メンバーとしてどのようなことを意識して作業を進めていくべきか？という点での私見

* 自動化に積極的に貢献する
    * 自分がやることを前提とせずに誰がやっても同じに作業できるようにする
* 細かく修正しトライ&エラーを繰り返す
    * エラー状態で抱えこまずどんどん出していく
* 他チームや他のメンバーとの協力を意識する
    * 自分の作業が運用面までつなげるにあたり情報の更新があれば積極的に共有する
* 非難する文化をやめる
    * 非難する文化では情報の隠蔽が行われる
    * 無責任な作業をしない
    * 誰がやってもミスの介在しないプロセスを考えて動く

### ツール
DevOpsを実現するために開発メンバーとして扱うべきツールまたはそのツールを使用する意図

* ソースコード管理ツール(Git等)
    * ソースコードのみならずインフラの設定ファイルまで含めてバージョン管理する
* CI/CDツール(Jenkins等)
    * 自動テストや環境構築の自動化によって運用コストや正確性を高める
* テストツール(Pytest, Selenium等)
    * 自動テストを行うにあたってサービスのデグレを防止する
* モニタリングツール(ZABBIX等)
    * 開発担当者がモニタリングできるようにし運用で発生した問題の早期対応を行う
* コラボレーションツール(REDMINE等)
    * 他者にも伝わるようにチケットの更新を行う

# 終わりに
DevOpsがもっとも素晴らしい開発と運用の方法であり、どの現場でも適応できる魔法のような方法であるとは思っておりません。
自分が参画したプロジェクトの中で取り入れられていないから良くないということではなく状況に応じて選択していくものだと考えています。
今回の記事がプロジェクトの中でDevOps取り入れようとする中でどのように動くのが適切か？というための一助になればと思います。