AWS Codeシリーズについてハンズオンを試してみた。

## はじめに

SAAを取ったはいいものの就職自体はさっぱりにっちもさっちもいかないのでせっかく取った資格を腐らせないというのと、元々次はDeveloperアソシエイトを狙っているのと、単純にSAA勉強していて興味がある分野の技術だったのでハンズオンしてみました。

結論から言うと非常にわかりやすく、GitやGithubを日頃から利用しているあるいはちょっと触ったことがあるだけの人でも理解できる内容なので興味がある人にはおすすめです。
利用料金も軽く遊ぶくらいなら全然無料枠も超えないという親切設計……

[ハンズオンはこちらから](https://pages.awscloud.com/JAPAN-event-OE-Hands-on-for-Beginners-cicd-2020-reg-event-LP.html?trk=aws_introduction_page)

## おさらい

ハンズオンに入る前に軽くおさらいをします。

### CI/CDって？

Continues Integration/Continues Deliveryの略。
つまり、継続的にアプリケーションを提供するためのアレコレを総称がこのワード。
前半部分が主に一連のパイプラインを構築・整備・自動化することを指し、後半は特に開発がメンテや新規コード追加の際のバグチェック等のテストを自動化し、リポジトリにプッシュするという行為(デリバリー)と、開発環境で更新した内容を本番環境にマージしリリースする処理を自動化する(デプロイ)行為を指す。

### なんでCI/CDって大切なの？

ざっくりいうと、現代社会じゃもうビジネスも商品展開も何もかもが高速で進化したり、消費されたりするので作りきりではなく、顧客やユーザーの反応に応じて常にリアルタイムで改善を続けていくような体制が求められるからという感じ。
そのためにはソースの管理、ビルド、テスト、デプロイの行程はなるべく自動化したいし、そのラインも統括的に構築・整備できたら楽だよなぁと。
私は思想的には最近のゲームがとりあえず出してみて、まずいことがあったら後々修正パッチを配布したりDLCを作ったりするようなことと同じ感覚なのだと思ってる。
顧客やユーザーのニーズは尽きることもなく、常に変化するし、バグもリリース前にすべて潰せるわけでもなければ想定外のこともあるだろうと思うのでこのCI/CDという思想が今の世の中でスタンダードになるのは必然的なのかなぁと感じてしまう。

### Codeシリーズって何？

AWSえもんにCI/CDをやりたいよーというとしょうがないなぁといって提示されるサービス群。
以下の5つのサービスがある。

### CodeCommit

リポジトリを管理するためのサービスでフルマネージドサービス。
役割としてはGithubと似たようなもの。
ただし、Forkとかはできないらしい。

### CodeBuild

ビルド担当。
ソースコードのコンパイル、テスト実行、ソフトウェアパッケージの作成を実行できる。
ざっくりいうとソースコードが設計図なので、それをコンパイルしてテストを行い、実際のアプリケーション等を構築してくれるもの。
ちなみにビルドサーバーをわざわざ用意しなくてもいいという利点もある。
ただし、コンテナサービス開発でCircleCIを使ってる人からすると割と微妙なサービスという声を聞く。

### CodeDeploy

デプロイ担当。
具体的にはEC2インスタンス等にエージェントと呼ばれるものをインストールして、それを常駐させることによりそのエージェントがCodeDeployに対してポーリングを行えるようにすることで、ソースコードの変更を感知すると変更内容をPullしてデプロイを行うという仕組みらしい。
デプロイグループを作成し、それを識別するタグをEC2インスタンスに付与することでそのインスタンスにデプロイを行えるようになる。
これがあることでインスタンスのスケールアウトにも対応できる。
利用するにはappspec.ymlという指示書を作成してソースに配置する必要がある。

### CodePipeline

パイプライン作成担当。
これを利用することでこれまでの各工程を可視化し、一連の処理として統合的に管理することができる。
具体的にはPipelineという大枠を作って、各工程をStageとして配置してそこに各Stageごとのタスクを記述して作成する。
これを利用すると

・ソースを変更
・開発環境にデプロイ
・承認するか否かのタスクを行うStage
・承認が降りたら本番環境にデプロイ

なんて工程も自動化できる。
これを利用するとCodeDeployの処理は自動で発火されるようになる。
ちなみにCodeCommitの部分はGithubに置き換えることもできる。

### CodeStar

1からCI/CD環境を作りたい！という場合に自分の環境に合わせたテンプレートからCodePipelineを含め各サービスを自動で構築してくれるサービス。

## ハンズオンの流れ

### CodeCommitとCodePipelineを使ってS3静的Webホスティングを起点にしたCI/CD環境を構築する。

CodeCommitでまずはリポジトリを作る。

![2021-08-12_00h51_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/11ee5e81-be7e-01a0-4128-0cc28905960e.png)

次に開発環境を用意する。
今回のハンズオンではAWSCloud9が利用されていた。
このように設定すると、指定したVPCやサブネットにEC2インスタンスが作成されそこを開発環境とするようになる。
![2021-08-12_01h03_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/b0eb5813-867b-b91b-35af-74eb0179f291.png)

作成するとこんな感じでブラウザ上でエディタが展開される。
![2021-08-12_01h05_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/d69d2c8c-ed97-3bdc-b019-4b1a5e62b5e3.png)

次に開発環境でGitが使えるようにヘルパーを導入する。
ドキュメントに以下のように解説されてるのでこの通りやればOK。
![2021-08-12_01h07_32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/2afbe503-3324-ba82-556c-5ee388c5dd2f.png)

リポジトリがこんな感じで出来上がっているのがわかる。
接続方法はSSHももちろん可能(ただし当然キーとかが必要になる)
![2021-08-12_01h10_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/f3a9c0b0-4e7c-d663-1195-629bd98d1342.png)
リポジトリの内容を変更すると
![2021-08-12_01h12_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/ecfba063-9cd4-4c21-92ff-3b8a0ea4c1d8.png)
ちゃんとコンソールでも反映されることが確認できる。
![2021-08-12_01h17_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/c1790ee2-f80e-7886-15e2-9a9a6508903c.png)
あとはS3にバケットを作ってソースにも配置したindex.htmlをこちらにもアップロードする。
バケットが用意できたらパイプラインを作る。
ArtifactっていうのはソースコードをZip化したもの。
パイプラインを作成すると専用のバケットが用意されて、そこに配置される。
今回は以下のように作成している。
ビルドの工程は今回ないので省略。
![2021-08-12_01h24_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/d44b5e1e-e02e-50de-d4a5-9818d88b5fad.png)
あとはS3の静的WebホスティングのURLから確認する。
![2021-08-12_01h27_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/28516ffa-ee93-a98b-50c2-31f4cccd2dc8.png)
開発環境側のindex.htmlを弄ると、S3の方でも反映される。
![2021-08-12_01h29_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/22b833b6-57bf-c547-3d31-9d1dde6d3104.png)

### CodeシリーズのみでPipelineを作る

今度は基本的な使い方であるEC2インスタンスを立ててそこにエージェントをインストールして……というやり方を試してみる。
事前に用意しておくものは

・S3バケット(CodeBuildで作成するArtifactの置き場所)
・EC2インスタンス(ArtifactへアクセスしないといけないのでS3へのアクセス権限を付与し、インターネットからアクセスできるようにHTTPポートを開く)
・エージェント(上記インスタンスに付与)

バケットとEC2インスタンスについては割愛。
EC2インスタンスを立ち上げて、`yum update`と`yum install httpd`(Apache)をやったらエージェントを入れていく。
EC2インスタンスへのアクセスはSSHポートが開いてあれば各種CLIからでいいけども今回はEC2 Interface Connectで簡易的にやっていく。

まずは[このドキュメント](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install.html)からインストール方法を選んでやっていく。
今回はSystem Managerを使ってないのでCLI経由からを選ぶ。
で、今回はLinuxのEC2インスタンスなので[このドキュメント](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/codedeploy-agent-operations-install-linux.html)の通り入れていく。
ちゃんとドキュメント通りにやりましょう、なんならコピペでいいです。
私はタイプミスで30分無駄にしました(1敗)
問題なくいくと下記の通りエージェントインストールできたよってなります。
![2021-08-12_02h06_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/739b172c-d76e-5506-5475-0547ec320126.png)

次はCodeBuildの準備をします。
サーバーを建てることになるので下記のように環境設定を行います。
ロールは自前のものを使う必要がなければ勝手にAWS側で作ってくれます。
![2021-08-12_02h11_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/6a27f25a-0bb6-28b7-d27a-32be13eb4a13.png)
ただし、CodeDeployへのアクセス権をCodeBuildに与えないといけないので作成されたロールに下記のようにCodeDeployへのアクセス権を付与しておきます。
![2021-08-12_02h17_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/bc4f7e48-37d5-8845-c540-f2f26aba829c.png)
次に`buildspec.yml`を用意します。
ソースのルートディレクトリに置いておきます。
詳しい仕様は随時[このドキュメント](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html)を見ながらやるとして

1番簡単な例は以下の通りになります。

```yml

version: 0.2
# phaseでビルドの各段階で実行するコマンドを記述する。
phases:
  build:
    commands:
      - rspec HelloWorld_spec.rb
# ビルドした結果何をアップロードするのか
artifacts:
# 全ファイルアップロードしてください
  files:
    - '**/*'

```

これを[このドキュメント](https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/application-revisions-push.html)に則って以下の通り改変します。

```yml

version: 0.2

phases:
  build:
# 今回はビルド後にCodeDeployでデプロイを行いたいので以下の通り記述する。
# application-nameでCodeDeployで作成したアプリケーション名を、s3-locationにはArtifactが保存されているS3バケットとそのパスを指定。
# sourceを記述することでビルド元のどの場所のコンテンツをビルドするか指定する。
    commands:
      - aws deploy push --application-name xxx --s3-location s3://xxx/artifact.zip --source src
artifacts:
  files:
    - '**/*'
# ビルド元の場所を基準とした、最上位ディレクトリを指定したい場合に記述。今回はsrcフォルダにソースは全部集めてるのでそちらを指定。
  base-directory: src

```

次にCodeDeployへ移って各種設定を行います。
まずはアプリケーションの作成を行います。
プラットフォームは今回はEC2使ってるのでそれを選択。
ここで作ったアプリケーションの名前を前述の`buildspec.yml`で指定する。
![2021-08-13_21h48_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/902a03ad-4247-f29a-71da-a0b261c62511.png)
次にデプロイグループを作成。
![2021-08-12_02h43_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/5cbcaee4-ab84-9c46-399c-048a7da5bbb0.png)
下記の通り、このグループに所属しているインスタンスにCodeDeployへのアクセス権限を付与しないといけないのでロールを事前に作っておきましょう。
![2021-08-12_02h46_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/ebc19c6b-4042-bedc-7c6f-44bcdbeedfc0.png)
今回はEC2インスタンスなのでチェックボックスはそちらに入れる。
先述の通りAuto Scalingグループにも対応していて、スケールアウトしてもデプロイ処理は一括で済むみたいなこともできる。
また、デプロイするしないはタグによって識別するのでちゃんとデプロイグループに任意のタグを設定しておく。
![2021-08-12_02h46_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/2476552f-402f-ebae-1b2a-7f41bf3385c1.png)
ちなみにSystem Managerにも対応しているのでSMエージェントを入れている場合はこちらの設定を行う。
![2021-08-12_02h47_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/059ed72f-cc8b-aed3-6892-8f95553d97dd.png)
ここまでやったら`appspec.yml`を用意する。
場所はsrcフォルダ内。

例としてはこんな感じで`buildspec.yml`と似たような感じで書いていく。
CodeDeployで作るアプリケーションの仕様書のようなものでデプロイのルールを決める。

```yml

version: 0.0
# 環境OS指定
os: linux
files:
    # ソースコードのどの部分やファイルを
  - source: /
    # インスタンスのどの位置に配置してほしいか指定
    destination: /var/www/html/WordPress
# スクリプトの実行とそのタイミングについて記述
hooks:
  BeforeInstall:
    - location: scripts/install_dependencies.sh
      timeout: 300
      runas: root
  AfterInstall:
    - location: scripts/change_permissions.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: scripts/start_server.sh
    - location: scripts/create_test_db.sh
      timeout: 300
      runas: root
  ApplicationStop:
    - location: scripts/stop_server.sh
      timeout: 300
      runas: root

```

今回は特にスクリプトは実行しないので

```yml

version: 0.0
os: linux
files:
  - source: index.html
    destination: /var/www/html/

```

これでOK。
ここまで終わったらリポジトリにpushしてとりあえずビルドを実行してみる。
無事完了するとこんなログになるはず。
![2021-08-12_03h00_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/668de051-457c-2209-8123-2b6f7345368f.png)
次にデプロイをやってみる。
先程作ったデプロイグループと事前に用意しておいたArtifactの保存先であるS3バケットを下記のように指定して実行すると、
![2021-08-12_03h01_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/5649aed7-063b-eee2-7210-4f18c6891022.png)
こうなる。
![2021-08-12_03h02_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/6f8effbb-9ea1-03e3-ed89-7cd95a8fe714.png)
EC2インスタンスのIPアドレスを確認するとちゃんと反映されている。
![2021-08-12_03h06_30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/4b8d9d61-714d-6f69-d757-a133e29d73b6.png)
最後にここまでの一連の流れをパイプラインにして自動化する。
最初と同じくパイプラインを作っていく。
変更点はBuildとDeployのStageを作ること。
![2021-08-12_03h08_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/e2e2555a-91ee-edea-1695-b053a0dd0261.png)
![2021-08-12_03h08_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/07c2de4b-722d-fa4a-0934-915341c50d20.png)
試しに開発環境でindex.htmlを弄って、pushすると自動でビルドとデプロイが始まる。
![2021-08-12_03h10_45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/4abdd74b-8481-0008-e4d3-f36519b93dd5.png)
確認するとindex.htmlもしっかり変更されているのがわかる。
![2021-08-12_03h14_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428779/35b60f48-a238-32e3-0622-9b925b1a8521.png)

## 最後に

次はプラスワンで紹介されてたEC2+ELBのハンズオンとS3の静的Webホスティングをやってみたら、昨年プロジェクトに参加させていただいたときに消化しきれなかった[Github Actionsを使ってGithubからEC2までのDjangoのCICD環境構築](https://qiita.com/fffukken/items/27b0bfa712940914d3f6)これに挑戦してみようと思う。
その後は基本情報にはちょっと時間が足りないので年内に他2つのアソシエイト資格取るための勉強をしながらTypeScriptとPython(基本情報対策と競プロCレベルくらいの力がほしいから)とDjango(ハイブリッドアーキテクチャへの理解)を進めていく感じになるのかなと思っています。
何よりLambdaとかやるにはあまりにもまだプログラミング言語への理解が足らない……

## 参考資料

[Codeシリーズで始めるはじめてのCI/CD (#higobashiawsで登壇しました)](https://dev.classmethod.jp/articles/cicd-with-code-series-at-higobashi-aws/)