GitHub Copilot in Visual Studio Codeを試してみた

かねてから気になっていたGitHub Copilotを試してみることにした。開発効率やコード品質の向上に繋げられるなら月額10$くらい安いものだが、その実力やいかに。

サインアップ

有償サービスなのでサインアップが必要である。自分が見たときは、クレジットカード/Paypalが使えるらしかった。クレジットカードを選択し、その情報と住所・名前を入力して確認すれば登録はすぐに完了した。

VSCodeを使うので、こちらのマニュアルからVSCodeに導入した。

連携の承認が必要だったが、イントロダクションに従ってポチポチクリックするだけであっけないほど完了。

20221127161342

迷うところは特になかった。

使ってみた

公式のAboutによれば、

GitHub Copilot is an AI pair programmer that offers autocomplete-style suggestions as you code. (GitHub Copilotはオートコンプリートでやるみたいにお勧めをあなたのコードに提供するAI ペアプログラマーだよ!)

だそうである。具体的にオートコンプリートはどのような場面で起動するのか、であるが、基本はコメントであるようだ。そこでコメントを下記のように記載してEnterを押すと

# ファイルを読み込む

下記のようなコードが表示された。

# ファイルを読み込む
def read_file():
    # ファイルを開く
    with open("data.txt", "r") as f:
        # ファイルの内容を読み込む
        data = f.read()
        # ファイルを閉じる
        f.close()
        # ファイルの内容を返す
        return data

TABを押すことで確定させられる。

"おいおい兄弟、ハードコーディングは勘弁してくれよ"と思うかもしれないが(自分は思った)、これはコメントのつけ方が悪い。下記のようにコメントを記載して出し直すと

# ファイルをファイル名から読み込む

ちゃんと引数を使った関数が表示された。素晴らしい。

# ファイルをファイル名から読み込む
def read_file(file_name):
    # ファイルを開く
    with open(file_name, "r") as f:
        # ファイルの内容を読み込む
        data = f.read()
        # ファイルを閉じる
        f.close()
        # ファイルの内容を返す
        return data

欲を言えば型もちゃんと指定してほしかったが、Pythonは型指定なしで書かれることも多いので、その影響だろう。十分、ともいえる。

ちなみに選択肢があればCTRL+Enterで複数の候補を表示させることもできる。

20221127164555

関数名が重要?

関数名に影響を受けるらしく、単純な動詞のほうが候補が出やすいようだ、scrapeだけであれば下記のようにそれらしいコードが表示されるのだが

20221127164204

一方で、scrape_dataとすると候補が出なかった。

20221127164207

元々のコードのベースから確からしいコードを推測して出しているのだろうから、これは納得いく理由。今ある労働のいくつかは人がAIに合わせるようになるであろうと思っていたが、プログラミングの世界がすでにそうなっていたことを感じさせる。

具体的な、いわゆる下位問題を解決するための関数の作成には、かなりの威力を発揮しそうな雰囲気である。実に素晴らしい。

ある程度抽象的な関数にも対応してくれるのか?

理想は抽象的なビジネスモデルをブレイクダウンしてコードにまで落とし込んでくれることだが、さすがにそれは望み過ぎとして、実際どの程度であれば抽象的な問題にも対応できるのか。

というわけで下記のコード(C#)を自前で書いた上で、

class Member{
    public int Id  {get; set;}
    public string Name {get; set;}
}

class MemberServiceで利用してみたところ、下記のようになった。

class MemberService{
    public Member GetMember(int id){
        return new Member {Id = id, Name = "John"};
    }

    public void SaveMember(Member member){
        //save member
    }

    public void DeleteMember(int id){
        //delete member
    }
}

まあ悪くない。EFを使ってくれても良かったか? しかしこんなものだろう。

むしろ驚いたのはTestMemberServiceと打ってCTRL+Enterで候補を一覧表示させたときのことで、なんと下記のようなコードが表示された。

class TestMemberService{
    public void TestGetMember(){
        MemberService memberService = new MemberService();
        Member member = memberService.GetMember(1);
        Assert.AreEqual(1, member.Id);
        Assert.AreEqual("John", member.Name);
    }

    public void TestSaveMember(){
        MemberService memberService = new MemberService();
        Member member = new Member {Id = 1, Name = "John"};
        memberService.SaveMember(member);
        //assert if member is saved
    }

    public void TestDeleteMember(){
        MemberService memberService = new MemberService();
        memberService.DeleteMember(1);
        //assert if member is deleted
    }

}

なにこれ凄い。しかも驚いたことに、既存クラスのプロパティが変わると追跡した(!)。これは楽である。

実際どう?

もっと使ってから判断すべきではあるが、極めて便利なツールと感じる。少なくとも自分の中では、プログラミングを行い続ける間、Github Copilotを是非使い続けたいと思う。

AWSでなぜコンテナ・Dockerを使うのか?

Software Design (ソフトウェアデザイン) 2022年11月号で"なぜコンテナ・Dockerを使うのか?"という問いかけがなされていたので、自分も思うところを書いてみたい。

コンテナのメリットは?

自分が思うコンテナのメリットは次の3つ。

  1. 環境を再現しやすい
  2. 一つ一つのコンテナの設定が分離しているので把握しやすい
  3. AWS ECSやECRといった様々なマネージドサービスに乗っかれる

1. 環境を再現しやすい

なんといっても環境を再現しやすい。ホストに直接ミドルウェアをインストールしつつ動かす方式だと、ホストに施した設定に依存する点が多く、"〇〇だと動いたのに!"なんてことが大変多い。下手をすると、本番環境で使いたいOSだと動作しない、などということさえ出かねない。

この点、コンテナで動かすようにしていると、基本的にコンテナが動くなら環境を選ばない(※)。これはC#.NETのようにどちらかというとマイナー気味なフレームワーク(失礼)を使う際には大きな安心感となる。

※ ただし絶対ではない。例えばARM/x64のようにCPUのアーキテクチャが違うとか。

またチーム開発の場合、別のメンバー(含む六か月後の自分)に同一の開発環境を提供するのも容易い。これも嬉しいところ。

2. 一つ一つのコンテナの設定が分離するので把握しやすい

一つ一つのコンテナに必要な分をDockerfileにまとめることになるため、それぞれの記載内容が自然と限定される。これは大きなメリットで、どこになにが依存しているのかわかったものじゃない、という惨状を避けることができる。

肥大化した超巨大な設定群がどこにどう依存しているのかよくわからないままモリッとかけられていて、もうなにが必要でなにが不要なのかもわからない、今さら各モジュールごとに分離するのも至難を極める、なんて地獄の状況に陥りづらい、ということである。これはとてもとてもとても助かる。

3. AWS ECSやECRをはじめとする様々なマネージドサービスに乗っかれる

ECSにはAutoscaling(ということはAuto Recoveryも含む)やデプロイ、ロールバックといった仕組みが予め備わっている。ビルドした結果を保存するためのECRもある。これらを自前の仕組みで構築しなくて済むのは大変に助かる。

CPUやメモリをはじめとするメトリクスも元から使えるので監視のためのソリューションを入れる必要もなく、大変よろしい。

これが自分の中では最大の理由で、もしこれらの優秀なマネージドサービスがなかったらコンテナ化には二の足を踏んだことだろう。

ではコンテナ化のデメリットは?

構成が複雑化する。この結果、構築に一手間増える他、トラブルシューティングに苦労しやすい。

それに見合うだけのメリットは十分にあるのだが、ECSで構築した一連のリソース群がうまく動かなかった時はウッとなる。ネットワーキングからアプリまで、あちこちに障害の原因があり得るので切り分けが大変なのだ。

EC2じゃダメなんです?

EC2ではダメということはない。ダメということはないのだが、トラブルシューティングはどちらかというとEC2直で運用しているほうがやりやすかったりもするのだが、EC2上でプロセス監視を入れてオートリカバリを入れてディスク監視入れてメモリ監視入れてCPU監視入れてホストのセキュリティパッチあてて、とやりながらデプロイどうしようとかロールバックどうしようとかあれこれあれこれ考えて構築しないといけないのはなかなか重い作業で、正直特に理由がないならEC2直は避けたいのが本音である。これらはサービスを動かすための要因でしかなく、頑張って作ってもサービスの使い勝手がよくなるわけではない――不便になるのをある程度防ぎはする――という点も悩ましい。

オンプレで物理サーバーを自前で運用することを思えば、EC2も隔世の感がある素晴らしいサービスなのだが、ECSに慣れてしまうと、うん。

Cannotpullcontainererror: pull image manifest has been retried 5 time(s): failed to resolve ref docker.io/library/nginx:latest: failed to do request

先の記事の後にもIPv6 Only VPCを試していたのだが、思わぬところに落とし穴がやはりあった。

問題

"Cannotpullcontainererror: pull image manifest has been retried 5 time(s): failed to resolve ref docker.io/library/nginx:latest: failed to do request"

Fargateを試していたのだが、どういうわけかDockerHubからコンテナが取得できない。上述のエラーメッセージが表示される。

なんとDockerHubがIPv6非対応らしい。

解決

仕方ないのでSubnetにIPv4を設定し、IPv4/IPv6デュアルスタックにして対応したところ解決した。しかしこの対応だと、Private Subnetに置くためにはNatゲートウェイが必要になるわけで、なんともトホホな結果である。

SecurityGroupで接続元をALBに限定すればpublic subnetに置いていてもおかしな通信を叩き込まれることはないはず、と言えばそうなのだが、SGの設定を間違えると素通しになるわけで、あまり嬉しくない防御法である。

その他

ちゃんと検証していないが、こちらの記事にもある通り、Target GroupもIPv4を使っていそうな雰囲気。

あとECRがIPv6対応しているのか公式ドキュメントを探しても記載箇所が見つからない。これもかなり心配な感じである。こちらはGatewayを使えばいいのかもしれないがegress only IGWで対応できないとなるとコスト面で(業務用なら屁でもないのだが)辛みがでる。

総じてIPv6 onlyはまだまだいばらの道という感がある。IPv6自体は便利だと思うのだが、周辺サービスがついてきていない。業務用だと、当面はIPv4を大人しく使うか、デュアルスタックで部分的に導入するか、といった選択になるのではなかろうか。見切り発車でIPv6 onlyを選択すると大変苦労するであろう。

IPv6オンリーのVPCで遊んでみた

タイトルの通り、IPv6オンリーのVPCを試してみた。以前の記事IPv6オンリーは業務用だと要注意、と書いたばかりだが、個人用途で試す分にはイケイケでいける。NAT ゲートウェイがいらない(Egress Only IGWが使えるので)ため安くあがる点が個人用途だとポイントである。

ネットワーク上の主な違い

  • SubnetにIPv4を振らない。
    • VPCにはIPv4を設定する必要があった。
  • NAT Gatewayがいらない。
    • 代わりにEgress-Only Internet Gatewayを使う。固定費がかからないNATゲートウェイみたいなものである。
  • Elastic IPがいらない。
    • IPv6だから元から固定IPとして使える。
  • 全ての場面でIPv6を使うことになる。
    • ローカルのPCからSSHで接続するのもIPv6である。
    • "いかん、ここはIPv4でないと困る"みたいなことがあっても対応不能である。
      • 大抵問題ないはずだが、"動くはず"と"動いた"は違う
        • という問題があるので業務用でIPv6オンリーはなかなか怖いわけだが。

気づいたこと

遊んでいて気になったことがいくつかあった。やはりIPv6オンリーで利用すると細かいところが違ってくる。

IPアドレス直での管理は避けたほうが良い

公式リファレンス

IPv6 CIDR ブロックと VPC の関連付けを解除できます。VPC から IPv6 CIDR ブロックの関連付けを解除すると、IPv6 CIDR ブロックと VPC を後で再び関連付けた場合に同じ CIDR を受け取ることは期待できません。

とある。従って、VPC内のEC2を管理するにあたって /etc/hostsにIPを直接記載したり、プログラム内でIPを直接記載したり(あまり良い造りではないが)すると変化に弱くなる。もしIPを振り直しになった場合、直接記載した部分を全部修正しないといけなくなるだろう。面倒に感じても、DNSを使ってホスト名で管理したほうが良い。

ElasticIPがいらない

公式ドキュメントによると

インスタンスを停止しても、次のものは保持されます。
IPv6 アドレス。

だそうである。実際、停止→起動を行ってみても保持されたので、本当だと思われる。つまりElasticIPを使う必要がない。例えば、踏み台サーバーを普段止めておいて使うときだけ起動する使い方をしても、EIP料金を取られなくていいわけだ。地味に嬉しいポイントである。

一方で、常に固定IP状態とも言えるので、セキュリティには十分気を使わないといけないが。

書式がIPv4と異なる

IP直で接続する場合、IPv6は[]で囲む必要がある。そのためWEBサーバーにアクセスする場合でも、IPv4ならhttp://192.168.1.1/のように接続できるが、IPv6だとhttp://[xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]/のように記載しないといけない。

20221115114157

これはSSHで接続する場合も同じで、configの記載が微妙に異なる。

Host bastion
    HostName xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:79c2


Host develop-target
    HostName xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:670b

Host develop-*
    ProxyCommand ssh bastion -W [%h]:%p <- ここである。
    #   ProxyCommand ssh bastion -W %h:%p <- IPv4ならこう書く。

Host *
    Port 22
    User ec2-user
    IdentityFile ~/.ssh/test.pem

多段SSHでPrivate SubnetにあるEC2インスタンスに接続する

Private SubnetにEC2インスタンスを立てて使う場合、踏み台を経由して接続できるようにしたほうが色々と便利なことが多い。

というわけで多段SSHの設定を書き残しておく。

結論

~/.ssh/config に、ProxyCommandを使いつつ、例えば下記のように記載する。

bastion(踏み台)がPublic SubnetにあるEC2インスタンス、targetがPrivate SubnetにあるEC2インスタンス

Host bastion
    HostName xxx.238.91.108 <- public ip

Host develop-target1
    HostName 10.0.72.5 <- private ip

Host develop-target2
    HostName 10.0.72.6

Host develop-*
    ProxyCommand ssh bastion -W %h:%p

Host *
    Port 22
    User ec2-user
    IdentityFile ~/.ssh/test.pem

同じ設定はまとめたほうが楽なのでまとめているが、まとめずにちゃんと全部記載するなら参考のページを参照。

これでssh develop-target1のようにコマンドをたたけば接続できる。

参考

AWSのVPCでIPv6にするべきか?

AWSVPCIPv6が使えるようになってからしばらく経つ。

そろそろIPv6にすべきなのか、IPv4にすべきなのかを考えてみた。

結論

  1. 特にIPv6を使う理由はなく判断しかねるならまだIPv4にしておいたほうが無難。
  2. IPv6だけで行けると確信できるならIPv6オンリーに。
  3. IPv4を排除しきる自身はないがIPv6を使いたい理由があるならデュアル構成。

業務用だとまだ1のほうが無難な感がある。なんならIPv6が必要になった後でIPv6を導入して3に移行とか。2022年の今でも、ごった煮で雑多なサービスを詰め込むようなケースでは、見切り発車的にIPv6オンリーにするのは危なそうな感じ。

IPv6のメリットは?

公式の見解はこちらだが、個人的にはEgress-Only インターネットゲートウェイを使える点と、CIDRの設計に悩まされずに済む点がメリットかなあ、という気がしている。

その他、パフォーマンスの向上は期待すべきでないらしいが、EKSIPv6を使ったほうがネットワークルーティング設定をシンプルにできるとかで、時折例外があるようなので、使うつもりのサービス+IPv6で調べておくのもいいかも。

注意点

AWSのサービス自体、IPv6完全対応とは言い難い。簡単に作り直せるサービスならいいが、いったん走りだしたら手出しが難しくなるもの(業務用は通常そうだと思うが)は見切り発車にしないほうがいい。

参考

AWS CLIをMFA付きで使う際のbatファイル(Windows用)

公式で公開されているMFAのためのコマンドを毎回コピペコピペで実行するのは嫌すぎるので、簡略に――作り込むほどのものではないので――バッチファイルを作って使うことにした。

言うまでもなくWindows用である。

内容

一部マスキングしているが、下記のような内容となる。

set AWS_ACCESS_KEY_ID=
set AWS_SECRET_ACCESS_KEY=
set AWS_SESSION_TOKEN=

aws sts get-session-token --serial-number arn:aws:iam::XXXXXXXXX:mfa/XXXXXXXXXX  --token-code %1 > tmp.json

for /f "usebackq" %%A in (`type tmp.json ^|  jq -r ".Credentials.AccessKeyId"`) do set AWS_ACCESS_KEY_ID=%%A
for /f "usebackq" %%A in (`type tmp.json ^|  jq -r ".Credentials.SecretAccessKey"`) do set AWS_SECRET_ACCESS_KEY=%%A
for /f "usebackq" %%A in (`type tmp.json ^|  jq -r ".Credentials.SessionToken"`) do set AWS_SESSION_TOKEN=%%A

--serial-numberには、AWSのマネジメントコンソールのSecurity credentialsのSign-in credentialsのAssigned MFA deviceにある値をハードコーディングしている。外部設定ファイルに入れたほうが使いまわしが効くのだが、業務用で他の人に配るならともかく、完全に個人用なので省略した。テンポラリファイルも作らなくてもなんとかする方法がありそうだが、これもわざわざ時間をかけて作り込むメリットがないのでオミットした。おかげで製作時間は多分10分くらいである。

余談ながら背景

先の実験でコンテナからTerraformを使うのは微妙という判断とあいなり、WSLを使ってみたところこれまたTerraformのコマンドキャンセルでコンソールから追い出される問題に遭遇し、しかたないので普段はWindowsからTerraformを使う状況に戻ってしまった。そこでしぶしぶ作ることにしたものである。

つまりAWS CLIを動かすのは途中経過で、Terraformを動かすのが最終目標なのだが、そのあたりの事情はバッチファイルの内容と対して関係しないため、タイトルはAWS CLI用とつけている。

参考