概要
ASP.NET CoreアプリをAWS LightsailでホスティングされているCentOS 7のサーバで公開するにあたり自動ビルド・自動デプロイ環境を構築しました。
所謂、CI環境と言うものですが自動テストは組み込んでいないためタイトルを「自動ビルド・デプロイ環境の構築」としています。
今回のインフラ基盤として使用したAWS LightsailはCodePipelineと統合されていないため、自動ビルド・デプロイ環境にAWS Codeシリーズを使用する旨味はあまりありません。
しかし、過去にAWS CodeBuildを使用してVue.jsの自動ビルド・デプロイ環境を整えた自身の経験があったためAWSを選択しました。
また、今回の構成では下記のような特徴があります。
- AWS EC2などではなくオンプレミスなサーバ
- AWS CodeDeployと統合されていない環境
- CodeDeployのAgentが必要な環境
- OSがCentOS
- UbuntuやRHELなどではない
1つ目のオンプレミス環境についてはLightsailもAWSの1つのサービスではありますが、CodeDeployとは統合されていません。
そのため、Agentのインストールが必要になると言う点。
2つ目のOSについて、CodeDeployを使用する上でUbuntuやRHELについては公式ドキュメント含め多少ナレッジが公開されています。
しかし、CentOSについてはAgentのインストール等々ナレッジが公開されていなかったので手探り状態での構築となった点です。
目次
- 環境構成(自動ビルド・デプロイの流れ)
- 自動ビルド・デプロイ環境の構築
- 雑感
環境構成(自動ビルド・デプロイの流れ)
構築する環境構成を整理します。自動ビルド・デプロイ環境の雰囲気を掴むことが目的です。
構成の雰囲気を掴んでしまえば構築はAWSを触っているだけでできると思うので。
下記の構成図は実際に構築した構成とは異なりますが説明しやすいように必要な部分を抜粋した図です。
実際の構成図についてはGitHubのリポジトリ 🔗をご参照ください。
アプリ基盤
- AWS Lightsail(アプリのデプロイ先)
- アプリ:ASP.NET Core MVC + MySQL + nginx(リバースプロキシ)
$ cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
処理の流れ
構成図を元に自動ビルド・デプロイの流れをまとめると下記の3つにまとめることができます。
①②③ローカルからPUSH〜ソースDL
ローカルからGitHubへPushするとCodePipelineに通知されパイプラインが起動する。
そして、GitHubから所定のS3バケットへソースをダウンロードする。
④⑤⑥⑦ビルド実行からアップロード
ソースのDLが完了するとCodePipelineからビルド実行が命令され、CodeBuildが起動する。
その後、S3バケットからビルド実行用コンテナにソースをダウンロード・ビルド実行・ビルド済み資産のアップロードが実行される。
CodeBuildでは内部的にLambdaが使用されています。
CodeBuildで使用可能なコンテナイメージは標準のモノからDockerHubに存在するモノまで各種選択可能なので、今回はMS公式のdotnet dockerイメージを使用します。
⑧⑨⑩⑪⑫デプロイの実行
ビルドの実行・アップロードの完了後、CodePipelineからCodeDeploy(親)へデプロイ実行命令が通知される。
その後、CodeDeployAgent(子)へデプロイ命令が通知されビルド済み資産をS3バケットからLightsail(アプリ稼働環境)へダウンロードされデプロイが実行される。
図の中で⑨と⑩のデプロイ実行命令と検知を分けたのはCodeDeployAgent(子)が5秒間隔でCodeDeploy(親)へ命令が発行されていないかの問い合わせをしているためです。
自動ビルド・デプロイ環境の構築
自動ビルド・デプロイ環境を構築します。
本来であればCodeBuildで使用するビルド指示ファイルのbuildspec.yml
とCodeDeployで使用するデプロイ指示ファイルのappspec.yml
を作成してからパイプライン等々を作成すればよいのですが、全体の流れが分かりづらくなるので下記の順番で構築します。
- 事前準備
- サーバにCodeDeploy Agentをインストール
- CodeDeployアプリケーションとデプロイグループの作成
- CodePipelineとCodeBuildプロジェクトの作成
- CodeBuildのbuildspec.ymlを作成
- CodeDeployの
appspec.yml
とデプロイスクリプトを作成 - 動作確認
事前準備
以下の準備を事前に行っておきます。
通信用ポートに穴をあける(事前準備)
CodeDeploy AgentはSSL通信でやり取りするので443
ポートの通信を許可します。
Lightsailの場合はサーバ側でファイアウォールの設定ができないためブラウザから設定します。
GitHubリポジトリの作成
CodePipelineプロジェクト作成時にソースとしてリポジトリを指定する必要があるため予めGitHub上にリポジトリを作成(アップ)しておきます。
S3バケットの作成
GitHubからDLしたソースやCodeBuildでビルドされた成果物の置き場所となるS3バケットを予め作成しておきます。
CodePipelineが自動で作成する方法も選択できますがバケットを固定したかったのでバケットを予め作成しておきパイプラインを作成する際にバケットを指定します。
サーバにCodeDeploy Agentをインストール
まず、最初にデプロイ先のサーバへCodeDeploy Agentをインストールします。
デプロイ先サーバの環境構築は完了しているものとします。
AWS CLIのインストール
CodeDeployでインスタンス(サーバ)を登録するためにAWS CLIが必要なのでインストールします。
インストール操作については下記の公式ドキュメント通りで問題なく行えました。
# pipインストール
$ curl -O https://bootstrap.pypa.io/get-pip.py
$ python get-pip.py --user
# インストール実行
$ pip install awscli --upgrade --user
# バージョン確認
$ aws --version
aws-cli/1.16.121 Python/2.7.5 Linux/3.10.0-957.5.1.el7.x86_64 botocore/1.12.111
# パス確認
$ which aws
~/.local/bin/aws
ユーザのアクセスキー設定
CodeDeployへインスタンスを登録するにあたり初回登録のためにIAMユーザのアクセスキーを設定する必要があります。
事前にインスタンス用のIAMユーザを作成する方法が正攻法なのでしょうが、インスタンス登録時に自動で作成される方法を今回は実施するため権限を持った別ユーザを登録に使用します。
ここで設定するIAMユーザのアクセス権限にはIAMユーザの作成・削除などの権限が必要になります。
詳細はこちらの公式ドキュメント下部に記載があります。
この部分はあとからいくらでも変更できるので問題ないかと思います。
$ aws configure
AWS Access Key ID :XXXXXXXXXXXXXXXXXXXX
AWS Secret Access Key :XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Default region name :ap-northeast-1
Default output format :json
CodeDeployのインスタンス登録
現在、作業しているサーバをオンプレミスのインスタンスとしてCodeDeployに登録します。
オンプレミスインスタンスの登録方法はAWS公式ドキュメントによると下記の3つがあります。
aws deploy register
コマンドを使う方法 🔗aws deploy register-on-premises-instance
コマンドでiam-user-arn
を使う方法 🔗aws deploy register-on-premises-instance
コマンドでiam-session-arn
を使う方法 🔗
今回は1つ目のregister
コマンドを使用します。オンプレミスインスタンスを登録する際に自動でIAMユーザを作成してくれるからです。
AWS公式ドキュメントのコマンド例ではオプション(引数)で--iam-user-arn
(IAMユーザ)を指定していますが
下記コマンドの様に--iam-user-arn
を指定せずにコマンドを発行することで適切なポリシーがアタッチされたIAMユーザが作成されます。
IAMユーザ名はインスタンス名(--instance-name
)が適用されます。
また、--tags
オプションで指定しているkey
とvalue
についてはインスタンスを特定するためのタグになります。
複数インスタンスを運用する際にインスタンスを区別するために使用されます。
これはあとからCodeDeployのダッシュボードから変更可能です。
$ aws deploy register --instance-name hogehoge-CentOS-1GB-Tokyo-1 --tags Key=Name,Value=CodeDeployhogehoge --region ap-northeast-1
Creating the IAM user... DONE
IamUserArn: arn:aws:iam::123456789123:user/AWS/CodeDeploy/hoge-CentOS-1GB-Tokyo-1
Creating the IAM user access key... DONE
AccessKeyId: XXXXXXXXXXXXXXXXXXXX
SecretAccessKey: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Creating the IAM user policy... DONE
PolicyName: codedeploy-agent
PolicyDocument: {
"Version": "2012-10-17",
"Statement": [ {
"Action": [ "s3:Get*", "s3:List*" ],
"Effect": "Allow",
"Resource": "*"
} ]
}
Creating the on-premises instance configuration file named codedeploy.onpremises.yml...DONE
Registering the on-premises instance... DONE
Adding tags to the on-premises instance... DONE
Copy the on-premises configuration file named codedeploy.onpremises.yml to the on-premises instance, and run the following command on the on-premises instance to install and configure the AWS CodeDeploy Agent:
aws deploy install --config-file codedeploy.onpremises.yml
これでオンプレミスインスタンス(CodeDeployAgent)用のユーザが作成されました。
IAMユーザのポリシーについては最初からS3の読み取り権限が付与されています。
これはCodeDeployでデプロイを実行する際にアプリなどの配置する資産をS3から取得するためです。
公式ドキュメントを見る限りではインスタンスごとにユーザが必要とのことですが本当なんですかね?
CodeDeploy Agentのインストール
上記のregister
コマンドを発行するとIAMユーザの作成に成功しましたと言った風なログが出力されます。
最下行に次は「aws deploy install --config-file codedeploy.onpremises.yml
を実行してください」とログが出ています。
これでAgentのインストールをするわけですがコマンドを発行すると下記の様にエラーになります。
$ aws deploy install --config-file codedeploy.onpremises.yml
Only Ubuntu Server, Red Hat Enterprise Linux Server and Windows Server operating systems are supported.
Oh… CentOSだとダメっぽいですね…
調べて見ると下記の方法でインストールが可能でした。
# インストールファイルの取得に必要なwgetとAgentの実行に必要なrubyのインストール
$ sudo yum install -y ruby wget
# インストールスクリプトのダウンロード
$ wget https://aws-codedeploy-ap-northeast-1.s3.amazonaws.com/latest/install
# 実行権限付与
$ chmod +x ./install
# インストールスクリプトの実行
$ sudo ./install auto
インストール:
codedeploy-agent.noarch 0:1.0-1.1597
完了しました!
下記コマンドでAgentの実行状態を確認可能です。
# 実行状態確認
$ sudo service codedeploy-agent status
The AWS CodeDeploy agent is running as PID 9149
# 使用可能コマンドの確認
$ sudo service codedeploy-agent
Usage: /etc/init.d/codedeploy-agent {start|stop|status|restart}
ただ、これだけではAgentがうまくCodeDeployの親と通信してくれないのでAgentの設定ファイルを編集します。
codedeployagent.yml
の編集
CodeDeploy Agentの設定ファイルであるcodedeployagent.yml
を編集します。
下記のパスに設定ファイルが存在するので編集します。
$ sudo vi /etc/codedeploy-agent/conf/codedeployagent.yml
/etc/codedeploy-agent/conf/codedeployagent.yml
# 省略
# 最下行に下記を追加
:on_premises_config_file: "/home/centos/codedeploy.onpremises.yml"
ここで指定しているcodedeploy.onpremises.yml
はAgentインストールの実行ディレクトリに作成されているのでそれを指定します。
ファイル自体の配置場所は任意の場所に移動させても構わないと思います。
codedeploy.onpremises.yml
にはオンレミスインスタンスを登録した際に作成されたIAMユーザのアクセスキーなどの情報が記載されています。
これをAgentで読み込むように編集しました。
$ cat codedeploy.onpremises.yml
---
region: ap-northeast-1
iam_user_arn: arn:aws:iam::123456789123:user/AWS/CodeDeploy/hogehoge-CentOS-512MB-Tokyo-1
aws_access_key_id: XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
以上でインスタンスの登録・エージェントにインストール作業は完了です。
ブラウザからCodeDeployのオンプレミスインスタンスの画面で登録したインスタンスが確認可能です。
CodeDeployアプリケーションとデプロイグループの作成
CodeBuildプロジェクトはCodePipelineのパイプライン作成過程で作成可能ですが、CodeDeployのアプリケーションとデプロイグループのみ先に作成が必要なので作成します。
※事前に作成しておくとCodePipelineのプロジェクト作成時にデプロイプロバイダとして選択可能となるため設定が容易となります。
- CodeDeploy画面よりアプリケーションを作成
- 作成完了後、作成したアプリケーション画面より「デプロイグループ」を作成
環境
設定:「オンプレミスインスタンス」を選択- タググループ:インスタンス登録時に登録したタグを指定します。
タグのkey
とvalue
はインスタンス一覧の画面から確認可能です。
また、あとから編集可能です。
上記以外の設定は適宜埋めて頂いてあとはデフォルトで構いません。
デプロイタイプやデプロイ設定の詳細は下記の公式ドキュメントをご参照ください。
下記のスクリーンショットは設定時の参考画像です。
Application作成 | DeployGroup作成 | 作成後 |
---|---|---|
![]() | ![]() | ![]() |
![]() | ![]() |
CodePipelineとCodeBuildプロジェクトの作成
CodePipelineでパイプラインを作成します。
パイプラインを作成する過程で一緒にCodeBuildのビルドプロジェクトを作成します。
※CodeBuildプロジェクトを先に作るとBuild SourceにCodePiplineを指定できなかったため。
CodePipelineのダッシュボードから「パイプラインの作成」を押下して作成を開始します。
ほぼデフォルト通りで、入力しなければいけないところを適宜変更して対応します。
Step1 パイプラインの設定の選択
CodePipelineに任せると自動的にバケットが作成されてしまうので今回は指定します。
- アーティファクトストア:「カスタムの場所」を選択
- 事前準備で作成したS3バケットを指定
Step2 ソースステージの追加
今回はGitHubのリポジトリをソースとしてmasterブランチにPUSHされたタイミングで自動ビルド・デプロイを走らせたいのでWebHookを使います。
- GitHubを選択し任意のリポジトリを指定
- 変更検出オプション:「GitHubのウェブフック(推奨)」を選択
GitHubに接続するとリポジトリが選択可能になります。
Step3 ビルドステージの追加(CodeBuildプロジェクトの作成)
ビルドプロバイダには任意のものを選択します。
今回はCodeBuildを使用するので「CodeBuild」を選択し、「Create Project」よりCodeBuildプロジェクトの作成を開始します。
CodePipeline作成途中に作成するのでビルドソースや出力アーティファクトの設定が自動でされます。
入力ソースを増やしたり編集したりも作成完了後にCodePipelineのブラウザ画面から操作可能です。
- ビルドプロバイダ:「AWS CodeBuild」を選択
- 「Create project」からCodeBuildプロジェクトを作成する。
- Environment image:「Custom image」を選択
- Environment type:「Linux」を選択
- Image registry:「Other registry」を選択
- External registry URL:MS公式のDockerイメージを使用 →「mcr.microsoft.com/dotnet/core/sdk」
ビルドプロジェクト作成完了後、下記画像の様に「Successful」と表示されてCodeBuildプロジェクトが選択可能となっています。
コンテナに使用するイメージを今回はMS公式のDockerイメージを指定しました。
Dockerイメージを指定した理由としてはAWS側に.NET Coreの実行環境が2.1までしか用意されていないので2.2のプロジェクトをビルドしたら普通にコケたためです(そりゃそうだ)
Step4 デプロイステージの追加
先程作成したデプロイアプリケーションとデプロイグループを選択します。
Step5 確認
入力値に間違いが無いかを確認し、作成を完了する。
作成完了するとPipelineが自動で走りますがbuildspec.yml
を置いてないので普通にコケます。
CodeBuildのbuildspec.ymlを作成
CodeBuildでビルドする際の指示ファイルを作成します。
リポジトリのrootに追加する形でbuildspec.yml
を作成し追加します。
参考: twe-todo-app/buildspec.yml at master · Lycheejam/twe-todo-app 🔗
各フェーズやartifactsセクションについてはAWSの公式ドキュメント 🔗が整備されているので参考にしてください。
※Artifactsとは聞き慣れないワードで最初戸惑いましたがビルドされた成果物(アウトプット)と言う考え方で問題ないと思います。
buildspec.yml
version: 0.2 # AWS指定のバージョン
phases:
install:
commands:
- dotnet --version
pre_build:
commands:
- dotnet clean -c Release # プロジェクトのクリーンアップ
build:
commands:
- dotnet build -c Release # プロジェクトのビルド
post_build:
commands:
- dotnet publish -c Release # プロジェクトの発行
# CodeDeployで使用するスクリプトをコピー
- cp -r deployscripts <hoge>/bin/Release/netcoreapp2.2/publish/
# CodeDeployで使用するappspec.ymlをコピー
- cp appspec.yml <hoge>/bin/Release/netcoreapp2.2/publish/
artifacts:
files:
- "**/*" # ベースディレクトリで指定されたディレクトリ配下を全て出力する
base-directory: "<hoge>/bin/Release/netcoreapp2.2/publish"
内容は単純でdotnet cliのコマンドでビルドしてアプリケーションを発行しています。
npm
などではdotnet
関連のコマンドがnpm install
やnpm build
に変わるだけです。
今回は起動したDockerコンテナにPrivileged
権限(特権)を与えていないのでコマンドに制約がありますが、普通にビルドするだけだったら不要じゃないかなと思ってます。
また、cp
コマンドでコピーしているファイル/ディレクトリはCodeDeployで使用するファイルになります。
環境によっては不要になるコマンドです。
ビルド実行時のカレントディレクトリと環境変数について
ビルド実行時のカレンドディレクトリはリポジトリのrootです(リポジトリ直下)
今回は単純な操作であるため相対パスで指定していますが、CodeBuild標準の環境変数を使用することで絶対パスとして指定できます。
使用可能な環境変数については下記の公式ドキュメントをご参照ください。
appspec.yml
とdeployscripts
について
appspec.yml
とdeployscripts
をリポジトリのrootに配置しています。
上記のファイルとディレクトリについては後述するCodeDeployでデプロイする際に使用するため、ビルド成果物の中に含める必要があります。
そのため、dotnet publish -c Release
で発行されたアプリケーションディレクトリ(<hoge>/bin/Release/netcoreapp2.2/publish
)にファイルをコピーします。
appspec.yml
とdeployscripts
の内容や役割については後述します。
CodeDeployのappspec.yml
とデプロイスクリプトを作成
リポジトリのroot(リポジトリ直下)に追加する形でappspec.yml
とdeployscripts
ディレクトリ(とスクリプト)を作成し追加します。
appspec.yml
にはデプロイする際にデプロイ先のサーバで実行したいシェルスクリプトを記述するためデプロイ時に必要なシェルも作成します。
前述していたdeployscripts
はこのシェルスクリプトを格納するディレクトリ名です。
今回はなんとなく長い名前を付けましたが任意の名前で問題ありません。
シェルの作成
今回はサービスとして登録しているものを起動・停止するシェルの2種類を用意します。
シェル用ディレクトリの作成
シェルスクリプト格納用にdeployscripts
と言うディレクトリをリポジトリのrootに追加します。
ディレクトリ名はappspec.yml
で指定するので任意のディレクトリ名で構いません。
サービス起動シェル
先程、作成したdeployscripts
ディレクトリ内にシェルを作成します。
内容はサービスを起動するだけの簡単なシェルです。
dotnetstart.sh
#!/bin/sh
service="hoge"
echo "systemctl start $service"
sudo systemctl start $service
サービス停止シェル
起動シェルと同様にdeployscripts
内にシェルを作成します。
dotnetstop.sh
#!/bin/sh
service="hoge"
echo "systemctl stop $service"
sudo systemctl stop $service
appspec.yml
の作成
デプロイ時にデプロイ先のサーバで実行させたいことを記述します。
各セクションの詳細については公式ドキュメントで確認してください。
version: 0.0
os: linux
files:
- source: / # ビルド済み資産のどの階層からを配置対象とするかを指定
destination: /var/mvcapp # デプロイ先のアプリ資産配置パス
hooks: # hookごとに実行したいシェルを指定
ApplicationStop:
- location: deployscripts/dotnetstop.sh
runas: centos # 実行ユーザの指定
ApplicationStart:
- location: deployscripts/dotnetstart.sh
runas: centos
filesセクションについて
files:
- source: /
destination: /var/mvcapp
filesセクションではsource
でリビジョンから取得するファイルを指定しています。
/
でリビジョンに配置されているファイル全てと言うことになります。
複数指定などの方法については公式ドキュメントで詳しく解説されています。下記をご参照ください。
※リビジョンって言葉は聞き慣れない言葉ですがCodeDeploy的に言うとappspec.yml
を含んだデプロイ対象のファイル群を指しているっぽいです。
今回はCodeBuildでビルドされた成果物と考えればいいです。
更に、destination
でリビジョンの展開先を指定します。
今回は/var/mvcapp
をアプリケーションのワーキングディレクトリとしているため指定しています。
hooksセクションについて
デプロイする一連の流れの中でイベントフックが設定されており、イベントフックのどの時点でスクリプトを実行するか選択可能です。
フックは13種類あり、内4種類はCodeDeploy側で使用するため実質9種類のタイミングがあります。
下記の公式ドキュメントに画像付きで細かく解説されています。
今回はアプリ資産の配置前にアプリを停止し、アプリ資産の配置後にアプリを起動したいので下記のフックを選択しました。
hooks:
ApplicationStop:
- location: deployscripts/dotnetstop.sh
runas: centos
ApplicationStart:
- location: deployscripts/dotnetstart.sh
runas: centos
シェルについては相対パスで指定しています。カレントディレクトリがビルドで出力されたファイル群のrootになります。 また、実行ユーザについても指定可能です。
動作確認
上記で全ての設定は完了です。
CodePipelineから手動で「変更のリリース」を実行してパイプラインを起動させるか指定したGitHubのリポジトリにPUSHして自動ビルド・デプロイが完了することを確認してください。
構築手順の中で公式ドキュメントばかりに頼っていますが、構築の大体の雰囲気を掴んで公式ドキュメントを参照していくのが良いと思います。
雑感
この記事長すぎね。書くの時間かかりすぎね。
書き始めてから2週間経過してやっと投稿に至ったんですが…
正直書いてる途中で力尽きてるよね。
appspec.yml
とbuildspec.yml
は別記事にもっとわかりやすくまとめたほうがいいなって書きながら思ってました。
正味書きたかったことはCodeDeployAgentの設定編集だけなんだけどね。