Kubernetes は、コンテナ化されたアプリケーションのデプロイと管理を自動化するコンテナオーケストレーションシステムです。Helm は、Kubernetes アプリケーションの定義、インストール、アップグレードに役立つ Kubernetes 用パッケージマネージャーです。Helm を使うと、Kubernetes コンポーネント (デプロイ、サービス、HPA、サービスアカウントなど) について、繰り返し使えるテンプレートを定義し、公開し、複数のアプリケーションで共有できます。
このチュートリアルでは、アプリケーション用の Helm チャートを作成し、AWS EKS クラスタにインストールする方法について説明します。具体的には、Python Flask アプリを作成し、Docker 化して、このアプリ用の Helm チャートを作成します。最後に、CircleCI を使用して、既存の AWS EKS クラスタへのデプロイを自動化します。
前提条件
このチュートリアルでは、Flask アプリを作成するために、Python をローカルシステムにセットアップする必要があります。また、アプリケーションをデプロイするための AWS アカウントと、デプロイを自動化するための CircleCI アカウントを作成する必要もあります。
以下のリストを参照して、このチュートリアルに必要な準備を整えてください。
- Python 3 のインストール
- Helm CLI のインストール (Helm チャートの作成とインストールに必要)
- AWS アカウントと CircleCI アカウントの作成
開発を進めながらローカルでテストしたい方は、以下の準備も必要です。
- Docker のインストール (Docker イメージをローカルでビルドするために必要)
- kubectl のインストール (クラスタをローカルでテストするために必要)
- AWS CLI のインストールと AWS 認証情報の設定
- eksctl のインストール (Amazon EKS で Kubernetes クラスタを管理するために必要)
*CircleCI アカウントをお持ちでない場合は、こちらから無料アカウントを作成してください。
Flask アプリを用意する
このセクションでは、Python 3 でサンプル Flask アプリを作成し、それを Docker でコンテナ化します。Flask は、Web アプリケーション開発用のオープンソース Python フレームワークであり、軽量アプリケーションの API サービスレイヤーの開発によく使われています。
アプリを作成する
まず、Flask プロジェクト用の新しいディレクトリを作成し、そのディレクトリに移動します。以下を実行します。
mkdir flask-helm-charts-circleci
cd flask-helm-charts-circleci
次は、アプリのライブラリ依存関係を定義するための requirements.txt
ファイルを、flask-helm-charts-circleci
ディレクトリ内に作成します。今回の Python アプリでは flask
パッケージを使用するので、この依存関係を requirements.txt
ファイルに追加します。
Flask==2.2.2
app.py
ファイルを flask-helm-charts-circleci
ディレクトリ内に作成し、以下のコードを入力します。
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0')
上記のコードスニペットでは、静的応答を返す GET
ルートを定義しています。ユースケースに応じて、hello_world
メソッドに独自のロジックを追加してもよいでしょう。また、以下のようなルーティングを追加することもできます。
@app.route('/health')
def health_check():
return 'Health OK'
このアプリは、次のコマンドでローカル実行できます。このコマンドを実行すると、Flask アプリが localhost
のポート 5000
で実行されます。
python app.py
http://127.0.0.1:5000
にアクセスして、応答を確認します。
アプリを Docker 化する
Flask アプリを作成できたので、次は Dockerfile
を追加してこのアプリをコンテナ化します。今回は、Docker Hub にある Python 3.9.1 ベースイメージを使用します。Dockerfile
を flask-helm-charts-circleci
ディレクトリ内に作成し、以下のコードスニペットを入力します。
FROM python:3.9.1
ADD . /app
WORKDIR /app
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]
このコードスニペットで行っている処理は次のとおりです。
- Python 3.9.1 ベースイメージを指定する
ADD .
ステップで、プロジェクトのルートディレクトリにあるファイルすべてをコンテナの/app
ディレクトリにコピーするWORKDIR
で、コンテナのカレントディレクトリを/app
に設定する。以降のコマンドはすべて、このディレクトリ内で実行されます。RUN
ステップで、requirements.txt
ファイルで定義した Python 依存関係をインストールするEXPOSE
ステップで、Docker コンテナのポート5000
を公開する- 最後に
CMD
で、コンテナのスタートアップコマンドを定義する。これで、コンテナの実行時に Flask アプリが起動されます。
Docker コンテナをビルドして実行する
ローカルで Docker コンテナをビルドして実行し、コンテナが期待どおり動作することを確認します。セットアップに CircleCI を使用する予定の場合、この手順を省略してかまいません。
Docker イメージをビルドするには、flask-helm-charts-circleci
ディレクトリで以下のコマンドを実行します。
docker build -t flask-hello-world .
注: 上記のコマンドを実行する前に、ローカルシステムに Docker をインストールしておく必要があります。インストール手順は、「前提条件」セクションのリンクを参照してください。
最後に、以下のコマンドを実行します。
docker run -it --rm -p 5001:5000 flask-hello-world:latest
このコマンドは、Docker コンテナのポート 5000
をローカルのポート 5001
に公開します。http://127.0.0.1:5001
にアクセスして、アプリが期待どおりに動作していることを確認してください。
AWS リソースを作成する
テストとデプロイを行うために、必要な AWS リソースをアカウントにプロビジョニングしましょう。アカウントにある既存のリソースを使用するか、新しいリソースをプロビジョニングしてください (リソースをまだ作成していない場合)。
- Flask Docker イメージをホストするための AWS ECR リポジトリを作成する
- Helm チャートをホストするための AWS ECR リポジトリを作成する
- Kubernetes ワークロードを実行するための AWS EKS クラスタを作成する
クラスタの作成時に使用した IAM ユーザーは、CircleCI で認証情報を設定する際にも使用します。
Helm Chart (チャート) を作成する
Chart (チャート) とは、Helm のパッケージ形式です。チャートには、関連する Kubernetes リソース一式を記述するファイルをまとめます。このセクションでは、先ほど作成したアプリ用の Helm チャートを定義します。
アプリのチャートを作成するには、flask-helm-charts-circleci
ディレクトリで以下のコマンドを実行します。
helm create charts
注: 上記のコマンドを実行する前に、Helm CLI をインストールしてください。インストール手順は、「前提条件」セクションのリンクを参照してください。
このコマンドを実行すると、次の構造の charts
ディレクトリが作成されます。
charts/
Chart.yaml
values.yaml
charts/
templates/
charts/templates/
ディレクトリには複数のファイルが含まれていますが、必要になるのは _helpers.tpl
、deployment.yaml
、service.yaml
だけです。それ以外のファイルは、templates
ディレクトリから削除してください。
次に、deployment テンプレートを更新して、このチュートリアルに不要ないくつかの変数を削除します。また、Flask アプリケーションを実行するポートは 5000
なので、containerPort
をこの値に変更します。charts/templates/deployment.yaml
の内容を、以下のコードスニペットで置き換えます。
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "charts.fullname" . }}
labels:
{{- include "charts.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "charts.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "charts.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 5000
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
次に、values.yaml
ファイル内の値を、アプリケーションにあわせて更新します。charts/values.yaml
の内容を、以下のコードスニペットで置き換えます。
replicaCount: 1
image:
repository: <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/<AWS_ECR_REPO_NAME>
pullPolicy: IfNotPresent
tag: "latest"
labels:
env: "staging"
service:
port: 5000
targetPort: 5000
type: LoadBalancer
replicaCount: 1
autoscaling:
enabled: false
ingress:
enabled: false
imagePullSecrets:
- name: regcred
<AWS_ACCOUNT_ID>
は使用する AWS アカウント ID に、<AWS_REGION>
は AWS のデプロイリージョンに、<AWS_ECR_REPO_NAME>
は Flask Docker イメージの AWS ECR リポジトリに置き換えてください。
最後に、charts/Chart.yaml
の name
を更新します。
name: flask-helm-chart
このチャート名は、AWS ECR Helm リポジトリの名前にする必要があります。
ローカルでテストを実行する
CircleCI でデプロイを自動化する前に、これまでに準備したものをローカルでテストすることをお勧めします。このセクションでは、手動でイメージと Helm チャートを AWS ECR にプッシュして、Helm チャートを AWS EKS クラスタにデプロイする方法を説明します。すぐに CircleCI でデプロイを自動化したい方は、次のセクションに進んでください。
Docker イメージを ECR にプッシュする
Docker イメージをプッシュするには、AWS ECR で Docker を認証する必要があります。以下のコマンドを実行して認証を行います。
aws ecr get-login-password --region <AWS_REGION> | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com
上述のコマンドを実行する前に、AWS CLI をインストールし、AWS 認証情報を設定しておく必要があります。詳細な手順は、「前提条件」セクションのリンクを参照してください。
次に、以下のコマンドを実行して、前のセクションでビルドした Docker イメージにタグを付けます。
docker tag flask-hello-world:latest <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/flask-hello-world
使用する AWS アカウントで、flask-hello-world
という名前の ECR リポジトリを用意しておいてください。
以下のコマンドを実行して、Docker イメージを AWS ECR リポジトリにプッシュします。
docker push <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/flask-hello-world:latest
Helm チャートを ECR にプッシュする
Helm チャートをプッシュするには、AWS ECR で helm
を認証する必要があります。以下のコマンドを実行して認証を行います。
aws ecr get-login-password --region us-west-2 | helm registry login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com
次に、プロジェクトのルートディレクトリで以下のコマンドを実行して、Helm チャートをパッケージ化します。
helm package charts
このコマンドにより、圧縮バージョンの Helm チャートを含む flask-helm-chart-0.1.0.tgz
ファイルが生成されます。
tgz
ファイルの名前は、charts/Chart.yaml
で定義したチャート名によって異なります。
最後に、以下のコマンドを実行して、Helm チャートを AWS ECR リポジトリにプッシュします。
helm push flask-helm-chart-0.1.0.tgz oci://<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/
Helm チャートを AWS EKS にデプロイする
Helm チャートを AWS ECR リポジトリにプッシュしたことで、チャートを AWS EKS クラスタにデプロイする準備が整いました。まず、eksctl
を使用して、ローカルの kubeconfig
を更新します。
eksctl utils write-kubeconfig --cluster=<AWS_EKS_CLUSTER_NAME> --region=<AWS_REGION>
このコマンドを実行する前に、eksctl
をローカルシステムにインストールしておいてください。 <AWS_EKS_CLUSTER_NAME>
は AWS EKS クラスタの名前に変更し、<AWS_REGION>
は選択した AWS リージョン名に変更してください。
このコマンドを実行すると、kubeconfig が $HOME/.kube/config
に書き込まれます。
最後に、helm install
コマンドを実行して、チャートを EKS クラスタにインストールします。
helm install flask-helm oci://<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/flask-helm-chart --version 0.1.0
このコマンドにより、ワークロードが EKS クラスタにインストールされます。クラスタの pods
を確認して、インストールが完了したか確かめてください。
kubectl get pods
CircleCI でデプロイを自動化する
ローカルでの Kubernetes クラスタのテストが完了したので、次はワークフローを自動化して、main ブランチにコードをプッシュするたびに変更後のコードがデプロイされるようにします。デプロイを自動化するには、以下の作業が必要です。
- Helm デプロイスクリプトを追加する
- コンフィグスクリプトを追加する
- CircleCI でプロジェクトをセットアップする
Helm デプロイスクリプトを追加する
クラスタ上の既存の Helm チャートを更新する場合、helm install
コマンドではなく helm upgrade
コマンドを使用する必要があります。flask-helm
という名前のチャートがクラスタ上にデプロイ済みであること、および適切なコマンドを使っていることを確認するシェルスクリプトを作成しましょう。scripts/deploy-flask.sh
というファイルを作成して、以下のコードを入力します。
#!/bin/bash
TAG=$1
AWS_ECR_ACCOUNT_URL=$2
AWS_ECR_HELM_REPO_NAME=$3
echo "oci://$AWS_ECR_ACCOUNT_URL/$AWS_ECR_HELM_REPO_NAME"
export KUBECONFIG=$HOME/.kube/config
result=$(eval helm ls | grep flask-helm)
if [ $? -ne "0" ]; then
helm install flask-helm "oci://$AWS_ECR_ACCOUNT_URL/$AWS_ECR_HELM_REPO_NAME" --version $TAG
else
helm upgrade flask-helm "oci://$AWS_ECR_ACCOUNT_URL/$AWS_ECR_HELM_REPO_NAME" --version $TAG
fi
このシェルスクリプトでは、TAG
、AWS_ECR_ACCOUNT_URL
、AWS_ECR_HELM_REPO_NAME
をパラメーターとして受け取ります。
コンフィグスクリプトを追加する
まず、CI パイプライン用のコンフィグファイルが置かれているプロジェクトのルートに、.circleci/config.yaml
スクリプトを追加します。このファイルに、以下のコードスニペットを追記します。
version: 2.1
orbs:
aws-ecr: orbies/aws-ecr@1.2.1
jobs:
install_helm_chart:
docker:
- image: cimg/python:3.10.7
steps:
- checkout
- run:
name: Install awscli
command: curl --silent "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && unzip awscliv2.zip && sudo ./aws/install
- run:
name: Install eksctl
command: curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp && sudo mv /tmp/eksctl /usr/local/bin
- run:
name: Install and configure kubectl
command: curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl && kubectl version --client
- run:
name: Install and configure kubectl aws-iam-authenticator
command: curl -Lo aws-iam-authenticator https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/v0.5.9/aws-iam-authenticator_0.5.9_linux_amd64 && chmod +x ./aws-iam-authenticator && mkdir -p $HOME/bin && cp ./aws-iam-authenticator $HOME/bin/aws-iam-authenticator && export PATH=$PATH:$HOME/bin
- run:
name: Install and configure helm
command: sudo curl -L https://get.helm.sh/helm-v3.10.1-linux-amd64.tar.gz | tar xz && sudo mv linux-amd64/helm /bin/helm && sudo rm -rf linux-amd64
- run:
name: "docker login"
command: |
aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ECR_ACCOUNT_URL
- run:
name: "helm login"
command: |
aws ecr get-login-password --region $AWS_DEFAULT_REGION | helm registry login --username AWS --password-stdin $AWS_ECR_ACCOUNT_URL
- run:
name: "cluster configs"
command: |
eksctl utils write-kubeconfig --cluster=$AWS_EKS_CLUSTER_NAME --region=$AWS_CLUSTER_REGION
- run:
name: "helm install"
command: bash ./scripts/deploy-flask.sh 1.0.0 $AWS_ECR_ACCOUNT_URL $AWS_ECR_HELM_REPO_NAME
workflows:
build_and_push_image:
jobs:
- aws-ecr/build-and-push-image:
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
repo: "${AWS_ECR_REPO_NAME}"
docker-login: false
account-url: AWS_ECR_ACCOUNT_URL
region: AWS_DEFAULT_REGION
tag: "latest"
- aws-ecr/push-helm-chart:
account-url: AWS_ECR_ACCOUNT_URL
aws-access-key-id: AWS_ACCESS_KEY_ID
aws-secret-access-key: AWS_SECRET_ACCESS_KEY
create-repo: false
path: ./charts
region: AWS_DEFAULT_REGION
repo: "${AWS_ECR_HELM_REPO_NAME}"
tag: 1.0.0
requires:
- aws-ecr/build-and-push-image
- install_helm_chart:
requires:
- aws-ecr/push-helm-chart
少し時間をとって、上記の CircleCI コンフィグファイルを確認してみましょう。このコンフィグファイルでは、3 つのジョブ
を定義しています。
aws-ecr/build-and-push-image
ジョブでは、aws-ecr orb を使用して Flask Docker イメージをビルドし、AWS ECR リポジトリにプッシュします。aws-ecr/push-helm-chart
ジョブも、aws-ecr orb を使用し、Helm チャートをパッケージ化して AWS ECR Helm リポジトリにプッシュします。install_helm_chart
ジョブでは、ECR リポジトリから Helm チャートをプルし、AWS EKS クラスタにインストールします。 このジョブのステップで行う処理は以下のとおりです。awscli
、eksctl
、kubectl
、aws-iam-authenticator
、helm
などの必要なツールをインストールする- AWS CLI を使用して Docker および Helm を AWS ECR で認証する
eksctl
を使用して kubeconfig を$HOME/.kube/config
に書き込むscripts/deploy-flask.sh
スクリプトを使用して Helm チャートを EKS クラスタにインストールする
コンフィグファイルを適切に用意できたので、GitHub にプロジェクトのリポジトリを作成して、すべてのコードをプッシュしましょう。手順については、GitHub にプロジェクトをプッシュする方法に関するブログ記事 (英語) を参照してください。
CircleCI でプロジェクトをセットアップする
次は、CircleCI アカウントにログインします。CircleCI ダッシュボードの [Projects (プロジェクト)] タブをクリックし、目的の GitHub リポジトリ名を検索して、該当するプロジェクトの [Set Up Project (プロジェクトのセットアップ)] をクリックします。
新しいコンフィグファイルを手動で追加するか、既存のコンフィグファイルを使用するかを確認するメッセージが表示されます。必要なコンフィグファイルはコードベースにプッシュ済みなので、[Fastest (最速)] オプションを選択し、コンフィグファイルが置かれているブランチの名前を入力します。[Set Up Project (プロジェクトのセットアップ)] をクリックして続行します。
セットアップが完了すると、パイプラインがトリガーされます。まだ環境変数を定義していないので、最初の実行ではパイプラインが失敗します。
環境変数を設定する
プロジェクトダッシュボードの [Project settings (プロジェクト設定)] をクリックし、[Environment variables (環境変数)] タブに移動します。[Add environment variable (環境変数を追加)] ボタンをクリックし、新しいキー値を追加します。
以下の環境変数を追加してください。
AWS_ACCESS_KEY_ID
: AWS 認証情報の作成中に取得されるアクセスキーAWS_SECRET_ACCESS_KEY
: AWS 認証情報の作成中に取得されるシークレットAWS_DEFAULT_REGION
: アプリケーションをデプロイするリージョンAWS_ECR_ACCOUNT_URL
: AWS アカウントの URL。<AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com
のと の値を適切なアカウント ID とリージョンに置き換え、アカウント URL として設定します。 AWS_ECR_HELM_REPO_NAME
: Helm チャートの AWS ECR リポジトリの名前AWS_ECR_REPO_NAME
: Flask アプリケーションの Docker イメージが置かれている AWS ECR リポジトリの名前AWS_EKS_CLUSTER_NAME
: AWS EKS クラスタの名前AWS_CLUSTER_REGION
: クラスタを作成したリージョン。このリージョンは、デフォルトと異なる場合があります。
環境変数を設定したら、パイプラインを再実行します。今回は、ビルドに成功するはずです。
まとめ
このチュートリアルでは、CircleCI を使用して、Helm チャートを自動的にビルドし AWS EKS クラスタにデプロイする方法について説明しました。Helm チャートは再利用可能なテンプレートであり、クラスタの複雑な面を簡単に処理し、クラスタの更新プロセスを簡素化できます。また、Helm チャートは、パブリックサーバーとプライベートサーバーの両方で簡単にバージョン管理、共有、ホストできます。CircleCI を使用すると、ビルドとデプロイのパイプラインを自動化して、継続的デリバリーを実現できます。パイプラインを使用して、Docker イメージと Helm チャートを AWS ECR リポジトリにプッシュし、ワークロードに更新があれば EKS クラスタも更新できます。
今回使用したソースコードは、こちらの GitHub リポジトリにあります。