TECH PLAY

電通総研

電通総研 の技術ブログ

814

メリークリスマス✨ISID 金融ソリューション事業部 市場系 イノベーション 部の寺山です! 新型コロナ禍の日々ですが、クリスマスイブはちょうど金曜日でしたし、皆さま楽しく過ごせましたかね?? さて、本記事は 電通国際情報サービス Advent Calendar 2021 の最終日のポストとなります。前回は、浦本さんの「フロントエンド依存ライブラリのバージョンアップ戦略」でした。 いやぁ、Advent Calendarに参加するぜ!ってエントリーしてしばらく経ってから、あれ??トリじゃん!?って気づきましたよ。。笑 しかしながらそんなことは気にせず、私が興味を持ったことについて投稿させていただきたいと思います! はじめに EKSにホストしたマイクロサービスの構成 コンテナー型マイクロサービスの可視化 K8sおよびエコシステムのバージョン AMPのアドオン (省略) AMGのアドオン 可視化 まとめと今後の展望 おまけ:VSCodeのKubernetes拡張機能 参考文献 はじめに インフラ・ クラウド というエンジニアリング領域を担当している私は、 Kubernetes (以下 K8s )を以下のように認識しております。 インフラスト ラク チャをソフトウェアでハイレベルに抽象化して制御する強力なテク ノロ ジー コミュニティが活発であり デベロッパ ーやインフラ管理者を支援するツールが日々進化している 2点目に関連し、コンテナレベルでの監視や可視化をサポートするエコシステムである、 Prometheus と Grafana がそれぞれ AWS のマネージドサービスとしてGAされました。 Amazon Managed Service for Prometheus(以下、AMP) Amazon Managed Service for Prometheus Is Now Generally Available with Alert Manager and Ruler Amazon Managed Grafana(以下、 AMG ) Amazon Managed Grafana Is Now Generally Available with Many New Features GAされてから4カ月ほど経過しますが、EKS+AMP+ AMG という記事は以外と私の目に入っておりません。そこで、AMPと AMG を用いて Amazon EKSにホストしたマイクロサービスの可視化をやってみた!というのが、本記事の主旨となります。 K8s に対してはキャッチアップ中というスキルセット&ブログを書くのが初めてということもあり、誤りや至らぬ点もあるかと存じますが、ご指摘等はコメントいただけるとありがたいです(>人<;) EKSにホストしたマイクロサービスの構成 本題へ入る前に、モニタリング対象のマイクロサービスのインフラ構成をご説明します。 コンポーネント は全て AWS のサービスで構成しています。リージョンはアジアパシフィック(東京)です。 マイクロサービスアプリケーションは、バックエンドとして、認証/認可機能、共通機能、これらの機能を用いて ビジネスロジック を実装し機能として提供するコンテナーをそれぞれのPod内で実行しています。コンテナーのイメージは Amazon ECR(以下ECR)から取得します。 コンテナランタイムの実行環境はサーバレスコンピューティングサービスである AWS Fargate(以下Fargate)を利用しています。 ステートフルな情報は永続化層としてプロビジョニングした Amazon Aurora (以下Aurora)より取得・保管します。 EKS クラスタ ーにデプロイした AWS Load Balancer Controllerにより、 ACM 証明書を適用して SSL / TLS 接続をオフロードするApplication Load Balancer(以下ALB)をプロビジョニングしています。 ALBは、フロントエンド(クライアント)からの REST API リク エス ト トラフィック を ビジネスロジック Serviceにパスベースでルーティングします。 これらのコンテナー関連リソースを制御する K8s マスターが AWS マネージドとして作成/運用される Amazon EKS(以下EKS)で クラスタ ーを構成しています。 エンドユーザーはSPAを取得して、 REST API でバックエンドとリソースのやり取りをするというマイクロサービスにおける代表的な アーキテクチャ です。SPAはS3に格納し、CloudFrontにより HTTPS で配信します。ただし、オリジンはS3 バケット のみであり、バックエンド(ALB)は指定していません。 全体的に、(プライベートサブネットとCloudFrontの構成をサボっているところ以外は) AWS での典型的な アーキテクチャ だと思っています。ちなみに、フロントエンド・バックエンドのアプリケーションを開発したのは私ではありません。ありがとう、チームのみんな〜 コンテナー型マイクロサービスの可視化 PodのログはFluent Bitを利用すれば比較的簡単( Fargate ログ記録 参照)にCloudWatch Logsで収集可能となります。 Fluent Bitは一部のメトリクスもログストリームに含めることをサポートしています 1 。 そのため、CloudWatchでメトリクスフィルターを頑張って設定すれば、可視化もできそうです。 ただし、せっかく K8s 使用しているのですから、有用(というか人気)なエコシステムは活用したいですよね?!というのが本件のモチベーションとなります。 K8s およびエコシステムのバージョン EKS: Kubernetes 1.21、eks.3 Prometheus:14.11.1 Grafana:8.3 AMPのアドオン AMPユーザーガイドの Getting started に従って、前述のEKS クラスタ ーにPrometheusをアドオンします。ガイドと違いがある点や特にコメントしたい内容以外は単に作業内容を記述するのみにとどめて説明します。 AMP ワークスペース の作成 bash $ aws amp create-workspace --alias tech-research-workspace Prometheus 用の Namespace と ServieAccount 作成 ``` bash $ kubectl create namespace prometheus namespace/prometheus created $ kubectl create serviceaccount prometheus -n prometheus serviceaccount/prometheus created ``` サービスロールを作成して Service Account に関連付け Set up IAM roles for service accounts で案内されている スクリプト を作成して実行します。 ガイドの スクリプト では eksctl を利用しており、実際にEKSを構築や運用する際には有用な CLI ツールかと思いますが、 K8s の理解を深めるために kubectl で書き直しました。 と言っても、差分は以下のみなのですが。。。 ガイド内の スクリプト bash eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --approve 私が書き直した スクリプト bash kubectl annotate serviceaccount -n ${SERVICE_ACCOUNT_NAMESPACE} ${SERVICE_ACCOUNT_NAMESPACE} \ eks.amazonaws.com/role-arn=${SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE_ARN} 実行します。 bash $ ./createIRSA-AMPIngest.sh arn:aws:iam::XXXXXXXXXXXX:role/amp-iamproxy-ingest-role serviceaccount/prometheus annotated EKSノードグループの作成 ここで、Prometheusの アーキテクチャ を確認すると、Prometeheus ServerのTSDBの保存先にストレージが要求されています。 ※下図は Prometeheus Overview - Architecture より抜粋したものです。 EKS クラスタ ーはデフォルトで Amazon EBSのStorage Classがデプロイ済みの環境となりますが、私が構築した クラスタ ーはFargateプロファイルのみを作成していたので、EBSをストレージとするPersistent Volumeを利用できません。 Amazon EFSのStorage Classを追加してFargate上で実行するか、EC2ノードでEBSを利用するか、なのですが本件では後者としました。 EC2ノードを利用する場合はEKS クラスタ ーにノードグループを追加します。ノードグループはマネージドノードグループとしました。 作成方法は AWS ユーザーガイドの ステップ 4 ノードを作成する の Managed nodes – Linux をご参照ください。 Prometheusのインストール Helm チャー トリポジ トリを追加 互換性があるということで、AMP用のHelm リポジトリ があるのではなく、コミュニティの リポジトリ を追加していますね。マネージドサービスでありながらコミュニティのアップデートを利用できるのは嬉しいですね! ``` bash $ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts $ helm repo add kube-state-metrics https://kubernetes.github.io/kube-state-metrics $ helm repo list NAME URL prometheus-community https://prometheus-community.github.io/helm-charts kube-state-metrics https://kubernetes.github.io/kube-state-metrics $ helm repo update Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "kube-state-metrics" chart repository ...Successfully got an update from the "prometheus-community" chart repository Update Complete. ⎈Happy Helming!⎈ ``` Helmチャートに渡すパラメーターファイルの作成 リモート書き込みエンドポイント( remoteWrite )は、AMPコンソールの ワークスペース の概要欄や、 aws amp describe-workspace --workspace-id <ワークスペースID> より確認可能です。 amp-ingest-values. yaml ```yml serviceAccounts: server: name: "amp-iamproxy-ingest-service-account" annotations: eks.amazonaws.com/role-arn: "arn: aws :iam::XXXXXXXXXXXX:role/amp-iamproxy-ingest-role" server: remoteWrite: - url: https://aps-workspaces.ap-northeast-1.amazonaws.com/workspaces/ws-XXXXXXXX-XXXXXXXXXXXXXXXX/api/v1/remote_write sigv4: region: ap-northeast-1 queue_config: max_samples_per_send: 1000 max_shards: 200 capacity: 2500 ``` Prometheus Serverのインストール ``` bash $ helm install tech-research-prometheus-chart prometheus-community/prometheus -n prometheus -f ./amp-ingest-values. yaml NAME: tech-research-prometheus-chart LAST DEPLOYED: Tue Dec 14 22:31:55 2021 NAMESPACE: prometheus STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster: tech-research-prometheus-chart-server.prometheus. svc .cluster.local (省略) ``` リソースの作成を確認 ``` bash $ kubectl get all -n prometheus NAME READY STATUS RESTARTS AGE pod/tech-research-prometheus-chart-alertmanager-646ff55c84-fmzfp 2/2 Running 0 2m19s pod/tech-research-prometheus-chart-kube-state-metrics-869b89746zhd5 1/1 Running 0 2m19s pod/tech-research-prometheus-chart-node-exporter-pft8p 1/1 Running 0 2m19s pod/tech-research-prometheus-chart-pushgateway-6cb64c488f-wrvdx 1/1 Running 0 2m19s pod/tech-research-prometheus-chart-server-76f7b57784-k8xj6 2/2 Running 0 2m19s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/tech-research-prometheus-chart-alertmanager ClusterIP 192.168.0.150 80/ TCP 2m19s service/tech-research-prometheus-chart-kube-state-metrics ClusterIP 192.168.0.47 8080/ TCP 2m19s service/tech-research-prometheus-chart-node-exporter ClusterIP None 9100/ TCP 2m19s service/tech-research-prometheus-chart-pushgateway ClusterIP 192.168.0.221 9091/ TCP 2m19s service/tech-research-prometheus-chart-server ClusterIP 192.168.0.244 80/ TCP 2m19s NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/tech-research-prometheus-chart-node-exporter 1 1 1 1 1 2m19s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/tech-research-prometheus-chart-alertmanager 1/1 1 1 2m20s deployment.apps/tech-research-prometheus-chart-kube-state-metrics 1/1 1 1 2m20s deployment.apps/tech-research-prometheus-chart-pushgateway 1/1 1 1 2m20s deployment.apps/tech-research-prometheus-chart-server 1/1 1 1 2m20s NAME DESIRED CURRENT READY AGE replicaset.apps/tech-research-prometheus-chart-alertmanager-646ff55c84 1 1 1 2m20s replicaset.apps/tech-research-prometheus-chart-kube-state-metrics-869b8974ff 1 1 1 2m20s replicaset.apps/tech-research-prometheus-chart-pushgateway-6cb64c488f 1 1 1 2m20s replicaset.apps/tech-research-prometheus-chart-server-76f7b57784 1 1 1 2m20s ``` AMG のアドオン こちらも AMG の Getting started に従って進めていきましょう! Grafana ワークスペース 作成 Create your first workspace に従って作成します。 Grafana ワークスペース ・コンソールには、SSOで認証を行いアクセスする必要があるのですが、本件ではSSOの方式に SAML を選択しました。 Amazon Grafana > Workspaces より ワークスペースを作成 を押下 名前と説明 ワークスペース 名 tech-research-grafana-workspace 認証アクセス Security Assertion Markup Language ( SAML ) アクセス許可タイプ サービスマネージド IAM アクセス許可アクセス設定 現在のアカウント データソース Amazon Managed Service for Prometheus ワークスペースを作成 を押下 ステータスが アクティブ となることを確認する ワークスペース の作成は一旦終了しますが、 SAML の設定を完了させるにあたり、 SAML IdPの構築が必要となります。 SAML IdPの構築 本件ではIDaaSである Okta を利用しました。 AWS にて動作確認済みのIdPは Connecting to your identity provider に一覧化されています。こちらに記載のないIdPは、Grafanaに必要な SAML アサーション 属性を マッピング できるかの検証が必要ですね。 Oktaコンソール > 管理者 より管理者ページにサインインする その際、多要素認証の設定を求められるので、Okta Verifyを利用しました。 Directory > Profile Editor と遷移 Users にてOkta用のプロファイルである User(default) を選択 Attributesの Add Attribute を押下 Add Attribute Display name Role Variable name Role Save を押下 Role 属性が追加されていること Directory > People と遷移 自分のアカウントを選択 Profile タブにてAttributesの Edit を押下 前述の手順で追加した Role 属性に ADMIN を設定 Save を押下 Applictions > Applications と遷移 Browse App Integration Catalog にて「 Amazon Managed Grafana」を Search して選択 アプリケーションの詳細画面にて Add を押下 General Settings デフォルトのままで Done を押下 Amazon Managed Grafana アプリケーションの Sing On タブに遷移 Settings において Edit を押下 Attributes (Optional) Attribute Statements (optional) Name:Role Value :user.Role Advanced Sign -on Settings Name Space:Grafana ワークスペース ID ※Grafana ワークスペース URLに含まれる ワークスペース IDを抜き出して設定します。 Region:ap-northeast-1 Save を押下 ユーザーの アサイ ン Amazon Managed Grafana アプリケーションの Assignments タブに遷移 Assign プルダウンメニューより Assign to People を選択 作成済みのアカウント(今回は私のOktaアカウント)を Assign Done を押下 AMG の SAML 設定 OktaをIdPとした SAML 認証に必要な設定を行います。 Amazon Grafana > Workspaces > 作成したワークスペース を選択 認証 タブの Security Assertion Markup Language (SAML) の セットアップを完了にする を押下 Security Assertion Markup Language ( SAML ) ステップ 2: メタデータ をインポートする インポート方法 URL メタデータ URL メタデータ URLは、Oktaアプリケーションの Sign On タブの Identity Provider metadata より確認可能です。 ステップ 3: アサーション 属性を マッピング する admin の アサーション 属性ロール アサーション 属性ロール:Role 管理者ロールの値:ADMIN ※Okta上で設定した属性と、アカウント(People)で入力した値を指定します。 追加設定 - オプション アサーション 属性名:displayName アサーション 属性 E メール:mail アサーション 属性のログイン:mail SAML設定を保存 を押下 Grafana ワークスペース の SAML 設定が 有効 となることを確認する Grafana workspace consoleにアクセスする SSOが設定できたかの動作確認も兼ねて、GrafaraのUI( ワークスペース ・コンソール)にアクセスします。 Amazon Grafana > Workspaces より作成した ワークスペース を選択し、 Grafana ワークスペース URL に表示されているリンクを開く Sign in with SAML を押下 Oktaと未認証状態の場合は、Oktaにサインインする Grafana ワークスペース ・コンソールが表示されることを確認する。 可視化 Grafana上でのメトリクスの可視化が行える状態になったので、実際に ダッシュ ボードを作成してみます。 AMPのデータソースを追加 Grafana ワークスペースコンソール> 左部メニューのAWSアイコン > AWS Data Sources を選択 AWS Data Sources Data sourcesタブ Service Amazon Managed Service for Prometheus Regions Asia Pacific (Tokyo) AMPの ワークスペース を選択して Add data source Provisioned data sourcesにAMPの ワークスペース が追加されていることを確認する。 ダッシュ ボードの追加 Grafana.com に公開されている ダッシュ ボードをインポートして、EKS クラスタ ーの可視化を行います。 Grafanaワークスペース・コンソール > Dashboards > Manage を選択 Import を押下 Kubernetes cluster monitoring (via Prometheus) の ダッシュ ボード ID をコピーして、 Import via grafana.com に入力し、 Load を押下 Options の Prometheus にて、AMP の ワークスペース を選択し、 Import を押下 無事、EKS クラスタ ーとPodのメトリクスを可視化することができました! CPU使用率 メモリ使用率 まとめと今後の展望 「やってみた」感想は以下のとおりです。 K8s はキャッチアップ中、PrometheusとGrafanaを触るのが初めての私でも、 クラスタ ーやPodのメトリクス収集と可視化を行うことができました。 Contributorの貢献、情報量の多さという OSS のメリットと、それを AWS がマネージドサービスとしてナレッジの集約・導入ハードルを下げて提供しているというメリットの両方を体感しました。 これはEKSにも当てはまると考えております。 今後の展望としては以下を検討しております。 各エコシステムの アーキテクチャ や仕様、制約の理解 ダッシュ ボードのカスタマイズやCloudWatchとの棲み分け整理 監視・可視化という運用機能ならなおさらFargateで実行したい 特に3点目は、調査できたら改めて記事にしたいと思っております! (実はFargateでトライして、苦戦したのでEC2で妥協したのはナイショ♡) おまけ: VSCode の Kubernetes 拡張機能 毎日 VSCode のお世話になっております。 VSCode を使い始める前の日々には戻れないです。 ご存じの方も多いかと思いますが、 VSCode には Microsoft 社製の Kubernetes 拡張機能 があります。 この 拡張機能 は YAML スキーマ で マニフェスト の入力補助を行う目的で導入していたのですが、左部パネルの K8s アイコンより、色々できることに気づきました。 K8s クラスタ ーやNamespaceの切り替え、各リソースの情報の取得もできます。 便利〜!という蛇足でした。 最後までご覧いただきありがとうございました。 この記事がお一人でも多く方の参考になる、または、弊社に少しでも興味も持った方がいらっしゃいましたら幸甚です。 それでは、良いお年を〜! 参考文献 AWS の公式ガイド Amazon Managed Service for Prometheus Amazon Managed Grafana AWS ワークショップ Monitoring using Amazon Managed for Prometheus / Grafana 執筆: 寺山 輝 (@terayama.akira) 、レビュー: @sato.taichi ( Shodo で執筆されました ) Fluent Bit - Metrics Tutorial https://docs.fluentbit.io/manual/pipeline/outputs/cloudwatch#metrics-tutorial ↩
アバター
メリークリスマス✨ISID 金融ソリューション事業部 市場系 イノベーション 部の寺山です! 新型コロナ禍の日々ですが、クリスマスイブはちょうど金曜日でしたし、皆さま楽しく過ごせましたかね?? さて、本記事は 電通国際情報サービス Advent Calendar 2021 の最終日のポストとなります。前回は、浦本さんの「フロントエンド依存ライブラリのバージョンアップ戦略」でした。 いやぁ、Advent Calendarに参加するぜ!ってエントリーしてしばらく経ってから、あれ??トリじゃん!?って気づきましたよ。。笑 しかしながらそんなことは気にせず、私が興味を持ったことについて投稿させていただきたいと思います! はじめに EKSにホストしたマイクロサービスの構成 コンテナー型マイクロサービスの可視化 K8sおよびエコシステムのバージョン AMPのアドオン (省略) AMGのアドオン 可視化 まとめと今後の展望 おまけ:VSCodeのKubernetes拡張機能 参考文献 はじめに インフラ・ クラウド というエンジニアリング領域を担当している私は、 Kubernetes (以下 K8s )を以下のように認識しております。 インフラスト ラク チャをソフトウェアでハイレベルに抽象化して制御する強力なテク ノロ ジー コミュニティが活発であり デベロッパ ーやインフラ管理者を支援するツールが日々進化している 2点目に関連し、コンテナレベルでの監視や可視化をサポートするエコシステムである、 Prometheus と Grafana がそれぞれ AWS のマネージドサービスとしてGAされました。 Amazon Managed Service for Prometheus(以下、AMP) Amazon Managed Service for Prometheus Is Now Generally Available with Alert Manager and Ruler Amazon Managed Grafana(以下、 AMG ) Amazon Managed Grafana Is Now Generally Available with Many New Features GAされてから4カ月ほど経過しますが、EKS+AMP+ AMG という記事は以外と私の目に入っておりません。そこで、AMPと AMG を用いて Amazon EKSにホストしたマイクロサービスの可視化をやってみた!というのが、本記事の主旨となります。 K8s に対してはキャッチアップ中というスキルセット&ブログを書くのが初めてということもあり、誤りや至らぬ点もあるかと存じますが、ご指摘等はコメントいただけるとありがたいです(>人<;) EKSにホストしたマイクロサービスの構成 本題へ入る前に、モニタリング対象のマイクロサービスのインフラ構成をご説明します。 コンポーネント は全て AWS のサービスで構成しています。リージョンはアジアパシフィック(東京)です。 マイクロサービスアプリケーションは、バックエンドとして、認証/認可機能、共通機能、これらの機能を用いて ビジネスロジック を実装し機能として提供するコンテナーをそれぞれのPod内で実行しています。コンテナーのイメージは Amazon ECR(以下ECR)から取得します。 コンテナランタイムの実行環境はサーバレスコンピューティングサービスである AWS Fargate(以下Fargate)を利用しています。 ステートフルな情報は永続化層としてプロビジョニングした Amazon Aurora (以下Aurora)より取得・保管します。 EKS クラスタ ーにデプロイした AWS Load Balancer Controllerにより、 ACM 証明書を適用して SSL / TLS 接続をオフロードするApplication Load Balancer(以下ALB)をプロビジョニングしています。 ALBは、フロントエンド(クライアント)からの REST API リク エス ト トラフィック を ビジネスロジック Serviceにパスベースでルーティングします。 これらのコンテナー関連リソースを制御する K8s マスターが AWS マネージドとして作成/運用される Amazon EKS(以下EKS)で クラスタ ーを構成しています。 エンドユーザーはSPAを取得して、 REST API でバックエンドとリソースのやり取りをするというマイクロサービスにおける代表的な アーキテクチャ です。SPAはS3に格納し、CloudFrontにより HTTPS で配信します。ただし、オリジンはS3 バケット のみであり、バックエンド(ALB)は指定していません。 全体的に、(プライベートサブネットとCloudFrontの構成をサボっているところ以外は) AWS での典型的な アーキテクチャ だと思っています。ちなみに、フロントエンド・バックエンドのアプリケーションを開発したのは私ではありません。ありがとう、チームのみんな〜 コンテナー型マイクロサービスの可視化 PodのログはFluent Bitを利用すれば比較的簡単( Fargate ログ記録 参照)にCloudWatch Logsで収集可能となります。 Fluent Bitは一部のメトリクスもログストリームに含めることをサポートしています 1 。 そのため、CloudWatchでメトリクスフィルターを頑張って設定すれば、可視化もできそうです。 ただし、せっかく K8s 使用しているのですから、有用(というか人気)なエコシステムは活用したいですよね?!というのが本件のモチベーションとなります。 K8s およびエコシステムのバージョン EKS: Kubernetes 1.21、eks.3 Prometheus:14.11.1 Grafana:8.3 AMPのアドオン AMPユーザーガイドの Getting started に従って、前述のEKS クラスタ ーにPrometheusをアドオンします。ガイドと違いがある点や特にコメントしたい内容以外は単に作業内容を記述するのみにとどめて説明します。 AMP ワークスペース の作成 bash $ aws amp create-workspace --alias tech-research-workspace Prometheus 用の Namespace と ServieAccount 作成 ``` bash $ kubectl create namespace prometheus namespace/prometheus created $ kubectl create serviceaccount prometheus -n prometheus serviceaccount/prometheus created ``` サービスロールを作成して Service Account に関連付け Set up IAM roles for service accounts で案内されている スクリプト を作成して実行します。 ガイドの スクリプト では eksctl を利用しており、実際にEKSを構築や運用する際には有用な CLI ツールかと思いますが、 K8s の理解を深めるために kubectl で書き直しました。 と言っても、差分は以下のみなのですが。。。 ガイド内の スクリプト bash eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --approve 私が書き直した スクリプト bash kubectl annotate serviceaccount -n ${SERVICE_ACCOUNT_NAMESPACE} ${SERVICE_ACCOUNT_NAMESPACE} \ eks.amazonaws.com/role-arn=${SERVICE_ACCOUNT_IAM_AMP_INGEST_ROLE_ARN} 実行します。 bash $ ./createIRSA-AMPIngest.sh arn:aws:iam::XXXXXXXXXXXX:role/amp-iamproxy-ingest-role serviceaccount/prometheus annotated EKSノードグループの作成 ここで、Prometheusの アーキテクチャ を確認すると、Prometeheus ServerのTSDBの保存先にストレージが要求されています。 ※下図は Prometeheus Overview - Architecture より抜粋したものです。 EKS クラスタ ーはデフォルトで Amazon EBSのStorage Classがデプロイ済みの環境となりますが、私が構築した クラスタ ーはFargateプロファイルのみを作成していたので、EBSをストレージとするPersistent Volumeを利用できません。 Amazon EFSのStorage Classを追加してFargate上で実行するか、EC2ノードでEBSを利用するか、なのですが本件では後者としました。 EC2ノードを利用する場合はEKS クラスタ ーにノードグループを追加します。ノードグループはマネージドノードグループとしました。 作成方法は AWS ユーザーガイドの ステップ 4 ノードを作成する の Managed nodes – Linux をご参照ください。 Prometheusのインストール Helm チャー トリポジ トリを追加 互換性があるということで、AMP用のHelm リポジトリ があるのではなく、コミュニティの リポジトリ を追加していますね。マネージドサービスでありながらコミュニティのアップデートを利用できるのは嬉しいですね! ``` bash $ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts $ helm repo add kube-state-metrics https://kubernetes.github.io/kube-state-metrics $ helm repo list NAME URL prometheus-community https://prometheus-community.github.io/helm-charts kube-state-metrics https://kubernetes.github.io/kube-state-metrics $ helm repo update Hang tight while we grab the latest from your chart repositories... ...Successfully got an update from the "kube-state-metrics" chart repository ...Successfully got an update from the "prometheus-community" chart repository Update Complete. ⎈Happy Helming!⎈ ``` Helmチャートに渡すパラメーターファイルの作成 リモート書き込みエンドポイント( remoteWrite )は、AMPコンソールの ワークスペース の概要欄や、 aws amp describe-workspace --workspace-id <ワークスペースID> より確認可能です。 amp-ingest-values. yaml ```yml serviceAccounts: server: name: "amp-iamproxy-ingest-service-account" annotations: eks.amazonaws.com/role-arn: "arn: aws :iam::XXXXXXXXXXXX:role/amp-iamproxy-ingest-role" server: remoteWrite: - url: https://aps-workspaces.ap-northeast-1.amazonaws.com/workspaces/ws-XXXXXXXX-XXXXXXXXXXXXXXXX/api/v1/remote_write sigv4: region: ap-northeast-1 queue_config: max_samples_per_send: 1000 max_shards: 200 capacity: 2500 ``` Prometheus Serverのインストール ``` bash $ helm install tech-research-prometheus-chart prometheus-community/prometheus -n prometheus -f ./amp-ingest-values. yaml NAME: tech-research-prometheus-chart LAST DEPLOYED: Tue Dec 14 22:31:55 2021 NAMESPACE: prometheus STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: The Prometheus server can be accessed via port 80 on the following DNS name from within your cluster: tech-research-prometheus-chart-server.prometheus. svc .cluster.local (省略) ``` リソースの作成を確認 ``` bash $ kubectl get all -n prometheus NAME READY STATUS RESTARTS AGE pod/tech-research-prometheus-chart-alertmanager-646ff55c84-fmzfp 2/2 Running 0 2m19s pod/tech-research-prometheus-chart-kube-state-metrics-869b89746zhd5 1/1 Running 0 2m19s pod/tech-research-prometheus-chart-node-exporter-pft8p 1/1 Running 0 2m19s pod/tech-research-prometheus-chart-pushgateway-6cb64c488f-wrvdx 1/1 Running 0 2m19s pod/tech-research-prometheus-chart-server-76f7b57784-k8xj6 2/2 Running 0 2m19s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/tech-research-prometheus-chart-alertmanager ClusterIP 192.168.0.150 80/ TCP 2m19s service/tech-research-prometheus-chart-kube-state-metrics ClusterIP 192.168.0.47 8080/ TCP 2m19s service/tech-research-prometheus-chart-node-exporter ClusterIP None 9100/ TCP 2m19s service/tech-research-prometheus-chart-pushgateway ClusterIP 192.168.0.221 9091/ TCP 2m19s service/tech-research-prometheus-chart-server ClusterIP 192.168.0.244 80/ TCP 2m19s NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE daemonset.apps/tech-research-prometheus-chart-node-exporter 1 1 1 1 1 2m19s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/tech-research-prometheus-chart-alertmanager 1/1 1 1 2m20s deployment.apps/tech-research-prometheus-chart-kube-state-metrics 1/1 1 1 2m20s deployment.apps/tech-research-prometheus-chart-pushgateway 1/1 1 1 2m20s deployment.apps/tech-research-prometheus-chart-server 1/1 1 1 2m20s NAME DESIRED CURRENT READY AGE replicaset.apps/tech-research-prometheus-chart-alertmanager-646ff55c84 1 1 1 2m20s replicaset.apps/tech-research-prometheus-chart-kube-state-metrics-869b8974ff 1 1 1 2m20s replicaset.apps/tech-research-prometheus-chart-pushgateway-6cb64c488f 1 1 1 2m20s replicaset.apps/tech-research-prometheus-chart-server-76f7b57784 1 1 1 2m20s ``` AMG のアドオン こちらも AMG の Getting started に従って進めていきましょう! Grafana ワークスペース 作成 Create your first workspace に従って作成します。 Grafana ワークスペース ・コンソールには、SSOで認証を行いアクセスする必要があるのですが、本件ではSSOの方式に SAML を選択しました。 Amazon Grafana > Workspaces より ワークスペースを作成 を押下 名前と説明 ワークスペース 名 tech-research-grafana-workspace 認証アクセス Security Assertion Markup Language ( SAML ) アクセス許可タイプ サービスマネージド IAM アクセス許可アクセス設定 現在のアカウント データソース Amazon Managed Service for Prometheus ワークスペースを作成 を押下 ステータスが アクティブ となることを確認する ワークスペース の作成は一旦終了しますが、 SAML の設定を完了させるにあたり、 SAML IdPの構築が必要となります。 SAML IdPの構築 本件ではIDaaSである Okta を利用しました。 AWS にて動作確認済みのIdPは Connecting to your identity provider に一覧化されています。こちらに記載のないIdPは、Grafanaに必要な SAML アサーション 属性を マッピング できるかの検証が必要ですね。 Oktaコンソール > 管理者 より管理者ページにサインインする その際、多要素認証の設定を求められるので、Okta Verifyを利用しました。 Directory > Profile Editor と遷移 Users にてOkta用のプロファイルである User(default) を選択 Attributesの Add Attribute を押下 Add Attribute Display name Role Variable name Role Save を押下 Role 属性が追加されていること Directory > People と遷移 自分のアカウントを選択 Profile タブにてAttributesの Edit を押下 前述の手順で追加した Role 属性に ADMIN を設定 Save を押下 Applictions > Applications と遷移 Browse App Integration Catalog にて「 Amazon Managed Grafana」を Search して選択 アプリケーションの詳細画面にて Add を押下 General Settings デフォルトのままで Done を押下 Amazon Managed Grafana アプリケーションの Sing On タブに遷移 Settings において Edit を押下 Attributes (Optional) Attribute Statements (optional) Name:Role Value :user.Role Advanced Sign -on Settings Name Space:Grafana ワークスペース ID ※Grafana ワークスペース URLに含まれる ワークスペース IDを抜き出して設定します。 Region:ap-northeast-1 Save を押下 ユーザーの アサイ ン Amazon Managed Grafana アプリケーションの Assignments タブに遷移 Assign プルダウンメニューより Assign to People を選択 作成済みのアカウント(今回は私のOktaアカウント)を Assign Done を押下 AMG の SAML 設定 OktaをIdPとした SAML 認証に必要な設定を行います。 Amazon Grafana > Workspaces > 作成したワークスペース を選択 認証 タブの Security Assertion Markup Language (SAML) の セットアップを完了にする を押下 Security Assertion Markup Language ( SAML ) ステップ 2: メタデータ をインポートする インポート方法 URL メタデータ URL メタデータ URLは、Oktaアプリケーションの Sign On タブの Identity Provider metadata より確認可能です。 ステップ 3: アサーション 属性を マッピング する admin の アサーション 属性ロール アサーション 属性ロール:Role 管理者ロールの値:ADMIN ※Okta上で設定した属性と、アカウント(People)で入力した値を指定します。 追加設定 - オプション アサーション 属性名:displayName アサーション 属性 E メール:mail アサーション 属性のログイン:mail SAML設定を保存 を押下 Grafana ワークスペース の SAML 設定が 有効 となることを確認する Grafana workspace consoleにアクセスする SSOが設定できたかの動作確認も兼ねて、GrafaraのUI( ワークスペース ・コンソール)にアクセスします。 Amazon Grafana > Workspaces より作成した ワークスペース を選択し、 Grafana ワークスペース URL に表示されているリンクを開く Sign in with SAML を押下 Oktaと未認証状態の場合は、Oktaにサインインする Grafana ワークスペース ・コンソールが表示されることを確認する。 可視化 Grafana上でのメトリクスの可視化が行える状態になったので、実際に ダッシュ ボードを作成してみます。 AMPのデータソースを追加 Grafana ワークスペースコンソール> 左部メニューのAWSアイコン > AWS Data Sources を選択 AWS Data Sources Data sourcesタブ Service Amazon Managed Service for Prometheus Regions Asia Pacific (Tokyo) AMPの ワークスペース を選択して Add data source Provisioned data sourcesにAMPの ワークスペース が追加されていることを確認する。 ダッシュ ボードの追加 Grafana.com に公開されている ダッシュ ボードをインポートして、EKS クラスタ ーの可視化を行います。 Grafanaワークスペース・コンソール > Dashboards > Manage を選択 Import を押下 Kubernetes cluster monitoring (via Prometheus) の ダッシュ ボード ID をコピーして、 Import via grafana.com に入力し、 Load を押下 Options の Prometheus にて、AMP の ワークスペース を選択し、 Import を押下 無事、EKS クラスタ ーとPodのメトリクスを可視化することができました! CPU使用率 メモリ使用率 まとめと今後の展望 「やってみた」感想は以下のとおりです。 K8s はキャッチアップ中、PrometheusとGrafanaを触るのが初めての私でも、 クラスタ ーやPodのメトリクス収集と可視化を行うことができました。 Contributorの貢献、情報量の多さという OSS のメリットと、それを AWS がマネージドサービスとしてナレッジの集約・導入ハードルを下げて提供しているというメリットの両方を体感しました。 これはEKSにも当てはまると考えております。 今後の展望としては以下を検討しております。 各エコシステムの アーキテクチャ や仕様、制約の理解 ダッシュ ボードのカスタマイズやCloudWatchとの棲み分け整理 監視・可視化という運用機能ならなおさらFargateで実行したい 特に3点目は、調査できたら改めて記事にしたいと思っております! (実はFargateでトライして、苦戦したのでEC2で妥協したのはナイショ♡) おまけ: VSCode の Kubernetes 拡張機能 毎日 VSCode のお世話になっております。 VSCode を使い始める前の日々には戻れないです。 ご存じの方も多いかと思いますが、 VSCode には Microsoft 社製の Kubernetes 拡張機能 があります。 この 拡張機能 は YAML スキーマ で マニフェスト の入力補助を行う目的で導入していたのですが、左部パネルの K8s アイコンより、色々できることに気づきました。 K8s クラスタ ーやNamespaceの切り替え、各リソースの情報の取得もできます。 便利〜!という蛇足でした。 最後までご覧いただきありがとうございました。 この記事がお一人でも多く方の参考になる、または、弊社に少しでも興味も持った方がいらっしゃいましたら幸甚です。 それでは、良いお年を〜! 参考文献 AWS の公式ガイド Amazon Managed Service for Prometheus Amazon Managed Grafana AWS ワークショップ Monitoring using Amazon Managed for Prometheus / Grafana 執筆: 寺山 輝 (@terayama.akira) 、レビュー: @sato.taichi ( Shodo で執筆されました ) Fluent Bit - Metrics Tutorial https://docs.fluentbit.io/manual/pipeline/outputs/cloudwatch#metrics-tutorial ↩
アバター
こんにちは、ISID デジタルビジネス推進室の浦本です。好きな食べ物は生八ツ橋です。 本記事は 電通国際情報サービス Advent Calendar 2021 の24日目の記事です。 はじめに 皆さんは、npmでインストールした依存パッケージを定期的に更新されていますか? 「面倒だから更新していない。。。」 「フロントエンドはそのままでも大丈夫でしょ?」 と思って放置されている方もいらっしゃるかもしれません。 しかーし!! 更新せずに放置していると、古い依存パッケージの 脆弱性 を突かれてしまったり、アプリのコード体系が古くなり開発に支障が出たりします。 そこで本日は、面倒になりがちな依存パッケージのバージョン更新を円滑に行うための戦略を書きたいと思います。 【用語について】 本記事では、依存パッケージを利用する開発プロジェクトを単に「アプリ」と呼称します。 戦略 以下より詳細をご説明します。 dependencies と devDependencies の違いを意識する お悩み:「色々とパッケージを追加したけれど、どれが重要か分からなくなってきた。」 package. json 内の dependencies にはプロダクション・コードに影響する依存パッケージ名を列挙し、 devDependencies にはプロダクション・コードに影響しない依存パッケージ名(ビルドツールなど)を列挙します。 【例】 { " name ": " my-app ", " version ": " 1.0.0 ", " private ": true , " dependencies ": { " hogehoge ": " ^2.1.0 ", " fugafuga ": " ^3.4.2 " } , " devDependencies ": { " piyopiyo ": " ^6.7.0 " } } 依存パッケージ名を両者に振り分けておくことで、更新作業の際に、どのパッケージがより重要であるか容易に把握できます。 また、 脆弱性 情報の有無を確認するための npm audit コマンドでは --production オプションを指定することで dependencies のみを調査対象とすることもできます。 【振り分け方法】 npm install <パッケージ名> コマンドにて依存パッケージを追加する際に、下記のオプションで指定します。 ## dependenciesに追加 (オプション指定不要) $ npm intall hogehoge ## devDependenciesに追加 (--devまたは-Dオプションを付与) $ npm install --dev hogehoge 既に依存パッケージを追加済みである場合は、package. json を手動で編集して対象パッケージ名の行を移動した後に、引数なしの npm install コマンドを実行すれば大丈夫です。 更新サイクルをチーム内で明確に決める お悩み:「依存パッケージ、最後に更新したのはいつだろう(遠い目)。」 フロントエンドの依存パッケージは非常に速いペースで機能拡張されていきますので、定期的に更新するという意識を持たないと、ついつい利用バージョンが古いままになってしまいます。 そこで下記のように、いつ(どのような頻度で)更新を行うかを明確化することが大切です。 臨時のセキュリティ更新 週に1回など短い間隔で依存パッケージの 脆弱性 の有無をチェックし、 脆弱性 があればピンポイントで更新する。これによりアプリをセキュアに保つ。 npm audit --production コマンドにて dependencies の依存パッケージに対し既知の 脆弱性 がないかチェックし、影響度に応じてパッケージを更新する。または、 GitHub Dependabot などの自動チェックサービスを用いる。 定期的な一括更新 3カ月に1回など具体的な頻度を決めて、依存パッケージを可能な範囲で全て更新する。これによりコードの陳腐化を防ぐ。 npm outdated コマンドにて新しいバージョンの存在チェックを一括で行い、影響調査・更新を実施する。 動作に影響を与える可能性が高い依存パッケージを把握する お悩み:「package.json内の記載順にパッケージ更新可否を調査し始めたが、何だか効率が悪い。」 以下に属する依存パッケージは芋づる式で他のパッケージの動作に影響を与えるため、優先的に更新の検討を行う事をオススメします。 TypeScript 最近はTypeScriptの型定義を提供するライブラリが多くありますが、型定義よりもTypeScriptのバージョンの方が古いと コンパイル エラーになる場合があるため、真っ先に更新可否を調査したいところです。 webpackなどのモジュール・バンドラ、および プラグイン 例えばwebpackの場合、基本的に複数のloader系 プラグイン を導入して使うことになるため、webpackと プラグイン 間のバージョン相性が悪いとビルドに失敗します。当然、各loader系 プラグイン の作者はバラバラなので、最適なバージョンの組み合わせは自分で探る必要が出てきます。まさにパズルであり、場合によっては更新を断念する事もあります。こちらも優先的に調査したいところです。 VueやReactなどの フレームワーク 、および プラグイン こちらは説明不要ですね! フレームワーク の更新内容はアプリの実装コード全体に影響してしまうので、早めに調査しましょう。 まとめると、以下の優先順位で影響を調査していけば、手戻りを防ぐことが出来ます。 npm audit --production コマンドにて 脆弱性 が表示されたパッケージ 本節で触れた、動作に影響を与える可能性が高い依存パッケージ 残りの dependencies 残りの devDependencies 【Tips】 プラグイン 系パッケージが想定している基幹パッケージ(例えば wepack-hogehoge-plugin の作者が想定している webpack)のバージョンを知りたいときは、 GitHub リポジトリ のリリースノートの確認に加えて、対象パッケージ直下にあるpackage. json 内の devDependencies と peerDependencies に書いてある基幹パッケージのバージョンをチェックしましょう。 peerDependencies の方は馴染みがないかもしれませんが、ここには、 プラグイン 側がアプリの dependencies または devDependencies に要求する基幹パッケージのバージョンが記載されています。 ただし、緩い範囲(例えばメジャーバージョンの指定のみ)でバージョン指定されていることもあるので、あくまで指標として捉えてください。 ビルド結果の差分を確認する お悩み:「従来まで取得できていた値がundefinedになっちゃった!」 TypeScriptなどのトランスパイラや、webpackなどのモジュール・バンドラのバージョンを更新すると、生成される JavaScript コードに差分が出ることがあります。 それが僅かな差分であっても、アプリの一部が機能しなくなる可能性があります。 生成されたコードの差分を確認するためには、以下の手順を踏んでください。 旧環境と新環境において、モジュール・バンドラのコード圧縮をOFFにしてそれぞれビルドを行う。 ※圧縮したままだと比較が困難であるため JavaScript コードの差分をdiffツールで確認する。明らかに影響がなければここで終了。 影響が有りそうな差分を発見した場合、その周辺のキーワードを元に、TypeScriptやwebpackの GitHub ソースコード を確認し、コードを生成している箇所を突き止める。その上で差分の意味を理解し、自分のアプリに影響があるかを判断する。 なお、開発環境のNode.jsをメジャーバージョンアップしたときも、念のため、同様の手順で確認しておいた方が良いでしょう。 バージョン選定の理由や注意点を表にまとめる お悩み:「何らかの理由でこのバージョンに固定したんだけど、思い出せない。」 依存パッケージの定期的な一括更新作業を行うとき、前回の実施時から日が空いてしまうと、前回採用したバージョンの選定理由を忘れてしまう事があります。 それを防ぐために、バージョン選定の理由や注意点を表にまとめておきましょう。未来の自分・自チームへの助けになります。 下表に例を示します。 No. 分類 パッケージ名 バージョン 選定理由や注意点 1 dependencies hogehoge 2.1.0 動作確認済みであるv2系の最新版を利用。v3系はアイコンの見た目が変わるので利用不可。 2 dependencies fugafuga 3.4.2 IE11向けのpolyfillなのでバージョンアップ不要。IE11のサポートを切ったら消す。 ... 70 devDependencies piyopiyo 6.7.0 極力最新版を利用するが、あくまでTypeScriptのバージョンに依存。 このような情報さえまとめておけば、更新作業担当者が変わっても差し支えないため、柔軟な作業 アサイ ンも可能となります。 コマンド一発で実行可能な 単体テスト を用意しておく お悩み:「影響範囲の確認が終わらないよ~!」 依存パッケージの更新時は、アプリの動作に影響がない事を確認する必要があります。 このテスト作業の負荷を少しでも減らすために、フロントエンドの各種実装コードに対して、普段からコマンド一発で実行可能な 単体テスト を書いておく事が非常に重要です。 最近ではVueやReactなどの普及によりUI コンポーネント を主眼に置いた開発が浸透してきているため、コードの部品化が促進され、比較的テストが作りやすい状況にあると言えます。 ご利用の フレームワーク で推奨されているテストライブラリを用い、UI コンポーネント やクラスの 単体テスト を実装しておきましょう。何千というテストケースが一気に通る様を眺めるのは爽快ですよ! 【テスト実装方法の参考リンク】 Vue.js公式ドキュメント - Testing React公式ドキュメント - Testing Overview おわりに いかがでしたでしょうか。 本記事の内容が、少しでも読者の方のお役に立つことができたなら幸いです。 最後に戦略図を再掲します。 電通国際情報サービス Advent Calendar 2021 の他の記事もぜひ、お楽しみください。 執筆: @uramoto.kazuhiro 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
こんにちは、ISID デジタルビジネス推進室の浦本です。好きな食べ物は生八ツ橋です。 本記事は 電通国際情報サービス Advent Calendar 2021 の24日目の記事です。 はじめに 皆さんは、npmでインストールした依存パッケージを定期的に更新されていますか? 「面倒だから更新していない。。。」 「フロントエンドはそのままでも大丈夫でしょ?」 と思って放置されている方もいらっしゃるかもしれません。 しかーし!! 更新せずに放置していると、古い依存パッケージの 脆弱性 を突かれてしまったり、アプリのコード体系が古くなり開発に支障が出たりします。 そこで本日は、面倒になりがちな依存パッケージのバージョン更新を円滑に行うための戦略を書きたいと思います。 【用語について】 本記事では、依存パッケージを利用する開発プロジェクトを単に「アプリ」と呼称します。 戦略 以下より詳細をご説明します。 dependencies と devDependencies の違いを意識する お悩み:「色々とパッケージを追加したけれど、どれが重要か分からなくなってきた。」 package. json 内の dependencies にはプロダクション・コードに影響する依存パッケージ名を列挙し、 devDependencies にはプロダクション・コードに影響しない依存パッケージ名(ビルドツールなど)を列挙します。 【例】 { " name ": " my-app ", " version ": " 1.0.0 ", " private ": true , " dependencies ": { " hogehoge ": " ^2.1.0 ", " fugafuga ": " ^3.4.2 " } , " devDependencies ": { " piyopiyo ": " ^6.7.0 " } } 依存パッケージ名を両者に振り分けておくことで、更新作業の際に、どのパッケージがより重要であるか容易に把握できます。 また、 脆弱性 情報の有無を確認するための npm audit コマンドでは --production オプションを指定することで dependencies のみを調査対象とすることもできます。 【振り分け方法】 npm install <パッケージ名> コマンドにて依存パッケージを追加する際に、下記のオプションで指定します。 ## dependenciesに追加 (オプション指定不要) $ npm intall hogehoge ## devDependenciesに追加 (--devまたは-Dオプションを付与) $ npm install --dev hogehoge 既に依存パッケージを追加済みである場合は、package. json を手動で編集して対象パッケージ名の行を移動した後に、引数なしの npm install コマンドを実行すれば大丈夫です。 更新サイクルをチーム内で明確に決める お悩み:「依存パッケージ、最後に更新したのはいつだろう(遠い目)。」 フロントエンドの依存パッケージは非常に速いペースで機能拡張されていきますので、定期的に更新するという意識を持たないと、ついつい利用バージョンが古いままになってしまいます。 そこで下記のように、いつ(どのような頻度で)更新を行うかを明確化することが大切です。 臨時のセキュリティ更新 週に1回など短い間隔で依存パッケージの 脆弱性 の有無をチェックし、 脆弱性 があればピンポイントで更新する。これによりアプリをセキュアに保つ。 npm audit --production コマンドにて dependencies の依存パッケージに対し既知の 脆弱性 がないかチェックし、影響度に応じてパッケージを更新する。または、 GitHub Dependabot などの自動チェックサービスを用いる。 定期的な一括更新 3カ月に1回など具体的な頻度を決めて、依存パッケージを可能な範囲で全て更新する。これによりコードの陳腐化を防ぐ。 npm outdated コマンドにて新しいバージョンの存在チェックを一括で行い、影響調査・更新を実施する。 動作に影響を与える可能性が高い依存パッケージを把握する お悩み:「package.json内の記載順にパッケージ更新可否を調査し始めたが、何だか効率が悪い。」 以下に属する依存パッケージは芋づる式で他のパッケージの動作に影響を与えるため、優先的に更新の検討を行う事をオススメします。 TypeScript 最近はTypeScriptの型定義を提供するライブラリが多くありますが、型定義よりもTypeScriptのバージョンの方が古いと コンパイル エラーになる場合があるため、真っ先に更新可否を調査したいところです。 webpackなどのモジュール・バンドラ、および プラグイン 例えばwebpackの場合、基本的に複数のloader系 プラグイン を導入して使うことになるため、webpackと プラグイン 間のバージョン相性が悪いとビルドに失敗します。当然、各loader系 プラグイン の作者はバラバラなので、最適なバージョンの組み合わせは自分で探る必要が出てきます。まさにパズルであり、場合によっては更新を断念する事もあります。こちらも優先的に調査したいところです。 VueやReactなどの フレームワーク 、および プラグイン こちらは説明不要ですね! フレームワーク の更新内容はアプリの実装コード全体に影響してしまうので、早めに調査しましょう。 まとめると、以下の優先順位で影響を調査していけば、手戻りを防ぐことが出来ます。 npm audit --production コマンドにて 脆弱性 が表示されたパッケージ 本節で触れた、動作に影響を与える可能性が高い依存パッケージ 残りの dependencies 残りの devDependencies 【Tips】 プラグイン 系パッケージが想定している基幹パッケージ(例えば wepack-hogehoge-plugin の作者が想定している webpack)のバージョンを知りたいときは、 GitHub リポジトリ のリリースノートの確認に加えて、対象パッケージ直下にあるpackage. json 内の devDependencies と peerDependencies に書いてある基幹パッケージのバージョンをチェックしましょう。 peerDependencies の方は馴染みがないかもしれませんが、ここには、 プラグイン 側がアプリの dependencies または devDependencies に要求する基幹パッケージのバージョンが記載されています。 ただし、緩い範囲(例えばメジャーバージョンの指定のみ)でバージョン指定されていることもあるので、あくまで指標として捉えてください。 ビルド結果の差分を確認する お悩み:「従来まで取得できていた値がundefinedになっちゃった!」 TypeScriptなどのトランスパイラや、webpackなどのモジュール・バンドラのバージョンを更新すると、生成される JavaScript コードに差分が出ることがあります。 それが僅かな差分であっても、アプリの一部が機能しなくなる可能性があります。 生成されたコードの差分を確認するためには、以下の手順を踏んでください。 旧環境と新環境において、モジュール・バンドラのコード圧縮をOFFにしてそれぞれビルドを行う。 ※圧縮したままだと比較が困難であるため JavaScript コードの差分をdiffツールで確認する。明らかに影響がなければここで終了。 影響が有りそうな差分を発見した場合、その周辺のキーワードを元に、TypeScriptやwebpackの GitHub ソースコード を確認し、コードを生成している箇所を突き止める。その上で差分の意味を理解し、自分のアプリに影響があるかを判断する。 なお、開発環境のNode.jsをメジャーバージョンアップしたときも、念のため、同様の手順で確認しておいた方が良いでしょう。 バージョン選定の理由や注意点を表にまとめる お悩み:「何らかの理由でこのバージョンに固定したんだけど、思い出せない。」 依存パッケージの定期的な一括更新作業を行うとき、前回の実施時から日が空いてしまうと、前回採用したバージョンの選定理由を忘れてしまう事があります。 それを防ぐために、バージョン選定の理由や注意点を表にまとめておきましょう。未来の自分・自チームへの助けになります。 下表に例を示します。 No. 分類 パッケージ名 バージョン 選定理由や注意点 1 dependencies hogehoge 2.1.0 動作確認済みであるv2系の最新版を利用。v3系はアイコンの見た目が変わるので利用不可。 2 dependencies fugafuga 3.4.2 IE11向けのpolyfillなのでバージョンアップ不要。IE11のサポートを切ったら消す。 ... 70 devDependencies piyopiyo 6.7.0 極力最新版を利用するが、あくまでTypeScriptのバージョンに依存。 このような情報さえまとめておけば、更新作業担当者が変わっても差し支えないため、柔軟な作業 アサイ ンも可能となります。 コマンド一発で実行可能な 単体テスト を用意しておく お悩み:「影響範囲の確認が終わらないよ~!」 依存パッケージの更新時は、アプリの動作に影響がない事を確認する必要があります。 このテスト作業の負荷を少しでも減らすために、フロントエンドの各種実装コードに対して、普段からコマンド一発で実行可能な 単体テスト を書いておく事が非常に重要です。 最近ではVueやReactなどの普及によりUI コンポーネント を主眼に置いた開発が浸透してきているため、コードの部品化が促進され、比較的テストが作りやすい状況にあると言えます。 ご利用の フレームワーク で推奨されているテストライブラリを用い、UI コンポーネント やクラスの 単体テスト を実装しておきましょう。何千というテストケースが一気に通る様を眺めるのは爽快ですよ! 【テスト実装方法の参考リンク】 Vue.js公式ドキュメント - Testing React公式ドキュメント - Testing Overview おわりに いかがでしたでしょうか。 本記事の内容が、少しでも読者の方のお役に立つことができたなら幸いです。 最後に戦略図を再掲します。 電通国際情報サービス Advent Calendar 2021 の他の記事もぜひ、お楽しみください。 執筆: @uramoto.kazuhiro 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
本記事は、 電通国際情報サービス Advent Calendar 2021 の23日目の記事です。 担当は、 エンタープライズ IT事業部 データマネジメント コンサルティング 部 宮城です。 データはDX推進における要であり、データを司る「環境」の重要性というのは昨今ますます高まってきています。 先日、ISIDは、 データクラウド"Snowflake"の販売代理店契約を締結しました。 本日は、この” Snowflake ”について特に私が感銘を受けている特徴を2つ紹介します。 特徴その①:独自の アーキテクチャ <ストレージとコンピュート> 一般的なコンピューターを構成する重要な要素はCPUとメモリとストレージです。" Snowflake "では、そのうちCPUとメモリだけを分離したものをコンピュートという概念で扱います。 ストレージは1つ。利用用途ごとに、さまざまなスペックのコンピュートを立てることができます。 各コンピュートはお互い影響せず、独立したパフォーマンスを実現できます。 このコンピュートを「ウェアハウス」と呼びます。ウェアハウスのスペックは「サイズ」と呼ばれ、XS⇒S⇒M⇒L⇒・・・とTシャツのサイズと同じように表現されます。 たとえば、下記のようなウェアハウスの使い方ができます。 データ加工専用ウェアハウス:   大量のデータを処理するため、大きいサイズのウェアハウスを1台 BI参照専用ウェアハウス:   複数のユーザーが同時にアクセスするため、小~中程度のウェアハウスを複数台 この「ウェアハウス」について、ポイントを2つご紹介します。 完全時間課金 大きいサイズのウェアハウスは高速なぶん高価なわけですが、上記の使い方でデータ連携が1時間内に終われば、データ連携用のウェアハウスは1時間分しか課金されません。データ連携処理が必要とする処理性能に合わせて、全体の性能を上げる必要はないのです。利用用途単位で処理性能を決めれば良いのです。用途によって求められる性能は違いますので、このような考え方は非常に効率的といえます。 ウェアハウスの運用が容易 たとえば、新しい部門がDWHを新たに使いたいと言い出したとします。このとき、その部門用に新たにウェアハウスを作成して提供してあげるだけです。既存のユーザーが使っているウェハアウスには、パフォーマンスを含めてなんの影響を与えません。 また、運用の中でウェアハウスのサイズや数を変えたい場合、Web画面から簡単に操作できるのも魅力的です。 特徴その②:「データ・シェアリング」機能 特徴①の アーキテクチャ の派生ともいえるのが、「データ・シェアリング」の機能です。 自社のアカウントのストレージにあるデータを、別アカウントに共有できます。共有した側を「データ・プロバイダー」、データを共有される側を「データ・コンシューマー」と呼びます。 このとき、ストレージは「データ・プロバイダー」が管理し、ウェアハウスは「データ・コンシューマー」が管理します。データの”保持”とデータの”利用”を、分離して考えることができます。 こちらも、ポイントを2つご紹介します。 データ連携処理が不要 たとえば、自社のデータを他社や他組織に共有しようとした場合、従来であれば、共有用のデータを出力する⇒そのデータを取り込んでもらうという処理フローを作ることになります。 データの構造が変わったり、共有したいデータが増えたりするたび、また追加で開発をしなければならないという保守性の問題が発生します。データに障害が発生した場合の リカバリ も大変ですね。 また、ガバナンスの観点からも、データをばらまきたくないという考えもあります。 データ連携処理を作らなくてもデータを共有できるわけなので、このような課題がクリアになる、というわけです。 データ共有で広がる世界 自社で 保有 しているデータに加えて、他組織や外部のデータを利用することで分析の幅が広がります。これは共有される側のメリットです。 一方、共有する側にもメリットがあります。たとえば、製品メーカーが各工場・各製品の販売・在庫・生産などのデータを Snowflake に一元管理し、販売代理店各社にデータ共有するというストーリーが考えられます。販売代理店は提供された情報をもとに高精度な販売見込データを立てることができ、販売見込販売機会の拡大や見込の精度向上に繋がります。販売代理店のパフォーマンスが向上することで、結果として製品メーカー側にもメリットが生まれます。 データを共有したり共有されたりすることにより、さらに可能性が拡がっていく。その考えを実現するのが、" Snowflake "の「データ・シェアリング」です。 まとめ 本記事は、データ クラウド " Snowflake "の特徴についてご紹介しました。 さらに詳しく知りたい方は、 30日無料トライアル で" Snowflake "を体験してみてください。 電通国際情報サービス Advent Calendar 2021 も残りわずかです。引き続きお楽しみください。 執筆: @miyagi.reiko 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
本記事は、 電通国際情報サービス Advent Calendar 2021 の23日目の記事です。 担当は、 エンタープライズ IT事業部 データマネジメント コンサルティング 部 宮城です。 データはDX推進における要であり、データを司る「環境」の重要性というのは昨今ますます高まってきています。 先日、ISIDは、 データクラウド"Snowflake"の販売代理店契約を締結しました。 本日は、この” Snowflake ”について特に私が感銘を受けている特徴を2つ紹介します。 特徴その①:独自の アーキテクチャ <ストレージとコンピュート> 一般的なコンピューターを構成する重要な要素はCPUとメモリとストレージです。" Snowflake "では、そのうちCPUとメモリだけを分離したものをコンピュートという概念で扱います。 ストレージは1つ。利用用途ごとに、さまざまなスペックのコンピュートを立てることができます。 各コンピュートはお互い影響せず、独立したパフォーマンスを実現できます。 このコンピュートを「ウェアハウス」と呼びます。ウェアハウスのスペックは「サイズ」と呼ばれ、XS⇒S⇒M⇒L⇒・・・とTシャツのサイズと同じように表現されます。 たとえば、下記のようなウェアハウスの使い方ができます。 データ加工専用ウェアハウス:   大量のデータを処理するため、大きいサイズのウェアハウスを1台 BI参照専用ウェアハウス:   複数のユーザーが同時にアクセスするため、小~中程度のウェアハウスを複数台 この「ウェアハウス」について、ポイントを2つご紹介します。 完全時間課金 大きいサイズのウェアハウスは高速なぶん高価なわけですが、上記の使い方でデータ連携が1時間内に終われば、データ連携用のウェアハウスは1時間分しか課金されません。データ連携処理が必要とする処理性能に合わせて、全体の性能を上げる必要はないのです。利用用途単位で処理性能を決めれば良いのです。用途によって求められる性能は違いますので、このような考え方は非常に効率的といえます。 ウェアハウスの運用が容易 たとえば、新しい部門がDWHを新たに使いたいと言い出したとします。このとき、その部門用に新たにウェアハウスを作成して提供してあげるだけです。既存のユーザーが使っているウェハアウスには、パフォーマンスを含めてなんの影響を与えません。 また、運用の中でウェアハウスのサイズや数を変えたい場合、Web画面から簡単に操作できるのも魅力的です。 特徴その②:「データ・シェアリング」機能 特徴①の アーキテクチャ の派生ともいえるのが、「データ・シェアリング」の機能です。 自社のアカウントのストレージにあるデータを、別アカウントに共有できます。共有した側を「データ・プロバイダー」、データを共有される側を「データ・コンシューマー」と呼びます。 このとき、ストレージは「データ・プロバイダー」が管理し、ウェアハウスは「データ・コンシューマー」が管理します。データの”保持”とデータの”利用”を、分離して考えることができます。 こちらも、ポイントを2つご紹介します。 データ連携処理が不要 たとえば、自社のデータを他社や他組織に共有しようとした場合、従来であれば、共有用のデータを出力する⇒そのデータを取り込んでもらうという処理フローを作ることになります。 データの構造が変わったり、共有したいデータが増えたりするたび、また追加で開発をしなければならないという保守性の問題が発生します。データに障害が発生した場合の リカバリ も大変ですね。 また、ガバナンスの観点からも、データをばらまきたくないという考えもあります。 データ連携処理を作らなくてもデータを共有できるわけなので、このような課題がクリアになる、というわけです。 データ共有で広がる世界 自社で 保有 しているデータに加えて、他組織や外部のデータを利用することで分析の幅が広がります。これは共有される側のメリットです。 一方、共有する側にもメリットがあります。たとえば、製品メーカーが各工場・各製品の販売・在庫・生産などのデータを Snowflake に一元管理し、販売代理店各社にデータ共有するというストーリーが考えられます。販売代理店は提供された情報をもとに高精度な販売見込データを立てることができ、販売見込販売機会の拡大や見込の精度向上に繋がります。販売代理店のパフォーマンスが向上することで、結果として製品メーカー側にもメリットが生まれます。 データを共有したり共有されたりすることにより、さらに可能性が拡がっていく。その考えを実現するのが、" Snowflake "の「データ・シェアリング」です。 まとめ 本記事は、データ クラウド " Snowflake "の特徴についてご紹介しました。 さらに詳しく知りたい方は、 30日無料トライアル で" Snowflake "を体験してみてください。 電通国際情報サービス Advent Calendar 2021 も残りわずかです。引き続きお楽しみください。 執筆: @miyagi.reiko 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
これは 電通国際情報サービス アドベントカレンダー の22日目の記事です。 こんにちは。 電通国際情報サービス (ISID) X(クロス) イノベーション 本部ソフトウェアデザインセンター セキュリティグループの富田です。 本記事はISIDで20年継続している全社的な施策のセキュリティレビューをご紹介し、その活動を振り返ってみたいと思います。 背景 現在の システム開発 を取り巻くセキュリティ概況は、顧客情報の漏えい、機密情報の窃取、 ランサムウェア による脅迫など被害報告が後を絶たず、攻撃は高度化・巧妙化していると言えます。そのため、セキュリティ確保の要求は日々高まっています。そのような状況の中、 システム開発 に目を向けると、開発チームが脅威とその対策方法を個別に調査するのには限界があると考えています。そこで必要になるのがセキュリティ専門チームによる調査・検証で得た知見の蓄積と展開です。 知見の展開では、全社的に進めることが欠かせません。ISIDではセキュリティ専門チームによる部門横断のセキュリティレビューを行っています。 セキュリティレビューの開始は、1990年代にさかのぼります。 1998年に、当時はチャレンジングだった Java 技術を適用したインターネットバンキング構築を行いました。その経験から、早期からWebアプリケーションのセキュリティに関心を持っていました。 2001年頃にWebならではの新たな脅威が発生します。 クロスサイトスクリプティング 等の新たな脅威が現れました。これを機に、2001年に組織的な対応を検討し、セキュリティレビューのプロセスを策定しました。そして、2002年にISIDのセキュリティレビューが開始されました。 2008年には、要件定義/設計時のセキュリティレビューに加え、構築システムのアプリケーション部分(及びインフラの一部)のセキュリティチェックを目的に、 脆弱性 検査(静的検査と動的検査)を開始しました。 その後、継続して改善と強化を行っています。 ISIDのセキュリティレビューについて セキュリティレビューの目的、受託開発やISIDの製品/サービス開発のプロジェクトにおけるセキュリティリスクを低減することです。レビューは、プロジェクトのフェーズに応じて段階的に実施し、設計面のセキュリティにおいて一般的な 脆弱性 検査より踏み込んだレビューによりセキュリティを確保します。 提案時のセキュリティレビュー RFP 、要求仕様書、提案書を元に ヒアリ ング形式でリスクの洗い出しを行います。 設計時のセキュリティレビュー 非機能要件定義書/基本設計書の内容を元に、チェックシートを使って対策のレビューを行います。 脆弱性 検査(静的検査、動的検査) 構築システムのアプリケーション部分およびインフラの一部に対して、検査ツールを使用して 脆弱性 検査を行います。 レビュー体制は、セキュリティ分野の 有識者 で構成された10名ほどの専門チームです。具体的には、セキュリティ専門グループのメンバーだけでなく、開発技術と クラウド アーキテクチャ の専門グループのメンバーも参画しています。 この20年で変わっているものは 2008年に 脆弱性 検査が追加され、その時点でレビュープロセスとしては概ねできあがっていますが、その後はプロセスに従って形式的にレビューを実施してきたのでしょうか? そうではありません。 それは、レビューの題材が変わるからです。以下の要素が変化し、絡み合ってきます。 脅威 対策方法 システム アーキテクチャ クラウド サービス 要素技術 開発手法 セキュリティ関連の法制度、 ガイドライン など幅広い分野の調査、知見の蓄積が必要になります。 その他の変化には、セキュリティレビューで使用するチェックシートがあります。 設計時のセキュリティレビューに利用するセキュリティ対策チェックシートも変わっていきます。例えば、以下のような分野別のチェックシートを新規に作成してきました。 2010年  クラウド 環境シートを作成 2011年 モバイルアプリシートを作成 2016年 認証と認可 プロトコル のシートを作成 2018年 IoTシステムのシートを作成 これらは、プロジェクトでの適用可能性が出てきたタイミングで作成しています。 その他にも、セキュリティ対策チェックシート本体を含めたチェックシートを定期的(半期に1回程度)に見直しています。 これらのチェックシートは、セキュリティ専門チームの調査・検証の成果をもとに、新しい脅威への対策やセキュリティ対策のベストプ ラク ティスなどをタ イムリ ーに反映(育てているイメージ)したものです。特長としては、何のための対策かが分かるようにしている点です。 大事にしてきたポリシー セキュリティレビューを進めるときに大事にしてきたことは、当事者意識を持って、「問題の指摘と対応案をセットで伝える」ということです。プロジェクトの開発チームとは別の第 三者 的なチームとしてセキュリティレビューを行いますが、最終的なゴールは同じです。良いシステムを作り、顧客の要求にこたえることです。 今後について セキュリティ確保の要求が強まっている状況において、最新の脅威や クラウド サービスの進化などに追随すべく、セキュリティ専門チームによるより広く深い知見の蓄積と展開が必要だと考えています。ISIDでは、従来のやり方にとらわれない、開発手法などのトレンドに応じたセキュリティレビューの仕組みを作っていってくための体制強化を行っています。 執筆: @tomita 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
これは 電通国際情報サービス アドベントカレンダー の22日目の記事です。 こんにちは。 電通国際情報サービス (ISID) X(クロス) イノベーション 本部ソフトウェアデザインセンター セキュリティグループの富田です。 本記事はISIDで20年継続している全社的な施策のセキュリティレビューをご紹介し、その活動を振り返ってみたいと思います。 背景 現在の システム開発 を取り巻くセキュリティ概況は、顧客情報の漏えい、機密情報の窃取、 ランサムウェア による脅迫など被害報告が後を絶たず、攻撃は高度化・巧妙化していると言えます。そのため、セキュリティ確保の要求は日々高まっています。そのような状況の中、 システム開発 に目を向けると、開発チームが脅威とその対策方法を個別に調査するのには限界があると考えています。そこで必要になるのがセキュリティ専門チームによる調査・検証で得た知見の蓄積と展開です。 知見の展開では、全社的に進めることが欠かせません。ISIDではセキュリティ専門チームによる部門横断のセキュリティレビューを行っています。 セキュリティレビューの開始は、1990年代にさかのぼります。 1998年に、当時はチャレンジングだった Java 技術を適用したインターネットバンキング構築を行いました。その経験から、早期からWebアプリケーションのセキュリティに関心を持っていました。 2001年頃にWebならではの新たな脅威が発生します。 クロスサイトスクリプティング 等の新たな脅威が現れました。これを機に、2001年に組織的な対応を検討し、セキュリティレビューのプロセスを策定しました。そして、2002年にISIDのセキュリティレビューが開始されました。 2008年には、要件定義/設計時のセキュリティレビューに加え、構築システムのアプリケーション部分(及びインフラの一部)のセキュリティチェックを目的に、 脆弱性 検査(静的検査と動的検査)を開始しました。 その後、継続して改善と強化を行っています。 ISIDのセキュリティレビューについて セキュリティレビューの目的、受託開発やISIDの製品/サービス開発のプロジェクトにおけるセキュリティリスクを低減することです。レビューは、プロジェクトのフェーズに応じて段階的に実施し、設計面のセキュリティにおいて一般的な 脆弱性 検査より踏み込んだレビューによりセキュリティを確保します。 提案時のセキュリティレビュー RFP 、要求仕様書、提案書を元に ヒアリ ング形式でリスクの洗い出しを行います。 設計時のセキュリティレビュー 非機能要件定義書/基本設計書の内容を元に、チェックシートを使って対策のレビューを行います。 脆弱性 検査(静的検査、動的検査) 構築システムのアプリケーション部分およびインフラの一部に対して、検査ツールを使用して 脆弱性 検査を行います。 レビュー体制は、セキュリティ分野の 有識者 で構成された10名ほどの専門チームです。具体的には、セキュリティ専門グループのメンバーだけでなく、開発技術と クラウド アーキテクチャ の専門グループのメンバーも参画しています。 この20年で変わっているものは 2008年に 脆弱性 検査が追加され、その時点でレビュープロセスとしては概ねできあがっていますが、その後はプロセスに従って形式的にレビューを実施してきたのでしょうか? そうではありません。 それは、レビューの題材が変わるからです。以下の要素が変化し、絡み合ってきます。 脅威 対策方法 システム アーキテクチャ クラウド サービス 要素技術 開発手法 セキュリティ関連の法制度、 ガイドライン など幅広い分野の調査、知見の蓄積が必要になります。 その他の変化には、セキュリティレビューで使用するチェックシートがあります。 設計時のセキュリティレビューに利用するセキュリティ対策チェックシートも変わっていきます。例えば、以下のような分野別のチェックシートを新規に作成してきました。 2010年  クラウド 環境シートを作成 2011年 モバイルアプリシートを作成 2016年 認証と認可 プロトコル のシートを作成 2018年 IoTシステムのシートを作成 これらは、プロジェクトでの適用可能性が出てきたタイミングで作成しています。 その他にも、セキュリティ対策チェックシート本体を含めたチェックシートを定期的(半期に1回程度)に見直しています。 これらのチェックシートは、セキュリティ専門チームの調査・検証の成果をもとに、新しい脅威への対策やセキュリティ対策のベストプ ラク ティスなどをタ イムリ ーに反映(育てているイメージ)したものです。特長としては、何のための対策かが分かるようにしている点です。 大事にしてきたポリシー セキュリティレビューを進めるときに大事にしてきたことは、当事者意識を持って、「問題の指摘と対応案をセットで伝える」ということです。プロジェクトの開発チームとは別の第 三者 的なチームとしてセキュリティレビューを行いますが、最終的なゴールは同じです。良いシステムを作り、顧客の要求にこたえることです。 今後について セキュリティ確保の要求が強まっている状況において、最新の脅威や クラウド サービスの進化などに追随すべく、セキュリティ専門チームによるより広く深い知見の蓄積と展開が必要だと考えています。ISIDでは、従来のやり方にとらわれない、開発手法などのトレンドに応じたセキュリティレビューの仕組みを作っていってくための体制強化を行っています。 執筆: @tomita 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
こんにちは。ISID CIT事業部の熊倉です。 Salesforce 開発者の皆様、Lightning Web Components(以下、LWC)活用されてますでしょうか? LWCを活用することでHTML/ CSS /JSを使用したリッチなフロントエンドを Salesforce で構築できるようになりましたが、その反面、 Salesforce の強みである「オブジェクトの スキーマ を定義した段階でフロントエンドが作成される」といった強みを活かすことができなくなりました。 ただ、反対に考えてみれば通常のWeb開発のフローと変わりがなくなったとも言え、Web開発では当たり前となっているプロトタイプや モックアップ の作成が有用になったのではないかと思います。 今回の記事は非デザイナーである自分が Salesforce から提供されているSketchの プラグイン を使用しデザイン( モックアップ )を制作してみた、いわゆる「やってみた」系記事となります。 1. Sketchとは 2. Salesforce プラグインについて 2.1. プラグインのインストール方法 2.1.1 (インストールしていない人は)Sketchのインストール 2.1.2 Salesforce プラグインをダウンロード 2.1.3 (オプション)Salesforce Sansをインストール 2.1.4 (オプション)Assistantsをインストール 2.2 プラグインの利用方法 2.2.1 Sketchのメニューから「Plugins」>「Lightning Design System Plugin」を選択(または⌘+Jを押下)し、プラグインを開く 2.2.2 使用したいコンポーネントを選択し、Sketchのキャンバスにドラッグ&ドロップ 3. デザインを制作してみた 3.1 Tips コンポーネントで複数の状態を使用する テンプレートの中の文字を修正する 4. 感想 1. Sketchとは (既にご存知の方もたくさん居るかと思いますので簡単に紹介します) SketchとはUIデザインツールと呼ばれる製品の一つで、フロントエンドのデザイン制作に特化したソフトウェアです。 今では Adobe XDや Figma など各社から色々なUIデザインツールが提供されておりますが、その分野の先駆けとなった製品でもあります。 日本では最近 Adobe XDや Figma の方がSketchより人気のようですが、Sketchにも先駆者としても強みがあり、例えば日本語のドキュメントが豊富だったり サードパーティ の プラグイン が豊富だったりします。 (ちなみに最近、 uxtools.coが発表した統計 によると世界的には Figma がかなりのシェアを占めてました) 今回は Salesforce が公式で プラグイン を提供しているSketchを利用し、デザイン制作を行ってみました。 なお、Sketchはデザイン制作を行えるネイティブアプリの提供が Mac に限定されておりますのでご注意ください。 2. Salesforce プラグイン について 皆様はLWCを使用した開発をする際、 CSS をどのように作成していますか? 人によってはBulmaといった CSS フレームワーク を利用しているケースもあるかと思いますが、 多くの方が Salesforce Lightning Design System (以下、SLDS) を使用して作成しているかと思います。 Salesforce が提供しているSketchの プラグイン はSLDSを使用したUIの作成をサポートしてくれるものとなります。 2.1. プラグイン のインストール方法 インストール方法や使用方法を学ぶには、まず SLDSのサイト に公開されている動画を見ていただくのが一番わかりやすいかと思います。 https://youtu.be/bm1ZhGoNIG4 ご視聴いただいた上で簡単に Salesforce プラグイン の使い方について説明していきたいと思います。 2.1.1 (インストールしていない人は)Sketchのインストール Sketchをインストールされてない方は、 Sketchの公式サイト の「 Sign Up for Free」からフォームに情報を入力し、アプリのインストールをしてください。 30日間限定ではありますが、無料で全ての機能を使用できます。 2.1.2 Salesforce プラグイン をダウンロード SLDSのサイト から Salesforce プラグイン をダウンロードしてください。 ZIP形式でファイルがダウンロードされますので解凍していただき、生成されたファイル design-system-sketch.sketchplugin をダブルクリックすることでSketchがインストールされて入れば自動で プラグイン がインストールされます。 2.1.3 (オプション) Salesforce Sansをインストール (2021/12/16 時点) Salesforce ではフォントとして Salesforce Sans という独自のフォントを使用してきた歴史がありますが、Summer '21のアップデートでネイティブのsans-serif系フォントを使用するように変更が加えられました。 https://help.salesforce.com/s/articleView?id=release-notes.rn_lex_default_font.htm&type=5&release=232 しかし、Sketchの Salesforce プラグイン では現在も Salesforce Sans が利用されているので、 プラグイン を使用する前にフォントをPCへインストールしておくことをお勧めします。 前述のように最新(2021/12/16 時点 Winter'21)のサイトからはフォントをダウンロードできませんので、 Salesforce Sans が使用されていたSpring'21の アーカイブ からファイルをダウンロードしてください。 https://spring-21.lightningdesignsystem.com/resources/downloads/ Mac にフォントを追加する方法は以下のドキュメントを参考にしていただければと思います。 https://support.apple.com/ja-jp/HT201749 2.1.4 (オプション)Assistantsをインストール Sketchには Assistants と呼ばれる特定のルールに則ってデザインファイル内をLintしてくれる機能があります。(便利!) Salesforce もSLDSに準拠したAsistantsを提供しておりますので、Sketchにインストールしておくことをお勧めします。 https://add-sketch-assistant.now.sh/api/main?pkg=lightning-design-system-linter 上記、URLをクリックすることでSketchに対してAssistantsをインストールできます。 Sketch内では「View」> 「Show Assistants」からLintされた内容を確認できます。 (後述しているデモのデザインを制作した後にAssistantsを有効にしたので、すごい怒られていました...笑) 2.2 プラグイン の利用方法 2.2.1 Sketchのメニューから「Plugins」>「Lightning Design System Plugin」を選択(または⌘+Jを押下)し、 プラグイン を開く Sketchで「Plugins」>「Lightning Design System Plugin」を選択するか、⌘+J を押下することで、 Salesforce プラグイン のウィンドウが開きます。 初回に起動した場合、以下のようなライブラリを選択する画面が表示されます。 ライブラリの右側のチェックを有効にすることで、ライブラリが使用できます(基本的に全て有効で良いと思います) ちなみに、各ライブラリの意味は以下のようになります( FAQ の内容を引用) - Components: Web - Sketch equivalents of component blueprints and tokens as seen on the SLDS website - Components: Mobile - Sketch collection of native mobile patterns and mobile web coded components - Icons - A file of design system icons which is automatically generated from design system code - Builder - Builder design guideline customized component symbols - User Engagement - User engagement design guideline customized component symbols - Chart - Chart design guideline customized component symbols - Rules, Filters, and Logic - Rules, Filters, Logic design guideline customized component symbols - Standard Artboards - Based on user data, Sketch artboards are sized to the common viewport dimensions used - Spec - A collection of symbols to use when documenting dimensions and details of designs for engineers - Wireframes - Grey box stencils of common Lightning interfaces 2.2.2 使用したい コンポーネント を選択し、Sketchのキャンバスに ドラッグ&ドロップ ライブラリを選択した後、 コンポーネント を選択できるウィンドウが開きます。 プラグイン の構成は次のようになっています。 検索し、 コンポーネント を絞り込むことができます。 (日本語では検索できません) 『ライブラリの選択』『FAQの参照』『 プラグイン のフィードバック』を行えます。2.2.1で選択したライブラリを無効化・有効化し直したい場合はこちらから再設定できます。 表示するライブラリを切り替えることができます。 『All Libraries』『Components: Web』ライブラリを選択している場合、上部の Show LWCs Only を有効化することで、 LWC コンポーネント のみ表示できます。 コンポーネント が一覧で表示されます。 プラグイン の使用方法自体は至って簡単で、表示されている コンポーネント をSketchのキャンバスに ドラッグ&ドロップ するだけです! 3. デザインを制作してみた 実際にデモとして次のようなデザインを制作してみました。 商品の登録画面 商品の一覧画面 Customer 360的な顧客の一元ビュー 3.1 Tips デモのデザインを制作した際に自分が感じたTipsを以下に記載します。 (Sketchそのものの機能も多分に紹介してますが、初めてSketchを触った人目線として記載しております) コンポーネント で複数の状態を使用する ボタンのように複数の状態(Brand , Success , Destructive , Inverse 等々)があるものはリストのプルダウンボタンを押すことで、それぞれの状態の コンポーネント が参照できます。(複数の状態が用意されているものは n Variants と表示されます) コンポーネント をキャンバスに配置してからでも状態を切り替えることが可能で、右のインスペクターからSYMBOLを選択することで切り替えることができます。 テンプレートの中の文字を修正する コンポーネント にはデフォルトでサンプルの文が入力されているものもあります。 サンプルの文より具体的な言葉に入れ替えたい場合、右のインスペクターにあるOverridesから文字を修正できます。 4. 感想 実はSketchを触る前に Salesforce がUI Kitを提供しているFrammerも試したことがあったのですが、比べると以下の部分で利点を感じました。 プラグイン の形で提供されており、検索機能も使用できるので使用したい コンポーネント を見つけやすい プラグイン の他にAssistantsといった便利機能も提供されているので、デザイン制作の統一を計ることができる 他の プラグイン に比べると Salesforce が力を入れているだけありドキュメントの種類も多い Sketchネイティブアプリの提供が Mac に限定されているのでソフト自体の利用のハードルはちょっと高いかもしれませんが、 LWC(やAura , VisualForceといったカスタマイズされたフロントエンド)の開発が多い方は是非試してみてはいかがでしょうか? 最後までお読みいただきありがとうございました。 執筆: @kumakura.koki.isid 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
こんにちは。ISID CIT事業部の熊倉です。 Salesforce 開発者の皆様、Lightning Web Components(以下、LWC)活用されてますでしょうか? LWCを活用することでHTML/ CSS /JSを使用したリッチなフロントエンドを Salesforce で構築できるようになりましたが、その反面、 Salesforce の強みである「オブジェクトの スキーマ を定義した段階でフロントエンドが作成される」といった強みを活かすことができなくなりました。 ただ、反対に考えてみれば通常のWeb開発のフローと変わりがなくなったとも言え、Web開発では当たり前となっているプロトタイプや モックアップ の作成が有用になったのではないかと思います。 今回の記事は非デザイナーである自分が Salesforce から提供されているSketchの プラグイン を使用しデザイン( モックアップ )を制作してみた、いわゆる「やってみた」系記事となります。 1. Sketchとは 2. Salesforce プラグインについて 2.1. プラグインのインストール方法 2.1.1 (インストールしていない人は)Sketchのインストール 2.1.2 Salesforce プラグインをダウンロード 2.1.3 (オプション)Salesforce Sansをインストール 2.1.4 (オプション)Assistantsをインストール 2.2 プラグインの利用方法 2.2.1 Sketchのメニューから「Plugins」>「Lightning Design System Plugin」を選択(または⌘+Jを押下)し、プラグインを開く 2.2.2 使用したいコンポーネントを選択し、Sketchのキャンバスにドラッグ&ドロップ 3. デザインを制作してみた 3.1 Tips コンポーネントで複数の状態を使用する テンプレートの中の文字を修正する 4. 感想 1. Sketchとは (既にご存知の方もたくさん居るかと思いますので簡単に紹介します) SketchとはUIデザインツールと呼ばれる製品の一つで、フロントエンドのデザイン制作に特化したソフトウェアです。 今では Adobe XDや Figma など各社から色々なUIデザインツールが提供されておりますが、その分野の先駆けとなった製品でもあります。 日本では最近 Adobe XDや Figma の方がSketchより人気のようですが、Sketchにも先駆者としても強みがあり、例えば日本語のドキュメントが豊富だったり サードパーティ の プラグイン が豊富だったりします。 (ちなみに最近、 uxtools.coが発表した統計 によると世界的には Figma がかなりのシェアを占めてました) 今回は Salesforce が公式で プラグイン を提供しているSketchを利用し、デザイン制作を行ってみました。 なお、Sketchはデザイン制作を行えるネイティブアプリの提供が Mac に限定されておりますのでご注意ください。 2. Salesforce プラグイン について 皆様はLWCを使用した開発をする際、 CSS をどのように作成していますか? 人によってはBulmaといった CSS フレームワーク を利用しているケースもあるかと思いますが、 多くの方が Salesforce Lightning Design System (以下、SLDS) を使用して作成しているかと思います。 Salesforce が提供しているSketchの プラグイン はSLDSを使用したUIの作成をサポートしてくれるものとなります。 2.1. プラグイン のインストール方法 インストール方法や使用方法を学ぶには、まず SLDSのサイト に公開されている動画を見ていただくのが一番わかりやすいかと思います。 https://youtu.be/bm1ZhGoNIG4 ご視聴いただいた上で簡単に Salesforce プラグイン の使い方について説明していきたいと思います。 2.1.1 (インストールしていない人は)Sketchのインストール Sketchをインストールされてない方は、 Sketchの公式サイト の「 Sign Up for Free」からフォームに情報を入力し、アプリのインストールをしてください。 30日間限定ではありますが、無料で全ての機能を使用できます。 2.1.2 Salesforce プラグイン をダウンロード SLDSのサイト から Salesforce プラグイン をダウンロードしてください。 ZIP形式でファイルがダウンロードされますので解凍していただき、生成されたファイル design-system-sketch.sketchplugin をダブルクリックすることでSketchがインストールされて入れば自動で プラグイン がインストールされます。 2.1.3 (オプション) Salesforce Sansをインストール (2021/12/16 時点) Salesforce ではフォントとして Salesforce Sans という独自のフォントを使用してきた歴史がありますが、Summer '21のアップデートでネイティブのsans-serif系フォントを使用するように変更が加えられました。 https://help.salesforce.com/s/articleView?id=release-notes.rn_lex_default_font.htm&type=5&release=232 しかし、Sketchの Salesforce プラグイン では現在も Salesforce Sans が利用されているので、 プラグイン を使用する前にフォントをPCへインストールしておくことをお勧めします。 前述のように最新(2021/12/16 時点 Winter'21)のサイトからはフォントをダウンロードできませんので、 Salesforce Sans が使用されていたSpring'21の アーカイブ からファイルをダウンロードしてください。 https://spring-21.lightningdesignsystem.com/resources/downloads/ Mac にフォントを追加する方法は以下のドキュメントを参考にしていただければと思います。 https://support.apple.com/ja-jp/HT201749 2.1.4 (オプション)Assistantsをインストール Sketchには Assistants と呼ばれる特定のルールに則ってデザインファイル内をLintしてくれる機能があります。(便利!) Salesforce もSLDSに準拠したAsistantsを提供しておりますので、Sketchにインストールしておくことをお勧めします。 https://add-sketch-assistant.now.sh/api/main?pkg=lightning-design-system-linter 上記、URLをクリックすることでSketchに対してAssistantsをインストールできます。 Sketch内では「View」> 「Show Assistants」からLintされた内容を確認できます。 (後述しているデモのデザインを制作した後にAssistantsを有効にしたので、すごい怒られていました...笑) 2.2 プラグイン の利用方法 2.2.1 Sketchのメニューから「Plugins」>「Lightning Design System Plugin」を選択(または⌘+Jを押下)し、 プラグイン を開く Sketchで「Plugins」>「Lightning Design System Plugin」を選択するか、⌘+J を押下することで、 Salesforce プラグイン のウィンドウが開きます。 初回に起動した場合、以下のようなライブラリを選択する画面が表示されます。 ライブラリの右側のチェックを有効にすることで、ライブラリが使用できます(基本的に全て有効で良いと思います) ちなみに、各ライブラリの意味は以下のようになります( FAQ の内容を引用) - Components: Web - Sketch equivalents of component blueprints and tokens as seen on the SLDS website - Components: Mobile - Sketch collection of native mobile patterns and mobile web coded components - Icons - A file of design system icons which is automatically generated from design system code - Builder - Builder design guideline customized component symbols - User Engagement - User engagement design guideline customized component symbols - Chart - Chart design guideline customized component symbols - Rules, Filters, and Logic - Rules, Filters, Logic design guideline customized component symbols - Standard Artboards - Based on user data, Sketch artboards are sized to the common viewport dimensions used - Spec - A collection of symbols to use when documenting dimensions and details of designs for engineers - Wireframes - Grey box stencils of common Lightning interfaces 2.2.2 使用したい コンポーネント を選択し、Sketchのキャンバスに ドラッグ&ドロップ ライブラリを選択した後、 コンポーネント を選択できるウィンドウが開きます。 プラグイン の構成は次のようになっています。 検索し、 コンポーネント を絞り込むことができます。 (日本語では検索できません) 『ライブラリの選択』『FAQの参照』『 プラグイン のフィードバック』を行えます。2.2.1で選択したライブラリを無効化・有効化し直したい場合はこちらから再設定できます。 表示するライブラリを切り替えることができます。 『All Libraries』『Components: Web』ライブラリを選択している場合、上部の Show LWCs Only を有効化することで、 LWC コンポーネント のみ表示できます。 コンポーネント が一覧で表示されます。 プラグイン の使用方法自体は至って簡単で、表示されている コンポーネント をSketchのキャンバスに ドラッグ&ドロップ するだけです! 3. デザインを制作してみた 実際にデモとして次のようなデザインを制作してみました。 商品の登録画面 商品の一覧画面 Customer 360的な顧客の一元ビュー 3.1 Tips デモのデザインを制作した際に自分が感じたTipsを以下に記載します。 (Sketchそのものの機能も多分に紹介してますが、初めてSketchを触った人目線として記載しております) コンポーネント で複数の状態を使用する ボタンのように複数の状態(Brand , Success , Destructive , Inverse 等々)があるものはリストのプルダウンボタンを押すことで、それぞれの状態の コンポーネント が参照できます。(複数の状態が用意されているものは n Variants と表示されます) コンポーネント をキャンバスに配置してからでも状態を切り替えることが可能で、右のインスペクターからSYMBOLを選択することで切り替えることができます。 テンプレートの中の文字を修正する コンポーネント にはデフォルトでサンプルの文が入力されているものもあります。 サンプルの文より具体的な言葉に入れ替えたい場合、右のインスペクターにあるOverridesから文字を修正できます。 4. 感想 実はSketchを触る前に Salesforce がUI Kitを提供しているFrammerも試したことがあったのですが、比べると以下の部分で利点を感じました。 プラグイン の形で提供されており、検索機能も使用できるので使用したい コンポーネント を見つけやすい プラグイン の他にAssistantsといった便利機能も提供されているので、デザイン制作の統一を計ることができる 他の プラグイン に比べると Salesforce が力を入れているだけありドキュメントの種類も多い Sketchネイティブアプリの提供が Mac に限定されているのでソフト自体の利用のハードルはちょっと高いかもしれませんが、 LWC(やAura , VisualForceといったカスタマイズされたフロントエンド)の開発が多い方は是非試してみてはいかがでしょうか? 最後までお読みいただきありがとうございました。 執筆: @kumakura.koki.isid 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
この記事は 電通国際情報サービス Advent Calendar 2021 21日目の記事です。 はじめに 学生時代の私とスパゲッティコード 脱!スパゲッティコード! コピー&ペーストしない!とことん処理の共通化 前処理・主処理・後処理を意識した構造化プログラミングの実施 まとめ 最後に はじめに こんにちは、ISID 金融ソリューション事業部の星野将吾と申します。2年目の ひよっこ エンジニアです。 アドベントカレンダー に参加したい!と思ったものの、 ひよっこ であるのもあり詳細な技術に踏み込んだことは書けないので、今回は「 "脱 スパゲッティコード " してプロダクトコードを書くスタートに立つまで」というタイトルでお話していこうと思います。 今まで大学の課題や研究において個人開発でしかコードを書いてこなかった私が、プロダクトコードを書く上で解決しなければならなかった課題をここに書きます。 これからプロダクトコードを書く新社会人や学生の皆様の一助になればと思います。 この手の技術記事は初めて書くので、読者の皆様お手柔らかにどうぞ… 学生時代の私と スパゲッティコード まず本タイトルにある「 スパゲッティコード 」とは何かという話ですが 「実行順序や構造が複雑に入り組んでいて整理されていないプログラム」 を指します。 コードを スパゲッティコード たらしめている要因を挙げてみると以下のようなものがあります。 同じ処理・似たような処理が複数箇所に書かれている。 処理構造化が適切に階層化されていない。 学生時代は自分しかそのコードに触れないため、 スパゲッティコード であっても自分さえ理解できていれば問題はありません。 しかしながら、プロダクトコードを書く上で スパゲッティコード は許されません。 なぜなら、 自分以外の人がそのコードを触る ことになるからです。他の人にとって読みにくいコードは、コードの保守性が著しく低下します。 次の章では スパゲッティコード を防ぐに当たって気をつけるべき観点について説明します。 脱! スパゲッティコード ! 脱 スパゲッティコード するための観点は以下です。 コピー&ペーストしない!とことん処理を共 通化 前処理・主処理・後処理を意識した構造化プログラミングの実施 コピー&ペーストしない!とことん処理の共 通化 個人で開発していると、共 通化 を考えるのが面倒で、自分や先輩、同僚が書いた既存コードベースからついつい コピー&ペースト してしまいませんか? 私はかなりやってました。 このコピー&ペーストをやってしまうと、1か所で済んだはずのコード修正が、3, 4箇所にも及んでしまい 修正コストが増加 しています。 ひとりなら3, 4箇所くらいなんとかなるかもしれません。 しかし、他の人が修正するにあたっては、どこにコピー&ペーストがされているか探す必要も出るのでプロダクトコードでは大きな問題になります。 コピー&ペーストをしたい部分を見つけたら、その部分を 共通関数 として切り出して使いたいところで呼びだすことができないか考えてみましょう。 もちろんコピー&ペーストをしていなくても、同じ処理を書いてしまうこともあります。まずは、コピー&ペーストを止めるところから共 通化 を行う意識を高めていけるとよいでしょう。 コピー&ペースト厳禁!みたいな話をしましたが、共通関数として切り出すことができなかったり、無理やり共 通化 するのが不適切なケースも多くあります。 しかし、この判断は個々のプロダクトにおける設計と関係する話になってくるので、本記事での説明は割愛します。 ただ、「共 通化 できないか?」と考える姿勢は スパゲッティコード 化を防ぐ上で大変重要です。 前処理・主処理・後処理を意識した構造化プログラミングの実施 構造化プログラミングとは、簡単に言ってしまえば 「処理を階層化すること」 です。 まず階層化されていないコードの例を以下に示します。 言語は大学のプログラミング演習等の講義でよく扱われるであろう、 C言語 で書いてみます。 内容は二つの数字の加減算を行う簡単なプログラムです。 #include <stdio.h> int main(void){ // 前処理 printf("加減算へようこそ。\n"); // 主処理 while(1) { int a, b, op, result, ctrl; char op_char; printf("1つ目の数字を入力してください\n"); scanf("%d",&a); printf("2つ目の数字を入力してください\n"); scanf("%d",&b); printf("加算なら0, 減算ならそれ以外を入力してください\n"); scanf("%d", &op); if (op == 0) { result = a + b; op_char = '+'; }else{ result = a - b; op_char = '-'; } printf("計算結果:%d %c %d = %d\n", a, op_char, b, result); printf("他の計算をしたい場合、1を入力してください\n"); scanf("%d", &ctrl); if( ctrl != 1 ){ break; } } // 後処理 printf("お疲れ様でした。\n"); return 0; } これは短いコードなので、一つの関数に書いても大きな問題はありません。 しかし、これ以上に長いコードや、if文やfor文などがされるコードは読みにくくなります。 読みにくいコード = 理解が困難なコード であるため、コードの保守性の悪化を招くこととなります。 このコードを構造化してみると以下のようになります。 #include <stdio.h> void input(int *ptr_a, int *ptr_b, int *ptr_op){ printf("1つ目の数字を入力してください\n"); scanf("%d", ptr_a); printf("2つ目の数字を入力してください\n"); scanf("%d", ptr_b); printf("加算なら0, 減算ならそれ以外を入力してください\n"); scanf("%d", ptr_op); } int calc(int a, int b, int op){ int result = 0; if (op == 0) { result = a + b; } else { result = a - b; } return result; } void output(int a, int b, int result, int op){ char op_char; if (op == 0) { op_char = '+'; } else { op_char = '-'; } printf("計算結果:%d %c %d = %d\n", a, op_char, b, result); } int scan_continue_ctrl(void){ int ctrl = 0; printf("他の計算をしたい場合、1を入力してください\n"); scanf("%d", &ctrl); return ctrl; } void exec(void){ while (1) { int a, b, op, result, ctrl = 0; // 前処理 input(&a, &b, &op); // 主処理 result = calc(a, b, op); // 後処理1 output(a, b, result, op); // 後処理2 ctrl = scan_continue_ctrl(); if ( ctrl != 1 ) { break; } } } int main(void){ // 前処理 printf("加減算へようこそ。\n"); // 主処理 exec(); // 後処理 printf("お疲れ様でした。\n"); return 0; } コード中のコメント文にあるように、多くのケースで 前処理・主処理・後処理 というパターンで構造化すると上手く構造化できます。 例えば、ファイルに対する書き込み処理という仕様のコードでも、 前処理:ファイルのオープン 主処理:ファイルへの書き込み 後処理:ファイルのクローズ のように構造化できます。 また、例のコードの exec関数 に着目してみてください。exec関数はmain関数の主処理として切り出されたものですが、その中でさらに 前処理・主処理・後処理 と分けることができています。 ただ後処理はあえて一つにまとめませんでした。というのも、output関数とscan_continue関数をまとめたときに与える関数名がpost_processくらいしか思いつかなかったためです。 該当行周辺における関数のまとめ方次第でこの判断の是非は分かれると思います。 今回例えばpost_process関数という関数を用意して後処理をまとめてしまうと、input関数とcalc関数より 抽象度が高い 関数名になってしまいます。抽象度がばらつくとそれだけで ソースコード 理解の難易度は上昇します。 また、今回に限った話ですが、 input関数 の存在は output関数 の存在を期待しています。ただ入力を受け付けるだけのプログラムならいいのですが、今回は結果を表示するまでが必要とされています。よって、output関数がないと、ぱっと見で「え?どこで出力するの?」となってしまいます。 このように、必ずしも 前処理・主処理・後処理 の3つにキレイに分かれるとは限りません。しかしながら、慣れないうちはこの分け方を意識すると、構造化の方針が見えやすいので非常におすすめです。 まとめ 今回この記事では、ただ動きさえすればいい スパゲッティコード を改善するための、2つの観点をお話しました。 1つ目は 「共 通化 」 、2つ目は 「構造化」 です。 この2点に気をつけるだけでぐっとコードの保守性はあがると思います。 もちろん最初は気をつけてもなかなかうまくいかないものです。私もまだ完璧にはできていません。 たくさんコードを書いてたくさんレビューをしてもらう。これをくじけずに継続することで着実に身についていくと思います。 私も引き続きくじけずに頑張ります… ただこの "脱 スパゲッティコード " を成し遂げたらプロダクトコードとして完璧かと言われればそうではありません。 他にも変数・関数の 命名 の仕方、変数のスコープなど、気をつける点は多くあります。 今後、今回触れていない内容についても、投稿できればなとは思っています。(今回は上手くまとめられず…) 最後に 今回この記事が書けるようになるレベルまでマンツーマンのご指導をしていただいた、12/1執筆者の太一さん( 記事 )と12/17執筆者の水野さん( 記事 )には大変感謝しております。 さて、明日12/22の アドベントカレンダー は、山口さんによるセキュリティ周りの記事が投稿される予定です。 是非他の記事もお楽しみください! 執筆: @hoshino.shogo 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
この記事は 電通国際情報サービス Advent Calendar 2021 21日目の記事です。 はじめに 学生時代の私とスパゲッティコード 脱!スパゲッティコード! コピー&ペーストしない!とことん処理の共通化 前処理・主処理・後処理を意識した構造化プログラミングの実施 まとめ 最後に はじめに こんにちは、ISID 金融ソリューション事業部の星野将吾と申します。2年目の ひよっこ エンジニアです。 アドベントカレンダー に参加したい!と思ったものの、 ひよっこ であるのもあり詳細な技術に踏み込んだことは書けないので、今回は「 "脱 スパゲッティコード " してプロダクトコードを書くスタートに立つまで」というタイトルでお話していこうと思います。 今まで大学の課題や研究において個人開発でしかコードを書いてこなかった私が、プロダクトコードを書く上で解決しなければならなかった課題をここに書きます。 これからプロダクトコードを書く新社会人や学生の皆様の一助になればと思います。 この手の技術記事は初めて書くので、読者の皆様お手柔らかにどうぞ… 学生時代の私と スパゲッティコード まず本タイトルにある「 スパゲッティコード 」とは何かという話ですが 「実行順序や構造が複雑に入り組んでいて整理されていないプログラム」 を指します。 コードを スパゲッティコード たらしめている要因を挙げてみると以下のようなものがあります。 同じ処理・似たような処理が複数箇所に書かれている。 処理構造化が適切に階層化されていない。 学生時代は自分しかそのコードに触れないため、 スパゲッティコード であっても自分さえ理解できていれば問題はありません。 しかしながら、プロダクトコードを書く上で スパゲッティコード は許されません。 なぜなら、 自分以外の人がそのコードを触る ことになるからです。他の人にとって読みにくいコードは、コードの保守性が著しく低下します。 次の章では スパゲッティコード を防ぐに当たって気をつけるべき観点について説明します。 脱! スパゲッティコード ! 脱 スパゲッティコード するための観点は以下です。 コピー&ペーストしない!とことん処理を共 通化 前処理・主処理・後処理を意識した構造化プログラミングの実施 コピー&ペーストしない!とことん処理の共 通化 個人で開発していると、共 通化 を考えるのが面倒で、自分や先輩、同僚が書いた既存コードベースからついつい コピー&ペースト してしまいませんか? 私はかなりやってました。 このコピー&ペーストをやってしまうと、1か所で済んだはずのコード修正が、3, 4箇所にも及んでしまい 修正コストが増加 しています。 ひとりなら3, 4箇所くらいなんとかなるかもしれません。 しかし、他の人が修正するにあたっては、どこにコピー&ペーストがされているか探す必要も出るのでプロダクトコードでは大きな問題になります。 コピー&ペーストをしたい部分を見つけたら、その部分を 共通関数 として切り出して使いたいところで呼びだすことができないか考えてみましょう。 もちろんコピー&ペーストをしていなくても、同じ処理を書いてしまうこともあります。まずは、コピー&ペーストを止めるところから共 通化 を行う意識を高めていけるとよいでしょう。 コピー&ペースト厳禁!みたいな話をしましたが、共通関数として切り出すことができなかったり、無理やり共 通化 するのが不適切なケースも多くあります。 しかし、この判断は個々のプロダクトにおける設計と関係する話になってくるので、本記事での説明は割愛します。 ただ、「共 通化 できないか?」と考える姿勢は スパゲッティコード 化を防ぐ上で大変重要です。 前処理・主処理・後処理を意識した構造化プログラミングの実施 構造化プログラミングとは、簡単に言ってしまえば 「処理を階層化すること」 です。 まず階層化されていないコードの例を以下に示します。 言語は大学のプログラミング演習等の講義でよく扱われるであろう、 C言語 で書いてみます。 内容は二つの数字の加減算を行う簡単なプログラムです。 #include <stdio.h> int main(void){ // 前処理 printf("加減算へようこそ。\n"); // 主処理 while(1) { int a, b, op, result, ctrl; char op_char; printf("1つ目の数字を入力してください\n"); scanf("%d",&a); printf("2つ目の数字を入力してください\n"); scanf("%d",&b); printf("加算なら0, 減算ならそれ以外を入力してください\n"); scanf("%d", &op); if (op == 0) { result = a + b; op_char = '+'; }else{ result = a - b; op_char = '-'; } printf("計算結果:%d %c %d = %d\n", a, op_char, b, result); printf("他の計算をしたい場合、1を入力してください\n"); scanf("%d", &ctrl); if( ctrl != 1 ){ break; } } // 後処理 printf("お疲れ様でした。\n"); return 0; } これは短いコードなので、一つの関数に書いても大きな問題はありません。 しかし、これ以上に長いコードや、if文やfor文などがされるコードは読みにくくなります。 読みにくいコード = 理解が困難なコード であるため、コードの保守性の悪化を招くこととなります。 このコードを構造化してみると以下のようになります。 #include <stdio.h> void input(int *ptr_a, int *ptr_b, int *ptr_op){ printf("1つ目の数字を入力してください\n"); scanf("%d", ptr_a); printf("2つ目の数字を入力してください\n"); scanf("%d", ptr_b); printf("加算なら0, 減算ならそれ以外を入力してください\n"); scanf("%d", ptr_op); } int calc(int a, int b, int op){ int result = 0; if (op == 0) { result = a + b; } else { result = a - b; } return result; } void output(int a, int b, int result, int op){ char op_char; if (op == 0) { op_char = '+'; } else { op_char = '-'; } printf("計算結果:%d %c %d = %d\n", a, op_char, b, result); } int scan_continue_ctrl(void){ int ctrl = 0; printf("他の計算をしたい場合、1を入力してください\n"); scanf("%d", &ctrl); return ctrl; } void exec(void){ while (1) { int a, b, op, result, ctrl = 0; // 前処理 input(&a, &b, &op); // 主処理 result = calc(a, b, op); // 後処理1 output(a, b, result, op); // 後処理2 ctrl = scan_continue_ctrl(); if ( ctrl != 1 ) { break; } } } int main(void){ // 前処理 printf("加減算へようこそ。\n"); // 主処理 exec(); // 後処理 printf("お疲れ様でした。\n"); return 0; } コード中のコメント文にあるように、多くのケースで 前処理・主処理・後処理 というパターンで構造化すると上手く構造化できます。 例えば、ファイルに対する書き込み処理という仕様のコードでも、 前処理:ファイルのオープン 主処理:ファイルへの書き込み 後処理:ファイルのクローズ のように構造化できます。 また、例のコードの exec関数 に着目してみてください。exec関数はmain関数の主処理として切り出されたものですが、その中でさらに 前処理・主処理・後処理 と分けることができています。 ただ後処理はあえて一つにまとめませんでした。というのも、output関数とscan_continue関数をまとめたときに与える関数名がpost_processくらいしか思いつかなかったためです。 該当行周辺における関数のまとめ方次第でこの判断の是非は分かれると思います。 今回例えばpost_process関数という関数を用意して後処理をまとめてしまうと、input関数とcalc関数より 抽象度が高い 関数名になってしまいます。抽象度がばらつくとそれだけで ソースコード 理解の難易度は上昇します。 また、今回に限った話ですが、 input関数 の存在は output関数 の存在を期待しています。ただ入力を受け付けるだけのプログラムならいいのですが、今回は結果を表示するまでが必要とされています。よって、output関数がないと、ぱっと見で「え?どこで出力するの?」となってしまいます。 このように、必ずしも 前処理・主処理・後処理 の3つにキレイに分かれるとは限りません。しかしながら、慣れないうちはこの分け方を意識すると、構造化の方針が見えやすいので非常におすすめです。 まとめ 今回この記事では、ただ動きさえすればいい スパゲッティコード を改善するための、2つの観点をお話しました。 1つ目は 「共 通化 」 、2つ目は 「構造化」 です。 この2点に気をつけるだけでぐっとコードの保守性はあがると思います。 もちろん最初は気をつけてもなかなかうまくいかないものです。私もまだ完璧にはできていません。 たくさんコードを書いてたくさんレビューをしてもらう。これをくじけずに継続することで着実に身についていくと思います。 私も引き続きくじけずに頑張ります… ただこの "脱 スパゲッティコード " を成し遂げたらプロダクトコードとして完璧かと言われればそうではありません。 他にも変数・関数の 命名 の仕方、変数のスコープなど、気をつける点は多くあります。 今後、今回触れていない内容についても、投稿できればなとは思っています。(今回は上手くまとめられず…) 最後に 今回この記事が書けるようになるレベルまでマンツーマンのご指導をしていただいた、12/1執筆者の太一さん( 記事 )と12/17執筆者の水野さん( 記事 )には大変感謝しております。 さて、明日12/22の アドベントカレンダー は、山口さんによるセキュリティ周りの記事が投稿される予定です。 是非他の記事もお楽しみください! 執筆: @hoshino.shogo 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
これは 電通国際情報サービス アドベントカレンダー の 20日 目の記事です。 こんにちは、 電通国際情報サービス (ISID)の上羽優貴です。 ISIDは今年に新しくスマートシティや 自治 体DX、脱炭素化などの社会課題対応型ビジネスの推進を目的とした スマートソサエティセンター という新組織を立ち上げました。私はここに所属し、スマートシティ関連のビジネスを推進しています。本記事では、スマートシティを実現するための基盤ソフトウェアであるFIWARE(ファイウェア)について解説します。 スマートシティと都市OS 近年、日本におけるスマートシティへの取り組みが盛んになっています。 トヨタ が推進する ウーブンシティ は有名ですね。スマートシティを実現するうえで欠かせないのがICTやデータの活用です。その活用の分野は防災や安全、モビリティ、教育・医療など多岐にわたります。今現在も様々な都市においてICTやデータを活用した住民サービスの提供がなされています。 しかし、日本でのスマートシティの実現に向けては、これまで大きく3つの課題がありました。 1. サービスの再利用・横展開 分野や組織ごとに個別にシステムを構築することが多く、他の地域での再利用や横展開が困難 2. 分野間データ利活用 分野や組織ごとにデータが独立し、分野や組織を横断してデータを活用することが困難 3. 拡張性の低さ 個別にシステムが構築されるため独自の仕様を持ち、機能拡張にかかるコストが大きく 、サービスを拡張させることが困難 そこで、近年注目を集めているのが都市OSというソフトウェア基盤です。都市OSは以下のような特徴を持ち、これまで課題だったサービスの再利用や、分野間のデータ利活用を促進する効果を期待されています。 1. 相互運用(つながる) 共通の機能や標準的なインタフェース( API )を具備して外部に公開する仕組みを持ち、分野や組織をまたがるサービス連携や横展開を可能にします。 2. データ流通(ながれる) スマートシティに利用するデータを仲介する機能を持ち、分野や組織を超えたデータ活用を可能にします。 3. 拡張容易(つづけられる) 機能間の 疎結合 なシステム構成を持ち、システムの拡張を容易にします。 都市OSを構築するためのソフトウェアの1つとして、この記事で紹介するFIWAREが挙げられます。 ※ スマートシティガイドブック(本文)第2章1 p.33より引用 FIWAREとは FIWARE とはFI(Future Internet)WARE(SOFTWARE)の略で、 自治 体や企業などの業種を超えたデータ利活用やサービス連携を促すために開発されたソフトウェア群です。ソフトウェア群と表現した通り、FIWAREは1つのソフトウェアのことではなく、いくつかのソフトウェアの総称です。 OSS として GitHub に公開されています。 FIWAREは欧州のFI-PPPという官民連携プログラムで開発されました(FI-PPPについては こちら を参照ください)。その後、FIWAREを民間主導で推進するための FIWARE Foundation が設立されました。現在はTelefonica( GitHubリポジトリ でFIWAREの コンポーネント を公開)や NEC ( 加古川市 や 高松市 にFIWAREを導入)などが中心メンバーとなって推進しています。 FIWAREは都市OSを構築するために利用されます。都市OSとして利用されるのは、前述の都市OSの特徴を持つためです。具体的には以下のとおりです。 1. 相互運用(つながる) NGSI(Next Generation Service Interfaces)と呼ばれる API 標準規格を採用しています。標準規格の API により、分野や組織間のサービス連携やデータの利活用を可能にします。NGSIについては後述します。 2. データ流通(ながれる) FIWAREのコア コンポーネント である FIWARE Orion がデータを分散管理したり、データを中継したり機能を持ちます。 3. 拡張容易(つづけられる) 下図の通り、FIWAREを構成する コンポーネント はそれぞれが独立して動作する「マイクロサービス アーキテクチャ 」を採用しています。必要な機能を持つ コンポーネント を取捨選択することにより、システムの拡張を容易にします。 ※ GitHubより引用 FIWAREの コンポーネント FIWAREは多くの コンポーネント を持ち、それぞれを組み合わせて利用します。FIWAREを構成する コンポーネント はGeneric Enablers(GE)と呼ばれます。ここではGEのうち、代表的なものを挙げて簡単に説明します。 FIWARE Orion FIWAREのコア(必須)となる コンポーネント です。コンテキストブローカーとしてデータを保管・管理するための機能を持ち、NGSIでデータを公開できます。MongoDBがデータストアとして利用されます。Orionに保管されるデータのライフサイクルは基本的に短く、最新のデータにより上書き更新されます。例えばビルの電力データをOrionで管理することを考えたときに、Orionに保管されるのは現在のビルの電力のみで、その履歴は保管されません。 FIWARE Cygnus (Cygnus) Orionに登録されたデータを蓄積するための コンポーネント です。データストアとして PostgreSQL 、ElasticSearch、 AWS のDynamoDBなど幅広く対応しています。 Orionに登録されたデータの変更をNGSIで受け取り、その変更履歴をデータストアに登録します。 IoT Agent For Json (IoT Agent) 名前の通り、IoTのデータ収集にかかわるコンポ―ネントです。RabbitMQなどのMQTTブローカーに集められたデータを収集してNGSIに変換し、Orionに登録する機能を持ちます。 WireCloud データを可視化するための コンポーネント です。OrionからNGSIでデータを受け取り、地図やグラフなどを活用した ダッシュ ボードを通じてデータを閲覧できます。 開発ツール Dockerイメージ や Helmチャート が公開されています。 AWS やAzureといった パブリッククラウド のコンテナプラットフォームを利用してFIWAREをホストすることが可能になります。 実際にFIWAREを使ってみる ここからは実際にFIWAREを使ったデータ登録、データ取得といった手順を試してみます。この手順で利用するFIWAREの コンポーネント はOrionのみです。Orionを利用するにあたり、事前にNGSIの説明します。 NGSIとは NGSIは、 NEC を中心にOMA(Open Mobile Alliance)によって標準化された API の規格です。アップデートが継続的に行われ、NGSI-LD(Linked Data)と呼ばれる規格が最新です。ただし、FIWAREに実装されているNGSIのバージョンはNGSIv2(NGSI-LDの以前のバージョン)であり、 NGSI-LDは開発中 になります。そのため、ここではNGSIv2に関して説明します。 まずはNGSIv2のデータモデルについて、以下の json を例に説明します。これはNGSIv2の規格に則り表現された json です。 { " id ":" urn:ngsi-ld:Vehicle:A4567 ", " type ":" Vehicle ", " brandName ": { " type ":" Property ", " value ":" Mercedes " } , " isParked ": { " type ":" Relationship ", " value ":" urn:ngsi-ld:OffStreetParking:Downtown1 ", " metadata ": { " observedAt ": { " value ":" 2017-07-29T12:00:04 ", " type ":" DateTime " } , " providedBy ": { " type ":" Relationship ", " value ":" urn:ngsi-ld:Person:Bob " } } } } この json は Vehicle という車のオブジェクトを表しています。NGSIv2において、このオブジェクトのことをエンティティ(Entity)と呼びます。このEntityは、どのブランドであるかを示す brandName と、どこに駐車されているかを示す isParked という属性を持ちます。これら属性のことを、 アトリビュート (Attribute)と呼びます。さらに isParked 属性は metadata と呼ばれるその属性に付随する情報を持ちます。 observedAt というそのデータが変更された時間や、 providedBy というそのデータが誰によって変更された、といったような情報です。この情報のことを メタデータ (Metadata) このようにNGSIv2でのデータモデルはEntity・Attribute・Metadataの3つにより構成される下図のようなモデルです。 ※ こちらより引用 NGSIでは API Specification も定められています。 Orionでのデータ管理を試す こちら で公開されている手順を参考に、Orionでのデータ管理を試してみます。実行にあたり、 Docker 、 Docker Compose が必要になりますので事前にインストールお願いします。なお、コマンドの実行環境は bash になります。 まずは チュートリアル 用の リポジトリ からファイルを取得し、 環境変数 を設定してます。 環境変数 を設定後、コンテナを起動します。 # リポジトリのクローン git clone https://github.com/FIWARE/tutorials.Getting-Started.git cd tutorials.Getting-Started git checkout NGSI-v2 # 環境変数の設定 export $( cat .env | grep " # " -v ) # コンテナを起動 docker compose -p fiware up -d docker-compose.yml の中を確認すると、Orionのコンテナと、MongoDBのコンテナを起動することが確認できます。前述の通り、MongoDBはOrionのデータストアとして利用されるためです。これがFIWAREを始めるにあたっての最小構成となります。 次に、起動したOrionへ以下のリク エス トを投げてみてください。 curl ではなくても、Postmanなどの GUI ソフトウェアを利用しても構いません。 curl -iX POST \ ' http://localhost:1026/v2/entities ' \ -H ' Content-Type: application/json ' \ -d ' { "id": "urn:ngsi-ld:Store:001", "type": "Store", "address": { "type": "PostalAddress", "value": { "streetAddress": "Bornholmer Straße 65", "addressRegion": "Berlin", "addressLocality": "Prenzlauer Berg", "postalCode": "10439" }, "metadata": { "verified": { "value": true, "type": "Boolean" } } }, "location": { "type": "geo:json", "value": { "type": "Point", "coordinates": [13.3986, 52.5547] } }, "name": { "type": "Text", "value": "Bösebrücke Einkauf" } } ' NGSIv2のEntityを作成する API ( /v2/entities )に対してリク エス トを送信していることがわかります。このリク エス トにより、Orionには Store Entityが作成されます。 次に登録したEntityを取得する API を実行してみます。以下のコマンドを実行してください。 curl -G -X GET \ ' http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001 ' \ -d ' options=keyValues ' NGSIv2のEntityを取得する API ( /v2entities/<Entity ID> )に対してリク エス トを送信していることがわかります。このリク エス トにより、Orionに登録されたEntityを取得します。なお、 options=keyValues というクエリが指定されていますが、これはAttributeの type と metadata を省略した形でレスポンスを得るためのオプションです。レスポンスとして、登録したEntityを取得できていることが確認できます。 { " id ": " urn:ngsi-ld:Store:001 ", " type ": " Store ", " address ": { " streetAddress ": " Bornholmer Straße 65 ", " addressRegion ": " Berlin ", " addressLocality ": " Prenzlauer Berg ", " postalCode ": " 10439 " } , " location ": { " type ": " Point ", " coordinates ": [ 13.3986 , 52.5547 ] } , " name ": " Bösebrücke Einkauf " } このように基本的な CRUD 操作のほか、都市OSの特徴である「データ流通(ながれる)」を実現するための機能を持ちます。Orionのデータが変更されたときにPush型で情報を通知する Subscription と呼ばれる機能や、外部のデータを仲介する Registration と呼ばれる機能などがそれに該当します。本記事では割愛しますが、リンク先で手順が公開されていますのでぜひ試してください。 おわりに 本記事ではスマートシティを実現する都市OSについて、また都市OSを構築するためのFIWAREについて紹介しました。 日本におけるFIWAREの状況として、 NEC 中心にコミュニティが形成され、日本語の記事も増えてきています。またいくつかの 自治 体にも導入され、今後ますます認知度も高まり、発展していくことが予想されます。そんな中、ISIDとしても個人としても、日本のスマートシティの分野に貢献できるよう情報発信を続けていきたいなと考えています。 最後までお読みいただきありがとうございました。 参考文献 スマートシティガイドブック スマートシティリファレンスアーキテクチャ FIWARE FIWARE GitHubリポジトリ Let's FIWARE https://www.slideshare.net/fisuda/fiware-context-information-management https://www.slideshare.net/fisuda/orioncontextbroker-ngsiv2-overview-for-developers-that-already-know-ngsiv1-20180615 執筆: @ueba.yuki 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
これは 電通国際情報サービス アドベントカレンダー の 20日 目の記事です。 こんにちは、 電通国際情報サービス (ISID)の上羽優貴です。 ISIDは今年に新しくスマートシティや 自治 体DX、脱炭素化などの社会課題対応型ビジネスの推進を目的とした スマートソサエティセンター という新組織を立ち上げました。私はここに所属し、スマートシティ関連のビジネスを推進しています。本記事では、スマートシティを実現するための基盤ソフトウェアであるFIWARE(ファイウェア)について解説します。 スマートシティと都市OS 近年、日本におけるスマートシティへの取り組みが盛んになっています。 トヨタ が推進する ウーブンシティ は有名ですね。スマートシティを実現するうえで欠かせないのがICTやデータの活用です。その活用の分野は防災や安全、モビリティ、教育・医療など多岐にわたります。今現在も様々な都市においてICTやデータを活用した住民サービスの提供がなされています。 しかし、日本でのスマートシティの実現に向けては、これまで大きく3つの課題がありました。 1. サービスの再利用・横展開 分野や組織ごとに個別にシステムを構築することが多く、他の地域での再利用や横展開が困難 2. 分野間データ利活用 分野や組織ごとにデータが独立し、分野や組織を横断してデータを活用することが困難 3. 拡張性の低さ 個別にシステムが構築されるため独自の仕様を持ち、機能拡張にかかるコストが大きく 、サービスを拡張させることが困難 そこで、近年注目を集めているのが都市OSというソフトウェア基盤です。都市OSは以下のような特徴を持ち、これまで課題だったサービスの再利用や、分野間のデータ利活用を促進する効果を期待されています。 1. 相互運用(つながる) 共通の機能や標準的なインタフェース( API )を具備して外部に公開する仕組みを持ち、分野や組織をまたがるサービス連携や横展開を可能にします。 2. データ流通(ながれる) スマートシティに利用するデータを仲介する機能を持ち、分野や組織を超えたデータ活用を可能にします。 3. 拡張容易(つづけられる) 機能間の 疎結合 なシステム構成を持ち、システムの拡張を容易にします。 都市OSを構築するためのソフトウェアの1つとして、この記事で紹介するFIWAREが挙げられます。 ※ スマートシティガイドブック(本文)第2章1 p.33より引用 FIWAREとは FIWARE とはFI(Future Internet)WARE(SOFTWARE)の略で、 自治 体や企業などの業種を超えたデータ利活用やサービス連携を促すために開発されたソフトウェア群です。ソフトウェア群と表現した通り、FIWAREは1つのソフトウェアのことではなく、いくつかのソフトウェアの総称です。 OSS として GitHub に公開されています。 FIWAREは欧州のFI-PPPという官民連携プログラムで開発されました(FI-PPPについては こちら を参照ください)。その後、FIWAREを民間主導で推進するための FIWARE Foundation が設立されました。現在はTelefonica( GitHubリポジトリ でFIWAREの コンポーネント を公開)や NEC ( 加古川市 や 高松市 にFIWAREを導入)などが中心メンバーとなって推進しています。 FIWAREは都市OSを構築するために利用されます。都市OSとして利用されるのは、前述の都市OSの特徴を持つためです。具体的には以下のとおりです。 1. 相互運用(つながる) NGSI(Next Generation Service Interfaces)と呼ばれる API 標準規格を採用しています。標準規格の API により、分野や組織間のサービス連携やデータの利活用を可能にします。NGSIについては後述します。 2. データ流通(ながれる) FIWAREのコア コンポーネント である FIWARE Orion がデータを分散管理したり、データを中継したり機能を持ちます。 3. 拡張容易(つづけられる) 下図の通り、FIWAREを構成する コンポーネント はそれぞれが独立して動作する「マイクロサービス アーキテクチャ 」を採用しています。必要な機能を持つ コンポーネント を取捨選択することにより、システムの拡張を容易にします。 ※ GitHubより引用 FIWAREの コンポーネント FIWAREは多くの コンポーネント を持ち、それぞれを組み合わせて利用します。FIWAREを構成する コンポーネント はGeneric Enablers(GE)と呼ばれます。ここではGEのうち、代表的なものを挙げて簡単に説明します。 FIWARE Orion FIWAREのコア(必須)となる コンポーネント です。コンテキストブローカーとしてデータを保管・管理するための機能を持ち、NGSIでデータを公開できます。MongoDBがデータストアとして利用されます。Orionに保管されるデータのライフサイクルは基本的に短く、最新のデータにより上書き更新されます。例えばビルの電力データをOrionで管理することを考えたときに、Orionに保管されるのは現在のビルの電力のみで、その履歴は保管されません。 FIWARE Cygnus (Cygnus) Orionに登録されたデータを蓄積するための コンポーネント です。データストアとして PostgreSQL 、ElasticSearch、 AWS のDynamoDBなど幅広く対応しています。 Orionに登録されたデータの変更をNGSIで受け取り、その変更履歴をデータストアに登録します。 IoT Agent For Json (IoT Agent) 名前の通り、IoTのデータ収集にかかわるコンポ―ネントです。RabbitMQなどのMQTTブローカーに集められたデータを収集してNGSIに変換し、Orionに登録する機能を持ちます。 WireCloud データを可視化するための コンポーネント です。OrionからNGSIでデータを受け取り、地図やグラフなどを活用した ダッシュ ボードを通じてデータを閲覧できます。 開発ツール Dockerイメージ や Helmチャート が公開されています。 AWS やAzureといった パブリッククラウド のコンテナプラットフォームを利用してFIWAREをホストすることが可能になります。 実際にFIWAREを使ってみる ここからは実際にFIWAREを使ったデータ登録、データ取得といった手順を試してみます。この手順で利用するFIWAREの コンポーネント はOrionのみです。Orionを利用するにあたり、事前にNGSIの説明します。 NGSIとは NGSIは、 NEC を中心にOMA(Open Mobile Alliance)によって標準化された API の規格です。アップデートが継続的に行われ、NGSI-LD(Linked Data)と呼ばれる規格が最新です。ただし、FIWAREに実装されているNGSIのバージョンはNGSIv2(NGSI-LDの以前のバージョン)であり、 NGSI-LDは開発中 になります。そのため、ここではNGSIv2に関して説明します。 まずはNGSIv2のデータモデルについて、以下の json を例に説明します。これはNGSIv2の規格に則り表現された json です。 { " id ":" urn:ngsi-ld:Vehicle:A4567 ", " type ":" Vehicle ", " brandName ": { " type ":" Property ", " value ":" Mercedes " } , " isParked ": { " type ":" Relationship ", " value ":" urn:ngsi-ld:OffStreetParking:Downtown1 ", " metadata ": { " observedAt ": { " value ":" 2017-07-29T12:00:04 ", " type ":" DateTime " } , " providedBy ": { " type ":" Relationship ", " value ":" urn:ngsi-ld:Person:Bob " } } } } この json は Vehicle という車のオブジェクトを表しています。NGSIv2において、このオブジェクトのことをエンティティ(Entity)と呼びます。このEntityは、どのブランドであるかを示す brandName と、どこに駐車されているかを示す isParked という属性を持ちます。これら属性のことを、 アトリビュート (Attribute)と呼びます。さらに isParked 属性は metadata と呼ばれるその属性に付随する情報を持ちます。 observedAt というそのデータが変更された時間や、 providedBy というそのデータが誰によって変更された、といったような情報です。この情報のことを メタデータ (Metadata) このようにNGSIv2でのデータモデルはEntity・Attribute・Metadataの3つにより構成される下図のようなモデルです。 ※ こちらより引用 NGSIでは API Specification も定められています。 Orionでのデータ管理を試す こちら で公開されている手順を参考に、Orionでのデータ管理を試してみます。実行にあたり、 Docker 、 Docker Compose が必要になりますので事前にインストールお願いします。なお、コマンドの実行環境は bash になります。 まずは チュートリアル 用の リポジトリ からファイルを取得し、 環境変数 を設定してます。 環境変数 を設定後、コンテナを起動します。 # リポジトリのクローン git clone https://github.com/FIWARE/tutorials.Getting-Started.git cd tutorials.Getting-Started git checkout NGSI-v2 # 環境変数の設定 export $( cat .env | grep " # " -v ) # コンテナを起動 docker compose -p fiware up -d docker-compose.yml の中を確認すると、Orionのコンテナと、MongoDBのコンテナを起動することが確認できます。前述の通り、MongoDBはOrionのデータストアとして利用されるためです。これがFIWAREを始めるにあたっての最小構成となります。 次に、起動したOrionへ以下のリク エス トを投げてみてください。 curl ではなくても、Postmanなどの GUI ソフトウェアを利用しても構いません。 curl -iX POST \ ' http://localhost:1026/v2/entities ' \ -H ' Content-Type: application/json ' \ -d ' { "id": "urn:ngsi-ld:Store:001", "type": "Store", "address": { "type": "PostalAddress", "value": { "streetAddress": "Bornholmer Straße 65", "addressRegion": "Berlin", "addressLocality": "Prenzlauer Berg", "postalCode": "10439" }, "metadata": { "verified": { "value": true, "type": "Boolean" } } }, "location": { "type": "geo:json", "value": { "type": "Point", "coordinates": [13.3986, 52.5547] } }, "name": { "type": "Text", "value": "Bösebrücke Einkauf" } } ' NGSIv2のEntityを作成する API ( /v2/entities )に対してリク エス トを送信していることがわかります。このリク エス トにより、Orionには Store Entityが作成されます。 次に登録したEntityを取得する API を実行してみます。以下のコマンドを実行してください。 curl -G -X GET \ ' http://localhost:1026/v2/entities/urn:ngsi-ld:Store:001 ' \ -d ' options=keyValues ' NGSIv2のEntityを取得する API ( /v2entities/<Entity ID> )に対してリク エス トを送信していることがわかります。このリク エス トにより、Orionに登録されたEntityを取得します。なお、 options=keyValues というクエリが指定されていますが、これはAttributeの type と metadata を省略した形でレスポンスを得るためのオプションです。レスポンスとして、登録したEntityを取得できていることが確認できます。 { " id ": " urn:ngsi-ld:Store:001 ", " type ": " Store ", " address ": { " streetAddress ": " Bornholmer Straße 65 ", " addressRegion ": " Berlin ", " addressLocality ": " Prenzlauer Berg ", " postalCode ": " 10439 " } , " location ": { " type ": " Point ", " coordinates ": [ 13.3986 , 52.5547 ] } , " name ": " Bösebrücke Einkauf " } このように基本的な CRUD 操作のほか、都市OSの特徴である「データ流通(ながれる)」を実現するための機能を持ちます。Orionのデータが変更されたときにPush型で情報を通知する Subscription と呼ばれる機能や、外部のデータを仲介する Registration と呼ばれる機能などがそれに該当します。本記事では割愛しますが、リンク先で手順が公開されていますのでぜひ試してください。 おわりに 本記事ではスマートシティを実現する都市OSについて、また都市OSを構築するためのFIWAREについて紹介しました。 日本におけるFIWAREの状況として、 NEC 中心にコミュニティが形成され、日本語の記事も増えてきています。またいくつかの 自治 体にも導入され、今後ますます認知度も高まり、発展していくことが予想されます。そんな中、ISIDとしても個人としても、日本のスマートシティの分野に貢献できるよう情報発信を続けていきたいなと考えています。 最後までお読みいただきありがとうございました。 参考文献 スマートシティガイドブック スマートシティリファレンスアーキテクチャ FIWARE FIWARE GitHubリポジトリ Let's FIWARE https://www.slideshare.net/fisuda/fiware-context-information-management https://www.slideshare.net/fisuda/orioncontextbroker-ngsiv2-overview-for-developers-that-already-know-ngsiv1-20180615 執筆: @ueba.yuki 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
ISID X(クロス) イノベーション 本部 の三浦です。本記事は 電通国際情報サービス Advent Calendar 2021 の19日目のポストです。 本記事では、Azure Web Appsでは簡単にモダンな開発ができることをキャプチャーを多めで説明します。 GUI からポチポチ設定でここまで簡単にできるということをお伝えするのが目的です。 (あまりインフラに詳しくない アプリ開発 者でも簡単にインフラ構築、運用ができるかもしれないといった内容です) 触れる技術要素 今回の模擬システムの説明 今回の環境でできること OpenID Connectによる認証(Office 365のユーザーでお手軽認証) の構築 webappsのディプロイスロットによる、ブルーグリーンディプロイ GitHub Actionによる継続的デリバリー まとめ 触れる技術要素 Azure Web Apps OpenID Connectによる認証(Office 365のユーザーでお手軽Single Sign On) ディプロイスロットによるブルーグリーンディプロイ GitHub ActionによるContinuous Delivery 今回の模擬システムの説明 Azure Web Apps + cognitive searchのシンプルなシステムです。 Azure Web Appsの機能で、 OpenID ConnectでSingle Sign Onし、 GitHub から、ステージング環境へ GitHub Actionで継続的デリバリーし、 GUI 上の操作で簡易にブルーグリーンディプロイをします。また、app insightsによる APM 監視、および、log analytics によるログ管理をしています。 今回のエントリでは用いてる技術のうち「 OpenID Connectによる認証」「ディプロイスロットによるブルーグリーンディプロイ」「 GitHub Actionによる継続的デリバリー」を紹介します。 今回の環境でできること Azure Web Apps の OpenID Connect認証による簡単な Office 365ユーザーで Single Sign On(AzureAD) ブルーグリーンディプロイによる停止時間の短縮、障害時の高速切り戻し GitHub acitionによる継続的デリバリー OpenID Connectによる認証(Office 365のユーザーでお手軽認証) の構築 下記のように GUI 操作していくだけで、簡易にOffice 365のユーザーで認証が可能となります。 ここの設定を間違えると、認証してないユーザー、全世界のAzureADのユーザーにこのシステムが公開されてしまうので注意です。 このように設定すると、webapp側にRelying Party としての設定が完了、AzureAD側に OpenID Provider としてのリソースが作成されています。 では、AzureADに遷移し、 OpenID Providerとしての追加設定をします。 まず、プロパティを開き下記の項目を設定します。 デフォルトの「いいえ」の場合、そのテナントの全てのユーザーがwebapps にアクセス可能となります。「はい」の場合は後述の手順により、webapps単位でアクセスするユーザーを制限できます。 例えば、マルチテナント的に複数の顧客をゲストユーザーとして登録しているテナントで、「いいえ」で設定してしまうと、他社向けのシステムが見えてしまうといったことが起こります。 次に、このアプリケーションへのアクセス権を与えるユーザーを追加してます。 ここでポイントなのは、AzureADのゲストユーザーも追加可能であるということです。これは、他社のOffice 365ユーザーに対して、他社の管理者の作業なしにSingle Sign Onが可能だということです。顧客が普段使ってるOffice 365のユーザーでのSingle Sign Onが数分で構築できる強力な機能です。 また、社内向けシステムを検討する際にも、煩雑な情シスへのIDP関連の申請を省略して普段使っているOffice 365のID、MFAを用いたシステムが提供可能となります。 Single Sign Onの設定は開発者、ID管理側、双方に一定の知識が必要でトラブリやすい作業です。しかし、Azure Web Apps + azureADのゲストユーザーを用いることでこの作業を大幅に簡易化できます。 webappsのディプロイスロットによる、ブルーグリーンディプロイ では、次に、ブルーグリーンディプロイを設定します。Azure Web Apps には、ブルーグリーンディプロイを実現するための ディプロイスロット という仕組みがあります。 これを作成します。 「スロットの追加」を選択し、「次から設定を複製」で元のスロットを選ぶだけで、アプリケーション設定がコピーされます( 環境変数 、 java バージョン、 tomcat バージョンなど)。 また、待機スロット用のURL、サーバが作成されます。 できあがったディプロイスロットを入れ替えるには スワップ するだけです。 これにより、待機スロットで Java や Tomcat をバージョンアップし、動作確認が取れたら スワップ するといったことも可能になります。 スワップ 時には、下記のように変更差分をわかりやすく表示してくれます。 また、待機スロットに GitHub Actionから継続的デリバリーし、動作確認してから スワップ で本番環境にアプリケーションを展開することもできます。 なお、今回の手順で待機スロットを使うためには、『 OpenID Connectによる認証』の再作成が必要です。スロットの設定を複製するときに認証設定も複製されているのですが、この認証設定はURLと結びついているためこのままでは動作しません。再作成で、待機スロット用の認証を構成しましょう。 GitHub Actionによる継続的デリバリー ここまでの作業で認証付きの待機スロットが準備でき、安全にアプリケーションをディプロイできる環境が整いました。では、 GitHub Actionによる継続的デリバリーを設定します。 継続的デリバリーは待機スロットにし、待機スロットでディプロイ後アプリケーションの動作確認して、 スワップ で運用スロットに展開するといったながれとなります。 「ディ プロイセン ター」で、 GitHub を選択し、レポジトリ、ブランチ情報を入力します。 そうすると、mvnを前提とした GitHub acitonの設定ファイルが自動的に GitHub にコミットされます。 が、本プロジェクトではgraldeを使用していたので、適宜修正をします。 これだけで継続的デリバリーが構成されます。 まとめ Azureでは、このように、 GUI によりポチポチするだけでこのような環境を作成できます。 とある案件では、今回の手順をキャプチャーベースでDevのメンバーに展開することにより 「Devで簡単に巻き取れそうなので横展開は今後Devでやっていきます」 というありがたいお言葉をいただくことができました。 Azureをうまく使うことにより、 Ops の関与を非常に減らした運用ができるのではないか?と思います。 Ops に負担をかけずに運用したいDevの方、構築・リリース作業で手一杯になっている Ops の方は、Azure Web Appsを使った環境構築を試してみてはいかがでしょうか 執筆: @miura.toshihiko 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
ISID X(クロス) イノベーション 本部 の三浦です。本記事は 電通国際情報サービス Advent Calendar 2021 の19日目のポストです。 本記事では、Azure Web Appsでは簡単にモダンな開発ができることをキャプチャーを多めで説明します。 GUI からポチポチ設定でここまで簡単にできるということをお伝えするのが目的です。 (あまりインフラに詳しくない アプリ開発 者でも簡単にインフラ構築、運用ができるかもしれないといった内容です) 触れる技術要素 今回の模擬システムの説明 今回の環境でできること OpenID Connectによる認証(Office 365のユーザーでお手軽認証) の構築 webappsのディプロイスロットによる、ブルーグリーンディプロイ GitHub Actionによる継続的デリバリー まとめ 触れる技術要素 Azure Web Apps OpenID Connectによる認証(Office 365のユーザーでお手軽Single Sign On) ディプロイスロットによるブルーグリーンディプロイ GitHub ActionによるContinuous Delivery 今回の模擬システムの説明 Azure Web Apps + cognitive searchのシンプルなシステムです。 Azure Web Appsの機能で、 OpenID ConnectでSingle Sign Onし、 GitHub から、ステージング環境へ GitHub Actionで継続的デリバリーし、 GUI 上の操作で簡易にブルーグリーンディプロイをします。また、app insightsによる APM 監視、および、log analytics によるログ管理をしています。 今回のエントリでは用いてる技術のうち「 OpenID Connectによる認証」「ディプロイスロットによるブルーグリーンディプロイ」「 GitHub Actionによる継続的デリバリー」を紹介します。 今回の環境でできること Azure Web Apps の OpenID Connect認証による簡単な Office 365ユーザーで Single Sign On(AzureAD) ブルーグリーンディプロイによる停止時間の短縮、障害時の高速切り戻し GitHub acitionによる継続的デリバリー OpenID Connectによる認証(Office 365のユーザーでお手軽認証) の構築 下記のように GUI 操作していくだけで、簡易にOffice 365のユーザーで認証が可能となります。 ここの設定を間違えると、認証してないユーザー、全世界のAzureADのユーザーにこのシステムが公開されてしまうので注意です。 このように設定すると、webapp側にRelying Party としての設定が完了、AzureAD側に OpenID Provider としてのリソースが作成されています。 では、AzureADに遷移し、 OpenID Providerとしての追加設定をします。 まず、プロパティを開き下記の項目を設定します。 デフォルトの「いいえ」の場合、そのテナントの全てのユーザーがwebapps にアクセス可能となります。「はい」の場合は後述の手順により、webapps単位でアクセスするユーザーを制限できます。 例えば、マルチテナント的に複数の顧客をゲストユーザーとして登録しているテナントで、「いいえ」で設定してしまうと、他社向けのシステムが見えてしまうといったことが起こります。 次に、このアプリケーションへのアクセス権を与えるユーザーを追加してます。 ここでポイントなのは、AzureADのゲストユーザーも追加可能であるということです。これは、他社のOffice 365ユーザーに対して、他社の管理者の作業なしにSingle Sign Onが可能だということです。顧客が普段使ってるOffice 365のユーザーでのSingle Sign Onが数分で構築できる強力な機能です。 また、社内向けシステムを検討する際にも、煩雑な情シスへのIDP関連の申請を省略して普段使っているOffice 365のID、MFAを用いたシステムが提供可能となります。 Single Sign Onの設定は開発者、ID管理側、双方に一定の知識が必要でトラブリやすい作業です。しかし、Azure Web Apps + azureADのゲストユーザーを用いることでこの作業を大幅に簡易化できます。 webappsのディプロイスロットによる、ブルーグリーンディプロイ では、次に、ブルーグリーンディプロイを設定します。Azure Web Apps には、ブルーグリーンディプロイを実現するための ディプロイスロット という仕組みがあります。 これを作成します。 「スロットの追加」を選択し、「次から設定を複製」で元のスロットを選ぶだけで、アプリケーション設定がコピーされます( 環境変数 、 java バージョン、 tomcat バージョンなど)。 また、待機スロット用のURL、サーバが作成されます。 できあがったディプロイスロットを入れ替えるには スワップ するだけです。 これにより、待機スロットで Java や Tomcat をバージョンアップし、動作確認が取れたら スワップ するといったことも可能になります。 スワップ 時には、下記のように変更差分をわかりやすく表示してくれます。 また、待機スロットに GitHub Actionから継続的デリバリーし、動作確認してから スワップ で本番環境にアプリケーションを展開することもできます。 なお、今回の手順で待機スロットを使うためには、『 OpenID Connectによる認証』の再作成が必要です。スロットの設定を複製するときに認証設定も複製されているのですが、この認証設定はURLと結びついているためこのままでは動作しません。再作成で、待機スロット用の認証を構成しましょう。 GitHub Actionによる継続的デリバリー ここまでの作業で認証付きの待機スロットが準備でき、安全にアプリケーションをディプロイできる環境が整いました。では、 GitHub Actionによる継続的デリバリーを設定します。 継続的デリバリーは待機スロットにし、待機スロットでディプロイ後アプリケーションの動作確認して、 スワップ で運用スロットに展開するといったながれとなります。 「ディ プロイセン ター」で、 GitHub を選択し、レポジトリ、ブランチ情報を入力します。 そうすると、mvnを前提とした GitHub acitonの設定ファイルが自動的に GitHub にコミットされます。 が、本プロジェクトではgraldeを使用していたので、適宜修正をします。 これだけで継続的デリバリーが構成されます。 まとめ Azureでは、このように、 GUI によりポチポチするだけでこのような環境を作成できます。 とある案件では、今回の手順をキャプチャーベースでDevのメンバーに展開することにより 「Devで簡単に巻き取れそうなので横展開は今後Devでやっていきます」 というありがたいお言葉をいただくことができました。 Azureをうまく使うことにより、 Ops の関与を非常に減らした運用ができるのではないか?と思います。 Ops に負担をかけずに運用したいDevの方、構築・リリース作業で手一杯になっている Ops の方は、Azure Web Appsを使った環境構築を試してみてはいかがでしょうか 執筆: @miura.toshihiko 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
本記事は 電通国際情報サービス Advent Calendar 2021 の18日目のポストです。 (本記事の太字部分は、 GitHub Copilotで補完された箇所です。その仕組みや感想などは記事の後半で説明します。) こんにちは。ISID X イノベーション 本部の半田です。普段は 社内向けの構成管理サービスの運用 をやっています。 今年の6月末、 GitHub は「関数名とコメントから、関数の ソースコード を補完する」機能、 GitHub Copilot のテクニカルプレビューを開始しました。 このCopilot、私もここ2ヶ月くらい使用しているのですが、最近 ソースコード だけではなく日本語も補完してくれることに気づきました。 そこで今日は、この GitHub Copilotにブログの記事を書いてみてもらおう と思います。 Copilotで使われている技術などの内容はありませんが、気休め程度にお付き合いください。 ※なお、以下のパラグラフでは GitHub Copilotによる補完をそのまま掲載します。以下の文章には事実関係の誤りや、日本語の揺れがあることをご容赦 ください。 GitHub Copilotに日本語を書いてもらった その1 まずは GitHub Copilotに、 GitHub Copilot自身の説明をしてもらいましょう。あくまで補完のためのツールなので、テーマや書き出しの文は私から与える必要があります。 はじめまして。今日は、 GitHub Copilotに日本語を書いてもらったのです。 GitHub Copilotは、 GitHub の補完機能を日本語で使えるようにしたものです。 みなさんは、 GitHub Copilotを使っているのですか?私は、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 ところで、 GitHub Copilotは、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 ということで、 GitHub Copilotに日本語を書いてもらったのです。 ありがとうございます。 GitHub Copilotによる補完部分は、以下の太字の部分でした。 はじめ まして。 今日は 、 GitHub Copilotに日本語を書いてもらったのです。 G itHub Copilotは、 GitHub の補完機能を日本語で使えるようにしたものです。 みなさん は、 GitHub Copilotを使っているのですか? 私は 、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 ところで 、 GitHub Copilotは、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 という ことで、 GitHub Copilotに日本語を書いてもらったのです。 あ りがとうございます。 私が与えた文章はとても少ないですが、なんだかそれっぽい日本語が生成されてきました。ただ、内容はとても薄いですし、なんだか日本語の補完ツールだという説明になっていますね。この記事の前段から学習したことを、ほとんどそのまま言っているようです。 一方で、文章のつながりはとても自然で驚きました。接続詞は私が与えていますが、例えば「ところで」に続くセンテンスとして「○○ですが、」という言葉がきていますね。ほかにも、「という(ことで)」に続くセンテンスとして要点を簡潔に 表現しているのです。 ちなみに補完はこのように表示されます。 その2 さすがに接続詞だけだと、文章の中身としてはほとんど空っぽですね。 次はもう少し意図を持って文章を与えてみましょう。普段書いている プログラミング言語 を紹介したいと思います。 こんにちは。 みなさんは、普段どのような プログラミング言語 を使っていますか? 私は、 Python とTypeScriptを使っています。主にWebアプリケーションを作っています。 ほかにも、 JavaScript や PHP なども使っています。 普段の仕事を便利にするための自動化もしています。 例えば、 GitHub の監査ログを収集して、 Google BigQueryに保存しています。 この収集では、RxJSを使っています。RxJSは、 JavaScript のPromiseをラップして、非同期処理を楽にするためのライブラリです。 ほかにも、社内システムから出力される 工数 の実績データを Google Sheetsに保存して、 Google Data Studioで統計を表示しています。 メンバーがどのような業務をしているのかを知りたいときなどに使います。 このような簡単な処理を自動化するのに、 Python は非常に便利です。 こちらも答え合わせです。 こんにちは。 みなさんは、普段どのような プログラミング言語 を使っていますか? 私は Python とType Scriptを使っています。 主にWeb アプリケーションを作っています。 ほかにも、 JavaScript や PHP なども使っています。 普段の仕事を便利にするための自動化 もしています。 例えば、 GitHub の監査ログを収集して、 Google B igQueryに保存しています。 この収集では、R xJSを使っています。 R xJSは、 JavaScript のPromiseをラップして、非同期処理を楽にするためのライブラリです。 ほかにも、社内システムから出力される 工数 の実績データを Google Sheetsに保存して、 Google D ata Studioで統計を表示しています。 メンバーがどのような 業務をしているのかを知りたいときなどに使います。 このような簡単な処理を自動化するのに、 Python は 非常に便利です。 いかがでしょうか。私は PHP をあまり書きませんし、RxJSを「Promiseをラップして、非同期処理を楽にするためのライブラリ」と説明するのは多少危険な気がします。それでもずいぶん正確に文章を構成できていますね。 例えば「自動化も しています 」という部分では、まるで前半の文章に情報を並立していることを理解しているかのように「も」という助詞を使用していますね。 GitHub Copilotに日本語を書いてもらって感じたこと GitHub Copilotの「クセ」 最初は「へえ、日本語も補完するんだ」という驚きをモチベーションに記事を書き始めましたが、ここまで書いてみて GitHub Copilotにはいくつかの癖があるように感じました。少しまとめます。 ファイル内の文章も学習する ソースコード もファイル内のコンテンツを学習しているそうですが、日本語も同じように学習しているようですね。文章の前半より後半の方が、補完の精度は高くなっていきます。 試しに新しいファイルを開いて同じ文章を入力してみましたが、得られる結果は異なるものでした。 文章内にあるキーワードや自分やチームが持っている書き方の「クセ」を、ちゃんと学習してくれているようです。 一文を短く終わらせる傾向にある どの文章も、表示される補完は一文が短く終わっているなと感じました。主語と述語を簡潔に結んでいるような印象です。 「傾向にある」と書いたのは、まれにとてつもなく長い補完を表示したり、同じ文章が何度もループして表示されたりする事があったためです。 私は一文を長く書いてしまう傾向にあるので、この補完を見て「そろそろ文章を終わらせなきゃ」と焦ることもしばしばありました。 与えられた接続詞に対して、適切な助詞を補ったセンテンスを補完している…? これは驚きましたね。接続詞にどのような文章を続けるかというのは、時に人間であっても難しいことがあります。 もちろん完全に正しいセンテンスが表示されるわけではないのですが、どうやって意味の通る文章を生成しているのかはとても興味深いですね。AIってすごいです。 どう付き合っていくか? 今回は日本語の補完を題材にしましたが、 ソースコード の補完であっても、今回の補完に感じた違和感や誤りと同じようなものがあると思われます。 言わずもがなではありますが、 GitHub Copilotによる補完は意味的に正しいものを確実に補完してくれるわけではないことを理解する必要があります。 日本語であればパッと見てその意味があっているかどうかを確認できることも多いですが、例えば ソースコード で条件分岐の式を書くときなど、正しく補完されているかを意識して確認した方が良さそうです。 また、補完は基本的に過去のコードを学習して、それらしきものを表示してくれるものです。同じようなコードを書くような状況と、まったく新しいコードを書くような状況では、精度も異なるはずです。 これらのことから、基本的には「stackoverflowやQiitaなどの ソースコード をコピー&ペースト」する時と同じように扱うと良いのかなと考えています。 そのコードは正常に コンパイル できるのか、ランタイムエラーはでないのか、要件は満たせているのかなどを丁寧に確認できるようであれば、 GitHub Copilotの補完は強力な相棒になってくれるでしょう。 日本語の補完に使うときは、文末を補完してもらうのが良いかなと思います。 文頭からの補完でセンテンスの意味的な整合性を確保するのは難しいですが、文末を少し補完してもらえるだけでも結構便利です。文末につける言葉のバリエーションに悩んだときも、ア イデア をもらえてありがたいですよ。 あるときは驚くような高い精度の補完を、またあるときはクスッと笑えるような補完を提示してくれる GitHub Copilotですが、引き続き Microsoft Office の「イルカ」のようにかわいがってみようかなと思いました。 あとがき 記事を書いている途中、 GitHub Copilotからは 「そうですね、日本語も補完するのは面倒だ」という苦痛を感じました。 という補完を出してくれました。タイミングが良すぎて笑っちゃいました。 公式ドキュメント では、日本語を学習しているとは明記されていません。もし万が一にも GitHub Copilotで日本語を書きたい時は、どうぞ自己責任でお願いします。 執筆: @handa.kenta 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
本記事は 電通国際情報サービス Advent Calendar 2021 の18日目のポストです。 (本記事の太字部分は、 GitHub Copilotで補完された箇所です。その仕組みや感想などは記事の後半で説明します。) こんにちは。ISID X イノベーション 本部の半田です。普段は 社内向けの構成管理サービスの運用 をやっています。 今年の6月末、 GitHub は「関数名とコメントから、関数の ソースコード を補完する」機能、 GitHub Copilot のテクニカルプレビューを開始しました。 このCopilot、私もここ2ヶ月くらい使用しているのですが、最近 ソースコード だけではなく日本語も補完してくれることに気づきました。 そこで今日は、この GitHub Copilotにブログの記事を書いてみてもらおう と思います。 Copilotで使われている技術などの内容はありませんが、気休め程度にお付き合いください。 ※なお、以下のパラグラフでは GitHub Copilotによる補完をそのまま掲載します。以下の文章には事実関係の誤りや、日本語の揺れがあることをご容赦 ください。 GitHub Copilotに日本語を書いてもらった その1 まずは GitHub Copilotに、 GitHub Copilot自身の説明をしてもらいましょう。あくまで補完のためのツールなので、テーマや書き出しの文は私から与える必要があります。 はじめまして。今日は、 GitHub Copilotに日本語を書いてもらったのです。 GitHub Copilotは、 GitHub の補完機能を日本語で使えるようにしたものです。 みなさんは、 GitHub Copilotを使っているのですか?私は、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 ところで、 GitHub Copilotは、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 ということで、 GitHub Copilotに日本語を書いてもらったのです。 ありがとうございます。 GitHub Copilotによる補完部分は、以下の太字の部分でした。 はじめ まして。 今日は 、 GitHub Copilotに日本語を書いてもらったのです。 G itHub Copilotは、 GitHub の補完機能を日本語で使えるようにしたものです。 みなさん は、 GitHub Copilotを使っているのですか? 私は 、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 ところで 、 GitHub Copilotは、 GitHub の補完機能を使っているのですが、その補完機能は日本語を使えるようになったのです。 という ことで、 GitHub Copilotに日本語を書いてもらったのです。 あ りがとうございます。 私が与えた文章はとても少ないですが、なんだかそれっぽい日本語が生成されてきました。ただ、内容はとても薄いですし、なんだか日本語の補完ツールだという説明になっていますね。この記事の前段から学習したことを、ほとんどそのまま言っているようです。 一方で、文章のつながりはとても自然で驚きました。接続詞は私が与えていますが、例えば「ところで」に続くセンテンスとして「○○ですが、」という言葉がきていますね。ほかにも、「という(ことで)」に続くセンテンスとして要点を簡潔に 表現しているのです。 ちなみに補完はこのように表示されます。 その2 さすがに接続詞だけだと、文章の中身としてはほとんど空っぽですね。 次はもう少し意図を持って文章を与えてみましょう。普段書いている プログラミング言語 を紹介したいと思います。 こんにちは。 みなさんは、普段どのような プログラミング言語 を使っていますか? 私は、 Python とTypeScriptを使っています。主にWebアプリケーションを作っています。 ほかにも、 JavaScript や PHP なども使っています。 普段の仕事を便利にするための自動化もしています。 例えば、 GitHub の監査ログを収集して、 Google BigQueryに保存しています。 この収集では、RxJSを使っています。RxJSは、 JavaScript のPromiseをラップして、非同期処理を楽にするためのライブラリです。 ほかにも、社内システムから出力される 工数 の実績データを Google Sheetsに保存して、 Google Data Studioで統計を表示しています。 メンバーがどのような業務をしているのかを知りたいときなどに使います。 このような簡単な処理を自動化するのに、 Python は非常に便利です。 こちらも答え合わせです。 こんにちは。 みなさんは、普段どのような プログラミング言語 を使っていますか? 私は Python とType Scriptを使っています。 主にWeb アプリケーションを作っています。 ほかにも、 JavaScript や PHP なども使っています。 普段の仕事を便利にするための自動化 もしています。 例えば、 GitHub の監査ログを収集して、 Google B igQueryに保存しています。 この収集では、R xJSを使っています。 R xJSは、 JavaScript のPromiseをラップして、非同期処理を楽にするためのライブラリです。 ほかにも、社内システムから出力される 工数 の実績データを Google Sheetsに保存して、 Google D ata Studioで統計を表示しています。 メンバーがどのような 業務をしているのかを知りたいときなどに使います。 このような簡単な処理を自動化するのに、 Python は 非常に便利です。 いかがでしょうか。私は PHP をあまり書きませんし、RxJSを「Promiseをラップして、非同期処理を楽にするためのライブラリ」と説明するのは多少危険な気がします。それでもずいぶん正確に文章を構成できていますね。 例えば「自動化も しています 」という部分では、まるで前半の文章に情報を並立していることを理解しているかのように「も」という助詞を使用していますね。 GitHub Copilotに日本語を書いてもらって感じたこと GitHub Copilotの「クセ」 最初は「へえ、日本語も補完するんだ」という驚きをモチベーションに記事を書き始めましたが、ここまで書いてみて GitHub Copilotにはいくつかの癖があるように感じました。少しまとめます。 ファイル内の文章も学習する ソースコード もファイル内のコンテンツを学習しているそうですが、日本語も同じように学習しているようですね。文章の前半より後半の方が、補完の精度は高くなっていきます。 試しに新しいファイルを開いて同じ文章を入力してみましたが、得られる結果は異なるものでした。 文章内にあるキーワードや自分やチームが持っている書き方の「クセ」を、ちゃんと学習してくれているようです。 一文を短く終わらせる傾向にある どの文章も、表示される補完は一文が短く終わっているなと感じました。主語と述語を簡潔に結んでいるような印象です。 「傾向にある」と書いたのは、まれにとてつもなく長い補完を表示したり、同じ文章が何度もループして表示されたりする事があったためです。 私は一文を長く書いてしまう傾向にあるので、この補完を見て「そろそろ文章を終わらせなきゃ」と焦ることもしばしばありました。 与えられた接続詞に対して、適切な助詞を補ったセンテンスを補完している…? これは驚きましたね。接続詞にどのような文章を続けるかというのは、時に人間であっても難しいことがあります。 もちろん完全に正しいセンテンスが表示されるわけではないのですが、どうやって意味の通る文章を生成しているのかはとても興味深いですね。AIってすごいです。 どう付き合っていくか? 今回は日本語の補完を題材にしましたが、 ソースコード の補完であっても、今回の補完に感じた違和感や誤りと同じようなものがあると思われます。 言わずもがなではありますが、 GitHub Copilotによる補完は意味的に正しいものを確実に補完してくれるわけではないことを理解する必要があります。 日本語であればパッと見てその意味があっているかどうかを確認できることも多いですが、例えば ソースコード で条件分岐の式を書くときなど、正しく補完されているかを意識して確認した方が良さそうです。 また、補完は基本的に過去のコードを学習して、それらしきものを表示してくれるものです。同じようなコードを書くような状況と、まったく新しいコードを書くような状況では、精度も異なるはずです。 これらのことから、基本的には「stackoverflowやQiitaなどの ソースコード をコピー&ペースト」する時と同じように扱うと良いのかなと考えています。 そのコードは正常に コンパイル できるのか、ランタイムエラーはでないのか、要件は満たせているのかなどを丁寧に確認できるようであれば、 GitHub Copilotの補完は強力な相棒になってくれるでしょう。 日本語の補完に使うときは、文末を補完してもらうのが良いかなと思います。 文頭からの補完でセンテンスの意味的な整合性を確保するのは難しいですが、文末を少し補完してもらえるだけでも結構便利です。文末につける言葉のバリエーションに悩んだときも、ア イデア をもらえてありがたいですよ。 あるときは驚くような高い精度の補完を、またあるときはクスッと笑えるような補完を提示してくれる GitHub Copilotですが、引き続き Microsoft Office の「イルカ」のようにかわいがってみようかなと思いました。 あとがき 記事を書いている途中、 GitHub Copilotからは 「そうですね、日本語も補完するのは面倒だ」という苦痛を感じました。 という補完を出してくれました。タイミングが良すぎて笑っちゃいました。 公式ドキュメント では、日本語を学習しているとは明記されていません。もし万が一にも GitHub Copilotで日本語を書きたい時は、どうぞ自己責任でお願いします。 執筆: @handa.kenta 、レビュー: @sato.taichi ( Shodo で執筆されました )
アバター
これは 電通国際情報サービス アドベントカレンダー の17日目の記事です。 はじめに 開発環境 ValueObject(値オブジェクト)とは? 実装方針のアプローチ 構造体として実装し、フィールドに値を保持し、必要な機能をメソッドで外部公開する defined typeとして実装し、必要な機能をメソッドで外部公開する 両アプローチの考察 データベースへの永続化 Value関数 Scan関数 実際にRDBに永続化してみる 値オブジェクトの実装 DB永続化処理 終わりに はじめに みなさんこんにちは。 電通国際情報サービス (ISID) 金融ソリューション事業部の水野です。 今回は、値オブジェクトをgo言語でどのように実装したかをご紹介します。 値の生成から振る舞いの実装、データベースへの永続化の際に如何に透過的に扱うかまでを見ていきます。 開発環境 Visual Studio Code 1.62.2 go 1.17 Docker Desktop 4.2.0 (内包されているDocker CLI を利用) 1 ValueObject(値オブジェクト)とは? Martin Fowlerさんの エンタープライズ アプリケーションアーキテクチャパターン で紹介されたのが最初だと理解しています。 「値オブジェクトとは何か」についての深堀りも非常に興味深いテーマですが、今回は以下と定義します。 イミュータブルである 不変条件が定義されており、条件を満たさない値では生成できない 特定の属性で等価性が定義される 値だけでなく、自身に属する機能を公開する 実装方針のアプローチ 以下の2つのアプローチを検討しました。 構造体 として実装し、フィールドに値を保持し、必要な機能をメソッドで外部公開する defined type で実装し、必要な機能をメソッドで外部公開する 上記どちらのアプローチでも、共通する戦略は以下です。 生成時にバリデーションを行い、エラー時は値を生成せずにerrorを返す 真偽値を返す等価性判定関数を実装する 自身が保持する値を使った何らかの操作を、メソッドで外部公開する イミュータブルに実装する方法は、構造体の場合はフィールドをエクスポートせず、値取得用のメソッドを設けて実現します。 defined typeとして実装する場合、基底にする型がイミュータブルかどうかに依存します。 構造体の場合、ボイラープレートとして値取得メソッドが毎回必要になるため、割り切ってフィールドをエクスポートしても良いと考えます。 その他の違いとしては、構造体だと複数の値を保持しやすいですが、defined typeだと単一の値の取り扱いが基本になります。 defined typeでも別の変数を保持することは可能ですが、やはりstringのような型として扱いたくなるでしょう。 例として、算術演算や税率算出などの機能を持つ「価格」という値オブジェクトを1.と2.で考えてみます。 構造体として実装し、フィールドに値を保持し、必要な機能をメソッドで外部公開する type Price struct { value int64 } func NewPrice(v int64 ) (Price, error ) { if err := isValidPrice(v); err != nil { ・・・エラーの場合の処理 } func isValidPrice(v int64 ) error { ...値の範囲チェックなど } func (price Price) Equal(other Price) bool { return price.value == other.value } func (price Price) CalcTax(rate uint ) Tax { ...省略} func (price Price) calcInternal(rate uint ) Tax { ...省略} // 内部処理用 value という構造体のフィールドに値を格納します。 defined typeとして実装し、必要な機能をメソッドで外部公開する type Price int64 func NewPrice(v int64 ) (Price, error ) { if err := isValidPrice(v); err != nil { ・・・エラーの場合の処理 } func isValidPrice(v int64 ) error { ...値の範囲チェックなど } func (price Price) Equal(other Price) bool { return int64 (price) == int64 (other) } func (price Price) CalcTax(rate uint ) Tax { ...省略} func (price Price) calcInternal(rate uint ) Tax { ...省略} // 内部処理用 両アプローチの考察 構造体による実装だと、Entityのような他の構造体のフィールドとして使用すると、構造体がネストすることになります。 今回は、 RDB への永続化時のシンプルさや、 JSON フィールドとして用いる際の取りまわしやすさから、defined typeで実装し必要な機能をメソッドで外部公開する方式を選択しました。 また、ValueObjectとして等価判定メソッドを実装する上では、以下のようなインタフェースを導入しても良いでしょう。 type ValueObject interface { Equal(other interface {}) bool } go 1.17の時点では ジェネリクス が未導入のため、上記のインタフェースでは毎回キャストする必要があります。 func (price Price) Equal(other interface {}) bool { val, ok := other.(Price) return ok && int64 (price) == int64 (val) } 余談ですが、レシーバの変数名は https://github.com/golang/go/wiki/CodeReviewComments#receiver-names では「It can be very short」と、非常に短い略称が理由と共に推奨されています。 ですが、レシーバ変数は関数内のローカル変数のスコープとしては最も広いものとなります。 また、ループカウンタの i や j と重複して意図せぬ シャドウイング を誘発したりと、短い変数名がゆえの弊害もあります。 そのため、レシーバの変数名には1, 2文字の略称ではなく、意味のある名称を割り当てています。 データベースへの永続化 defined type として実装する場合、基底型によっては意図した永続化が出来ず、動作をカスタムしたくなるケースがあります。 その場合、 Valuer インタフェースと Scanner インタフェースを実装します。 Valuerインタフェースの Value 関数は、ドライバから呼び出され、DBカラム型に対応する型に変換する役割を担います。 ScannerインタフェースのScan関数は、DBから取得したカラムの値を自身型に変換するための関数で、定められた特定の型から、ValueObject型への変換を実装します。 では、具体的に見ていきましょう。 Value 関数 シグニチャ は Value() (Value, error) と、非常にシンプルです。 前出のPrice型で実装するなら以下となります。 func (price Price) Value() (driver.Value, error ) { val := int64 (price) if val == - 1 { return NaNPrice, エラーオブジェクト } return int64 (price), nil } 上述のような実装にする必要はありませんが、int64のようなビルトイン型ではなく、固定小数10進表現を利用するケースなども考えられるため、例として無効な値が入っていた場合に無効値とerrorを返す疑似コードにしています。 Scan関数 Scan関数は若干特殊な仕組みで、ポインタを介して値をやり取りします。 そのため、ポインタレシーバとして実装する必要があります。 func (price *Price) Scan(value interface {}) error { switch v := value.( type ) { case int64 : if p, err := NewPrice(v); err == nil { *price = p return nil } else { return err } default : return errInvalidPrice } } 実際に RDB に永続化してみる RDB はPostgresSQLを使います。 今回は、Docker Desktopに内包されているDocker CLI で、最新の公式イメージでホストしつつ、デフォルトのpostgres スキーマ を使いました。 実際の開発では、アプリケーション用の スキーマ を別途用意する方が良いでしょう。 詳細は割愛しますが、 docker pull でイメージをダウンロードした後、 docker run するだけですぐに使えます。 PostgreSQL に接続し、非常にシンプルな以下の単価テーブルを作成します。 CREATE TABLE unit_price ( id serial PRIMARY KEY , lower_price bigint NOT NULL , upper_price bigint NOT NULL ); これで準備が整いました。サンプルなので、テーブル物理設計の妥当性は無視します。 PostgresSQLに永続化するにあたって、ドライバには pgx を利用します。 他のドライバに lib/pq がありますが、現在メンテナンスモードに入っており、公式ドキュメントではアクティブにメンテナンスされているpgxの利用が推奨( 2 )されています。 PostgreSQL を使うなら、今はpgxを使うのが良いでしょう。 実装した値オブジェクト Price を永続化するコードサンプルです。 税の算出などの機能を持たせていますが、紙面の都合上 RateやTaxの実装は省略しています。 値オブジェクトの実装 const ( minPrice, maxPrice = 0 , 9999999999999 NaNPrice = Price(- 1 ) ) type Price int64 func NewPrice(v int64 ) (Price, error ) { if err := isValidPrice(v); err != nil { return NaNPrice, err } return Price(v), nil } var ( errOutOfRangePrice = errors.New( "Price is out of range" ) errInvalidPrice = errors.New( "invalid Price" ) ) func isValidPrice(v int64 ) error { if v < minPrice && maxPrice < v { return fmt.Errorf( "Price must be between %d and %d, but [%d]: %w" , minPrice, maxPrice, v, errOutOfRangePrice) } return nil } func (price Price) Equal(other Price) bool { return int64 (price) == int64 (other) } func (price Price) String() string { return strconv.FormatInt( int64 (price), 10 ) } func (price Price) CalcTax(rate Rate) Tax { tax := float64 (price) * rate.AsPercentage() return NewTax(math.Floor(tax)) } func (price Price) Value() (driver.Value, error ) { if err := isValidPrice( int64 (price)); err != nil { return NaNPrice, err } return int64 (price), nil } func (price *Price) Scan(value interface {}) error { switch v := value.( type ) { case int64 : if p, err := NewPrice(v); err == nil { *price = p return nil } else { return fmt.Errorf( "invalid value [%v]: %w" , v, errOutOfRangePrice) } default : return errInvalidPrice } } func (price Price) EncodeBinary(ci *pgtype.ConnInfo, buf [] byte ) ([] byte , error ) { var numeric pgtype.Int8 if err := numeric.Set( int64 (price)); err != nil { return nil , err } return numeric.EncodeBinary(ci, buf) } Equal、String、CalcTaxというメソッドを定義しているのが分かりますね。 以下、実装におけるポイントを記します。 CalcTaxが ドメイン 固有処理 Value 、Scanはデータベースアクセスのために必要なメソッド pgx でカスタムされた型を使用するにはEncodeBinaryが必要 ( 3 参考) なため実装している pgxドライバを使うために、 github .com/jackc/pgx/v4と github .com/jackc/pgtypeを go getしている DB永続化処理 値オブジェクトを PostgreSQL に永続化する実装です。(エラー処理は意図的に省略しています) func main() { ctx := context.TODO() conn, _ := Connect(ctx, "postgres://postgres:testpass1@localhost:5432/postgres" ) defer func () { conn.Close(ctx) }() argLower, _ := value.NewPrice( 1000 ) argUpper, _ := value.NewPrice( 2500 ) conn.Exec(ctx, "INSERT INTO unit_price (lower_price, upper_price) VALUES ($1, $2)" , argLower, argUpper) // (1)挿入 rows, _ := conn.Query(ctx, "SELECT lower_price, upper_price from unit_price" ) // (2)選択 defer func () { rows.Close() }() for rows.Next() { var lower, upper value.Price rows.Scan(&lower, &upper) // (3)変数へ読み込み fmt.Printf( "lowerPrice=[%s], upperPrice=[%s]" , lower.String(), upper.String()) } } func Connect(ctx context.Context, connString string ) (*pgx.Conn, error ) { if conn, err := pgx.Connect(ctx, connString); err != nil { return nil , err } else { return conn, nil } } コード中の(1)で、値オブジェクトを引数にデータベースにINSERT文を発行しています。 デバッグ すると、以下のようにEncodeBinaryメソッドが呼び出されることが分かります。 (2)では、データベースから値を取得し、(3)でPrice値オブジェクトに読み込んでいます。 ここでは、Scanメソッドがドライバから呼び出されます。 構造体と結果セットを マッピング するライブラリを使えば、Entity構造体として以下のような実装も可能になります。 type UnitPrice struct { Id string `db:"id"` LowerPrice value.Price `db:"lower_price"` UpperPrice value.Price `db:"upper_price"` } 終わりに go言語で値オブジェクトを実装するための、一つの方法を紹介させていただきました。 全てstring, intなどのbuiltin型でいく戦略もあるので、これが正解と言うものではありません。 これからも試行錯誤しつつ、goでいろいろなアプリケーションを実装していきたいと思います。 最後までご覧になっていただき、誠にありがとうございました。 執筆: @mizuno.kazuhiro 、レビュー: @sato.taichi ( Shodo で執筆されました ) 個人利用は問題ありませんが、条件によっては2022年2月1日以降、有料になります。 https://www.docker.com/blog/updating-product-subscriptions/ ↩ https://github.com/lib/pq/blob/master/README.md#status ↩ https://pkg.go.dev/github.com/jackc/pgtype#example-package-CustomCompositeTypes ↩
アバター
これは 電通国際情報サービス アドベントカレンダー の17日目の記事です。 はじめに 開発環境 ValueObject(値オブジェクト)とは? 実装方針のアプローチ 構造体として実装し、フィールドに値を保持し、必要な機能をメソッドで外部公開する defined typeとして実装し、必要な機能をメソッドで外部公開する 両アプローチの考察 データベースへの永続化 Value関数 Scan関数 実際にRDBに永続化してみる 値オブジェクトの実装 DB永続化処理 終わりに はじめに みなさんこんにちは。 電通国際情報サービス (ISID) 金融ソリューション事業部の水野です。 今回は、値オブジェクトをgo言語でどのように実装したかをご紹介します。 値の生成から振る舞いの実装、データベースへの永続化の際に如何に透過的に扱うかまでを見ていきます。 開発環境 Visual Studio Code 1.62.2 go 1.17 Docker Desktop 4.2.0 (内包されているDocker CLI を利用) 1 ValueObject(値オブジェクト)とは? Martin Fowlerさんの エンタープライズ アプリケーションアーキテクチャパターン で紹介されたのが最初だと理解しています。 「値オブジェクトとは何か」についての深堀りも非常に興味深いテーマですが、今回は以下と定義します。 イミュータブルである 不変条件が定義されており、条件を満たさない値では生成できない 特定の属性で等価性が定義される 値だけでなく、自身に属する機能を公開する 実装方針のアプローチ 以下の2つのアプローチを検討しました。 構造体 として実装し、フィールドに値を保持し、必要な機能をメソッドで外部公開する defined type で実装し、必要な機能をメソッドで外部公開する 上記どちらのアプローチでも、共通する戦略は以下です。 生成時にバリデーションを行い、エラー時は値を生成せずにerrorを返す 真偽値を返す等価性判定関数を実装する 自身が保持する値を使った何らかの操作を、メソッドで外部公開する イミュータブルに実装する方法は、構造体の場合はフィールドをエクスポートせず、値取得用のメソッドを設けて実現します。 defined typeとして実装する場合、基底にする型がイミュータブルかどうかに依存します。 構造体の場合、ボイラープレートとして値取得メソッドが毎回必要になるため、割り切ってフィールドをエクスポートしても良いと考えます。 その他の違いとしては、構造体だと複数の値を保持しやすいですが、defined typeだと単一の値の取り扱いが基本になります。 defined typeでも別の変数を保持することは可能ですが、やはりstringのような型として扱いたくなるでしょう。 例として、算術演算や税率算出などの機能を持つ「価格」という値オブジェクトを1.と2.で考えてみます。 構造体として実装し、フィールドに値を保持し、必要な機能をメソッドで外部公開する type Price struct { value int64 } func NewPrice(v int64 ) (Price, error ) { if err := isValidPrice(v); err != nil { ・・・エラーの場合の処理 } func isValidPrice(v int64 ) error { ...値の範囲チェックなど } func (price Price) Equal(other Price) bool { return price.value == other.value } func (price Price) CalcTax(rate uint ) Tax { ...省略} func (price Price) calcInternal(rate uint ) Tax { ...省略} // 内部処理用 value という構造体のフィールドに値を格納します。 defined typeとして実装し、必要な機能をメソッドで外部公開する type Price int64 func NewPrice(v int64 ) (Price, error ) { if err := isValidPrice(v); err != nil { ・・・エラーの場合の処理 } func isValidPrice(v int64 ) error { ...値の範囲チェックなど } func (price Price) Equal(other Price) bool { return int64 (price) == int64 (other) } func (price Price) CalcTax(rate uint ) Tax { ...省略} func (price Price) calcInternal(rate uint ) Tax { ...省略} // 内部処理用 両アプローチの考察 構造体による実装だと、Entityのような他の構造体のフィールドとして使用すると、構造体がネストすることになります。 今回は、 RDB への永続化時のシンプルさや、 JSON フィールドとして用いる際の取りまわしやすさから、defined typeで実装し必要な機能をメソッドで外部公開する方式を選択しました。 また、ValueObjectとして等価判定メソッドを実装する上では、以下のようなインタフェースを導入しても良いでしょう。 type ValueObject interface { Equal(other interface {}) bool } go 1.17の時点では ジェネリクス が未導入のため、上記のインタフェースでは毎回キャストする必要があります。 func (price Price) Equal(other interface {}) bool { val, ok := other.(Price) return ok && int64 (price) == int64 (val) } 余談ですが、レシーバの変数名は https://github.com/golang/go/wiki/CodeReviewComments#receiver-names では「It can be very short」と、非常に短い略称が理由と共に推奨されています。 ですが、レシーバ変数は関数内のローカル変数のスコープとしては最も広いものとなります。 また、ループカウンタの i や j と重複して意図せぬ シャドウイング を誘発したりと、短い変数名がゆえの弊害もあります。 そのため、レシーバの変数名には1, 2文字の略称ではなく、意味のある名称を割り当てています。 データベースへの永続化 defined type として実装する場合、基底型によっては意図した永続化が出来ず、動作をカスタムしたくなるケースがあります。 その場合、 Valuer インタフェースと Scanner インタフェースを実装します。 Valuerインタフェースの Value 関数は、ドライバから呼び出され、DBカラム型に対応する型に変換する役割を担います。 ScannerインタフェースのScan関数は、DBから取得したカラムの値を自身型に変換するための関数で、定められた特定の型から、ValueObject型への変換を実装します。 では、具体的に見ていきましょう。 Value 関数 シグニチャ は Value() (Value, error) と、非常にシンプルです。 前出のPrice型で実装するなら以下となります。 func (price Price) Value() (driver.Value, error ) { val := int64 (price) if val == - 1 { return NaNPrice, エラーオブジェクト } return int64 (price), nil } 上述のような実装にする必要はありませんが、int64のようなビルトイン型ではなく、固定小数10進表現を利用するケースなども考えられるため、例として無効な値が入っていた場合に無効値とerrorを返す疑似コードにしています。 Scan関数 Scan関数は若干特殊な仕組みで、ポインタを介して値をやり取りします。 そのため、ポインタレシーバとして実装する必要があります。 func (price *Price) Scan(value interface {}) error { switch v := value.( type ) { case int64 : if p, err := NewPrice(v); err == nil { *price = p return nil } else { return err } default : return errInvalidPrice } } 実際に RDB に永続化してみる RDB はPostgresSQLを使います。 今回は、Docker Desktopに内包されているDocker CLI で、最新の公式イメージでホストしつつ、デフォルトのpostgres スキーマ を使いました。 実際の開発では、アプリケーション用の スキーマ を別途用意する方が良いでしょう。 詳細は割愛しますが、 docker pull でイメージをダウンロードした後、 docker run するだけですぐに使えます。 PostgreSQL に接続し、非常にシンプルな以下の単価テーブルを作成します。 CREATE TABLE unit_price ( id serial PRIMARY KEY , lower_price bigint NOT NULL , upper_price bigint NOT NULL ); これで準備が整いました。サンプルなので、テーブル物理設計の妥当性は無視します。 PostgresSQLに永続化するにあたって、ドライバには pgx を利用します。 他のドライバに lib/pq がありますが、現在メンテナンスモードに入っており、公式ドキュメントではアクティブにメンテナンスされているpgxの利用が推奨( 2 )されています。 PostgreSQL を使うなら、今はpgxを使うのが良いでしょう。 実装した値オブジェクト Price を永続化するコードサンプルです。 税の算出などの機能を持たせていますが、紙面の都合上 RateやTaxの実装は省略しています。 値オブジェクトの実装 const ( minPrice, maxPrice = 0 , 9999999999999 NaNPrice = Price(- 1 ) ) type Price int64 func NewPrice(v int64 ) (Price, error ) { if err := isValidPrice(v); err != nil { return NaNPrice, err } return Price(v), nil } var ( errOutOfRangePrice = errors.New( "Price is out of range" ) errInvalidPrice = errors.New( "invalid Price" ) ) func isValidPrice(v int64 ) error { if v < minPrice && maxPrice < v { return fmt.Errorf( "Price must be between %d and %d, but [%d]: %w" , minPrice, maxPrice, v, errOutOfRangePrice) } return nil } func (price Price) Equal(other Price) bool { return int64 (price) == int64 (other) } func (price Price) String() string { return strconv.FormatInt( int64 (price), 10 ) } func (price Price) CalcTax(rate Rate) Tax { tax := float64 (price) * rate.AsPercentage() return NewTax(math.Floor(tax)) } func (price Price) Value() (driver.Value, error ) { if err := isValidPrice( int64 (price)); err != nil { return NaNPrice, err } return int64 (price), nil } func (price *Price) Scan(value interface {}) error { switch v := value.( type ) { case int64 : if p, err := NewPrice(v); err == nil { *price = p return nil } else { return fmt.Errorf( "invalid value [%v]: %w" , v, errOutOfRangePrice) } default : return errInvalidPrice } } func (price Price) EncodeBinary(ci *pgtype.ConnInfo, buf [] byte ) ([] byte , error ) { var numeric pgtype.Int8 if err := numeric.Set( int64 (price)); err != nil { return nil , err } return numeric.EncodeBinary(ci, buf) } Equal、String、CalcTaxというメソッドを定義しているのが分かりますね。 以下、実装におけるポイントを記します。 CalcTaxが ドメイン 固有処理 Value 、Scanはデータベースアクセスのために必要なメソッド pgx でカスタムされた型を使用するにはEncodeBinaryが必要 ( 3 参考) なため実装している pgxドライバを使うために、 github .com/jackc/pgx/v4と github .com/jackc/pgtypeを go getしている DB永続化処理 値オブジェクトを PostgreSQL に永続化する実装です。(エラー処理は意図的に省略しています) func main() { ctx := context.TODO() conn, _ := Connect(ctx, "postgres://postgres:testpass1@localhost:5432/postgres" ) defer func () { conn.Close(ctx) }() argLower, _ := value.NewPrice( 1000 ) argUpper, _ := value.NewPrice( 2500 ) conn.Exec(ctx, "INSERT INTO unit_price (lower_price, upper_price) VALUES ($1, $2)" , argLower, argUpper) // (1)挿入 rows, _ := conn.Query(ctx, "SELECT lower_price, upper_price from unit_price" ) // (2)選択 defer func () { rows.Close() }() for rows.Next() { var lower, upper value.Price rows.Scan(&lower, &upper) // (3)変数へ読み込み fmt.Printf( "lowerPrice=[%s], upperPrice=[%s]" , lower.String(), upper.String()) } } func Connect(ctx context.Context, connString string ) (*pgx.Conn, error ) { if conn, err := pgx.Connect(ctx, connString); err != nil { return nil , err } else { return conn, nil } } コード中の(1)で、値オブジェクトを引数にデータベースにINSERT文を発行しています。 デバッグ すると、以下のようにEncodeBinaryメソッドが呼び出されることが分かります。 (2)では、データベースから値を取得し、(3)でPrice値オブジェクトに読み込んでいます。 ここでは、Scanメソッドがドライバから呼び出されます。 構造体と結果セットを マッピング するライブラリを使えば、Entity構造体として以下のような実装も可能になります。 type UnitPrice struct { Id string `db:"id"` LowerPrice value.Price `db:"lower_price"` UpperPrice value.Price `db:"upper_price"` } 終わりに go言語で値オブジェクトを実装するための、一つの方法を紹介させていただきました。 全てstring, intなどのbuiltin型でいく戦略もあるので、これが正解と言うものではありません。 これからも試行錯誤しつつ、goでいろいろなアプリケーションを実装していきたいと思います。 最後までご覧になっていただき、誠にありがとうございました。 執筆: @mizuno.kazuhiro 、レビュー: @sato.taichi ( Shodo で執筆されました ) 個人利用は問題ありませんが、条件によっては2022年2月1日以降、有料になります。 https://www.docker.com/blog/updating-product-subscriptions/ ↩ https://github.com/lib/pq/blob/master/README.md#status ↩ https://pkg.go.dev/github.com/jackc/pgtype#example-package-CustomCompositeTypes ↩
アバター