EKSでRailsをCI/CDする(Kubernetes CI/CDまでの道②)
はじめに
前回はEKSへのデプロイに挑戦しました。そこで今回はEKSを利用したCI/CDを行っていきます。
今回のハンズオンはこちらのリポジトリを利用します。
また、ベース環境、RDS、EKSのクラスター作成までは各自行っている前提でハンズオンを進めていきます。(前回行いました)
開発環境
- WSL2 20.04 LTS
- Docker 20.10.12
- docker-compose v2.2.3
- Git 2.25.1
- AWS CLI 2.4.25
- eksctl 0.87.0
- kubectl v1.23.4
参考書籍
このハンズオンでは以下の書籍を参考にしています。
Kubernetes on AWS ~アプリケーションエンジニア 本番環境へ備える
また、基本的なインフラ環境(VPC, subnetなど)はAWSではじめるインフラ構築入門 安全で堅牢な本番環境のつくり方を参考にしています。
詳しく知りたい方は書籍を読んでいただければと思います。
Kubernetes CI/CDまでの道シリーズ
- kubernetesでRails+Nginxをデプロイする(kubernetes CI/CDまでの道①)
- EKSでRailsをCI/CDする(Kubernetes CI/CDまでの道②)
方針
EKSのCI/CDはCodeBuild
を利用することで実現が可能です。
Rails(Docker)をCodeBuildでCIする (CI/CDまでの道⑧)で利用したCodeBuildを行った後に、デプロイを行うビルドプロジェクトを実行してデプロイすることにします。
デプロイで行うことは
① CodeBuildにEKSを利用できる環境(AWS CLI、Kubectlのインストール)を用意
② kubectl applyでデプロイメントの更新
を行っています。ローカルで更新するのをCodeBuildの環境で行うので難しくはありません
前準備
/config
にcredentials.yml
とmaster.key
を追加します。
ECRにsample-rails
とsample-nginx
を作成してイメージのPushを行います。プッシュコマンド通りに実行をしますが、2つ目を変更します。
# Railsの2つ目のコマンド
$ docker build --target build --no-cache -t sample-rails .
# Nginxの2つ目のコマンド
$ docker build -f ./containers/nginx/Dockerfile -t sample-nginx .
デプロイの準備
デプロイを行う上でいくつかのファイルを用意していきます。
まずは/cicd/kustomization
のフォルダを作成します。
/cicd/kustomization
にprod
というフォルダを作成して以下のファイルを追加します。
kustomization.yml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../base # ベースとなるマニフェストの場所
images:
- name: rails-image # ここは変更しない
newTag: 0.1.1 # アプリケーションのバージョン番号(ここをgit pushする前に毎回変更することでECRのタグになり、applyでイメージ変更することができる)
newName: [ユーザーID].dkr.ecr.ap-northeast-1.amazonaws.com/sample-rails # ECRレジストリのURI
ユーザーIDを埋めてください。
newTag
はgit push
するたびに変更します。Railsイメージのタグに利用しています。
/cicd/kustomization
にbase
というフォルダを作り、以下の3つのファイルを作成します。
kustomization.yml
resources:
- deployment.yml
- service.yml
deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: rails
labels:
app: rails
spec:
replicas: 2
selector:
matchLabels:
app: rails
template:
metadata:
labels:
app: rails
spec:
volumes:
- name: public-data
emptyDir: {}
- name: tmp-data
emptyDir: {}
initContainers:
- name: pre-rails
# image: [ECRのRailsイメージURL]
image: rails-image
command: ['/bin/sh', '-c', 'cp -a /myapp/public/* /mnt/empty-dir-content/']
volumeMounts:
- name: public-data
mountPath: "/mnt/empty-dir-content/"
containers:
- name: rails
image: rails-image
# image: [ECRのRailsイメージURL]
ports:
- containerPort: 3000
command: ['/bin/sh', '-c', 'bundle exec puma -C config/puma.rb']
env:
- name: DB_USERNAME
# value: root
value: admin
- name: DB_PASSWORD
value: password
- name: DB_DATABASE
value: myapp
- name: DB_HOST
value: [RDSのエンドポイント]
# value: mysql-server
- name: SECRET_KEY_BASE
value: [master.keyの値]
volumeMounts:
- name: tmp-data
mountPath: /myapp/tmp/sockets
- name: nginx
# image: [ECRのNginxイメージURL]
image: [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/sample-nginx
ports:
- containerPort: 80
volumeMounts:
- name: public-data
mountPath: /myapp/public
- name: tmp-data
mountPath: /myapp/tmp/sockets
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 250m
memory: 769Mi
ユーザーIDとRDSのエンドポイント、master.keyの値を各自埋めてください。
service.yml
apiVersion: v1
kind: Service
metadata:
name: rails-server
spec:
type: LoadBalancer
selector:
app: rails
ports:
- protocol: TCP
port: 80
targetPort: 80
次にルートディレクトリにapplyspec.yml
を作成します。
このファイルをCodeBuildで利用することでデプロイを行います。
applyspec.yml
version: 0.2
phases:
pre_build:
commands:
# Install AWS CLI
- apt-get update
- apt install -y wget python
- wget https://bootstrap.pypa.io/get-pip.py
- python get-pip.py
- pip install awscli --upgrade
# Install kubectl
- export KUBECTL_VERSION=1.23.0
- wget https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VERSION}/bin/linux/amd64/kubectl
- chmod 755 kubectl
- mv kubectl /usr/local/bin/
# Set up kubectl
# - aws --region us-east-2 eks update-kubeconfig --name kaizawa-eks-test-20190222
- export EKS_CLUSTER_NAME=eks-work-cluster
- export REGION=ap-northeast-1
- aws --region ${REGION} eks update-kubeconfig --name ${EKS_CLUSTER_NAME}
- kubectl
build:
commands:
# Apply to EKS-Cluster.
- kubectl get all
- kubectl kustomize cicd/kustomization/prod
- kubectl apply -k cicd/kustomization/prod
- kubectl get all
また、カレントディレクトリにあるbuildspec.yml
も修正します。
version: 0.2
env:
variables:
DOCKER_BUILDKIT: "1"
phases:
pre_build:
commands:
- echo Logiging in to Amazon ECR...
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
- docker pull $AWS_ECR_BUILD_REPOSITORY:latest
- docker tag $AWS_ECR_BUILD_REPOSITORY:latest build:latest
- IMAGE_TAG=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | head -c 7)
- IMAGE_NAME=sample-railis
build:
commands:
- echo Build started on `date`
- echo Building the Docker image...
- docker build --target production --cache-from build:latest --build-arg BUILDKIT_INLINE_CACHE=1 -t $IMAGE_NAME:$IMAGE_TAG .
- docker build --target build --cache-from build:latest --build-arg BUILDKIT_INLINE_CACHE=1 -t build:latest .
# - docker tag $IMAGE_NAME:$IMAGE_TAG $AWS_ECR_BUILD_REPOSITORY:$IMAGE_TAG
# install yq
- wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
- chmod a+x /usr/local/bin/yq
- VERSION_NO=`cat cicd/kustomization/prod/kustomization.yml | yq .images[].newTag`
- docker tag $IMAGE_NAME:$IMAGE_TAG $AWS_ECR_BUILD_REPOSITORY:$VERSION_NO
- docker tag build:latest $AWS_ECR_BUILD_REPOSITORY:latest
post_build:
commands:
- echo Build completed on `data`
- echo Pushing the Docker image...
- docker images
- docker push $AWS_ECR_BUILD_REPOSITORY:latest
# - docker push $AWS_ECR_BUILD_REPOSITORY:$IMAGE_TAG
- docker push $AWS_ECR_BUILD_REPOSITORY:$VERSION_NO
- echo "[{\"name\":\"sample-rails\",\"imageUri\":\"${AWS_ECR_BUILD_REPOSITORY}:${IMAGE_TAG}\"}]" > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
- appspec.yml
- taskdef.json
実行した際にcicd/kustomization/kustomization.yml
で設定したタグのついたイメージをECRにPushするように変更しました。
リポジトリの作成
Gitのリポジトリを作成してPushしておきます。
ここでは「test2」というリポジトリを作成しました。
前準備
Rails(Docker)をCodeBuildでCIする (CI/CDまでの道⑧)の通りにCodeBuildでテストとビルドがCodePipelineで動くところまで準備を行います。
1度動かして0.1.0のイメージをPushします。
このあとデプロイメントの立ち上げに利用します。
アクセス権限の追加
そこからデプロイをパイプラインに追加します。
まずはクラスターに接続できるように以下のコマンドをWSL2で実行します。
$ eksctl create iamidentitymapping --region ap-northeast-1 --username codebuild --group system:masters --name eks-work-cluster --role arn:aws:iam::[ユーザーID]:role/sample-build-role
これでクラスター接続がCodeBuildからできるようになるのでkubectl
コマンドが実行できるようになります。
また。ロールの指定で/service-role
を省略して設定することもポイントです。
次にCodeBuildにポリシーを追加します。
以下のポリシーを「IAM」→「ポリシー」→「ポリシーの作成」で以下を入力します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "eks:DescribeCluster",
"Resource": "arn:aws:eks:*:*:cluster/*"
}
]
}
ポリシー名をここでは「eks-access-codebuild」として作成しました。
作成したらCodeBuildのロール(sample-build-role)にアタッチします。
この設定を行うことでCodeBuildからEKSへのアクセスを行うことができます。
(2022年3月にはIAMを開いたあとにバージニア北部にリージョンが変わるバグがあるので東京に戻してください)
CodeBuild作成
テストやビルドと同じ流れで作成していきます。
項目名 | 値 |
---|---|
プロジェクト名 | apply |
ソースプロバイダ | GitHub |
リポジトリ | GitHubアカウントのリポジトリ |
GitHubリポジトリ | test2 (作成したリポジトリ名) |
オペレーティングシステム | Ubuntu |
ランタイム | Standard |
イメージ | aws/codebuild/standard:5.0 |
イメージのバージョン | aws/codebuild/standard:5.0-21.10.15 |
特権付与 | ☑ |
サービスロール | 既存のロール |
ロールのARN | 作成したロール |
Buildspec 名 - オプショナル | applyspec.yml |
後は今まで通りで作成して大丈夫です
CodePipelineに追加
最後にいま作成したapply
を追加します。
追加の設定などは不要です。
項目名 | 値 |
---|---|
アクション名 | apply |
アクションプロバイダー | AWS CodeBuild |
入力アーティファクト | SourceArtifact |
プロジェクト名 | apply |
一番下に追加します。
「保存」をクリックしてパイプラインの設定は終わりです。
Railsのデプロイ
./kubernetes/deployment_rails.yml
の以下の箇所を修正します。
- RailsとNginxのイメージをECRのURLにする
- Railsのイメージの末尾に
:0.1.0
を追加 - 環境変数DB_USERNAMEを
admin
に変更 - 環境変数DB_HOSTをRDSのエンドポイントに変更
- 環境変数SECRET_KEY_BASEを追加してmaster.keyの値をいれる
apiVersion: apps/v1
kind: Deployment
metadata:
name: rails
labels:
app: rails
spec:
replicas: 2
selector:
matchLabels:
app: rails
template:
metadata:
labels:
app: rails
spec:
volumes:
- name: public-data
emptyDir: {}
- name: tmp-data
emptyDir: {}
initContainers:
- name: pre-rails
# image: [ECRのRailsイメージURL]
image: [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/sample-rails:0.1.0
command: ['/bin/sh', '-c', 'cp -a /myapp/public/* /mnt/empty-dir-content/']
volumeMounts:
- name: public-data
mountPath: "/mnt/empty-dir-content/"
containers:
- name: rails
# image: [ECRのRailsイメージURL]
image: [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/sample-rails:0.1.0
ports:
- containerPort: 3000
command: ['/bin/sh', '-c', 'bundle exec puma -C config/puma.rb']
env:
- name: DB_USERNAME
# value: root
value: admin
- name: DB_PASSWORD
value: password
- name: DB_DATABASE
value: myapp
- name: DB_HOST
value: [RDSのエンドポイント]
# value: mysql-server
- name: SECRET_KEY_BASE
value: [master.keyの値]
volumeMounts:
- name: tmp-data
mountPath: /myapp/tmp/sockets
- name: nginx
# image: [ECRのNginxイメージURL]
image: [アカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/sample-nginx
ports:
- containerPort: 80
volumeMounts:
- name: public-data
mountPath: /myapp/public
- name: tmp-data
mountPath: /myapp/tmp/sockets
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 250m
memory: 769Mi
master.keyはGitには上がらないため環境変数として設定する必要があります。
以下のコマンドでデプロイを行います。
$ kubectl apply -f ./kubernetes/deployment_rails.yml
$ kubectl get all
# 別のターミナルを開く
$ kubectl exec -it [ポッドの名前] sh
$ rails db:create
$ kubectl port-forward [ポッドの名前] 8080:80
localhost:8080/test
にアクセスできることを確認します。
確認ができたらCtrl+Cできります。
CI/CDの確認
/app/views/test/index.html.haml
を以下に変更します。
%h1 デプロイtest
%h2 CSSが適応されると色が変わる
%button.btn.btn-primary{:type => "button"} Bootstrap適応
= I18n.t("word.greeting.hello")
変更ができたらGitにPushします。
$ git add .
$ git commit -m "view変更"
$ git push origin main
パイプラインが実行されるのですべてが成功しているか確認します。
更新を確認するため再度アクセスしてみます。
$ kubectl port-forward [ポッドの名前] 8080:80
localhost:8080/test
にアクセスして変更されていれば成功です。
注意としてはcicd/kustomiation/prod/kustomization.yml
のタグを毎回変えてPushしないとイメージの更新がされないのです。
お片付け
以下のコマンドでEKS関連を削除します。
$ kubectl delete deployment rails
$ kubectl delete service rails-server
$ kubectl delete deployment mysql-server
$ kubectl delete service mysql-server
$ kubectl delete service kubernetes
$ eksctl delete cluster --name eks-work-cluster
CloudFormationで立ち上げたものをすべて削除
ECRで作成した2つのリポジトリの削除
CodeBuildのプロジェクト3つ削除
CodePipeline削除
CodePipelineで作成されたS3削除
IAMのCodeBuildで作成したポリシーを削除
CloudWatch LogsのCodebuildのログを削除
おわりに
今回はEKSを利用したCI/CDパイプラインを構築しました。
EKSはマネージメントなのでかなり簡単に実現することができました。
タイトルにあるCI/CDはできましたが、引き続きKubernetesの記事をこのタイトルで出していきます。
今回作成したものは以下のリポジトリにあります。
Discussion