OSSデータカタログ「Datahub」をできる限りAWSマネージドサービスを利用した構成でデプロイしてみた
どーもsutoです。
今回は以前に記事で予告していたとおり、LinkedIn製のOSSデータカタログ「DataHub」をフロントエンド部分はAWS EKSに、それ以外のコンポーネントをAWSマネージドサービスに変更した構成で構築してみたいと思います。
今回のゴール
以前の記事では、全てのコンポーネントをAWS EKSクラスター上に構成した手順で紹介しました。
今回は以下の下側の図のように、Datahubのストレージにあたる部分を各AWSサービスに変更した構成で構築します。
つまり、Frontend部分(WebUIとMetadata Ingection)→EKSに残し、Metadata Store→RDS、Metadata Crawler→Glue schema registry、Elasticsearch→Amazon Opensearch、MAE/MCE機能→Amazon Managed Kafka(MSK)、CLB→ALBに変えます。
前提条件と作業環境の設定について
※作業環境はMacです。
事前に以下のツールをインストールしておく必要があります。Python3については省略させていただきますが、その他はリンク先を参考に実施できると思います。
- Pthon3.6以降の環境をセットアップしておく
- AWS CLIインストールとCredentials,Config設定
- kubectlインストール
brew install kubectl kubectl version --client
brew tap weaveworks/tap brew install weaveworks/tap/eksctl eksctl version
brew install helm
EKSクラスター作成
eksctlコマンドを使用してクラスターを作成します。今回は以下のパラメータで作成します。
- VPC:CIDRを指定して新規作成(10.1.0.0/16)
- クラスター名:datahub-test
- リージョン:ap-northeast-1
- インスタンスタイプ:t3.medium
- ノード数:3
eksctl create cluster \ --vpc-cidr 10.1.0.0/16 \ --name datahub-test \ --region ap-northeast-1 \ --with-oidc \ --node-type t3.medium \ --node-private-networking \ --nodes 3
実行するとCloudformationスタックが実行され、完了後にkubectl get nodes
を実行することで以下のように3ノードが表示されます
% kubectl get nodes NAME STATUS ROLES AGE VERSION ip-10-1-107-176.ap-northeast-1.compute.internal Ready <none> 3m6s v1.21.5-eks-bc4871b ip-10-1-152-244.ap-northeast-1.compute.internal Ready <none> 3m10s v1.21.5-eks-bc4871b ip-10-1-184-243.ap-northeast-1.compute.internal Ready <none> 3m7s v1.21.5-eks-bc4871b
ストレージ部分のリソースを構築
次に以下のCloudformationテンプレートを使って、RDS、OpenSearchクラスター、Glue schema registry、MSKクラスター、関連するSGやIAMロールを作成します。
Parameters: ProjectName: Description: ProjectName Type: String Default: 'datahub-test' DBInstanceClass: Type: String Default: 'db.t3.small' DbPassword: Type: String Description: Password string for master user of DB Instance Default: '' NoEcho: true KafkaInstanceType: Type: String Default: 'kafka.t3.small' OSSInstanceType: Type: String Default: 't3.medium.search' VPCId: Type: String Description: id for VPC. Default: 'vpc-xxxxxx' PrimarySubnetId: Type: String Description: Private Subnet id 1. Default: 'subnet-xxxxxx' SecondarySubnetId: Type: String Description: Private Subnet id 2. Default: 'subnet-xxxxxx' ThirdSubnetId: Type: String Description: Private Subnet id 3. Default: 'subnet-xxxxxx' EKSNodeRole: Type: String Description: Name of EKS nodegroup IAM Role. Default: 'eksctl-datahub-nodegroup-ng-NodeInstanceRole-xxxxxxxx' NodeInstanceSG: Type: String Description: EKS nodegroup instances SG id. Default: 'sg-xxxxxx' Resources: DatahubSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub ${ProjectName}-strage-sg GroupDescription: Enable datahub service instances access VpcId: !Ref VPCId SecurityGroupIngress: - IpProtocol: tcp FromPort: '443' ToPort: '443' SourceSecurityGroupId: !Ref NodeInstanceSG - IpProtocol: tcp FromPort: '3306' ToPort: '3306' SourceSecurityGroupId: !Ref NodeInstanceSG - IpProtocol: tcp FromPort: '9092' ToPort: '9092' SourceSecurityGroupId: !Ref NodeInstanceSG - IpProtocol: tcp FromPort: '2181' ToPort: '2181' SourceSecurityGroupId: !Ref NodeInstanceSG - IpProtocol: tcp FromPort: '9091' ToPort: '9091' SourceSecurityGroupId: !Ref NodeInstanceSG Tags: - Key: Name Value: !Sub ${ProjectName}-strage-sg MyDB: Type: AWS::RDS::DBInstance Properties: AllocatedStorage: '10' DBInstanceClass: !Ref DBInstanceClass DBInstanceIdentifier: !Sub ${ProjectName}-mysql DBSubnetGroupName: !Ref DBSubnetGroup Engine: MySQL MasterUsername: 'root' MasterUserPassword: !Ref DbPassword StorageType: gp2 VPCSecurityGroups: - !GetAtt 'DatahubSecurityGroup.GroupId' #DeletionPolicy: Snapshot DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: custom subnet group SubnetIds: - !Ref PrimarySubnetId - !Ref SecondarySubnetId - !Ref ThirdSubnetId MSKCluster: Type: 'AWS::MSK::Cluster' Properties: ClusterName: !Sub ${ProjectName}-kafka KafkaVersion: 2.6.2 NumberOfBrokerNodes: 3 EncryptionInfo: EncryptionInTransit: ClientBroker: TLS_PLAINTEXT BrokerNodeGroupInfo: BrokerAZDistribution: DEFAULT InstanceType: !Ref KafkaInstanceType SecurityGroups: - Ref: DatahubSecurityGroup StorageInfo: EBSStorageInfo: VolumeSize: 100 ClientSubnets: - !Ref PrimarySubnetId - !Ref SecondarySubnetId - !Ref ThirdSubnetId GlueSchemaRegistry: Type: AWS::Glue::Registry Properties: Description: 'Datahub schema registry' Name: !Sub ${ProjectName}-schema-registry GlueIAMPolicy: Type: 'AWS::IAM::Policy' Properties: PolicyName: !Sub ${ProjectName}-schema-registry-policy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - 'glue:GetRegistry' - 'glue:ListRegistries' - 'glue:CreateSchema' - 'glue:UpdateSchema' - 'glue:GetSchema' - 'glue:ListSchemas' - 'glue:RegisterSchemaVersion' - 'glue:GetSchemaByDefinition' - 'glue:GetSchemaVersion' - 'glue:GetSchemaVersionsDiff' - 'glue:ListSchemaVersions' - 'glue:CheckSchemaVersionValidity' - 'glue:PutSchemaVersionMetadata' - 'glue:QuerySchemaVersionMetadata' Resource: - !Sub arn:aws:glue:*:${AWS::AccountId}:schema/* - !GetAtt GlueSchemaRegistry.Arn - Effect: Allow Action: 'glue:GetSchemaVersion' Resource: '*' Roles: - !Ref EKSNodeRole OpenSearchServiceDomain: Type: AWS::OpenSearchService::Domain Properties: DomainName: !Sub ${ProjectName}-oss EngineVersion: 'OpenSearch_1.1' ClusterConfig: DedicatedMasterEnabled: true InstanceCount: '2' ZoneAwarenessEnabled: true InstanceType: !Ref OSSInstanceType DedicatedMasterType: !Ref OSSInstanceType DedicatedMasterCount: '3' DomainEndpointOptions: EnforceHTTPS: true EBSOptions: EBSEnabled: true Iops: '0' VolumeSize: '30' VolumeType: 'gp2' AccessPolicies: Version: '2012-10-17' Statement: - Effect: Allow Principal: AWS: '*' Action: 'es:*' Resource: '*' VPCOptions: SubnetIds: - !Ref PrimarySubnetId - !Ref SecondarySubnetId SecurityGroupIds: - Ref: DatahubSecurityGroup
※スタック内でOpensearch作成時にBefore you can proceed, you must enable a service-linked role~
のエラーが出たら以下コマンドでサービルリンクロールを事前に作成しましょう。
aws iam create-service-linked-role --aws-service-name es.amazonaws.com
作成したリソースの情報を控えておきます。
- RDSのマスターユーザー名とパスワード
- RDSのエンドポイント(以下を参照)
- MSKクラスターのブートストラップサーバのエンドポイント(テキストプレーン)
- MSKクラスターのzookeeperサーバのエンドポイント(テキストプレーン)
- ※どちらも3エンドポイントをまるごとコピーしてください
- OpensearchのVPCエンドポイント
- ※以下の赤枠の”https://”を除いた部分をコピー
EKSクラスターにALBコントローラーをセットアップ
- コントローラがAWSAPIを呼び出すことができるようにするためのIAMポリシードキュメントをダウンロードします。
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.2.0/docs/install/iam_policy.json
- 次のコマンドを実行して、ポリシードキュメントに基づいてIAMポリシーを作成します。
aws iam create-policy \ --policy-name AWSLoadBalancerControllerIAMPolicy \ --policy-document file://iam_policy.json
- eksctlを使用して、上記のポリシーをkubernetesポッドにアタッチできるサービスアカウントを作成します。
eksctl create iamserviceaccount \ --cluster=<<cluster-name>> \ --namespace=kube-system \ --name=aws-load-balancer-controller \ --attach-policy-arn=arn:aws:iam::<<account-id>>:policy/AWSLoadBalancerControllerIAMPolicy \ --override-existing-serviceaccounts \ --approve
- 次のコマンドを実行して、TargetGroupBindingカスタムリソース定義をインストールします。
kubectl apply -k "github.com/aws/eks-charts/stable/aws-load-balancer-controller//crds?ref=master"
- 最新バージョンのALBコントローラーを含むヘルムチャートリポジトリを追加します。
helm repo add eks https://aws.github.io/eks-charts helm repo update
- 次のコマンドを実行して、コントローラーをkubernetesクラスターにインストールします。
helm upgrade -i aws-load-balancer-controller eks/aws-load-balancer-controller \ --set clusterName=<<cluster-name>> \ --set serviceAccount.create=false \ --set serviceAccount.name=aws-load-balancer-controller \ -n kube-system
- インストールが完了すれば、以下のコマンドで次のような結果が返されるはずです。
% kubectl get deployment -n kube-system aws-load-balancer-controller NAME READY UP-TO-DATE AVAILABLE AGE aws-load-balancer-controller 2/2 2 2 142m
Helmを使用してEKSクラスターにDataHubをセットアップ
- DatahubのリポジトリをHelmに追加します。
helm repo add datahub https://helm.datahubproject.io/
- 作成したRDSのパスワードを格納したkubernetesシークレットを作成します。
kubectl create secret generic mysql-secrets --from-literal=mysql-root-password=<RDSのパスワード>
- Helmチャートのvalues.yamlを作成します。各パラメータを以下のようにAWSリソースの情報に書き換えて保存してください。
- 【参考】デフォルト構成はこちら
datahub-gms: enabled: true image: repository: linkedin/datahub-gms tag: "v0.8.23" ingress: enabled: true annotations: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: instance alb.ingress.kubernetes.io/inbound-cidrs: 0.0.0.0/0 hosts: - paths: - /* datahub-frontend: enabled: true image: repository: linkedin/datahub-frontend-react tag: "v0.8.23" ingress: enabled: true annotations: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: instance alb.ingress.kubernetes.io/inbound-cidrs: 0.0.0.0/0 hosts: - paths: - /* datahub-mae-consumer: image: repository: linkedin/datahub-mae-consumer tag: "v0.8.23" datahub-mce-consumer: image: repository: linkedin/datahub-mce-consumer tag: "v0.8.23" datahub-ingestion-cron: enabled: false image: repository: linkedin/datahub-ingestion tag: "v0.8.23.0" elasticsearchSetupJob: enabled: true image: repository: linkedin/datahub-elasticsearch-setup tag: "v0.8.23" extraEnvs: - name: USE_AWS_ELASTICSEARCH value: "true" kafkaSetupJob: enabled: true image: repository: linkedin/datahub-kafka-setup tag: "v0.8.23" mysqlSetupJob: enabled: true image: repository: acryldata/datahub-mysql-setup tag: "v0.8.23.0" datahubUpgrade: enabled: true image: repository: acryldata/datahub-upgrade tag: "v0.8.23.0" noCodeDataMigration: sqlDbType: "MYSQL" global: graph_service_impl: elasticsearch datahub_analytics_enabled: true datahub_standalone_consumers_enabled: false elasticsearch: host: "<OpensearchクラスターのVPCエンドポイント>" port: "443" useSSL: "true" kafka: bootstrap: server: "<MSKクラスターのbootstrap-server endpoint>" zookeeper: server: "<MSKクラスターのzookeeper endpoint>" ## For AWS MSK set this to a number larger than 1 partitions: 3 # MSKクラスタ数と同じにする replicationFactor: 3 # MSKクラスタ数と同じにする schemaregistry: type: AWS_GLUE glue: region: ap-northeast-1 # <<region>> registry: <Glue schema registry name> sql: datasource: host: "<RDSのエンドポイント>:3306" hostForMysqlClient: "<RDSのエンドポイント>" port: "3306" url: "jdbc:mysql://<RDSのエンドポイント>:3306/datahub?verifyServerCertificate=false&useSSL=true&useUnicode=yes&characterEncoding=UTF-8" driver: "com.mysql.jdbc.Driver" username: "<RDSのマスターユーザー>" password: secretRef: mysql-secrets secretKey: mysql-root-password datahub: gms: port: "8080" mae_consumer: port: "9091" appVersion: "1.0"
- 以下のコマンドを実行してDatahubをデプロイします。
helm upgrade --install datahub datahub/datahub --values <yamlファイルを保存したフォルダパス>/values.yaml
- デプロイ完了後、podの一覧が以下のようになるはずです。
% kubectl get pods NAME READY STATUS RESTARTS AGE datahub-datahub-frontend-6c47c77468-v2hsn 1/1 Running 0 4m23s datahub-datahub-gms-59dfd97556-9vrsr 1/1 Running 0 4m23s datahub-datahub-upgrade-job-8xbhc 0/1 Error 0 4m23s datahub-datahub-upgrade-job-g4qpn 0/1 Completed 0 3m14s datahub-elasticsearch-setup-job-kj95r 0/1 Completed 0 6m9s datahub-kafka-setup-job-kwkqs 0/1 Completed 0 6m datahub-mysql-setup-job-64grv 0/1 Completed 0 4m29s
- デプロイ完了後、Frontendアクセス用とメタデータ取り込み(GMS)用2つのALBが作成されています。
- ※Helmによるデプロイの影響で、同時にCLBも2つ作成されていますが、ALBがあるため不要なので削除して大丈夫です。
-
以下コマンドで確認して「ADDESS」列のFrontend用ALBのエンドポイントを控える
% kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE datahub-datahub-frontend <none> * k8s-default-datahubd-xxxxxxxx.ap-northeast-1.elb.amazonaws.com 80 32h datahub-datahub-gms <none> * k8s-default-datahubd-xxxxxxxx.ap-northeast-1.elb.amazonaws.com 80 32h
- Frontend用ALBにブラウザでアクセスしてみると以下のようにログイン画面が表示されます。
- デフォルトユーザとパスワード(datahub:datahub)でログインできるはずです。
【おまけ】ALBのアクセスポイントをhttpsにする場合
ALBのアクセスをhttpsで構成したい場合は、Route53やACMなどを利用してパブリックDNSと証明書を用意した上でvalues.yamlのingress部分を以下のように構成してください。
ingress: enabled: true annotations: kubernetes.io/ingress.class: alb alb.ingress.kubernetes.io/scheme: internet-facing alb.ingress.kubernetes.io/target-type: instance alb.ingress.kubernetes.io/certificate-arn: <<certificate-arn>> # ACMで作成した証明書のArnを入力 alb.ingress.kubernetes.io/inbound-cidrs: 0.0.0.0/0 alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]' alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}' hosts: - host: <<host-name>> # パブリックDNS名を入力 redirectPaths: - path: /* name: ssl-redirect port: use-annotation paths: - /*
まとめ
今回のようにストレージ部分をAWSサービスに外出しする構成にできれば、データベース部分のバックアップがやりやすくなる他、アップデート作業にも対応しやすくなります。最新バージョンを保てればセキュリティ面でも安心できますね。
また、AWS Opensearchに移行できたことで、クラスター内のElasticsearchの日本語プラグインの対応をしなくて済むのも大きなメリットです。
しかしながら、日本語の検索機能をより向上させるには別途インデックスのカスタマイズは必要になるかと思います。