TECH PLAY

NTTドコモビジネス

NTTドコモビジネス の技術ブログ

602

サマリ Single-AS で構成されるネットワークにおいて、 SR-MPLS + VPNv4 + explicit path による Traffic Engineering (TE) を実現 IOS XR + Junos の Multi-vendor 環境で動作検証に成功 この記事は Multi-AS Segment Routing 検証連載の第 4 回です。目次は こちら 概要 イノベーションセンターの竹中です。普段の業務では Multi-AS Segment Routing に関する技術検証や、ネットワーク運用効率化のためのコントローラー開発などを行なっています。 本記事では Single-AS において SR-MPLS を用いた TE についてご紹介します。 第 0 回の記事 でもご紹介した通り、我々は Multi-AS/Multi-domain SR-MPLS における VPN と TE の実現手法を検証しています。 第 1 回の記事 、 第 2 回の記事 、 第 3 回の記事 を通して、 Multi-AS/Multi-domain SR-MPLS における L3VPN、L2VPN について説明、検証しました。 本記事では SR を用いた TE 手法についてご紹介する上で、まずは TE に必要となる SR Policy について説明します。 その後に 第 1 回の記事 で作成した Single-AS アンダーレイでの Color-Based Steering の手法を、サンプルトポロジーや設定例を添えながら紹介します。 Multi-AS での TE は第 5 回の記事にて紹介する予定です。 SR Policy SR Policy とは SR Policy 1 とは、SR で構成されたネットワークにおいて、運用者の意図に基づいたソースルーティングを行うためポリシーであり、下記の3つの要素で構成されます。 SR domain の Tailend である endpoint TE 経路を指定する Segment List ポリシーと経路を紐づける識別子である color SR Policy の適用方法はいくつか存在しますが、本記事においては MP-BGP で広告された VPN 経路に対して SR Policy を適用する流れを紹介します。 VPN 経路への SR Policy の適用方法としては、 SR Policy を適用対象の color と endpoint を指定して定義しておき、 BGP で受け取った経路のうち Color Extended Community と nexthop の組み合わせが一致する経路に適用する方法があります。 BGP の color はネットワークの運用意図の伝達として用いることができ、同じ endpoint に対して color ごとに異なる SR Policy を定義可能です。このように、経路に付加された color に応じて SR Policy を適用する TE 手法を Color-Based Steering と呼びます。 本記事においては color と endpoint を指定して SR Policy を適用しますが、color のみを指定して endpoint を動的に評価し SR Policy をインスタンス化する On-Demand Next-Hop (ODN) と呼ばれる技術も存在します。ただし、この技術は本記事の検証トポロジーでは使わないため、後続の記事にて紹介・検証します。 explicit path と dynamic path SR Policy の持つ経路は、その生成手法によって explicit path と dynamic path に分類されます。 explicit path は、Segment List をあらかじめ設定し、静的に経路を指定します。 一方で dynamic path は制約に基づいた Segment List の計算方法を表現するもので、SR Policy の設定時やトポロジー、メトリックなどの状態変更時に Segment List を動的に計算します。 本記事においては、TE の実現方法として explcit path を用いた検証を紹介します。 dynamic path を用いた検証例は後続の記事をご参照ください。また、我々の dynamic path に関する取り組みについては NTT Tech Conference 2022 や ENOG74 でも発表しています。 color の仕組みと利用例 BGP で経路を export または import する際に Color Extended Community を経路に付加することで、その経路への SR Policy 適用の可否、ならびに適用する SR Policy の決定が可能です。 BGP 経路に対する Color Extended Community の付加範囲は様々です。特定のネットワークの経路に対して付加することで当該ネットワークのみ挙動を変えたり、特定の Route-Distinguisher (RD) や Route-Target (RT) を持つ経路情報に対して付加することで当該 VRF 全体の挙動を変えたりできます。 上記の図は Color-Based Steering の模式図で、Single-AS の SR-MPLS において VPN 経路ごとの TE を実装する様子を表しています。なお、TE で利用するラベルのみ図中に明記し、各顧客を示す VPN ラベルは省略しています。 SR Policy に従った TE までの手順は以下の通りです。 ingress PE (PE1) にて SR Policy を設定する egress PE (PE2) で IBGP による Customer A、B の経路を IBGP で広告する 広告の際に各 VRF の RD ごとに Color Extended Community を付加する IBGP によって広告された経路を PE1 で受け取り、各 VRF table に import する import の際に経路に対して color、endpoint の一致する SR Policy を適用する SR Policy を適用した経路を参照するパケットを各 Customer から受け取った時に、SR Policy に従った Segment List を付与し SR-MPLS で構成されるコアネットワークに送出する 次章の検証例では、上記手順に沿った検証を紹介します。 検証例の紹介 下記のトポロジーを利用し、 Color-Based Steering の検証例を紹介します。 また、L3VPN 環境準備までの設定例は こちら を参照ください。 以降は Color-Based Steering にて Customer ごとに下図の TE を行うための設定と確認方法を記載します。ingress PE には SR Policy の設定、egress PE には VPN 経路に Color Extended Community を付加する設定を入れます。 Customer A の TE Customer B の TE SR Policy の設定と確認 まず SR Policy を設定します。explicit path を利用する場合は Segment List の設定も必要です。 PE1 (IOS XR) 側 segment-routing traffic-eng segment-list vrf-100-segment-list index 10 mpls label 16002 index 20 mpls label 16004 index 30 mpls label 16003 ! segment-list vrf-200-segment-list index 10 mpls label 16004 index 20 mpls label 16002 index 30 mpls label 16003 ! policy vrf-100-policy color 100 end-point ipv4 10.255.0.3 candidate-paths preference 50 explicit segment-list vrf-100-segment-list ! ! ! ! policy vrf-200-policy color 200 end-point ipv4 10.255.0.3 candidate-paths preference 50 explicit segment-list vrf-200-segment-list ! ! ! ! ! ! データベースに SR Policy が追加されたことを確認します。 RP/0/RP0/CPU0:PE1#show segment-routing traffic-eng policy Tue Jul 12 11:38:51.837 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.0.3 Name: srte_c_100_ep_10.255.0.3 Status: Admin: up Operational: up for 00:05:04 (since Jul 12 11:33:47.109) Candidate-paths: Preference: 50 (configuration) (active) Name: vrf-100-policy Requested BSID: dynamic Protection Type: protected-preferred Maximum SID Depth: 10 Explicit: segment-list vrf-100-segment-list (valid) Weight: 1, Metric Type: TE 16002 16004 16003 Attributes: Binding SID: 24008 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Color: 200, End-point: 10.255.0.3 Name: srte_c_200_ep_10.255.0.3 Status: Admin: up Operational: up for 00:00:32 (since Jul 12 11:38:19.552) Candidate-paths: Preference: 50 (configuration) (active) Name: vrf-200-policy Requested BSID: dynamic Protection Type: protected-preferred Maximum SID Depth: 10 Explicit: segment-list vrf-200-segment-list (valid) Weight: 1, Metric Type: TE 16004 16002 16003 Attributes: Binding SID: 24011 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no PE2 (Junos) 側 set protocols source-packet-routing segment-list vrf-100-segment-list P2 label 16004 set protocols source-packet-routing segment-list vrf-100-segment-list P1 label 16002 set protocols source-packet-routing segment-list vrf-100-segment-list PE1 label 16001 set protocols source-packet-routing source-routing-path vrf-100-policy to 10.255.0.1 set protocols source-packet-routing source-routing-path vrf-100-policy color 100 set protocols source-packet-routing source-routing-path vrf-100-policy primary vrf-100-segment-list set protocols source-packet-routing segment-list vrf-200-segment-list P1 label 16002 set protocols source-packet-routing segment-list vrf-200-segment-list P2 label 16004 set protocols source-packet-routing segment-list vrf-200-segment-list PE1 label 16001 set protocols source-packet-routing source-routing-path vrf-200-policy to 10.255.0.1 set protocols source-packet-routing source-routing-path vrf-200-policy color 200 set protocols source-packet-routing source-routing-path vrf-200-policy primary vrf-200-segment-list データベースに SR Policy が追加されたことを確認します。 user@PE2> show spring-traffic-engineering lsp name vrf-100-policy detail Name: vrf-100-policy Tunnel-source: Static configuration Tunnel Forward Type: SRMPLS To: 10.255.0.1-100<c> State: Up Path: vrf-100-segment-list Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A Compute Status:Disabled , Compute Result:N/A , Compute-Profile Name:N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 3 Hop 1 (Strict): NAI: None SID type: 20-bit label, Value: 16004 Hop 2 (Strict): NAI: None SID type: 20-bit label, Value: 16002 Hop 3 (Strict): NAI: None SID type: 20-bit label, Value: 16001 Total displayed LSPs: 1 (Up: 1, Down: 0) user@PE2> show spring-traffic-engineering lsp name vrf-200-policy detail Name: vrf-200-policy Tunnel-source: Static configuration Tunnel Forward Type: SRMPLS To: 10.255.0.1-200<c> State: Up Path: vrf-200-segment-list Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A Compute Status:Disabled , Compute Result:N/A , Compute-Profile Name:N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 3 Hop 1 (Strict): NAI: None SID type: 20-bit label, Value: 16002 Hop 2 (Strict): NAI: None SID type: 20-bit label, Value: 16004 Hop 3 (Strict): NAI: None SID type: 20-bit label, Value: 16001 Total displayed LSPs: 1 (Up: 1, Down: 0) BGP Color Extended Community 付加の設定と確認 BGP で広告する経路に color を付加します。 PE1 (IOS XR) 側 vrf 100 address-family ipv4 unicast export route-policy add-color-100 ! ! vrf 200 address-family ipv4 unicast export route-policy add-color-200 ! ! extcommunity-set opaque color-100 100 end-set ! extcommunity-set opaque color-200 200 end-set ! route-policy add-color-100 set extcommunity color color-100 end-policy ! route-policy add-color-200 set extcommunity color color-200 end-policy 対向の PE2 にて、受け取った経路に color がついていることを確認します。 user@PE2> show route table bgp.l3vpn.0 detail bgp.l3vpn.0: 2 destinations, 2 routes (2 active, 0 holddown, 0 hidden) 65000:100:192.168.0.0/24 (1 entry, 0 announced) *BGP Preference: 170/-101 Route Distinguisher: 65000:100 Next hop type: Indirect, Next hop index: 0 Address: 0x704dfc4 Next-hop reference count: 3 Source: 10.255.0.1 Next hop type: Router, Next hop index: 0 Next hop: 10.0.2.1 via ge-0/0/0.0 Label operation: Push 24004, Push 16001(top) Label TTL action: prop-ttl, prop-ttl(top) Load balance label: Label 24004: None; Label 16001: None; Label element ptr: 0x780fa88 Label parent element ptr: 0x782ea28 Label element references: 4 Label element child references: 0 Label element lsp id: 0 Session Id: 0x0 Next hop: 10.0.3.1 via ge-0/0/1.0, selected Label operation: Push 24004, Push 16001(top) Label TTL action: prop-ttl, prop-ttl(top) Load balance label: Label 24004: None; Label 16001: None; Label element ptr: 0x780fa88 Label parent element ptr: 0x782ea28 Label element references: 4 Label element child references: 0 Label element lsp id: 0 Session Id: 0x0 Protocol next hop: 10.255.0.1 Label operation: Push 24004 Label TTL action: prop-ttl Load balance label: Label 24004: None; Indirect next hop: 0x7198904 1048576 INH Session ID: 0x144 State: <Active Int Ext ProtectionPath ProtectionCand> Local AS: 65000 Peer AS: 65000 Age: 3:58 Metric: 0 Metric2: 30 Validation State: unverified ORR Generation-ID: 0 Task: BGP_65000.10.255.0.1 AS path: ? Communities: target:65000:100 color:0:100 Import Accepted VPN Label: 24004 Localpref: 100 Router ID: 10.255.0.1 Secondary Tables: 100.inet.0 Thread: junos-main 65000:200:192.168.0.0/24 (1 entry, 0 announced) *BGP Preference: 170/-101 Route Distinguisher: 65000:200 Next hop type: Indirect, Next hop index: 0 Address: 0x704e234 Next-hop reference count: 3 Source: 10.255.0.1 Next hop type: Router, Next hop index: 0 Next hop: 10.0.2.1 via ge-0/0/0.0, selected Label operation: Push 24005, Push 16001(top) Label TTL action: prop-ttl, prop-ttl(top) Load balance label: Label 24005: None; Label 16001: None; Label element ptr: 0x780fd30 Label parent element ptr: 0x782ea28 Label element references: 4 Label element child references: 0 Label element lsp id: 0 Session Id: 0x0 Next hop: 10.0.3.1 via ge-0/0/1.0 Label operation: Push 24005, Push 16001(top) Label TTL action: prop-ttl, prop-ttl(top) Load balance label: Label 24005: None; Label 16001: None; Label element ptr: 0x780fd30 Label parent element ptr: 0x782ea28 Label element references: 4 Label element child references: 0 Label element lsp id: 0 Session Id: 0x0 Protocol next hop: 10.255.0.1 Label operation: Push 24005 Label TTL action: prop-ttl Load balance label: Label 24005: None; Indirect next hop: 0x7198a84 1048577 INH Session ID: 0x144 State: <Active Int Ext ProtectionPath ProtectionCand> Local AS: 65000 Peer AS: 65000 Age: 2:42 Metric: 0 Metric2: 30 Validation State: unverified ORR Generation-ID: 0 Task: BGP_65000.10.255.0.1 AS path: ? Communities: target:65000:200 color:0:200 Import Accepted VPN Label: 24005 Localpref: 100 Router ID: 10.255.0.1 Secondary Tables: 200.inet.0 Thread: junos-main PE2 (Junos) 側 ※ Junos のポリシーは accept の後のタームが実行されないため順序にご注意ください set policy-options community COLOR-100 members color:0:100 set policy-options community COLOR-200 members color:0:200 delete policy-options policy-statement EXPORT-POLICY-100 set policy-options policy-statement EXPORT-POLICY-100 term ROUTE-TARGET then community add VRF100-65000-RT set policy-options policy-statement EXPORT-POLICY-100 term ADD-COLOR-100 then community add COLOR-100 set policy-options policy-statement EXPORT-POLICY-100 term REDIST-DIRECT from protocol direct set policy-options policy-statement EXPORT-POLICY-100 term REDIST-DIRECT then accept set routing-instances 100 vrf-export EXPORT-POLICY-100 delete policy-options policy-statement EXPORT-POLICY-200 set policy-options policy-statement EXPORT-POLICY-200 term ROUTE-TARGET then community add VRF200-65000-RT set policy-options policy-statement EXPORT-POLICY-200 term ADD-COLOR-200 then community add COLOR-200 set policy-options policy-statement EXPORT-POLICY-200 term REDIST-DIRECT from protocol direct set policy-options policy-statement EXPORT-POLICY-200 term REDIST-DIRECT then accept set routing-instances 200 vrf-export EXPORT-POLICY-200 対向の PE1 にて、受け取った経路に color がついていることを確認します。 RP/0/RP0/CPU0:PE1#show bgp vpnv4 unicast vrf 100 192.168.1.0/24 Tue Jul 12 15:24:30.125 JST BGP routing table entry for 192.168.1.0/24, Route Distinguisher: 65000:100 Versions: Process bRIB/RIB SendTblVer Speaker 28 28 Last Modified: Jul 12 15:01:55.551 for 00:22:34 Paths: (1 available, best #1) Not advertised to any peer Path #1: Received by speaker 0 Not advertised to any peer Local 10.255.0.3 C:100 (bsid:24008) (metric 20) from 10.255.0.3 (10.255.0.3) Received Label 20 Origin IGP, localpref 100, valid, internal, best, group-best, import-candidate, imported Received Path ID 0, Local Path ID 1, version 28 Extended community: Color:100 RT:65000:100 SR policy color 100, up, not-registered, bsid 24008 Source AFI: VPNv4 Unicast, Source VRF: 100, Source Route Distinguisher: 65000:100 RP/0/RP0/CPU0:PE1#show bgp vpnv4 unicast vrf 200 192.168.1.0/24 Tue Jul 12 15:24:46.645 JST BGP routing table entry for 192.168.1.0/24, Route Distinguisher: 65000:200 Versions: Process bRIB/RIB SendTblVer Speaker 29 29 Last Modified: Jul 12 15:01:55.551 for 00:22:51 Paths: (1 available, best #1) Not advertised to any peer Path #1: Received by speaker 0 Not advertised to any peer Local 10.255.0.3 C:200 (bsid:24011) (metric 20) from 10.255.0.3 (10.255.0.3) Received Label 21 Origin IGP, localpref 100, valid, internal, best, group-best, import-candidate, imported Received Path ID 0, Local Path ID 1, version 29 Extended community: Color:200 RT:65000:200 SR policy color 200, up, not-registered, bsid 24011 Source AFI: VPNv4 Unicast, Source VRF: 200, Source Route Distinguisher: 65000:200 SR Policy が適用された経路の確認 まず PE1 から PE2 の経路について確認します。 PE1 にて、対向の Customer ネットワークへの経路の nexthop が SR Policy の処理へ転送するためのローカルラベルとなっていることが確認できます。 RP/0/RP0/CPU0:PE1#sho cef vrf 100 Tue Jul 12 11:14:45.051 JST Prefix Next Hop Interface ------------------- ------------------- ------------------ 0.0.0.0/0 drop default handler 0.0.0.0/32 broadcast 192.168.0.0/24 attached GigabitEthernet0/0/0/2 192.168.0.0/32 broadcast GigabitEthernet0/0/0/2 192.168.0.254/32 receive GigabitEthernet0/0/0/2 192.168.0.255/32 broadcast GigabitEthernet0/0/0/2 192.168.1.0/24 24008 (via-label) <recursive> 224.0.0.0/4 0.0.0.0/32 224.0.0.0/24 receive 255.255.255.255/32 broadcast RP/0/RP0/CPU0:PE1#sho cef vrf 200 Tue Jul 12 11:16:24.775 JST Prefix Next Hop Interface ------------------- ------------------- ------------------ 0.0.0.0/0 drop default handler 0.0.0.0/32 broadcast 192.168.0.0/24 attached GigabitEthernet0/0/0/3 192.168.0.0/32 broadcast GigabitEthernet0/0/0/3 192.168.0.254/32 receive GigabitEthernet0/0/0/3 192.168.0.255/32 broadcast GigabitEthernet0/0/0/3 192.168.1.0/24 24011 (via-label) <recursive> 224.0.0.0/4 0.0.0.0/32 224.0.0.0/24 receive 255.255.255.255/32 broadcast ローカルラベル処理は以下で確認できます。 RP/0/RP0/CPU0:PE1#show mpls forwarding labels 24008 detail Tue Jul 12 11:18:35.029 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ 24008 Pop No ID srte_c_100_e point2point 0 Updated: Jul 12 07:43:42.409 Version: 18, Priority: 2 Label Stack (Top -> Bottom): { Unlabelled Imp-Null } NHID: 0x0, Encap-ID: N/A, Path idx: 0, Backup path idx: 0, Weight: 0 MAC/Encaps: 0/0, MTU: 0 Outgoing Interface: srte_c_100_ep_10.255.0.3 (ifhandle 0x00000014) Packets Switched: 0 RP/0/RP0/CPU0:PE1#show mpls forwarding labels 24011 detail Tue Jul 12 11:17:27.943 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ 24011 Pop No ID srte_c_200_e point2point 0 Updated: Jul 12 07:43:37.156 Version: 14, Priority: 2 Label Stack (Top -> Bottom): { Unlabelled Imp-Null } NHID: 0x0, Encap-ID: N/A, Path idx: 0, Backup path idx: 0, Weight: 0 MAC/Encaps: 0/0, MTU: 0 Outgoing Interface: srte_c_200_ep_10.255.0.3 (ifhandle 0x0000001c) Packets Switched: 0 次に PE2 から PE1 の経路について確認します。 PE2 にて VRF routing table を確認したところ、SR Policy が適用された経路となっていないことが確認できます。 user@PE2> show route table 100.inet.0 100.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 192.168.0.0/24 *[BGP/170] 00:00:14, MED 0, localpref 100, from 10.255.0.1 AS path: ?, validation-state: unverified to 10.0.2.1 via ge-0/0/0.0, Push 24012, Push 16001(top) > to 10.0.3.1 via ge-0/0/1.0, Push 24012, Push 16001(top) 192.168.1.0/24 *[Direct/0] 00:37:16 > via ge-0/0/2.0 192.168.1.254/32 *[Local/0] 00:37:16 Local via ge-0/0/2.0 user@PE2> show route table 200.inet.0 200.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 192.168.0.0/24 *[BGP/170] 00:00:23, MED 0, localpref 100, from 10.255.0.1 AS path: ?, validation-state: unverified to 10.0.2.1 via ge-0/0/0.0, Push 24013, Push 16001(top) > to 10.0.3.1 via ge-0/0/1.0, Push 24013, Push 16001(top) 192.168.1.0/24 *[Direct/0] 00:37:25 > via ge-0/0/3.0 192.168.1.254/32 *[Local/0] 00:37:25 Local via ge-0/0/3.0 Junos は BGP 経路の nexthop 解決をする際にデフォルトで color を考慮しません。 次の設定を追加することで color を考慮して nexthop を解決するようになります。 set policy-options policy-statement BGP-IMPORT-POLICY term VRF-100 from community VRF100-65000-RT set policy-options policy-statement BGP-IMPORT-POLICY term VRF-100 then accept set policy-options policy-statement BGP-IMPORT-POLICY term VRF-100 then resolution-map map1 set policy-options policy-statement BGP-IMPORT-POLICY term VRF-200 from community VRF200-65000-RT set policy-options policy-statement BGP-IMPORT-POLICY term VRF-200 then accept set policy-options policy-statement BGP-IMPORT-POLICY term VRF-200 then resolution-map map1 set policy-options resolution-map map1 mode ip-color set protocols bgp group ibgp import BGP-IMPORT-POLICY 改めて PE2 にて、 SR policy が適用された経路が採用されていることが確認できます。 user@PE2> show route table 100.inet.0 100.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 192.168.0.0/24 *[BGP/170] 02:45:08, MED 0, localpref 100, from 10.255.0.1 AS path: ?, validation-state: unverified > to 10.0.3.1 via ge-0/0/1.0, Push 24012, Push 16001, Push 16002(top) 192.168.1.0/24 *[Direct/0] 03:27:59 > via ge-0/0/2.0 192.168.1.254/32 *[Local/0] 03:27:59 Local via ge-0/0/2.0 user@PE2> show route table 200.inet.0 200.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 192.168.0.0/24 *[BGP/170] 02:45:23, MED 0, localpref 100, from 10.255.0.1 AS path: ?, validation-state: unverified > to 10.0.2.1 via ge-0/0/0.0, Push 24013, Push 16001, Push 16004(top) 192.168.1.0/24 *[Direct/0] 03:28:14 > via ge-0/0/3.0 192.168.1.254/32 *[Local/0] 03:28:14 Local via ge-0/0/3.0 疎通確認 PE1 と PE2 から VPN 経路の traceroute を行い、TE によって経路制御されていることを確認します。 PE1 → PE2 RP/0/RP0/CPU0:PE1#traceroute 192.168.1.254 vrf 100 Wed Jul 13 19:14:09.683 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.0.2 [MPLS: Labels 16004/16003/16 Exp 0] 17 msec 3 msec 9 msec 2 10.0.4.2 [MPLS: Labels 16003/16 Exp 0] 3 msec 5 msec 12 msec 3 192.168.1.254 3 msec 10 msec 4 msec RP/0/RP0/CPU0:PE1#traceroute 192.168.1.254 vrf 200 Wed Jul 13 19:17:27.790 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.1.2 [MPLS: Labels 16002/16003/17 Exp 0] 13 msec 13 msec 5 msec 2 10.0.4.1 [MPLS: Labels 16003/17 Exp 0] 12 msec 14 msec 5 msec 3 192.168.1.254 11 msec 15 msec 5 msec PE2 → PE1 user@PE2> traceroute 192.168.0.254 routing-instance 100 traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 10.0.3.1 (10.0.3.1) 3.025 ms 2.434 ms 39.277 ms MPLS Label=16002 CoS=0 TTL=1 S=0 MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24012 CoS=0 TTL=1 S=1 2 10.0.4.1 (10.0.4.1) 11.748 ms 2.363 ms 2.796 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24012 CoS=0 TTL=2 S=1 3 10.0.0.1 (10.0.0.1) 11.454 ms user@PE2> traceroute 192.168.0.254 routing-instance 200 traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 10.0.2.1 (10.0.2.1) 3.372 ms 2.207 ms 2.184 ms MPLS Label=16004 CoS=0 TTL=1 S=0 MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24013 CoS=0 TTL=1 S=1 2 10.0.4.2 (10.0.4.2) 2.474 ms 2.358 ms 2.206 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24013 CoS=0 TTL=2 S=1 3 10.0.1.1 (10.0.1.1) 11.859 ms このように、Single-AS SR-MPLS で構成されたネットワークでの TE が実現できました。 まとめ 本記事では、SR における TE の実現に利用される SR Policy について説明し、Multi-vendor 環境における Single-AS SR-MPLS での Color-Based Steering の検証結果を紹介しました。次回の記事では、Multi-AS における TE について紹介予定です。 (2022/08/08 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #5] Traffic Engineering in Multi-AS Internet-Drafts として https://datatracker.ietf.org/doc/draft-ietf-spring-segment-routing-policy/ で議論中。 ↩
アバター
はじめに DevOpsプラットフォームの取り組みを紹介する3回目の記事です。 Qmonus Value Stream のアーキテクトの牧志 ( @JunMakishi ) です。 本記事では、Qmonus Value Streamの独自技術であるCloud Native Adapterを紹介します。はじめにInfrastructure as Codeの課題を指摘し、Cloud Native Adapterを使ってこれらの課題をどう解決するのかを解説します。 Infrastructure as Codeの課題 Infrastructuer as Code (以下IaC) は、特定のツールを指すのではなく、インフラストラクチャをコードで記述し、ソフトウェアと同じように取り扱うプラクティスを指します。インフラストラクチャのリソース構成や設定をコードで記述・適用することで、再現性、一貫性、および透明性のあるインフラストラクチャを得られます。分散されたコンポーネントを組み合わせてシステムを組み上げるクラウドアーキテクチャを運用する上で、その構成をコードで管理することは必要不可欠であると考えます。 Cloud Native Days Tokyo 2021の 発表 では、IaCを、「User Interface」「Workflow」「Test」および「Feedback Loop」の4つに分解して考察しました(図)。Qmonus Value Streamのコア技術であるCloud Native Adapterは、これらのうち User Interface と Workflow の課題を解決するための方式です。 User Interfaceの課題 IaCにおけるUser Interfaceは、インフラストラクチャのリソース構成と設定の期待値をコードで定義するための方式です。一般的に、JSON、YAML、または専用のDSLなどが利用されています。 試験環境と商用環境の間で一貫したインフラストラクチャ構成を得るためには、IaC実装のパラメータ化を進める必要があります。例えば、APIアプリケーションをデプロイする場合、ユーザに公開するAPIのFQDNをパラメータ化するようなIaC実装が求められます。試験環境と商用環境で想定する負荷が異なり、割り当てるCPUやメモリ量をパラメータ化することもあるでしょう。 ここで、Kustomize、Python+Jinjaなどのツール、またはDSL専用の記法を用いてパラメータ化することが考えられます。しかし、コードの規模が大きくなるほど、パラメータの管理が煩雑になります。インフラストラクチャは指定可能な設定値が多岐にわたるため、複数のユースケースに対応しようとするあまり、パラメータの数が多くなる傾向にあります。IaCを実践していて、各パラメータがインフラストラクチャのどの値を設定しているかを追いかけるのが難しいと感じたことがある方は少なくないでしょう。 また、ソフトウェア開発のプラクティスに従い、インフラストラクチャ設定をグループ化・抽象化していくことで、より規模の大きいインフラストラクチャを管理できます。しかし、YAMLなどの既存のインターフェースでは、リソース間の依存関係や構成全体の見通しが悪くなる傾向にあると考えます。例えば、繰り返し処理やまとまったコンテクストを1つのモジュールにまとめる、実装したコードを事前に検証する、といったソフトウェア開発プラクティスを適用することが難しいと考えています。 私たちは、 IaCがソフトウェアエンジニアにとって、読み書きしやすく、かつメンテナンスしやすいものである必要がある と考えます。IaCの「User Interface」として、ソフトウェアプラクティスを適用でき、スケーラブルにインフラストラクチャ構成を宣言できるデータ記述言語が求められます。インフラストラクチャのコンフィグ定義を扱うのに適したもので、見通しよくIaC実装をモジュール化やグループ化できる言語が必要です。 Workflowの課題 IaCにおけるWorkflowは、インフラストラクチャを設定するまでの一連の手続きを自動化するための方式です。例えば、Terraformは、列挙されたリソース間の依存関係を解決して、リソース設定を適用する順序を制御します。 様々なIaCツールがありますが、チームのDevOpsとソフトウェアアーキテクチャにもとづき、 IaCを実装してからツールを実行してインフラストラクチャに適用するまでの一連のワークフローを簡単に自動化する手段やデファクトスタンダードがまだないと考えています。チームごとに独自のスクリプトを用意し、多様なツールを組みあわせるためのグルーコードや、前述したパラメータを埋めるための煩雑な処理などを実装する必要があります。その高いメンテナンスコストは運用フェーズでチームに重くのしかかってきます。 再現性の高いインフラストラクチャ構成を得るためにIaCを実践したとしても、それを適用するスクリプト実装を信頼性高く保たない限り、変更に弱いインフラストラクチャとなってしまうリスクを孕んでいます。そのため、パラメータ設定ミスを誘発してしまわないよう、注意深くスクリプトを修正する必要がでてきます。例えば、Jenkinsとそこで動かすスクリプトをメンテナンスするために多大な労力を割いているチームも多いことでしょう。 私たちは、 システムアーキテクチャ設計の肝の一つは、継続的にデリバリする仕組みを作ること であり、 IaC実装においてそのワークフローを考慮するべき と考えます。 IaCの「Workflow」として、インフラストラクチャをデプロイするまでの一連のワークフローを簡単に自動化し、使いたいクラウド技術・ツールを、自チームのCI/CDパイプラインに苦痛なく組み入れられるような方式が求められます。 発展途上のエコシステム 近年、前述したIaCのワークフローの課題に取り組むソリューションが台頭してきています。 Terraform Cloud は、HashiCorp社が提供するSaaSです。「Plan, Policy Check, Apply」というTerraformを実行するまでの一連の流れをSaaSで管理することを可能とし、チームで一貫したプラクティスを適用可能としています。 Waypoint は、同様にHashiCorp社が開発しているオープンソースソフトウェアです。「Build, Deploy, Release」という開発からソフトウェアリリースまでのワークフローを連結するソリューションを提供しています。 Dagger は、Dockerの創始者であるSolomon Hykesらにより開発されているオープンソースソフトウェアです。ポータブルなCI/CDワークフローを記述・実行できるエンジンを提供し、チームサイロでメンテナンスしていたGlueコードを無くすことができます。 KubeVela は、Alibaba Cloudのチームが中心となって開発しているオープンソースソフトウェアです。プラットフォームチームが、複数のKubernetesリソースとそれらをデプロイするワークフローとをセットでパッケージングすることを可能としています。 それぞれ、既存ツールでは足りなかったワークフローの自動化を達成するためのソリューションを提供しています。詳細については、それぞれのWebサイトを参照ください。 このように、前述したIaCの課題は、エコシステムの中でも近年注目されている課題であるものの、まだまだデファクトスダンダードと呼べるソリューションが確立されていません。そこで、私たちは、これらの課題を解決するための独自の方式をQmonus Value Streamに実装し、提案しています。 私たちの提案:Cloud Native Adapter Qmonus Value Streamは、Cloud Native Adapterと呼ばれる独自のIaC実装を提案し「User Interface」と「Workflow」の課題を解決しています。そのアイデアは、 KubeCon EU 2020 で「Design Pattern as Code」という名称で紹介しています。 Cloud Native Adapterのコアとなるアプローチを以下に挙げます。 ワークフロー定義を含む統一的なインターフェース CUE言語を使って、クラウドアーキテクチャを構成する「インフラストラクチャ構成」とそれをデプロイする「ワークフロー」をまとめて1つのインターフェースで宣言する。 上記によって、インフラストラクチャを継続的にデリバリする「Workflow」を提供する。 コンフィグのモジュール化と結合 宣言的に記述された「インフラストラクチャ構成」と「ワークフロー」について、再利用可能な形に分割して、そのデータ構造を隠蔽せずに「結合」する。 1の統一的なインターフェースで上記の仕組みを提供することで、見通しの良いIaC実装のモジュール化を促進し、ソフトウェアエンジニアにとってメンテナンスしやすい「User Interface」を提供する。 以下、それぞれのアプローチを紹介します。 1. ワークフロー定義を含む統一的なインターフェース 以下のイメージ図で示すように、Cloud Native Adapterは、「インフラストラクチャの構成」と「ワークフロー」をCUE言語を使って宣言します。Qmonus Value Streamは、このCloud Native Adapterのうち、インフラストラクチャ構成を宣言する箇所を読み出し、インフラストラクチャに適用するマニフェストを生成します。また、ワークフローを宣言する箇所を読み出すことで、このマニフェストを適用するためのCI/CDパイプラインを生成します。 CUE言語は、型付データ記述言語であり、データ構造をシンプルに表現・操作できるだけでなく、モジュール化などといったソフトウェアプラクティスを適用できます。前述したDaggerやKubeVelaでもCUE言語を採用しています。CUE言語の詳細は、別の記事で紹介予定です。 以下に、Cloud Native Adapterの実装例として、APIアプリケーションを公開する「GKE API Adapter」を紹介します。 resources fieldにインフラストラクチャのリソース設定を宣言し、 pipelines fieldにCI/CDパイプライン定義を宣言します。この例では、GCPへ適用する publicIp/securityPolicy 設定とKubernetesへ適用する service/ingress 設定に加えて、CI/CDパイプライン中でKubernetesへの適用後に実行すべきAPIの正常性確認 testApi タスクを1つのAdapterとしてパッケージ化しています。GCPとKubernetesという異なるAPIだけでなく、CI/CDのロジックまで1つのインターフェースで記述できることが分かります。 package api DesignPattern: { name: "GKE API Adapter" // infrastructure configuration resources: { gcpPublicIp: ... gcpSeculicyPolicy: ... k8sService: ... k8sIngress: ... } // CI/CD operations pipelines: { // tasks in "deploy" stage deploy: { applyManifest: ... // a task to test API endpoint after deploying resources testApi: ... } } } 上記の例のようにInfrastructureとCI/CDパイプラインを1つのCloud Native Adapterとしてまとめるのではなく、それぞれ異なるAdapterとして実装することも可能です。第2回連載の 前編 では、GKEでAPIサービス公開するインフラストラクチャ構成を宣言したCloud Native Adapterを、また 後編 では、bookinfoをデプロイするワークフローを宣言したCloud Native Adapterを使ったデモを紹介しました。 マルチクラウドのインフラストラクチャ構成だけでなく、その構成をデプロイするワークフローまでを1つのCloud Native Adapterとしてまとめることで、継続的なデリバリ・自動化までを視野に入れたIaCを実装できます。CUE言語を書くだけで実践的なパイプラインとともにインフラストラクチャを拡張・成長させられるようなユーザ体験(Developer Experience)を提供したいと考えています。 さらに、CUE言語を活用して、宣言したインフラストラクチャ構成およびパイプライン定義におけるデータ構造を安全に評価・検証できます。このCUE言語の強みについては、別の記事で紹介します。 2. コンフィグのモジュール化と結合 Cloud Native Adapterは、宣言的に記述した「インフラストラクチャ構成」と「ワークフロー」をグループ化、モジュール化できます。以下のイメージ図に示すように、ユーザは、再利用できる単位にパッケージ化された複数のモジュールを選択して、1つのシステムアーキテクチャを組み上げることができます。 CUE言語は、宣言的に記述されたデータ構造を結合し一貫した結果を出力できることが特徴です。Cloud Native Adapterは、インフラストラクチャ構成とワークフロー定義について、そのデータ構造を過度に隠蔽・抽象化せずに、再利用出来る単位に分割できます。Qmonus Value Streamのユーザは、このCUE言語の特徴を活用し、分割された任意のCloud Native Adapterを結合することで、目的に沿ったインフラストラクチャ構成とワークフロー定義を得られます。 上記イメージ図で示したCloud Native Adapterを結合する実装例を以下に示します。前述した「GKE API Adapter」に加えて、Securityルールを適用するAdapterや、監視 (uptime check) 設定するAdapterを composites field中に宣言しています。Qmonus Value Streamは、これをコンパイルすることで、各Adapterがそれぞれ宣言しているリソース設定とCI/CDパイプライン定義を結合して、1つのシステムとCI/CDパイプラインを生成します。さらに、コード例の下部に示しているように、 resources fieldや pipelines fieldに独自のインフラストラクチャ構成ルールやワークフロータスクを宣言することで、カスタマイズを加えることができます。 DesignPattern: { // unify exisitng Cloud Native Adapter composites: [ { pattern: api.DesignPattern params: { ... } }, { pattern: containerSecurity.DesignPattern params: { ... } }, { pattern: uptimecheck.DesignPattern params: { ... } } ] // customize resources: { // application specific configuration k8sConfigmap: { metadata: name: "application-config" data: "example.conf" : "..." // mount the configuration k8sDeployment: spec: template: spec: volumes: [ { name: "config" configMap: name: "application-config" } ] } // application specific tasks pipelines: { ... } } Helm のように自己完結型のパッケージを作るアプローチの場合、アプリケーション単体の設定を1つのパッケージにまとめられますが、クラウドプロバイダごとの設定や、セキュリティや監視といった運用設定については、別の手段で追加することになります。カスタマイズ性を追求する場合、1つのパッケージが受け取るパラメータを増やすことになり、パッケージが複雑化します。 一方、Cloud Native Adapterのアプローチでは、機能や目的単位でIaC実装をモジュール化し、ユーザ側で必要なモジュールを選択することでアーキテクチャを拡張します。具体的には、ユーザは、1つのモジュールを過度に抽象化およびパラメータ化することなく、コンテクストごとにまとまったデータだけを宣言したCloud Native Adapterを作成します。上記の例では、 resources fieldに、拡張したい追加のリソース設定だけを宣言しています。Qmonus Value Streamは、結合対象のCloud Native Adapterを読み出し、それぞれが宣言したリソース設定について、データ構造をそのままに結合します。 このアプローチによって、Qmonus Value Streamのユーザは、汎化したモジュールを作りながらIaC全体を見通しよくシンプルに記述できます。 このように、Cloud Native Adapterは、アーキテクチャやコンテクストの境界でモジュール化し、ユーザ側で再利用するモジュールを選択・結合するプラクティスを提供します。例で挙げたようなセキュリティ設定や監視設定を1つのモジュールに押し込む必要がなくなります。私たちは、モジュール化というソフトウェアプラクティスを活用し、スケーラブルで直感的にクラウドアーキテクチャを組み上げることができるユーザ体験を提供したいと考えています。 おわりに DevOpsプラットフォームの取り組み連載の3回目の記事として、Qmonus Value Streamチームが取り組んでいるInfrastructure as Codeの課題と、それを解決するための独自IaC実装であるCloud Native Adapterを紹介しました。 Qmonus Value Streamチームは、メンバ一人一人が、利用者であるアプリケーション開発者のために信念を持って活動を続けています。 プラットフォームを内製開発するポジション、プラットフォームを使ってNTTグループのクラウド基盤やCI/CDを構築支援するポジションそれぞれで 、一緒に働く仲間を募集していますので、興味を持たれた方は応募いただけると嬉しいです。 CI/CDプラットフォーム開発、ソフトウェアエンジニア NTTグループのクラウド基盤構築およびCI/CD導入、ソリューションアーキテクト 次回は、CUE言語についてさらに踏み込んでご紹介します。お楽しみに!
アバター
はじめに DevOpsプラットフォームの取り組みを紹介する2回目の記事の後半です。 Qmonus(クモナス) Value Stream の開発チームの奥井( @HirokiOkui )です。 連載第2回では、 Qmonus Value Stream を使ってアプリケーションを実際にビルド・デプロイする事例を2つ、前編・後編に分けて紹介します。 前編では、Cloud Native Adapterを用いてパブリッククラウドとKubernetesの両方をまとめてInfrastructure as Code(以下、IaC)として表現し、クラウド基盤を構築する事例を紹介しました。 後編では、Cloud Native Adapterを用いたContinuous Integration/Continuous Delivery(以下、CI/CD)パイプラインの構築を中心に説明します。 1回目、2回目前編の記事をまだご覧になってない方は、ぜひそちらもご覧ください。 1回目 Qmonus Value Streamの紹介 2回目 Qmonus Value Streamを使ってみた(前編) - クラウド基盤の構築 ユースケース2: Istio BookInfo アプリケーションのデリバリを実践する事例として、第2回前編では「KubernetesとGCPによるパブリックAPI公開」を紹介しました。後編では2つ目の事例として、Istioプロジェクトのサンプルアプリケーションである BookInfo を扱います。 BookInfoは、書籍のカタログを表示するシンプルなWebアプリケーションですが、言語の異なる4つのマイクロサービスから構成されており、実践的なアプリケーションに近い複雑さとデリバリの難しさを備えています。このため、IaCやCI/CDのツールを評価するのに適しています。 ( Istioのドキュメント より) リリースエンジニアリングの難しさ Kubernetesに対してコンテナをデリバリするためには、 Tekton CI によるビルド、 Kustomize や Helm によるマニフェスト生成、 Argo CD によるデプロイを用いてCI/CDパイプラインを構成することが一般的です。 これらを用いてBookInfoをデリバリする方法が、インプレス社の Kubernetes CI/CDパイプラインの実装 に詳しく記されています。 こちら で公開されているBookInfoのデリバリに必要なマニフェストを見ると、デリバリ対象のKustomizeマニフェスト、CI/CDのためのTektonとArgo CDのマニフェストと、多くのマニフェストとパラメータが含まれています。 CI/CDパイプラインを構築するためには、それぞれを正しく結合して使いこなす必要があります。 このように、アプリケーションをリリースするためのCI/CDパイプラインを信頼性高く構築し保守する技術領域のことを、リリースエンジニアリングと言います。 BookInfoのように、マイクロサービスアーキテクチャを採用したアプリケーションのリリースには、高度なリリースエンジニアリングのスキルが要求されます。 これを少人数の開発チームで実践するのは至難の業です。 Qmonus Value Streamを用いたCI/CDパイプラインの構成 Qmonus Value Stream(以下、QVS)では、前編で紹介したとおり、独自のIaC実装であるCloud Native Adapter(以下、Adapter)を用いて上述の問題を解決します。 以降の節で、BookInfoをデリバリするCI/CDパイプラインをAdapterを用いて構成する実例について、順を追って説明します。 ここでは、BookInfoを構成する4つのマイクロサービスのそれぞれをイメージビルドし、生成されたイメージを用いたKubernetes Deploymentと内部通信用のServiceをマイクロサービスごとにデプロイするCI/CDパイプラインを構成します。 Cloud Native Adapterの選択 まず、QVSの設定ファイルであるQVS Configを作成し、その中で使用するAdapterを選択します。 BookInfoの reviews アプリケーションでは、以下のQVS Configを使用します。 params : - name : namespace type : string - name : imageName type : string designPatterns : - pattern : qmonus.net/adapter/official/kubernetes/deployment/simple params : appName : reviews namespace : $(params.namespace) imageName : $(params.imageName) port : "9080" env : - name : LOG_DIR value : "/tmp/logs" useDefaultService : true - pattern : qmonus.net/adapter/official/pipeline/build:buildkit - pattern : qmonus.net/adapter/official/pipeline/deploy:simpleDeploy ここでは、QVSで用意している3つのAdapterを使用します。 1つ目は、Applicationマニフェストを扱う Adapterであり、Kubernetes DeploymentリソースとServiceリソースを提供します。 2つ目と3つ目は Pipelineマニフェストを扱う Adapterであり、それぞれ Buildkit を用いたBuild Taskと、IaCツールである Pulumi を用いたDeploy Taskを提供します。 BookInfoの残り3つのサービスのQVS Configも、reviewsと同様に、上記で紹介した3つのAdapterを使用します。 Tekton Pipelineの生成 次に、上述したBookInfoの4つのマイクロサービスに対する各QVS Configを指定してコンパイルします。 すると、各マイクロサービスごとのビルド用とデプロイ用のTekton Pipelineが自動生成されます。 $ kubectl get pipeline -o custom-columns=NAME:.metadata.name NAME details-build details-deploy productpage-build productpage-deploy rating-build rating-deploy reviews-build reviews-deploy Tekton Pipelineから使用されるTekton Taskも自動生成されます。 今回の場合、1つのマイクロサービスにつき5個、計20個のTekton Taskが生成されます。 $ kubectl get task -o custom-columns=NAME:.metadata.name | grep reviews reviews-app-compile-design-pattern reviews-app-deployment-worker reviews-auth-google-registry reviews-buildkit reviews-git-checkout # productpage, details, rating についても同様 生成されたTekton PipelineおよびTaskを以下の図に示します。 Build PipelineはソースコードからBuildkitを用いてdocker imageをビルドし、Google Artifact Registryにプッシュします。 Deploy Pipelineは、QVS Configで指定されているAdapterをコンパイルしてInfrastructureマニフェストを生成し、Pulumiを用いてKubernetesクラスターに対してデプロイします。 Adapterをコンパイルして生成されるTekton Pipelineは、CI/CDパイプライン全体を担うのではなく、上記例のようにBuild、Deploy、Test、Release…といったまとまったプロセス単位ごとに分割されます。 Tekton Pipelineを再利用可能で可搬性がある単位に分割することで、Tekton Pipelineをビルディングブロックのように扱い、柔軟にCI/CDパイプラインを組み立てることができます。 AssemblyLineの作成 最後に、AssemblyLineというQVS独自のリソースを用いてCI/CDパイプラインを構成します。 AssemblyLineに実行対象のTekton Pipelineと実行順序を定義することで、その順序に従いTekton Pipelineが実行されます。 今回は、BookInfoの4つのマイクロサービスをそれぞれビルドし、ステージング環境にデリバリするAssemblyLineを作成します。 作成したAssemblyLineを以下に記します。簡単のため、reviewsのビルドとデプロイに限定し、他の3つは省略しています。 apiVersion : vs.axis-dev.io/v1 kind : AssemblyLine metadata : name : bookinfo-build-and-deploy spec : params : - name : gitRevision - name : imageTag stages : - name : reviews-build-staging spec : deployment : app : reviews name : staging pipeline : reviews-build params : - name : gitRevision value : $(inputs.gitRevision) - name : imageTag value : $(inputs.imageTag) - name : reviews-deploy-staging runAfter : - reviews-build-staging spec : deployment : app : reviews name : staging pipeline : reviews-deploy params : - name : gitRevision value : $(inputs.gitRevision) - name : imageName value : $(stages.reviews-build-staging.results.imageFullNameTag) # 以下は省略 - name : rating-build-stating... - name : rating-deploy-stating... - name : productpage-build-stating... - name : productpage-deploy-stating... - name : details-build-stating... - name : details-deploy-stating.... AssemblyLineには複数のStageが設定可能であり、1つのStageは1つのTekton Pipelineで構成されます。 Stageごとに、Tekton Pipelineで記述された処理をどのマイクロサービスのどの環境向けに作用させるのか、を選択します。 各Stageの runAfter に依存するStageを指定することで、DAG(有向非巡回グラフ)が形成され、依存関係を持つ複数のStageを順番に実行できます。 また、各Stageの params を記述することで、Tekton Pipeline実行時のパラメータをどこから取得するかを定義します。 ここでは、Build Pipelineの引数である gitRevision や imageTag の値をAssemblyLine実行時のパラメータから取得しており、Deploy Pipelineの imageName の値をBuild Pipelineの実行結果から取得しています。 CI/CDパイプラインの構築が簡単に 以上、Adapterを用いてCI/CDパイプラインを構成する手順を紹介しました。 前述したように、Tekton、Kustomize、ArgoCDなどを用いてCI/CDパイプラインを構築するためには、各ツールに対するスキルに加えてツールごとに異なる様式で多くのマニフェストを記述し結合する必要があり、高い専門性が要求されます。 BookInfoの例では20〜30ほどのマニフェストが必要になり、アプリケーションの構成がより複雑化するにつれて大きく増大します。 QVSでは、ベストプラクティスとして提供されているAdapterを選択するだけで必要なTekton Pipelineが生成されるので、マニフェストを記述する必要がありません。 BookInfoのようなマイクロサービスアプリケーションの例であっても、生成されたTekton Pipelineの実行順序をAssemblyLineに記述するだけで、CI/CDパイプラインを構築できます。 リリースエンジニアリングの専門家でなくても、信頼性の高いCI/CDパイプラインを構成できるようになります。 CI/CDパイプラインの実行 前述した構成の元、 CI/CDパイプラインがどのように実行されるか説明します。 BookInfoのAssemblyLineマニフェストをQVSに登録すると、QVSのWebUI上で以下のようにAssemblyLineが表示されます。 今回は、各マイクロサービスごとにBuild PipelineのあとにDeploy Pipelineを実行するだけのシンプルなAssemblyLineなので2層だけですが、実際のAssemblyLineはCI/CDの様々なタスクや承認行為を含み、より複雑なパイプラインを構成することになります。 AssemblyLineを実行するためには、BookInfoを構成するマイクロサービスやデプロイ先の環境情報などをQVSに事前に入力する必要があります。 ここでも、マニフェストの記述量やパラメータ数を減らすための様々な工夫が導入されていますが、こちらについては別の記事で改めて紹介します。 AssemblyLineは、WebUI上から実行するか、gitのプッシュイベントをトリガとして実行されます。 実行されると、AssemblyLineの実行順序定義に従い先頭から順番にTekton PipelineRunおよびTaskRunが実行され、Kubernetes SchedulerによってPodがスケジューリングされて、各CI/CDタスクが実行されていきます。 WebUIでは、リアルタイムな進捗表示に加えて、Taskを実行しているPodのLogやEventなどの情報を確認できます。 Taskが正常に動作していない場合には、Tektonが動作しているKubernetesクラスターにアクセスすることなく、WebUI上から問題の切り分けを行うことが可能です。 以下のように全てのStageに正常終了のマークがつけば、AssemblyLineの処理は完了です。 デプロイ先のKubernetesクラスターにアクセスして、BookInfoアプリケーションが正常動作していることを確認します。以下の通り、4つのマイクロサービスのDeploymentとServiceが生成され、全てのPodが上がっていることが確認できます。 $ kubectl get all -n sample-bookinfo NAME READY STATUS RESTARTS AGE pod/details-78457fc5c9-7szn5 1/1 Running 0 12h pod/productpage-7fccdbdb9c-8cqw8 1/1 Running 0 12h pod/rating-6c8db77566-gfkxj 1/1 Running 0 12h pod/reviews-855b779f4d-xqs7t 1/1 Running 0 12h NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/details ClusterIP 10.44.12.16 <none> 9080/TCP 12h service/productpage ClusterIP 10.44.10.189 <none> 9080/TCP 12h service/rating ClusterIP 10.44.0.210 <none> 9080/TCP 12h service/reviews ClusterIP 10.44.0.249 <none> 9080/TCP 12h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/details 1/1 1 1 12h deployment.apps/productpage 1/1 1 1 12h deployment.apps/rating 1/1 1 1 12h deployment.apps/reviews 1/1 1 1 12h NAME DESIRED CURRENT READY AGE replicaset.apps/details-78457fc5c9 1 1 1 12h replicaset.apps/productpage-7fccdbdb9c 1 1 1 12h replicaset.apps/rating-6c8db77566 1 1 1 12h replicaset.apps/reviews-855b779f4d 1 1 1 12h productpageのPodにport-forwardしてUIを表示してみると、画面が表示され、正常にデプロイできていることが確認できます。 おわりに DevOpsプラットフォームの取り組み連載の2回目の記事として、Cloud Native Adapterを使用することでクラウド基盤とCI/CDパイプラインが簡単に構築できることを、2つの事例を用いて紹介しました。 NTTコミュニケーションズでは多くのプロジェクトで内製開発をしており、開発チームがプロダクト開発の最も重要な部分に注力できるように、Qmonus Value Streamの開発とCloud Native Adapterの拡充を進めています。 Qmonus Value Streamチームは、メンバ一人一人が、利用者であるアプリケーション開発者のために信念を持って活動を続けています。 プラットフォームを内製開発するポジション、プラットフォームを使ってNTTグループのクラウド基盤やCI/CDを構築支援するポジションそれぞれで 、一緒に働く仲間を募集していますので、興味を持たれた方は応募いただけると嬉しいです。 https://hrmos.co/pages/nttcom0033/jobs/0000019 https://hrmos.co/pages/nttcom0033/jobs/1692796 次回は、Cloud Native Adapterについてさらに踏み込んで紹介します。お楽しみに!
アバター
はじめに DevOpsプラットフォームの取り組みを紹介する2回目の記事の前半です。 こんにちは。 Qmonus (クモナス) Value Stream の開発チームの松本です。 前回の記事 では、 Qmonus Value Stream の概要を紹介しました。 連載第2回となる今回は、Qmonus Value Streamの重要で特徴的な機能の1つであるCloud Native Adapterを使い、アプリケーションをデリバリする事例を2つ、前編と後編の2回に分けて紹介します。 前編では、Cloud Native Adapterを用いたクラウド基盤の構築を中心に説明します。 クラウド技術の多様化・複雑化 近年のアプリケーション開発において、GCP/Azure/AWS等のパブリッククラウドの利用はデファクトスタンダードとなりました。 また、多くのプロダクトはコンテナアプリケーションとして開発され、Kubernetesやその他のコンテナ実行環境を使ってサービス提供されています。 システムの基盤はパブリッククラウドを使う事で迅速に構築できるようになりましたが、パブリッククラウドが提供するサービスは多様化・複雑化しており、要件によっては監視や認証、メール送信などで別々のパブリッククラウドやSaaSを組み合わせる必要があります。 それぞれのサービスには適切なパラメータの決定も必要です。 商用提供しているプロダクトでは、開発環境やステージング環境そしてプロダクション環境と複数の環境を適切に構築するために、Infrastructure as Code(以下、「IaC」といいます)の導入も必要となるでしょう。 このように、クラウド技術によりアジリティは大幅に向上したものの、要件に応じてクラウドサービスを組み合わせてアーキテクチャを設計し構築するためには、高い専門性だけではなく多大な労力をも必要とするのが現状の課題です。 Qmonus Value Stream(以下、「QVS」といいます)では、このような課題をCloud Native Adapter(以下、「Adapter」といいます)を提供して解決します。 ユースケース1: KubernetesとGCPによるパブリックAPI公開 アプリケーションのデリバリを実践する1つ目の事例として、GCPのWAF/FirewallであるCloud Armorを適用したWebアプリケーションを構築します。 コンテナでWebアプリケーションを公開する際には、多くの場合GKEのようなマネージドKubernetesサービスを利用しますが、マネージドKubernetesを用意するだけではWebアプリケーションを公開できません。 例えば、WEBアプリケーションをWAFを適用して提供する場合、マネージドKubernetesの準備に付随して次の作業が必要となります。 グローバルIPアドレスの確保 確保したグローバルIPアドレスに名前解決するためのDNSレコードの設定 SSL/TLSサーバ証明書の発行 適切なSSL/TLS暗号スイートの設定 WAF/Firewallのポリシー設定 ロードバランサの設定 不適切な設定や設定漏れがあると、アプリケーションが動作しないだけではなく、最悪の場合セキュリティホールを作ってしまいプロダクトの継続に大きな悪影響を与えてしまうので、確実な実施が必要です。 これらのリソースを構築するには、TerraformでCloud ArmorやGCLBなどのGCPリソースを構築し、そして、Kustomizeなどを使ってService・Ingress・BackendConfigなど多くのKubernetesマニフェストを記述し適用する必要があります。 また、これら多くのサービスを相互参照しながら正しく構成する必要があります。意図した動作をしない場合には、利用する複数サービスのログやマニフェストを調査して修正した後に再度動作確認するということを繰り返すこととなります。 このように、QVSを使わない場合には、パブリッククラウド、Kubernetes、IaCツールに対する高い専門性と多くの時間が要求されます。 Qmonus Value Streamを用いたクラウド基盤構築 前回ご紹介したように、Adapterは、クラウド基盤とContinuous Integration/Continuous Delivery(以下、「CI/CD」といいます)パイプラインを構築するためのベストプラクティスをパッケージ化したものです。 アプリケーション開発者は、Adapterを選択しパラメータを指定するだけで、クラウド基盤とCI/CDパイプラインを高い専門知識がなくても構築できます。 Adapterは、デプロイ対象のApplicationマニフェストと、Pipelineマニフェストの両方を生成します。 Applicationマニフェストは、Kubernetesリソースやクラウドリソースにより構成され、デプロイするアプリケーションのシステム構成を示します。 Pipelineマニフェストは、CI/CDパイプラインのビルドからリリースに至る過程において「いつどのようなTaskを実行するか」を定義しています。QVSはCI/CDの実行エンジンとして Tekton を採用しており、PipelineマニフェストはTekton Pipeline/Taskリソースとして記述されます。 Adapterの選択 QVSの設定ファイルであるQVS Configを作成し、その中で使用するAdapterを選択します。 以下に、QVS Configの具体例を示します。Adapterは、 designPatterns の下に配列で配置されます。 params : - name : namespace type : string - name : imageName type : string designPatterns : - pattern : qmonus.net/adapter/official/kubernetes/deployment/simple params : appName : public-api-test-app namespace : $(params.namespace) imageName : $(params.imageName) port : "5000" replicas : "1" - pattern : qmonus.net/adapter/official/kubernetes/gke/publicapi params : appName : public-api-test-app namespace : $(params.namespace) port : "5000" domainName : public-api-app-demo1.example.com gcpExternalAddressName : matsumoto-gip2 gcpSecurityPolicyName : matsumoto-policy1 gcpSslPolicyName : matsumoto-ssl-policy - pattern : qmonus.net/adapter/official/pipeline/build:buildkit - pattern : qmonus.net/adapter/official/pipeline/deploy:simple 1つ目と2つ目のAdapterは、クラウド基盤の構築を担当し、1つ目は下図の赤破線、2つ目は下図の青破線で囲まれた場所を構築します。 1つ目のAdapterではKubernetesのDeploymentを生成し、2つ目のAdapterではGCPのWAF/FirewallであるCloud ArmorやCloud Load Balancingを構築しつつ、連携するようにKubernetesのService、Ingress、ManagedCertificate、BackendConfig、FrontendConfigを構成します。 これらのリソースを全てTerraformやKubernetesマニフェストで記述すると多くのリソースが必要になり複雑ですが、Adapterを使えば上記の通り簡潔に記述できます。 各Adapterは、実行時にパラメータを外部から注入できます。 1つ目のAdapterでは、外部から注入されたimageNameパラメータによりコンテナイメージとバージョンを受け取り、Kubernetes Deploymentを生成しています。 パラメータとして注入するコンテナイメージとバージョンを変更することで、QVS Config自体に変更を加えることなく簡単にバージョンアップが実現できます。 Adapterにおけるパラメータの正規化・外部インターフェース化の機構はCUE言語の表現力を用いて実現されています。 この詳細については、別の記事で改めて紹介します。 3つ目と4つ目のAdapterは、CI/CDパイプラインを構築するものです。 この部分の詳細は後編で説明します。 今回説明した4つのAdapterは、全てQVS開発チームが提供し保守しているOfficial Cloud Native Adapter(以下、「Official Adapter」といいます)です。 広く一般的に使われるクラウド構成や CI/CDパイプラインの基本となるタスクについては、Official Adapterに含まれているので、そのまま利用できます。 CI/CDパイプラインの構成 QVSでは、AssemblyLineという独自のリソースを用いてCI/CDパイプラインを構成し、Adapterより生成されたTekton Pipelineを実行することでアプリケーションをデリバリします。 AssemblyLineの詳細については、後編でご説明します。 CI/CDパイプラインの実行 AssemblyLineマニフェストをQVSに登録し実行すると、Build PipelineとDeploy Pipelineが実行されて、Adapterより生成される各種ApplicationマニフェストがGCPとGKEに対してデプロイされます。 AssemblyLineを実行すると、QVSのWebUIから進捗状況が確認できます。 Build PipelineとDeploy Pipelineが実行されて、処理が正常終了したことを確認できました。 デプロイ先のGKEにアクセスし、アプリケーションが正常に動作していることを確認します。以下の通り、DeploymentやService、Ingressが正しく作成されていました。 % kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE public-api-test-app 1/1 1 1 1h %kubectl get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE public-api-test-app NodePort 10.0.72.51 <none> 5000:32663/TCP 1h %kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE public-api-test-app <none> * 203.0.113.10 80 1h また、GCPのコンソールからもWebアプリケーション提供に必要となるCloud ArmorやロードバランサなどのGCPリソースが適切に作成されていることが確認できます。 以下のCloud Armorの画面で、ロードバランサにポリシーが適用されています。 以下のロードバランサの画面では、SSLのポリシーや証明書などが設定されておりバックエンドであるKubernetesに転送されるように設定されています。 そして、実際に構築したWebアプリケーション( https://public-api-app-demo1.example.com )にアクセスすると、サンプルアプリケーションからの応答が確認できました。 前編では、複雑なTerraformやKubernetesマニフェストの記述する代わりに、QVSチームから提供されるOfficial Adapterをいくつか選択し記述するだけでクラウド基盤を構築する事例を紹介しました。 後編 に続きます。
アバター
サマリ Multi-AS で構成されるネットワークにおいて、 SR-MPLS + EVPN による L2VPN を実現 IOS XR + Junos の Multi-vendor 環境で動作検証に成功 この記事は Multi-AS SR 検証連載の第 3 回です。目次は こちら 概要 イノベーションセンターの田島です。 本記事では Single-/Multi-AS SR-MPLS での L2VPN について解説します。 我々の一連の検証では 第 0 回の記事 にもありますように、 Multi-AS でアンダーレイを構築することで拡張性や保守性を担保したまま拡張可能なネットワークを検討しています。 第 2 回の記事 では Multi-AS/Multi-domain SR-MPLS で分割されているアンダーレイを用いて L3VPN を実現しました。 本記事は同様のアンダーレイで L2VPN を実現します。 なお L2VPN という総称は、 2 点間の接続 (point-to-point) を仮想的に L2 レベルで行う Pseudo Wire や VPWS 1 とも呼ばれる E-Line と、複数拠点間 (point-to-multipoint) を仮想的に L2 スイッチングする VPLS 2 とも呼ばれる E-LAN の 2 種類を指すことがあります。 本記事では後者の、複数拠点間の E-LAN を対象にします。 このあとは L2VPN の仕組みについて概説したのち、 Multi-vendor 環境における検証トポロジーや設定例を紹介します。 Segment Routing (SR) や Multi-AS/Multi-domain SR-MPLS についての紹介は 第 0 回の記事の目次 から L3VPN の各記事をご参照ください。 EVPN による L2VPN L2VPN 実現の手段として EVPN (Ethernet VPN) があります。 EVPN は RFC7432 - BGP MPLS-Based Ethernet VPN で規格化された L2VPN の方式で、経路情報の交換や転送が定められています。 誤解を恐れずに表現すると L2 における経路とはつまり MAC アドレスのことです。 EVPN では、各ルーターは MP-BGP 3 を用いて交換された MAC アドレスを学習し、照合することで各 MAC アドレス宛てのフレームを転送します。 上記の図は L2VPN の模式図で、複数台のルーターからなるコアネットワークがあたかも単一の巨大な L2SW のように転送する様子を表しています。 顧客 A と B それぞれ専用の L2SW が存在し、各 PE からアクセスできるイメージです。 顧客 A と B の間では通信ができず、それぞれ独立しています。 なお先ほど交換する経路とは L2 の MAC アドレスのことであるとしましたが、実際のところ EVPN においては MAC アドレスの他に L3 情報である IP アドレスや Ethernet Segment 識別子 (ESI) なども交換することにより、マルチホーミングなどの単なる L2VPN 以上の機能を実現しています。 機能が広範囲にわたる EVPN では交換する情報を Route Type として定義しています。 この記事内では Type 2 を詳細に見ますが、そのほかにも Type はあります。 Type 1 は Ethernet Auto-Discovery とよばれ、 ESI を広告し障害時の高速な収束などのために使われます。 Type 2 は MAC アドレスや IP アドレスを扱います。これは文字通り、 CE 側の端末のアドレスを交換します。 Type 3 は BUM トラフィックの送信先に使われます。 Type 4 は ESI に基づきマルチホーム時のトラフィック制御に使われます。 EVPN においては VLAN との対応付けに際して、 3 つの方式があります。 VPN を識別して転送するために EVPN Instance (EVI) / Bridge Domain (BD) / Broadcast Domain (VLAN) をどう構成するかによって、下記のような 3 方式があります。 VLAN-Based: それぞれ1対1で対応する。単一の EVI には 単一の BD が対応し、それには単一の VLAN のみが対応する。 VLAN Bundle: EVI と BD は 1 対 1 だが、BD には複数の VLAN が所属する。 VLAN-Aware Bundle: EVI には 複数の BD が所属するが、各 BD にはそれぞれ単一の VLAN が対応する。 今回はわかりやすく各インスタンスが 1 対 1 で対応する 1. VLAN-Based を用いて検証します。 なお、方式ごとに設定方法があるため、特に Multi-vendor 環境においてはどの方式がどの設定に対応しているのかは要確認です。 これら3つに相互運用性はなく、同じ方式にそろえる必要があります。 実際に我々も検証中に 1. VLAN-Based と 3. VLAN-Aware が混在し相互接続できなくなり原因調査することがありました。 EVPN と Inter-AS Option B の対応状況 Multi-AS/Multi-domain SR-MPLS で分割されているアンダーレイを通じて VPN を実現する際に、拡張性を求めると Option B での実装が望ましいことは 第 2 回の記事 で示しました。 当該記事中でも言及しましたが、このようなアンダーレイにおいて EVPN を Option B で AS 間接続する実装は始まったばかりです。 具体的には Cisco ASR 9000 シリーズでは IOS XR 7.4.1 から 4 実装されています。 2022年7月現在では、 ASBR として使用できる機器が限られています。 Single-AS での L2VPN まずは単純な Single-AS でのアンダーレイ環境において L2VPN を実現することで、基本的な設定を紹介します。 トポロジーは下記のような、 IOS XR と Junos それぞれ 1 台ずつ、 PE が P ルーターを介して接続された状態です。 使用した機材および OS バージョンは次の通りです。 PE PE1: Cisco ASR9901 (IOS XR 7.6.1) PE2: Juniper MX204 (Junos 21.3R1.9) P P1: Cisco C8201 (IOS XR 7.5.1) P2: Juniper PTX10001 (Junos 21.4R1.15-EVO) IGP や SR の設定は割愛します。下記の設定および確認を順に行います。 CE 接続インターフェースの設定 EVI と BD の設定 BGP の設定 経路の確認 疎通試験 なお検証の都合上、 PE では顧客 A と B は同じ物理ポートで VLAN で分けて収容することとします。 CE 接続インターフェースの設定 PE1 (IOS XR) 側 後の BD にはタグなしとして見せるため VLAN タグを取り外す設定を入れておきます。 interface TenGigE0/0/0/32.1000 l2transport encapsulation dot1q 1000 rewrite ingress tag pop 1 symmetric ! interface TenGigE0/0/0/32.2000 l2transport encapsulation dot1q 2000 rewrite ingress tag pop 1 symmetric ! PE2 (Junos) 側 この時点で既にインターフェースに ESI を設定しておきます。 set interfaces xe-0/1/6 flexible-vlan-tagging set interfaces xe-0/1/6 encapsulation flexible-ethernet-services set interfaces xe-0/1/6 esi 00:00:00:00:00:00:00:00:00:02 set interfaces xe-0/1/6 esi all-active set interfaces xe-0/1/6 unit 1000 encapsulation vlan-bridge set interfaces xe-0/1/6 unit 1000 vlan-id 1000 set interfaces xe-0/1/6 unit 2000 encapsulation vlan-bridge set interfaces xe-0/1/6 unit 2000 vlan-id 2000 EVI と BD の設定 PE1 (IOS XR) 側 ESI と RT を設定します。 advertise-mac の有無で EVI ごとに Type-2 経路として MAC を出すかを制御できます。 evpn evi 1000 bgp route-target import 65001:1000 route-target export 65001:1000 ! control-word-disable advertise-mac ! ! evi 2000 bgp route-target import 65001:2000 route-target export 65001:2000 ! control-word-disable advertise-mac ! ! interface TenGigE0/0/0/32 ethernet-segment identifier type 0 00.00.00.00.00.00.00.00.01 ! ! ! BD を設定します。 顧客 A 用と、顧客 B 用、それぞれの EVI を追加しておきます。 l2vpn bridge group EVPN bridge-domain 1000 interface TenGigE0/0/0/32.1000 ! evi 1000 ! ! bridge-domain 2000 interface TenGigE0/0/0/32.2000 ! evi 2000 ! ! ! ! PE2 (Junos) 側 EVI 兼 BD の routing-instance を作成します。 RT も設定します。 set policy-options policy-statement EXPORT-POLICY-1000 term ROUTE-TARGET then community add RT1000 set policy-options policy-statement IMPORT-POLICY-1000 term ROUTE-TARGET-iBGP from community RT1000-iBGP set policy-options policy-statement IMPORT-POLICY-1000 term ROUTE-TARGET-iBGP then accept set policy-options community RT1000 members target:65001:1000 set policy-options community RT1000-iBGP members target:65001:1000 set policy-options policy-statement EXPORT-POLICY-2000 term ROUTE-TARGET then community add RT2000 set policy-options policy-statement IMPORT-POLICY-2000 term ROUTE-TARGET-iBGP from community RT2000-iBGP set policy-options policy-statement IMPORT-POLICY-2000 term ROUTE-TARGET-iBGP then accept set policy-options community RT2000 members target:65001:2000 set policy-options community RT2000-iBGP members target:65001:2000 set routing-instances evpn-1000 instance-type evpn set routing-instances evpn-1000 protocols evpn set routing-instances evpn-1000 vlan-id none set routing-instances evpn-1000 interface xe-0/1/6.1000 set routing-instances evpn-1000 route-distinguisher 10.255.1.2:1000 set routing-instances evpn-1000 vrf-import IMPORT-POLICY-1000 set routing-instances evpn-1000 vrf-target target:65001:1000 set routing-instances evpn-2000 instance-type evpn set routing-instances evpn-2000 protocols evpn set routing-instances evpn-2000 vlan-id none set routing-instances evpn-2000 interface xe-0/1/6.2000 set routing-instances evpn-2000 route-distinguisher 10.255.1.2:2000 set routing-instances evpn-2000 vrf-import IMPORT-POLICY-2000 set routing-instances evpn-2000 vrf-target target:65001:2000 BGP の設定 PE1 (IOS XR) 側 RT の設定などは EVI の項で行ったため、 address-family の宣言のみです。 router bgp 65001 bgp router-id 10.255.1.1 address-family l2vpn evpn ! neighbor 10.255.1.2 remote-as 65001 update-source Loopback0 address-family l2vpn evpn ! ! ! PE2 (Junos) 側 こちらもネイバーの定義のみです。 set protocols bgp family evpn signaling set protocols bgp group blog type internal set protocols bgp group blog local-address 10.255.1.2 set protocols bgp group blog family evpn signaling set protocols bgp group blog neighbor 10.255.1.1 経路の確認 まず PE1 から PE2 方向に経路を見ていきます。 PE1 での MAC アドレス学習結果と、それぞれの EVI に対する VPN ラベルがついていることが確認できます。 RP/0/RSP0/CPU0:PE1#show evpn evi vpn-id 1000 mac Wed Jun 29 15:04:57.503 JST VPN-ID Encap MAC address IP address Nexthop Label SID ---------- ---------- -------------- ---------------------------------------- --------------------------------------- -------- --------------------------------------- 1000 MPLS 0200.0011.1111 :: TenGigE0/0/0/32.1000 24010 RP/0/RSP0/CPU0:PE1#show evpn evi vpn-id 2000 mac Wed Jun 29 15:05:04.384 JST VPN-ID Encap MAC address IP address Nexthop Label SID ---------- ---------- -------------- ---------------------------------------- --------------------------------------- -------- --------------------------------------- 2000 MPLS 0200.0033.3333 :: TenGigE0/0/0/32.2000 24000 これらの VPN ラベルが着信すると EVPN として処理されます。 RP/0/RSP0/CPU0:PE1#show mpls forwarding Wed Jun 29 15:08:24.725 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ (snip) 24000 Pop EVPN:2000 U BD=0 E point2point 8136 (snip) 24010 Pop EVPN:1000 U BD=1 E point2point 0 (snip) PE2 側では顧客 A の経路を学習しています。相手の VPN ラベルが 24010 であることも認識しています。 PE2 に CE から着信したフレームは VPN ラベルと、 PE1 の SID を付けて網内に転送されます。 user@PE2> show route table evpn-1000.evpn.0 evpn-mac-address 02:00:00:11:11:11 extensive evpn-1000.evpn.0: 7 destinations, 15 routes (7 active, 0 holddown, 0 hidden) 2:10.255.1.1:1000::0::02:00:00:11:11:11/304 MAC/IP (3 entries, 1 announced) *BGP Preference: 170/-101 Route Distinguisher: 10.255.1.1:1000 Next hop type: Indirect, Next hop index: 0 Address: 0x853435c Next-hop reference count: 51 Source: 10.255.1.1 Protocol next hop: 10.255.1.1 Indirect next hop: 0x2 no-forward INH Session ID: 0x0 State: <Secondary Active Int Ext> Local AS: 65001 Peer AS: 65001 Age: 6:34 Metric2: 20 Validation State: unverified ORR Generation-ID: 0 Task: BGP_65001.10.255.1.1 Announcement bits (1): 0-evpn-1000-evpn AS path: I Communities: target:65001:1000 origin:10.255.1.1:1000 unknown iana evpn 0x60e:0x0:0x3e8 Import Accepted Route Label: 24010 ESI: 00:00:00:00:00:00:00:00:00:01 Localpref: 100 Router ID: 10.255.1.1 Primary Routing Table: bgp.evpn.0 Thread: junos-main Indirect next hops: 1 Protocol next hop: 10.255.1.1 Metric: 20 Indirect next hop: 0x2 no-forward INH Session ID: 0x0 Indirect path forwarding next hops: 1 Next hop type: Router Next hop: 10.1.2.1 via xe-0/1/0.0 Session Id: 0x0 10.255.1.1/32 Originating RIB: inet.3 Metric: 20 Node path count: 1 Forwarding nexthops: 1 Next hop type: Router Next hop: 10.1.2.1 via xe-0/1/0.0 Session Id: 0x0 疎通試験 顧客 A の CE に相当する VM を両端で 2 台作成し、その間で ping による通信を確認しました。 ping による通信確認のための 1 台目の IP 設定と ping の実施例です。 L3VPN とは異なり、 L2VPN では途中経路を CE 側から調査する方法がないため、 ping が通ることを確認するのみです。 user@vm01:~$ ip a (snip) 4: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 02:00:00:11:11:11 brd ff:ff:ff:ff:ff:ff inet6 fe80::ff:fe11:1111/64 scope link valid_lft forever preferred_lft forever (snip) 6: ens224.1000@ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 02:00:00:11:11:11 brd ff:ff:ff:ff:ff:ff inet 192.168.0.1/24 scope global ens224.1000 valid_lft forever preferred_lft forever inet6 fe80::ff:fe11:1111/64 scope link valid_lft forever preferred_lft forever user@vm01:~$ ping 192.168.0.2 PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data. 64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.758 ms 64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.485 ms 64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=0.440 ms ^C --- 192.168.0.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2025ms rtt min/avg/max/mdev = 0.440/0.561/0.758/0.140 ms 2 台目における IP 設定と、パケットキャプチャによる確認です。 user@vm02:~$ ip a (snip) 4: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 02:00:00:22:22:22 brd ff:ff:ff:ff:ff:ff inet6 fe80::ff:fe22:2222/64 scope link valid_lft forever preferred_lft forever (snip) 6: ens224.1000@ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 02:00:00:22:22:22 brd ff:ff:ff:ff:ff:ff inet 192.168.0.2/24 scope global ens224.1000 valid_lft forever preferred_lft forever inet6 fe80::ff:fe22:2222/64 scope link valid_lft forever preferred_lft forever user@vm02:~$ sudo tcpdump -i ens224.1000 -n -nn -e tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on ens224.1000, link-type EN10MB (Ethernet), capture size 262144 bytes 20:18:36.037958 02:00:00:11:11:11 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 60: Request who-has 192.168.0.2 tell 192.168.0.1, length 46 20:18:36.038032 02:00:00:22:22:22 > 02:00:00:11:11:11, ethertype ARP (0x0806), length 42: Reply 192.168.0.2 is-at 02:00:00:22:22:22, length 28 20:18:36.038364 02:00:00:11:11:11 > 02:00:00:22:22:22, ethertype IPv4 (0x0800), length 98: 192.168.0.1 > 192.168.0.2: ICMP echo request, id 1, seq 1, length 64 20:18:36.038394 02:00:00:22:22:22 > 02:00:00:11:11:11, ethertype IPv4 (0x0800), length 98: 192.168.0.2 > 192.168.0.1: ICMP echo reply, id 1, seq 1, length 64 20:18:37.039357 02:00:00:11:11:11 > 02:00:00:22:22:22, ethertype IPv4 (0x0800), length 98: 192.168.0.1 > 192.168.0.2: ICMP echo request, id 1, seq 2, length 64 20:18:37.039405 02:00:00:22:22:22 > 02:00:00:11:11:11, ethertype IPv4 (0x0800), length 98: 192.168.0.2 > 192.168.0.1: ICMP echo reply, id 1, seq 2, length 64 20:18:38.063289 02:00:00:11:11:11 > 02:00:00:22:22:22, ethertype IPv4 (0x0800), length 98: 192.168.0.1 > 192.168.0.2: ICMP echo request, id 1, seq 3, length 64 20:18:38.063320 02:00:00:22:22:22 > 02:00:00:11:11:11, ethertype IPv4 (0x0800), length 98: 192.168.0.2 > 192.168.0.1: ICMP echo reply, id 1, seq 3, length 64 20:18:41.054244 02:00:00:22:22:22 > 02:00:00:11:11:11, ethertype ARP (0x0806), length 42: Request who-has 192.168.0.1 tell 192.168.0.2, length 28 20:18:41.054632 02:00:00:11:11:11 > 02:00:00:22:22:22, ethertype ARP (0x0806), length 60: Reply 192.168.0.1 is-at 02:00:00:11:11:11, length 46 ^C 10 packets captured 10 packets received by filter 0 packets dropped by kernel このようにして、 Multi-vendor での Single-AS における L2VPN が実現できました。次にこれを Multi-AS へと拡張します。 Multi-AS での L2VPN Multi-AS へと拡張すると、トポロジーは次の図のようになります。 ASBR は前述の通り対応している機器が限られるため Multi-vendor の環境ではありません。 PE はそれぞれの AS で IOS XR と Junos を 1 台ずつ、 ASBR と P ルーターを介して接続された状態です。 図中ではループバックアドレスの採番や Node SID の採番が AS 間で重複しないようになっていますが、これは説明のわかりやすさのためで、実際には重複することも可能です。ただしループバックアドレスをルーター ID として使用している場合は、特に EVI の RD に気をつける必要があります。 使用した機材および OS バージョンは次の通りです。 PE PE1: Cisco ASR9901 (IOS XR 7.6.1) PE2: Juniper MX204 (Junos 21.3R1.9) P P1: Cisco C8201 (IOS XR 7.5.1) P2: Juniper PTX10001 (Junos 21.4R1.15-EVO) P3: Cisco NCS55A2 (IOS XR 7.5.1) P4: Juniper MX10003 (Junos 21.4R1.12) ASBR ASBR1 および ASBR2:Cisco ASR9902 (IOS XR 7.6.1) IGP や SR の設定、および前述の CE 接続インターフェースの設定は Single-AS と同等なので割愛します。 下記の各項目で、主に Single-AS との差異を順に示します。 EVI と BD の設定 BGP の設定 経路の確認 疎通試験 EVI と BD の設定 Route Target を相互に学習するための仮想的な AS: 64999 を設定し、有効にします。 BD の設定は Single-AS と同等なので割愛します。 PE1 (IOS XR) 側 evpn evi 1000 bgp route-target import 64999:1000 route-target import 65001:1000 route-target export 64999:1000 route-target export 65001:1000 ! control-word-disable advertise-mac ! ! evi 2000 bgp rd 65002:2000 route-target import 64999:2000 route-target import 65001:2000 route-target export 64999:2000 route-target export 65001:2000 ! control-word-disable advertise-mac ! ! interface TenGigE0/0/0/32 ethernet-segment identifier type 0 00.00.00.00.00.00.00.00.01 ! ! ! PE2 (Junos) 側 送受信で RT を増やします。 set policy-options policy-statement EXPORT-POLICY-1000 term ROUTE-TARGET then community add RT1000 set policy-options policy-statement IMPORT-POLICY-1000 term ROUTE-TARGET-iBGP from community RT1000-iBGP set policy-options policy-statement IMPORT-POLICY-1000 term ROUTE-TARGET-iBGP then accept set policy-options policy-statement IMPORT-POLICY-1000 term ROUTE-TARGET-eBGP from community RT1000-eBGP set policy-options policy-statement IMPORT-POLICY-1000 term ROUTE-TARGET-eBGP then accept set policy-options community RT1000 members [ target:64999:1000 target:65001:1000 target:65002:1000 ] set policy-options community RT1000-iBGP members target:65001:1000 set policy-options community RT1000-eBGP members target:64999:1000 set policy-options policy-statement EXPORT-POLICY-2000 term ROUTE-TARGET then community add RT2000 set policy-options policy-statement IMPORT-POLICY-2000 term ROUTE-TARGET-iBGP from community RT2000-iBGP set policy-options policy-statement IMPORT-POLICY-2000 term ROUTE-TARGET-iBGP then accept set policy-options policy-statement IMPORT-POLICY-2000 term ROUTE-TARGET-eBGP from community RT2000-eBGP set policy-options policy-statement IMPORT-POLICY-2000 term ROUTE-TARGET-eBGP then accept set policy-options community RT2000 members [ target:64999:2000 target:65001:2000 target:65002:2000 ] set policy-options community RT2000-iBGP members target:65001:2000 set policy-options community RT2000-eBGP members target:64999:2000 set routing-instances evpn-1000 instance-type evpn set routing-instances evpn-1000 protocols evpn set routing-instances evpn-1000 vlan-id none set routing-instances evpn-1000 interface xe-0/1/6.1000 set routing-instances evpn-1000 route-distinguisher 10.255.1.2:1000 set routing-instances evpn-1000 vrf-import IMPORT-POLICY-1000 set routing-instances evpn-1000 vrf-target target:64999:1000 set routing-instances evpn-2000 instance-type evpn set routing-instances evpn-2000 protocols evpn set routing-instances evpn-2000 vlan-id none set routing-instances evpn-2000 interface xe-0/1/6.2000 set routing-instances evpn-2000 route-distinguisher 10.255.1.2:2000 set routing-instances evpn-2000 vrf-import IMPORT-POLICY-2000 set routing-instances evpn-2000 vrf-target target:64999:2000 BGP の設定 このトポロジーでは下記の 3 つの BGP セッションが最低限必要です。 PE1 <-> ASBR1: AS 65001 内の IBGP ASBR1 <-> ASBR2: AS 65001 と AS 65002 間の EBGP PE2 <-> ASBR2: AS 65002 内の IBGP これらのうち 3. は 1. と同等のため割愛します。 まず 1. について、 ASBR で next-hop-self の設定が必要になります。 ASBR1 (IOS XR) 側 router bgp 65001 bgp router-id 10.255.1.7 neighbor 10.255.1.1 remote-as 65001 address-family l2vpn evpn next-hop-self 次に 2. について、 ASBR 間では Option B を有効にします。また、 EBGP ではデフォルトで経路が拒否されるため検証用にすべてを許可します。 ASBR2 側も同様です。 ASBR1 (IOS XR) 側 router bgp 65001 bgp router-id 10.255.1.7 address-family l2vpn evpn label mode per-nexthop-received-label retain route-target all option-b-asbr-only ! neighbor 10.100.2.2 remote-as 65002 address-family l2vpn evpn route-policy pass-all in route-policy pass-all out route-policy pass-all pass end-policy 経路の確認 Single-AS と同様に PE1 から PE2 方向に経路を見ていきます。 PE1 での MAC アドレス学習結果と、 VPN ラベル 24010 がついていることが確認できます。 RP/0/RSP0/CPU0:PE1#show evpn evi vpn-id 1000 mac Thu Jun 30 15:11:19.848 JST VPN-ID Encap MAC address IP address Nexthop Label SID ---------- ---------- -------------- ---------------------------------------- --------------------------------------- -------- --------------------------------------- 1000 MPLS 000c.2915.e72d :: 10.255.1.7 24010 1000 MPLS 000c.2915.e72d 192.168.0.4 10.255.1.7 24010 1000 MPLS 0200.0011.1111 :: TenGigE0/0/0/32.1000 24010 1000 MPLS 0200.0022.2222 :: 10.255.1.2 122 1000 MPLS 0200.0022.2222 192.168.0.2 10.255.1.2 122 ASBR1 では Inter-AS の VPN から受信したときに AS 65001 内の VPN へと Swap します。 ここでは Inter-AS 側から 24020 を受け取ったときに 24010 へと Swap して PE1 に送ることから、 Inter-AS ではこの VPN 用に 24020 が使われていることがわかります。 RP/0/RP0/CPU0:ASBR1#show mpls forwarding Thu Jun 30 15:13:15.420 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ (snip) 24020 24010 No ID 10.255.1.1 0 (snip) ASBR2 では AS 65002 内で VPN 識別に使用するラベルを生成し、そのラベルがついたものを 24020 へと Swap し Inter-AS に送り出します。 ここでは Inter-AS 向けの出力で 24020 を使用するルールを探すことで AS 65002 内では 24013 がこの VPN 用に使用されていることがわかります。 RP/0/RP0/CPU0:ASBR2#show mpls forwarding Thu Jun 30 15:18:38.479 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ (snip) 24013 24020 No ID Te0/0/0/25 10.100.2.1 0 (snip) PE2 で学習される経路とラベルを見ると、上記で確認した 24013 であり、 ASBR2 で正しく転送されるラベルが学習されています。 user@PE2> show route table evpn-1000.evpn.0 evpn-mac-address 02:00:00:11:11:11 extensive evpn-1000.evpn.0: 13 destinations, 22 routes (13 active, 0 holddown, 0 hidden) 2:10.255.1.1:1000::0::02:00:00:11:11:11/304 MAC/IP (2 entries, 1 announced) *BGP Preference: 170/-101 Route Distinguisher: 10.255.1.1:1000 Next hop type: Indirect, Next hop index: 0 Address: 0xc2275dc Next-hop reference count: 62 Source: 10.255.2.5 Protocol next hop: 10.255.2.1 Indirect next hop: 0x2 no-forward INH Session ID: 0x0 State: <Secondary Active Int Ext> Local AS: 65002 Peer AS: 65002 Age: 21:38 Metric2: 30 Validation State: unverified ORR Generation-ID: 0 Task: BGP_65002.10.255.2.5 Announcement bits (1): 0-evpn-1000-evpn AS path: 65001 I (Originator) Cluster list: 0.0.0.1 Originator ID: 10.255.2.1 Communities: target:64999:1000 target:65001:1000 origin:10.255.1.1:1000 unknown iana evpn 0x60e:0x0:0x3e8 Import Accepted Route Label: 24013 ESI: 00:00:00:00:00:00:00:00:00:01 Localpref: 100 Router ID: 10.255.2.1 Primary Routing Table: bgp.evpn.0 Thread: junos-main Indirect next hops: 1 Protocol next hop: 10.255.2.1 Metric: 30 Indirect next hop: 0x2 no-forward INH Session ID: 0x0 Indirect path forwarding next hops: 1 Next hop type: Router Next hop: 10.2.4.1 via et-0/0/1.0 weight 0x1 Session Id: 0x0 10.255.2.1/32 Originating RIB: inet.3 Metric: 30 Node path count: 1 Forwarding nexthops: 1 Next hop type: Router Next hop: 10.2.4.1 via et-0/0/1.0 weight 0x1 Session Id: 0x0 疎通試験 Single-AS の時と同様に、顧客 A の CE に相当する VM を両端で 2 台作成し、その間で ping による通信を確認しました。 前述の繰り返しですが、CE 側からは経路を確認できず ping の通信で確かめるため、コマンド出力は全く同じになります。掲載は省略します。 まとめ 本記事では Single-/Multi-AS SR-MPLS での L2VPN について解説しました。 VLAN-based で構築する際の、 EVI 等の設定や確認例を紹介しました。 機能実装に差があり完全な Multi-vendor ではありませんが、 Multi-AS での検証について紹介しました。 次回の記事では、 BGP color による Explicit-path TE を紹介予定です。 (2022/07/25 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #4] Color-Based Steering in Single-AS Virtual Private Wire Service の略。仮想専用線サービス。 ↩ Virtual Private LAN Service の略。仮想専用 LAN サービス。 ↩ Multiprotocol BGP の略。BGP の拡張コミュニティを用いて追加情報を交換する仕組み。 ↩ Release Notes for Cisco ASR 9000 Series Routers, IOS XR Release 7.4.1 より、 Inter-AS EVPN Option B の新機能。 ↩
アバター
はじめに 本記事は前回の記事である「 ソフトウェア設計についてtwada技術顧問と話してみた 〜 A Philosophy of Software Design をベースに 〜 - NTT Communications Engineers' Blog 」の続編です。 前回の記事の内容がベースとなっていますので、「APoSD って何だっけ?」という場合はぜひ前回の記事をご覧になってから、以下にお進みください。 ということで、後編の対話パートにさっそく入っていきましょう! Pull Complexity Downwards iwashi: APoSD では、複雑性を下に追いやる(Pull Complexity Downwards)という話が出てきます。何らかの処理が複雑になる場合、それを隠蔽してインターフェースを極力シンプルに保つ、というのがAPoSDの主張です。 こちらに関しても、社内勉強会中で「議論があるよな」と twada さんがコメントされています。具体的に、どのような議論があるのでしょうか? twada: 「シンプルな実装よりも、シンプルなインタフェースを持つべきだ」に関しては、設計流派としては色々あるところです。 古くは、Worse is Better の話につながります。この話には、2つの設計の流派が出てきます。それぞれ「MIT/Stanfordアプローチ」と、「New Jerseyアプローチ」と呼ばれています。 両派とも「設計は実装とインターフェースの両面において単純でなければならない」と述べています。ここまではまあ当然のことですが、実装の単純さとインタフェースの単純さが両立できないときなどに両派の違いが出てきます。書籍『 The Art of UNIX Programming 』には次のように説明されています。 簡潔性 (Simplicity) MIT/Stanford: インターフェースが単純になることの方が、実装が単純になることより重要である New Jersey: 実装が単純な方が、インターフェースが単純なことより重要である。単純さが設計において最も重視されるべき事柄である APoSDでは、シンプルなインターフェイスで複雑な実装を隠蔽するのが良いと説いています。コードが多少複雑になろうと正しいことをせよ。コードの複雑さを抽象によって隠蔽し、認知負荷を下げよ。これがAPoSDでいう、Deep Moduleになります。 これは、MIT/Stanford アプローチと呼ばれている流派に近いように思えます。いま話しているうちに気がつきましたが、本書はStanford大学の教科書で、著者は同大学の教授でしたね。ですから、MIT/Stanfordアプローチに寄った意見になっているのも頷けます。 MIT/StanfordアプローチとNew Jerseyアプローチは、正当性や一貫性に関してもポリシーがやや異なります。書籍『 The Art of UNIX Programming 』にはこう記されています。 正当性 (Correctness) MIT/Stanford: 設計はすべての点において正しいものでなければならない。誤りは許されない New Jersey: 設計はすべての点において正しいものでなければならない。ただし、どちらかといえば、正しいことよりは単純なことの方が重要である 一貫性 (Consistency) MIT/Stanford: 設計は一貫性を欠いたものであってはならない。一貫性を保つためには完全性や単純さを多少犠牲にしてもよい。一貫性は正当性と同じぐらい重要である。 New Jersey: 設計は一貫性を大きく欠いたものであってはならない。単純さを保つために、一貫性は犠牲にされることがある。しかし、あまり起こらない状況に対応しようとして実装を複雑にしたり一貫性を欠いたものにするよりは、それを捨てる方がよい 他にもいろいろあるのですが、詳しくは書籍『The Art of UNIX Programming』や、私の講演資料( 過去を知り、未来に備える。技術選定の審美眼 2019 edition )を読んでみてください。 ( Worse Is Better - 過去を知り、未来に備える。技術選定の審美眼 2019 edition / Worse Is Better - Understanding the Spiral of Technologies 2019 edition P.35 より) もちろん APoSD は Worse Is Better 以降の議論を踏まえた内容になっていまして、なにより複雑性を最大の敵としていますから、「コードがいくら複雑になろうとも、正しいことをせよ」と言っている本ではないということは強調しておきます。複雑性を廃して、どう破綻なくソフトウェアを設計するかの本です。その上で、競合する制約に出会ったときに、どちらを取るか、またはどうバランスをとるかというところにポリシーが現れるのですね。 iwashi: このあたりの具体的な事例って、どのようなものがあるのでしょうか? twada: New Jersey派が結果的に生き残ったサンプルとしては、歴史的には、LinuxとMINIX、C言語とLisp、gitとbazaar-ngなどがあります。gitなどは典型例と言えそうですね。他には、MongoDB vs RethinkDBなどもそう考えられます。 OSSの世界においては、歴史的にはNew Jersey派が生き残ることも多かったと考えています。 iwashi: 何が理由で生き残ってきたのでしょうか? twada: 目の前の問題を解決しつつも、実装がシンプルなために、後日他の人が開発に参加できるハードルが下がるから、ですね。 iwashi: まさに、OSSと相性がいいですね。 twada: そういうことなんです。 実装の単純さを選択するときに、漏れのある抽象化があったとしても目の前の問題を解決できます。かつ実装がシンプルだと第三者が開発に参加するハードルが下がります。 これは、結果的には進化的な強さにつながります。つまり、変化に耐えて、生き残るための十分な数の目を得られる。 技術の歴史において、「なんでこっちの技術が生き残っているんだ?」みたいな話が出てきたときに、設計の一貫性・正しさ・美しさだけが生き残りを決めるファクターではない、という実例ですね。 New Jersey派はPrgramaticと表現できそうです。どちらかというとUnixカルチャー。Eric Raymond氏もそうですね。 その対立軸としてあるのが、MIT/Stanford アプローチであり「Do the Right Thing(正しいことをせよ)」という流派です。StanrfordのJohn Ousterhout教授もDo The Right Thing派に近いように思えますが、実はUncle Bobも同じ傾向がありますよね。 John Ousterhout教授とUncle Bobは批判し合っていて水と油のように見えますが、やや理想主義に寄っている点では似ています。現実よりは理想を選びがちであると。 iwashi: 世の中にあるソフトウェアを振り返って、「これってどっちに寄ってるんだろうな?」って考えるのは面白いかもしれないですね。 twada: そうですね。 実装をどれぐらい抽象で隠蔽すべきかというよりは、その発言をしている人はどういう影響下にいるのかが見えてくると誤読を避けやすくなりますし、過大評価も避けられるでしょう。 iwashi: 技術の歴史や、根っこにある流れを知っているとわかりやすいですね。 twada: 技術の歴史でいえば、もちろんMIT/Stanford流にインターフェースが優れていて生き残っている例もあるでしょう。 APoSDのShallowモジュールは、Functionalityが浅いものを意味します。New Jersey派は、過度に隠蔽しないことを重視します。漏れのある抽象化で良いので、あまり隠蔽しない方向になります。一方で、MIT/Stanford側はきちんと抽象化して認知負荷を下げる方向ですよね。 iwashi: どちらが唯一正しいわけではない、ってことですよね? twada: はい。なのでAPoSD本は、色々と本を読んでみて、自分の中で最適のバランスを探すための1つの本だ、と考えると良いです。 Uncle Bobの本を読んで「そこまではやらないけどこういう考え自体は大事だよね」と一歩引いて読むのと同じで、John Ousterhout教授はこんなことを書いているが、一歩引いて読むことができれば、設計の世界が広がる。極論があるから全体の設計空間は広がるわけです。 iwashi: そういう意味では、もし若者がAPoSDだけ読むと偏りすぎるかもしれないですね。 twada: 素晴らしい本ですが、ある意味1つの流派の総本山に連なる本でもあるので、それが正解かというとそうでもない。だから、「色々読んでみましょう」という話になります。 私自身、本読みとしては、意見の強い本は好きです。たとえば、Bertrand Meyerのオブジェクト指向入門や、アジャイル関係でいえばJim Coplienもそうですね。そういった戦うことを辞さないベテラン論客たちは視野を広げてくれます。 ある一方に心酔せず複数の意見を見て、自分の設計としてそのどこかにバランスを見出すのが大事です。特定の状況によって設計の流派を使い分けられます。 たとえば、OSSなら抽象を深くしすぎないで、第三者の参加がしやすいようにする方向性もあるでしょう。なぜなら、New Jersey派のほうが生き残りやすいだろうという算段があるためです。 それに対して、プロプライエタリな製品開発・クローズドな開発であれば、実装の単純さではなく設計の完全性や一貫性のほうが大事かもしれません。 どちらに設計の重きを置くか、というのは状況によって変わってくるわけですね。 ミッションクリティカルなシステムなのか、それともエンドユーザーが多くても一時的に落ちるのは許容されるのかでも、ソフトウェア設計で重視する内容は変わります。正確性が大事なのか、堅牢性が大事なのか。文脈によって大事にすべき品質特性は異なります。ソフトウェア設計に唯一の正解というものはないんです。 もちろん基本となるレベル、たとえば「可読性の高いコード」などはどこであっても正しいんです。そこから先で両立しにくい設計上の制約に面したときにどういう設計を選択していくかというのはソフトウェア設計の一番難しいところ。これは学んでいくしかないでしょう。 急がば回れで、失敗から繰り返して学ぶほうが多い分野です。おすすめなのは自分が設計したシステムを長い期間(たとえば2年以上)メンテナンスすることです。自分の設計判断がどういう結果になるのか、オーバーエンジニアリングやアンダーエンジニアリング、失敗や成功を繰り返すことで経験が増えるので、設計の打率が高まります。 iwashi: リーダブルコード のような書籍は汎用的ですよね。 twada: そうですね。一般的なプログラミング原則はどこでも適用可能です。たとえば、良い名前付けは、どのような状況下でも重要でしょう。 ただ、たとえば関数プログラミングとオブジェクト指向プログラミングはパラダイムが異なり、得意分野もやや異なります。「何が正しい」というよりは「向いている・向いてない」があります。ですから、自分の道具箱を増やすのが良いですね。 iwashi: 道具箱のツールを増やすために何をしたらいいでしょうか? twada: 書籍『 達人プログラマー 』に「年に1つプログラミング言語を学ぼう」という教えがあります。ちょっとずつパラダイムの違う言語をやるのがお勧めですね。 おわりに 本記事では、APoSDを起点として、MIT/StanfordアプローチとNew Jerseyアプローチについての対話を紹介しました。 当社では、このようなソフトウェア設計に興味があるソフトウェアエンジニアを募集しております! NTTコミュニケーションズ株式会社 ソフトウェアエンジニア の求人一覧 より、具体的なポストを是非ご確認ください!
アバター
はじめに DevOpsプラットフォームの取り組みを紹介する1回目の記事です。 こんにちは、イノベーションセンターでプラットフォームチームのアーキテクトをしている牧志 ( @JunMakishi ) です。NTTグループ向けのDevOpsプラットフォームである「 Qmonus (クモナス) Value Stream 」の開発をリードしています。 本日から、数週間にわたり、 Qmonus Value Stream チームの取り組みや利用技術を連載形式で紹介させていただきます。 第1回目の記事では、Qmonus Value Streamの概要を紹介します。 Qmonus (クモナス) Value Streamとは Qmonus Value Streamは、アプリケーション開発者が、ユーザにとって価値のある機能やサービスを継続的に提供するためのDevOpsプラットフォームです。Cloud Native Adapterと呼ばれる独自のInfrastructure as Code実装と、アプリケーションを商用環境にリリースするための継続的デリバリ機能を有しています。 Kubernetesを知っている方は、アプリケーション構成を管理するHelmと、CI/CDパイプライン機能をバンドルしたものをイメージいただくと良いと思います。 アプリケーション開発者は、Qmonus Value Streamを使うことで、クラウドやCI/CDに精通していなくても簡単にクラウドアーキテクチャのベストプラクティスを再利用してアプリケーションをデリバリできます。 Qmonus Value Streamの目的 ソフトウェアをアジリティ高く開発しながら安定運用するためには、クラウドコンピューティングの活用が必要不可欠です。しかし、 Cloud Native Landscape が拡大していっているように、クラウド技術は年々多様化・複雑化しており、クラウド技術を組み合わせて1つのアーキテクチャを組み上げるには高い専門性と多大な労力を必要とします。 NTTコミュニケーションズでは、多数のソフトウェア開発の現場でクラウド技術を活用していますが、個々のソフトウェアプロジェクトでクラウド技術の専門性を持つのは難易度が高いだけでなく、アーキテクチャガバナンスが効かないという課題がありました。私自身、以前、ネットワークコントローラ開発プロジェクトに所属していましたが、当時はソフトウェアデリバリやクラウド環境の管理のためだけに1つのサブチームを構成しており、主目的のコントローラアプリケーション開発以外にかかる労力は馬鹿になりませんでした。 こういったチームに代わってクラウド技術の複雑性を解決し、ユーザに価値を届けるアプリケーションの開発に注力できるよう、2020年から本格的にQmonus Value Streamの開発を開始しました。チームを超えて再利用できるInfrastructure as Codeを使って、早く簡単にクラウドを活用したアーキテクチャを構築し、アプリケーションをデリバリできるようにすることを目指しています。 Cloud Native Adapterによるベストプラクティスの提供 Cloud Native Adapterは、アプリケーションをクラウド基盤上で動作させる過程を仲介する独自のInfrastructure as Code実装です。商用環境で実績のあるシステム構成やCI/CDパイプラインを再利用可能な形でまとめることができ、アプリケーションと Cloud Native Adapter を組み合わせることで、ベストプラクティスに沿ったシステム構成やCI/CDを実現できます。 例えば、Qmonus Value Streamでは、Google Kubernetes EngineでWeb APIをセキュアに公開するためのシステム構成を、1つのCloud Native Adapterとして用意しています。このAdapterを使うだけで、Qmonus Value Streamの利用者は、コンテナを立ち上げてAPIを公開するためのマニフェストを生成できます。さらに、そのAPIにGCPのWAF/FirewallであるCloud Armorを適用して、安全にAPIを公開できます。 Cloud Native Adapterは、CUE言語で宣言的にシステム構成およびCI/CDパイプラインを記述します。CUE言語を活用することで、複数のAdapterを結合して複雑なアーキテクチャを実現できます。さらに、既存のAdapterに、独自のシステム仕様に合わせたロジックを追加することも可能です。 NTTグループで、ベストプラクティスを蓄積し育てていくことで、様々なアーキテクチャパターンを簡単に構築できる仕組みを確立し、かつアーキテクチャガバナンスを効かせることを目指しています。 アプリケーションのリリースパイプラインを統合管理 Qmonus Value Streamでは、Tektonと呼ばれるオープンソースのCI/CDフレームワークを活用し、ソフトウェアを商用環境にデリバリするためのCI/CD機能を提供しています。ユーザに価値を届けるアプリケーションを中心に据え、そのアプリケーションを開発してから商用環境にリリースするまでの一連の流れを統合管理できます。 以下の図の例のように、マージしたコードからソフトウェアをビルドし結合環境にデプロイする「ビルドパイプライン」と、総合試験環境や商用環境にソフトウェアをリリースする「リリースパイプライン」を構成し、それらを連結できます。結合環境で継続的に試験してソフトウェアを常にデプロイ可能な状態に保ち、任意のタイミングで試験を通過したソフトウェアを"Promote"して、商用環境にリリースできます。 Qmonus Value Streamによって、アプリケーションをユーザに届けるまでの一連の流れ、すなわちバリューストリームを自動化・管理することで、安全でアジリティの高いDevOpsプロセスを組織に適用することを目指しています。 Qmonus Value Streamの導入効果 Qmonus Value Streamの本格開発を開始した2020年以降、徐々にNTTコミュニケーションズ内で導入を進めてきました。2022年3月末時点で18のアクティブなソフトウェアプロジェクトで利用され、1月あたり100以上のソフトウェアリリースに活用されています。また、2022年4月からは、NTTグループ各社への提供を開始し、さらに利用が拡大しています。 Cloud Native Adapterとそれを使ってアプリケーションを継続的に商用環境にデリバリする仕組みを使うことで、アプリケーション開発者は、簡単にクラウドのベストプラクティスを活用でき、もっとも注力したいアプリケーションの開発とそのリリースに集中できます。その結果が、リリース回数という数字に表れていると考えています。 おわりに DevOpsプラットフォームの取り組みを紹介する1回目の記事として、NTTグループ向けのDevOpsプラットフォームQmonus Value Streamの概要を紹介させていただきました。fukabori.fmでも本プラットフォームについて紹介する機会をいただいたので、よかったらこちらも参考にしてください。( 50. 社内DevOps基盤、Tekton、Cuelang w/ JunMakishi ) Qmonus Value Streamチームは、メンバひとりひとりが、利用者であるアプリケーション開発者のために信念を持って活動を続けています。プラットフォームを内製開発するポジション、プラットフォームを使ってNTTグループのクラウド基盤やCI/CDを構築支援するポジションそれぞれで、一緒に働く仲間を募集していますので、興味を持たれた方は応募いただけると嬉しいです。 https://hrmos.co/pages/nttcom0033/jobs/0000019 https://hrmos.co/pages/nttcom0033/jobs/1692796 次回以降、Qmonus Value Streamチームの具体的な取り組みや要素技術を紹介していきます。お楽しみに!
アバター
サマリ Multi-AS で構成されるネットワークにおいて、 SR-MPLS + VPNv4 による L3VPN を実現 IOS XR + Junos の Multi-vendor 環境で動作検証に成功 この記事は Multi-AS SR 検証連載の第 2 回です。目次は こちら 概要 イノベーションセンターの岩田です。普段の業務では Multi-AS Segment Routing に関する技術検証や、ベンチャー企業への技術支援でスマートフォンアプリケーション開発業務などを行なっています。 本記事では我々の検証のメインターゲットである Multi-AS/Multi-domain SR-MPLS を用いた L3VPN についてご紹介します。 第 0 回の記事 でもご紹介した通り、我々は Multi-AS/Multi-domain SR により拡張性のあるネットワークの実現について検討、検証しています。本記事の流れとしては、まずアンダーレイを複数の AS やドメインに分割する利点について述べ、分割した AS をどのように接続するかについて検討します。その後、Multi-AS/Multi-domain SR-MPLS を用いた L3VPN の Multi-vendor 環境における検証例を、サンプルトポロジーや設定例を添えながら紹介します。 L3VPN や Segment Routing については 第 1 回の記事 で説明しているので、あわせてご確認ください。 アンダーレイを Multi-AS/Multi-domain に分ける利点 本節では、Multi-AS/Multi-domain で アンダーレイ(Underlay)を構築する利点について説明します。 アンダーレイを Multi-AS/Multi-domain に分けて構築する最大の利点は、アンダーレイの拡張性や保守性が担保される点です。 アンダーレイ の拡張性の観点では、単一の SR domain を構築する際に IP 重複できない制約があるため、プライベート IP を使用している場合だと規模の拡大とともに設計の制約が増加する、という課題への対処が必要になります。AS を分割することによって AS 間での IP 重複を考えずにすみます。 また保守性の観点からも、大規模ネットワークを IGP で管理している場合、機器トラブルなどが発生しルーティングに影響があった際の罹障範囲が大きくなるという課題があります。こちらも AS や SR domain を分割することで罹障範囲を局所化できます。 このように AS や SR domain を複数に分割することで、設計の自由度を保ち IGP や IBGP の影響範囲を局所化できるため、拡張性と保守性のあるネットワークが構築できます。 Inter-AS の接続方式について Multi-AS で MPLS VPN を行うためには Inter-AS の接続方式について考える必要があります。 本節では、RFC にて標準化されている 3 つの接続方式とそれらを組み合わせた方式について説明し、今回どの方式を採用するかについて説明します。 接続方式の選定方針は前節で説明した通り、設計の自由度を保ち IGP や IBGP の影響範囲を局所化し、拡張性と保守性のあるネットワークが構築できるかどうかとします。 Multi-AS で MPLS VPN を実現する方法としては RFC4364 で標準化されている Option A、B、C の3つの方式と、A と B を組み合わせた方式 D が知られています。 それぞれ以下のような特徴があります。 Option A ASBR で顧客毎に サブインタフェースと VRF を作成し EBGP セッション経由で経路を交換する ASBR 間の各サブインタフェース間では単純な IP パケットを転送し AS を接続する ASBR 間に顧客の数だけサブインターフェースを作成する必要がある EBGP セッションは各 VRF で行うため、各サブインタフェースに設定が必要になる Option B ASBR で単一のインタフェースを用いて EBGP セッションを張り VPN 経路を交換する ASBR で VPN ラベルを Swap し ASBR 間を別のラベルを用いて転送し AS を接続する Option C VPN 経路を RR 経由で交換し、互いのアンダーレイ情報を共有する ASBR に VRF が不要 Multi-AS で 単一の SR domain を構築できる アンダーレイを共有するため制約が増え、前述の Multi-AS の利点のうちの拡張性は減少する Option D(A+B) ASBR で単一のインタフェースを用いて EBGP セッションを張り VPN 経路を交換する (B と同様) ASBR 間は顧客毎に作成したサブインタフェース間で単純な IP パケットの転送をすることで AS を接続する (A と同様) ASBR 間に顧客の数だけサブインターフェースを作成する必要がある(A と同様) VPN 経路の交換は単一インタフェースを用いて行うため各インタフェースへの設定が不要(A ではインタフェース毎) まとめますと以下のようになります。 A : ASBR に顧客ごとのサブインターフェースや EBGP セッションなどの設定が必要 B : AS が制限少なく独立して存在でき、 ASBR 間も単一のセッションになる C :アンダーレイ が AS 間で広告されるため、今回の目的に不向き A+B(D): A の状況のうちサブインターフェースの設定が同様に残る その中でも、今回は Option B を採用しました。IGP、IBGP の責任範囲を疎に分離して運用できる方式であり、拡張性・保守性が他の Option に比べて高いことが選択理由です。 ただし機器と SR、VPN、Option-B を組み合わせて ASBR として用いるにはサポート状況に差があります。 L3VPN については、我々が検証を始めた時点での ASR9901(IOS XR 7.3.1) や MX204(Junos 21.2)で既にサポートされています。 しかし L2VPN(EVPN)は IOS XR 7.4.1 1 からの対応で、Junos は 2022 年 6 月時点で未対応です。 検証例の紹介 以降は L3VPN の検証例を実際の設定例を添えつつ紹介します。 サンプルトポロジー&想定するユースケース 検証のため、下記のようなトポロジーを作成します。 3 つのルーターによる AS を 2 つ接続したコア網で、顧客 A と B を収容します。 検証環境は Multi-vendor に構成します。使用する機器は下記の通りです。 SR Domain 1 PE1、ASBR1: Cisco IOS XR 7.4.1 ASBR2: Junos OS 21.3R1.9 SR Domain 2 ASBR3: Cisco IOS XR 7.4.1 PE2、ASBR4: Junos OS 21.3R1.9 この後は下記の流れに従って設定を紹介します。 IS-IS 設定と状態確認 VRF と MP-BGP 設定と状態確認 PE 間の ping 疎通試験 IS-IS - 設定 IS-IS の SR 拡張を利用し、SR-MPLS の SID 情報を広告します。 また、運用効率化のため MPLS OAM の設定もしておきます。 IOS XR(例: PE1) router isis 1 is-type level-2-only net 49.0000.0000.0aff.0001.00 segment-routing global-block 16000 23999 address-family ipv4 unicast metric-style wide segment-routing mpls ! interface Loopback0 address-family ipv4 unicast prefix-sid index 1 ! ! interface GigabitEthernet0/0/0/0 point-to-point address-family ipv4 unicast ! ! interface GigabitEthernet0/0/0/1 point-to-point address-family ipv4 unicast ! ! ! segment-routing ! Junos(例: PE2) set protocols isis interface ge-0/0/0.0 level 2 metric 10 set protocols isis interface ge-0/0/0.0 point-to-point set protocols isis interface ge-0/0/1.0 level 2 metric 10 set protocols isis interface ge-0/0/1.0 point-to-point set protocols isis interface lo0.0 passive set protocols isis source-packet-routing srgb start-label 16000 set protocols isis source-packet-routing srgb index-range 8000 set protocols isis source-packet-routing node-segment ipv4-index 1 set protocols isis level 1 disable set protocols isis level 2 wide-metrics-only set interfaces ge-0/0/0 unit 0 family inet address 10.0.0.1/30 set interfaces ge-0/0/0 unit 0 family iso set interfaces ge-0/0/0 unit 0 family mpls set interfaces ge-0/0/1 unit 0 family inet address 10.0.1.1/30 set interfaces ge-0/0/1 unit 0 family iso set interfaces ge-0/0/1 unit 0 family mpls set interfaces lo0 unit 0 family inet address 10.255.0.1/32 set interfaces lo0 unit 0 family iso address 49.0000.0000.0aff.0001.00 SR Global Block(SRGB)の変更後は再起動が必要です。 IS-IS - 動作確認 IS-IS ネイバーの状態と SID Table を確認します。 IOS XR RP/0/RP0/CPU0:pe1#show isis neighbors Fri Jun 17 13:37:40.431 JST IS-IS 1 neighbors: System Id Interface SNPA State Holdtime Type IETF-NSF asbr1 Gi0/0/0/0 *PtoP* Up 24 L2 Capable asbr2 Gi0/0/0/1 *PtoP* Up 20 L2 Capable Total neighbor count: 2 RP/0/RP0/CPU0:pe1#show mpls forwarding Fri Jun 17 13:37:45.932 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ 16002 Pop SR Pfx (idx 2) Gi0/0/0/0 10.0.0.2 0 16003 Pop SR Pfx (idx 3) Gi0/0/0/1 10.0.1.2 324511 24000 Pop SR Adj (idx 1) Gi0/0/0/0 10.0.0.2 0 24001 Pop SR Adj (idx 3) Gi0/0/0/0 10.0.0.2 0 24004 Pop SR Adj (idx 1) Gi0/0/0/1 10.0.1.2 0 24005 Pop SR Adj (idx 3) Gi0/0/0/1 10.0.1.2 0 Junos user@pe2> show isis adjacency Interface System L State Hold (secs) SNPA ge-0/0/0.0 asbr3 2 Up 28 ge-0/0/1.0 asbr4 2 Up 26 user@pe2> show route protocol isis table mpls.0 mpls.0: 14 destinations, 14 routes (14 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 18 *[L-ISIS/14] 1d 03:49:30, metric 0 > to 10.0.1.2 via ge-0/0/1.0, Pop 18(S=0) *[L-ISIS/14] 1d 03:49:30, metric 0 > to 10.0.1.2 via ge-0/0/1.0, Pop 19 *[L-ISIS/14] 1d 03:48:12, metric 0 > to 10.0.0.2 via ge-0/0/0.0, Pop 19(S=0) *[L-ISIS/14] 1d 03:48:12, metric 0 > to 10.0.0.2 via ge-0/0/0.0, Pop 16002 *[L-ISIS/14] 00:08:15, metric 20 > to 10.0.0.2 via ge-0/0/0.0, Pop 16002(S=0) *[L-ISIS/14] 00:08:15, metric 20 > to 10.0.0.2 via ge-0/0/0.0, Pop 16003 *[L-ISIS/14] 1d 03:49:30, metric 10 > to 10.0.1.2 via ge-0/0/1.0, Pop 16003(S=0) *[L-ISIS/14] 1d 03:49:30, metric 10 > to 10.0.1.2 via ge-0/0/1.0, Pop Static Route(IOS XR) - 機器設定 ※こちらハマりポイントなのでご注意ください。 IOS XR で Inter-AS を行う際には、ASBR のピアの片方で /32 の Static Route を設定する必要があります。 これを行わないとコントロールプレーンは立ち上がりますがトラフィックが転送されません。 詳しくは こちら をご参照ください。 router static address-family ipv4 unicast 10.100.0.2/32 GigabitEthernet0/0/0/2 ! ! Static Route(IOS XR) - 動作確認 RP/0/RP0/CPU0:asbr1#show route static Fri Jun 17 13:29:28.349 JST S 10.100.0.2/32 is directly connected, 1d02h, GigabitEthernet0/0/0/2 VRF、MP-BGP VPNv4 - 機器設定 顧客 A、B の VRF 設定と、経路を広告する MP-BGP L3VPN を設定します。 VPN の各パラメータは下記の通りです。 SR Domain 1 AS 番号: 65000 VRF 100: RD/RT 65000:100 AS 間での VPN 経路広告用 RT: 64999:100 VRF 200: RD/RT 65000:200 AS 間での VPN 経路広告用 RT: 64999:200 SR Domain 2 AS 番号: 65001 VRF 100: RD/RT 65001:100 AS 間での VPN 経路広告用 RT: 64999:100 VRF 200: RD/RT 65001:200 AS 間での VPN 経路広告用 RT: 64999:200 PE(IOS XR : PE1) vrf 100 description for simple L3VPN rd 65000:100 address-family ipv4 unicast import route-target 64999:100 65000:100 ! export route-target 64999:100 65000:100 ! ! ! vrf 200 description for simple L3VPN rd 65000:200 address-family ipv4 unicast import route-target 64999:200 65000:200 ! export route-target 64999:200 65000:200 ! ! ! interface GigabitEthernet0/0/0/2 vrf 100 ipv4 address 192.168.0.254 255.255.255.0 ! interface GigabitEthernet0/0/0/3 vrf 200 ipv4 address 192.168.0.254 255.255.255.0 ! router bgp 65000 bgp router-id 10.255.0.1 address-family vpnv4 unicast ! neighbor-group ibgp remote-as 65000 update-source Loopback0 address-family vpnv4 unicast ! ! neighbor 10.255.0.2 use neighbor-group ibgp ! neighbor 10.255.0.3 use neighbor-group ibgp ! vrf 100 rd 65000:100 address-family ipv4 unicast label mode per-vrf redistribute connected ! ! vrf 200 rd 65000:200 address-family ipv4 unicast label mode per-vrf redistribute connected ! ! ! PE(Junos : PE2) set policy-options policy-statement EXPORT-POLICY-100 term ROUTE-TARGET then community add VRF100-65001-RT set policy-options policy-statement EXPORT-POLICY-100 term ROUTE-TARGET then community add SHARED-100-RT set policy-options policy-statement EXPORT-POLICY-100 term REDIST-DIRECT from protocol direct set policy-options policy-statement EXPORT-POLICY-100 term REDIST-DIRECT then accept set policy-options policy-statement EXPORT-POLICY-200 term ROUTE-TARGET then community add VRF200-65001-RT set policy-options policy-statement EXPORT-POLICY-200 term ROUTE-TARGET then community add SHARED-200-RT set policy-options policy-statement EXPORT-POLICY-200 term REDIST-DIRECT from protocol direct set policy-options policy-statement EXPORT-POLICY-200 term REDIST-DIRECT then accept set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-65001 from community VRF100-65001-RT set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-65001 then accept set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-SHARED from community SHARED-100-RT set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-SHARED then accept set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-65001 from community VRF200-65001-RT set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-65001 then accept set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-SHARED from community SHARED-200-RT set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-SHARED then accept set policy-options community SHARED-100-RT members target:64999:100 set policy-options community SHARED-200-RT members target:64999:200 set policy-options community VRF100-65001-RT members target:65001:100 set policy-options community VRF200-65001-RT members target:65001:200 set routing-instances 100 instance-type vrf set routing-instances 100 protocols bgp family inet unicast set routing-instances 100 interface ge-0/0/2.0 set routing-instances 100 route-distinguisher 65001:100 set routing-instances 100 vrf-import IMPORT-POLICY-100 set routing-instances 100 vrf-export EXPORT-POLICY-100 set routing-instances 100 vrf-table-label set routing-instances 200 instance-type vrf set routing-instances 200 protocols bgp family inet unicast set routing-instances 200 interface ge-0/0/3.0 set routing-instances 200 route-distinguisher 65001:200 set routing-instances 200 vrf-import IMPORT-POLICY-200 set routing-instances 200 vrf-export EXPORT-POLICY-200 set routing-instances 200 vrf-table-label set routing-options router-id 10.255.0.1 set routing-options autonomous-system 65001 set protocols router-advertisement interface fxp0.0 set protocols bgp family inet unicast set protocols bgp family inet-vpn unicast set protocols bgp group ibgp type internal set protocols bgp group ibgp local-address 10.255.0.1 set protocols bgp group ibgp family inet-vpn unicast set protocols bgp group ibgp neighbor 10.255.0.2 set protocols bgp group ibgp neighbor 10.255.0.3 ASBR(IOS XR : ASBR1) vrf 100 description for simple L3VPN rd 65000:100 address-family ipv4 unicast import route-target 64999:100 65000:100 ! export route-target 64999:100 65000:100 ! ! ! vrf 200 description for simple L3VPN rd 65000:200 address-family ipv4 unicast import route-target 64999:200 65000:200 ! export route-target 64999:200 65000:200 ! ! ! router bgp 65000 bgp router-id 10.255.0.2 address-family vpnv4 unicast ! neighbor-group ibgp remote-as 65000 update-source Loopback0 address-family vpnv4 unicast next-hop-self ! ! neighbor-group 65001 remote-as 65001 address-family vpnv4 unicast route-policy pass-all in route-policy pass-all out ! ! neighbor 10.100.0.2 use neighbor-group 65001 ! neighbor 10.255.0.1 use neighbor-group ibgp ! neighbor 10.255.0.3 use neighbor-group ibgp ! vrf 100 rd 65000:100 address-family ipv4 unicast label mode per-vrf ! ! vrf 200 rd 65000:200 address-family ipv4 unicast label mode per-vrf ! ! ! ASBR(Junos : ASBR4) set policy-options policy-statement EXPORT-POLICY-100 term ROUTE-TARGET then community add VRF100-65001-RT set policy-options policy-statement EXPORT-POLICY-100 term ROUTE-TARGET then community add SHARED-100-RT set policy-options policy-statement EXPORT-POLICY-200 term ROUTE-TARGET then community add VRF200-65001-RT set policy-options policy-statement EXPORT-POLICY-200 term ROUTE-TARGET then community add SHARED-200-RT set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-65001 from community VRF100-65001-RT set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-65001 then accept set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-SHARED from community SHARED-100-RT set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-SHARED then accept set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-65001 from community VRF200-65001-RT set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-65001 then accept set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-SHARED from community SHARED-200-RT set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-SHARED then accept set policy-options community SHARED-100-RT members target:64999:100 set policy-options community SHARED-200-RT members target:64999:200 set policy-options community VRF100-65001-RT members target:65001:100 set policy-options community VRF200-65001-RT members target:65001:200 set routing-instances 100 instance-type vrf set routing-instances 100 protocols bgp family inet unicast set routing-instances 100 route-distinguisher 65001:100 set routing-instances 100 vrf-import IMPORT-POLICY-100 set routing-instances 100 vrf-export EXPORT-POLICY-100 set routing-instances 100 vrf-table-label set routing-instances 200 instance-type vrf set routing-instances 200 protocols bgp family inet unicast set routing-instances 200 route-distinguisher 65001:200 set routing-instances 200 vrf-import IMPORT-POLICY-200 set routing-instances 200 vrf-export EXPORT-POLICY-200 set routing-instances 200 vrf-table-label set routing-options router-id 10.255.0.3 set routing-options autonomous-system 65001 set protocols router-advertisement interface fxp0.0 set protocols bgp family inet-vpn unicast set protocols bgp group ibgp type internal set protocols bgp group ibgp local-address 10.255.0.3 set protocols bgp group ibgp neighbor 10.255.0.1 set protocols bgp group 65000 type external set protocols bgp group 65000 peer-as 65000 set protocols bgp group 65000 neighbor 10.100.1.1 MP-BGP VPNv4 - 動作確認 下記3ステップで L3VPN の経路学習と転送動作を確認します。 今回は各ベンダーの ASBR の組で動作確認がしたいので、選択させたくない ASBR 間のリンクを落とすことで選択させます。 動作確認したい ASBR の組以外の ASBR 間のリンクを落とす BGP から受信した経路を確認 ping による疎通確認 IOS XR の ASBR(ASBR1->ASBR3)を利用する場合 顧客 A(VRF100)が利用する場合を想定し動作確認をしていきます。 Junos の ASBR 間(ASBR2<->ASBR4)のリンクを落とします。 user@asbr2> configure Entering configuration mode [edit] user@asbr2# set interfaces ge-0/0/2 disable [edit] user@asbr2# show | compare [edit interfaces ge-0/0/2] + disable; [edit] user@asbr2# commit commit complete user@asbr2# exit Exiting configuration mode user@asbr2> ping 10.100.1.2 count 5 PING 10.100.1.2 (10.100.1.2): 56 data bytes ^C --- 10.100.1.2 ping statistics --- 5 packets transmitted, 0 packets received, 100% packet loss 次に、PE1(IOS XR) が受信した経路を確認します。 RP/0/RP0/CPU0:pe1#show bgp vpnv4 unicast vrf 100 192.168.1.0/24 detail Mon Jun 20 14:14:13.121 JST BGP routing table entry for 192.168.1.0/24, Route Distinguisher: 65000:100 Versions: Process bRIB/RIB SendTblVer Speaker 62 62 Flags: 0x00001001+0x00000000; Last Modified: Jun 20 14:12:27.073 for 00:01:46 Paths: (1 available, best #1) Not advertised to any peer Path #1: Received by speaker 0 Flags: 0x2000000005060005, import: 0x80 Not advertised to any peer 65001 10.255.0.2 (metric 20) from 10.255.0.2 (10.255.0.2), if-handle 0x00000000 Received Label 24007 Origin IGP, localpref 100, valid, internal, best, group-best, import-candidate, imported Received Path ID 0, Local Path ID 1, version 62 Extended community: RT:64999:100 RT:65001:100 Source AFI: VPNv4 Unicast, Source VRF: default, Source Route Distinguisher: 65001:100 RP/0/RP0/CPU0:pe1#show route vrf 100 Mon Jun 20 12:35:07.389 JST Codes: C - connected, S - static, R - RIP, B - BGP, (>) - Diversion path D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2 E1 - OSPF external type 1, E2 - OSPF external type 2, E - EGP i - ISIS, L1 - IS-IS level-1, L2 - IS-IS level-2 ia - IS-IS inter area, su - IS-IS summary null, * - candidate default U - per-user static route, o - ODR, L - local, G - DAGR, l - LISP A - access/subscriber, a - Application route M - mobile route, r - RPL, t - Traffic Engineering, (!) - FRR Backup path Gateway of last resort is not set C 192.168.0.0/24 is directly connected, 22:01:15, GigabitEthernet0/0/0/2 L 192.168.0.254/32 is directly connected, 22:01:15, GigabitEthernet0/0/0/2 B 192.168.1.0/24 [200/0] via 10.255.0.2 (nexthop in vrf default), 00:08:01 経路が受信できている事と、ASBR1(IOS XR)が選択されていることが確認できました。 ping で疎通確認を行なってみます。 RP/0/RP0/CPU0:pe1#ping 192.168.1.254 vrf 100 Mon Jun 20 12:33:13.840 JST Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 192.168.1.254, timeout is 2 seconds: !!!!! Success rate is 100 percent (5/5), round-trip min/avg/max = 2/6/12 ms パケットが転送でき、VPN が構築できていることが確認できました。 Junos の ASBR(ASBR2->ASBR4)を利用する場合 今度は顧客 B(VRF200)が利用する場合を想定し動作確認をしていきます。 まず、IOS XR の ASBR 間(ASBR1<->ASBR3)のリンクを落とします。 RP/0/RP0/CPU0:asbr1#configure Mon Jun 20 12:40:44.843 JST RP/0/RP0/CPU0:asbr1(config)#interface gigabitEthernet 0/0/0/2 RP/0/RP0/CPU0:asbr1(config-if)#shutdown RP/0/RP0/CPU0:asbr1(config-if)#show commit changes diff Mon Jun 20 12:41:02.632 JST Building configuration... !! IOS XR Configuration 7.4.1 + interface GigabitEthernet0/0/0/2 + shutdown ! end RP/0/RP0/CPU0:asbr1(config-if)#commit Mon Jun 20 12:41:06.779 JST RP/0/RP0/CPU0:asbr1(config-if)#exit RP/0/RP0/CPU0:asbr1(config)#exit RP/0/RP0/CPU0:asbr1#ping 10.100.0.2 Mon Jun 20 12:41:56.670 JST Type escape sequence to abort. Sending 5, 100-byte ICMP Echos to 10.100.0.2, timeout is 2 seconds: UUUUU Success rate is 0 percent (0/5) 次に、Junos が受信した経路を確認します。 user@pe2> show route advertising-protocol bgp 10.255.0.3 detail 100.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) * 192.168.1.0/24 (1 entry, 1 announced) BGP group ibgp type Internal Route Distinguisher: 65001:100 VPN Label: 16 Nexthop: Self Flags: Nexthop Change Localpref: 100 AS path: [65001] I Communities: target:64999:100 target:65001:100 200.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) * 192.168.1.0/24 (1 entry, 1 announced) BGP group ibgp type Internal Route Distinguisher: 65001:200 VPN Label: 17 Nexthop: Self Flags: Nexthop Change Localpref: 100 AS path: [65001] I Communities: target:64999:200 target:65001:200 hanabi@pe2> show route receive-protocol bgp 10.255.0.3 table bgp.l3vpn.0 detail bgp.l3vpn.0: 2 destinations, 4 routes (2 active, 0 holddown, 0 hidden) * 65000:100:192.168.0.0/24 (2 entries, 0 announced) Import Accepted Route Distinguisher: 65000:100 VPN Label: 16 Nexthop: 10.255.0.3 Localpref: 100 AS path: 65000 ? Communities: target:64999:100 target:65000:100 * 65000:200:192.168.0.0/24 (2 entries, 0 announced) Import Accepted Route Distinguisher: 65000:200 VPN Label: 17 Nexthop: 10.255.0.3 Localpref: 100 AS path: 65000 ? Communities: target:64999:200 target:65000:200 user@pe2> show route 192.168.0.0/24 100.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 192.168.0.0/24 *[BGP/170] 00:49:52, localpref 100, from 10.255.0.3 AS path: 65000 ?, validation-state: unverified > to 10.0.1.2 via ge-0/0/1.0, Push 16 200.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 192.168.0.0/24 *[BGP/170] 00:49:52, localpref 100, from 10.255.0.3 AS path: 65000 ?, validation-state: unverified > to 10.0.1.2 via ge-0/0/1.0, Push 17 bgp.l3vpn.0: 2 destinations, 2 routes (2 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 65000:100:192.168.0.0/24 *[BGP/170] 00:49:52, localpref 100, from 10.255.0.3 AS path: 65000 ?, validation-state: unverified > to 10.0.1.2 via ge-0/0/1.0, Push 16 65000:200:192.168.0.0/24 *[BGP/170] 00:49:52, localpref 100, from 10.255.0.3 AS path: 65000 ?, validation-state: unverified > to 10.0.1.2 via ge-0/0/1.0, Push 17 経路が受信できている事と、ASBR4(Junos)の ループバックアドレス(10.0.1.2)が nexthop として選択されていることが確認できました。 ping で疎通確認を行なってみます。 user@pe2> ping 192.168.0.254 routing-instance 200 count 5 PING 192.168.0.254 (192.168.0.254): 56 data bytes 64 bytes from 192.168.0.254: icmp_seq=0 ttl=253 time=4.264 ms 64 bytes from 192.168.0.254: icmp_seq=1 ttl=253 time=10.481 ms 64 bytes from 192.168.0.254: icmp_seq=2 ttl=253 time=11.079 ms 64 bytes from 192.168.0.254: icmp_seq=3 ttl=253 time=6.493 ms 64 bytes from 192.168.0.254: icmp_seq=4 ttl=253 time=3.516 ms --- 192.168.0.254 ping statistics --- 5 packets transmitted, 5 packets received, 0% packet loss round-trip min/avg/max/stddev = 3.516/7.167/11.079/3.114 ms パケットが転送でき、VPN が構築できていることが確認できました。 これで、ASBR が IOS XR の場合と、Junos の場合共に、VPN が構築できていることが確認できました。 まとめ 本記事では、 Multi-AS/Multi-domain での アンダーレイ 構築の利点、SR-MPLS を用いた L3VPN 実装時の Inter-AS 方式を紹介するとともに、 Multi-vendor 環境における Multi-AS での L3VPN の検証結果を紹介しました。 次回の記事では、SR-MPLS EVPN in Single/Multi-AS について紹介予定です。 (2022/07/11 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #3] SR-MPLS L2VPN (EVPN) in Single-/Multi-AS 参考資料 この検証の一部は弊チームの田島、三島がそれぞれ MPLS JAPAN 2021 、社外勉強会で発表しました。以下に資料を公開していますので併せてご覧ください。 マルチASかつマルチベンダにおけるSR-MPLS TEの実現 (田島) マルチAS/マルチドメインなSegment Routingの実現 (三島) Release Notes for Cisco ASR 9000 Series Routers, IOS XR Release 7.4.1 https://www.cisco.com/c/en/us/td/docs/routers/asr9000/software/asr9k-r7-4/general/release/notes/b-release-notes-asr9000-r741.html ↩
アバター
はじめに はじめまして。 プラットフォームサービス本部 データプラットフォームサービス部門の森分です。 もともと私は、NTT Comのクラウドサービスをベースにした法人向けソリューションの個社別運用やインフラ関連のプロジェクトマネージャ業務を担当しておりました。 最近はSmart Data Platform(以下、SDPF)アーキテクトなる、お客様課題の解決やNTT Comのビジネスの中でSDPFの活用を推進する部隊に参画しています。 データ利活用を支えるSDPFのアーキテクトがデータ利活用に詳しくなければ立つ瀬がありません。 そうならないように日々研鑽を積んでいるわけですが、その中で作ったTwitter分析システムっぽいもののご紹介が本稿の趣旨となります。 本来のデータ利活用プロジェクトでは、課題および仮説をまず明確にして、それに応じたデータ解析を進めていくのですが、本稿では堅苦しいものは全部すっとばしたゆるい雰囲気でお送りします。 きっかけ 私の所属するチームは、某自治体のお客様と地域活性化のためデータ利活用するプロジェクトに参画しています。 そのノウハウをもっと広く遍く、特に中小規模の自治体向けにデータ利活用サービスとして提供できないか検討する営みがありました。 その中で、お客様が住民の思っていることをアクティブに汲み取る(更には分析する)手段がないか…と考えたときにTwitterを分析してみてはとのインスタントアイディアがきっかけです。 …というのは後付けで、リアルタイムで情報が追加されているデータを可視化できたらかっこいいんじゃんとの思いで手を動かし始めました。 実際のデータ活用基盤でもデータ更新などの運用が問題になってくることが多いので、リアルタイム更新の事例を作りにいきました。 やりたいこと 自治体の担当者様が使うことを妄想すると以下の要件がまずありそうです。 対象の自治体エリア付近で情報を絞り込めること 住民がどんなことをつぶやいているかの傾向が手間を掛けずにわかること まずは1.をかなえるため、位置情報が付属しているツイートを分析の対象としました。 Twitterでは、本文の内容から類推する以外だと、位置情報くらいしか地方自治体に紐づける属性がなさそうだったためです。 全体ツイートから見ると位置情報をもつツイートはかなり限られますが、そこそこあるだろうとのなかば見切り発車しました。 2.については、ワードクラウドを使って、頻出しているワードを可視化し、ツイートの全体傾向をざっくり掴みに行く方針としました。 つくってみた 3分クッキング方式となり恐縮ですが、某自治体のお客様の取り組みの中で、Tableauで参照できるデータベース(PostgreSQL)を構築した検証環境があり、そちらを間借りさせていただきます。 可視化については、BIに特化したデータ視覚化ツールであるTableauのおかげで盤石なので、どれだけちゃんとしたデータを準備できるかが勝負です。 簡単ですが、システム概要図です。 やっていること Twitter Developerアカウントは無料アカウントを使用 プログラム実行環境は踏み台サーバ(CentOS)にPython環境を作って実装 ツイート取得はTwitterAPIでのTwitter操作を簡単に実装できるPythonライブラリであるtweepyのStreamListenerをベースに改造 取得するツイートは緯度経度でフィルター。日本を覆うくらいの 適用に 広めで設定。 ツイートから必要な情報を掻い摘んで、DataFrameに放り込んでいく DataFrameをPostgreSQLのtweetテーブルに放り込む ツイート取得スクリプトは5分毎にcronで起動させ、エラー耐性を無理やり上げる ワードクラウドのためのワードデータはtweetテーブルのデータを形態素解析して、Wordテーブルに放り込む 形態素分析はmecabを使って、名詞のみを抽出 形態素分析は、1時間に1回、毎時10分に前の1時間分をまとめて処理 ワードクラウドはTableau内の表現で完結するので、追加で実装する必要なし(すばらしい) ちゃんとツイート取得できるか ちゃんとツイート取得できているか可視化してみます。 1時間ごとに1,000件~15,000件くらいの幅でツイート取得ができています。 深夜帯はがっつりツイートが減りますがそれでも1,000件程度あります。 位置情報をもったツイートはほとんどなかったなんてオチにならなくてよかったです。 Tableauの更新ボタン押下は必要ですが、ツイート取得のリアルタイム性も問題なさそうです。 形態素分析できてるか 次に、形態素分析して生成される名詞数を可視化してみます。 1時間当たりに5,000語~85,000語くらいが生成されている感じです。 波形がツイート数とほぼ一致していて、だいたい1ツイートあたり平均7語くらい生成されてます。 ツイートをマッピングしてみる 位置情報をもとに、Tableauに地図マッピングしてもらいましょう。 日本を覆いつくす一面のツイート。 ついでに韓国も覆いつくしてますね。中国や北朝鮮にマッピングがないのはお国柄でしょうか。 たまに、指定エリア外のデータが出現していますが、これはStreaming apiのバグということで異常値扱いとします。 ワードクラウドしてみる 次にワードクラウドを作ってみましょう。さっきのツイートマッピングに重ね合わせて配置してみます。 密度が高く見づらいですが、形にはなってます。 頻出のワードもちゃんと大きく表示されています。 位置フィルターを導入してみる 基本機能的なものは実装できたので、地域ごとにフィルターできる機能を追加します。 「指定位置」と「指定距離」のパラメータを作成して、特定位置から半径〇kmを対象にしたツイートのみをフィルターする機能を実装します。 「指定位置」は手動入力でもいいですが操作性が悪くなるので、ツイートのマークをクリックしたら自動指定されるようにしてみました。 青マークが「指定位置」のツイート、オレンジマークが「指定距離」内のツイートになります。 それに合わせてワードクラウドの対象ツイートも同期させてみました。 また、ワードクラウドの表示密度の調整として、表示させるワードの最低出現数を閾値として設定できるようにしました。 千代田区あたりを「指定位置」にして、半径50kmを対象に絞ってみました。 3月31日夜は地震が発生していたので、その傾向も見えてます。 ついでに、ワードクラウドのワードをクリックすればツイートマッピング側もそのワードを含むものでフィルターできるようにしてみます。 これで具体的なツイートを追いかけるのが楽になりました。 各地方の傾向を見てみる これまでは例として関東周辺のものを参照してきましたが、各地方の傾向を見ていきます。 札幌近郊 まずは札幌近郊から。 地名が頻出するのはどこにも同じ傾向があるようです。(位置情報ありのツイートのためでしょう) この日は日ハムがシーズン初勝利したのでその様子も反映されてます。 仙台近郊 次に仙台近郊。 関東と同様に地震の影響をうけたのがほのかに分かります。 東海エリア 愛知や静岡をメインに東海エリアを見てみます。 桜前線的にこのエリアが満開なのかなと想像できます。 この日は関東と関西で地震が起こったので、間に挟まれた東海でも地震ワードが目立ってます。 関西エリア 大阪、兵庫、京都を中心に。 関西でも地震が発生していたため、やはり地震傾向が強いです。 関東と比較すると時事関連ワードが見えるのは地域柄でしょうか。 中四国エリア 広島を中心に中四国を対象に。 かなりエリアを広げたからか地名系が多いです。 カープが開幕戦絶好調でしたので広島近郊でカープの話題が多く、その傾向も少し見えます。 九州エリア そろそろ見飽きてませんでしょうか。あと少しです。お付き合い願います。 熊本を中心に、九州7県をすっぽり対象にしました。 九州エリアも地名系が多いですが、BTSが見えているのは韓国と近いからでしょうか。 沖縄近郊 一気に傾向が変わります。沖縄近郊です。 軍事機の飛行情報をトラッキングしているMilitary Aircraft Spotterというアカウントが、嘉手納基地の情報を頻繁につぶやいているのが影響を出しているようです。 沖縄エリアのツイート数自体が少ないこともあり、頻繁につぶやいているアカウントの影響が大きくなってしまいます。 日本は笑顔で溢れている ここまで読んでくれた皆様、どうもありがとうございます。 各地方のワードクラウドを通しで眺めてみると、多くの地方で「笑顔」のワードが出現しています。 これはこの記事を読んで切る皆様、ひいては日本国民が毎日を笑顔で過ごしているいるからに他なりません。 そんな素敵な国、日本に産まれてよかったなぁと感じます。 お付き合い頂きまして、ありがとうございました。 そんなことはない わかってます。そこまで日常に笑顔があふれていないことは。調べてみましょう。 「笑顔」のワードでツイートをフィルターしてマッピングしてみます。 純粋にツイート本文に「笑顔」が入っているものはこれだけしかないため、何かしら別要因で笑顔が増殖しているはずです。 笑顔はたしかにそこにあった 辿っていくと、笑顔を吐き出しているツイート文は以下のようなものが多い傾向にありました。(下記は創作した例文です) このツイート文に対するMecabの形態素解析の結果から名詞の出力を見てみましょう。 いました「笑顔」。 笑った絵文字が笑顔を量産していたようです。 あわせて、英語の半角全角やURL情報、具体的な数値など、傾向把握の文脈上必要ない部分を前処理する必要がありそうです。 余談となりますが、"鬼滅の刃"を判断してくれるMecab、 もといmecab-ipadic-neologdは時流をとらえておりとても便利です。 ipadicはIPAが提唱した「IPA品詞体系(THiMCO97)」に基づいて作成されたデータの辞書なのですが、すでに更新が止まっています。 そこでipadicをベースにして、様々なデータベースをクロールして自動更新されているのが今回使用したmecab-ipadic-neologd。ありがたいです。(最近はこちらも更新がとまっているようで、2020年連載開始の"海が走るエンドロール"は判別できていません。) さよなら笑顔 ということで、文整形の前処理をいれ、この日を境に日本中から架空の笑顔が消えたのでした。 めでたし めでたし。 さいごに 大変長くなってしまいましたが、ご覧いただきましてありがとうございました。 本稿はもともと社内ブログとして公開した内容を、本ブログ用に加筆修正したものとなります。 ソースコード記載が皆無だったり、ストーリーがゆるかったりしておりますが、ご容赦頂けますと幸いです。 SDPFアーキテクトではお客様の課題解決やNTT Comビジネスの中でSDPF活用を推進しています。 ただ、SDPFアーキテクトはSDPF専任の導入コンサルのポジションは取っていません。 SDPF以外の商材でも課題解決に必要なものは導入し、時には自分たちで検証しながら、課題解決のサポートやNTT Comのサービスへのフィードバックするなど幅広い活動をしています。 自由度の高いミッションですし、データ利活用の道をひた走ってきた人材や膨大なSDPFの商材を知り尽くした人材ばかりではありません。 それでもこの大きい会社を次のステージのために変わっていくぞ、変えていくぞと、切磋琢磨しながら前に進めるそんな良き雰囲気がNTT Comにはあると思っています。 【余談】振り返りとSDPF データ連携がつらい 今回はPoCレベルでしたので、各システムのデータ連携部分は力業で手組みしました。 ところが、データ型の整合性を取ったり、渡し方の設計でもトライアンドエラーがあり、この規模でも結構苦労しました。 (最終的に、細かい部分はPandasに丸め込んでもらっちゃいました) 運用を考えた場合、ある程度の頻度のColumn追加やデータ仕様変更を想定するとELTやデータ仮想化などを入れて管理していかないと、運用稼働が跳ね上がり、開発に支障をきたしてしまうのではと感じました。 そんな要望を叶えるサービスがSDPFにはありますので紹介させてください。 コードを修正、動作確認、DBの中身を確認するサイクルを何度も繰り返しながら進めていた作業が、GUI上でサクっとできてしまいます。 1日かかった作業が30分で終わる感覚になれたので、ROIが高いのではないでしょうか。 データ統合インフォマティカ ソリューション 複数システムにまたがるデータを統合管理 https://www.ntt.com/business/services/data-utilization/dxplatform/sdpf/informatica.html https://sdpf.ntt.com/services/informatica/ データ仮想化 TIBCO Data Virtualization 複数のデータを複製することなくリアルタイムで統合。一元的なデータアクセスを可能に https://sdpf.ntt.com/services/tdv/ データ可視化は楽だった 逆にデータ可視化については、Tableauを使用したので地図へのマッピングや、ワードクラウド作成などの作成稼働は本当に短かったです。 これをたとえばエクセルでやれなどとなったら太刀打ちできないでしょう。リアルタイムなんて夢の夢になってしまいそうです。 データ可視化サービスとして、TIBCO SpotfireをSDPFでは提供しています。 BI/BAツール TIBCO Spotfire 直感的な操作でデータを可視化し、探索的な分析を可能にするBI/BAツール https://www.ntt.com/business/services/data-utilization/dxplatform/sdpf/tibco.html https://sdpf.ntt.com/services/spotfire/
アバター
はじめに こんにちは、データプラットフォームサービス部の下公、真山、イノベーションセンターの原田です。 NTTコミュニケーションズ株式会社 (以下、NTT Com) は、世界最大級のイベントネットワークである 「Interop Tokyo 2022(会場:幕張メッセ、会期:2022年6月15日〜17日)」 において構築されるShowNet *1 に対して、400G-ZR *2 を用いたイーサネット回線を提供し、実運用することに成功しました。 また、今年のShowNetでは多種多様なローカル5Gシステムが集合し、マルチベンダーでの5Gコア/RAN端末の相互接続や多様なローカル5G実現方法について提案します。NTT Comはコントリビューターとしてスライシングの検証と、ローカル5Gの低遅延通信を活用したアプリケーションについて紹介します。 400G-ZRを用いたイーサネット回線 NTT Comは、長距離での400ギガビットイーサネット接続を可能とする400G-ZRを用いて、今年初めてShowNetへ400Gアクセス回線を提供しました。 本回線はトランジット・IXに接続し、Interop会場の出展社・来場者のインターネットアクセスに利用されています。 400G-ZRは、コヒーレント伝送技術を利用した光トランシーバをスイッチ/ルータに直接搭載し、接続する規格です。 専用の伝送装置を複数用意する必要があった従来の構成に比べ、よりシンプルな構成を実現できるため消費電力が少なく、また標準化により異なるメーカー同士であっても柔軟な長距離接続が可能となっており、DCIなどの拠点間通信に適用する際、特に注目されている技術です。 次世代の拠点間通信のプロトタイプとして、今回は光増幅器(アンプ)にあたる光トランシーバ(OSFP-LS)と8チャネルのMux/Demuxを行えるブレークアウトケーブル(Colorless Fiber Mux/Demux)、OSFP 400G-ZRをすべて1Uのスイッチに搭載し、省スペースで大容量の長距離伝送を実現しました。 また、本回線の構築にあたり、ShowNetを構成する機器の回線に対する試験を事前に実施しました。 スループットや遅延、ゆらぎなどの一般的な試験項目に加えて、400G-ZRの評価に必要となる光トランシーバ内部で実施されているエラー訂正の状況についても確認し、拠点間で送受信される通信の品質が実運用に耐えるものであることを確認しました。 本回線の試験についてはArista Networksとアンリツ株式会社の協力のもと、実施しました。 ローカル 5G の技術検証・展示内容 今年のShowNetでは5G/ローカル5Gを取り入れ、下記のテーマで多数のベンダーがコントリビューターとして参加し、技術検証・アプリケーションを展示しています。 マルチベンダーでの5Gコア/RAN/端末の相互接続への挑戦 多様なローカル5G実現方式 ローカル5Gを利用したデモンストレーション マルチベンダーによるEnd to Endスライシング 私たちは、ユーザーの要求に応じてネットワークやエッジコンピューティング環境といったリソースを柔軟に提供することを目標としています。 それを達成するには、ユーザーやアプリケーション単位での細かい粒度でネットワークスライシング技術が必要になります。 今回、NTT ComではShowNetにて、マルチベンダーによる基地局(gNB)から5Gコアの先のData Network (N6) 含めたEnd to Endスライシングの実現にチャレンジしました。 5Gではユースケースに合わせて異なる性能や機能のネットワークを提供できることが特徴とされており、「大容量(eMBB)」「超低遅延(URLLC)」「大量接続(mMTC)」の3つの要件が定義されています。 ネットワークスライシングとは、単一のネットワークを複数の論理的なネットワークに分割し、それぞれの論理ネットワークに対してユースケースに合わせた異なる経路や品質制御を提供する仕組みのことです。 ネットワークスライシングにより、高精細カメラの映像を流すスライスは大容量(eMBB)の経路を選択したり、自動運転車のような低遅延を要求するスライスは遅延の少ない経路(URLLC)を選択したりすることが可能となります。 5GCのアーキテクチャの大きな特徴として、マイクロサービス志向のサービス分割がなされ、コンテナプラットフォームでの運用を想定している点が挙げられます。 コンテナプラットフォームを採用することで、機能単位での迅速なデプロイや負荷に応じたスケーリング、故障時のオートヒーリングなどが可能となります。 また、商用使用において特に期待される機能として、E2EでのネットワークスライシングやAPIを介したオーケストレーション、トラフィック制御の容易性が挙げられます。 本検証では、コンテナ型5GCとしてCapgemini Engineering ViNGCを、コンテナ型UPFとしてKaloom Cloud Edge Fabricを用いて構築します。 ユーザー端末(UE)と基地局(gNB)は、スパイレントコミュニケーションズ社のシミュレータLandslideを使用します。 Capgemini Engineering ViNGCは、Kubernetes/OpenShiftで動作する5GCであり、各Functionは1つ以上のPodとして構成されます。 C/U分離が可能であることから、UPFはKaloom UPFを使用します。Kaloom UPFはOpenShift上で動作するUPFであり、Edgecore Networks社のホワイトボックススイッチ上で動作します。 LeafとUPFからなる1つのクラスタで、複数の仮想UPFを動作させることが可能です。 N3/N6でのネットワークスライシングを実現できることから、Capgemini Engineering ViNGCとの異ベンダー構成として検証していきます。 今回は、5Gの3要件として挙げられている”eMBB(高速大容量)”、”URLLC(超低遅延)”、”mMTC(超大量端末)”を想定した3つのスライスを作成し、N6でのスライシングによるそれぞれ異なる経路でネットワークに抜けていくような構成を構築しました。 UPFはBGPでShowNetのバックボーンに接続されます。 上述したCapgemini Engineering ViNGCおよびKaloom UPFを用いた構成において、スライシングの分割はgNBでのDNN及びS-NSSAIとVLAN mappingによりN3/N6区間において実現し、N6から先のネットワークはFlex-Algoによりそれぞれ異なる経路を通って上流ネットワークに抜けていくという、gNBからData Network間のEnd to Endでのネットワークスライシングにチャレンジしました。 検証結果についてはShowNet会場にて展示します。ぜひお越しいただければと思います。 ローカル5Gの低遅延通信を利用した遠隔操作アプリケーション NTT Comは、スライシング技術などの最先端な技術検証だけでなく、現在黎明期であるローカル5Gの様々なユースケースを創造することも、自社のミッションだと考えています。 従来技術では解決が困難であったお客様の課題をローカル 5G を用いることでいかに解決できるのか?といった観点のもと、Smart Data Platformを含む様々なアプリケーションとの相互検証を実施しています。 今回のShowNetでは、NTT Comのローカル5G環境が提供する低遅延通信を活用し、ソリトンシステムズ社のSmart-telecaster Zao-X *3 を搭載した遠隔操作型ラジコンカーを用いたアプリケーションを展示します。 Smart-telecaster Zao-Xは4Kの映像伝送が可能で高精細映像をモバイル回線で伝送できます。 H.265/HEVC コーデックにより転送データ量を抑え、ローカル5GやMECといった低遅延通信と親和性の高いアプリケーションです。 ラジコンカーにカメラを搭載し、Zao-Xを通じて走行中の映像をローカル5Gシステムを経由してリアルタイムに伝送します。 またローカル5Gシステムを経由してラジコンカーの走行を制御し、遠隔でも操縦性に違和感なく低遅延通信を実感いただけます。 ShowNet会場にて設置しているシールドテント内にローカル5G端末およびZao-Xを搭載したラジコンカーを配置し、Data Network (N6) に接続しているサーバーにてラジコンカー視点での映像およびラジコンカーの操縦をご覧いただけます。 おわりに この記事では、Interop Tokyo 2022(会場:幕張メッセ、会期:2022年6月15日〜17日)において構築されたShowNetについて、400G-ZRを用いたイーサネット回線の提供、およびローカル5GのマルチベンダーによるEnd to Endでのスライシングとアプリケーションの検証を紹介しました。今後は、今回のフィールドテストの成果を踏まえ、NTT Comのネットワークサービスへの応用について検討していきます。 *1 : ShowNet:Interop Tokyoにおいて、様々な機器・サービスの相互接続性検証を実施するとともに、近未来のサービスアーキテクチャを実際に動かしている形で見ることができる世界最大規模のライブネットワークデモンストレーション。Interop Tokyoへの出展社がインターネットへの接続性を利用して製品の動態展示のほか、来場者のインターネットへのアクセスとしても利用されるネットワーク。 https://www.interop.jp/2022/shownet/ *2 : 400G-ZR:業界団体であるOIF(Optical Internetworking Forum)が標準化し、単一波長により400ギガビットイーサネットを最大40kmまで接続可能とする光トランシーバの規格。光増幅器(アンプ)を組み合わせることで最大120kmまでの長距離接続にも対応する。 *3 : Zao-X:モバイル回線を利用して高画質の動画をライブ中継する”Smart-telecaster™”シリーズの最新モデルです。 モバイル回線に最適化されたSolitonオリジナルプロトコル「RASCOW2」を採用。映像伝送の課題だった遅延を大幅に短縮し、解像度を4Kに拡大。 https://www.soliton.co.jp/lp/zao-x_h/
アバター
サマリ Single-AS なネットワークにおいて、 SR-MPLS + VPNv4 による L3VPN を実現 IOS XR + Junos の Multi-vendor 環境で動作検証に成功 この記事は Multi-AS Segment Routing 検証連載の1つです。目次は こちら 概要 イノベーションセンターの三島です。普段の業務では Multi-AS Segment Routing に関する control plane/data plane の技術検証や、運用効率化のためのコントローラー開発などを行なっています。 本記事では SR-MPLS を用いた L3VPN の Multi-vendor 環境における検証例を、サンプルトポロジーやコンフィグ例を添えながら紹介します。 今回の記事ではまず Single-AS での L3VPN 検証結果を紹介し、 Multi-AS 構成については次回以降の記事で扱っていきます。 L3VPN とは L3VPN とは、複数のネットワーク同士をレイヤー3で接続する仮想的な専用回線を構築する技術です。 こちらの図に L3VPN の概要を示しました。 この例では、 A、B の2種類の顧客を1つのコア網に収容しています。 VPN は仮想的な専用回線であり、図の通り同一の顧客同士は通信可能にしつつ、異なる顧客同士は通信不可になるよう構成します。 L3VPN は レイヤー3 での IP の相互接続を担保する VPN であり、異なる拠点同士は別の Prefix で構成します。 L3VPN には、顧客を VRF 単位で識別する per-VRF L3VPN と、 CE 単位で識別する per-CE L3VPN があります。 VRF とは、1つのルーターを複数のルーティング面に多重化する技術です。 per-VRF L3VPN は、複数の顧客を各々の VRF で分離し識別する方式です。 一方、 per-CE L3VPN では顧客の CE ルーターを単一の default-VRF に収容し、 nexthop で識別する方式です。 今回の検証では、顧客側に専用のゲートウェイとなる CE ルーターが不要となる利点や、 VRF 単位で経路表を分離できる利点を考慮し、 per-VRF L3VPN を扱います。 ある PE における、収容対象のセグメント、当該 PE のアドレス、顧客識別子、その他メタデータの組などの L3VPN の情報は MP-BGP の VPNv4/VPNv6 で伝達します。 VRF の識別子を Route Distinguisher(RD)、経路の識別子を Route Target(RT)と呼び、これらの組み合わせで収容する顧客を識別します。 Segment Routing とは Segment Routing(SR)とは、 RFC8402 で標準化された次世代の経路制御技術です。 SR では転送対象や処理を Segment と呼ばれる単位で扱い、ソースルーティングのパラダイムに基づき転送します。 ソースルーティングとは、現在最も一般的であるパケットの宛先アドレスを基にした転送先決定ではなく、送信元があらかじめパケットに埋め込んだ情報によって転送先を決定するルーティング手法です。 各パケットに Segment ID(SID)のリストである Segment List を付与することで、そのパケットに対する網内での経路や処理などを指定します。 こちらの図に SR-MPLS による転送例を示しました。この例では顧客 A、B の通信に対して VPN を提供するとともに、 PE1 → P1 → P2 → PE2 となるような Traffic Engineering(TE)を提供しています。 PE1 は顧客 A からパケットが送られた際、顧客 A を示す VPN ラベル(SID: 24001)を付与するとともに、事前に定義した SR Policy に従い、 P1 → P2 → PE2 という経路を示す TE ラベルとなる Segment List (16004/16003)を付与し、 P1 へと転送します。 P1 は パケットに付与された Segment List の先頭要素(Active Segment)を参照し、処理します。ここでは P2 を示す SID(16004)が付与されているため、ラベルを Pop することで Active Segment を進めるとともに、 P2 へと転送します。 P2 は Active Segment を参照し、 Active Segment を進めると共に、 PE2 へとパケットを転送します。 PE2 は Active Segment を参照し、 Segment が示す L3VPN 動作を実施し、顧客 A へとパケットを転送します。 これらの動作により、本記事の目的である VPN が実現できます。 また、送信元 PE が付与する Segment List を変更することにより、柔軟に経路を変更する TE も実現可能となります。 このように、 SR では Segment List に格納された SID により、 TE や VPN など様々な動作を実現できます。 検証例の紹介 ここまでの話で、 SR による TE や VPN の実現手法について説明しました。 以降は L3VPN の検証例を実際のコンフィグ例を添えつつ紹介します。 サンプルトポロジー&想定するユースケース 検証のため、下記のようなトポロジーを作成します。 4つのルーターからなる SR domain をコア網として構成し、顧客 A、B を収容します。 検証環境は Multi-vendor に構成します。使用する機器は下記の通りです。 PE1、P1: Cisco IOS XR 7.4.1 PE2、P2: Junos OS 21.3R1.9 基本設定 ここからは機器設定と動作確認を行います。 基本的なインターフェース設定等は既に実施されているものとし、 SR の SID を広告する IS-IS と、 L3VPN を実現する BGP VPNv4 の設定・動作確認を実施します。 IS-IS - 設定 IS-IS の SR 拡張を利用し、 SR-MPLS の SID 情報を広告します。 また、運用効率化のため MPLS OAM の設定もしておきます。 IOS XR router isis 1 is-type level-2-only net 49.0000.0000.0aff.0001.00 segment-routing global-block 16000 23999 address-family ipv4 unicast metric-style wide segment-routing mpls ! interface Loopback0 address-family ipv4 unicast prefix-sid index 0 ! ! interface GigabitEthernet0/0/0/0 point-to-point address-family ipv4 unicast ! ! interface GigabitEthernet0/0/0/1 point-to-point address-family ipv4 unicast ! ! ! mpls oam ! segment-routing Junos set protocols isis interface ge-0/0/0.0 level 2 metric 10 set protocols isis interface ge-0/0/0.0 point-to-point set protocols isis interface ge-0/0/1.0 level 2 metric 10 set protocols isis interface ge-0/0/1.0 point-to-point set protocols isis interface lo0.0 passive set protocols isis source-packet-routing srgb start-label 16000 set protocols isis source-packet-routing srgb index-range 8000 set protocols isis source-packet-routing node-segment ipv4-index 2 set protocols isis level 1 disable set protocols isis level 2 wide-metrics-only set protocols mpls icmp-tunneling set protocols mpls interface ge-0/0/0.0 set protocols mpls interface ge-0/0/1.0 set interfaces ge-0/0/0 unit 0 family inet address 10.0.2.2/30 set interfaces ge-0/0/0 unit 0 family iso set interfaces ge-0/0/0 unit 0 family mpls set interfaces ge-0/0/1 unit 0 family inet address 10.0.3.2/30 set interfaces ge-0/0/1 unit 0 family iso set interfaces ge-0/0/1 unit 0 family mpls set interfaces lo0 unit 0 family inet address 10.255.0.3/32 set interfaces lo0 unit 0 family iso address 49.0000.0000.0aff.0003.00 SR Global Block(SRGB)の変更後は再起動が必要です。 IS-IS - 動作確認 IS-IS ネイバーの状態と SID Table を確認します。 IOS XR RP/0/RP0/CPU0:pe01#show isis neighbors Mon Jun 6 18:48:40.985 JST IS-IS 1 neighbors: System Id Interface SNPA State Holdtime Type IETF-NSF p01 Gi0/0/0/0 *PtoP* Up 22 L2 Capable p02 Gi0/0/0/1 *PtoP* Up 21 L2 Capable Total neighbor count: 2 RP/0/RP0/CPU0:pe01#show mpls forwarding Mon Jun 6 18:53:26.322 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ 16001 Pop SR Pfx (idx 1) Gi0/0/0/0 10.0.0.2 2529 16002 16002 SR Pfx (idx 2) Gi0/0/0/0 10.0.0.2 5446 16002 SR Pfx (idx 2) Gi0/0/0/1 10.0.1.2 0 16003 Pop SR Pfx (idx 3) Gi0/0/0/1 10.0.1.2 5366 24000 Pop SR Adj (idx 1) Gi0/0/0/1 10.0.1.2 0 24001 Pop SR Adj (idx 3) Gi0/0/0/1 10.0.1.2 0 24002 Pop SR Adj (idx 1) Gi0/0/0/0 10.0.0.2 0 24003 Pop SR Adj (idx 3) Gi0/0/0/0 10.0.0.2 0 Junos user@pe02> show isis adjacency Interface System L State Hold (secs) SNPA ge-0/0/0.0 p01 2 Up 25 ge-0/0/1.0 p02 2 Up 24 user@pe02> show route protocol isis table mpls.0 mpls.0: 15 destinations, 15 routes (15 active, 0 holddown, 0 hidden) + = Active Route, - = Last Active, * = Both 16 *[L-ISIS/14] 07:36:19, metric 0 > to 10.0.3.1 via ge-0/0/1.0, Pop 16(S=0) *[L-ISIS/14] 07:36:19, metric 0 > to 10.0.3.1 via ge-0/0/1.0, Pop 17 *[L-ISIS/14] 00:22:25, metric 0 > to 10.0.2.1 via ge-0/0/0.0, Pop 17(S=0) *[L-ISIS/14] 00:22:25, metric 0 > to 10.0.2.1 via ge-0/0/0.0, Pop 16000 *[L-ISIS/14] 00:21:54, metric 30 to 10.0.2.1 via ge-0/0/0.0, Swap 16000 > to 10.0.3.1 via ge-0/0/1.0, Swap 16000 16001 *[L-ISIS/14] 00:22:07, metric 20 > to 10.0.2.1 via ge-0/0/0.0, Pop 16001(S=0) *[L-ISIS/14] 00:22:07, metric 20 > to 10.0.2.1 via ge-0/0/0.0, Pop 16003 *[L-ISIS/14] 07:36:19, metric 10 > to 10.0.3.1 via ge-0/0/1.0, Pop 16003(S=0) *[L-ISIS/14] 07:36:19, metric 10 > to 10.0.3.1 via ge-0/0/1.0, Pop これらの結果より、 IOS XR、Junos 共に LinkState が正しく広告され、 SID Table が生成されたことが確認できました。 VRF、MP-BGP VPNv4 - 機器設定 顧客 A、B の VRF 設定と、経路を広告する MP-BGP L3VPN を設定します。 VPN の各パラメータは下記の通りです。 AS 番号: 65000 VRF 100: RD/RT 65000:100 VRF 200: RD/RT 65000:200 IOS XR vrf 100 rd 65000:100 address-family ipv4 unicast import route-target 65000:100 ! export route-target 65000:100 ! ! ! vrf 200 rd 65000:200 address-family ipv4 unicast import route-target 65000:200 ! export route-target 65000:200 ! ! ! interface GigabitEthernet0/0/0/2 vrf 100 ipv4 address 192.168.0.254 255.255.255.0 ! interface GigabitEthernet0/0/0/3 vrf 200 ipv4 address 192.168.0.254 255.255.255.0 ! router bgp 65000 bgp router-id 10.255.0.1 address-family vpnv4 unicast ! neighbor-group ibgp remote-as 65000 update-source Loopback0 address-family vpnv4 unicast ! ! neighbor 10.255.0.2 use neighbor-group ibgp ! neighbor 10.255.0.3 use neighbor-group ibgp ! neighbor 10.255.0.4 use neighbor-group ibgp ! vrf 100 rd 65000:100 address-family ipv4 unicast label mode per-vrf redistribute connected ! ! vrf 200 rd 65000:200 address-family ipv4 unicast label mode per-vrf redistribute connected ! ! ! Junos set policy-options policy-statement EXPORT-POLICY-100 term ROUTE-TARGET then community add VRF100-65000-RT set policy-options policy-statement EXPORT-POLICY-100 term REDIST-DIRECT from protocol direct set policy-options policy-statement EXPORT-POLICY-100 term REDIST-DIRECT then accept set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-65000 from community VRF100-65000-RT set policy-options policy-statement IMPORT-POLICY-100 term ROUTE-TARGET-65000 then accept set policy-options community VRF100-65000-RT members target:65000:100 set policy-options policy-statement EXPORT-POLICY-200 term ROUTE-TARGET then community add VRF200-65000-RT set policy-options policy-statement EXPORT-POLICY-200 term REDIST-DIRECT from protocol direct set policy-options policy-statement EXPORT-POLICY-200 term REDIST-DIRECT then accept set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-65000 from community VRF200-65000-RT set policy-options policy-statement IMPORT-POLICY-200 term ROUTE-TARGET-65000 then accept set policy-options community VRF200-65000-RT members target:65000:200 set routing-instances 100 instance-type vrf set routing-instances 100 protocols bgp family inet unicast set routing-instances 100 interface ge-0/0/2.0 set interface ge-0/0/2 unit 0 family inet address 192.168.1.254/24 set routing-instances 100 route-distinguisher 65000:100 set routing-instances 100 vrf-import IMPORT-POLICY-100 set routing-instances 100 vrf-export EXPORT-POLICY-100 set routing-instances 100 vrf-table-label set routing-instances 200 instance-type vrf set routing-instances 200 protocols bgp family inet unicast set routing-instances 200 interface ge-0/0/3.0 set interface ge-0/0/3 unit 0 family inet address 192.168.1.254/24 set routing-instances 200 route-distinguisher 65000:200 set routing-instances 200 vrf-import IMPORT-POLICY-200 set routing-instances 200 vrf-export EXPORT-POLICY-200 set routing-instances 200 vrf-table-label set routing-options router-id 10.255.0.3 set routing-options autonomous-system 65000 set protocols bgp family inet-vpn unicast set protocols bgp group ibgp type internal set protocols bgp group ibgp local-address 10.255.0.3 set protocols bgp group ibgp neighbor 10.255.0.1 MP-BGP VPNv4 - 動作確認 下記2ステップで L3VPN の経路学習と転送動作を確認します。 BGP から受信した経路を確認 traceroute + MPLS OAM による Segment List の確認 IOS XR → Junos Junos 側の VPN ラベルを確認します。 mpls.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden) 18 (1 entry, 1 announced) *VPN Preference: 0 Next hop type: Router, Next hop index: 638 Address: 0x704e02c Next-hop reference count: 2 Next hop: via lsi.0 (100), selected Label operation: Pop Load balance label: None; Label element ptr: 0x780ffb0 Label parent element ptr: 0x0 Label element references: 2 Label element child references: 0 Label element lsp id: 0 Session Id: 0x143 State: <Active Int Ext LsiL3> Age: 51:18 Validation State: unverified Task: RT Announcement bits (2): 1-KRT 3-Resolve tree 3 AS path: I Ref Cnt: 0 (Stale Peers: 16104) Stale - Will time out in -32224 seconds Thread: junos-main user@pe02> show route table mpls.0 label 19 detail mpls.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden) 19 (1 entry, 1 announced) *VPN Preference: 0 Next hop type: Router, Next hop index: 628 Address: 0x704def4 Next-hop reference count: 2 Next hop: via lsi.1 (200), selected Label operation: Pop Load balance label: None; Label element ptr: 0x780ffb0 Label parent element ptr: 0x0 Label element references: 2 Label element child references: 0 Label element lsp id: 0 Session Id: 0x142 State: <Active Int Ext LsiL3> Age: 51:24 Validation State: unverified Task: RT Announcement bits (2): 1-KRT 3-Resolve tree 3 AS path: I Ref Cnt: 0 (Stale Peers: 16104) Stale - Will time out in -32228 seconds Thread: junos-main ラベル 18 が VRF 100(顧客 A)、19が VRF 200(顧客 B)に対応する VPN ラベルであることが確認できます。 IOS XR が受信した経路と、 traceroute の結果を確認します。 RP/0/RP0/CPU0:pe01#show route vrf 100 Mon Jun 6 19:13:33.372 JST Codes: C - connected, S - static, R - RIP, B - BGP, (>) - Diversion path D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2 E1 - OSPF external type 1, E2 - OSPF external type 2, E - EGP i - ISIS, L1 - IS-IS level-1, L2 - IS-IS level-2 ia - IS-IS inter area, su - IS-IS summary null, * - candidate default U - per-user static route, o - ODR, L - local, G - DAGR, l - LISP A - access/subscriber, a - Application route M - mobile route, r - RPL, t - Traffic Engineering, (!) - FRR Backup path Gateway of last resort is not set C 192.168.0.0/24 is directly connected, 00:44:47, GigabitEthernet0/0/0/2 L 192.168.0.254/32 is directly connected, 00:44:47, GigabitEthernet0/0/0/2 B 192.168.1.0/24 [200/0] via 10.255.0.3 (nexthop in vrf default), 00:02:06 RP/0/RP0/CPU0:pe01#traceroute 192.168.1.254 vrf 100 Mon Jun 6 19:14:06.584 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.0.2 [MPLS: Labels 16002/18 Exp 0] 54 msec 4 msec 14 msec 2 192.168.1.254 4 msec 5 msec 2 msec RP/0/RP0/CPU0:pe01#traceroute 192.168.1.254 vrf 200 Mon Jun 6 19:15:22.568 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.0.1.2 [MPLS: Labels 16002/19 Exp 0] 7 msec 10.0.0.2 15 msec 17 msec 2 192.168.1.254 3 msec 9 msec 3 msec Junos → IOS XR IOS XR 側の VPN ラベルを確認します。 RP/0/RP0/CPU0:pe01#show mpls forwarding labels 24004 detail Mon Jun 6 19:52:54.388 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ 24004 Aggregate 100: Per-VRF Aggr[V] \ 100 2576 Updated: Jun 6 18:30:03.767 Label Stack (Top -> Bottom): { } MAC/Encaps: 0/0, MTU: 0 Packets Switched: 23 RP/0/RP0/CPU0:pe01#show mpls forwarding labels 24005 detail Mon Jun 6 19:53:02.618 JST Local Outgoing Prefix Outgoing Next Hop Bytes Label Label or ID Interface Switched ------ ----------- ------------------ ------------ --------------- ------------ 24005 Aggregate 200: Per-VRF Aggr[V] \ 200 2076 Updated: Jun 6 18:30:03.768 Label Stack (Top -> Bottom): { } MAC/Encaps: 0/0, MTU: 0 Packets Switched: 18 ラベル 24004 が VRF 100(顧客 A)、24005が VRF 200(顧客 B)に対応する VPN ラベルであることが確認できます。 Junos が受信した経路と、 traceroute の結果を確認します。 user@pe02> show route advertising-protocol bgp 10.255.0.1 100.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) Prefix Nexthop MED Lclpref AS path * 192.168.1.0/24 Self 100 I 200.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden) Prefix Nexthop MED Lclpref AS path * 192.168.1.0/24 Self 100 I user@pe02> show route receive-protocol bgp 10.255.0.1 table bgp.l3vpn.0 bgp.l3vpn.0: 2 destinations, 2 routes (2 active, 0 holddown, 0 hidden) Prefix Nexthop MED Lclpref AS path 65000:100:192.168.0.0/24 * 10.255.0.1 0 100 ? 65000:200:192.168.0.0/24 * 10.255.0.1 0 100 ? user@pe02> traceroute 192.168.0.254 routing-instance 100 traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 10.0.3.1 (10.0.3.1) 3.768 ms 1.947 ms 2.121 ms MPLS Label=16000 CoS=0 TTL=1 S=0 MPLS Label=24004 CoS=0 TTL=1 S=1 2 10.0.1.1 (10.0.1.1) 23.648 ms * 3.814 ms user@pe02> traceroute 192.168.0.254 routing-instance 200 traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 10.0.3.1 (10.0.3.1) 6.866 ms 3.196 ms 9.220 ms MPLS Label=16000 CoS=0 TTL=1 S=0 MPLS Label=24005 CoS=0 TTL=1 S=1 2 10.0.1.1 (10.0.1.1) 7.612 ms * 12.066 ms これにより、目的であった Multi-vendor 環境における L3VPN を実現できました! まとめ 本記事では、 L3VPN や SR の概要を紹介するとともに、 Multi-vendor 環境における Single-AS での L3VPN の検証結果を紹介しました。 次回の記事では、我々がメインターゲットとして取り組んでいる、 Multi-AS での L3VPN について紹介します。 (2022/06/27 追記) 公開しました: [Multi-AS Segment Routing 検証連載 #2] SR-MPLS L3VPN in Multi-AS 参考資料 Segment Routing 関連技術の解説資料を Segment Routing 入門 として公開しています。 TE、VPN 双方の基礎知識から SR の応用的な技術まで解説していますので、併せてご覧ください。
アバター
サマリ Multi-AS での Segment Routing 活用について検証しています。 検証結果についてこれから連載します。 この記事では検証の概要(Multi-AS、Multi-vendor、SR で VPN と TE を実現)について述べます。 はじめに イノベーションセンターの田島です。主にサービスプロバイダー網の技術検証から検証用 AS の設計・構築・運用まで担当しています。 我々が検証に取り組む Multi-AS での Segment Routing 活用について、今回から複数回にわたって解説します。 記事一覧 概要説明(この記事) #1 SR-MPLS L3VPN in Single-AS #2 SR-MPLS L3VPN in Multi-AS #3 SR-MPLS EVPN in Single/Multi-AS #4 Color-Based Steering in Single-AS #5 TE in Multi-AS #6 IGP Flexible Algorithm #7 Delay-based TE #8 SR Policy の適用方法と活用 (Per-Flow Steering) #9 TI-LFA を用いた障害時の高速迂回 #10 PCE を用いた SR-TE の一元管理 #11 PCE 実装の検証 #12 SR-MPLS L3VPN in Multi-AS using IOS XR, Junos and SR OS #13 SR-MPLS L2VPN(EVPN) in Multi-AS using IOS XR, Junos and SR OS #14 Color-Based Steering using IOS XR, Junos and SR OS #15 IGP Flexible Algorithm Interoperability Updates #16 Delay Measurement using IOS XR, Junos and SR OS #17 Per-Flow Steering using SR OS #18 TI-LFA を用いた障害時の高速迂回 と Microloop の回避 (using SR OS with IOS XR / Junos) #19 SR OS での PCE を用いた LSP Provisioning #20 Multi-AS の SR-MPLS + VPNv4 環境における AS 間での TE ※記事が公開され次第、リンクを追加します。 検証の背景と記事のねらい 我々サービスプロバイダーが拠点間通信の需要に応えるときは、専用の機器や回線を用意する場合ばかりではなく、共用の IP バックボーンを用意したうえで VPN などのサービスを実装し提供する場合もあります。 この取り組みは、その IP バックボーンと VPN を構築する技術の話です。 IP バックボーンは 単一の IGP で作られることが多いですが、中には単一の IGP で管理するのが難しいネットワークもあります。 例えば単純にノード数が多かったり、構築経緯の異なるネットワーク同士を相互接続している、などの理由が考えられます。 我々の取り組みはそういった場合においても、次のような利点をいかにして実現するのか実証するものです。 ネットワークを分割しそれぞれの独立性を保ちつつも同じ end-to-end の通信を提供する 単に通信できる以上の付加価値を付与する この検証では AS による IGP 分割でネットワーク同士の疎結合を保ちつつ、それぞれの AS で Segment Routing(SR)を用いて end-to-end の通信をより詳細にコントロールする方式を実際に試しています。 SR は比較的新しい分野で情報が限定的になりがちなため、実証した内容をこうして記事として執筆することで情報提供と SR のさらなる発展を期待しています。 我々の記事が SR の VPN と TE の技術に興味を持つきっかけとなり、技術理解や各キャリアルーターでの実装の一助となれば幸いです。 お約束ごとではありますが、一連の内容は NTT Com の現在および未来にわたるネットワークサービスの仕様を定めるものではありません。 また記載内容は我々の検証環境で実際に行った結果を基にしており、再現性のある記述に努めますが、言及する各ルーターメーカーの実装内容や動作を保証するものではありません。 検証の概要 まずネットワークサービスの模式図を例示しネットワークの概念と用語を説明します。 サービスプロバイダー側が用意する中央のネットワークに、顧客のルーター Customer Edge(CE)が接続されます。 CE を収容するプロバイダー側のルーターを Provider Edge(PE)といい、特にパケットが CE から PE に入る方を ingress PE とよび、出る方を egress PE とよびます。 図に示すように実際のルーターとリンクで構成されるアンダーレイ(Underlay)の上に ingress PE から egress PE までの仮想的なオーバーレイ(Overlay)経路があるというのが VPN の実装です。この経路を制御することを Traffic Engineering(TE)とよびます。 台数が増えるなどにより、アンダーレイを分割した場合を考えます。図には多くのルーターを例示できませんが次の図のように変わります。 検証では、このような状態でもオーバーレイの機能を維持するためには何が必要なのかを考えます。 オーバーレイの機能とは複数のアンダーレイ AS を経由した VPN の実現と、そのような VPN 経路の TE の実現です。 その際に各アンダーレイの実装自由度を確保するため、構成する機器のメーカー制約は導入しません。 つまり、異機種間の相互接続ということになります。 まとめますと、これから紹介する我々の取り組みは IGP ドメインが分割されていて Multi-vendor で構成されるアンダーレイの上に SR を用いて VPN を実装し TE を実装する 取り組みです。 おわりに 本記事では Multi-AS SR の構築を目指す我々の取り組みの概要と、今後の記事の目次を記載しました。現在リストにある項目以外にも執筆予定ですのでご期待ください。 参考資料 この検証の一部は MPLS JAPAN 2021 にて発表しました。 発表資料がイベントページにありますので、併せてご覧ください。
アバター
はじめに こんにちは。プラットフォームサービス本部 データプラットフォームサービス部でSmart Data Platform(SDPF)のサービス企画を行っている小野・砂田です。 ご覧頂きありがとうございます。我々は当社が提供しているSDPFサービスを組み合わせた具体的な事例紹介をしております。 前回の 第3回 では、Flexible Remote Accessを利用しながら、合わせて適切なログ保存を行って運用していくユースケースを説明させて頂きました。 今回は、現在も利用拡大が続いているIoT関連サービスを使った事例をご紹介いたします。 IoTが組み込まれた環境は当たり前になってきており、IoTシステムの効率的な環境構築・運用が求められています。以下を目標としながら、お客様が気軽に利用できる環境を作成してみました。 <実現したいこと> モバイルデータ通信サービスを使って、データの収集や各種機器の管理をしたい。 IoTのセンサーデバイスや各種機器から収集したデータを、クラウド側へセキュアに転送したい。 多数のデバイス・機器の状態を統合的に可視化、分析、管理したい。 上記を実現する各種サービスの構築を素早く、かつ簡易に行いたい。 背景 増加するIoTデバイスを簡易に組み込んで管理することが必要 お客様の課題解決のために、IoTユースケースでは以下の機能が求められています。 IoTデバイスにeSIMを組み込み、モバイルデータ通信サービスを利用してクラウドへ簡単にセキュアに接続する機能 IoTデバイスの状態の可視化、分析、管理などを統合的に行う機能 上記を以下の3つのサービスを利用して実現します。 IoT Connect Mobile® Type S : eSIM採用のモバイルデータ通信サービス IoT Connect Gateway : IoTのセンサーデバイスなどから収集したデータの転送を簡単・セキュアに実現 Things Cloud® : 多様なセンサー/デバイス接続からのデータ収集、可視化、分析、管理などのIoTに必要な機能をパッケージ化 機能実現するには複数のサービスを組合せて構築することが必要 3つのサービスを申し込んだ後、IoT機能を実現するには複数の手順を実施する必要があります。今回の取り組みでは、速やかに機能実現するために手順をなるべく効率化し、簡易に機能実現できる仕組み作りを行いました。 IaCツールを活用して、各種IoTサービスの設定を自動構築する TerraformやAnsible、各サービスのAPIを活用して自動構築に必要な設定ファイル、コードを作成する 上記の実現のため、以下のサービスやツールを利用しています。 Azure DevOps : Microsoft Azureサービスの1つで、開発を計画的かつ効率的に行うためのツール群を提供 Terraform : HashiCorp社が開発する、インフラ環境をコードとして管理・自動構築できるツール Ansible : レッドハット社により開発されている、構成管理やOS・ソフトウェアの設定を自動化するツール Azure CLI : Azureリソースを作成・管理するためのコマンドセット 検証を通じた確認 実際に検証を行った構成 上記背景を基に、IoT機能実現の効率化を図る環境として以下2つの構成について検証しました。 なお、検証ではIoTデバイスとしてRaspberry Piを利用しました。 構成(IoTデバイス→IoT Connect Gateway→Things Cloud) 室内の温湿度データをセンサで計測し、IoTデバイスでデータを受け取ります。 NTT CommunicationsのモバイルサービスであるIoT Connect Mobile® Type Sのモバイルネットワークを利用し、IoTデバイスからIoT Connect Gatewayに温湿度データを送信します。 IoT Connect Gatewayのプロトコル変換機能を利用してMQTTからMQTTSにプロトコルを変換し、Things Cloudに暗号化された温湿度データを転送します。 Things Cloudのデータ表示機能を活用し、温湿度データをグラフに表示します。 構成(IoTデバイス→IoT Connect Gateway→Azure IoT Hub) 室内の温湿度データをセンサで計測し、IoTデバイスでデータを受け取ります。 NTT CommunicationsのモバイルサービスであるIoT Connect Mobile® Type Sのモバイルネットワークを利用し、IoTデバイスからIoT Connect Gatewayに温湿度データを送信します。 IoT Connect Gatewayのプロトコル変換機能を利用してMQTTからMQTTSにプロトコルを変換し、Azure IoT Hubに暗号化された温湿度データを転送します。 実際に検証してみた Azure DevOpsを活用したThings Cloudの環境構築と検証 Azure DevOpsを用いてIoT Connect GatewayとThings Cloudの設定を自動化した上で、温湿度センサで取得したデータをIoTデバイスからIoT Connect Gateway経由でThings Cloudにデータ転送し、そのデータをThings Cloud上で可視化する検証を実施しました。 Azure DevOpsを活用したAzure IoT Hubの環境構築と検証 Azure DevOpsを用いてIoT Connect GatewayとAzure IoT Hubの設定を自動化した上で、温湿度センサで取得したデータをIoTデバイスからIoT Connect Gateway経由でAzure IoT Hubにデータを転送する検証を実施しました。 確認後の具体的な気付きポイント IoT Connect Gateway 今回の構成ではIoTデバイスからIoT Connect Gateway経由で各種クラウドサービスにデータの転送をしましたが、その間の通信プロトコル変換(MQTT→MQTTS)をIoT Connect Gatewayの設定のみで簡単に行うことができました。IoTデバイス側でSSL設定をせずに、プロトコル変換によりSSL通信を実現できる点において有用性が高いと感じました。また、プロトコル変換によりIoTデバイス側のSSL通信分のトラフィックが削減されるため、通信量やIoTデバイスの負荷軽減にもなると考えております。 Things Cloud 今回の検証でThings Cloudを用いて温湿度データの可視化を実現しましたが、他にも受け取ったデータで異常値が発生した場合、任意のメールアドレスに自動的にアラートメールを送信することも可能です。 また、本記事では温湿度センサを用いた環境について紹介しておりますが、他にもGPSセンサから取得したデータをThings Cloudに転送することで、Things CloudのマップからIoTデバイスの位置情報の確認もできます。以下の図はIoTデバイスの位置情報をThings Cloudで確認している画面です。 具体的な構成例や設定例の見える化 具体的な構成例や設定例、注意点等の知見についてはKnowledge Centerにて記載しておりますのでご確認ください。記載している情報は2022年5月時点の情報となります。 具体的な構成例・設定例 IoT機器導入ソリューション【手動構築】 IoT機器導入ソリューション【自動構築】 各種サービス IoT Connect Mobile® Type S IoT Connect Gateway Things Cloud® Hybrid Cloud with Microsoft Azure Azure DevOps 最後に 今回の検証ではIoTデバイスをクラウドサービスへ登録する作業や、利用するクラウドサービスの設定を自動化しました。今後は、IoTデバイスの追加・変更・削除等の運用を想定した作業を自動化する検証を進めていきます。 「ここの記載がわかりにくい」等のご意見や、「SDPFでこんなことができないか」等の疑問がありましたら、以下メールアドレスに対してお気軽にコメント等ご連絡ください。 sdpf-testbed-02@ntt.comにメールを送る ※お手数ですが@を全角文字から半角文字に置き換えてください。
アバター
はじめに イノベーションセンターの神田です。 みなさんはVisionalistというサービスをご存じでしょうか。 VisionalistはNTTコミュニケーションズのグループ会社であるNTTコム オンライン・マーケティング・ソリューション(以下 NTTコム オンライン)が提供していたアクセスログ解析サービスです。 2020年7月にサービスを終了しましたが、2022年5月にこのサービスで利用していたドメインを第三者が再登録し、セキュリティ上問題があるスクリプトを設置している可能性が確認されました(以下 本件)。 本件について、NTTコム オンラインは 広く注意を呼びかけています 。 本件に対して、イノベーションセンターでは事象が確認された直後からそのリスクの有無や程度について詳細な調査を実施してきました *1 。 本記事は、本件におけるリスクがこれ以上拡大しないようにこれまでに我々が確認した事実とそれに基づく分析の結果を社外に広く共有するものです。 また、今後同様の事象が起きないように本件を教訓としてドメインのライフサイクル管理を今一度見直していただくきっかけとなることも期待しています。 社内に向けても先日開催された勉強会において情報共有・啓発しており、本記事はその発表内容をベースとして再編集しています。 目次 アクセスログ解析サービス(Visionalist)の仕組み 経緯 脅威分析 関連事例 影響範囲 おわりに 謝辞 IoC アクセスログ解析サービス(Visionalist)の仕組み アクセスログ解析サービスに詳しくない方のために、Visionalistがどのような仕組みでアクセスを計測していたかを簡単に説明します。 (すでにご存じの方は読み飛ばしてください) Visionalistでは、サービス利用者が予め自分のWebサイトにタグ(tracer[.]jpへのリンク)を埋め込んでおきます。 訪問者がWebサイトにアクセスすると(上図①)、タグを介して計測用スクリプトがWebブラウザにダウンロードされ(上図②)、Webブラウザ上で計測用スクリプトが実行されることでログが生成、収集されます(上図③)。 図のようなアクセス解析サービスの形態は「ビーコン型」と呼ばれます。 Visionalistに限らずビーコン型のアクセス解析サービスは基本的に同様の仕組みでアクセスを計測しています。 経緯 発生日 イベント 2020年7月31日 Visionalistのサービス終了 2022年4月30日 tracer[.]jpが完全に失効 2022年5月5日 第三者がtracer[.]jpを再登録 〜2022年5月17日 不審なスクリプト配置 2022年5月18日 NTTコム オンラインによる注意喚起 Visionalistでは、アクセスログ解析に用いるタグとしてtracer[.]jpを利用していました。 このドメインはサービス終了後、2022年4月30日に期限を迎え、先願による登録が可能な状態となりました。 2022年5月5日になってNTTコム オンラインではない第三者がtracer[.]jpを再度登録しました。 その後、不審なスクリプトが配置されていることが発見され、報告されています。 (ご無沙汰しております、、、) 2020年にサービス提供を終えたVisionalist ASPで使用されていたドメイン名 ( tracer[.]jp ) が数日前に第三者に再登録され、不審なスクリプトが配置されています。 タグの消し忘れが結構ありそうです。 ご注意ください! pic.twitter.com/QWMd6SI6PD — tike (@tiketiketikeke) 2022年5月10日 tracer[.]jpに不審なスクリプトが配置されたということは、タグが残置されたままのWebサイトにアクセスしたWebブラウザ上で不審なスクリプトが実行される状態にあることを意味します。 不審なスクリプトが配置された状況はしばらく続きましたが、2022年5月17日に権威DNSサーバが切り替わったタイミングでDNSレコードが削除され、tracer[.]jpは名前解決のできない、どこにもつながらない状態となりました。 以後、2022年6月7日現在までその状況が続いています。 2022年5月18日にはNTTコム オンラインが注意を喚起する ニュースリリース を公開しました。 脅威分析 事象を認識した2022年5月10日頃から2022年5月17日にDNSレコードが削除されるまでの間tracer[.]jpに不審なスクリプトが配置されていた事実を確認しています。 スクリプトが実行されると、2種類のハッシュ値やタイムスタンプ値などがWebブラウザからtracer[.]jpに送信されます。 スクリプト上ではその後tracer[.]jpからの応答に応じてWebブラウザのページを別URLに遷移させるようなコードとなっています。 具体的には、JSON形式の応答を受け取った場合に応答データ内の情報を元に parent.top.window.location.href の値を書き換える動作をします。 観測の範囲内ではJSON形式の応答は確認されませんでしたが、応答によって任意のURLに遷移させるリダイレクタとして動作することから、潜在的に悪意のあるURLに誘導される可能性が考えられます。 不審なスクリプトが配置されていたインフラではtracer[.]jpの他に少なくとも数百のドメインが紐づけられていることを確認しています。 これらのドメインの中には2022年5月18日以降も引き続き名前解決できる状態のものがあり、tracer[.]jpで確認されたスクリプトと同一のスクリプトが配置されているドメインも複数存在しています。 ドメインに紐づくIPアドレスの追加も行われていることから、当該スクリプトが配置されているインフラは依然として稼働状態にあると考えられます。 関連事例 本件と関連する事例が過去にセキュリティベンダから報告されています。 2021年にESETは中東をターゲットとした水飲み場攻撃 *2 を報告しています *3 。 この事例で水飲み場攻撃としてWebサイトに埋め込まれたリンクの先に配置されていたスクリプトの1つはtracer[.]jpで確認されたスクリプトと同一のものでした。 当該スクリプトが配置されていたインフラも共通していることから、高い確度で同一のグループであると考えられます。 2020年にはPalo Alto NetworksがCOVID-19関連ドメインについて報告しています *4 。 この事例で複数のコロナウイルス関連ドメインが多数ホスティングされていた不審なパーキングページ *5 に関連するスクリプトとして、tracer[.]jpで確認されたスクリプトと類似のスクリプトが報告されています。 確度はあまり高くありませんが、関数名やリダイレクトロジックの共通性から、何らか関係している可能性が考えられます。 影響範囲 2022年6月7日現在、調査した範囲ではおよそ800のWebサイトにtracer[.]jpへのリンクが残存している可能性があります。 現時点ではtracer[.]jpはどこにもつながっていないため、当該Webサイトにアクセスしても不審なスクリプトが実行されることはありません。 しかし、再び不審なスクリプトが配置された際には、当該WebサイトにアクセスしたWebブラウザ上で任意のスクリプトが実行されうる状態であることに変わりはありません。 おわりに 本記事では、Visionalistで利用していたタグドメインtracer[.]jpが第三者によって再登録された件について確認できている事実とそれに基づく分析の結果を共有しました。 今回の事例がドメインの取得やライフサイクル管理を改めて考えるきっかけとなれば幸いです。 また、tracer[.]jpタグが残置されているWebサイトの管理者さまにおかれましては、リスク回避のためにも当該タグをサイトより削除していただけますよう改めてお願いします。 謝辞 本件を代表するような立場ではありませんが、まがりなりにもセキュリティに携わる一人の人間として、本件について警鐘を鳴らし、各方面に注意を呼びかけてくださった皆さまに感謝をお伝えしたいと思います。 特に、本件を発見し初めに広く注意を呼びかけてくださったtikeさん( @tiketiketikeke )、各方面に積極的に働きかけてくださった坂本一仁さん( @_taka_sakamoto )に心より感謝します。 ありがとうございました。 IoC *.tracer[.]jp 45[.]77.192.33 68[.]183.47.155 165[.]232.142.149 206[.]81.5.96 *1 : 調査結果については社内関連部署にも報告し、対応に活用してもらっています。 *2 : ターゲットが日頃アクセスしているWebサイトを改ざんし不正なコードを設置することで、ターゲットのWebアクセスをトリガーにして攻撃を仕掛ける手法。ライオンが水飲み場の近くで獲物を待ち伏せて狩る様子になぞらえて名付けられました。 *3 : ESET, " Strategic web compromises in the Middle East with a pinch of Candiru " *4 : Palo Alto Networks, " 新型コロナウイルス感染症につけこむサイバー攻撃者たち:関心の高いドメイン名登録で収益化 " *5 : ドメインパーキングサービスに用いられるWebページ。ドメインパーキングサービスとは使用していないドメインを管理するサービスのことで、当該ドメインへのアクセスを「現在ドメインが使用されていない」旨を伝えるページに誘導したり、広告を掲載したページに誘導したりします。
アバター
はじめに イノベーションセンターの神田です。 みなさんはVisionalistというサービスをご存じでしょうか。 VisionalistはNTTコミュニケーションズのグループ会社であるNTTコム オンライン・マーケティング・ソリューション(以下 NTTコム オンライン)が提供していたアクセスログ解析サービスです。 2020年7月にサービスを終了しましたが、2022年5月にこのサービスで利用していたドメインを第三者が再登録し、セキュリティ上問題があるスクリプトを設置している可能性が確認されました(以下 本件)。 本件について、NTTコム オンラインは 広く注意を呼びかけています 。 本件に対して、イノベーションセンターでは事象が確認された直後からそのリスクの有無や程度について詳細な調査を実施してきました *1 。 本記事は、本件におけるリスクがこれ以上拡大しないようにこれまでに我々が確認した事実とそれに基づく分析の結果を社外に広く共有するものです。 また、今後同様の事象が起きないように本件を教訓としてドメインのライフサイクル管理を今一度見直していただくきっかけとなることも期待しています。 社内に向けても先日開催された勉強会において情報共有・啓発しており、本記事はその発表内容をベースとして再編集しています。 目次 アクセスログ解析サービス(Visionalist)の仕組み 経緯 脅威分析 関連事例 影響範囲 おわりに 謝辞 IoC アクセスログ解析サービス(Visionalist)の仕組み アクセスログ解析サービスに詳しくない方のために、Visionalistがどのような仕組みでアクセスを計測していたかを簡単に説明します。 (すでにご存じの方は読み飛ばしてください) Visionalistでは、サービス利用者が予め自分のWebサイトにタグ(tracer[.]jpへのリンク)を埋め込んでおきます。 訪問者がWebサイトにアクセスすると(上図①)、タグを介して計測用スクリプトがWebブラウザにダウンロードされ(上図②)、Webブラウザ上で計測用スクリプトが実行されることでログが生成、収集されます(上図③)。 図のようなアクセス解析サービスの形態は「ビーコン型」と呼ばれます。 Visionalistに限らずビーコン型のアクセス解析サービスは基本的に同様の仕組みでアクセスを計測しています。 経緯 発生日 イベント 2020年7月31日 Visionalistのサービス終了 2022年4月30日 tracer[.]jpが完全に失効 2022年5月5日 第三者がtracer[.]jpを再登録 〜2022年5月17日 不審なスクリプト配置 2022年5月18日 NTTコム オンラインによる注意喚起 Visionalistでは、アクセスログ解析に用いるタグとしてtracer[.]jpを利用していました。 このドメインはサービス終了後、2022年4月30日に期限を迎え、先願による登録が可能な状態となりました。 2022年5月5日になってNTTコム オンラインではない第三者がtracer[.]jpを再度登録しました。 その後、不審なスクリプトが配置されていることが発見され、報告されています。 (ご無沙汰しております、、、) 2020年にサービス提供を終えたVisionalist ASPで使用されていたドメイン名 ( tracer[.]jp ) が数日前に第三者に再登録され、不審なスクリプトが配置されています。 タグの消し忘れが結構ありそうです。 ご注意ください! pic.twitter.com/QWMd6SI6PD — tike (@tiketiketikeke) 2022年5月10日 tracer[.]jpに不審なスクリプトが配置されたということは、タグが残置されたままのWebサイトにアクセスしたWebブラウザ上で不審なスクリプトが実行される状態にあることを意味します。 不審なスクリプトが配置された状況はしばらく続きましたが、2022年5月17日に権威DNSサーバが切り替わったタイミングでDNSレコードが削除され、tracer[.]jpは名前解決のできない、どこにもつながらない状態となりました。 以後、2022年6月7日現在までその状況が続いています。 2022年5月18日にはNTTコム オンラインが注意を喚起する ニュースリリース を公開しました。 脅威分析 事象を認識した2022年5月10日頃から2022年5月17日にDNSレコードが削除されるまでの間tracer[.]jpに不審なスクリプトが配置されていた事実を確認しています。 スクリプトが実行されると、2種類のハッシュ値やタイムスタンプ値などがWebブラウザからtracer[.]jpに送信されます。 スクリプト上ではその後tracer[.]jpからの応答に応じてWebブラウザのページを別URLに遷移させるようなコードとなっています。 具体的には、JSON形式の応答を受け取った場合に応答データ内の情報を元に parent.top.window.location.href の値を書き換える動作をします。 観測の範囲内ではJSON形式の応答は確認されませんでしたが、応答によって任意のURLに遷移させるリダイレクタとして動作することから、潜在的に悪意のあるURLに誘導される可能性が考えられます。 不審なスクリプトが配置されていたインフラではtracer[.]jpの他に少なくとも数百のドメインが紐づけられていることを確認しています。 これらのドメインの中には2022年5月18日以降も引き続き名前解決できる状態のものがあり、tracer[.]jpで確認されたスクリプトと同一のスクリプトが配置されているドメインも複数存在しています。 ドメインに紐づくIPアドレスの追加も行われていることから、当該スクリプトが配置されているインフラは依然として稼働状態にあると考えられます。 関連事例 本件と関連する事例が過去にセキュリティベンダから報告されています。 2021年にESETは中東をターゲットとした水飲み場攻撃 *2 を報告しています *3 。 この事例で水飲み場攻撃としてWebサイトに埋め込まれたリンクの先に配置されていたスクリプトの1つはtracer[.]jpで確認されたスクリプトと同一のものでした。 当該スクリプトが配置されていたインフラも共通していることから、高い確度で同一のグループであると考えられます。 2020年にはPalo Alto NetworksがCOVID-19関連ドメインについて報告しています *4 。 この事例で複数のコロナウイルス関連ドメインが多数ホスティングされていた不審なパーキングページ *5 に関連するスクリプトとして、tracer[.]jpで確認されたスクリプトと類似のスクリプトが報告されています。 確度はあまり高くありませんが、関数名やリダイレクトロジックの共通性から、何らか関係している可能性が考えられます。 影響範囲 2022年6月7日現在、調査した範囲ではおよそ800のWebサイトにtracer[.]jpへのリンクが残存している可能性があります。 現時点ではtracer[.]jpはどこにもつながっていないため、当該Webサイトにアクセスしても不審なスクリプトが実行されることはありません。 しかし、再び不審なスクリプトが配置された際には、当該WebサイトにアクセスしたWebブラウザ上で任意のスクリプトが実行されうる状態であることに変わりはありません。 おわりに 本記事では、Visionalistで利用していたタグドメインtracer[.]jpが第三者によって再登録された件について確認できている事実とそれに基づく分析の結果を共有しました。 今回の事例がドメインの取得やライフサイクル管理を改めて考えるきっかけとなれば幸いです。 また、tracer[.]jpタグが残置されているWebサイトの管理者さまにおかれましては、リスク回避のためにも当該タグをサイトより削除していただけますよう改めてお願いします。 謝辞 本件を代表するような立場ではありませんが、まがりなりにもセキュリティに携わる一人の人間として、本件について警鐘を鳴らし、各方面に注意を呼びかけてくださった皆さまに感謝をお伝えしたいと思います。 特に、本件を発見し初めに広く注意を呼びかけてくださったtikeさん( @tiketiketikeke )、各方面に積極的に働きかけてくださった坂本一仁さん( @_taka_sakamoto )に心より感謝します。 ありがとうございました。 IoC *.tracer[.]jp 45[.]77.192.33 68[.]183.47.155 165[.]232.142.149 206[.]81.5.96 *1 : 調査結果については社内関連部署にも報告し、対応に活用してもらっています。 *2 : ターゲットが日頃アクセスしているWebサイトを改ざんし不正なコードを設置することで、ターゲットのWebアクセスをトリガーにして攻撃を仕掛ける手法。ライオンが水飲み場の近くで獲物を待ち伏せて狩る様子になぞらえて名付けられました。 *3 : ESET, " Strategic web compromises in the Middle East with a pinch of Candiru " *4 : Palo Alto Networks, " 新型コロナウイルス感染症につけこむサイバー攻撃者たち:関心の高いドメイン名登録で収益化 " *5 : ドメインパーキングサービスに用いられるWebページ。ドメインパーキングサービスとは使用していないドメインを管理するサービスのことで、当該ドメインへのアクセスを「現在ドメインが使用されていない」旨を伝えるページに誘導したり、広告を掲載したページに誘導したりします。
アバター
はじめに スタンフォード大学の John Ousterhout 教授が執筆された “ A Philosophy of Software Design ”(以下 APoSD と略す) という書籍をご存じでしょうか? 書籍のタイトルを直訳すると、「ソフトウェア設計の哲学」となります。書籍の内容はまさに、ソフトウェア設計について扱っています。 本書籍をベースに、「A Philosophy of Software Design を30分でざっと理解する」というお題で社内ランチ勉強会が開催されました。本記事執筆者である岩瀬( @iwashi86 )が発表者であり、勉強会資料は以下のとおりです。 スライド P.4 に記載したとおり、本書籍は John Ousterhout 教授の意見が強く反映されており、ソフトウェアエンジニアであれば、議論を呼ぶ箇所があります。実際、勉強会の実況Slackでは、「これはどうなんだろう?」といった疑問があがっていました。 本記事では勉強会であがっていた疑問について、 twada 顧問と対話した内容を紹介 1 していきます。読者の皆様のソフトウェア設計に関する知見の拡大が記事のゴールです。 なお、事前の免責が1点あります。対話の起点は APoSD ですが途中からソフトウェア開発全般のトピックに派生していきます。「お、APoSD から外れて、ソフトウェア設計全般のトピックに派生していったな」という気持ちで読んでいただけるとありがたいです。 さて以降は、大きく2部構成となります。前半では、 APoSD での主張概要を紹介します。後半を読むためのベースラインを合わせるのが狙いです。SpeakerDeckの資料をお読みの場合は飛ばしていただいて構いません。後半は、本題となるtwada 顧問との議論パートです。早速いってみましょう! APoSD の概要 APoSD の主題は「複雑性」です。この複雑性はソフトウェアの構造に関するものであり、システムの理解や修正を難しくするものです。システムの理解や修正が容易であれば、そのシステムは「複雑ではない」ということになります。仮にどんなに大規模なシステムだったとしても、理解や修正が容易であれば、APoSDの定義では、複雑ではないシステムになります。 複雑性が増大すると、以下の3つが起こります。 Change Amplification (変更の増大) Cognitive Load (認知的負荷) Unknown Unknowns (未知の未知) たとえば、一見単純に見えても変更箇所が多い場合が 「変更の増大」 に該当します。また、覚えるべきAPIや、気にしないといけない変数が多い、といった場合は「認知的負荷」の増大につながります。さらに、あるタスクを完了させたいとして、何を変更すればいいかわからない状況が未知の未知となります。APoSDでは、未知の未知が3つの中で最悪と述べられています。なぜならば、何らかの変更を加えても、バグが出るまで発見できないためです。 このような複雑性はどこから生まれてきてしまうのでしょうか?APoSDではその要因として、 Dependency (依存性) Obscurity (不明瞭性) の2点が挙げられています。たとえば、何らかのモジュールを操作する場合に、1つのモジュールで完結できない場合は、依存性がある状態になります。また、コードを読み解かないとわからない場合は不明瞭性がある状態となります。一例として、 “time” という変数があった場合、単位などの補足がなければどのように理解していいかわかりません。 この複雑性に、ソフトウェアエンジニアはどのように立ち向かえばいいのでしょうか? APoSDでは、その方法として以下2点が挙げられています。 複雑性の排除(たとえば、特別なケースを排除する) 複雑性の隠蔽(たとえば、難解な部分が見えなくても使えるようにカプセル化する) 書籍では、この1と2を中心に、全22章でより具体的に説明されていきます。詳細は原著に譲るとして、ここから一部のトピックについて当社技術顧問のtwadaさんと議論していきます。 技術顧問とAPoSDについて話してみた 小クラス主義 と 大クラス主義 APoSD では4章の中で、以下のソースコードが記載されています。 FileInputStream fileStream = new FileInputStream(fileName); BufferedInputStream bufferedStream = new BufferedInputStream(fileStream); ObjectInputStream objectStream = new ObjectInputStream(bufferedStream); やりたいのは、ファイルを開いてシリアライズ化されたオブジェクトを読み込むことです。クラス設計としては、 Gang of Four デザインパターンでいう、Decorator パターンが活用されています。結果として、小さいクラスを組み合わせて実装を進めるコードになっています。 本件を勉強会で話していた際に、Slackでtwada顧問からコメントが上がっています。 これについて、以下で深堀りしていきます。 iwashi: 小クラス主義ってことは大クラス主義があります? twada: はい、小クラス主義と大クラス主義があります。私のこれまでの言語キャリアから考えると、小クラス主義の代表はJavaで、大クラス主義の代表はRubyです。 小クラス主義の例としては、Javaの先ほどの File I/O がわかりやすいですね。大クラス主義におけるRubyの例としては、 Array とか String のクラスに多くの機能や責務がありますね。少ないクラスが、それぞれ多くの機能を持つ。必然的にクラスが大きくなります。 iwashi: この辺りは、APoSDの主張とどう関連がありますか? twada: APoSDの著者が述べているのは、Deep Moduleが良いということですよね。Deep Module(深いモジュール)とは、インターフェースが狭くて実装が深いことです。反対に、Shallow Module(浅いモジュール)はインタフェースが広いわりに、実装が浅いことを意味します。 これ自体は小クラス主義とも、大クラス主義ともちょっと違います。ただ、小クラス主義は必然的にShallowなModuleに近づきがちです。 iwashi: Shallow ModuleのようにIFが広いのを避けたいのは、認知負荷の増大でしょうか? twada: そうです。よく似た機能がいくつもあって、その中から選ばないといけない、利用者としては認知的負荷が高い。 よく似ていて、ちょっとずつ機能が違うクラスがたくさんあり、その中から選んで使ってください、みたいなパターンはありますよね。あるいは、よく似ていて、ちょっとずつ違うメソッドがあるというパターン。これも認知負荷が高いですよね。 Javaの設計思想 iwashi: 小クラス主義にも一定のメリットがあるのかと思います。たとえば、Javaのもともとの設計思想だと何を意識されていたのでしょうか? twada: これから述べる課題は最近のJavaでは解決されていますが、歴史からお話します。 もともとのJavaの設計の背景にあったのは、すべての状況を破綻なく扱える設計を提供しようという価値観です。 たとえば巨大なファイルを開こうとすると、out of memoryになり得ますよね。だから、たとえば、ArrayとかListにファイルの全行を簡単に読み込むのは、意図的にできないようにしていました。それよりは、どのような場合でも適切に動く汎用的な組み合わせを提供しています。たとえば、InputStreamを開いて、InputStreamReaderでCharレベルに変換して、BufferedReaderでバッファリングするという例のコードになります。 「これは理屈は分かるんだが、めんどくさいよね」「正しいけど、面倒だよね」という話になるんです。ちなみにこの問題はJavaの進化と共にだんだん解決されていき、現在では単純な問題は短く解決できるようになりました。この正しさと面倒くささの問題に対してRubyも大クラス主義の観点からアプローチしています。 Rubyでの設計例 - utc_offset Rubyの標準ライブラリの設計者はMatzさんを中心に何人かいます。その中の1人の田中 哲(akr)さんは、正しさと使いやすさの関係をとても敏感に考えています。正しいやり方が簡単でないと、みんな正しいやり方を使ってくれない。だから、正しいやり方がもっとも簡単であるべきというAPI設計をしたんです。 簡単だけど若干間違っているコードと、正しいけどすごく面倒なコードがあるときに、人は簡単だけど若干まちがっているコードに引き寄せられがちです。その典型例は、UTCからの時差の求め方。時刻処理は正確にやろうとするとかなり面倒な部分があります。具体的には、うるう秒があると一秒ずれるとか。この部分を正しく実装しようとすると、2行だったものが8行ぐらいになるわけです。すると、誤差があっても簡単な方に開発者は寄っていきます。 そんなとき、田中さんはRubyのTimeクラスに utc_offset を定義して、正しいけど面倒なやり方を1回のメソッド呼び出しで済むように設計しました。Timeクラスのutc_offsetメソッドを呼び出すほうが明らかに短くてかつ正しいやり方というわけです。「一番正しいやり方が、一番短くて簡単であるべき」という考え方によって正しい方向に利用者を誘導する設計をしたわけです。この辺りの詳細は『 APIデザインケーススタディ ――Rubyの実例から学ぶ。問題に即したデザインと普遍の考え方 』に載っています。 良いインタフェースとは? 実は同じ主旨の内容が、 プログラマが知るべき97のことの1つ にも載っています。 良いインタフェースとは次の2つの条件を満たすインタフェースのことです。 正しく使用する方が操作ミスをするより簡単 誤った使い方をすることが困難 このような考え方に照らし合わせると、小クラス主義で、利用者に適切な組み合わせ方や責務の選び方を委ねるのもなかなか難しいな、という話になります。もちろん、言うは易く行うは難し、という話でもあります。 iwashi: なるほど、たしかに設計のもっとも難しいポイントですね。 1つのことを実現するために1つの正しいやり方がある、というのはシンプルでとても素晴らしいと思うのですが、現実として、1つのことを実現するために複数のやり方がある例って、これまでどのようなものがあったのでしょうか? twada: 古典である『 プログラミング作法 』から1つ紹介します。この中では、Cの標準ライブラリが紹介されていますね。たとえば、アウトプットストリームに1文字書きこむ場合に、putc、fputc、fprintf、fwriteがありますね。 iwashi: たしかに利用者側は混乱しますね。ライブラリを作るなり、ビジネスロジックを作る場合には、そういう設計を避けるのが大事ということですね。 twada: そうです。たとえば、『プログラミング作法』から引用すると 少なくとも、どうしても関数を増やさなければならない明確な根拠が 生まれるまでは、広いインターフェイスよりも狭いインターフェイスのほうが 望ましい。1つのことだけ実行し、それをうまく実行すること。 可能だからというだけでインターフェイスに追加してはならないし、問題があるのは実装のほうなのにインターフェイスに手直ししたりしないこと。 たとえば、速度面で有利なmemcpyと安全面で有利なmemmoveがあるよりも、常に安全に利用でき、できれば高速に動作する関数が1種類存在する方がいい。 とあります。 iwashi: なるほど。 似た機能でちょっと違うものの実装ってどうする? iwashi: 少し話を戻して、「似た機能でちょっと動作が違う場合」というのは現実の開発現場でよく出会う例かと思います。これって、どうすればいいんでしょうか? twada: たとえば、動作をオプションで変えるかどうか等で悩むことになりますよね。APoSDでも言及されています。 この点は、私だったらリファクタリングをしていきます。 iwashi: リファクタリングの方向性は? twada: 基本戦略としては、よく似ていてちょっとずつ違う箇所があるときに、ちょっとずつ違う部分をくくりだしていきます。そうすると、完全に一致する部分と、それぞれ違う部分に分かれていきます。完全に一致する部分は外部に抽出して共通化できます。昔は親クラスに抽出して継承による差分プログラミングをしていた時期がありましたが、現代では推奨されません。オブジェクトを組み合わせ、共通部分を委譲していきます。 その際にインタフェースを広げないように、バリエーションを追加するのにポリモーフィズムを使います。ここは、APoSDの著者とは流派が違うかもしれませんね。 iwashi: ポリモーフィズムの箇所の実装イメージをもう少し具体的にお話しいただくとどうなりますか? twada: まず、よく似ていてちょっとずつ違う部分で共通を抽出すると、共通部分の大部分は利用者が直接触らない部分に移動します。共通部分の多くは利用者に露出していないので、設計はある程度自由になります。 次に、新しいバリエーションが増えるときに、それをどう扱うか、という話になります。1つ目のユースケース、2つ目のユースケース、3つ目のユースケースで似たような抽象が見て取れるのであれば、共通部分との界面にインタフェースを導入して、汎用的な実装として組み合わせられるようにします。リファクタリングが結果的にドメインモデリングに近づいていきます。 iwashi: これは共通部分の抽出とは違うってことですか? twada: そうです。具体例を出します。 たとえば、私が受託して開発していたある学内システムがあります。最初はあるレポートを出すという要件が1つあり、機能は一枚岩で作られていました。しばらくして新たな連携先がでてきて、ちょっとずつ要件が違うレポートを出す必要が増えました。 学生向けはその学生だけの情報が出る 教員向けレポートならクラス全員の情報が出る 担当クラスの教員向けには全情報を出すが、担当外の教員が見る場合は、いくつかの情報がマスクされる 今後は連携している他大学の教員もレポートが見えるようになる予定。その際にはさらに情報がマスクされる このようによく似ているけど、ちょっとずつ違う要件が出てくるんですよね。単純にやるならば、if文で条件分岐します。閲覧するユーザも引数で渡すとか。 iwashi: 一般ユーザーと管理者ロールで分けるべきケースとかですよね。 twada: そうです。 新しいロジックが入るたびに、if文が増えることになります。ただ、リファクタリングしていくと、次のような気づきがあります。 たとえば「何を見せる」「見せない」というロジックは、データレベルの認可、データ可視性のロジックになっているわけですよね。リファクタリングしていく中で、完全に同じ部分と違う部分を寄せる・集めていくと、違う部分というのはだいたいにおいてこのようなロジックであることに気づきました。 そうなると、何をやればいいかというと、ユーザーが誰かではなく、レポートの元データの抽出機能とコンテクスト毎に異なるデータのフィルターがあって、2つの組み合わせで動けば要件を満たせる、という話になります。 たとえば、ログインユーザーがその授業の担当教員の場合は全部見えるフィルターを渡します。フィルターの中身は実質的にはNull Objectパターンで良いわけです。学生だったら、担任以外だったら、学外だったら、という形で各々のフィルターインスタンスを作れるようにします。GoFのStrategyパターンですね。それを状況に応じて共通のレポーターに渡せばいいわけです。 (学生の場合は全員検索してからフィルターするよりもそもそも自分のデータだけを抽出する方が効率が良いので、まずは動くもののパフォーマンス改善の余地が大きいバージョンでリリースし、後にデータ抽出部分のフィルタリングも含んだ複合的なインターフェイスになりました) iwashi: Javaで実装するならフィルターの interface を定義することになりますか? twada: Javaでいえばそんな感じですね。 このレポートの場合はいくつかのユースケースがありますが、総じて何を見せるか・見せないかのロジックの差異が多かったので、ロジックの差異を表現したクラスを作って渡していく。そうすることで、レポート機能のコードはほぼ変更無く、新たなフィルタリングが提供できます。たとえば今後は連携している他の大学の教員もレポートが見えるようになり、そのときはマスクする項目がさらに増える予定です。この場合も、他大学の教員用のStrategyクラスを用意すれば良いことになります。 これは、継承に頼ると出てこないレベルの抽象なんです。継承でやるなら、AbstractReporterが出てきて、if文が抽象メソッドになってて穴埋めする実装になるでしょう。具象クラスに穴埋め部分を実装してフィルタリングするわけですね。つまり、親クラスが抽出を行い、サブクラスがフィルタリングをする。この方向性だと、データ抽出とデータレベルの認可が異なることに気づきにくい。 iwashi: これは差分クラスに近い考え方ですね。GoFパターンでいえば、Template Methodパターンに近いですね。 iwashi: Template Methodパターンって、現代だと筋が悪いのでしょうか? twada: 筋がいいのは、現代ではあまりありませんが、処理の順序を示すパターンですね。たとえば、ユニットテストにありますよね。 iwashi: テスト前のsetUp、テスト後のtearDownですね。 twada: そうです。ただそれも、Template Methodパターンが必須というわけではありません。 たとえばObserverパターンでも実装できますよね。現代においては、そもそも継承をあまり使わなくなってきています。 APoSD の内容で継承を考えると? iwashi: なるほど。この辺で、もう一度APoSDに戻りたいのですが、APoSDで述べられていた次の3つの複雑性から招かれる事象と、継承の関連は何なのでしょうか? Change Amplification (変更の増大) Cognitive Load (認知的負荷) Unknown Unknowns (未知の未知) twada: 継承は親クラスとの間に強い依存関係が生まれますよね。継承の使い方を誤ってしまうと、継承階層が深くなります。また処理を読み解くときにサブクラスを読んで、次に親クラスを読んで、親の中身を見たと思ったら、またサブクラスでオーバーライドされていた、みたいな不可解さを持つことがあります。 iwashi: APoSDでは、複雑性の要因は APoSD で次の2つがあがっていましたね。 Dependency (依存性) Obscurity (不明瞭性) まさに、この2つにもつながるわけですね。不明瞭性でいえば、コードの距離が遠くなりますからね。 twada: そういうわけです。というわけで、最近出てきたプログラミング言語では継承という概念がないことも増えてきて、継承ベースのテクニックが推奨されなくなってきたんですね。 おわりに 本記事では、前半でAPoSDの概要を紹介しました。後半では、小クラス主義・大クラス主義、インターフェース設計、リファクタリングによるドメイン抽出といった内容の対話を紹介してきました。 実は、後半の対話内容は全体の内容のうち、半分程度しか記事に起こせていません(半分だけでも非常に多いかも?)。本記事が好評でしたら、後半パートや、続編を検討していきますので、 Twitter などでフィードバックいただけますとありがたいです! 「fukabori.fm の文字起こしじゃないか」と思った方。だいたいあってます。 ↩
アバター
はじめに こんにちは、イノベーションセンターの鍔木(GitHub: takuma0121 )です。 今回は OT(Operational Technology)ネットワークのセキュリティリスク可視化サービスである OsecT(オーセクト)をリリースしたので、これまでの取り組みとサービスの特徴についてご紹介します。 OsecTとは OsecT は、OT ネットワークを流れるパケットを収集して、ネットワークの可視化及び脅威・脆弱性を検知するセキュリティリスク可視化サービスです。 主なターゲットは大規模なセキュリティ投資が難しい中堅企業・中小企業で、特徴の1つとして低価格であることが挙げられます。 OT や OsecT の機能については、「制御システムのセキュリティと対策技術 OsecT のご紹介( 前編 ・ 後編 )」で詳しく説明しています。 ご興味がある方はぜひご覧ください。 サービス名は「OT の中心に一本セキュリティ(SECurity)を軸に据える」の意味を込めて命名しました。 ロゴはセキュリティリスクの可視化・検知・対処を3つの箱でイメージして、周りを取り囲むようにセキュリティを守ることを連想させるものにしました。 また、セキュリティの世界ではブルーチーム = 防御側を表現するため、青色をベースにしました。 実証実験の実施 OsecT は「制御システムに影響を与えることなくセキュリティを可視化する技術」として、2021年7月〜2022年2月までの間、 実証実験を実施 していました。 お問い合わせを多数いただき、その中から3社に実験参加していただきました(1社あたり3ヶ月間)。 実証実験の進め方 本章ではこれまでの取り組みとして、実証実験についてご紹介します。 実証実験では、OsecT の機能だけでなく、サービスの導入・利用・廃止までのライフサイクルやユーザーマニュアルなどのお客さまが参照するドキュメントなどあらゆる観点を検証しました。 OsecT はパケットを取得・ログ化するセンサーと、可視化・検知機能を有するクラウドに分かれています。 OT システムの運用担当者である実証実験参加ユーザー(以下、ユーザー)には、センサーとセンサー設置手順書、及び ユーザーマニュアルを送付しました。 センサー到着後、ユーザー自身でセンサーを設置、ユーザーマニュアルを参照して OsecT を利用してもらう、という実サービスで想定している手順通りに実施しました。 実験期間中に最大限のフィードバックを得られるようにするため、利用方法や不具合などの各種問い合わせは Slack を利用して、お問い合わせに迅速に回答する環境を用意しました。 また、実験期間中に計3回のユーザーインタビューを実施しました。 インタビューでは、サービスの導入のしやすさ・利用頻度・ユースケース・Web UI の使いやすさ・価格などをヒアリングしました。 ヒアリングでは、ユーザーインタビューに関する知見がある KOEL に協力してもらい、インタビューから得られるフィードバックを最大化するようにしました。 実証実験を通じて得られたこと 各社3ヶ月間の実験期間の間に、日々のお問い合わせやユーザーインタビューを通じて、想定通りの部分や改善が必要な部分を洗い出すことができました。 導入部分 実証実験では、ユーザーにセンサーとセンサー設置手順書、及びユーザーマニュアルの送付のみで導入から利用開始まで問題が発生しませんでした。 これにより、低価格サービスに向けた導入部分のコスト削減の目処が立ちました。 また、ユーザーインタビューでは想定価格5~10万円であれば OsecT を使いたいというニーズも把握できました。 これにより、サービスリリース後に、価格が導入のネックにならないことを確認できました。 一方で、日々のお問い合わせの中で、可視化機能はユーザーマニュアルや Web UI を見るだけで使いこなせることが確認できましたが、「学習・検知機能はそもそもどのように使えばいいか全くわからない」と言った声をいただきました。 そのため、ユーザーマニュアルの改良に加えて、学習・検知機能を使うためのチュートリアルを提供予定です。 Web UI OsecT は機能が充実している一方で、画面にすべての機能が表示されると何を使っていいのかわからなくなる ≒ ユーザーフレンドリーから遠いデザインであることも明らかになりました。 そのため、KOEL の協力のもとデザインを1から見直しました。 新デザインのポイントは、ユーザーのデイリーユースをインタビューに基づいて想定し、繰り返しアクセスする箇所の使いやすさを重視した点です。 下図はデザイン見直し前後のトップページです。 ダッシュボードの導入・画面左メニューの機能を集約すること・英語表記を日本語表記に変えるなどして、ユーザーが使いやすいデザインとしています。 機能や性能 機能面は必要な機能が揃っているという評価をいただけました。 特に、端末一覧を可視化する機能は手作業で管理している資産台帳との突合による確認や、台帳そのものの更新が滞るという課題解決につながる可能性があると好評でした。 一方で、機能追加や性能改善の要望もありました。 例えば、脅威・脆弱性を検知したタイミングでアラート通知が必要であること。 さらに、学習状況によっては大量のアラート発出が想定されるため、一定間隔でアラートを集約して集約結果をメールでアラート通知する機能を実装しました。 その他に、Web UI 全体を通じてデータ量によっては応答速度が遅くなることも課題になりました。 内部アルゴリズムの見直しなどで、ストレスがないレベルまで応答速度を改善しました。 その他 その他に、実データを踏まえたクラウドやセンサースペック、モバイルデータ量などインフラコストを最適化することで、低価格サービスの実現につなげることができました。 4月25日に OsecT 正式リリース! 実証実験で得られたことを活かして OsecT を改良し、2022年4月25日に サービスリリースしました 。 本サービスの特徴 実証実験を踏まえて改善した OsecT の特徴をご紹介します。 低価格(初期費用 25万円+月額 6万円)で、大規模なセキュリティ投資が難しい中堅企業・中小企業でも導入がしやすくなっています。 複雑な構築は不要でスイッチのミラーポートに接続するだけで、OT システムに影響を与えることなく導入可能です。可用性が求められる OT システム向けには不可欠な特徴です。 リモート環境から Web UI が利用可能で、セキュリティ監視のための出勤や VPN 接続は不要です。 ユーザーインタビューを通じて明らかになった、OT 環境に求められる OT ネットワーク可視化と脅威・脆弱性検知の機能を具備しています。 おわりに 今回は実証実験の取り組みや得られた知見、及びリリースしたサービスの特徴をご紹介しました。 実証実験を通して、想定通りの部分や改善を要する部分が明らかになり、過不足のない開発を進めることができました。 実証実験にご参加いただいた企業さまには、この場をお借りして御礼申し上げます。 また、低価格で OT ネットワークの可視化や脅威検知ができるので、セキュリティ対策に手が出ていない・不十分と感じている企業さまにはぜひご利用いただきたいです。 ご興味がある企業さまは、 こちら からご契約またはお問い合わせをしていただければ幸いです。
アバター
はじめに こんにちは、田啓文と申します。NTTコミュニケーションズで開催された2週間のインターンシップに参加させていただきました。 普段はSDNアーキテクチャにおけるDDoS検知法について研究しています。 今回のインターンシップでは「 次世代のサービスを生み出す検証網Testbedの設計構築業務 」をテーマとして、Segment Routingという技術を中心とした様々な検証をしました。 私は学部時代にInter-AS MPLS VPNに関して研究していたため、今回は経験を活かしてL3VPN over SRv6 を検証しました。この記事では、その体験談を記載します。 インターンシップに参加するまでの経緯 日本へ留学する前のことですが、2018年に中国でNTTの求人情報(NTT communications China)を拝見しました。 その時はまだ日本語が流暢に話せず、日本に関する知識もあまりありませんでしたが、そのNTTの求人情報をきっかけに日本の通信企業への関心が高まり、日本への留学を決めました。 そのため、今回インターンシップに参加する際、迷わずNTTコミュニケーションズのインターンに申し込みました。 インターンシップで取り組んだこと 今回のインターンシップでは、Segment Routing(SR)技術とIPv6を融合するSRv6という新しいルーティング技術をCisco機器にて検証しました。 まず前半ではSegment Routingの概要と動作原理を紹介します。 Segment Routing Segment Routingでは、ネットワークドメインにあるそれぞれのノード、パスなどの転送対象をセグメントという単位で扱い、各ノードはSegment ID(SID)で指示された処理を行いながらパケットを転送します。 パケットに経路制御用のヘッダーを追加して、ノードはそのヘッダーの中にあるラベルを使用した上でフォワーディングを行います。 従来のMPLSには、中間ノードのステートが増加するという課題があります。 そこでSegment Routingを利用することで、ネットワークを簡素化して中間ノードの持つステートを削減し、ネットワークの管理と運用を容易にできます。 更に既存のコントロールプレーンとデータプレーンとの親和性があるために、最近では各領域に幅広く使われ始めています。 以下の図がSRで転送する一例となっています。 引用: https://www.juniper.net/us/en/research-topics/what-is-segment-routing.html SRv6 SRv6は、専用のIPv6ヘッダーを利用し、Segment Routingを実現する技術です。 ヘッダーに含まれる各SIDには、パケットの宛先を示すLocator Field、パケットに適用する機能を示すFunction Fieldが用意されています。 Function Fieldに各種の指示を埋め込むことで、SIDを参照した際に各ノードがどのような処理を行うかを指定できます。 Decap後の処理により、End.DT4、End.DT6、End.DX6、End.DX2などのFunctionが定義されています。 SRv6の利点としてはIPv6ネットワークと共存しつつ、128bitのSIDを用いてFunctionを自由に定義できることによる高い拡張性が挙げられます。 実装内容 Ciscoの環境においてSRv6プロトコルを検証するために、まずはシンプルなネットワークトポロジーを構築します。 SRv6に関わるサービスの中で、今回の検証にはIPv4 L3VPN over SRv6 を選びます。以下の図に作成した検証環境を示します。 最初に軽く検証機器について説明します。HostにはUbuntu20.04.3を2台を使います。Provider Edge(PE)としてCisco IOS-XRv 9000 Router を2台使います。 ルーターにはIOS XR Release 7.4.1を使用するため、コマンドは下記のリンクを参照しました。 Cisco IOS XR 7.4.xConfiguration Guides 検証環境を構築するために、以下の手順に従ってセットアップします。 事前準備として各機器のIP addressをセットアップする 2つのPEルーターにベーシックなIntermediate System to Intermediate System(IS-IS)をセットアップする SRv6用にIS-ISプロトコルへLocatorを発行する追加コマンドを設定する L3VPN用のBorder Gateway Protocol(BGP)環境をセットアップする Host間はIPv4 addressを使用し通信します。簡略化のために、Customer Edge(CE)を省略して、SRv6 domainにPEとするルーター2台のみを用意しました。 各PEにIS-ISプロトコルをInterior Gateway Protocol(IGP)としてセットアップし、VPNのためにBGPもセットアップします。 SRv6が正しく動かせるかどうかを検証するために、Host1からHost2へパケットが正常に発送できることを確認します。 また、SRv6 domainにLocatorとSIDが正しく発行、共有されるかどうかも確認します。 結果 インターンシップ最終日までの取り組みと結果を説明します。 まずはIS-ISを確認します。 以下がPEにおけるLocatorの学習状況です。本ルーターのLocatorの状態が up となって、さらに反対側のLocatorも学習できています。 この状態が確認できるとIS-ISのセットアップが成功となります。 次にPE側でSIDの発行状況を表示させた結果です。 End.DT4 のSIDが存在することを確認できると、BGPのコンフィグに成功したことがわかります。 次のステップはPE2でDecapする End.DT4 SIDがPE1に正しく交換されたことを確認します。 以下の図の通り正しく交換されたことが確認できます。ここで、 Behavior:19 はEnd.DT4に対応する番号です。 最後はVPN 経路構築について確認します。 上記のCisco Configuration Guidanceを参照すると、SID 情報が載るはずですが、下記の図では載っていません。 最終日でその原因を考えた結果、恐らくIPv4 packetをSRv6 domainを経由し相手へフォワーディングする際に、SRv6のヘッダーが正しくEncapされないのではないかと推測しました。 このEncapが実施できた場合、当初の期待通りIPv4 L3VPN over SRv6 が実現できると推測しています。 インターンシップの感想 インターンシップ前に何回も日本の職場の雰囲気をイメージしましたが、実際に体験するまでは色々と心配していました。 しかし、先輩社員と一緒に過ごした2週間の体験によって、日本に就職したいという気持ちがさらに湧いてきました。 本当にありがとうございます。 今回テーマとして選択したSRv6は最先端の技術です。 そのため、正しく検証するためにはまずSRv6の動作原理を明確に把握しなければ、問題等がありうまく動かない時に、実際にどの部分が足りていないかがわかりません。 一方、SRv6の情報はベンダーのテキスト以外参照できる文献も少ないという現状もあります。エラーが発生した際には、従来の経験が問題点の把握や対応にあたって大変役に立ちました。 今回のインターンを通じて、仕事上では様々な業務やコミュニケーション、経験、また思考力などが非常に大事なことを再発見いたしました。 特にコミュニケーションについては、外国人なので、専門用語や省略語などが難しく、意識して学習する必要があると感じました。 また、今回のインターンをきっかけとして、本物の職場の雰囲気も体験できました。 メンターの竹中さんには、いつも理解しやすい言葉で複雑な専門用語を説明して頂き本当に感謝いたします。 トレーナーとしてではなく、休憩時間の雑談でも親身にアドバイスやお話をして頂きました。 人見知りの私は飲み会の前にずっと心配していましたが、皆様が本当に親切に接してくれましたので、本番の時にチームの方々と盛り上がってたくさんお話ができました。 最後になりますが、二週間のインターンを通じて貴重な経験をさせていただき、改めて竹中さん、三島さん、木村さんに感謝いたします。 メンターからのコメント メンターを担当したイノベーションセンターの竹中です。2週間のインターンシップお疲れ様でした。 今回のインターンシップにおいて、田さんには我々のチームでの取り組みを理解していただいた後に、興味を持ってもらった部分である Cisco 機器での L3VPN over SRv6 の動作検証に取り組んでいただきました。 インターンシップ初日には SRv6 に関する知識はあまりないとお聞きしていたのですが、持ち前のネットワークに関する知識と探求心、情報を整理し説明/質問する力を存分に活用し、 SRv6 の動作について正確に理解しつつ機器への設定やトラブルシューティングを行い、成果として検証内容を資料にまとめていただきました。 馴染みのない技術に触れながらも、楽しんで次々と技術理解や検証に取り組んでいただけたようでメンターとしても嬉しく思います。 作成いただいた資料は config 例や verificaiton を参考として、今後私たちが行う SRv6 の実用網へのデプロイにおいて活用させていただきます。 このインターンシップを通して得られた様々な経験が今後の田さんの取り組みや意思決定のお役に立てると幸いです。 改めて、インターンシップへのご参加とご活躍、ありがとうございました!
アバター
みなさんこんにちは、社内のエンジニアが働きやすくすることを目標にする Engineer Empowerment プロジェクトの @Mahito です。 先日 NTT グループのエンジニア有志が開催する NTT Tech Conference 2022 を開催しました。 私は運営スタッフとして立ち上げからこれまでの運営に関わってきましたが、本記事では運営スタッフの立場でイベントの概要と運営の裏話についていくつか紹介いたします。 NTT Tech Conference とは NTT Tech Conference は、NTT グループのエンジニア有志が開催する技術系カンファレンスです。 NTT グループには各種 OSS のコミッタ、メンテナ、コントリビュータをはじめとしたエンジニアや、各社の様々な案件でシステムやサービスを開発をするエンジニアがいます。 しかしながら、こうしたエンジニアたちの取り組みを会社として紹介する機会は限られています。そこで、NTT グループのエンジニア有志たちが自分たちのエンジニアとしての取り組みを紹介できる場として用意したのが NTT Tech Conference です。 このイベントは、NTT グループに様々なエンジニアがいることや、扱っている技術について多くの人に知っていただくことを目的に行っています。 また、NTT グループ各社が開催するイベントとは異なり、NTT グループのエンジニアたちがやりたいこと・話したいことを通じて、参加したエンジニア同士が技術交流することも目的にしています。 イベント当日の様子 NTT Tech Conference 2022 は、午前の部と午後の部に分けて行われ、午前午後で合計 506 名の参加登録がありました。 午前の部では参加者が手を動かし学ぶ 2つの Hands-on と、参加者でグループを作り体験をしながら学ぶ Workshop が行われました。 午後からは発表が行われ、2 Track で LT も含めて 15 の発表がありました。 どのような発表があったのかご興味のある方は、以下のイベントページからご確認ください。 NTT Tech Conference 2022 イベントページ 午後の発表については、発表者の許諾を得られたものは公開しています。もし興味のあるセッションがあればぜひご覧ください。 また、発表資料が公開されたものについては上記イベントページの発表詳細に資料へのリンクがあります。 午後の部 - Track1 www.youtube.com 午後の部 - Track2 www.youtube.com イベントの裏話(運営の話) さて、前置きが長くなりましたがここからが本題です。 何度も言うように、NTT Tech Conference は NTT グループのエンジニア有志によって開催されているという少し変わったイベントです。 今回は以下の 4 つについて、エンジニアたち自身が運営上行った工夫を紹介します。 発表提案募集 招待講演 イベントページ miro を使った交流 1. 発表提案募集 イベントでの Hands-on、Workshop、発表については Call for Proposals (CFP) という形で、 NTT グループ内のエンジニアたちに手伝ってもらいながらグループ内に発表提案の募集案内を流しています。 発表を希望する側のエンジニアは、自分たちが面白いと思っている技術や紹介したい技術についての発表を提案します。 運営側は、集まった CFP の内容を確認の上、採否を通知します。 ちなみに、現在までにイベントは 6 回の開催をしましたが、毎回ほぼすべての発表提案を通しております。 (私の怪しい記憶では、過去に1度だけ同じ人からの発表提案が同時に2つあったのを1つにしてもらっただけで、ほぼ出せば通るという状態です。) これには理由があり、なるべく多くのエンジニアに発表する機会を提供したいとの思いから、可能な限り全員が発表できるように調整しています。 そのため毎回タイムテーブルの決定にはかなり苦労します。 一方で、CFP は NTT グループ内から幅広く受け付けているものの、グループ各社からまんべんなく出てくるというよりは、かなり偏りがあります。 今回は NTT Com からの発表がかなり多かったのですが、各社の発表数のバランスを考えるというような配慮は全く行っていません。 上記の通り、CFP を出してくれた人に発表をしていただいた結果、「そういう回もある」ぐらいで考えているので今後も偏りは出続けると思います。 しかし、願わくばいろいろな NTT グループの会社から発表提案をいただければと常々思っています。 2. 招待講演 今回は NTT Tech Conference #2 (2017) を最後に行っていなかった招待講演を復活させ、NTT研究所の方にお願いをして下記のタイトルで発表をしていただきました。 「スポーツ脳科学プロジェクトの女子ソフトボール日本代表との取り組み "秘密兵器" の誕生秘話」 招待講演は NTT Tech Conference #1 (2017) 、#2 で行っていました。 しかし、 #1 では講演をお願いをした方がここでは書けないようなお話をされ、会場は大いに盛り上がったものの、後ろでスタッフが慌てるという事態になりました(その後、講演してくれた方は怒られたそうですが)。 そして #2 では事故(?)を起こさないよう話をしてくれる方にお願いをしたのですが、参加者と発表内容が少々ミスマッチだったと、イベント終了後のアンケートでわかりました。そこで、イベントにマッチしない招待講演を避けるため、以降は招待講演を行ってきませんでした。 ただ、今回のイベントを行うにあたりスタッフミーティングの中で、いつもとはちょっと違うなにか面白い発表が欲しいという話になりました。 その時たまたま私が以前ニュースで見た NTT 研究所の取り組みを思い出し、エンジニアコミュニティのツテを使って関係者にコンタクトをとり、今回の発表につなげることが出来ました。 発表では、ソフトボール日本代表選手が無意識のうちに相手投手のフォームから球種を予測していることを突き止め、ピッチャーのフォームと球筋を再現するしくみを作り、それを選手たちの練習に使ってもらうまでのお話をしていただきました。 残念ながら発表者の意向で当日の発表動画の公開は行っていませんが、取り組みを紹介する記事はありますので、興味がある方はぜひご覧いただければと思います。 女子ソフトボール × スポーツ脳科学 | NTT 技術ジャーナル 3. イベントページ 先にも紹介した NTT Tech Conference 2022 イベントページ ですが、 こちらはスタッフがデザインから実装までを行い、GitHub Pages を使って公開をしています。 イベントページを変更する際は以下のような流れで GitHub Actions による CI/CD を組み合わせながら作業しました。 変更のブランチを GitHub に Push Pull Request を作成 GitHub Actions が起動 textlint による文章のチェック Firebase にレビュー用のページをデプロイ デプロイしたレビュー用ページの URL を Pull Request に投稿 2 人以上の Approve でマージ可能に main にマージ後、GitHub Pages に変更を反映 第 1 回目からイベントページの作成は自動化も含め上記のような流れで行ってきており、 変更自体は比較的簡単に行えるのですが、レビューについてはスタッフの空き時間確保が必要なため、どうしても時間がかかります。 そこで、急ぎの場合は NTT グループのエンジニアコミュニティの面々にもレビューを手伝ってもらいました。これにより速やかに変更を反映できたので、非常に助かりました。 4. miro を使った交流 NTT Tech Conference ではイベントの目的にエンジニアの交流を挙げていて、オフラインで開催していた頃は廊下での雑談や、展示会場、懇親会などを用意していました。 しかしながら、前回からオンラインでの開催となり、こうしたちょっとした場での交流というのが難しくなったと感じています。 前回は Ask the Speaker という発表者に直接質問や話をできる場を用意したのですが、まったく質問に来る人がいませんでした。 この状況に対し、「オンラインのカンファレンスで誰がいるかわからない場に飛び込んで質問をするのは敷居が高いのではないか?」という話がスタッフからでたため、今回はゆるく技術交流のきっかけになるような方法としてオンラインホワイトボードサービスの miro を採用しました。 miro には予めセッションごとの枠を作り、そこに参加者の方は付箋に書いたメッセージを通じて質問やコメントなどを残し、発表者の方には質問への回答をお願いするという非同期コミュニケーションの形をとりました。 上の画像は当日いただいた質問やコメントですが、それなりの数のコメントや質問をいただき、発表者からの回答もいただけているのでひとまず良かったかなと思っています。それでも、次回のイベントではもっと参加されるエンジニアの方々と交流できるしくみを今から考えていくつもりです。 まとめ 今回は NTT Tech Conference 2022 のご紹介と、その裏側について少しだけご紹介しました。 イベント本編だけでは伝わらないスタッフの工夫や苦労を少し知っていただき、次回以降のイベントをちょっと違った視点で楽しんでいただけたらと思います。 また、このようなイベントを開催したいなと考えている方に少しでも参考になれば幸いです。 次回の NTT Tech Conference は来年に開催する予定ですので、ご都合が合えばぜひご参加ください!
アバター
こんにちは。マネージド&セキュリティサービス部セキュリティサービス部門の閏間です。総合リスクマネジメントサービス「 WideAngle 」の新サービスの企画を担当しています。 本記事では、私がセキュリティの知識・技術向上のために業務外で取り組んでいるバグバウンティプログラムについて、3回にわたって紹介します。 本記事により、バグバウンティプログラムの有効性と、脆弱性探しのおもしろさの両方を伝えられれば幸いです。 (前編)バグバウンティプログラムの有効性について (中編)脆弱性探しの魅力と調査方法について (後編)実際に発見した脆弱性の詳細について【本記事】 なお、バグバウンティに関する記事としては、 NTT Com社内バグバウンティのご紹介 もありますので、ぜひそちらもご覧ください。 脆弱性の実例:3つの問題が重なってXSS(Cross Site Scripting)が発生 本記事では、私が過去に発見した脆弱性を1つ、技術的な詳細も含めてご紹介します(公開についてはアプリケーションの開発元に承諾を得ています)。 あるWebアプリケーションに、ユーザーのプロフィール画像を設定するという機能がありました。ここに以下の3つの問題があり、これらの問題が重なってXSSの脆弱性が発生していました。 プロフィール画像のURLに直接アクセスすると、画像ファイルがダウンロードされるのではなく、直接Webブラウザー上で表示される。 プロフィール画像を設定する際に指定した Content-Type の値が、プロフィール画像にアクセスした際のレスポンスにそのまま設定される。 プロフィール画像を設定する際、画像以外のデータを設定でき、さらに、XSSフィルタ機能はあるものの、回避する手段がある。 XSSとは、Webページにスクリプトなどを埋め込むことができてしまう脆弱性です。悪用されるとWebページを閲覧しているユーザーの情報漏洩などにつながることもある、危険な脆弱性です。 1について Webサイトにアクセスしたとき、Webサーバーから送り返されたコンテンツがWebブラウザー上で表示されるか、それともダウンロードされてファイルとして保存されるかは、Webサーバーからのレスポンスに含まれる Content-Disposition ヘッダで決まります。以下のように attachment という値が付いていれば、Webブラウザーはコンテンツをファイルとして保存しようとしますが、付いていなければWebブラウザー上に表示します。 Content-Disposition: attachment 当該アプリケーションでは、プロフィール画像のURLに直接アクセスすると、レスポンスの Content-Disposition ヘッダに attachment は付いておらず、Webブラウザー上に画像が表示されました。ここで私は、「もしプロフィール画像として< script >タグを含むHTMLを保存でき、かつ、そのコンテンツがHTMLであるとWebブラウザーに認識させられれば、XSSを起こせるな」と考えました。 2について 画像ファイルにアクセスした場合、そのレスポンスの Content-Type ヘッダには、画像コンテンツのファイル形式を示す値が設定される挙動が正しい動作です。例えば、jpeg画像の場合は以下のようになることが期待されます。 Content-Type: image/jpeg しかし当該アプリケーションでは、プロフィール画像を設定する際に自動的に設定される Content-Type の値をローカルプロキシーツールで書き換えると、その後その画像ファイルにアクセスした際のレスポンスに書き換え後の値がそのまま使用されるという動きをしていました。プロフィール画像として設定するデータの中身が実際には何であっても、です。例えば、プロフィール画像の設定時に Content-Type の値を text/html に書き換えると、画像ファイルのURLにアクセスした際、 Content-Type の値が text/html であるレスポンスが返ってきていました。 これで、「1について」に書いた「そのコンテンツがHTMLであるとWebブラウザーに認識させられれば」という条件を満たせることになります。残る壁は、いかにして「< script >タグを含むHTMLを保存するか」です。 3について プロフィール画像を設定する際、データとして単純に <script>alert("XSS!!")</script> のような文字列を渡しただけでは、エラーとなってしまって< script >タグを埋め込むことはできませんでした。どうやら、 < や > があったらエラーにするというXSSフィルタ機能がWebサーバー側に備わっていたようです。 そこでこのXSSフィルタを突破すべく、以下の文字列をWebサーバーに渡しました。 +/v8-+ADw-script+AD4-alert("XSS!!")+ADw-/script+AD4- これはエラーにならずに、プロフィール画像のデータとして保存されました。 そしてプロフィール画像のURLにアクセスすると、< script >タグが動作し、「XSS!!」と書かれたダイアログが表示されました。これでXSSの成立です。 ここで、なぜ< script >タグが動作したが疑問に思われた方もおられると思います。その原理は以下の通りです。 このXSSはInternet Explorer 11でのみ発動するのですが、以下のような流れでXSSが発動していました。 +/v8 は、UTF-7のBOM(Byte Order Mark; 符号化の種類の判別に使用する数バイトのデータ)である。 Internet Explorer 11は、UTF-7のBOMがコンテンツの先頭にあると、エンコーディングがUTF-7であるとみなしてコンテンツを表示する。 エンコーディングがUTF-7の場合、 +ADw- 、 +AD4- はそれぞれ < 、 > を表すので、 +/v8-+ADw-script+AD4-alert("XSS!!")+ADw-/script+AD4- は <script>alert("XSS!!")</script> と解釈され、スクリプトが実行される。 ※XSSフィルタの突破方法については、 Browser's XSS Filter Bypass Cheat Sheet を参考にさせていただきました。 本事例で紹介した脆弱性の要因はそれほど一般的なものではないため、作りこみ防止や検出方法はパターン化されていません。そのため、セキュア開発ライフサイクル活動や脆弱性診断をどんなに丁寧に行っても、このような脆弱性が運用フェーズに流出するリスクは残ると思います。そのようなリスクを少しでも0に近づけるために、バグバウンティプログラムは有効であると考えています。もちろん、脆弱性が運用フェーズに流出してしまったあとの検出手段としても、バグバウンティプログラムは有効です。 まとめ バグバウンティプログラムの有効性と脆弱性探しの魅力について、3回にわたってご紹介しました。 ソフトウェアやシステムの開発・運用に携わる人の中には、セキュリティを専門に扱っていなくとも、脆弱性を発見できるだけの知識やスキルを持った方は大勢いると思います。そのような人たちも巻き込んでバグバウンティプログラムが発展を続け、世の中のシステム全体がよりセキュアな方向に向かえば素晴らしいことだと思います。 その一方で、ソフトウェアの脆弱性はセキュリティインシデントの原因の一部に過ぎないので、たとえ脆弱性がなくなっても、それだけでセキュリティに関するリスクが完全になくなるということはありません。ソフトウェアの脆弱性とは関係のない、人間の不注意を突くような攻撃や、組織内部の人物が行う不正行為によっても被害は発生し得るからです。 そのような事態に備え、防御、検知、対応、復旧といった活動をスムーズに行える体制づくりとその運用は、ソフトウェアの安全性がどんなに高まったとしても重要であり続けると考えています。
アバター