Private Linkを用いたAKS + Azure Database for MySQLの構築
初めに
AKSとAzure Database for MySQL間の通信をセキュアにするために、
両者をPrivate Linkを用いて接続することで、両者間の通信をMicrosoftバックボーンネットワーク内で完結できるようにします。
また、構築後に、Djangoのチュートリアルで紹介されている質問アプリをコンテナ化したものを用いて動作確認を行います。
※詳細はページ下部の参考資料リンクからどうぞ
目次
0.構成図
1.AKSとAzure Database for MySQLをAzure上に作成
2.Private Linkを用いてAKSとAzure Database for MYSQLを接続
3.FQDNを用いてDBに接続できるようにする
4.WebアプリをAKS上にデプロイ
5.ブラウザ経由でWebアプリへアクセスして質問を登録
6.MySQLクライアントを用いてDBへの変更内容を確認
7.クリーンアップ
0.構成図
1.AKSとAzure Database for MySQLをAzure上に作成
#リソースグループの作成 az group create -g test -l japaneast #VNetとSubnetの作成 az network vnet create -g test -l japaneast -n test-vnet \ --address-prefixes 10.1.0.0/16 --subnet-name test-vnet-subnet \ --subnet-prefixes 10.1.0.0/24 #Subnetの設定を更新 #PrivateEndpointをSubnetにデプロイするために、PrivateEndpointNetworkPolicyを無効化 az network vnet subnet update -g test --vnet-name test-vnet \ -n test-vnet-subnet --disable-private-endpoint-network-policies true #SubnetのResourceIDを変数に入れる SUBNET_ID=$(az network vnet subnet show -g test --vnet-name test-vnet \ -n test-vnet-subnet --query id -otsv) #AKSをVNet上に作成 #"--network-plugin azure"を指定することで、 Azure CNI networkingを有効化され、 #VNet Subnet上にAKSを作成することが可能になる #コスト削減のため、ノード数を1、ノードVMのサイズをStandard_B2sに変更 az aks create -g test -n test-aks --network-plugin azure \ --vnet-subnet-id $SUBNET_ID --node-count 1 --node-vm-size Standard_B2s #Azure Database for MySQLを作成 #"--ssl-enforcement Disabled"を指定してSSL無効化(WebアプリがSSL接続できなかったため) #注1:"-n"で指定しているDB名は全世界で一意である必要がある #注2:"-l"でregionを指定しないと、AKSとは異なるregionに作成されてしまい後々困る az mysql server create -g test -l japaneast -n test-mysqlXXX \ --admin-user user --admin-password 1qA2wS3eD \ --ssl-enforcement Disabled
2.Private Linkを用いてAKSとAzure Database for MYSQLを接続
#DBのResourceIDを変数に入れる DB_ID=$(az mysql server show -g test -n test-mysqlXXX --query id -otsv) #今回作成したDBをサポートしているGroupIDを変数に入れる GROUP_ID=$(az network private-link-resource list -g test --id $DB_ID \ --query [].properties.groupId -otsv) #Private Endpointの作成 az network private-endpoint create -g test --vnet-name test-vnet \ --subnet test-vnet-subnet -n test-privateendpoint \ --private-connection-resource-id $DB_ID --group-id $GROUP_ID \ --connection-name test-connection
2.(番外編)
#ちなみに、先程作成したPriavteEndpointに対して設定されているのは、10.1.XXX.XXX(プライベートIPアドレス) az network private-endpoint show -g test -n test-privateendpoint --query customDnsConfigs[0].ipAddresses[0] "10.1.XXX.XXX" #AKS上のPodから、このプライベートIPアドレスを用いてDBに接続することは可能 mysql -h 10.1.XXX.XXX -u user@test-mysqlXXX -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is ... #けれども、DBのFQDNを調べて az mysql server show -g test --name test-mysqlXXX --query fullyQualifiedDomainName "test-mysqlXXX.mysql.database.azure.com" #AKS上のPodから、FQDNを用いた接続を試みると、エラーを吐く mysql -h test-mysqlXXX.mysql.database.azure.com -u user@test-mysqlXXX -p Enter password: ERROR 9000 (HY000): Client with IP address '20.44.XXX.XXX' is not allowed to connect to this MySQL server. #試しに、AKS上のPodから、このFQDNに対して名前解決を行うと、パブリックIPアドレス(40.79...)が返ってくる nslookup test-mysqlXXX.mysql.database.azure.com Server: 10.0.0.10 Address: 10.0.0.10#53 Non-authoritative answer: test-mysqlXXX.mysql.database.azure.com canonical name = cr6.japaneast1-a.control.database.windows.net. Name: cr6.japaneast1-a.control.database.windows.net Address: 40.79.XXX.XXX
現時点で、AKS側からプライベートIPアドレスを用いてDBにセキュアに接続することは可能。
しかし、FQDNで接続しようとするとプライベートIPアドレスではなく、パブリックIPアドレスを用いて接続しようとしてしまう。
3.FQDNを用いてDBに接続できるようにする
※この状態でも、プライベートIPアドレスを用いて、DBにPrivateLink経由で接続できるが、 今回はFQDNで接続したいので、FQDNとプライベートIPアドレスの関連付けを行います。
#プライベートDNSゾーンを作成 az network private-dns zone create -g test \ -n privatelink.mysql.database.azure.com #DNSゾーンへのVnetリンクを作成 az network private-dns link vnet create -g test \ --zone-name privatelink.mysql.database.azure.com --name test-dnslink \ --virtual-network test-vnet --registration-enabled false #DNSゾーングループを作成 az network private-endpoint dns-zone-group create -g test \ --endpoint-name test-privateendpoint --name test-zonegroup \ --private-dns-zone privatelink.mysql.database.azure.com --zone-name test-zone
4.WebアプリをAKS上にデプロイ
#AKSにkubectlで接続するための認証情報を入手 az aks get-credentials -g test -n test-aks #Webアプリをデプロイ、環境変数で接続先DBを指定 kubectl run test-django --image dekabitasp/test-django:v2.3 \ --env DATABASE_URL=mysql://user@test-mysqlXXX:1qA2wS3eD@test-mysqlXXX.mysql.database.azure.com:3306/defaultdb \ --port 8000 #Webアプリにインターネット経由でアクセスできるように kubectl expose po test-django --port 8000 --type LoadBalancer #今までk8s上に作った物の確認 kubectl get all NAME READY STATUS RESTARTS AGE pod/test-django 0/1 ContainerCreating 0 26s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.0.0.1 443/TCP 12m service/test-django LoadBalancer 10.0.191.177 20.89.80.13 8000:32167/TCP 21s
5.ブラウザ経由でWebアプリへアクセスして質問を登録
#Webアプリにログインする際に必要なUserを作成 kubectl exec test-django -it -- python manage.py createsuperuser #こんな感じで入力していく(メアドは不要) Username (leave blank to use 'root'): Email address: Password: Password (again): This password is too common. Bypass password validation and create user anyway? [y/N]: y Superuser created successfully. #Webアプリに接続するためのIPアドレスを確認(今回は20.89.80.13) kubectl get svc #又は kubectl get svc test-django -ojsonpath={.status.loadBalancer.ingress[0].ip} #ブラウザで"20.89.80.13:8000/admin"にアクセス
ログイン画面が出てくるので先程登録したユーザ名とパスワードを入力してログインする
一番下のQuestionsのaddボタンを押す
こんな感じで適当に質問、日付時間を登録して、保存する
(画像では、日付時間を入力せずに保存しようとして怒られてます)
6.MySQLクライアントを用いてDBへの変更内容を確認
#DBのホスト名を変数に入れる DB_HOSTNAME=$(az mysql server show -g test -n test-mysqlXXX \ --query fullyQualifiedDomainName -o tsv) #mysqlclient用のPodをデプロイ kubectl run mysqlclient --image mysql -- sleep infinity #先程のPodを用いてDBに接続する #DB作成時に指定したユーザ名を指定する kubectl exec -it mysqlclient -- mysql -u user@test-mysqlXXX \ -h $DB_HOSTNAME -p #DB作成時に指定したパスワードを入力してログイン Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 64240 Server version: 5.6.47.0 MySQL Community Server (GPL) Copyright (c) 2000, 2021, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. #全DBを表示 mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | defaultdb | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.05 sec) #操作対象のDBを指定 mysql> use defaultdb Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed #全Tableを表示 mysql> show tables; +----------------------------+ | Tables_in_defaultdb | +----------------------------+ | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | | polls_choice | | polls_question | +----------------------------+ 12 rows in set (0.01 sec) #polls_question Tableの中身を確認 #ブラウザから登録した質問がちゃんとDBに保存されてるのを確認 mysql> select * from polls_question; +----+---------------+----------------------------+ | id | question_text | pub_date | +----+---------------+----------------------------+ | 1 | sample | 2021-06-03 11:18:21.000000 | +----+---------------+----------------------------+ 1 row in set (0.00 sec) mysql> exit Bye
7.クリーンアップ
#現在存在しているResourceGroupを全部表示する az group list -otable #test ResourceGroupを削除 #--no-waitを指定すると、削除中でもターミナルが使うことが可能 az group delete -g test --no-wait -y #VNet作成時に自動で作成されたResourceGruopも削除 az group delete -g NetworkWatcherRG --no-wait -y #全てのResourceGroupが削除中になっているか確認 az group list -otable
参考資料
What is Azure Private Link? | Microsoft Docs
Quickstart - Create an Azure Private Endpoint using Azure CLI | Microsoft Docs
Disable network policies for private endpoints in Azure | Microsoft Docs
Azure プライベート エンドポイントの DNS 構成 | Microsoft Docs
Quickstart - Create an Azure private DNS zone using the Azure CLI | Microsoft Docs
az network private-endpoint | Microsoft Docs
Writing your first Django app, part 1 | Django documentation | Django