ちゃなべの備忘録

ほぼ備忘録です。

エンジニアのためのデイリーニュースラジオ欲しくない?【備忘録】

はじめに

ある日朝起きた時に思った。

「朝ラジオ聴いたら目が覚めるけど、ニュースラジオは日経のものばっかり、エンジニアのものってないのかな?」

いろいろ調べてみた、だけどない!!!!
特にデイリーのがない。

え、じゃあ作らしてもらってもよろしい?

前提イメージ

  • 毎日その日のITニュースを聞きたい
  • マジで技術特化で良いと思われる
  • ラジオはどこかで流したい、パブリックにね。
  • んで、ラジオいう人は最初は自動音声で良い気がする。
    • 聞きやすければいい
  • 原稿もAIに作ってもらおうよ。ChatGPTがあるんだから。
  • 10日後くらいにだそうか。3月中にやってみようよ。(思案日:2023/3/19)
  • 必要なのは3つ
    • ラジオ場所
    • 自動音声の選定
    • 自動記事作成ツールを作成

調査

ラジオ作るのに必要なことを調査しましょう。

ラジオ場所

たぶんポッドキャストがいいと思う。 だいたいシェアで上位なのはこの3つかな。(参考:ポッドキャスト国内利用実態調査2021)

んーーーいい感じになってきたら広げていっていいけど、まずはSpotifyからかな。僕使ってるし。

Podcastのあげかたもむずくなさげ。 あとでやりましょ。

studentwalker.com

んで、今知ったけど、収益化できなそう。 海外だと収益化できるみたいだけど、現状日本だとできなそうだね。

podcasters.spotify.com

けど将来的にできそうだから、一旦ユーザー集める感じでいきたみ。

自動音声

これが一番未知。聞きやすいかが調べたいとこ。

ondoku3.com

→ ドンピシャの記事みっけ、だけど「音読さん」という会社がやっているところだから音読さんがよく見える。まぁ候補。

aicross.co.jp

→ 参考

nlab.itmedia.co.jp

→ 無料ですげぇ自然。

coestation.jp

→ これもありだけど、どうやら150字までしか無理そう。

原稿作成

ChatGPTは2021年以降の情報を学習していないので、昨日今日のニュースはまとめてくれなそう。 だからbing一択になりそう。

Google Bardも期待できそうだけど、リリースがまだなので一旦様子見。

ここらへんの原稿作成が一番アップデートしがいがあるので、随時情報を得ていきましょ。とりあえず始めることが大事。

実装

SpotifyのPodcasterを登録

まずは配信する場所を作りましょう。

podcasters.spotify.com

...え?これだけ?簡単すぎぃ。

イコン画像の作成

イコン画像欲しいからなんか作ろうか。

とりあえずCanvaで作ってみた。

これから

こう

これを登録 ...てかこんなのできたのかよ!!www

原稿作成依頼文の作成

次は原稿作成依頼をbingに投げるんだけど、それを作る。

じゃあやってみよう。

第一稿
昨日から今日までのエンジニアのためのニュースを2つほどピックアップして、1000字程度の原稿にまとめてください。
その原稿はニュースキャスターに読ませる想定で作成してください。
これを聞く人はラジオで聞くため、引用などの表記はしないでください。

改善点

第二稿
次のサイトらに上がっている昨日から今日までのニュースを2つほどピックアップして、紹介するための原稿を作成してください。
- https://techcrunch.com/
- https://www.itmedia.co.jp/
- https://www.businessinsider.jp/sai/
原稿を作る際の注意点は以下です。
- 必ず900字〜1100字にしてください。
- この原稿はニュースキャスターに読ませる想定で作成してください。
- 最初の挨拶は「おはようございます。本日もエンジニアのためのニュースをお届けします。」にしてください。
- これを聞く人はラジオで聞くため、引用などの表記はしないでください。

こいつ、、そういうことか、、「読み」で1000字くらいにしてくれたのか。。

改善点 - 1400文字くらいで良さげ。 - ピックアップするニュースは2-3つにしよう - あと話題はソフトウェアエンジニア向けにしぼろう。

第三稿
次のサイトらに上がっている昨日から今日までのニュースを2〜3つほどピックアップして、紹介するための原稿を作成してください。
- https://techcrunch.com/
- https://www.itmedia.co.jp/
- https://www.businessinsider.jp/sai/
原稿を作る際の注意点は以下です。
- 必ず1300文字以上にしてください。
- ピックアップするニュースはソフトウェアエンジニアに関係するものにしてください。
- この原稿はニュースキャスターに読ませる想定で作成してください。
- 最初の挨拶は「おはようございます。本日もエンジニアのためのニュースをお届けします。」にしてください。
- これを聞く人はラジオで聞くため、引用などの表記はしないでください。

よくなってきた、だけどトヨタの話とかが半年以上も前のものになってしまっている。

改善点 - 必ず前日のものにしよう

第四稿
次のサイトらに上がっているニュースを2〜3つほどピックアップして、紹介するための原稿を作成してください。
- https://techcrunch.com/
- https://www.itmedia.co.jp/
- https://www.businessinsider.jp/sai/

ニュースをピックアップする際の注意点は以下です。
- 必ず2023年3月18日に掲載された記事にしてください。
- ピックアップするニュースはソフトウェアエンジニアに関係するものにしてください。

原稿を作る際の注意点は以下です。
- 必ず1100文字以上にしてください。
- この原稿はニュースキャスターに読ませる想定で作成してください。
- 最初の挨拶は「おはようございます。本日もエンジニアのためのニュースをお届けします。」にしてください。
- これを聞く人はラジオで聞くため、引用などの表記はしないでください。

第五稿
昨日掲載されたニュースを2〜3つほどピックアップして、紹介するための原稿を作成してください。
ピックアップには以下のサイトを利用してください。

- https://techcrunch.com/
- https://www.itmedia.co.jp/
- https://www.businessinsider.jp/sai/

原稿を作る際の注意点は以下です。
- 必ず1100文字以上にしてください。
- この原稿はニュースキャスターに読ませる想定で作成してください。
- 最初の挨拶は「おはようございます。本日もエンジニアのためのニュースをお届けします。」にしてください。
- これを聞く人はラジオで聞くため、引用などの表記はしないでください。

ちゃんと記事も全部最新だし、よきだね。

改善点 - 音声に読ませてみたら、固有名詞のところがうまく読めてなかった - やよい軒 → やよいのき - 最後の締めの言葉も用意しよう。

第五稿
昨日掲載されたニュースを2〜3つほどピックアップして、紹介するための原稿を作成してください。
ピックアップには以下のサイトを利用してください。

- https://techcrunch.com/
- https://www.itmedia.co.jp/
- https://www.businessinsider.jp/sai/

原稿を作る際の注意点は以下です。
- 必ず1100文字以上にしてください。
- この原稿はニュースキャスターの声をした自動音声読み上げに読ませる想定で作成してください。なので固有名詞はひらがなで表記し、アルファベット表記のところはひらがなで表記してください。
- 最初の挨拶は「おはようございます。本日もエンジニアのためのニュースをお届けします。」にしてください。
- 最後の挨拶は「以上エンジニアのためのニュースでした。明日もまた聞いてくださいね。今日も一日頑張りましょう。」にしてください。
- これを聞く人はラジオで聞くため、引用などの表記はしないでください。

いいね、一旦完成。

音声に読み上げさせる。

文章を分けて、CoeFontにあげた。

これ最高だわ。

だけど、少しだけ自動音声の読み上げが弱いところがある。 だからそれを修正する。

よし、結合してダウンロードもできた。

spotifyにアップロード

とりあえずアップ準備。

あと規約も改めて読みましょう。

support.spotify.com

よし!公開!

とりあえず運用とか考えず公開してしまいますた。

URLはこれです。

open.spotify.com

今後

とりあえず毎日やってみようか。 前日までの記事をピックアップすればいいから、夜中にやって、次の日の朝に公開するように。

さくらVPSでサーバー間通信をやってみた【備忘録】

はじめに

これにいろいろ機能をつけてきた。 さぁ次はサーバー間通信かな。

localでのサーバー間通信はやってきたけど、本番環境でのサーバー間通信はやったことがない。 ワクワク。やっていこ!

前提イメージ

  • さくらVPSにサーバー間通信の機能があるっぽいけど、どうなんだろ。
    • なるべく「これ便利サービス」は使いたくない。
  • もしできなそうであればネットワークを構築する必要がありそうなので頑張るわ。

調査

vps.sakura.ad.jp

→ これでやるっぽい。こんな簡単にできるのか?

manual.sakura.ad.jp

→ manualはこれ。やってみるか。

phoenixknight.jp

→ やり始めまで参考

blog.tilfin.net

→ いまはようわからんけど、あとあと参考になりそう

qiita.com

→ 参考になりそう

w.atwiki.jp

→ クソ参考になりそう

cat-marketing.jp

→ まんまやん

検索方法はこれ [さくらVPS ローカル接続]

実装

他の人のサイトを見ながらやってみよう。

スイッチの作成

とりあえず作ってみた。

さくらVPSスイッチ作成 さくらVPSスイッチ作成2 さくらVPSスイッチ作成3

サーバー間通信を行うための、サーバー3つ(front, back, db)をシャットダウンする。

各サーバーに接続先スイッチを追加

サーバーの設定画面 > ネットワーク > ネットワークインターフェース から接続先を設定。
eth0は基本的にインターネットに接続するみたい。だからeth1をさっきのスイッチに設定。

だけどあとでbackサーバーとdbサーバーはeth0のインターネット接続を外す、必要ないしね。

さくらVPS接続先スイッチ設定

各サーバーのネットワーク設定

んで、いろいろみていたら、どうやらこの後はnmcliコマンドというものを使うっぽい。 そして、どうやらこっから先のさくらVPS関連の設定はなさげ? ということは [ nmcli サーバー間通信 ]とかで調べたら出てきそう。

調べてみたけど、やっぱりさくらVPSありきの設定ではありそう。local通信を行うために今回スイッチ作ったしね。

これらを参考にやってみる

cat-marketing.jp

manual.sakura.ad.jp

clientの設定

clientのサーバー入ってみて、設定ファイル見てみたらこうなっていた。ので編集した。

$ cat /etc/sysconfig/network-scripts/ifcfg-ens4
DEVICE=”ens4″
ONBOOT=”no”
TYPE=“Ethernet”

$ vi /etc/sysconfig/network-scripts/ifcfg-ens4
DEVICE="ens4"
ONBOOT=”yes”
TYPE=”Ethernet”
IPADDR=”192.168.1.1″
NETMASK=“255.255.255.0″

$ systemctl restart NetworkManager

けど、この後にpingで確認してもどうやらうまく設定できていないみたいだす。

$ nmcli networking off && nmcli connection reload && nmcli networking on
Error: failed to set networking: Not authorized to enable/disable networking
$ sudo nmcli networking off

あ、、やっちまったwww ネットワーク切ったからssh切れちゃった。 さくらVPSからコンソール入って、以下のコマンド打って再起動しました。 そしたらsshで入れた、よかったーー。

$ sudo nmcli connection reload 
$ sudo nmcli networking on

んーーーだけどやっぱり起動しない。というかconnection見てみたら消えてるし。deviceは残っている。じゃあ追加してみるか。

$ nmcli connection add type ethernet ifname ens4 con-name ens4
Connection 'ens4' successfully added.
$ nmcli connection
NAME         TYPE      DEVICE
ens4         ethernet  ens4
System ens3  ethernet  ens3
System ens5  ethernet  --
$ nmcli device
DEVICE  TYPE      STATE                                  CONNECTION
ens3    ethernet  connected                              System ens3
ens4    ethernet  connecting (getting IP configuration)  ens4
ens5    ethernet  disconnected                           --
lo      loopback  unmanaged                              -- 
$ nmcli device show ens4
GENERAL.DEVICE:                         ens4
GENERAL.TYPE:                           ethernet
GENERAL.HWADDR:                         ~~~~
GENERAL.MTU:                            1500
GENERAL.STATE:                          70 (connecting (getting IP configuration))
GENERAL.CONNECTION:                     ens4
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/4
WIRED-PROPERTIES.CARRIER:               on
IP4.GATEWAY:                            --
IP6.ADDRESS[1]:                         ~~~~
IP6.GATEWAY:                            --
IP6.ROUTE[1]:                           dst = fe80::/64, nh = ::, mt = 1024

おおーきた!! ん、ということはさっきのファイル直接編集はやっぱりしなくても良さげだな。connectionを一旦落として、addして、network設定すればよさげ。

というかこれ見てみたら、さっきの設定ファイル反映できてなくね? ....あとでそれはするとしてIP6のアドレス設定しかできてないからちょっとnmcliコマンドで修正しよう。

$ sudo nmcli connection modify ens4 ipv4.method manual ipv4.addresses "192.168.1.1/24" ipv6.method "ignore" connection.autoconnect yes
[rocky@ik1-443-53839 ~]$ nmcli device
DEVICE  TYPE      STATE         CONNECTION
ens3    ethernet  connected     System ens3
ens4    ethernet  connected     ens4
ens5    ethernet  disconnected  --
lo      loopback  unmanaged     --
[rocky@ik1-443-53839 ~]$ nmcli device show ens4
GENERAL.DEVICE:                         ens4
GENERAL.TYPE:                           ethernet
GENERAL.HWADDR:                         ~~~~~
GENERAL.MTU:                            1500
GENERAL.STATE:                          100 (connected)
GENERAL.CONNECTION:                     ens4
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/15
WIRED-PROPERTIES.CARRIER:               on
IP4.ADDRESS[1]:                         192.168.1.1/24
IP4.GATEWAY:                            --
IP4.ROUTE[1]:                           dst = 192.168.1.0/24, nh = 0.0.0.0, mt = 101
IP6.GATEWAY:                            --

できているっぽいな。設定ファイルどうなったんかな。

$ cat /etc/sysconfig/network-scripts/ifcfg-ens4
DEVICE="ens4"
ONBOOT=”yes”
TYPE=”Ethernet”
IPADDR=”192.168.1.1″
NETMASK=“255.255.255.0

なんにも変わってないな。ええーー。サービス再起動してみるか。 ....変わらん。。 えぇ。。じゃあ設定ファイルもとに戻して、再起動して、pingしてみるか。

$ vi /etc/sysconfig/network-scripts/ifcfg-ens4
DEVICE=”ens4″
ONBOOT=”no”
TYPE=“Ethernet”

$ nmcli networking off && nmcli connection reload && nmcli networking on
$ ping -c 10 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.015 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.032 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.030 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=0.026 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=0.029 ms
64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=0.030 ms
64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=0.038 ms
64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=0.029 ms
64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=0.031 ms
64 bytes from 192.168.1.1: icmp_seq=10 ttl=64 time=0.027 ms

--- 192.168.1.1 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9230ms
rtt min/avg/max/mdev = 0.015/0.028/0.038/0.005 ms

うん、まぁいいんかな。 じゃあこれで他のサーバーも同じ設定でいこか。

backの設定

さっきと違う方法でやってみよう。

$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
System ens4  e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  --
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --

$ nmcli connection modify e27f182b-d125-2c43-5a30-43524d0229ac connection.id ens4
$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
ens4         e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  --
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --

$ nmcli connection modify ens4 ipv4.method manual ipv4.addresses "192.168.1.2/24" ipv6.method "ignore" connection.autoconnect yes
$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
ens4         e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  --
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --
$ nmcli device
DEVICE  TYPE      STATE         CONNECTION
ens3    ethernet  connected     System ens3
ens4    ethernet  disconnected  --
ens5    ethernet  disconnected  --
lo      loopback  unmanaged     --

$ nmcli networking off && nmcli connection reload && nmcli networking on
$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
ens4         e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  --
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --

$ nmcli connection up ens4
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3)
$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
ens4         e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  ens4
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --
$ nmcli device show ens4
GENERAL.DEVICE:                         ens4
GENERAL.TYPE:                           ethernet
GENERAL.HWADDR:                         ~~~~~
GENERAL.MTU:                            1500
GENERAL.STATE:                          100 (connected)
GENERAL.CONNECTION:                     ens4
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/3
WIRED-PROPERTIES.CARRIER:               on
IP4.ADDRESS[1]:                         192.168.1.2/24
IP4.GATEWAY:                            --
IP4.ROUTE[1]:                           dst = 192.168.1.0/24, nh = 0.0.0.0, mt = 101
IP6.GATEWAY:                            --

お、できたっぽい。たぶん以下のことをすると設定できるかな。

  • nmcli connection up をしてつなぐ
  • プライベートaddressの設定はするべき
  • networkingの再起動はいるかわからん
  • nmcli connection reloadはファイルを読む感じだから関係なさそう

じゃあpingで他のアドレスとも繋がっているか確認しよう。

$ ping -c 10 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.018 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.031 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=0.031 ms
64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=0.038 ms
64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=0.036 ms
64 bytes from 192.168.1.2: icmp_seq=6 ttl=64 time=0.038 ms
64 bytes from 192.168.1.2: icmp_seq=7 ttl=64 time=0.030 ms
64 bytes from 192.168.1.2: icmp_seq=8 ttl=64 time=0.039 ms
64 bytes from 192.168.1.2: icmp_seq=9 ttl=64 time=0.032 ms
64 bytes from 192.168.1.2: icmp_seq=10 ttl=64 time=0.031 ms

--- 192.168.1.2 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9200ms
rtt min/avg/max/mdev = 0.018/0.032/0.039/0.005 ms

$ ping -c 10 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.355 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.202 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.183 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=0.282 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=0.418 ms
64 bytes from 192.168.1.1: icmp_seq=6 ttl=64 time=0.160 ms
64 bytes from 192.168.1.1: icmp_seq=7 ttl=64 time=0.325 ms
64 bytes from 192.168.1.1: icmp_seq=8 ttl=64 time=0.294 ms
64 bytes from 192.168.1.1: icmp_seq=9 ttl=64 time=0.459 ms
64 bytes from 192.168.1.1: icmp_seq=10 ttl=64 time=0.233 ms

--- 192.168.1.1 ping statistics ---
10 packets transmitted, 10 received, 0% packet loss, time 9221ms
rtt min/avg/max/mdev = 0.160/0.291/0.459/0.094 ms

$ ping -c 10 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
From 192.168.1.2 icmp_seq=1 Destination Host Unreachable
From 192.168.1.2 icmp_seq=2 Destination Host Unreachable
From 192.168.1.2 icmp_seq=3 Destination Host Unreachable
From 192.168.1.2 icmp_seq=4 Destination Host Unreachable
From 192.168.1.2 icmp_seq=5 Destination Host Unreachable
From 192.168.1.2 icmp_seq=6 Destination Host Unreachable
From 192.168.1.2 icmp_seq=7 Destination Host Unreachable
From 192.168.1.2 icmp_seq=8 Destination Host Unreachable
From 192.168.1.2 icmp_seq=9 Destination Host Unreachable
From 192.168.1.2 icmp_seq=10 Destination Host Unreachable

--- 192.168.1.3 ping statistics ---
10 packets transmitted, 0 received, +10 errors, 100% packet loss, time 9199ms
pipe 4

おおーーー。ちゃんと自身のアドレスにpingできるだけじゃなくて、clientサーバー(192.1681.1)へpingもちゃんと送れてる。そして、まだ設定していないアドレス(192.168.1.3)には送れていない。

やりたいことできてるぅ!!

dbの設定

次は検証も含めて設定していこう。

$ nmcli connection modify e27f182b-d125-2c43-5a30-43524d0229ac connection.id ens4
$ nmcli connection up ens4
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/2)
$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
ens4         e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  ens4
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --
$ nmcli device show ens4
GENERAL.DEVICE:                         ens4
GENERAL.TYPE:                           ethernet
GENERAL.HWADDR:                         9C:A3:BA:08:67:56
GENERAL.MTU:                            1500
GENERAL.STATE:                          100 (connected)
GENERAL.CONNECTION:                     ens4
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/2
WIRED-PROPERTIES.CARRIER:               on
IP4.GATEWAY:                            --
IP6.GATEWAY:                            --

あーーーやっぱり起動はするし、つながりもするっぽいな。 だけど、private addressの設定指定なからこのままだとダメだね。

じゃあprivate addressの設定をしていこう。

$ nmcli connection down ens4
Connection 'ens4' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/2)
$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
ens4         e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  --
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --

$ nmcli connection modify ens4 ipv4.method manual ipv4.addresses "192.168.1.3/24" ipv6.method "ignore" connection.autoconnect yes
$ nmcli connection up ens4
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/3)
$ nmcli connection
NAME         UUID                                  TYPE      DEVICE
System ens3  21d47e65-8523-1a06-af22-6f121086f085  ethernet  ens3
ens4         e27f182b-d125-2c43-5a30-43524d0229ac  ethernet  ens4
System ens5  8126c120-a964-e959-ff98-ac4973344505  ethernet  --
$ nmcli device show ens4
GENERAL.DEVICE:                         ens4
GENERAL.TYPE:                           ethernet
GENERAL.HWADDR:                         9C:A3:BA:08:67:56
GENERAL.MTU:                            1500
GENERAL.STATE:                          100 (connected)
GENERAL.CONNECTION:                     ens4
GENERAL.CON-PATH:                       /org/freedesktop/NetworkManager/ActiveConnection/3
WIRED-PROPERTIES.CARRIER:               on
IP4.ADDRESS[1]:                         192.168.1.3/24
IP4.GATEWAY:                            --
IP4.ROUTE[1]:                           dst = 192.168.1.0/24, nh = 0.0.0.0, mt = 101
IP6.GATEWAY:                            --

よし、できた。じゃあpingで確認する。

$ ping -c 5 192.168.1.3
PING 192.168.1.3 (192.168.1.3) 56(84) bytes of data.
64 bytes from 192.168.1.3: icmp_seq=1 ttl=64 time=0.032 ms
64 bytes from 192.168.1.3: icmp_seq=2 ttl=64 time=0.045 ms
64 bytes from 192.168.1.3: icmp_seq=3 ttl=64 time=0.040 ms
64 bytes from 192.168.1.3: icmp_seq=4 ttl=64 time=0.032 ms
64 bytes from 192.168.1.3: icmp_seq=5 ttl=64 time=0.033 ms

--- 192.168.1.3 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4124ms
rtt min/avg/max/mdev = 0.032/0.036/0.045/0.005 ms

$ ping -c 5 192.168.1.1
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=0.391 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=64 time=0.235 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=64 time=0.181 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=64 time=0.211 ms
64 bytes from 192.168.1.1: icmp_seq=5 ttl=64 time=0.267 ms

--- 192.168.1.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4115ms
rtt min/avg/max/mdev = 0.181/0.257/0.391/0.072 ms

$ ping -c 5 192.168.1.2
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=0.290 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=0.219 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=3.83 ms
64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=5.86 ms
64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=0.294 ms

--- 192.168.1.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4075ms
rtt min/avg/max/mdev = 0.219/2.098/5.856/2.331 ms

$ ping -c 5 192.168.1.4
PING 192.168.1.4 (192.168.1.4) 56(84) bytes of data.
From 192.168.1.3 icmp_seq=1 Destination Host Unreachable
From 192.168.1.3 icmp_seq=2 Destination Host Unreachable
From 192.168.1.3 icmp_seq=3 Destination Host Unreachable
From 192.168.1.3 icmp_seq=4 Destination Host Unreachable
From 192.168.1.3 icmp_seq=5 Destination Host Unreachable

--- 192.168.1.4 ping statistics ---
5 packets transmitted, 0 received, +5 errors, 100% packet loss, time 4083ms
pipe 4

いいねーー。ちゃんと自身と設定したサーバーには繋がって、それ以外には繋がらないっぽい。

サーバー同士の接続アドレス変更

じゃああとは、アドレスの向き先を変えればいいんかな?

  1. backとclientの接続
  2. dbとbackの接続

この二つを修正していこう。

backとclientの接続

clientからbackにapiを要求する時のアドレスを変えてみる。 このときはまだサーバーとdbはパブリックアドレスでつながっている。

まずはcurlコマンドで要求できるか確認する。

$ curl http:/192.168.1.2:8080/shops
{
    "message": "get shops",
    "shops": [
        {
            "ID": 5,
            "CreatedAt": "2023-01-15T07:40:33+09:00",
            "UpdatedAt": "2023-01-15T07:40:33+09:00",
            "DeletedAt": null,
            "Name": "test",
            "Description": "test"
        },
        {
            "ID": 7,
            "CreatedAt": "2023-01-21T23:23:37+09:00",
            "UpdatedAt": "2023-01-21T23:23:37+09:00",
            "DeletedAt": null,
            "Name": "test",
            "Description": "test"
        },
        {
            "ID": 8,
            "CreatedAt": "2023-03-05T18:28:01+09:00",
            "UpdatedAt": "2023-03-05T18:28:01+09:00",
            "DeletedAt": null,
            "Name": "牛角",
            "Description": "焼肉食べ放題ならここしかない。"
        }
    ]
}

完璧。じゃあ、コードを変えてみよう。

envファイルを変えてscpで送信してnginxを再起動してみる。

REACT_APP_SERVER_URL='http://192.168.1.2:8080'
$ scp services/client/app/.env.production rocky@133.125.51.93:/usr/local/src/app
.env.production                                                                     100%   46     1.4KB/s   00:00
$ ssh nabelog_client
$ cd /usr/local/src/app
$ sudo systemctl restart nginx

あれ、だけどlog見てみてもどうにも切り替わっていない。もしかしてbuildファイルにenvも含まれる系かな?いや、だけどCDでbuildするけどgithubにenvは持っていっていないし。

だとすると再起動ミスか?一回CD回してみて、それでもダメだったらサーバーを再起動してみよう。

.... ↓

一回CDしてみたけどダメだ。サーバーを再起動してみます。

... ↓

サーバー再起動してもダメだ。え、なんでだろ。 あーーproxyだ。package.jsonのproxy設定が前の状態になってるわ。

えーーこれ環境によって変えたいんだけど、どうしよう。。 んーーまぁとりあえずハードコーディングで変えましょか。

  "proxy": "http://パブリックアドレス:8080",
  ↓
  "proxy": "http://192.168.1.2:8080",

そして、再度npm installしてCDを再度回してみる。

[rocky@ik1-443-53839 app]$ npm install
(#########⠂⠂⠂⠂⠂⠂⠂⠂⠂) ⠹ reify:fsevents: sill reify mark deleted [ '/usr/local/src/app/node_modules/fsevents' ]
<--- Last few GCs --->

[1816:0x5ef0630]   140346 ms: Scavenge (reduce) 248.1 (256.9) -> 248.0 (257.4) MB, 10.7 / 0.0 ms  (average mu = 0.381, current mu = 0.368) allocation failure
[1816:0x5ef0630]   140408 ms: Scavenge (reduce) 248.6 (257.4) -> 248.4 (257.7) MB, 4.6 / 0.0 ms  (average mu = 0.381, current mu = 0.368) allocation failure
[1816:0x5ef0630]   140459 ms: Scavenge (reduce) 249.0 (257.7) -> 248.8 (258.2) MB, 9.0 / 0.0 ms  (average mu = 0.381, current mu = 0.368) allocation failure


<--- JS stacktrace --->

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
 1: 0xb09c10 node::Abort() [npm install]
 2: 0xa1c193 node::FatalError(char const*, char const*) [npm install]
 3: 0xcf8dbe v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [npm install]
 4: 0xcf9137 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [npm install]
 5: 0xeb09d5  [npm install]
 6: 0xeb14b6  [npm install]
 7: 0xebf9de  [npm install]
 8: 0xec0420 v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [npm install]
 9: 0xec339e v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [npm install]
10: 0xe84612 v8::internal::Factory::AllocateRaw(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [npm install]
11: 0xe7cc24 v8::internal::FactoryBase<v8::internal::Factory>::AllocateRawWithImmortalMap(int, v8::internal::AllocationType, v8::internal::Map, v8::internal::AllocationAlignment) [npm install]
12: 0xe7ee82 v8::internal::FactoryBase<v8::internal::Factory>::NewDescriptorArray(int, int, v8::internal::AllocationType) [npm install]
13: 0x10cced7 v8::internal::DescriptorArray::CopyUpTo(v8::internal::Isolate*, v8::internal::Handle<v8::internal::DescriptorArray>, int, int) [npm install]
14: 0x10bb011 v8::internal::Map::CopyAddDescriptor(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Map>, v8::internal::Descriptor*, v8::internal::TransitionFlag) [npm install]
15: 0x10bb5cc v8::internal::Map::CopyWithField(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Map>, v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::FieldType>, v8::internal::PropertyAttributes, v8::internal::PropertyConstness, v8::internal::Representation, v8::internal::TransitionFlag) [npm install]
16: 0x10bd3f8 v8::internal::Map::TransitionToDataProperty(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Map>, v8::internal::Handle<v8::internal::Name>, v8::internal::Handle<v8::internal::Object>, v8::internal::PropertyAttributes, v8::internal::PropertyConstness, v8::internal::StoreOrigin) [npm install]
17: 0x10abf52 v8::internal::LookupIterator::PrepareTransitionToDataProperty(v8::internal::Handle<v8::internal::JSReceiver>, v8::internal::Handle<v8::internal::Object>, v8::internal::PropertyAttributes, v8::internal::StoreOrigin) [npm install]
18: 0x10cf3da v8::internal::Object::AddDataProperty(v8::internal::LookupIterator*, v8::internal::Handle<v8::internal::Object>, v8::internal::PropertyAttributes, v8::Maybe<v8::internal::ShouldThrow>, v8::internal::StoreOrigin) [npm install]
19: 0x120860f v8::internal::Runtime_StoreDataPropertyInLiteral(int, unsigned long*, v8::internal::Isolate*) [npm install]
20: 0x15f2099  [npm install]
Aborted (core dumped)
[rocky@ik1-443-53839 app]$ free
               total        used        free      shared  buff/cache   available
Mem:          468352       84176      311032        5696       73144      366360
Swap:              0           0           0
[rocky@ik1-443-53839 app]$ free -m
               total        used        free      shared  buff/cache   available
Mem:             457          86         299           5          71         353
Swap:              0           0           0

これ容量たぶんたりないやつーーー(泣)

node_modules重すぎやろ。どうすればええねん。

たぶんdevのやつも全部installしようとしていたから、それを除いてinstallしてみる

$ rm -rf node_modules
$ npm cache clean --force
$ npm install --production

だめだ、なんかうまくいかん。ためしにlocalの容量を調べてみると。

$ du -h
796M    .

796Mか〜〜。そりゃぁたらんなぁ。だって僕の環境0.5Gだもの。 えーーー、あげるーー? ....あげるか。 お金かかるよ〜〜。。

結局上げました。 1GBにして、再度npm install --productionしたら無事でけた。

けど、まだサーバー間通信はうまくいかない。 試しにlocalでbuildして送ってみる。

すると、、うまくいった!正確にはうまくいっていないんだけど、ページ自体は表示された。 だけどaxiosで到達していない。こいつはもしかしてグローバルipアドレスだと思っている?ローカルipアドレスが?

うーーん、調べるか。

調べていったらわかったーーー

なるほどな、nginxのリバースプロキシを使うのか。

Q. リバースプロキシってなに? A. リバースプロキシ (Reverse Proxy) とは、Webサーバーとして動作し、クライアントからのリクエストを受け取り、対象のサーバーに転送する役割を持つサーバーのことを指します。リバースプロキシサーバーは、その機能により、次のような利点を持っています。 ロードバランシング リバースプロキシサーバーは、バックエンドサーバーの負荷を分散するために、リクエストを複数のサーバーに振り分けることができます。これにより、トラフィックの過負荷を回避し、ユーザーエクスペリエンスを向上させることができます。 キャッシング リバースプロキシサーバーは、一度クライアントからリクエストを受け取ったコンテンツをキャッシュすることができます。これにより、バックエンドサーバーへの負荷を軽減し、ユーザーにより高速なコンテンツ配信を提供することができます。 セキュリティ リバースプロキシサーバーは、フロントエンドとバックエンドサーバーの間に置かれるため、クライアントからの直接接続を防ぐことができます。これにより、バックエンドサーバーを直接攻撃されるリスクを軽減し、セキュリティを向上させることができます。 URLの書き換え リバースプロキシサーバーは、クライアントがアクセスするURLを変更することができます。例えば、example.com/api にアクセスした場合に、api.example.com にアクセスするように変更することができます。これにより、バックエンドサーバーのURLを隠蔽することができます。 リバースプロキシは、Webアプリケーションの運用において、非常に重要な役割を担っています。特に、アプリケーションのロードバランシングやセキュリティ対策には、欠かせない存在となっています。
引用元:ChatGPT

じゃあ設定していこう。

server {
  listen       80;
  server_name  nabelog-client;
  charset      utf-8;

  root /usr/local/src/app/build;
  index index.html;

  location / {
    try_files $uri /index.html;
  }
# ↓これを追加
  location /api/ {
    proxy_pass http://192.168.1.2:8080/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

これは「/api/が先頭につく要求はhttp://192.168.1.2:8080/にプロキシするよ」っていう意味。

あと、これが悪さしてたから消しとこう。

  "proxy": "http://192.168.1.2:8080", ← 削除

さぁこれで????

サーバー間通信成功証拠写真

きたーーー!!! 長かった、やっと。 いい勉強になりました。

んで、一応backが他の人からアクセスされないようにネットワークとの接続を切っておこう。

$ sudo nmcli connection down 'System ens3'
client_loop: send disconnect: Broken pipe

あ、、、www そりゃそうか。インターネット経由でサーバーにsshアクセスしてるんだからそうなる。

んーけどなんかens3の接続切ったらローカル通信も切れるっぽい。 CORS設定もしてるし、一旦放置。

dbとbackの接続

次はdbとbackの接続です。 これはどうなんだろ、buildファイルと繋がるのはmysqlを直接参照するし、そんなに問題なさげ。 まずbackサーバーからdbサーバーにプライベートネットワークでmysql接続できるか試してみる。

$ mysql -u server -h 192.168.1.3 -p
-bash: mysql: command not found

うわ、だる。。うーーーん。。mysqlをinstallするとそれだけ容量減るからやだな。。

とりあえずできている前提でつぎやろうか。もしできなかったらやむなくinstallしよう。

ということで早速buildファイルの向き先をプライベートipアドレスに変えてやってみる。

# Clientからの呼び出し元URL
CLIENT_URL="http://192.168.1.1"

MYSQL_DATABASE=DB名
MYSQL_USER=ユーザー名
MYSQL_PASSWORD=パスワード
MYSQL_ROOT_PASSWORD=パスワード
MYSQL_HOST=192.168.1.3

じゃあsystemctlで再起動してみよう。

,,,動かない。。 やっぱりダメかぁ。。じゃあbackサーバーにmysqlを入れよう。

けどこれ思った。clientからアクセスを確認しても実質同じじゃね? ということで容量があるclientからやろう。

$ sudo dnf install mysql-server
43M
・
・
success

$ sudo systemctl start mysqld.service
$ systemctl status mysqld
● mysqld.service - MySQL 8.0 database server
     Loaded: loaded (/usr/lib/systemd/system/mysqld.service; disabled; vendor preset: disabled)
     Active: active (running) since Sun 2023-03-19 00:35:01 JST; 6s ago

43Mは辛いけど、うごいたっぽい。 じゃあclientからdbに呼び出してみよう。

$ mysql -u server -h 192.168.1.3 -p
Enter password:
ERROR 1045 (28000): Access denied for user 'server'@'192.168.1.1' (using password: YES)

んーーー、なんかアクセス拒否されてるな。dbサーバーから直接mysql潜ってみてみよう。

mysql> SELECT host, user from mysql.user;
+-----------------+------------------+
| host            | user             |
+-----------------+------------------+
| %               | exuser           |
| パブリックip      | server           |
| localhost       | mysql.infoschema |
| localhost       | mysql.session    |
| localhost       | mysql.sys        |
| localhost       | root             |
+-----------------+------------------+
6 rows in set (0.00 sec)

あーーそうだった。アクセス制限してるんだった。 じゃあこのユーザー名:serverのやつのhostを変えよう。

mysql> drop user server@パブリックip;
Query OK, 0 rows affected (0.01 sec)

mysql> create user 'server'@'192.168.1.2' identified by パスワード;
Query OK, 0 rows affected (0.00 sec)

mysql> create user 'client'@'192.168.1.1' identified by パスワード;
Query OK, 0 rows affected (0.01 sec)

これでアクセスできるかな?

$ mysql -u client -h 192.168.1.3 -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 222
Server version: 8.0.30 Source distribution

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

でけたーーー!

じゃあ権限絞って、最後に確認しよう。

mysql> grant all on nabelog_pro.* to server@192.168.1.2

サーバーとdbのプライベートネットワーク化完了

でーーーけた!!! 終わり!!

MySQLを使っているアプリで日本語を保存できるように【備忘録】

はじめに

こちらでアプリをアップしました。

dockerを安くデプロイしたい【備忘録】【読みづらい】 - Qiita

そのアプリがDBに日本語の登録ができません。 そこの設定周りをやっていこうと思います。

前提イメージ

まずlocalでも登録できなかったし、本番環境でも登録できなかった。 local → 本番 の順番で実装していく。 んで、おそらくMySQLの設定が悪い。詳しくはlogを見てみないとだけど。

実装

開発環境

まずはdockerを起動して、みてみる。 日本語を登録しようとすると、サーバー側でエラーがでた。

server  | (/usr/local/src/main.go:77)
server  | [2023-03-05 08:10:36]  Error 1366: Incorrect string value: '\xE3\x81\x84\xE3\x81\xA3...' for column 'name' at row 1

これは指定の行を見てみるとGORMでデータを送信しようとしている時だ。

逆に日本語の登録がDBに直接できないかやってみた。すると同様のエラーがでた。

MySQLに直接日本語を入れてみようとした結果

あーーじゃあMySQLの設定っぽいな。

これの設定をまんまやってみるか。

qiita.com

この設定でやってみた。 docker でdebianを指定したら、apt-getがないって言われないよ。

# 認証周りでエラーが起こったので、versionを5.7に
# TODO: いつかmysql:8.0に
FROM mysql:5.7-debian

RUN apt-get update
RUN apt-get -y install locales-all

ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8

COPY my.cnf /etc/mysql/my.cnf

お、問題なく立ち上がっているっぽい...?

mysqlにログインして、設定を見てみると、

mysql> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | utf8                       |
| character_set_connection | utf8                       |
| character_set_database   | utf8                       |
| character_set_filesystem | utf8                       |
| character_set_results    | utf8                       |
| character_set_server     | utf8                       |
| character_set_system     | utf8                       |
| character_sets_dir       | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.10 sec)

よいね!ちゃんとutf8になってる。 DBにデータをぶち込んでみる。

MySQLに直接日本語を入れてみようとした結果(after)

はいった!じゃあアプリケーションからやってみる。

アプリの店舗一覧ページのテスト

でーけた!

本番環境

ということは同じような設定を本番環境ですれば良いな。

...って思ったけどやっているっぽい?

https://qiita.com/ayumu-1212/items/47c7ff72dd280c11a57a#mysql%E3%81%AE%E8%A8%AD%E5%AE%9A

え、じゃあ本番環境で日本語の入力をしてみる。

本番環境の日本語設定

いやできるんかい!!w じゃあ終わり!

CI/CDをマイクロサービスに導入してみた【GitHubActions】【備忘録】

はじめに

最近マイクロサービスをscpだけでデプロイしてみました。

dockerを安くデプロイしたい【備忘録】【読みづらい】 - Qiita

まだまだ追加実装したいのですが、毎回scpするの面倒なのでCICDを実装します! なお、CICDは初めてやるので足からず! 前回CICDツールの選定をした結果、今回はGitHubActionsを使います。

前提イメージ

いろいろ調べる前のイメージ

CI

  • lintは回したいかな
    • フロント: typescript, React
    • バック:Go
  • あとはわからん、actionsみて随時きめよう
  • もしかしたらbuildもこっちに入る?

CD

  • デプロイしたい
    • フロント:npm run buildしたbuildフォルダをアップ
    • バック:go buildしたmainファイルをアップ
    • DB:migrationとかするんかな?
  • サーバーの再起動とかしたいよね
  • build自体の処理がCIなのかCDなのかがわからん

調査

やり方が全くわからんので、まずは調べまする。

GitHubActionsの概要を掴む

GitHubの新機能「GitHub Actions」で試すCI/CD | さくらのナレッジ

→ 長かったけど、だいったいわかった

GitHub ActionsでCI/CD環境を学びながら構築する - Qiita

→ work-flowの全体を掴めた。案外簡単そうという印象を持てた。

GitHub Actionsを使ったマイクロサービスのCI/CDモジュール管理 | MoT Lab (Mobility Technologies Engineering Blog)

→ ようわからんかったんで、また今度読もう。CICDのコンポーネント化してるんかな。

【GitHub Actions】ワークフローはなんとなく書けるけど構造や仕組みの理解が曖昧な方々向けの記事 - Qiita

Git Hub Actions入門

実装の参考探し

Go でのビルドとテスト - GitHub Docs

→ バックのCDはこれを参考にしよう。公式だし。

GitHub ActionsでSpringBootアプリをさくらVPSにSSHしてデプロイする - Qiita

GitHub Actions から安価なVPSのサーバーにデプロイするための Self-hosted Runner - 猫でもわかるWebプログラミングと副業

→ さくらVPSへのデプロイの参考

CI実装

まぁまぁ調べたのでさっそくやってみようか。

キャッチアップ

このページを元に試しに使ってみる。

zenn.dev

マジでこのページに沿ってやっているだけなので、学びになったことをメモにまとめます。

  • あ、結構簡単
  • コマンドの意味
    • runs-on: 実行環境設定
    • steps/run: 実行したいシェルスクリプト
    • steps/uses: 実行したいactionsを選択
  • stepsはrunかusesだけ。だけ!

これだけでも相当GithubActionsを理解した。 やっぱり実装してみるのがはええわ。

CIの導入 - client

さぁ実際のアプリケーションに導入してみましょう。 じゃあ、とりあえずフォルダ作って動かしてみよう。

$ mkdir .github
$ cd .github
$ mkdir workflows
$ cd workflows
$ touch client-ci.yaml

yamlファイルにまずは、「PRが出されたときにechoがでるように」する。

name: client-ci
on: 
  pull_request:
    types: [opened]
jobs:
  lint-run:
    runs-on: ubuntu-latest
    steps:
      - run: echo "this is pen"

じゃあこれをpushしてPRを出してみましょ。

でーけた!いいね。 だけど、これ思ったけど、先にlintを作らんとあかんな。 簡単に作りましょう。

lint作った。

ayumu1212.hatenablog.com

それをGithubActionsで実行してみる。

name: client-ci
on: 
  pull_request:
    types: [opened]
jobs:
  lint-run:
    runs-on: ubuntu-latest
    steps:
      - name: checkout pushed commit
        uses: actions/checkout@v3
      - run: cd nabelog/services/client/app/
      - name: set up node
        uses: actions/setup-node@v3
        with:
          node-version: 16.15.0
      - name: run lint
        run: |
          npm install
          npm run lint
    

そしたらエラーでた。

あぁ、フォルダ階層間違えたわ、修正して再プッシュ。 だけど、再プッシュしただけではGithubActionsは再度回ってくれない。 だから、onを変える。あとnode入れてからcdしよう。

name: client-ci
on: pull_request
jobs:
  lint-run:
    runs-on: ubuntu-latest
    steps:
      - name: checkout pushed commit
        uses: actions/checkout@v3
      - name: set up node
        uses: actions/setup-node@v3
        with:
          node-version: 16.15.0
      - name: run lint
        run: |
          cd services/client/app/
          npm install
          npm run lint
    

そしたらおーー!でけた。

CIの導入 - back

いろいろ調べてみたけど、まとまった記事があんまりなさげ。 これを参考

zenn.dev

どうやら、githubActionsにあるgoのlintはこれが一番ポピュラーっぽい。

GitHub - golangci/golangci-lint-action: Official GitHub action for golangci-lint from its authors

これを参考にファイルを作ってみた。

name: back-ci
on: pull_request
jobs:
  lint-run:
    runs-on: ubuntu-latest
    steps:
      - name: checkout pushed commit
        uses: actions/checkout@v3
      - name: set up go
        uses: actions/setup-go@v3
        with:
          node-version: 1.19.1
      - name: run lint
        uses: golangci/golangci-lint-action@v3
        with:
          version: v1.29
          working-directory: services/server/src

じゃあこれでPR出してみよう。 だが、エラーでた。lintのエラーというよりか、setupにおけるエラーっぽい。

GithubActionsのbackCIのエラー内容

言ってることは、「他のモジュールがimportできないよ」 これ、思ったんだけど、localで同じツール使ってないのに、いきなりCIで別のツール使うっておかしいよね。 ということは、actionsを使うんじゃなくて、clientみたいにコマンド実行した方が良いんじゃないかなとおもた。

だけど、そもそもgoimportsはformatterでありlinterではない。なので、エラーとして出力することができないように思えた。 なので、やっぱりこれで実行できるまでやってみる。 そして最終このようになった。

name: back-ci
on: pull_request
jobs:
  lint-run:
    runs-on: ubuntu-latest
    steps:
      - name: checkout pushed commit
        uses: actions/checkout@v3
      - name: set up go
        uses: actions/setup-go@v3
        with:
          go-version: 1.19.1
      - name: run lint
        uses: golangci/golangci-lint-action@v3
        with:
          version: latest
          working-directory: services/server/src

やったこと

  • set up goのversion指定でタイポしてたので修正
  • golangci-lint-actionをとりあえずversion:latestに

これで動くので一旦よし。

CDの実装

次はCDの実装をやっていくぅ。

CDの導入 - db

さぁこっからむずくなってくる。 なんせ、さくらVPSをgithubActionsから触らないといけないからね。 セキュリティどうなんやろう。

調べてみた結果、dbへのCDはまだ必要なさそう。DBサーバーのおおもとのスイッチングは流石に手動でやるし、現状サーバーが起動したらmysqlも動くようになっているし、migrateやseedの実行もDBサーバーでさせる予定がない。
これはREADMEに書けば大丈夫そう。

CDの導入 - back

方針決め

このサイトみてみた。

www.utakata.work

self-hosted runnerいいよって言ってるんだけど、たぶん本番環境でbuildして実行しちゃった方がよくない?っていってる。 それは少し困る。buildはgithubActions内でやって、scpでファイルを移動して実行してほしい。

いろいろ調べた結果、方針はこんな感じかと。

  1. mainにマージかpushされたことを検知してCD開始
  2. buildしてバイナリファイルをつくる
  3. バイナリファイルを本番環境に移動する
  4. systemctlをrestartする

バイナリファイルだけ本番環境に移動するのちょっと嫌なんだよね。。容量的には圧倒的にいいけど、開発環境と完全に同期してないから不安。だけどそんなのdockerを使ってないっていうところから一緒だからもういっか!w. あとは作りながらやってこ。

1. mainにマージかpushされたことを検知してCD開始

いろいろ調べたけど、記述的にはこう。

name: back-cd
on:
  push:
    branches:
      - main

これだと、mainに直接pushされた時も検知するので使い勝手が良い。 なお、検知しないのは以下のコード

name: back-cd
on:
  pull_request:
    branches:
      - main
    types: [closed]
2. buildしてバイナリファイルをつくる

じゃあ早速作ってこ。 なお、jobsより前は省略してる。

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout pushed commit
        uses: actions/checkout@v3
      - name: set up go
        uses: actions/setup-go@v3
        with:
          go-version: 1.19.1
      - name: start building
        run: go build main.go
        working-directory: ./services/server/src
      - name: deketa?
        run: ls services/server/src

これをやってみると、無事できてるーー。 バイナリファイルはGithub上にあげていないので、mainファイルがあるということはbuildできていますね。

githubActionsのCDテストの実行結果

3. バイナリファイルを別jobに移す

先ほどbuildしたファイルを本番環境に上げましょう!
job名はdeployとして、先ほどのjobと分けたい。そのため、さっきのjobからbuildしたファイルを取得する。
まずここまでやってみましょ。

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
・
・
      - name: archive build file
        uses: actions/upload-artifact@v3
        with:
          name: nabelog-server-build-main-file
          path: services/server/src/main

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: download build file
        uses: actions/download-artifact@v3
        with:
          name: nabelog-server-build-main-file
      - name: deketa?
        run: ls -l

githubActionsのCDテストの実行結果
githubActionsのCDテストの実行結果

いいね!できてる。じゃあ次はこれを本番環境にscpして送りましょ。

4. バイナリファイルを本番環境に移動する

これ参考

qiita.com

まずはGithubActionsとbackサーバーを繋ぐための鍵をローカルで作る。

# RSA形式で、4096bitで、ユーザー名とホスト名をコメントでつけて、file名も指定する。
$ ssh-keygen -t rsa -b 4096 -C "ユーザー名@ホスト名" -f id_rsa_nabelog_server_cd

でけたので、これの公開鍵をまずはbackサーバーに載せる。

backサーバーにログインして、~/.ssh/authorized_keysを開いてみるとすでに利用されているsshのpublicキーが登録されている。 ここの一番下に、さっき作成したpublicキーを貼り付ける。

これでできたのかな?ちょっとPCからログインしてみる。

$ ssh -i ~/.ssh/id_rsa_nabelog_server_cd ユーザー名@ホスト名

SAKURA internet [Virtual Private Server SERVICE]

おーーー!でけた。

次はGithubActionsで使うためにGithub秘密鍵を登録する。

Githubのレポジトリを開いて、 Settings > Security > Secrets and variables > Actions > New repository secretを押したら、以下の画面が出てくるのでキーを登録しましょう。

Githubのsecretkeyの登録画面
Githubのsecretkeyの登録画面

私は参考サイトをもとにこうしました。

じゃあ実際にCDで送ってみる。yamlファイルはこういうふうにやった。

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: download build file
        uses: actions/download-artifact@v3
        with:
          name: nabelog-server-build-main-file
      - name: scp for back server
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SK }}
          port: ${{ secrets.SSH_PORT }}
          source: "./main"
          target: "/usr/local/src/"
      - name: deketa?
        uses: appleboy/ssh-action@master
        with: 
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SK }}
          port: ${{ secrets.SSH_PORT }}
          script: ls -l /usr/local/src/

だけど、なんかエラーがでた。どうやらscpのコマンドでミスっているっぽい。ということは鍵系でミスってる? じゃあ、sshだけ実行できるようにしてみよう。

あれ、成功した。じゃあscpのコマンドがなんかミスっているっぽい。 sshコマンドはlocalもgithubAction上でもできたけど、そういえばscpコマンドは確かめてないな。

localでscpしてみる。すると、

$ scp -i ~/.ssh/id_rsa_nabelog_server_cd main ユーザー名@ホスト名:/usr/local/src/
scp: /usr/local/src//main: Text file busy

ん?忙しい?あーーー実行中だからか。 じゃあsystemctlで止めてからもう一回やってみよう。

$ ssh nabelog-server
# サーバーにログイン
$ sudo systemctl stop start-apiserver.service
$ sudo systemctl status start-apiserver.service
○ start-apiserver.service - Start nabelog API server
     Loaded: loaded (/etc/systemd/system/start-apiserver.service; enabled; vendor preset: disabled)
     Active: inactive (dead) since Sat 2023-03-04 12:56:18 JST; 3s ago
    Process: 533 ExecStart=/usr/local/src/main & (code=killed, signal=TERM)
   Main PID: 533 (code=killed, signal=TERM)
        CPU: 53.620s

$ exit
# localのterminalに戻る
$ scp -i ~/.ssh/id_rsa_nabelog_server_cd main ユーザー名@ホスト名:/usr/local/src/
main                                                                                100%   15MB 909.3KB/s   00:16

おおーーーでけた。これが原因だったとか? もう一回CDやってみましょ。

あとなんか環境変数の使い方まちがえていたっぽいから変えてみた。

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: download build file
        uses: actions/download-artifact@v3
        with:
          name: nabelog-server-build-main-file
      - name: download secretKey
        env:
          PRIVATE_KEY: ${{ secrets.SK }}
        run: |
          mkdir -p ~/.ssh
          echo "$PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
          chmod 700 ~/.ssh/id_rsa
      - name: scp for back server
        env:
          USER_NAME: ${{ secrets.SSH_USERNAME }}
          HOST: ${{ secrets.SSH_HOST }}
        run: scp -i ~/.ssh/id_rsa main ${USER_NAME}@${HOST}:/usr/local/src/
      - name: deketa?
        uses: appleboy/ssh-action@master
        with: 
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SK }}
          port: ${{ secrets.SSH_PORT }}
          script: ls -l /usr/local/src/

これでどうだ?

githubActionsのCDテストの実行結果

えーーーまだだめ?てかhostでずっと怒られているの何だろう。。 んーーなんか秘密鍵を別から持ってきたらいろいろ設定する必要があるっぽいな。

参考

zenn.dev

mgre.co.jp

これをもとにまたyaml変えてみた。

      - name: download secretKey
        env:
          PRIVATE_KEY: ${{ secrets.SK }}
          HOST: ${{ secrets.SSH_HOST }}
        run: |
          mkdir -p ~/.ssh
          echo "$PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
          chmod 700 ~/.ssh/id_rsa
          eval $(ssh-agent -s)
          ssh-add ~/.ssh/id_rsa
          ssh-keyscan -H ${HOST} >> ~/.ssh/known_hosts

おおーーー!成功した。 ちゃんとmainファイルの日時変更されているっぽいし、おk。

githubActionsのCDテストの実行結果

あとやること

  • yamlファイルのリファクタ
  • sshでsystemctlを停止して起動する処理追加
  • サーバーがちゃんと動いているか確認する処理追加
yamlファイルのリファクタ

うーーん、このappleboyさん、ちょっと不穏な感じがする。。 Issueがすごく溜まっている感じがするし、解消の仕方も雑に見える。。

なるべくactionはつかわない方針でいこう。

sshでsystemctlを停止して起動する処理追加

たぶん、こう。 これでやってみる。

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: download build file
        uses: actions/download-artifact@v3
        with:
          name: nabelog-server-build-main-file
      - name: setup secretKey
        env:
          PRIVATE_KEY: ${{ secrets.SK }}
        run: |
          mkdir -p ~/.ssh
          echo "$PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
          chmod 700 ~/.ssh/id_rsa
          eval $(ssh-agent -s)
          ssh-add ~/.ssh/id_rsa
          ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
      - name: systemctl stop main by ssh
        run: |
          ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "
            sudo systemctl status start-apiserver.service;
            sudo systemctl stop start-apiserver.service;
            sudo systemctl status start-apiserver.service;
          "
      - name: scp for back server
        run: scp main ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:/usr/local/src/
      - name: systemctl start main by ssh
        run: |
          ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "
            sudo systemctl start start-apiserver.service;
            sudo systemctl status start-apiserver.service;
          "

これやると、systemctl statusの時にサービスが止まっていたら、なぜかエラーを出す。どう調べてもそんなはずはないのに。 だから方針を変えて、systemctl is-activeを使ってこう書いてみた。

その結果、完璧すぎた。

 

サーバーの起動を確認する処理

これなんだけど、さっきの謎のバグを利用する。 systemctl statusを最後に仕込んどけば、inactiveの時に止まってくれる。

そしてあと、やっぱりif文をshell側に責任持たすのはちょっと嫌なので、GithubActions内でやることにする。

完成したコードがこれ 重要なのは以下

  • linuxコマンドでは変数代入の右辺で処理をしたいときは、$()ではなく``
  • outputsを扱いたいときは>> $GITHUB_OUTPUT
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: download build file
        uses: actions/download-artifact@v3
        with:
          name: nabelog-server-build-main-file
      - name: setup secretKey
        env:
          PRIVATE_KEY: ${{ secrets.SK }}
        run: |
          mkdir -p ~/.ssh
          echo "$PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
          chmod 700 ~/.ssh/id_rsa
          eval $(ssh-agent -s)
          ssh-add ~/.ssh/id_rsa
          ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
      - name: systemctl is-acitve main by ssh
        id: check_active_pre
        run: |
          IS_ACTIVE_STATUS=`ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "sudo systemctl is-active start-apiserver.service"`
          echo "IS_ACTIVE_STATUS=${IS_ACTIVE_STATUS}" >> $GITHUB_OUTPUT
      - name: systemctl stop main by ssh
        if: steps.check_active_pre.outputs.IS_ACTIVE_STATUS == 'active'
        run: ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "sudo systemctl stop start-apiserver.service"
      - name: main for back server by scp
        run: scp main ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }}:/usr/local/src/
      - name: systemctl start main by ssh
        run: ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "sudo systemctl start start-apiserver.service"
      - name: systemctl is-acitve main by ssh
        id: check_active_post
        run: |
          IS_ACTIVE_STATUS=`ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_HOST }} "sudo systemctl is-active start-apiserver.service"`
          echo "IS_ACTIVE_STATUS=${IS_ACTIVE_STATUS}" >> $GITHUB_OUTPUT
      - name: check active
        if: steps.check_active_pre.outputs.IS_ACTIVE_STATUS != 'active'
        run: exit 3

これで、デプロイまで完成した。

参考

docs.github.com

qiita.com

CDの導入 - client

最後はclientだ。 backのCDやったから何となくわかる。流れはこんな感じやろな。

  1. buildする
  2. buildしたフォルダをscpで送る
  3. nginxをsystemctlで再起動する

以上かな?やってく。

buildする

ひとまつbuildして、deployのjobに移すまで。

name: client-cd
on: pull_request
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: checkout pushed commit
        uses: actions/checkout@v3
      - name: set up node
        uses: actions/setup-node@v3
        with:
          node-version: 16.15.0
      - name: run build
        run: |
          npm install
          npm run build
        working-directory: ./services/client/app
      - name: archive build folder
        uses: actions/upload-artifact@v3
        with:
          name: nabelog-client-build-folder
          path: services/client/app/build/
  
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: download build folder
        uses: actions/download-artifact@v3
        with:
          name: nabelog-client-build-folder
      - name: deketa?
        run: |
          ls
          cd build
          ls
buildしたフォルダをscpで送る&nginxの再起動を行う

まずはclientサーバーの~/.ssh/authorized_keysにpublicキーを追加する。これはbackと同じものでよい。

それが終わったら、github秘密鍵の登録をする。これもbackと同じ作業。名前はSSH_CLIENT_HOSTにした

結果こうなった。

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: download build file
        uses: actions/download-artifact@v3
        with:
          name: nabelog-server-build-main-file
      - name: setup secretKey
        env:
          PRIVATE_KEY: ${{ secrets.SK }}
        run: |
          mkdir -p ~/.ssh
          echo "$PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
          chmod 700 ~/.ssh/id_rsa
          eval $(ssh-agent -s)
          ssh-add ~/.ssh/id_rsa
          ssh-keyscan -H ${{ secrets.SSH_BACK_HOST }} >> ~/.ssh/known_hosts
      - name: systemctl is-acitve main by ssh
        id: check_active_pre
        run: |
          IS_ACTIVE_STATUS=`ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_BACK_HOST }} "sudo systemctl is-active start-apiserver.service"`
          echo "IS_ACTIVE_STATUS=${IS_ACTIVE_STATUS}" >> $GITHUB_OUTPUT
      - name: systemctl stop main by ssh
        if: steps.check_active_pre.outputs.IS_ACTIVE_STATUS == 'active'
        run: ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_BACK_HOST }} "sudo systemctl stop start-apiserver.service"
      - name: main for back server by scp
        run: scp main ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_BACK_HOST }}:/usr/local/src/
      - name: systemctl start main by ssh
        run: ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_BACK_HOST }} "sudo systemctl start start-apiserver.service"
      - name: systemctl is-acitve main by ssh
        id: check_active_post
        run: |
          IS_ACTIVE_STATUS=`ssh ${{ secrets.SSH_USERNAME }}@${{ secrets.SSH_BACK_HOST }} "sudo systemctl is-active start-apiserver.service"`
          echo "IS_ACTIVE_STATUS=${IS_ACTIVE_STATUS}" >> $GITHUB_OUTPUT
      - name: check active
        if: steps.check_active_post.outputs.IS_ACTIVE_STATUS != 'active'
        run: exit 3

これでちゃんと動いた! CICDの実装は以上!

vscodeの環境はプロジェクトごとがいいよね?【備忘録】

はじめに

ぼくはVSCodeを使っている。
そしてプラグインをよく入れたりするし、設定もよく変える。
だが、このプロジェクトにはいるけど他のプロジェクトには入らないプラグインや設定ってあるよな。
つまり

  • どのプロジェクトにも共通して使いたいプラグインと設定
  • 特定のプロジェクトしか使いたくないプラグインと設定

を分けたいんです。

調査

しらべてき。

参考サイト

www.mitsue.co.jp

→ 何をgithubに上げて、何を禁止するか書いてある。

qiita.com

→ そもそものやり方。setting.jsonの分け方教えてくれる。

maku.blog

→ お作法を教えてくれる。

amateur-engineer-blog.com

→ 言語ごとに設定変えれるの?驚き。

code.visualstudio.com

→ 公式。いっちゃん助かった。やっぱり一次情報。

考察

てかこれって、マイクロサービスだった場合、serviceごとがいいのかな?projectごとがいいのかな?どっちもできそうではある。

これ3階層じゃないかな?

  1. ローカル環境依存の設定
  2. プロジェクト依存の設定
  3. サービス依存の設定

だとするとこれ全部分けてみようか。ここまでやる必要あるか?とも思うが、単一責務だとも思う。

(...数時間後)

こんな文が公式から出てきた。

Note: A VS Code "workspace" is usually just your project root folder. Workspace settings as well as debugging and task configurations are stored at the root in a .vscode folder. You can also have more than one root folder in a VS Code workspace through a feature called Multi-root workspaces. You can learn more in the What is a VS Code "workspace"? article.

公式に「こう思うよね?w」って言われてるようで腹たつ。 まぁみてみよう。

code.visualstudio.com

みたけども、ちょっとだけ意図とは違った。以下のようなことができる。

同じVSCodeのwindowでいろんな階層をrootとして開けるんだと。 そうすると、それぞれの階層ごとのsetting.jsonは競合しちゃうんだけど、それをworkspace.jsonという形でプロジェクト依存のsetting.jsonみたいに修正できる。 かつそれを保存できるから永続化も可能。

だーーけーーどーー、そもそもdockerでserviceを開いた時にこのworkspace.jsonを開けないんじゃ意味ないやん。

つぎここから

Visual Studio Code User and Workspace Settings

だけど、みてみるとこれが書いてあった

構成は、異なる設定スコープによって複数のレベルで上書きできます。次のリストでは, 後期のスコープは以前のスコープをオーバーライド:

リモート設定ができるっぽい? もうちょい調べてみよう。

お、こういうこと

Developing inside a Container using Visual Studio Code Remote Development

だけど、こんなに強制力を持たせなくてもいいかな。

と、ここまで調べてみて、結論がでた。 まず、階層は3つでよい。

  1. ローカル環境依存の設定
  2. プロジェクト依存の設定
  3. サービス依存の設定

だけど、2と3は1の環境を引き継ぐけど、3は2の環境を引き継がなくてもいい気がする。 なぜならそのような状態が必要なことはほぼないし、あったとしても2と3の環境にそれぞれ設定を記述するで対応をすれば良いと思う。

また、DevContainerという拡張機能でサービスごとの開発環境を開くのだが、その時に2で適用している拡張機能は3の時にレコメンド表示してくれるため、2の環境の一部を3に引き継ぎたい時も容易かと思われる。

方針結論

以下の3段階にvscodeの設定を変えていく。

  1. ローカル環境依存の設定
  2. プロジェクト依存の設定
  3. サービス依存の設定

だが、3の環境は2の環境を拡張するような設定ではなく、あくまで2と3は独立しているものとする。

やってき

まぁ簡単な話、分けるだけ。 階層構造は以下。

  1. ローカル(ユーザー)依存の設定:~/Library/Application Support/Code/User/settings.json
  2. プロジェクト依存の設定:プロジェクトフォルダのrootに.vscode/を用意して設定
  3. サービス依存の設定:サービスごとのworking_dirのrootに.vscode/を用意して設定

じゃあ試しに自分のやつを公開します。

1. ユーザー依存の設定

以下が設定ファイル それぞれの言語に依存していた設定はそれぞれのサービスに移動させている。

{
  "editor.accessibilitySupport": "off",
  "editor.tabSize": 2,
  "emmet.triggerExpansionOnTab": true,
  "security.workspace.trust.untrustedFiles": "open",
  "editor.formatOnSave": true,
  "codic.ACCESS_TOKEN": "mU2hEdPGcRbrWeRGk9sDH5aQl1OBjmlvxv",
  "codic.case": "snake_case",
  "explorer.confirmDelete": false,
  "terminal.integrated.scrollback": 10000,
  "editor.fontFamily": "Menlo, Monaco, 'Courier New', monospace, 'MesloLGS NF'",
  "git.autofetch": true,
}

拡張機能はどのプロジェクトでも使うであろうものにした。

2. プロジェクト依存の設定

setting.json に関してはなかった。プロジェクト固有で持ちたい設定もなかったし、他の開発者に強制したいような設定もなかった。

拡張機能に関しては、他の開発者に強制したいものがあったので、設定。 dockerとDev Containersの2つ。

{
  "recommendations": [
    "ms-vscode-remote.remote-containers",
    "ms-azuretools.vscode-docker"
  ]
}

3. サービス依存の設定

大きく分けて、フロントエンドとバックエンドに分けた。 今回だったらクライアントはTypescriptで記述しており、バックエンドはGolangで記述している。

フロント

linterとprettierの設定。

{
  "editor.defaultFormatter": "esbenp.prettier-vscode", //デフォルトのフォーマットはprettierを指定
  "editor.formatOnSave": true, //保存したときにフォーマットする
  "editor.codeActionsOnSave": {
    //ESLintに対応しているファイルはESLintでフォーマット
    "source.fixAll.eslint": true
  },
  "typescript.updateImportsOnFileMove.enabled": "always"
}
{
  "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
}
バック

こちらもformatterの設定のみ。

{
  "go.toolsManagement.autoUpdate": true,
  "emeraldwalk.runonsave": {
    "commands": [
      {
        "cmd": "goimports -w ${file}",
        "match": "\\.go$"
      }
    ]
  }
}
{
  "recommendations": [
    "golang.go",
    "emeraldwalk.runonsave"
  ]
}

快適なVSCodeライフを。

TypeScriptとGolangにformatterを適用してみた【備忘録】

はじめに

実装してみたプロジェクトにCICDを導入しようとしたんだが、 そもそもCIにやってほしいことがformatterくらいしかしらん。 そしてまだ導入してなかった。 じゃあ導入しよう(いま)

ぼくの前提知識

  • TypeScriptがESLintでできることは知ってる、Prettierも。
  • Goはわからん。
  • VScodeとも連携できたらうれぴい
  • formatterの設定
    • import文の順序決め
    • 変数のケース決め
    • 関数のケース決め
    • ファイル名のケース決め

調査

なんか、自分のプロジェクト調べてみたら、TypeScriptはやってたらしい。。けどまとめる。とりあえず調査はGolangを中心にやってみよう。

zenn.dev

gofmtgoimportsgoreturnsgolinesのどれかが選択肢で、この人はgoreturnsを選択

github.com

→ うわ。Golangすげぇ、公式でこんなわかりやすいdocument出してるのか。これが、formatterの基礎となりそう。

blog.y-yuki.net

→ あ、gofmtって公式なのか。知らんかった。goreturnsの立ち位置が強くないな。

alenkacz.medium.com

→ 比較記事。gofmtgoimportsgoreturnsの比較。

gofmt = golang formatter
goimports = gofmt + fixing imports
goreturns = goimports + return statement syntactic sugar

この方はCLIではgofmtで、IDEではgoreturnsと言っているが、、goreturnsだけでいい気がする。

log.include.co.jp

goreturnsの実装を書いてくれてる。

github.com

→ いやこれ最終更新が2018年!?5年前から止まっとるんかい。やめよ。

実装

  • Golanggoimports
  • TypeScriptはESLintPrettierでいこう

TypeScript

じつはこっち作ってたので、共有。 てか、リンターとフォーマッターって意味が違うのか。

  • リンター:コードチェックをしてくれる。整形は基本しない。ESLintとか。
  • フォーマッター:コード整形をしてくれる。Prettierとか。
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: ['plugin:react/recommended', 'standard-with-typescript'],
  overrides: [],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    project: './tsconfig.json',
  },
  plugins: ['react', '@typescript-eslint', 'unused-imports', 'import'],
  rules: {
    'comma-dangle': ['error', 'only-multiline'],
    '@typescript-eslint/comma-dangle': 0,
    'space-before-function-paren': 0,
    '@typescript-eslint/space-before-function-paren': 0,
    '@typescript-eslint/no-unused-vars': 'off', // or "no-unused-vars"
    'unused-imports/no-unused-imports': 'error',
    'unused-imports/no-unused-vars': [
      'warn',
      { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' },
    ],
    'import/order': [
      'error',
      {
        // グループごとの並び順
        groups: [
          'builtin', // 1. fsや path などの node "builtin" のモジュール
          'external', // 2. npm install したパッケージ
          'internal', // 3. webpack などでパス設定したモジュール
          ['parent', 'sibling'], // 4. 親階層と小階層のファイル
          'object', // object"-imports
          'type', // 型だけをインポートする type imports
          'index', // 同階層のファイル
        ],
        // グループごとに改行を入れる
        'newlines-between': 'always', // "never" を指定すると改行なし
        // FIXME: ちょっとよく分かってない
        // This defines import types that are not handled by configured pathGroups. This is mostly needed when you want to handle path groups that look like external imports.
        pathGroupsExcludedImportTypes: ['builtin'],
        // アルファベット順・大文字小文字を区別しない
        alphabetize: { order: 'asc', caseInsensitive: true },
        // パターンマッチしたものをグループにする
        // "newlines-between": "always" の場合は pathGroups  ごとに空行が入る
        pathGroups: [
          // react 関連を external より前にする
          // "pathGroupsExcludedImportTypes": ["react"], にしてみたが `react`, `react-dom` などが別グループになってしまったので pattern で無理やり同じグループにした
          {
            pattern: 'react**',
            group: 'external',
            position: 'before',
          },
          // `@/entity` の import をグルーピング
          {
            pattern: '@/entity/**',
            group: 'internal',
            position: 'before',
          },
          // `@/pages` の import をグルーピング
          {
            pattern: '@/pages/**',
            group: 'internal',
            position: 'before',
          },
          // `@/components` の import をグルーピング
          {
            pattern: '@/components/**',
            group: 'internal',
            position: 'before',
          },
          // CSS module を一番最後に
          {
            pattern: './**.module.css',
            group: 'index',
            position: 'after',
          },
        ],
      },
    ],
  },
}
module.exports = {
  printWidth: 120, //1行の文字列を120文字にする
  singleQuote: true, //ダブルに代わりシングルクオーテーションを使う
  trailingComma: 'es5', //複数行の場合は可能な限り末尾のカンマを表示
  semi: false,
};

基本的にやったことは以下

  • 1行の文字数設定
  • シングルクオートに統一
  • 配列が複数行の場合は末尾にカンマ
  • importsの順番を整形

参考は以下

chaika.hatenablog.com

Go

goimportsを使っていきます。

pkg.go.dev

nishinatoshiharu.com

goimportをCLI で実行できるように

じゃあinstall

$ go install golang.org/x/tools/cmd/goimports@latest
warning: GOPATH set to GOROOT (/usr/local/go) has no effect
go: downloading golang.org/x/tools v0.6.0
go: downloading golang.org/x/sys v0.5.0
go: downloading golang.org/x/mod v0.8.0
$ ls $GOPATH/bin
go         gofmt      goimports

入ったっぽいので、早速実行

$ go imports -w main.go

おーーー!修正された! goのimports文も修正されたし、インデントも整形された。 Goらしい書き方があんまりわかってないので、とりあえずdefault(gofmt)のままでいいかな。

だけど、これが保存のたびにされてほしいな。 それこそVSCodeのprettierのプラグインみたいに。

goimportsを保存のタイミングで実行できるように

こちらのVSCodeでの設定でやる方法をしてみたのだが、うまく行かなかった。 import文は保存のタイミングで整形されたのだが、インデントとかが治らなかった。

Go言語のためのVisual Studio Codeの設定方法 | Casual Developers Note

いーーろいろ探したけど、これででけた!たぶんベスト。

zenn.dev

まず、VScode拡張機能の"Run on Save"をinstallする。

そのあと、vscodeの設定jsonファイルに行ってこれを追記。

  "emeraldwalk.runonsave": {
    "commands": [
      {
        "cmd": "goimports -w ${file}",
        "match": "\\.go$"
      }
    ]
  },

いわゆる、保存のたびにCLIを回しましょってやつですね。 するとできた!! これで保存のたびにgoimportsが効きますね。

なんかGitHubから「mainブランチ守って!」って言われた【備忘録】

アラートは突然に。

いきなりこんなの言われた。

Your main branch isn't protected
Protect this branch from force pushing or deletion, or require status checks before merging.

ほう、守ってくれるのか。 ぜひやってくれ。

やってみよう

参考

dev.classmethod.jp

ver1

CICDの設定前だったので、ここまで。

設定画像

考慮したこと

Your GitHub Free plan can only enforce rules on its public repositories, like this one.

へーー、無料だと公開レポジトリだけつくのか。

Require a pull request before merging

そうだね、pull requestからに制限した方が、ログが残るし良さそう。このプロジェクトは個人開発だから承認人数は特に設けない。

Require status checks to pass before merging

これはCIのチェック通った後にしますかーー?ってやつだね。そうしたい。だけど、まだCI作ってないからまたあとで。

GitHubで複数種類のCI/CDサービスと連携時の必須ステータスチェックの動作を確認してみた | DevelopersIO

Require deployments to succeed before merging

これも同様にやりたいけども、まだCDを作ってないから今はできないな。またあとで。

これら以外は特に入らなそう〜〜

ver2

CICDの設定ファイル作ったらまたこれ編集する。