<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by 17 Media Technology on Medium]]></title>
        <description><![CDATA[Stories by 17 Media Technology on Medium]]></description>
        <link>https://medium.com/@17media.blog?source=rss-9e124a81fcdb------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/2*KXMatLicZlgNsGQl0qSjng.png</url>
            <title>Stories by 17 Media Technology on Medium</title>
            <link>https://medium.com/@17media.blog?source=rss-9e124a81fcdb------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 01:45:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@17media.blog/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[在 17 Media 擔任 SRE 的所見及所聞]]></title>
            <link>https://medium.com/17live-tech/what-i-see-and-hear-as-an-sre-at-17-media-315c97bca8e?source=rss-9e124a81fcdb------2</link>
            <guid isPermaLink="false">https://medium.com/p/315c97bca8e</guid>
            <category><![CDATA[backend]]></category>
            <category><![CDATA[17-media]]></category>
            <category><![CDATA[site-reliability-engineer]]></category>
            <category><![CDATA[work-experience]]></category>
            <category><![CDATA[system-migration]]></category>
            <dc:creator><![CDATA[17 Media Technology]]></dc:creator>
            <pubDate>Wed, 22 Jan 2020 03:57:30 GMT</pubDate>
            <atom:updated>2020-01-22T03:57:30.478Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*12norWVRadQplY4uNOsDbg.png" /><figcaption>圖為當時 17 Media 工程團隊進行 migration 時的留影</figcaption></figure><h3>前言</h3><p>大家好，我是 17 Media 的 SRE Rick，進入 17 Media 已過兩年半，希望能藉這篇文章紀錄在這間公司的心得及成長。進入 17 Media 之前，工作內容以維運實體機房上的服務為主，包括設備採購、安裝、維護及故障排除，也撰寫 Perl/Bash 解決各種使用場景的 script。那時正是 Docker 嶄露頭角，DevOps 一詞也越來越多人討論的時間點，因此閒暇之餘，便會查看一些技術文章做些 lab 練習，同時追蹤一些產業新知，漸漸地對於 DevOps 相關的內容越感興趣，在 2016 年時，Google 發布了 <a href="https://landing.google.com/sre/sre-book/toc/">SRE Book</a>，內容詳述 Google 內部如何去維運龐大的伺服器集群，並提出實現 DevOps 精神的方法論。當時正巧看見 17 Media 正在招募 SRE 工程師，因此便嘗試投遞履歷，也很幸運地加入了 17 Media。</p><p>雖然稍微讀過 DevOps/SRE 的相關文章，大致上瞭解它們的意涵，但在未實際參與及投入的狀況下感覺並不深，直到加入後才慢慢體會其有趣之處及挑戰。與上一間公司的工作內容相比，有許多差異的地方，17 Media 是以手機 app 為主，backend 與 client 間透過 RESTful 的方式溝通，基本上是 web-based 的服務，從非 web-based 轉換到 web-based 這是第一點落差。而 backend 的架構又包括了三種主要資料庫及支撐 application 的各種服務，這些服務全部運行在 AWS 上（於 2017/5 時）完全的 <a href="https://about.gitlab.com/cloud-native/">Cloud Native</a>，這是第二點落差。最後一點則是從較為傳統的 IT 公司轉到新創公司，從封閉到開放、從穩定到彈性、從緩慢到迅速，我認為這兩者的差異並無所謂優劣之分，而是面對不同的商業場景而發展出的不同文化差異之處。</p><h3>簡述 backend 架構</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*srCH8huQVph2XR-s" /></figure><p>在經歷過遷移（於後面的段落補充）之後，我們 backend 服務從 AWS ECS 搬到了 GCP GKE，目前的架構大致如下，</p><ul><li><strong>Route 53（DNS）</strong><br>我們的 DNS 基本上是託管在 AWS Route 53 上，並未隨著主要服務進行 migrate，考慮到 AWS Route 53 功能及穩定性後，並無 migrate 的必要。使用者在經過 AWS 的 GeoDNS 的判斷後，我們會依照不同的地區給予不同的 api endpoint，為的是服務的穩定性。</li><li><strong>Wansu/Qiniu（streaming CDN）</strong><br>Wansu/Qiniu 是我們負責視訊流的第三方服務，app 發出 request 給 backend 後經過一些邏輯後會得到推/拉流網址，app 在得到推/拉流網址後便會向 Wansu/Qiniu 的 PoP（point of presence）進行連線，視訊流本身並不會經過我們的 backend。</li><li><strong>Akamai（static file CDN）</strong><br>靜態網頁及圖檔則是透過 Akamai 與 AWS S3 提供給使用者。</li><li><strong>PubNub（message CDN）</strong><br>PubNub 用於我們 app 內的訊息及一些事件廣播的服務。</li><li><strong>Google Cloud（GCP, cloud provider）</strong><br>我們的主要服務都隨著遷移進入到 GCP，所有微服務基本上都運行在 GKE 上，搭配 etcd 做為我們的 config service，GCP <a href="https://cloud.google.com/pubsub/">PubSub</a> 則作為我們的 job queue。</li><li><strong>Fluentd &amp; ElasticSearch &amp; Kibana（log）</strong><br>Log system 則是 EFK 的架構，之所以用 Fluentd 而不是 Logstash 是因為在我們的使用場景上的效能考量。</li><li><strong>MySQL（DB）</strong><br>MySQL 用作儲存所有跟金流相關資料的資料庫，目前的配置是透過 ProxySQL 去連線到 1 個 master 及 6 個 slave，並不是所有的 slave 都會拿來服務 application 的流量，其中有部分節點是作為報表系統所有，但也能在緊急情況時去分擔過大的流量。</li><li><strong>MongoDB（DB）</strong><br>MongoDB 儲存 user 相關資料以及在一些 NoSQL 相較於 relation DB 更易用的場景，目前託管在 MongoDB Atlas，包含了 6 個 shard 及各 3 個 replica set。</li><li><strong>Redis（DB）</strong><br>Redis 目前託管在 RedisLabs，主要作為 cache 使用，同時也儲存部分效能敏感的服務所使用到的資料來加速 api 的回應。</li><li><strong>GCP Kubernetes Engine, GKE（application）</strong><br>最後則是 backend 服務主要運行的環境 — GKE。以 revprox 作為我們對外的入口，在更早的環境中我們是以 Node.js 運行後端服務，但考慮效能及後續維護等問題後我們逐步以 Go 淘汰 Node.js，也因此 revprox 負責判斷 request 並導到正確的地方，而在完成階段性任務後，revprox 以此為基礎上加入了 rate limiter 的功能，繼續為整體服務扮演單一入口及控制流量的角色。goapi 則是我們主要運行的微服務，主要以單體的方式運作，正逐步拆解成更獨立的微服務。如觀看直播的服務已分成另一個服務 golives，金流則放在 gotrade 上。以上就是我們服務基本上的架構，但隨著業務的成長也會時不時增減，其他的服務就暫不一一帶過。</li></ul><h3>難得的 migration 經驗</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/850/0*2PRrbuOewg06BLcD" /></figure><p>隨著公司規模逐漸擴大，業務面的需求也慢慢多樣化，我們於 2017 年四月開始討論將現有的服務從原本的 AWS 遷移到 Google Cloud Platform（GCP）的可能性，AWS 並不是不好用，主要是 SRE 在功能在整合起來是較為耗費心力，舉例來說，在管理 Docker 上， AWS 並不是那麼便利。所以在評估 migration 的方面，我們著重在四個要點</p><ol><li>新的雲端可以提供更好的 Docker 支援</li><li>新的雲端可以降低現有的成本</li><li>易於整合的資料分析工具</li><li>主機離使用者更近的連線</li></ol><p>第一是 GCP 上的 <a href="https://cloud.google.com/kubernetes-engine">GKE</a> 支援程度，<a href="https://kubernetes.io/">Kubernetes</a> 原本是 Google 內部使用的 <a href="https://kubernetes.io/blog/2015/04/borg-predecessor-to-kubernetes/">Borg</a> ，於 2015 年交給 Cloud Native Computing Foundation（CNCF）託管，也因此對於當時正在使用 AWS Elastic Container Service（<a href="https://aws.amazon.com/tw/ecs/">ECS</a>）的我們，GKE 成為我們所感興趣及躍躍欲試的新工具，而由於業務特性的關係，我們的尖峰流量與非尖峰流量落差極大，透過 Kubernetes 上便利的自動擴容機制：<a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">HPA</a> 給了我們極大的幫助。HPA 幫助我們在使用者突然變多、產品使用量上升導致 CPU 使用率超過一定值，例如 60% 時，來擴展容器使服務持續運行，不會因為人過多而使服務品質下降。除此之外，由於先天上設計的差異，Kubernetes 在部署新版服務時從原本的 30~40 分鐘左右縮短至 6~10 分鐘，減少 rolling state（新舊版本交替之間）造成的不便，更重要的是 Kubernetes 對 <a href="https://martinfowler.com/bliki/CanaryRelease.html">canary release</a> 有更好的支援。</p><p>第二點是由於 GCP 在雲端服務提供商的市場上屬於追趕者，因此提供了更好的價格，對於我們有相當程度的吸引力。</p><p>第三點，當時我們的數據分析工作流程已使用 GCP 上的一些服務，例如 <a href="https://cloud.google.com/bigquery/">BigQuery</a> 及 <a href="https://cloud.google.com/dataflow/">Dataflow</a> 所建立起來的，在跨雲的使用上並無遇到問題，但考量在能在同一個平台上整合就可以省去權限管控上相當多瑣碎的設定，也是一個評估的重點。</p><p>最後一點，當時只有 GCP 在台灣擁有機房，對於當時主要市場在台灣的我們，能夠提供更快速的網路反應，在以毫秒來計算 api 回應速度的世界裡，不跨海的連線基本上就省去了 100 毫秒的時間。我們會這樣重視服務提供商與區域之間連線所造成的延遲，可以參考這篇文章《<a href="https://medium.com/@sachinkagarwal/public-cloud-inter-region-network-latency-as-heat-maps-134e22a5ff19">Public Cloud Inter-region Network Latency as Heat-maps</a>》。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/837/1*6S7fMEmFTTh2AJrb6lE3bQ.png" /><figcaption>當日凌晨 migration 時程表</figcaption></figure><p>由於大部分參與的成員都未經歷過這樣大型的搬遷服務，因此遇到的困難自然也不少，從最早 2017 年四月的發想，到 2017 年十月將預備環境搬進 GCP 的 GKE 中，最後於 2018 年五月完成正式環境的遷移，整整耗時了一年左右。我們並不是一整年都將時間花在遷移這項工作，從前期的會議及各項評估，到後來的撰寫執行計畫及演練，我們花費了相當多精神去達到各 module 盡可能的在 30 分鐘內完成，資料庫連線的中斷不超過 2 分鐘（最快的 Redis 僅在 10 秒內），而由於執行時間皆是在離峰時間，使用者基本上是完全沒有任何感覺，業務面影響的程度極小化。關於我們如何進行 migration，有興趣的讀者可以參考我們架構師 Roy 大大於 2018 DevOpsDays 上所講的 keynote，<a href="https://s.itho.me/devopsdays/2018/0911tracka-2.pdf">Cloud migration：Why ? How ? What happened ?</a>以及當時簡報的<a href="https://hackmd.io/c/DevOpsDays2018/%2F7dgRr29KQx-Jk_kGE4qnjg">共筆</a>，其中我們評估了 plan A 一次性的 downtime 遷移及 plan B 多次的 online migration，plan A 乾淨簡單，一天就能搞定，但伴隨著的是需要有服務中斷時間，雖然在 17 Media 並沒有絕對不能使服務中斷，只要工程團隊提出合理的評估及計畫，管理階層都是可以被說服的。這點很好的體現了我們是尊重專業的公司，不會有外行領導到內行的現象，但身為工程師的尊嚴是，如果可以有更好的方案，何必選擇一個雖然省事但暴力的做法，況且對於 GCP 的使用體驗當時我們是不可知的，所謂的不可知，是指當我們真正把正式流量運作在 GCP 上，會發生什麼事呢？我們審慎樂觀的看待這件事，因此我們選了一條可能比較辛苦但卻相對有信心且能在有任何萬一發生時，對公司最不造成影響的 plan B，當時 Roy 給了一個比喻，今天當你在過河的時候不會一口氣就往對面衝過去，而是在每次踏下一步的時候都確認石頭是穩固的，才會踏出下一步，也因此 plan B 成為了我們最後的決定，並且我們也進行了十次左右的演習。</p><p>於遷移任務中，我負責的項目是 Redis、container（GKE）、DNS 的切換，也是整份遷移計畫的最後一步，由於我們將 Redis 做為 cache 服務使用，自然會需要將服務與資料庫靠得夠近以獲得較低的延時，曾經我們測試過在不同的雲之間讓 container 跟 Redis 連線，但測試出來的結果並不可用，因此我們便捨棄分次遷移的做法，改為要一次到位。至於 DNS 則是很直觀的，由於服務的 endpoint 更改，因此需要更動，但技術上的細節是，我們除了服務對外的 load balancer 在前面多加了一組存在於 GCP 上的 HAProxy 讓使用者會先進入到 HAProxy 後，透過 Google 的 backbone 連線到海外後再進入運行在 AWS 上的服務，之所以會有這組 HAProxy 存在的原因是，在 2017 年四月左右的<a href="https://www.hinet.net/notifyPage.html?id=15b933a38dd000005b54&amp;type=0">中華電信海纜故障事件</a>，在那段時間一到晚上對海外的連線便會有很高的延遲，而正巧我們的用戶的活動時間大多集中在晚上因此影響甚大，當時某位前輩便靈機一動，透過額外建立的 HAProxy 利用 Google 服務的特性避免跟大家擠在同一條海纜上，讓我們得以緩解此問題。</p><p>這三個 module（Redis, container, DNS）本身並不算複雜，尤其 Redis 是託管在 RedisLabs，也因此主要的資料搬遷會是由 RedisLabs 的人幫我們負責處理，但這同時是優點也是缺點，優點是我們並不需實際設立環境及操作，資料的還原也會是 RedisLabs 的人幫我們處理，但缺點是對我們而言，RedisLabs 就是個黑盒子，我們無法得知他即時運作的狀態或是能在意外發生時第一時間做出反應，為此，我們與 RedisLabs 前後開了數次會議，ticket 的長度也是前所未有的長，不斷地確認計畫細節及評估，最終我們資料不僅是備份到 GCP 上的 DB ，更要求在 AWS 上也新增備用 DB 來同時備份，以防一有狀況時還能有退路讓我們 rollback 回到原本的狀態，即便是部分資料的丟失，也能保證服務能持續運作。</p><p>在前置事項準備就緒後，我們終於開始進行整體演練，模擬整個服務的 migration，而演練本身是枯燥且令人感到挫折的，第一次的演練甚至在開頭 30 分鐘內就宣告終止，起初幾次演練就算順利完成也無法將時間壓在目標內，大家不斷來回修改擠出更多時間，相關 script 也逐漸從半人工轉化成全自動，協助執行遷移的 co-pilot 都僅需要確認狀態如預期後便能不斷按 Y 繼續下一步，終於能得到令人滿意的時間，而最後遷移也如同大家所知的順利完成。在這幾行的段落中我無法詳盡記錄下所有的細節，若有興趣也歡迎與我們聊聊。</p><h3>克服的難點</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/730/0*ldP2XIyT-KcImKrF" /></figure><p>前段所提的遷移是一次性的大任務，日常的 SRE 工作當然不會是～一言不合就 migration～，諸如每天（除假日前）的切版上 production，到回應各種外部需求，開權限、調整架構、與廠商溝通、將重複的事自動化，其中也包括了 on-call。而說到 on-call 必然就會談到 outage，最好的學習便是能不斷地對自己提出問題然後找出解答或者遇上不得不找出解答的問題，例如每次 on-call 遇到的 outage，透過每次追 outage 的過程，能夠更對該 module 更加深入了解：QPS 不等價、page fault 讓 MongoDB 非常忙碌、Redis 的 eviction 雖然是合理的現象但需要被控制、SSD 的 <a href="https://zh.wikipedia.org/wiki/IOPS">IOPS</a>、資料庫類的服務並不適合用 container 來運作、每個監控 metrics 的因果關係、CPU 的使用率也包含了 waiting 的時間、GKE 的 DNS bug 少了結尾的 . 會讓 kube-dns 過度忙碌。有些限制或原理可能在求學時已經有個概念，但直到每次遇上狀況就又能建立更深的認知成為反射動作。</p><p>Hope is not a strategy，這是 Google 的 <a href="https://landing.google.com/sre/sre-book/chapters/introduction/">SRE Book</a> 開章第一句話，我們不能去期待系統能自己運作的完美無缺，而 outage 也不會平白無故的解決，然而的確在某些情況下，在有限的知識範圍內我們對根因並不可知，但總歸來說，在 outage 是不可避免的前提下，我們要如何慢慢地建立一套有系統且可靠的制度及基礎建設，自然得依靠我們對 outage 更深一層的認識，而這樣認識的過程，則是我們每次產出的 postmortem。</p><p>Postmortem 的中文直譯是「驗屍報告」，其中最大的意涵是我們從這次事件學習到了什麼，我們撰寫 postmortem 是有其固定的格式，包括「摘要」、「事件時間表」、「根因分析」、「改進事項」，而在這之中「改進事項」是最為重要的一環，我們可以很淺薄的調整資源，增加成本來去避免這樣的問題，這只解了表面的問題；或是追根究底地找到程式運行時發生的 bug，甚至是改進底層用到的 package，根本的改進系統。在日常的工作內容中，上述兩者都是有可能發生的，端看當時的我們手上有多少資源可以運用，新創公司在做某些決定或發展時，往往時間比金錢來的珍貴，這是在過去公司所沒有體驗到的。</p><h3>結語</h3><p>如開頭所述，至今在 17 Media 作 SRE 已約兩年半，其實感到最有趣的是，這裡的新鮮事永無止境，當你開始對一項工作開始感到了無新意時，永遠有其他東西給你研究。過去花了比較多的時間在基礎設施的維護上，而最近為了實踐 SRE 的精神之一「減少工作重複事項」，我們團隊也以 backend 主要的語言 — Go 開發一些內部的工具，在減少雜事及學習新的語言保持競爭力的同時又能對系統運作的有更深的認識，這是在過去只作維護相關工作時很難接觸到的部分。</p><p>而伴隨著流量慢慢增加，服務也越來越龐大，我們也開始需要嘗試不同的架構去解決當前的問題。</p><p>例如，MySQL 內有不少過大的表，對整體的效能造成負擔，目前已經對它切出不同 <a href="https://dev.mysql.com/doc/refman/5.7/en/partitioning.html">partition</a>，但當時如何切出 partition 是個棘手的問題，這邊我們比較了 Percona 的 <a href="https://www.percona.com/doc/percona-toolkit/LATEST/pt-online-schema-change.html">pt-online-schema-change</a> 以及 GitHub 的 <a href="https://github.com/github/gh-ost">gh-ost</a>，兩套對 MySQL schema 修改的工具，但比較的結果都是會對線上流量造成影響，lock 時間過久以及 binlog 相關的錯誤，最終仍然選擇了以 script 去 migrate 資料，有時簡單反而最有效。</p><p>17 的 Redis 目前仍在 3.2.11，計畫會升級到 Redis 5，我們主要的 Redis 有兩座，用來作為 cache 使用的 RedisDB 在之前升級 Redis 4.0.2 時曾經因為 evict 量過大，同時 client buffer 的行為也在這版有不同行為，造成當 Redis evict 時便會嚴重拉高延時造成 outage，這次我們決定透過改善 backend code 的效能，減少不需要的 Redis 用量。</p><p>最後則是發生在前陣子的 GCP outage 事件，有興趣的讀者請參考<a href="https://medium.com/17media-tech/%E5%BE%9E-gcp-%E6%95%85%E9%9A%9C%E7%9C%8B-17-media-%E5%B7%A5%E7%A8%8B%E5%9C%98%E9%9A%8A%E7%9A%84%E7%81%BD%E9%9B%A3%E6%87%89%E8%AE%8A-51aa5701ba77">從 GCP 故障看 17 Media 工程團隊的災難應變</a>，目前我們規劃了 cross-cloud 的災備方案，能夠在必要時，將流量導到 AWS 的 <a href="https://aws.amazon.com/tw/eks/">EKS</a> cluster，來讓整體的服務的容錯率更高。</p><p>以上，簡略的紀錄在 17 Media SRE 兩年半的所見及所聞，礙於篇幅其實還有些有趣的內容無法完整呈現，也歡迎加入有興趣的人一同討論或加入我們打造世界級的工程團隊。</p><p>Site Reliability Engineer（網站可靠性工程師）：<a href="http://bit.ly/2TKsH7z">http://bit.ly/2TKsH7z</a><br>職缺列表：<a href="http://bit.ly/2qnPLwm">http://bit.ly/2qnPLwm</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=315c97bca8e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/17live-tech/what-i-see-and-hear-as-an-sre-at-17-media-315c97bca8e">在 17 Media 擔任 SRE 的所見及所聞</a> was originally published in <a href="https://medium.com/17live-tech">17LIVE Tech Insight</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[17 Media 資料生命週期概述]]></title>
            <link>https://medium.com/17live-tech/overview-of-17-media-data-life-cycle-51e9d2fa7658?source=rss-9e124a81fcdb------2</link>
            <guid isPermaLink="false">https://medium.com/p/51e9d2fa7658</guid>
            <category><![CDATA[google-cloud-platform]]></category>
            <category><![CDATA[data]]></category>
            <category><![CDATA[17-media]]></category>
            <category><![CDATA[data-analysis]]></category>
            <category><![CDATA[data-engineering]]></category>
            <dc:creator><![CDATA[17 Media Technology]]></dc:creator>
            <pubDate>Thu, 02 Jan 2020 07:01:45 GMT</pubDate>
            <atom:updated>2020-01-20T09:16:43.079Z</atom:updated>
            <content:encoded><![CDATA[<h3>引言</h3><p>過往，企業依賴顧問的經驗來提升經營績效、開拓新市場、或是解決問題。而今天，企業則是透過數據分析來完成上述的所有事情。</p><p>數據分析是指用適當的統計分析方法對收集來的數據進行分析，從中提取對人們有意義的資訊，詳加研究，最後概括總結的過程。</p><p>從字面上看，數據分析本身就是「數據」和「分析」兩塊工作。一方面是採集、加工、整理數據，另一方面是結合數據背景分析數據，從中提取出對業務有幫助的結論。</p><p>那支撐 17 Media 營運的數據分析是怎麼完成的呢？就請大家繼續往下看這篇文介紹的主角：資料團隊。</p><h3>17 Media 的資料是如何收集的</h3><p>首先，簡單介紹一下目前公司本身的系統架構：</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*-RMRxvTupJpsDsfg" /></figure><p>為了讓讀者方便理解，本文會分成三個部分來介紹，分別是 application 本身的資料來源、後端所產生的資料來源、資料庫的資料來源。這些資料就是讓資料團隊變成數據王國。</p><h4>從 app 收集使用者資訊</h4><p>首先，要介紹的第一個部分就是資料來自 application 本身，有三個工具讓我們收集使用者的操作行為。透過 <a href="https://firebase.google.com/">Firebase</a> 追蹤手機 app UI 上的使用紀錄，這在分析使用者的行為數據是非常重要的，為了要做出能打中觀眾的直播內容與產品，故蒐集使用者在 app 的行為來使產品更完善，包含使用者在哪個畫面做了什麼點擊，瀏覽了什麼資料等。另外在開發新功能時，我們也會使用 Firebase 的 custom metrics 功能來紀錄對於該功能有意義的資料點，以作後續分析。</p><p>另一個來源，則透過 <a href="https://www.appsflyer.com/">AppsFlyer</a> 這個平台，與行銷部門做資料交換，目的是要讓行銷部門知道每個廣告渠道的數位行銷成效與指標，例如 CPI 跟 CPA，會這樣做的目的在於行銷部門需要投廣告，透過這些資料知道哪些廣告商的績效是否如他說得好。透過曝光所有廣告成效相關資料，使合作無論對外對內都公平。又因為 AppsFlyer 產生的資料量非常大，且沒有像 Firebase 一樣跟 <a href="https://cloud.google.com/bigquery/">BigQuery</a> 有整合，故透過使用 Fluentd 這個資料蒐集工具做緩衝，不要讓資料一口氣全部進入 BigQuery 中。</p><p>圖下方有個工具叫 <a href="https://onesignal.com/">OneSignal</a> ，是個推播管理工具。主要用以區分用戶，並提供簡單介面讓行銷決定發不同的訊息內容給不同類別的用戶並做訊息管理。在訊息發出後，資料團隊就可以蒐集推播資料，以及推播的時間點等。</p><p>光是從 app 端就有多種資料來源，整理方面就非常地麻煩，17 Media 使用 Google 的 BigQuery 服務，作為所有資料的集散中心。</p><h4>從後端收集資料</h4><p>17 的後端是透過 log 機制紀錄使用者資訊，並將這些資訊推播到 BigQuery 裡面。使用 schema-less log 是因為他對開發者很輕量化，只需要一行代碼就可傳遞所需訊息，而不需額外定義 schema。</p><p>後端發出的 log 透過兩個工具傳遞處理：<a href="https://cloud.google.com/pubsub/">Cloud Pub/Sub</a> 跟 <a href="https://cloud.google.com/dataflow/">Cloud Dataflow</a>。Cloud Pub/Sub 是 GCP 推出的訊息佇列工具，用來暫存傳入的大量訊息以避免下一級工具處理不及。Cloud Dataflow 是個專門用來處理資料的 worker，他讓開發者自定義資料處理邏輯以處理不同類別的資料事件。同時 Cloud Dataflow 是個全代管服務，會自動處理應該要有 worker 數量，不需維運人員擔心。</p><h4>從資料庫收集資料</h4><p>17 目前使用的資料庫包括 MongoDB、MySQL 以及 Redis。其中 MongoDB 負責絕大部分的商業邏輯資料，與交易有關需要確保一致性與完整性的資料存放在 MySQL，Redis 則是負責快取以及少部分需要大量讀取的即時性資料。</p><p>17 App 每天透過使用者產生的後端相關數據約是 2 億多筆，這些資料透過 <a href="https://www.embulk.org/">embulk</a> 框架將大量資料搬移至BigQuery。目前資料庫每小時同步一次，透過 <a href="https://www.digdag.io/">digdag</a> 作為排程伺服器，確保每小時的資料同步如期完成，而不讓資料的時間序列錯誤。這些資料會透過腳本同步至 BigQuery 中的 layer 1 dataset。</p><h3>資料處理</h3><p>如上所述，不同來源的原始資料都會匯集至 BigQuery。這是資料匯集的終點，也是分析的起點。17 Media 的資料分析以資料整理的想法出發，將原始資料一層一層匯總成具商業價值的資料報表。以下介紹各層之間的轉換，以及執行數據分析的手法。</p><h4>layer 1 到 layer 2</h4><p>Layer 1 是 17 的原始資料集散地，收集了不同來源的資料，每個來源的資料格式都不盡相同。這些原始資料會先進行預處理以利後續分析：以來自 Firebase 的事件為例，我們會按照 UI 或是 app 的功能把事件做分類，這些整理好的資料就會存放到 layer 2。由於 layer 1 每張資料表之間沒有交互關係，所以不同 layer 1 資料表到 layer 2 的工作可以平行進行互不干擾。</p><h4>layer 2 到 layer N</h4><p>不同表格之間會有不同的交互作用，例如把使用者資料與他相關的付費記錄組合起來，就可以針對用戶付費行為做後續分析。從 layer 2 到 layer N 之間，需要不同資料來源或是資訊，聚合 (aggregation) 成下一個層級需要的資料。同時也會加入非 BigQuery 來的資料，舉例來說：行銷部門針對各個廣告渠道整理出的試算表，或是節目部門所維護的直播主帳號名單。這些非系統或軟體所產生的資料，與其他數據整合之後，就可以輔助決策組織的下一步，像是前面提到的節目部配合數據，協助直播主產生更多元的直播內容吸引觀眾，如此就能比競爭對手更有效率地拿下市場。</p><p>從前面提供的架構圖中省略了好幾層 layer，原因是中間經過不少的邏輯演算、資料處理，有些資料在某層就產生了人們想要知道的結果，但有些資料則是要經過非常多層的演變才能提供可分析的樣態。</p><p>經過多道程序後完成的資料表，資料格式更適合分析，可以隨時讓分析師或內部人員取用。一般我們會把同性質的資料整合成一張表格，例如 UserBehavior 這張表格包含用戶基本資料、追蹤主播、送禮點數、直播觀看次數、App 功能使用情形。匯總的資料表利於非工程人員透過簡單的操作達到分析目的。</p><p>最後對於業務需求高度相關的資料，團隊會建立 dashboard 讓公司所有人一目了然。每張 dashboard 背後會有一個對應的資料表稱為 view，針對 dashboard 上所想要呈現的資料跟型態做最終的資料整理。</p><h3>資料支撐營運</h3><p>如果只是單純要產生營運資訊，為什麼要刻意用那麼多層資料處理程序呢？其實過程中資料產出物，除了內部自己的團隊要使用，也有兩個主要用途。</p><h4>推薦主播給用戶</h4><p>當一則直播結束時，觀眾會收到名為「猜你喜歡」的推薦，這是一個由用戶的關注紀錄、主播資料、互相追蹤的事件，透過資料分析所產生的推薦。資料團隊會計算每個用戶對每位主播的喜好程度，透過一個綜合了非常多的維度的公式產生分數，再根據這個計算結果去推斷每位用戶可能會喜好的主播，進而推薦給他。這些計算是以 Spark 撰寫，利用其中的推薦模型執行機器學習，將運算得到的結果寫進 Redis。當用戶看完一則直播後，後端服務會從 Redis 拉取該用戶專屬的推薦名單，希望用戶能更快的找到喜歡的主播。</p><p>日前 17 media 在印度及新加坡成立了機器學習團隊，為了讓機器學習團隊更方便取得資料，日後會重整資料提供流程，將這些 data 產品轉變成 api 服務。</p><h4>提供資料給公司營運</h4><p>另外一種會主動提供給其他部門的，就是公司內部營運用的資料，例如點數的購買及消耗紀錄。這些交易紀錄都存在 MySQL 中，但如果直接從 MySQL 中撈取資料，處理上會非常耗時；透過 BigQuery 的服務以及整理過後的資料表，直接讓系統去撈整理好的資料並做呈現。</p><h4>每日報表 v.s. 即時報表</h4><p>以上數據工程所執行的流程是以產生日報表為主，在每日固定時間產生報表給營運團隊。這一類資訊不需要即時呈現，所以每日只執行一次，例如新安裝 app 以及註冊人數，營運團隊會在每日整理出前一天相關的資訊，以作為策略改進方針。</p><p>對於營收相關報表，由於對公司影響較大，營運團隊需要即時數據以作更快的應變。與金流相關的報表會透過另外一個流程產生，此流程下報表與實際數據的時間差約為五分鐘，讓營運團隊第一時間可以應變。</p><h3>資料生命週期範例：功能使用率</h3><p>17 產品團隊平均每週都有新功能上線。為了讓團隊更了解一個功能的使用情形，包括：營運成效如何計算、是否達成部門目標、新功能是否有表現較好等。資料團隊會針對每個功能算出使用率並且視覺化；同時也須克服各種不同的功能有不同的資料格式的問題，及要如何呈現在同一個 dashboard 來比較成效。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/786/0*D-yCwfZhlaZLg3Gn" /><figcaption>功能的資料生命週期示意圖</figcaption></figure><p>上圖是簡化的資料流向圖，在此我們拿兩個功能作為範例：一是遊戲功能、一是聊天功能。當用戶使用該功能時，後端會將使用資訊記載在資料庫中；這些資料會定時同步至位於 BigQuery 的 layer 1。Layer 1 的資料會包含一些無效紀錄，例如遊戲資料欄位中 player_id 或 timestamp 為空值，或是聊天功能中時長小於 1 分鐘的對話時間，這些都會在 layer 1 到 layer 2 的預處理中刪除。目前難以在這個階段判讀出一個結果，所以從 layer 2 到 layer N 的過程中，與不同的資料表交集、匯總計算出目標資料內容，找出不重複的用戶數，以及這個功能的 id，寫進 report_summary 這張資料表中。有了這些資料內容，就可以將各功能不重複的使用次數視覺化成如下的使用率比較圖，從中了解不同功能的被使用率。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*dQ0QjdjeOTMNrCzs" /><figcaption>日均功能使用率比較。由圖上可知第一個功能的使用率有 48.13%。這數據可讓產品經理判斷是否要花更多的心力來加強功能。</figcaption></figure><p>我們也可以在分析的過程中找出單一功能的使用次數是否逐日增加，例如遊戲功能的使用是否有成長的趨勢。我們一樣可按照前面的分析流程，將遊戲功能的資料格式加入時間戳記，利用不同天數作為比較基準進行交集，經過幾層的數據分析所產生的結果寫進 report_summary 中，透過每日定時的時間戳記進行比較，就可以知道此功能是否有逐漸被多數人使用，是否有達到預期的成效。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jqv41elXdw9FPsbnTtg7JA.png" /><figcaption>單從功能與使用人數來看，是呈現上漲的趨勢</figcaption></figure><h3>結語</h3><p>任何從事數據工作的人，尊重數據結果，並分析形成結論。</p><p>數據分析是條企業永遠會持續要走的路，在任何情境下都會有數據分析的需求，從中產生洞見，掘發商機，也許你會想知道怎麼加快壯大自己的數據分析技能組。</p><p>在 17 你可以遇到頂尖好手，加速您上手 data 相關技能，這是一個多元化 app 的壯大旅程，歡迎加入我們！</p><p>職缺列表：<a href="http://bit.ly/2qnPLwm">http://bit.ly/2qnPLwm</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=51e9d2fa7658" width="1" height="1" alt=""><hr><p><a href="https://medium.com/17live-tech/overview-of-17-media-data-life-cycle-51e9d2fa7658">17 Media 資料生命週期概述</a> was originally published in <a href="https://medium.com/17live-tech">17LIVE Tech Insight</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>