弊社ECサイト用APIサーバのCI/CD環境今昔

[ハンズラボ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handslab) 16日目の記事です。

ECサイト担当になって早3年を過ぎ、その間にCI/CD環境を継続して改善し続けてきました。
この度ようやくParavirtualなインスタンスで動作していたJenkinsサーバを停止し、一段落したこともあり、来し方を振り返ってみます。

# APIサーバのCI/CD環境今昔
## チーム参加時点(2015/09)
```diff
  Gitリポジトリ: Atlassian Stash
    - 今はBitbucketに吸収されてしまいました
  CI: Jenkins
    - Antから以下を実行
        - PHP Lint(php -l)
        - PHP Mess Detector
        - PHP Code Sniffer
        - PHPUnit
  CD: Jenkins
    - eb deployコマンドをbashスクリプトで実行
```

### 当時を振り返って
- Atlassian Stashにログインできるメンバーが社内に数名しかいなかった・・・
- AntジョブがXMLで書いてありメンテナンスできない

## Gitリポジトリ移行(2015/12)
```diff
+ Gitリポジトリ: Backlog
```

### 当時を振り返って
- BacklogのGitリポジトリでプルリクエスト機能が追加されたこともありBacklogへ移行
    - Githubも社内で利用開始していたが、Backlogは当時からエンタープライズプランで追加費用がなかった

## 決済APIサーバ分離(2016/02)
```diff
+ CI: Jenkins
+    - Antをやめて、composerスクリプトでの実行に修正
+ CD: Jenkins
+    - bashスクリプトで待機系を判定し、待機系へ`eb deploy`
```

### 当時を振り返って
- ステージング環境と本番環境のAWSアカウントの分離にチャレンジした
    - ステージング環境にdeployしたCommitと本番環境に差異を発生させたくなかったため、ステージング環境deploy時に本番環境の待機系へもdeploy
    - 本番環境のCNAMEから待機系を判断
- Jenkinsユーザが実行するジョブをvisudoで追加するのを忘れていて動かない

## 決済APIサーバのCI/CD環境移行(2018/06)
```diff
+ Gitリポジトリ: CodeCommit
+ CI: CodePipeline + CodeBuild
+   - PHP Parallel Lint に変更
+   - CodeBuildのDockerイメージでhttpdを起動してAPIのテストを実行
+ CD: CodeBuild
+   - CodeBuildからeb deployコマンドをbashスクリプトで実行
```

### 当時を振り返って
- Meltdown/Spectre以降、Paravirtualインスタンスへのメンテナンス頻度が上がり、Jenkins廃止の機運が高まった
- CodePipelineはBacklogのGitリポジトリからhookできない・・・
    - CodeCommitの使い心地を試してみる
- CodeBuildでのCIでハマる
    - 今はローカル実行できていいですよね・・・
- CodePipelineからElastic BeanstalkへdeployしたいがAWSアカウントをまたいでのdeployは素直には無理
    - 仕方なくCodeBuildから`eb deploy`する

## APIサーバのCI/CD環境移行(2018/11)
```diff
+ Gitリポジトリ: Github
+ CD: CodePipeline + Lambda
+   - CodePipelineからElastic Beanstalkの待機系へdeploy
+   - 手動承認したらLambdaでswap
+   - swap完了後に未deploy側へdeploy
+   - deploy完了したら再swap
```

### 当時を振り返って
- CodeCommit辛い
    - プルリクエストしたらAWSのマネジメントコンソール開くの？？？
    - httpsにしてもsshにしても辛い
    - 諦めてGithubに移行
- CodeBuildでのPHPMD/PHPCSで落ちる
    - Jenkinsサーバでは通るが、新環境では落ちる・・・
    - 仕方なく、通るまでチェックルールからルールを一つずつ削除する。辛い。
- ステージング環境と本番環境のAWSアカウントを分離
    - featureブランチからdevelopブランチにマージ→ステージング環境にdeploy
    - developブランチからmasterブランチにマージ→本番環境にdeploy
- CodePipelineからのLambda呼び出しでハマる
    - LambdaからCodePipelineへ「ジョブが完了した」と通知する必要がありました

```python
import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

SOURCE_EB = os.environ['source_eb']
DEST_EB = os.environ['dist_eb']

def lambda_handler(event, context):
    logger.info(event)
    
    try:
        beanstalk = boto3.client('elasticbeanstalk')
        response = beanstalk.swap_environment_cnames(
            SourceEnvironmentName = SOURCE_EB,
            DestinationEnvironmentName = DEST_EB,
        )
    
        logger.info(response)
    
    except Exception as e:
        logger.error(e)
        raise e
        
    try:
        codepipeline = boto3.client('codepipeline')
        jobid = event['CodePipeline.job']['id']
        response = codepipeline.put_job_success_result(
            jobId=jobid,
        )
        
        logger.info(response)
    
    except Exception as e:
        logger.error(e)
        raise e
```

## 今後
Elastic BeanstalkへのAmazon Linux2の公式対応待ちです。
その際には決済APIサーバのCI/CD方式をAPIサーバに合わせる予定です。
ECS + Fargate環境にも挑戦したいです。
今後も改善は続きます。

## 参考資料
- [Blue-Green Deployments to AWS Elastic Beanstalk on the AWS Cloud](https://aws-quickstart.s3.amazonaws.com/quickstart-codepipeline-bluegreen-deployment/doc/blue-green-deployments-to-aws-elastic-beanstalk-on-the-aws-cloud.pdf)

[ハンズラボ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/handslab) 明日17日目は@sr-mtmtです！