AKS クラスターでcert-managerからApplication Gatewayに証明書を構成してみる
前置き
これはGMOペパボ エンジニア Advent Calendar 2024の5日目です。
背景
7ヶ月ぐらい前に初挑戦のSREチームにジョイン(って言いたいだけ)しました。
いかんせんAzureのことはそこそこオタクでもkubernetesのことは何も分からんということで環境構築とかに脳のリソースを割かずにkubernetesについて学べないかと思った次第で第一回目にAKSでcert-manager使ってみることにチャレンジしてみました。
それと折角なので雰囲気で使っていた節のある用語たちを整理しつつな感じで。
マサカリは手渡しぐらいの勢いだと助かります。
用語
cert-managerとはTLS証明書を定期的に更新してくれるすごいやつです。
これで証明書期限切れとはおさらばですな。
cert-manager
Helmとは構成済みのkubernetesリソースをパッケージとして扱える便利なやつです。
cert-managerもHelmを使えば簡単にインストールできます。
また、このまとめたパッケージのことをHelmチャートと呼ぶのですがチャート(海図)とはkubernetes(操舵手)とかけていてなかなかオシャレですな。
the-purpose-of-helm
ACME(アクミーと読みます)とはCA(認証局)に対してサーバー証明書の発行者がするあれこれ(CSRの作成・送信、ドメインの所有者確認、証明書設定)を自動化するプロトコルです。cert-managerとは別にACMEクライアントとして有名なのはcertbotがあります。
ACMEについて
ACMEチャレンジとはACMEプロトコルにおいてドメインの所有者確認をすることを指します。
ACMEチャレンジには2種類あり、HTTP-01チャレンジとDNS-01チャレンジがあります。
HTTP-01チャレンジだとサーバーに特定のHTTPリソースをプロビジョニングできることを持って所有者であることを証明します。
DNS-01チャレンジだとTXTレコードに指定の文字列を埋め込んで所有者であることを証明します。
これは僕の勝手な感覚的な話ですがHTTP-01チャレンジのがすぐ反映する分良いのではないかなという印象です。(DNSレコードのTTLを1secにしたら気にしなくても良いのかもだけど)とはいえDNS-01チャレンジだとTXTレコードを登録する手間はかかるのでやっぱりHTTP-01チャレンジが良さそうですね。
Which ACME Challenge Type Should I Use? HTTP-01 or DNS-01?
Automatic Certificate Management Environment (ACME)
前提
Kubernetes バージョン:1.29.9
での検証です。(2024-12-04時点)
test-aks
というリソースグループ名でコマンド実行します。適宜、置き換えてください。
環境はCloud ShellのBashで行います。
kubectl
は入ってなければ入れてください。 kubectl
はaliasで k
としてます。(公式のおすすめ設定)
Kubectl autocomplete
helm
はCloud Shellでならデフォルトで入っています。必要であれば最新版をインストールしてください。
Helmインストール(必要なら)
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
AKSクラスターのデプロイ
サービスプリンシパルを作成します。
強い権限で作成するため不要になったら削除してください。
subscriptionID
はご自身のSubscriptionIDに変えておいてください。
resourceGroupName="test-aks"
location="japaneast"
deploymentName="ingress-appgw"
subscriptionId="xxxxx-xxxxx-xxxxx-xxxxx"
az ad sp create-for-rbac --role Contributor --scopes /subscriptions/${subscriptionId} -o json > auth.json
AKSクラスターをデプロイします。
wget https://raw.githubusercontent.com/Azure/application-gateway-kubernetes-ingress/master/deploy/azuredeploy.json -O template.json
appId=$(jq -r ".appId" auth.json)
password=$(jq -r ".password" auth.json)
objectId=$(az ad sp show --id $appId --query "id" -o tsv)
cat <<EOF > parameters.json
{
"aksServicePrincipalAppId": { "value": "$appId" },
"aksServicePrincipalClientSecret": { "value": "$password" },
"aksServicePrincipalObjectId": { "value": "$objectId" },
"aksEnableRBAC": { "value": false },
"aksAgentVMSize": { "value": "Standard_D2s_v3" }
}
EOF
az group create -n $resourceGroupName -l $location
az deployment group create -g $resourceGroupName -n $deploymentName --template-file template.json --parameters parameters.json
az deployment group show -g $resourceGroupName -n $deploymentName --query "properties.outputs" -o json > deployment-outputs.json
自身のクラスターの資格情報を取得して作成したAKSクラスターに切り替えます。
aksClusterName=$(jq -r ".aksClusterName.value" deployment-outputs.json)
az aks get-credentials --resource-group $resourceGroupName --name $aksClusterName
k config set-context $aksClusterName
k config current-context
Microsoft Entra ポッド IDのリソースをデプロイします。
端的にマネージドIDがAKS上から使えるようです。(詳しくはない)
k create -f https://raw.githubusercontent.com/Azure/aad-pod-identity/master/deploy/infra/deployment-rbac.yaml
Azure Kubernetes Service で Microsoft Entra ポッドマネージド ID を使用する (プレビュー)
AGICパッケージをインストールします。
applicationGatewayName=$(jq -r ".applicationGatewayName.value" deployment-outputs.json)
identityClientId=$(jq -r ".identityClientId.value" deployment-outputs.json)
identityResourceId=$(jq -r ".identityResourceId.value" deployment-outputs.json)
wget https://raw.githubusercontent.com/Azure/application-gateway-kubernetes-ingress/master/docs/examples/sample-helm-config.yaml -O helm-config.yaml
sed -i "s|<subscriptionId>|${subscriptionId}|g" helm-config.yaml
sed -i "s|<resourceGroupName>|${resourceGroupName}|g" helm-config.yaml
sed -i "s|<applicationGatewayName>|${applicationGatewayName}|g" helm-config.yaml
sed -i "s|<identityResourceId>|${identityResourceId}|g" helm-config.yaml
sed -i "s|<identityClientId>|${identityClientId}|g" helm-config.yaml
helm install agic-controller oci://mcr.microsoft.com/azure-application-gateway/charts/ingress-azure --version 1.7.5 -f helm-config.yaml
新しい Application Gateway デプロイを使用して AGIC をインストールする
cert-managerインストール
helm repo add jetstack https://charts.jetstack.io --force-update
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.16.2 \
--set crds.enabled=true
k label namespace cert-manager cert-manager.io/disable-validation=true
テスト環境の作成
DNSレコードの登録します。
先にApplicationGatewayからフロントエンドIPを取得して、ご自身の権威DNSサーバーにDNSレコードを登録しましょう。
※画像は僕のRoute53に登録するとこ
az network public-ip show \
-g $resourceGroupName \
-n $(az network public-ip list -g $resourceGroupName --query '[0].name' -o tsv) \
--query 'ipAddress' -o tsv
cert-managerをデプロイします。
必ずLet's Encryptのために <YOUR.EMAIL@ADDRESS>
をご自身の持つアドレスに置き換えてください。
k apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
email: <YOUR.EMAIL@ADDRESS>
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: example-issuer-account-key
solvers:
- http01:
ingress:
class: azure/application-gateway
EOF
AKS クラスター用の Application Gateway で Let's Encrypt 証明書を使用する
unknown field "spec.acme.solvers[0].http01.ingress.ingressclassName" #121159
テスト用のguestbookアプリケーションを作成します。
今回はIngressからトラフィックを受けたいのでNodeportを使わないようにします。
curl -fsSL -o guestbook-all-in-one.yaml https://raw.githubusercontent.com/kubernetes/examples/master/guestbook/all-in-one/guestbook-all-in-one.yaml
sed -i 's/gb-frontend:v4/gb-frontend:v5/g' guestbook-all-in-one.yaml
sed -i 's/type: NodePort/type: ClusterIP/g' guestbook-all-in-one.yaml
k apply -f guestbook-all-in-one.yaml
Ingressをデプロイします。
corp.yukarism.net
は僕の所有するドメイン名なので適宜ご自身の独自ドメインに書き換えてください。
k apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: guestbook-letsencrypt-staging
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
ingressClassName: azure-application-gateway
tls:
- hosts:
- corp.yukarism.net
secretName: guestbook-secret-name
rules:
- host: corp.yukarism.net
http:
paths:
- backend:
service:
name: frontend
port:
number: 80
path: /
pathType: Prefix
EOF
証明書がTrueになっていれば成功です!
k get cert guestbook-secret-name
NAME READY SECRET AGE
guestbook-secret-name True guestbook-secret-name 4m13s
動作検証
サイトを確認してみても問題なくstaging用の証明書が発行されていることがわかります。
トラシュー
k8s内での通信確認
IPアドレスとかNamespaceとかは適宜変えてください。
k run curl --image=curlimages/curl -n default -it --rm -- curl 10.0.0.2
ingressでの通信が取れないなどあればAGICのlogを確認してみてください。
何らかの理由で弾かれているかもしれません。
k logs agic-controller-ingress-azure-xxxxxxxx
証明書発行がうまくいかなければcert-managerのPodのlogを確認してみてください。
k logs cert-manager-xxx-xxx -n cert-manager
ApplicationGatewayの構成がうまくいかないといった報告も見かけました。
今回はこのようなことにはなりませんでしたが、参考程度に。
Solving Cert-Manager and Azure Application Gateway Integration for AKS
完走した感想
久々に検証して張り切っちゃった
楽しい〜〜〜〜〜〜〜!
でも、あれだね。ドキュメントにあること上から垂れ流せばええんやろ!楽勝楽勝とか思ってた僕を殴りたいね。めっちゃ詰まったわ。
Discussion