TECH PLAY

スマートキャンプ株式会社

スマートキャンプ株式会社 の技術ブログ

226

エンジニアの笹原です。 スマートキャンプ は6/24より 新オフィスに移転 するのですが、引越し日と入居日がずれることになり、6/1~21の3週間はリモートワーク期間となりました。弊社ではこの期間を リモートウィーク と読んでいます。 代表の記事がこちらです。↓↓↓ note.mu 基本の就業時間を守っていれば、3週間入居しているシェアオフィスのほかに北海道支社や実家などで業務を行うことが可能だったので、タイや北海道からリモートワークをしています。 AWS Client VPNが 東京リージョン で利用できるようになり、今回のリモートワークで試してみたので、導入方法やセキュリティグループの設定について書いてみたいと思います! AWS Client VPNとは リモートワーク時の通信要件 通信要件を実現する上での課題: IPアドレスの固定 なぜIPアドレスを固定することができないのか 解決策: プライベートサブネットを使う やってみる 相互認証用の証明書とキーの生成とACMへの登録 AWS Client VPN エンドポイントの作成 サブネットの関連付け 認証ルールの追加 VPN接続をする 終わりに AWS Client VPNとは AWS Client VPNとは、昨年12月にリリースされたクライアントベースのマネージドVPNサービスです。Open VPNベースのVPNクライアントを使用するだけで、AWS上のリソースにアクセスすることが出来ます。 昨年リリースされたときは一部リージョンのみサービスが提供され、東京リージョンでは利用することが出来ませんでしたが、先月ついに東京リージョンでもサービス提供が開始されました!!! リモートワーク時の通信要件 リモートワークで利用する際のVPNの要件として、以下を定めました。 開発環境にインターネットを経由してHTTP/HTTPSアクセスできる 開発・本番環境にインターネットを経由せずにSSHアクセスできる 表にするとこんな感じです。 本番環境 開発環境 http/https - インターネット経由 ssh インターネット経由せず インターネット経由せず SSHは、インターネット経由せず内部ネットワークを利用するため、AWS Client VPNに割り当てるセキュリティグループを適切に設定することで通信路を開けることが出来ます。 HTTP/HTTPSについては、インターネットを経由させたいので、セキュリティグループで通信路を開けることができず、インターネット利用時のIPが固定されることが必要になります。 通信要件を実現する上での課題: IPアドレスの固定 VPNからインターネットを利用する方法は以下のドキュメントに記載されています。 docs.aws.amazon.com ただ、このドキュメント通りに設定を行っても、インターネット利用時のIPアドレスの固定は出来ません。 なぜIPアドレスを固定することができないのか AWS Client VPNの利用を開始して、サブネットを関連付けると、関連付けられたサブネットに、ENIが2個作成されます。 その2個のENIを介して、VPNの利用者は通信を行うことになります。 ただ、この2個のENIは固定されることはなく、AWS Client VPNが定期的に作成し直します。 上記のドキュメントではインターネットゲートウェイが紐付けられたサブネットにAWS Client VPNエンドポイントを関連付けることにしています。 そのため、インターネットにアクセスする際には2個のENIに割り当てられたGIPを利用することになるのですが、先程言ったように、AWS Client VPNのENIは定期的に作成され直すので、GIPも変わってしまうのです。 解決策: プライベートサブネットを使う 解決策自体は単純です。 AWS Client VPNの関連付けを、インターネットゲートウェイが紐付けられているパブリックなサブネットではなく、デフォルトルートが NAT ゲートウェイに設定されているプライベートサブネットにすればいいのです。 NATゲートウェイ自体は作成され直すこともなくEIPも割り当てられるので、IPアドレスが固定されます!! やってみる 相互認証用の証明書とキーの生成とACMへの登録 OpenVPN Easy-RSAのリポジトリをクローンしてきます。 $ git clone https://github.com/OpenVPN/easy-rsa.git $ cd easy-rsa/easyrsa3 新しい PKI 環境、認証機関 (CA) を初期化します。 $ ./easyrsa init-pki $ ./easyrsa build-ca nopass サーバー証明書とキーを生成します。 $ ./easyrsa build-server-full server nopass $ ./easyrsa build-client-full client1.domain.tld nopass サーバー証明書とキーをACMに登録します。 $ aws acm import-certificate --certificate file://pki/issued/server.crt --private-key file://pki/private/server.key --certificate-chain file://pki/ca.crt --region ap-northeast-1 $ aws acm import-certificate --certificate file://pki/issued/client1.domain.tld.crt --private-key file://pki/private/client1.domain.tld.key --certificate-chain file://pki/ca.crt --region ap-northeast-1 AWS Client VPN エンドポイントの作成 https://ap-northeast-1.console.aws.amazon.com/vpc/home?region=ap-northeast-1#CreateClientVpnEndpoint: クライアントIPV4CIDRはVPNを利用するクライアントに割り当てられるIPアドレスです。 接続先のVPCのCIDRと被らないようにします。 サーバー証明書とクライアント証明書は、先程ACMに登録したものを設定します。 DNSサーバについては、指定しなくても利用できます。 サブネットの関連付け 続いてサブネットを関連付けます。 このとき、デフォルトルートが NAT ゲートウェイに設定されているプライベートサブネットを指定するようにしましょう。 認証ルールの追加 最後に認証ルールを追加します。 インターネットを利用するので 0.0.0.0/0 にアクセス許可しましょう。 VPN接続をする いよいよVPN接続してみましょう! クライアントVPNの設定ファイルをコンソールからダウンロードします。 ダウンロードした設定ファイルに、クライアント証明書のパスを追記します。 cert path/to/client1.domain.tld.crt key path/to/client1.domain.tld.key Open VPNのクライアントとして Tunnelblick が利用できるので、設定ファイルを読み込んで接続します! IPアドレスを確認すると、NATゲートウェイのものに変わっているはずです!!! 終わりに 今回は、AWS Client VPNで固定IPを利用する方法を解説しました。 ハードウェアも要らず、初期コストをかなり抑えた状態でVPNを利用し始めることができるのは最高ですね。 リモートワークなどする際はぜひ利用してみてください。
アバター
こんにちは。今週はスマートキャンプ札幌オフィスに出張中の米元です。 北海道は空気がきれいで食べ物も美味しく、湿度が低くてとても過ごしやすいです。 また、オフィスの窓からは大通り公園の街路樹が見えて気持ちよく働けています。 本記事ではそんな素敵な札幌オフィスで約一ヶ月前に行われた新卒エンジニア研修の話をご紹介したいと思います! 経緯 目的 1. SQLを使いこなせるようにしてほしい 2. エンジニアと仕事するうえで必要なことを教えてほしい 研修内容を決める そして、北の大地へ・・・ 座学(70分) 内容 実際に使ったスライドの例 プログラミング研修(90分) 内容 SQL講座(130分) 内容 グループワーク(360分) 内容 成果物 目的 結果 反省会(空港) フィードバック 反省点 今後 経緯 弊社では今年度から新卒採用を行っており、4月に5名の新入社員が入社しました。 全員セールスやコンサルタントといった職種のため、4月中はビジネス職向けの研修を受けていました。 そんな中、ある日人事から私に 「新卒研修の一環としてエンジニア研修をしてほしい」 との依頼がありました。 詳しく聞くと新卒メンバーが5日間の研修合宿を札幌にて行うことになっており、そのうちの2日間をエンジニアの研修にあててほしいとのことでした。 という訳で1泊2日の北海道の札幌オフィスにて新卒エンジニア研修を行うことになりました。 目的 今回の研修では大きく2つの要望がありました。 1. SQLを使いこなせるようにしてほしい 弊社ではBigQueryとRedashを用いて自社サービスのデータ分析基盤を構築しており、エンジニアだけでなくセールスやマーケターなど含めた全社員が利用出来るようになっています。 そのためエンジニアではない職種でもRedashを使って自分で必要なデータを抽出・分析し成果につなげているメンバーが多く、新卒も同様に自分でデータ分析できるようにしてほしいとのことでした。 ※この時点で講師の1人は SQLが得意な瀧川に決まりました 2. エンジニアと仕事するうえで必要なことを教えてほしい 弊社ではプロダクト開発をするうえでエンジニアと他の職種の方々が関わる機会が多く、新卒のメンバーも近いうちにプロダクト開発に関わる事が予想されます。 そのために必要な知識やスキルを身に付けてほしいという要望でしたが、2日間という限られた時間の中で何をすれば最も効果が高いかを考えるのに苦労しました。 その結果、 ビジネス職でかつ開発に関わった事が無いメンバーが分からないこと エンジニア自身がビジネス職の人に知ってもらいたいこと の2点を重点的に学んでもらうことにしました。 エンジニアとしても他の職種の方から「エンジニアは何をやっているか分からない」「魔法使いみたい」「黒い画面でなんかやってる」などなど言われがちなため自分達の仕事内容があまり理解されていない(理解されにくい)実感があり、それをこの機会に解消したいという思いもありました。 研修内容を決める 2人で考えた結果、最終的にSQLと合わせて下記のような内容で行うことに決めました。 エンジニアの思考を知る 自社のサービスがどのように動いているかを知る プログラミングを実際にやってみてエンジニアの仕事内容を体感する 開発をするためにどんな情報が必要なのかを知る SQL基礎・実践 また、準備期間があまり無かったので 資料は最低限にすること ハンズオン形式やグループワーク形式で出来る内容にすること を念頭において準備を進め、研修当日を迎えました。 そして、北の大地へ・・・ 朝8時発の新千歳行きに乗って出発し、11時前に札幌オフィスに到着しました。 若干の眠気を抱えつつ、初めての札幌オフィスの環境の良さに感動しつつ研修スタートです。 札幌オフィス到着直後。広くてきれい!とはしゃいでます 座学(70分) 最初は私から以下の内容を講義形式で話しました。 自社サービスを例にWebサービスがどのように動作しているかの大まかな仕組み、エンジニアの職種、エンジニアの仕事内容、開発の流れ、思考・文化、障害対応などを説明しました。 内容 弊社主力サービスの1つであるBOXILはどうやって動いているか エンジニアの仕事内容 自社のエンジニア組織の体制 開発の流れ 要件定義、設計、開発、テスト、リリースといった大まかな開発の流れを説明 一緒に働くうえで知っておいて欲しいこと 作業依頼の仕方 好きな事/きらいな事 情報共有の文化 技術的負債とは 障害対応について 障害とは 障害のパターン 障害を防ぐためにしていること(監視、テスト、レビューなど) 実際に使ったスライドの例 エンジニアの好き嫌いを独断と偏見で紹介しました。 仕事をする上で困る曖昧な仕様や無駄・バグの原因になるものは避けたいという思考と、良いものは積極的に共有する文化などについて伝えたいという意図で作ってます。 プログラミング研修(90分) 座学の後はプログラミング講座です。 ビジネス職のメンバーに対してプログラミング研修を行うかどうかは正直迷いました。 ただ話だけではエンジニアが何をやっているのか理解するのは難しいと思い、実際にプログラミングして簡単なアプリケーションを作ってもらうところまでやる事にしました。 内容については 日々の業務でSpreadSheetsやGmailなどG Suiteを利用している 何が出来るのか目に見えて実感しやすい 開発環境の用意が必要ない などの理由から今回は GoogleAppScript を学んでもらうことにしました。 内容 プログラミング研修 基礎編 プログラミングとは プログラミング言語とは GoogleAppScriptとは GoogleAppScriptの基本文法 プログラミング研修 実践編 CalendarAPIを使って今日の予定をSlackで通知してみよう 便利ツールを作ってみよう 基礎編では以下のようなコードを書きながら基本的な文法の説明をおこないました。 function myFunction() { var message = "Hello, world!" ; // String(文字列) Logger.log(message) var number = 1; // Integer(整数) var number2 = 2; var total = number + number2; Logger.log( "合計は" + total + "です。" ); var hoge1 = "Hoge" ; var hoge2 = "Huge" ; var isHoge = hoge1 === hoge2; // Boolean(真理値, true|false) Logger.log(hoge1 + "と" + hoge2 + "は" + isHoge + "です。" ); if (hoge1 === hoge2) { Logger.log( "等しいです。" ); } else { Logger.log( "違います。" ); } var message2 = createGreetMessage( "takigawa" ) Logger.log(message2) var total2 = plus(1, 2) Logger.log(total2) } function createGreetMessage(name) { var baseMessage = "Hello, " ; var message = baseMessage + name; return message; } function increment(a) { return a + 1; } function getSchedule(email, date) { return null ; } その後の実践編ではハンズオン形式で手を動かして実際にプログラムを作っていってもらいました。 流石にほとんどのメンバーはプログラミング自体が初めてのため、括弧が無いだけで動作しなかったり if や for の使い方がよく分からず四苦八苦する状態でした。 ですがGoogleCalendarからAPIを通じて当日の予定を取得し、Slackに通知するところまで作ると「おお・・!」という反応に変わり、正しく動いた時は楽しそうでした。 実際に作ったコードは以下のようなものです。 function getMyMail() { var mails = GmailApp.search( 'smartcamp' ) Logger.log(mails [ 0 ] .getFirstMessageSubject()) } function getMyCalendar() { var targetDate = new Date ( "2019/05/07" ) var myCalendar = CalendarApp.getCalendarById( 'xxxxx@smartcamp.asia' ) var schedules = myCalendar.getEventsForDay(targetDate) schedules.forEach( function (schedule) { Logger.log(schedule.getTitle()) Logger.log(Utilities.formatDate(schedule.getStartTime(), 'Asia/Tokyo' , 'YYYY-MM-dd HH:mm' ) + "から始まります!" ) var startDate = Utilities.formatDate(schedule.getStartTime(), 'Asia/Tokyo' , 'YYYY-MM-dd HH:mm' ) var endDate = Utilities.formatDate(schedule.getEndTime(), 'Asia/Tokyo' , 'YYYY-MM-dd HH:mm' ) var message = startDate + "~" + endDate if (include(schedule.getTitle(), '来訪' )) { notify(schedule.getTitle(), message, '#008000' , '予定ボット' , '@takigawa' ) } else { notify(schedule.getTitle(), message, '#cd5c5c' , '予定ボット' , '@takigawa' ) } } ) } function include(message, text) { return message.indexOf(text) !== -1 } function notify(title, message, color, botName, channel) { var postUrl = 'SlackのWebhook URL' var jsonData = { "username" : botName, "icon_emoji" : ':seedling:' , "channel" : channel, "attachments" : [{ "title" : title, "text" : message, "color" : color, "footer" : "Script ID: " + ScriptApp.getScriptId() }] } ; var payload = JSON.stringify(jsonData); var options = { "method" : "post" , "contentType" : "application/json" , "payload" : payload } ; UrlFetchApp.fetch(postUrl, options); } function addDays (date, n) { var date = new Date (date) date.setDate(date.getDate() + n) return date } 最後は2チームに分かれて、私たちでサポートしつつGoogleのAPIを使って以下のような便利ツールを作りました。 GoogleDriveから特定の文字列にマッチする名前のドキュメントをメールに添付して送信する 問い合わせフォームからのメールなど、特定のタイトルのメールを受信したらSlackに通知する 最初はプログラムで何が出来るのかピンときてなかった新卒メンバー達も、実際に業務で使えそうなものを作るとプログラミングで出来ることのイメージがついたようでした。 SQL講座(130分) 瀧川先生によるSQL講座です。 新卒メンバーが自分でデータ分析が出来るようになるのが最終目標ですが、今回はまず自分でクエリを書いて欲しいデータを抽出できるようになるところまでがゴールです。 内容 基礎編 データベースとは SQLとは BigQueryとは 基本的なクエリ、検索条件の指定、JOIN 応用編 リード(見込み客)情報を取得する リードをユーザー属性で分析しよう Redashを使って可視化しよう 私と瀧川の2人でサポートしつつ、基本的なクエリについてはなんとかできましたが、応用編に入るとJOINや検索条件が複雑になったため人によって理解度にばらつきがありました。 Redashで作成したPIVOT TABLE。ほぼマスクしてます 最後にRedashでPIVOT TABLEを作りましたが、これを見て初めて実際に業務に使えるイメージがついたようです。 グループワーク(360分) 最後はチームに分かれてのグループワークを行いました。 内容 新規プロダクトBiscuetのダッシュボードを設計し、発表する チームに分かれてそれぞれで社内のインサイドセールスのメンバーにヒアリングを実施 ヒアリング結果をもとにダッシュボードを設計 エンジニアがその設計をもとに開発を始められるものを作る 成果物 以下の2点としました。 UIのモック 表示データの取得元とそれらの表示条件をまとめた仕様書 目的 目的は下記のとおりです。 何かを開発する際にエンジニアにどのような情報を渡せば足りるのかを知ってもらう システムの事を意識しながら要件をまとめる過程を体験・理解してもらう 結果 合計360分の長時間の内容でしたが、札幌支社のインサイドセールスメンバーへのヒアリングと合間に中間レビューを挟みつつディスカッションをして、最終的に2チームともダッシュボードの設計を作りあげて発表を行いました。 中間レビューでUIの説明中 プログラミングやSQL研修の時とは打って変わって、5人の新卒メンバー全員が物怖じせず堂々とした発表を行いました。 発表後のフィードバックタイム。 ここまでで2日間に渡るエンジニア研修は終了です。 新卒メンバーはこのあともビジネス研修が続きました。私と瀧川だけ先に札幌支社を発ちました。 反省会(空港) 帰りの飛行機の時間が迫っていたため、ダッシュで新千歳空港の回転寿司(函太郎)に駆け込み15分で寿司を食べつつ反省会。 アブラガレイなど初めて食べるネタも多く、美味しかったです 🍣 フィードバック 研修終了後、以下のような項目でアンケートをとりました。 それぞれの講義について理解できたかどうか 研修全体を通してよかった事、逆に改善点 その他感想など 回答の中からいくつかピックアップしてみました グループワークで学んだこと 実際に要件定義〜ヒアリングを通じてのUI設計が経験できこと。思った以上にビジネス目線と開発目線を行き来していく苦悩を感じることができたので、エンジニアの方への質問の仕方を工夫しようと思うことが出来ました。 グループワークでの改善点 ワーク終盤のヒアリングでUI設計を改めて考え直さなければならなくなったが課題を解決するアイデアが生まれなかった。こんなサービスを作ることができれば良いという意見は生まれたが、プロダクト側がアイデアを実現するためにどうやって開発するかの視点が不足していた。 研修全体を通してよかったこと 実際にコードを書く(SQL、プログラミング両方)経験出来た点。思った以上にビジネスサイドとの関わりがあるんだなと感じることが出来た点。 その他感想など コードを書く経験をもう少しして、要件定義までしっかりできるようになる目的で、2日間だけでなく、5日間やって見るのも面白いかなと思いました。 他には時間が足りなかったという意見が多かったです。 反省点 アンケートでの回答にもあったように全体的に時間が足りず、キツめのスケジュールだったのかなとは思いました。 ただ限られた時間の中で、彼らが今後エンジニアと仕事するうえで最低限必要なレベルで「エンジニアが何をしているのか」や「プログラミングとは何か」を理解してもらう事はできたかと思います。 SQLに関してはまだまだ理解が足りていないので、学習機会を増やしたり自分自身で学ぶための情報を整備する事も必要だと感じました。 また、グループワークはエンジニアが仕様を決める時にどのような情報が必要かにフォーカスしたかったのですが、結果的にはユーザーの課題をヒアリングしながら洗い出して要件をまとめるところがメインになってしまい、エンジニア研修というよりは企画に近いものになってしまいました。 研修の目的に沿う形にするには要件は事前に決めておき、 どんなデータを表示するのか そのデータはどこから取得するのか どのような条件で表示するのか などUIやDBの設計を考える内容に絞るべきだったなーと寿司を食べながら反省しました・・・。 今後 今回の研修を通して、他の職種の方達から見た時に自分達が考えている以上にエンジニアの仕事内容がわかりにくい事を改めて実感しました。 弊社ではエンジニアもセールスやマーケティングの勉強をすることが多く、それによって開発依頼の目的や意図が理解しやすくなったりエンジニア側からも提案を出来る機会が増えているのですが、今後は逆にエンジニア自身が何をやっているかを理解してもらう事も必要だと感じました。 異なる職種でも相手が何をやっているかをお互いに理解したうえで業務を進められるようにしていきたいです。 今後は新卒だけでなく他の社員の方にも今回の内容をカスタマイズして提供していきたいと思います!
アバター
今週末から北海道オフィスに出張でワクワクしている瀧川です。 私はデータ分析基盤の構築をする機会がよくあり、FluentdやEmbulk、Digdag、BigQueryを好んで使っています。 構築する際に気をつけることというと、冪等性やログ欠損(リカバリ)などいろいろあるかと思いますが、その中でも重要になるのが 個人情報などの見せられないデータ(機密情報) の扱いかな思っています。 構造化されたデータの個人情報であれば、そもそも分析基盤に転送しないことや、マスキングして送るなど対策は容易 *1 ですが、 例えば「バグでログの可変な箇所に個人情報が入ってしまった」とか「アンケートの集計をしたいが電話番号など入ってしまっていて隠したい」 などの場合、検出や秘匿化はかなり難しいと感じています。 その課題を解決してくれるのが GoogleCloud DLP(Data Loss Prevention) API ではないかなと思っています! 本記事ではGoogleCloud DLP APIをGolangのクライアントを使って、簡単なサンプルを作っていきます。 なかなか機能が多く、とっつきにくいところもあるので、その辺り参考になればと思います! Google Cloud DLP(Data Loss Prevention) とは 使ってみる 準備 認証 機密データ検出(Inspect) 秘匿化(Deidentify) コード全体 最後に Google Cloud DLP(Data Loss Prevention) とは 機密データ保護に有用な DLP API の新機能 | Google Cloud Blog 上記の公式Blogを見ていただくと、わかりやすいアニメーションもあるのでイメージしやすいかと思います。 ざっくりと説明すると、 「あらゆる場所に存在するデータの中から、90種類以上の機密情報を検出し、機密情報に応じた秘匿化を実施できる」 サービスです。 ワークフローへの組み込み 対象のデータはテキストまたは画像で、Cloud Storage、BigQuery、Cloud DatastoreといったGCPのストアに配置するか、直接APIにPOSTすることで実行することができます。 また、リアルタイムでの実行や、PubSubへの通知なども対応しているようです。 検出可能な機密情報 機密情報は、「クレジット カード番号、氏名、社会保障番号、電話番号、認証情報」などがすでに機械学習によってモデル化されており、その中から必要に応じて検出項目を指定することができます。 infoType 検出器リファレンス  |  Data Loss Prevention のドキュメント  |  Google Cloud 柔軟な秘匿化方法 機密情報によっては秘匿化方法をしっかりと考える必要があるかもしれません。 例えば、「携帯電話の番号は先頭3桁だけ残してマスキングしてほしい(@080- - @)」とか「Emailごと集計したいので同一Emailは同一の文字列になるようにしてほしい(仮名化、ハッシュ化)」などなど。 そういったこともCloud DLPであれば実現することができるようです。 使ってみる 早速プログラムから使ってみたいと思います! APIに対して、Node, Golang, Java, Pythonのクライアントライブラリが提供されています。 本記事ではGolangでやっていきます。 APIドキュメントは以下なので適宜参考にしてください。 cloud - GoDoc 準備 事前に以下を準備しておいてください。 Golang depなど依存解決ツール サービスアカウントのJSONキー Cloud DLPへのアクセス権を忘れずに 以下のような1ファイルにつらつら書いていきます! main.go package main func main() { } 認証 まずはサービスを利用するために認証を通す必要があります。 DLPのクライアントにJSONキーを渡しています。 この辺りはGCPのどのサービスでも共通しているお決まりな書き方ですね! 特にエラーがでなければ先に進みましょう。 エラーが出る場合、サービスアカウントの権限やJSONキーが正しいか、依存解決がうまくできているかなど確認してみてください! ( dep ensure や go get などは実行してください) package main import ( "context" "log" dlp "cloud.google.com/go/dlp/apiv2" "google.golang.org/api/option" ) func main() { credJSON := "ここはJSONキーで置き換えてください" ctx := context.Background() cred := option.WithCredentialsJSON([] byte (credJSON)) client, err := dlp.NewClient(ctx, cred) if err != nil { log.Fatal(err) } defer client.Close() } 機密データ検出(Inspect) それでは実際に機密データを検出してみたいと思います! 以下のように呼び出す inspectString 関数を実装していきます。 infoTypes が検出する機密情報の種類で、 message が検出対象ですね。 新しくimportされている dlppb はDLP APIと通信する際のデータ構造定義になります。(Protocol Bufferで定義していますね) こちらがdlppbのドキュメントになります。 dlp - GoDoc 設定データを構築する際には参考にするといいと思います! package main import ( "context" "fmt" "log" dlp "cloud.google.com/go/dlp/apiv2" "google.golang.org/api/option" dlppb "google.golang.org/genproto/googleapis/privacy/dlp/v2" ) func main() { ... projectID := "サービスアカウントを発行したProject ID" infoTypes := [] string { "PHONE_NUMBER" , "EMAIL_ADDRESS" } message := ` TO:hoge@hoge.com こんにちわ、瀧川です。 080-0000-0000 までご連絡ください。 ` inspectString(projectID, client, infoTypes, message) } 検出を実行し、結果を標準出力するコードが以下のようになります。 リクエストのデータ構造は若干難しいですね。 func inspectString(projectID string , client *dlp.Client, infoTypes [] string , input string ) { // 検出機密種別をdlppb.InfoTypeに変換 var i []*dlppb.InfoType for _, it := range infoTypes { i = append (i, &dlppb.InfoType{Name: it}) } // リクエストのデータを構築 req := &dlppb.InspectContentRequest{ Parent: "projects/" + projectID, // 必ずprojects/を前につける InspectConfig: &dlppb.InspectConfig{ // 検出条件など InfoTypes: i, }, Item: &dlppb.ContentItem{ // 検出対象 DataItem: &dlppb.ContentItem_Value{ Value: input, }, }, } resp, err := client.InspectContent(context.Background(), req) // 検出を実行 if err != nil { log.Fatal(err) } // 以下は結果表示のみ resultFormatter := func (inputStr string , result *dlppb.Finding) string { tmp := ` 機密種別: %s もっともらしさ(尤度): %s 範囲: %d ~ %d 検出文字列: %s ` start := result.GetLocation().GetCodepointRange().GetStart() end := result.GetLocation().GetCodepointRange().GetEnd() return fmt.Sprintf(tmp, result.GetInfoType().GetName(), result.GetLikelihood(), start, end, string ([] rune (input)[start:end])) } fmt.Printf( "対象文字列: %s \n " , input) for _, result := range resp.GetResult().GetFindings() { // 検出されたものはFinding fmt.Println(resultFormatter(input, result)) } } 実行すると以下のようになります! 検出できてそうですね! 検出結果は dlppb.Finding というデータになり、機密種別(InfoType)や精度の指標である尤度(Likelihood)、検出された箇所(Location)などが取得できるのがわかるかと思います。 $ go run main.go 対象文字列: TO:hoge@hoge.com こんにちわ、瀧川です。 080-0000-0000 までご連絡ください。 機密種別: EMAIL_ADDRESS もっともらしさ(尤度): LIKELY 範囲: 4 ~ 17 検出文字列: hoge@hoge.com 機密種別: PHONE_NUMBER もっともらしさ(尤度): POSSIBLE 範囲: 31 ~ 44 検出文字列: 080-0000-0000 秘匿化(Deidentify) 検出された機密情報を秘匿化するコードは以下のようになります! (急にデータ構造が複雑になった気がしますね...型名が冗長だったりするだけなのでそこまで複雑ではないですよ!) PHONE_NUMBER は「末尾から9文字をアスタリスクに変換」するようにしています。 EMAIL_ADDRESS は「 abcdefghijklmnop をキーにしてハッシュ化」するようにしています。 func deidentifyString(projectID string , client *dlp.Client, infoTypes [] string , input string ) { var i []*dlppb.InfoType for _, it := range infoTypes { i = append (i, &dlppb.InfoType{Name: it}) } req := &dlppb.DeidentifyContentRequest{ // Deidentifyリクエスト Parent: "projects/" + projectID, InspectConfig: &dlppb.InspectConfig{ InfoTypes: i, }, DeidentifyConfig: &dlppb.DeidentifyConfig{ Transformation: &dlppb.DeidentifyConfig_InfoTypeTransformations{ InfoTypeTransformations: &dlppb.InfoTypeTransformations{ Transformations: []*dlppb.InfoTypeTransformations_InfoTypeTransformation{ // 変換方法の配列 { InfoTypes: []*dlppb.InfoType{&dlppb.InfoType{Name: "PHONE_NUMBER" }}, PrimitiveTransformation: &dlppb.PrimitiveTransformation{ Transformation: &dlppb.PrimitiveTransformation_CharacterMaskConfig{ // 変換方法 CharacterMaskConfig: &dlppb.CharacterMaskConfig{ MaskingCharacter: "*" , NumberToMask: 9 , ReverseOrder: true , }, }, }, }, { InfoTypes: []*dlppb.InfoType{&dlppb.InfoType{Name: "EMAIL_ADDRESS" }}, PrimitiveTransformation: &dlppb.PrimitiveTransformation{ Transformation: &dlppb.PrimitiveTransformation_CryptoHashConfig{ CryptoHashConfig: &dlppb.CryptoHashConfig{ CryptoKey: &dlppb.CryptoKey{ // 変換方法 Source: &dlppb.CryptoKey_Unwrapped{ Unwrapped: &dlppb.UnwrappedCryptoKey{ Key: [] byte ( "abcdefghijklmnop" ), }, }, }, }, }, }, }, }, }, }, }, Item: &dlppb.ContentItem{ DataItem: &dlppb.ContentItem_Value{ Value: input, }, }, } r, err := client.DeidentifyContent(context.Background(), req) // Deidentify実行 if err != nil { log.Fatal(err) } fmt.Println(r.GetItem().GetValue()) } 実行結果はこちら! よさそうですね。 $ go run main.go TO:9ELfD7kXS0HfGr8KjitHndGCWSO8ReSM1l8GwLKqjok= こんにちわ、瀧川です。 080-********* までご連絡ください。 ※ ドキュメントを見ると、 インタフェース名_具象クラス名 のような形でポリモフィックに定義されているようです。(以下が変換方法一覧) type PrimitiveTransformation_BucketingConfig type PrimitiveTransformation_CharacterMaskConfig type PrimitiveTransformation_CryptoDeterministicConfig type PrimitiveTransformation_CryptoHashConfig type PrimitiveTransformation_CryptoReplaceFfxFpeConfig type PrimitiveTransformation_DateShiftConfig type PrimitiveTransformation_FixedSizeBucketingConfig type PrimitiveTransformation_RedactConfig type PrimitiveTransformation_ReplaceConfig type PrimitiveTransformation_ReplaceWithInfoTypeConfig type PrimitiveTransformation_TimePartConfig コード全体 package main import ( "context" "fmt" "log" dlp "cloud.google.com/go/dlp/apiv2" "google.golang.org/api/option" dlppb "google.golang.org/genproto/googleapis/privacy/dlp/v2" ) func main() { credJSON := "JSONキー" ctx := context.Background() cred := option.WithCredentialsJSON([] byte (credJSON)) client, _ := dlp.NewClient(ctx, cred) defer client.Close() infoTypes := [] string { "PHONE_NUMBER" , "EMAIL_ADDRESS" } message := ` TO:hoge@hoge.com こんにちわ、瀧川です。 080-0000-0000 までご連絡ください。 ` inspectString( "プロジェクトID" , client, infoTypes, message) deidentifyString( "プロジェクトID" , client, infoTypes, message) } func inspectString(projectID string , client *dlp.Client, infoTypes [] string , input string ) { var i []*dlppb.InfoType for _, it := range infoTypes { i = append (i, &dlppb.InfoType{Name: it}) } req := &dlppb.InspectContentRequest{ Parent: "projects/" + projectID, InspectConfig: &dlppb.InspectConfig{ InfoTypes: i, }, Item: &dlppb.ContentItem{ DataItem: &dlppb.ContentItem_Value{ Value: input, }, }, } resp, err := client.InspectContent(context.Background(), req) if err != nil { log.Fatal(err) } resultFormatter := func (inputStr string , result *dlppb.Finding) string { tmp := ` 機密種別: %s もっともらしさ(尤度): %s 範囲: %d ~ %d 検出文字列: %s ` start := result.GetLocation().GetCodepointRange().GetStart() end := result.GetLocation().GetCodepointRange().GetEnd() return fmt.Sprintf(tmp, result.GetInfoType().GetName(), result.GetLikelihood(), start, end, string ([] rune (input)[start:end])) } fmt.Printf( "対象文字列: %s \n " , input) for _, result := range resp.GetResult().GetFindings() { fmt.Println(resultFormatter(input, result)) } } func deidentifyString(projectID string , client *dlp.Client, infoTypes [] string , input string ) { var i []*dlppb.InfoType for _, it := range infoTypes { i = append (i, &dlppb.InfoType{Name: it}) } req := &dlppb.DeidentifyContentRequest{ Parent: "projects/" + projectID, InspectConfig: &dlppb.InspectConfig{ InfoTypes: i, }, DeidentifyConfig: &dlppb.DeidentifyConfig{ Transformation: &dlppb.DeidentifyConfig_InfoTypeTransformations{ InfoTypeTransformations: &dlppb.InfoTypeTransformations{ Transformations: []*dlppb.InfoTypeTransformations_InfoTypeTransformation{ { InfoTypes: []*dlppb.InfoType{&dlppb.InfoType{Name: "PHONE_NUMBER" }}, PrimitiveTransformation: &dlppb.PrimitiveTransformation{ Transformation: &dlppb.PrimitiveTransformation_CharacterMaskConfig{ CharacterMaskConfig: &dlppb.CharacterMaskConfig{ MaskingCharacter: "*" , NumberToMask: 9 , ReverseOrder: true , }, }, }, }, { InfoTypes: []*dlppb.InfoType{&dlppb.InfoType{Name: "EMAIL_ADDRESS" }}, PrimitiveTransformation: &dlppb.PrimitiveTransformation{ Transformation: &dlppb.PrimitiveTransformation_CryptoHashConfig{ CryptoHashConfig: &dlppb.CryptoHashConfig{ CryptoKey: &dlppb.CryptoKey{ Source: &dlppb.CryptoKey_Unwrapped{ Unwrapped: &dlppb.UnwrappedCryptoKey{ Key: [] byte ( "abcdefghijklmnop" ), }, }, }, }, }, }, }, }, }, }, }, Item: &dlppb.ContentItem{ DataItem: &dlppb.ContentItem_Value{ Value: input, }, }, } r, err := client.DeidentifyContent(context.Background(), req) if err != nil { log.Fatal(err) } fmt.Println(r.GetItem().GetValue()) } 最後に Google Cloud DLPがどのようなサービスで、なにができるのか伝わりましたでしょうか。 機械学習なので検出は100%とはいきませんが、潜在的なリスクを減らしたり、重大なセキュリティインシデントにすばやく気づくなど今まで見過ごされてきたような場所で活躍すると思います。 また、先日WebConsoleもベータで発表されて今後、さらに使いやすく、信頼性も高まっていくかなとも思います。 Cloud Console での Cloud DLP  |  Data Loss Prevention のドキュメント ぜひ利用してみてください! 近々Cloud DLPでBigQueryのテーブルを秘匿化して出力するちょっと実践に近い記事も書こうと思うのでそちらも見ていただければ嬉しいです! *1 : 一例を別記事に書いてありますので参考にしてください tech.smartcamp.co.jp
アバター
エンジニアの井上です! 今回は私が最近気になっていたAuth0とNuxt を使って簡単な認証機能を作っていきたいと思います。 認証をどのように実装するかは皆さん結構悩まれているかなと思います。 Auth0は様々な既存プロバイダと自由に連係可能かつマルチデバイス対応、多要素認証に対応しているなどのメリットがあり、かつ導入がとても簡単そうなので個人的に注目しています! Auth0とは Nuxtとは Auth moduleとは Nuxt middlewareとは 実際にAuth0で認証作ってみる Nuxt でプロジェクト作成 Auth Moduleを追加する Auth0を設定する 認証ページを作成する ログインしてみる middlewareを使用してログインチェックする 終わりに Auth0とは Auth0 は認証基盤サービス (IDMaaS) です。マルチデバイス対応しOpenID Connect、JWT、OAuthなどのプロトコルも使用可能なのでとても導入しやすいものになっています。 また、Social連携の多さも魅力の1つで基本的に必要なものは全て揃っているかと思います。 そして、多要素認証も対応しており色々試したくなると感じるサービスです。 Nuxtとは Vueフレームワークとして最近かなり有名になっていますね。 私としては設定を気にせず開発に集中できること、SSRが主な強みとして感じています。 他の公式にも挙げられている機能としては下記のようなものがあります! Vue ファイルで記述できること(*.vue) コードを自動的に分割すること サーバーサイドレンダリング 非同期データをハンドリングするパワフルなルーティング 静的ファイルの配信 ES2015+ のトランスパイレーション JS と CSS のバンドル及びミニファイ化 <head> 要素( <title> 、 <meta> など)の管理 開発モードにおけるホットリローディング プリプロセッサ: Sass, Less, Stylus など HTTP/2 push headers ready モジュール構造で拡張できること Auth moduleとは Nuxtで認証を簡単に導入するための公式モジュールです。 auth0の認証でも利用することができます! Nuxt middlewareとは ページやそのページグループがレンダリングされる前に実行されるカスタム関数を定義することができます。 それぞれSPAとユニバーサルモードで動きが異なります。 SPA モード: 最初のリクエスト時、他のルートへ移動したとき ユニバーサルモード: 初期のリクエスト時、ページの再読込み時、他のルートへ移動したとき ミドルウェアは下記の順に実行されます: nuxt.config.js レイアウト ページ このmiddlewareを利用することで簡単にログインチェックを実装できます。 実際にAuth0で認証作ってみる Nuxt でプロジェクト作成 まずはNuxtプロジェクトを作っていきます。 下記コマンドを実行し $ npx create-nuxt-app <project-name> 質問されるので下記のような設定になるよう回答していきます。 ? Project name authTestApp ? Project description My astounding Nuxt.js project ? Use a custom server framework none ? Use a custom UI framework Element UI ? Choose rendering mode Universal ? Use axios module yes ? Use eslint yes ? Use prettier yes ? Author name myname ? Choose a package manager yarn 一通り回答したら、下記のコマンドでNuxt環境を立ち上げてみます。 yarn run dev すると下記のような画面が表示されます。 Auth Moduleを追加する 作ったNuxtアプリケーションに、Auth Moduleを入れていきます。 Auth Moduleの導入作業は2つでyarnでの追加とNuxtのconfigに追加するのみです。 yarn add @nuxtjs/auth 次にnuxt.config.jsにauthを追加します。 //nuxt.config.js modules: [ '@nuxtjs/axios' , '@nuxtjs/auth' , // 追加 ] 続いて同じnuxt.config.jsに認証周りのルーティング設定をしてきます。 コールバックURLはプロバイダからの認証コールバックに使用します。 //nuxt.config.js auth: { redirect: { login: '/' , // 未ログイン時のリダイレクト先 logout: '/logout' , // ログアウト処理を実行した直後のリダイレクト先 callback: '/callback' , // コールバックURL home: '/mypage' , // ログイン後に遷移するページ } , } Auth0を設定する 続いて Auth0の設定を追加してきます。 手順としては、Auth0はアカウントを作成すると認証用のドメインやキーが発行されるので、それをauth moduleへ追加していきます。 Auth0でアカウントを発行すると下記の画面でDomainとClient IDが表示されます。 こちらをnuxt.config.jsに追記していきます。 auth0 そして、同じ画面の下の方に Allowed Callback URLs という項目に下記のように先程設定したコールバックURLを設定します。 callback 設定は先ほどnuxt.config.jsに追加したauth部分に下記のよう追加するのみです。 //nuxt.config.js auth: { strategies: { auth0: { domain: 'Domain' , // 追加 client_id: 'Client ID' // 追加 } } , redirect: { login: '/login' , // 未ログイン時のリダイレクト先 logout: '/logout' , // ログアウト処理を実行した直後のリダイレクト先 callback: '/callback' , // コールバックURL home: '/mypage' , // ログイン後に遷移するページ } , } , なんと、これでAuth0の設定全て終わりです! 認証ページを作成する 続いて認証ページを追加していきます。 Nuxtではpageフォルダ配下にvueファイルをおくと、ファイル名に合わせてルーティングを自動的に設定してくれます。 まずは、全体のレイアウトから先に作っていきます。 // layout/deault.vue <template> <div> <div class = "line" ></div> <el-menu mode= "horizontal" background-color= "#545c64" text-color= "#fff" active-text-color= "#ffd04b" > <el-menu-item><nuxt-link to= "/" >Home</nuxt-link></el-menu-item> <el-menu-item><nuxt-link to= "/mypage" >Mypage</nuxt-link></el-menu-item> <el-menu-item><nuxt-link to= "/login" >Login</nuxt-link></el-menu-item> </el-menu> <el-card> <el-container fluid fill-height> <nuxt/> </el-container> </el-card> </div> </template> 続いてログインページですloginWithAuthZeroでauth0を使用してログイン処理するようにしています。 // page/loging.vue <template> <el-container> <el-form> <el-button type= "primary" @click= "loginWithAuthZero" >Auth0でログイン </el-button> </el-form> </el-container> </template> <script> export default { methods: { loginWithAuthZero: function () { this .$auth.loginWith( 'auth0' ) } } } </script> そして先ほどAuth0で設定したcallbackページです。 // page/callback.vue <template> <el-container> <p>Please Wating・・・・</p> </el-container> </template> 最後にログイン後にユーザー名を表示するmypageです。 // page/mypage.vue <template> <el-container> <h1>こんにちは、 {{this .$auth.$state.user.family_name }} さん!</h1> </el-container> </template> ログインしてみる これでログイン処理が出来上がったかと思いますので yarn run dev のコマンドを実行し http://localhost:3000/login にアクセスしてみましょう。 すると下記のような画面が表示されるかと思います。 Auth0でログインボタンをクリックすると下記のような画面が出てソーシャルログインが可能になります。 そしてログインすると、callback画面が一瞬見えたのちmypageへ遷移しユーザ名が表示されます。 これでログイン処理は完成しました! middlewareを使用してログインチェックする ログイン処理は完成しましたが、このままでは特にページに制限をかけていないため、意味がありません...。 なのでログインしたユーザのアクセス制御を Nuxt middlewareを使ってしていこうと思います。 middlewareではページレンダリングされる前の関数を定義できるということもあり、よくアクセス制御やログ周りなどで使用されています。 修正と追加に関しては下記の2つをやるだけです。 // middleware/auth.js export default function ( { store, redirect } ) { if (!store.state.auth.loggedIn) { redirect( '/login' ); } } nuxt configに下記を追記 // nuxt.config.js router: { middleware: 'auth' } これでログインしていないい状態で http://localhost:3000/mypage にアクセスしてみましょう。 するとmiddlewareのチェックによりlogin画面にリダイレクトされます! 終わりに 今回はNuxt.jsとAuth0認証機能を作ってみました。 試しにAuth0触ってみたい、Nuxtで遊びたいという時の1つの参考になれば嬉しいなと思います!
アバター
はじめまして、スマートキャンプの郷田と申します。 この記事では、エンジニアがコーポレートITを立ち上げた経験を元に学んだことをアウトプットしています。 コーポレートITに関わる人の助けになればいいなぁと思って書きました。 経緯 立ち上げの過程でやったこと 情報セキュリティの勉強 コーポレートIT関連の業務の切り出しと巻取り 社員の教育や啓蒙活動 Pマーク申請・取得 採用 立ち上げで学んだ6つのこと 1. セキュリティやシステム管理の知識 2. 想像以上の悲惨な状況 3. バックオフィスの業務 4. 社員のセキュリティへの漠然とした不安 5. 協力者の重要性 6. 自分がやるべきこと コーポレートIT部門をこれから作る企業へ 必要だと感じている方 立ち上げてみたいエンジニアの方 今のスマートキャンプ の状況 さいごに 経緯 2016年の10月頃にスマートキャンプにエンジニアとして入社しました。 当時は社員数が10人弱だったのですが、2018年には社員数が30人を超えました。 事業が成長する一方で、前職では当たり前だったPCやアカウントなどの資産管理や、それにに関連するセキュリティ施策などは入社時からほとんどされてない状況でした。 それにより弊社ではセキュリティインシデントによる事業継続リスクが見えるようになってきました。 このような状況になっている企業はまだまだあるのではないでしょうか? この状況を脱却するためにセキュリティ強化は必須であると考え、弊社代表に「セキュリティやばいっすよ!」と相談したところ「思うようにやっていいよ。」といわれ、私は会社全般のセキュリティの強化(実質的なコーポレートITの立ち上げ)を行うこととなりました。 立ち上げの過程でやったこと 2018年2月から11月までの約9か月間で立ち上げを行いました。 最初は開発業務と兼務して進めていましたが、コーポレートIT立ち上げに関する全ての業務を一人で進めていたため、開発業務に手が回らなくなり、2018年5月ごろからはコーポレートIT業務だけを行っていました。 情報セキュリティの勉強から全社的な啓蒙活動、Pマーク取得まで、やったことを順を追って説明したいと思います。 情報セキュリティの勉強 情報系の大学出身のため、座学でのITセキュリティ強化はわかるものの、企業で必要なコーポレートITのセキュリティ知識はなかったため、これらを学習するために情シスに深い知識を持つ業務委託の方を採用し、実践を通じて知識を増やしていきました。 コーポレートIT関連の業務の切り出しと巻取り いわゆる情報システム部門が行うべき業務が、代表、総務・労務・財務担当といったバックオフィスメンバー、各部門長が行っていたため、それらの実態把握と、巻取りをしました。 業務例 PCやスマホのキッティング作業 PCやスマホ等の情報記憶媒体の採番と管理 入退社時のアカウントやPCの発行・変更・削除 社内情報システムの一元管理 全従業員に付与されるサービスのアカウント管理 インターンやパートの情報アクセス権限の制御 etc... 社員の教育や啓蒙活動 また、社員教育のために次のような啓蒙活動を行いました。 契約書や履歴書といった機密情報の管理方法の説明 クラウドサービス利用時に 複数人で共通IDを利用しちゃう文化がやばい あなたが今取り扱ってる情報は個人情報なんですよ! という意識付け これら対策が行われなかった場合のリスクと、インシデント発生による事業継続リスクの大きさの説明を、全従業員に説明する機会も設けました。 Pマーク申請・取得 バックオフィスのメンバーを中心に行っていたPマーク取得業務ですが、セキュリティ知識が無いメンバーのみで進めていたので、実態が全然ついてきていない状態でした。 これらの実態整備をし、最終的に申請から現地審査、取得の旗振りまで行いました。 採用 自身の知識や適性、やりたいことを考えたときに、コーポレートITを継続するのは効率的ではないと考えたため、情シス業務の経験がある専任の採用活動を進めました。 募集要項作成から面談まで行い、社員1名、業務委託2名の採用を行いました。 立ち上げで学んだ6つのこと 1. セキュリティやシステム管理の知識 コーポレートITとして企業で発生するセキュリティリスクの対策方法を学ぶことができました! Pマークは個人情報を適切に扱うためのノウハウのフレームワークのため、個人情報保護法や、個人情報のマネジメント方法を学ぶことができました。 また、自社で定めている規程類、規約類にはどういった内容が書かれているか、Pマーク取得やコーポレートIT立ち上げの中でどれをどのように変更する必要があるか、といった点も学ぶことができたのは良い経験でした。 どちらもコツとしては、リモートが多い企業や、セキュリティを重要視する企業など、業界や企業規模、取り扱う商品やサービスなどによって全く違うということは、エンジニアの業務だけだと知ることができなかったことであり、良い学びとなりました。 これらの学びを通して、普段の開発業務でも「これは個人情報のマネジメント観点で問題があるのでは?」ということを詳細に理解説明できるようになったことも、良い学びでした。 2. 想像以上の悲惨な状況 コーポレートITの立ち上げをするためには、自社の現状を把握する必要があります。そのため、Division Manager(いわゆる部門長)にヒアリングをしたり、コーポレートITに関わる実務をされているメンバーにヒアリングをしたりすることを、立ち上げ期間は繰り返し行いました。 結果として、業務として比較的セキュリティリテラシーを求められるエンジニアにとって当たり前だと思っていた管理は、他Divisionではほとんど管理されていないことがわかりました。 たとえば、機密情報にあたる契約書や顧客情報のやり取りを行う連絡媒体がDivisionや会社として統一されておらず、slack、Chartwork、FacebookMessenger、メール、etc...と、複数媒体に乗って、アクセス権がその都度設定されており、管理しているとは言えない状況でした。 他の例としては、ユーザー管理機能があるサービスなのに、1つのID,PWを全員で使いまわして利用している、といった具合です。 このような状況であることは、コーポレートITを立ち上げようと思わない限りずっと知ることができなかったので、セキュリティリテラシーの当たり前を当たり前のように実行することは、人によっては意外とハードルが高いものだとということが学びでした。 3. バックオフィスの業務 コーポレートITの立ち上げの中で業務巻取りを行っていたのですが、それぞれ、入退社フローで労務の業務との連携、PCの準備で経理業務との連携といったように、各バックオフィスと連携をとって業務を進める必要がありました。 開発業務は複数人で1つのシステムに手を入れていく作業のため、タスクレベルの共有だけでなく、思想レベルでメンバー同士が認識し合うことが、業務成果につながるのですが、バックオフィス業務は、一人ひとりが専門知識、専門業務を行う集団であるため、その業務における思想の共有はほとんどされないこと、そして、それぞれの個を尊重して信頼することで成り立っているチームなんだということを知れたのはとても新鮮でした。 また、エンジニアは負として捉える「繰り返し作業」について、バックオフィス業務ではかなりの数の繰り返し作業が発生しがちで、それらを淡々と高速にこなしていく姿をみて高い精神力を感じたため、今ではとても尊敬しています。 4. 社員のセキュリティへの漠然とした不安 コーポレートITの立ち上げを行う前は、一人ひとりがSaaSなどを使いこなし、セキュリティに関しても自身で対策して進めていける良い集団だと勝手に思っていたのですが、立ち上げ時に、「システムやセキュリティ、PCやスマホ管理といった、ITに関わることなんでも私に質問していいよ!」と言って回ったところ、すごい数の質問が来るようになりました。 特に、2018年以降に入社した中途メンバーから、前職とくらべてセキュリティに関する制約があまりにも少なすぎて不安に思う声が多かったです。 ただ、どのように対策すればいいのか、このままでいいのかわからないという声がとても多かったため、正しい知識を持つ人は希少性が高いということを身をもって知ることができました。 5. 協力者の重要性 立ち上げを進めていく中で、セキュリティリスク対策をすすめるとどちらかというと事業成果に直接つながらない守りの業務を各社員に実施してもらう必要が出てきました。 いままで一人で進められていた作業を、コーポレートITがやるようになったり、外部とのファイルのやり取りを各媒体に統一する必要があったりと、手間が増えてしまうことが多い状況でした。 なのでお願いをして回ることが多いのですが、なかなか理解してもらえず何も実施してもらえないこともありました。 たとえば営業からエンジニアの業務にあーだこーだ言われてもなかなか聞く気になれない気持ちはわかるので、コーポレートITに関しても他部署の協力者を増やす努力をすることで、段々と話がしやすくなりました。 最初は協力者が居なかったためほんとに辛い時期が続いたので、戦略として協力者を作ることがとても大切だと学べました。 6. 自分がやるべきこと 開発で解決できることは、関わる人の生産性が上がることが多く、アジャイルな手法をとっているチームでは成果が出るまでのスピードも早く、関係者が喜んでいることを感じられる仕事です。 一方、コーポレートITによる施策は、リスク対策のために工数増えてみんなの生産性を下げてしまうことも多いし、成果が見えないことがほとんどだし、なかなか褒めてもらえないし、興味を持ってもらえないことが多い仕事です。 今回の立ち上げで、自分の評価が180度変わったように思えることが多く、人生の中でも記憶にのこる辛い期間となりました。 ただ、業務が変わっても、自分や会社が目指している世界は変わらないし、職は違ってそれぞれいろんな苦労がある中で、同じ目標を目指して進んでいるということがわかり、目標を失わずに進むことがとても大切であると学びました。 コーポレートIT部門をこれから作る企業へ 必要だと感じている方 コーポレートIT立ち上げは、全社の業務に影響がでます!なので、皆さん自分ごとだと思って取り組むことが、短期的に立ち上げるコツです。 特に、セキュリティリスクの対策の最初の一歩は、ヒューマンエラーを減らすことです。社員全員がコーポレートITの業務に理解を持てる状況を作ることは、セキュリティリスク対策でとても効果的です。 また、コーポレートITは立ち上げをされる方を評価してあげてください。他の人の業務を増やして整理しただけで何も生み出してないと考えるのではなく、業務を整理して最適化することで、なにか問題起きたときに対処できるようになったと考えて、評価をしてあげることが大切です。 特に誰かのためになりたいと思う人が多いため、評価をしないと担当のモチベーションがどんどん下がってしまいます。 立ち上げてみたいエンジニアの方 もし、エンジニアがコーポレートITを立ち上げるであれば、本気で会社を変える意気込みを持ってください。片手間でやろうと思うと、なかなか成果もでず「やらされている」感がでてしまいます。 ただ、本気で取り組んでもすぐには成果は出ないです。それを継続することで成果がでるようになります。最初は嫌な顔されたとしても、やりきればコーポレートITが必然となるので、その環境を目指して、全力で取り組んでください。 今のスマートキャンプ の状況 「コーポレートITすごく状況良くなってるよー!」 立ち上げはなんとか終わり、専任も採用でき、現在は2名がコーポレートITとして常時稼働している状況を作ることができました。 また、ITに関してなんでも簡単に聞ける環境も作ることができ、全社員から直接感謝してもらえる環境ができたため、働きやすい状況になったのではと考えています。 私は立ち上げを行ったため、過去の経緯などで深く認識していることもあるため、定期的にコーポレートIT担当とランチに行き、サポートできる状況を継続しています。 さいごに 自社内の話ですが、コーポレートIT立ち上げを気持ちよくサポートしていただいた皆さんに、感謝を書き残しておきたいと思います。 立ち上げ時に超辛かったときに、私の業務を自主的に巻き取ってくれたエンジニアの皆さん ITわからないなりにも、「コーポレートITは重要だよ!」といい続けてくれた上司 「やりたいことなんだったらやってみればいいと思う。」と言ってくれた代表 最後まで読んでいただきありがとうございます。ここに記した私の学びがなにか参考になれば幸いです。
アバター
こんにちは。エンジニアの笹原です。 スマートキャンプではフロントエンドの開発にVue.jsを取り入れています。 Vue.jsで開発するうえで、 SFC(単一ファイルコンポーネント) を利用できることは良さの一つだと思います。 今回は、Sprocketsでアセット管理を行っているRailsプロジェクトに、Webpackを入れることでSFCを使えるようにする方法を紹介します。 SFC(単一ファイルコンポーネント)とは SFCの良さ SFCの実装方法 Sprocketsを利用しているRailsプロジェクトにSFCを導入する 課題と解決方法 完成予想プロジェクト構成 導入手順①: 必要なnpmモジュールのインストール 導入手順②: Webpackの設定 導入手順③: ビルドの実行 終わりに SFC(単一ファイルコンポーネント)とは Single File Componentの略で、テンプレート、ロジック、スタイルを一つのファイルで管理しているコンポーネントのことです。 SFCの良さ 一つのファイルで管理することは、 コンポーネントベースで画面を構成していく上で保守性を増す ことに繋がります。 公式ドキュメント( 単一ファイルコンポーネント — Vue.js )にも書いてあるとおり、関心事項がコンポーネントごとに分離されているような画面において、コンポーネント内のテンプレート、ロジック、スタイルは結合されているため、同一ファイルにまとまっているのが自然です。 一方で、 ロジックやスタイルが別のコンポーネントに影響しないような作りにもなっている ことから、それぞれを他のコンポーネントとまとめる必要性も薄れています。 SFCの実装方法 SFCで実装は、 .vue 拡張子のファイル内に template 、 script 、 style の3つのタグ内にそれぞれ、テンプレート、ロジック、スタイルを記述していきます。 そのため、実際にブラウザで読み込める形にするには、WebpackやBrowserifyといったビルドツールを使うことになります。 <template> <p> Hello World </p> </template> <script> export default { name: "Hello" } </script> <style scoped> p { font-size: 2em } </style> Sprocketsを利用しているRailsプロジェクトにSFCを導入する 課題と解決方法 ここで、ようやく本題に入っていくんですが、Railsのデフォルトのアセット管理ライブラリであるSprocketsはこの .vue 拡張子のビルドは行ってくれません。 そのため、新規にビルドツールを導入することになるのですが、既存のSprockets管理下のアセットも同時に移行するのは大変です。 そこで、既存のアセットは従来どおりSprockets管理下としつつ、新しくWebpackを導入した上で、 Webpackがビルドした生成物を既存のSprocketsが併せて管理する 方法を取りたいと思います。 RailsでWebpackを使う際に、Webpackerを利用する方法もありますが、Webpackerはconfig記述が独自の部分があるなど、Webpack単体よりも気にしなければならないことが増えそうなため、素のWebpackを利用するようにしています。 完成予想プロジェクト構成 もろもろ端折っているのもありますが、構成自体はシンプルです。 app ディレクトリ配下がRailsアプリケーションで frontend 配下が導入するWebpack管理下になります。 Webpack管理下のアセットをビルドした上で、生成物をRailsアプリケーションのSprockets管理下に配置します。 ├── app/ │ ├── assets/ │ │ ├── javascripts/ │ │ │ ├── webpack/ │ │ │ │ ├── plugins.js ・・・・・・ビルド生成物 │ │ ├── stylesheets/ │ │ │ ├── webpack/ │ │ │ │ ├── plugins.css ・・・・・ビルド生成物 ├── frontend/ │ ├── src/ │ │ ├── components/ │ │ │ │ ├── Hello.vue │ │ ├── entry.js │ ├── webpack.config.js ├── package.json 導入手順①: 必要なnpmモジュールのインストール まずは、nodeプロジェクトを初期化して、必要なモジュールをインストールしていきます。 $ yarn init $ yarn add vue $ yarn add -D @babel/core babel-loader babel-preset-env clean-webpack-plugin css-loader mini-css-extract-plugin vue-loader vue-template-compiler webpack webpack-cli webpack-merge webpack-node-externals 導入手順②: Webpackの設定 続いて、Webpackの設定ファイルを作成していきます。 普通にVueのビルド設定をしたうえで、outputのpathをRailsアプリケーションが管理している app 配下にするだけです! シンプルな構成例を用意しておきます! 実際に使う際には、scriptやstyleでTypeScriptやSCSSを使うためのloaderなどを追加してください。 webpack.config.js const path = require( 'path' ); const webpack = require( 'webpack' ); const { VueLoaderPlugin } = require( 'vue-loader' ); const MiniCssExtractPlugin = require( "mini-css-extract-plugin" ); function resolve (dir) { return path.resolve(__dirname, dir) } const RAILS_ASSETS_ROOT = resolve( '../app/assets/' ); module.exports = { entry: resolve( './src/entry.js' ), output: { path: RAILS_ASSETS_ROOT, filename: 'javascripts/webpack/plugins.js' , } , module: { rules: [ { test: /\.vue$/ , use: { loader: 'vue-loader' } } , { test: /\.js$/ , loader: 'babel-loader' , exclude: file => ( /node_modules/ .test(file) && ! /\.vue\.js/ .test(file) ) } , { test: /\.css/ , use: [ MiniCssExtractPlugin.loader, 'css-loader' ] } ] } , resolve: { extensions: [ '.vue' , '.js' ] , } , plugins: [ new webpack.ProvidePlugin( { 'Vue' : 'vue' } ), new VueLoaderPlugin(), new MiniCssExtractPlugin( { filename: 'stylesheets/webpack/plugins.css' } ) ] } ; webpack.prod.config.js const webpack = require( 'webpack' ); const merge = require( 'webpack-merge' ); const CleanWebpackPlugin = require( 'clean-webpack-plugin' ); const baseConfig = require( './webpack.config' ); module.exports = merge(baseConfig, { mode: 'production' , plugins: [ new CleanWebpackPlugin( { cleanOnceBeforeBuildPatterns: [ 'javascripts/webpack/' , 'stylesheets/webpack' ] } ), new webpack.LoaderOptionsPlugin( { minimize: true } ) ] } ); webpack.dev.config.js const merge = require( 'webpack-merge' ); const baseConfig = require( './webpack.config' ); module.exports = merge(baseConfig, { mode: 'development' , devtool: 'inline-cheap-module-source-map' } ); 導入手順③: ビルドの実行 Webpackの設定までできたらあとはビルドするのみです。 package.jsonのscriptsにwebpackのコマンドを追加します。 { ... " scripts ": { " dev ": " webpack --progress --hide-modules --watch --config frontend/webpack.dev.config.js ", # -- watch が入っているので変更を検知して自動で build し続けてくれます。 " build ": " webpack --progress --hide-modules --config frontend/webpack.prod.config.js " } , ... } 以下のコマンドを実行することで、Railsアプリケーションが管理している app 配下にビルドされ、生成物がRailsから利用できるようになります! # 開発時 $ yarn dev # リリース時 $ yarn build 終わりに 今回はVue.jsのSFCを使うためにRailsプロジェクトにWebpackを導入してみました。 既存のRailsアプリケーションの大枠の構成を変えずに、とりあえずWebpackを入れてみることでコストをかけずに新しい技術を取り入れることができました。 規模がある程度大きくなったアプリケーションではすべてを一度にリプレースする判断も難しいので、このように一部から取り入れることで技術が合うかどうか見ていけると良いと思います。
アバター
エンジニアの今川( @ug23_ )です。 本番環境サーバのユーザ管理、みなさんはどうしていますか? みんなで同じユーザ・同じ鍵を使う 入社・退職時にはインフラ担当者がユーザ追加・削除する という感じのレガシーなやり方をしてしまいがちですよね。 全員で同じユーザを使う運用は入社時はとってもラクですが、 退職者がでた時、漏洩したかもしれない時 、鍵を変えて全員に伝えて…と、無駄が多いですし、 サーバ上での動作を監査できるようにする という意味でも、全員がそれぞれ自分のユーザでログインできるようにしておきたいです。 また、人に依存しないしくみにしたいですね。「○○さん休みだからユーザ作れないね」というのはナシにしたいです。 ということでAnsible Playbookでユーザ作成をはじめ、 ユーザの削除 や sudo権限をなくす処理 を実行できるようにし、 冪等なユーザ管理 ができるPlaybookを公開することにしました。 記事内ではリポジトリ内の構成や仕組みを解説しており、記事末尾でリポジトリを掲載していますので参考にしてください。 コードの紹介 基本的な構成 ディレクトリ構成 Inventoryファイル(production, staging) group_vars main.yml, internal_servers.yml, management.yml roles ロール: common ロール: sudoers リポジトリ 運用方法 まとめ コードの紹介 基本的な構成 Ansible Playbookにはベストプラクティスが公開されていて、Ansible力の向上のためにこれにのっとってPlaybookを作ろう、と思いました。 分かりづらいところなどは以下を参考にしてください。 docs.ansible.com ディレクトリ構成 ベストプラクティスのディレクトリ構成にのっとって以下のようになりました。 説明上 tree コマンドの出力をちょっといじっています。 . ├── production ├── staging ├── ansible.cfg ├── group_vars │   └── all.yml ├── internal_servers.yml ├── main.yml ├── management.yml ├── roles │   ├── common │   │   └── tasks │   │   ├── main.yml │   └── sudoers │   └── tasks │   └── main.yml └── README.md 情報のもたせ方としては以下のような棲み分けがされています Inventoryファイル(production, staging) : 管理対象のホスト名・環境依存の変数 group_vars : ホストのグループごとの変数(今回はユーザ名等) main.yml : (internal_servers|management).yml を呼びだす (internal_servers|management).yml : どのホストにどのロールを割り当てるかを定義 roles/[ロール名]/tasks/main.yml : 実際にロールが割り当てられたときに実行される処理の定義 実際に本番環境へユーザの変更を適用する場合は以下のようなコマンドを打っています。 ansible-playbook main.yml -i production -f 5 --private-key secret.key -f 5 は並列実行オプション、 --private-key で全サーバ共通の秘密鍵を指定しています。さらに込み入った設定をする場合は ansible.cfg を用います。 Inventoryファイル(production, staging) [app] app1 app2 [app:vars] ansible_ssh_user=ec2-user [management] management [management:vars] ansible_ssh_user=ubuntu [analysis] analysis1 analysis2 [analysis:vars] ansible_ssh_user=ubuntu [jenkins] 127.0.0.1 [jenkins:vars] ansible_connection=local Ansibleを適用するホストをグループごとにまとめて定義します。ホスト名はAnsible Playbookを実行するサーバから到達可能なホスト名であればOKです。IPアドレスも可能です。 グループごとにvarsを定義できます。ここでは環境ごとにAmazon LinuxとUbuntuが混在しています。 localhostに向けて適用したい場合は jenkins のように 127.0.0.1 と ansible_connection=local を指定してください。 また、productionとstagingを分けているように、環境による差異はこの2つで区別します。 環境によって差し替えたい値が多い場合Best Practiceで紹介されている Alternative Directory Layout を利用することをおすすめします。 group_vars Inventoryで定義したグループごとにvarsを定義できます。 今回は全体で使うので all のみ定義しています --- users : - name : 'ug23' public_key : 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABA...' sudoers : - 'ug23' admin_group : 'admin' user_group : 'developers' ユーザIDと公開鍵をここに配置します。パスワードなしでsudoを許可するユーザのユーザIDをsudoers配下に入れます。 ここには公開鍵が載ります。よりセキュアにしたい場合、このファイルを ansible-vault を用いて暗号化する方法がありますがここでは扱いません。 qiita.com main.yml, internal_servers.yml, management.yml 実際にホストにたいして行われる処理を記述するYAMLファイルを見ていきます。 main.yml --- - import_playbook : internal_servers.yml - import_playbook : management.yml internal_servers.yml --- - hosts : app analysis jenkins become : True roles : - common - sudoers management.yml --- - hosts : management become : True roles : - common 「どのホストに」「どのロールを適用するか」を定義します。 roles につけるロールをコントロールすることで適用したい処理を選択することができます。 roles roles配下にロール名でディレクトリを切ることでロールを表現します。 tasks/main.yml が最初に呼ばれるので基本的にはここに書いた処理が実行されます。 ロール: common 全体に適用する処理を定義しています。 --- - name : ユーザグループの作成 group : name={{ user_group }} - name : ユーザ作成とグループ登録 user : > name={{ item.name }} group={{ user_group }} groups={{ user_group }} with_items : '{{ users }}' - name : ユーザ作成とグループ登録 user : > name={{ item.name }} group={{ user_group }} groups={{ user_group }} with_items : '{{ users }}' # Amazon Linux上でauthorized_keyモジュールを実行するのに必須 - name : libselinux-pythonインストール yum : name=libselinux-python state=present when : "ansible_distribution == 'Amazon'" - name : ユーザごとの公開鍵の登録 authorized_key : > user={{ item.name }} key={{ item.public_key }} with_items : '{{ users }}' - name : インスタンス上のユーザリストの取得 shell : 'getent group developer | cut -d: -f4 | tr "," "\n"' register : current_users - name : usersにないユーザを削除 user : > name={{ item }} state=absent remove=yes with_items : "{{ current_users.stdout_lines | difference(users | map(attribute='name') | list) }}" - name から始まる各要素が1つのtaskを示しています。 {{ user_group }} などのように文字列中で二重braceでくくることで変数展開できます。 group user yum などはAnsibleのモジュールであり、Ansibleの公式ドキュメントに「どんな値をいれるべきか」「どんなオプションがあるのか」が載っています。 docs.ansible.com 今回のポイントは以下の不要ユーザ削除処理です。 - name : インスタンス上のユーザリストの取得 shell : 'getent group developer | cut -d: -f4 | tr "," "\n"' register : current_users - name : usersにないユーザを削除 user : > name={{ item }} state=absent remove=yes with_items : "{{ current_users.stdout_lines | difference(users | map(attribute='name') | list) }}" 実際に存在しているユーザのリストを行区切りで取得し、 current_users に保存 current_users と users のnameだけのリストを用意 上記リストの差集合をとり、current_usersにだけ多く存在するユーザを削除 という処理を行っています。 Ansibleでなんらかの削除処理をする場合 実際の値を取得して 存在してはいけない要素を削除する、という流れでやれば「作りっぱなしのまま残ってしまう」を防ぐことができます。 ロール: sudoers --- - name : 管理者グループの作成 group : name={{ admin_group }} - name : 管理者グループをsudoersに追加 lineinfile : path : /etc/sudoers state : present regexp : "%{{ admin_group }}" line : "%{{ admin_group }} ALL=(ALL) NOPASSWD: ALL" - name : 管理者ユーザを管理者グループに追加 user : > name={{ item }} group={{ admin_group }} groups={{ user_group }}, {{ admin_group }} with_items : '{{ sudoers }}' - name : 実際に管理者グループに属するユーザのリストを取得 shell : 'getent group {{ admin_group }} | cut -d: -f4 | tr "," "\n"' register : present_sudoers - name : sudoersに存在しないユーザを管理者グループから外す user : > name={{ item }} groups={{ user_group }} with_items : '{{ present_sudoers.stdout_lines | difference(sudoers) }}' ignore_errors : yes # ユーザが削除済みの場合があるため sudoersの編集は安全な環境で試したほうがよいです。 visudo ではsudoersファイルの文法チェックをやってくれるのですが、 lineinfile モジュールを使った編集では文法チェックが走りません。私はこれをミスって本番環境にsudoできなくさせました。気をつけましょう。 sudoできなくなったらこうやって復帰させましょう。 qiita.com リポジトリ 紹介したコードをGithubの公開リポジトリにおきました!参考にしてください github.com 運用方法 ユーザ登録してほしいひとに自身のPCで ssh-keygen してもらい、キーペアを作成してもらいます。 ユーザID, 公開鍵を group_vars/all.yml に記入してもらい、プルリクを出してもらいます。 レビューOKならマージしてAnsible Playbookを実行、という流れでやっています。 現状はできていませんが、マージ後即Playbook実行というのもできますね! まとめ Ansible Playbookのベストプラクティスとされているディレクトリ構成で、ユーザの登録・削除を行うPlaybookを作成しました。 具体的には以下の条件を満たすPlaybookを作成しました。 ユーザの作成・削除を冪等に行う 設定ファイルにユーザ情報を追加したらユーザが追加される 設定ファイルから消えたらそのユーザはいないようにする sudoersの管理もやる Ansible PlaybookのベストプラクティスのDirectory Layoutにしたがう ベストプラクティスに従うのは最初の習得コストがかかるものの「どこになにを置くか」が明確になるので最初に整えておきたいですね。 春は出会いと別れの季節、それらがつらいものにならないように普段からラクできる構成にしておきたいですね!
アバター
平成も残るところわずかとなりましたね。 ティム・バーナーズ=リーによって、WWWの原型が生み出されたのが平成元年(1989年)なんですよね。 そんなことをエンジニア達で話していて、せっかくなので 平成に登場した情報技術を時系列にしてまとめる 自由研究をしてみることにしました 。 「自分の生まれ年になにができたのか」 「こんな技術もあったなあ」 など思いを馳せていただければ幸いです。 ※ スマホなどでは見にくいかと思いますので、 画像版やPDF版などもお試しください 。 ※ 片手間でまとめたので、「年が間違ってる」「ロゴがおかしい」などあるかと思いますがご容赦ください。 【追記 2019-04-27】 たくさんコメントありがとうございます! 皆さんそれぞれ平成を思い返していただけたようでとても嬉しく思います。 思い入れのある技術が入っていない、誤りがあるなどのお声も頂いているので、再度調べまして反映させていただこうと思います。 表が見にくい件については、PDF化したものもありますので取り急ぎそちらでみていただければと思います。 drive.google.com それではよいGWを! テーブル版 画像版 PDF版 テーブル版 年 言語 Linux系 Web全般 ミドルウェア ライブラリ ツール 平成元年 (1989) SQL Server bash 平成2年 (1990) Haskell zsh CVS 平成3年 (1991) Python VisualBasic Linux 平成4年 (1992) 平成5年 (1993) Lua 平成6年 (1994) Debian Slackware 平成7年 (1995) JavaScript Ruby PHP Java Redhat InternetExplorer MySQL Apache HTTP Server IIS 平成8年 (1996) R Opera 平成9年 (1997) ECMAScript PostgreSQL 平成10年 (1998) 平成11年 (1999) Vine Linux Akamai Apache Tomcat JBoss 平成12年 (2000) C# Gentoo Linux KNOPPIX SQLite Subversion 平成13年 (2001) D Cacti HAProxy Zabbix Apache Struts 平成14年 (2002) Whitespace Arch Linux Firefox Nagios Spring ASP.NET 平成15年 (2003) Scala Groovy Fedora Safari memcached Munin Seasar Word Press 平成16年 (2004) CentOS Ubuntu Nginx Ruby on Rails 平成17年 (2005) F# dojo django Grails CakePHP puppet Git fish 平成18年 (2006) Rust PowerShell jQuery AWS Selenium 平成19年 (2007) Clojure heroku Neo4j PlayFramework LiftFramework Sinatra ZendFramework ASP.NET VirtualBox 平成20年 (2008) Hyper-V Google Chrome BitBucket GitHub Google Cloud Platform Apache Cassandra 平成21年 (2009) CoffeeScript Go MariaDB mongoDB Redis Node Chef 平成22年 (2010) Cloudflare Microsoft Azure Elasticsearch Couchbase AngularJS Backbone.js Express Vagrant 平成23年 (2011) Kotlin Dart GitLab Jenkins ember.js Laravel FuelPHP Bootstrap 平成24年 (2012) Elixir TypeScript Julia CloudForge CSS3 Ansible 平成25年 (2013) SteamOS fastly React Xamarin Docker 平成26年 (2014) Swift Crystal Hack Vue.js HTML5 Terraform 平成27年 (2015) Solidity AMP PWA Microsoft Edge netlify GraphQL Ethereum 平成28年 (2016) Alibaba Cloud SVELTE Nuxt.js HUGO 平成29年 (2017) GatsbyJS 平成30年 (2018) 平成31年 (2019) 画像版 平成元年(1989)-平成10年(1999) 平成12年(2000)-平成22年(2010) 平成23年(2011)-平成31年(2019) PDF版 drive.google.com そして令和へ...
アバター
デザイナー兼エンジニアの葉栗です! スマートキャンプでは以前からWebフロントエンド開発にVue.jsを取り入れています。 Vue.jsなどコンポーネント指向のフレームワークは、UIフレームワークも豊富で、お手軽にリッチなUIが構築できるのでいいですよね。 今回は私のお気に入りの、 Vue.js + Elelment UI + Lottie というライブラリを使って、数十分でできる簡易的なログインページを作ってみようと思います。 0から構築をはじめて、レイアウト設計、ElementUIで実装、Lottie組み込み、完成といった感じで詳しく説明していきます! 完成画面 😊 使用技術 Vue.js Element UI Lottie(ロッティー) 実装します! 事前準備 Vue CLIをインストール プロジェクトを作成 ElementUIをインストール ログイン画面を作成 不要コードの削除 コンポーネント(Element UI)の配置 Lottieの導入 Lottieを使うためのコンポーネント作成 アニメーション追加 ログインできた幸せを祝福する花火を追加 完成! さいごに 完成画面 😊 使用技術 Vue.js 言わずとしれたJSフレームワークです。 コンポーネント指向に基づいて設計し、SFC(Single File Component)として実装するフレームワークです。 最近はそこから一歩踏み込んで、AtomicDesignをベースに設計するケースが増えていますね。 弊社でもVue.jsでAtomicDesignでSPA!のようなプロダクト開発を進めています!(これのツラミなんかは別記事で書こうかな) Element UI Vue.jsのコンポーネントライブラリです。 他にVuetifyやBootstrap等ありますが、ElementUIはデフォルトであたっているCSSが少なく、デザインする上でも使い勝手がいいので採用しています。 Lottie(ロッティー) 2017年2月にAirbnbが発表したWeb、iOS、Android、React Nativeなどのアニメーションライブラリです。 LottieはJSONデータをリソースファイルとして使用しているので、軽量でスムーズなアニメーションを実装することができます。 実装します! 事前準備 yarnは事前にインストールお願いします。 npm install -g yarn Vue CLIをインストール yarn global add @vue/cli vue -V // バージョンが表示されればOK // 3.6.3 プロジェクトを作成 名前は「new-app」というプロジェクトにします。 vue create new-app ? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) Manually select features 今回は、説明を簡単にするために「 default 」で作成します。 (vue routerやvuexを利用する場合は、「Manually select features」を選択して設定していきます) 🎉 Successfully created project new-app. 👉 Get started with the following commands: $ cd new-app $ yarn serve 作成が完了したら、表示されているコマンドを入力して、アプリケーションを起動してみます! cd new-app yarn serv ビルドが開始され、サーバーが立ち上がります。 http://localhost:8080/ にアクセスして、以下のようになっていれば完成! ElementUIをインストール vue add element ? How do you want to import Element? (Use arrow keys) ❯ Fully import Import on demand Elementをどのようにインストールしたいか。 「Fully import」と「Import on demand」のいずれかを選択します。 今回は「 Fully import 」を選択して、全てImportします。 ? How do you want to import Element? Fully import ? Do you wish to overwrite Element's SCSS variables? (y/N) ElementUI用のSCSS変数を上書きするかどうか確認されます。 今回は、「 y 」を選択します。 ? How do you want to import Element? Fully import ? Do you wish to overwrite Element's SCSS variables? Yes ? Choose the locale you want to load (Use arrow keys) ❯ zh-CN zh-TW af-ZA ar bg ca cs-CZ (Move up and down to reveal more choices) 最後に、上下キーで移動して「 ja 」を選択 ✔ Successfully invoked generator for plugin: vue-cli-plugin-element The following files have been updated / added: src/element-variables.scss src/plugins/element.js package.json src/App.vue src/main.js yarn.lock You should review these changes with git diff and commit them. 上記のような表示がされれば成功です。 先程の、 http://localhost:8080/ にアクセスして、el-buttonが挿入されていれば、Elemen UIの導入完了です。 ログイン画面を作成 ここから実際にUIを作っていきます! 今回はシンプルに、以下のApp.vueだけを変更していきます! 不要コードの削除 App.vueにデフォルトで実装されているものを消してしまいましょう。 // ./src/App.vue <template> <div id= "app" > </div> </template> <script> export default { name: 'app' } </script> <style> </style> コンポーネント(Element UI)の配置 Element UIを画像のような構成で配置していきます。 // ./src/App.vue <template> <div id= "app" > <el-card class = "login-card" > <!-- divでタイトル追加 --> <div class = "login-title" >ログイン</div> <el-form :model= "loginForm" > <el-form-item label= "メールアドレス" prop= "email" > <el-input type= "text" v-model= "loginForm.email" autocomplete= "off" ></el-input> </el-form-item> <el-form-item label= "パスワード" prop= "password" > <el-input type= "password" v-model= "loginForm.password" autocomplete= "off" ></el-input> </el-form-item> <el-button type= "primary" :loading= "checking" @click= "handleLogin" >ログイン</el-button> </el-form> </el-card> </div> </template> <script> export default { name: 'app' , data () { return { checking: false , loginForm: { email: '' , password: '' , } } } , methods: { handleLogin() { this .checking = true } } } </script> <style> body { text-align: center; /* いい感じの背景の色 */ background-color: #E0EAF6; } .login-title { font-size: 32px; font-weight: 600; margin-bottom: 20px; } .login-card { max-width: 800px; margin: auto 0; } </style> これで基本的はフォーム部分のUIはできました。 次は動きのある部分を追加していきます。 Lottieの導入 yarn add vue-lottie Lottieを使うためのコンポーネント作成 公式ドキュメントを参考にしながら、Lottie.vueを追加します! // ./src/components/Lottie.vue <template> <div :style= "style" ref= "lavContainer" ></div> </template> <script> import lottie from 'lottie-web' ; export default { props: { options: { type: Object , required: true } , height: Number , width: Number , } , data () { return { style: { width: this .width ? `$ {this .width } px` : '100%' , height: this .height ? `$ {this .height } px` : '100%' , overflow: 'hidden' , margin: '0 auto' } } } , mounted () { this .anim = lottie.loadAnimation( { container: this .$refs.lavContainer, renderer: 'svg' , loop: this .options.loop !== false , autoplay: this .options.autoplay !== false , animationData: this .options.animationData. default , rendererSettings: this .options.rendererSettings } ); this .$emit( 'animCreated' , this .anim) } } </script> これでLottieが使えるようになったので、画像の動く顔の部分を追加していきます。 アニメーション追加 Lottieのアニメーションは自分でAfter Effectsで作成もできますし、誰かが作ったものを使用することもできます! https://lottiefiles.com/ ここで検索して気に入ったのがあれば、JSONデータでダウンロードできるので、それをアプリケーション内でimportして、使用します。 まずは、この動く顔を追加してみます。 lottiefiles.com src/assets 配下にダウンロードしたファイルを「welcome.json」として配置します。 それをApp.vueでimportします。 // ./src/App.vue <template> <div id= "app" > <el-card class = "login-card" > <!-- lottieを追加 --> <lottie class = "login-lottie" :options= "welcomeLottie" :height= "300" :width= "300" :animCreated= "handleAnimation" /> <div class = "login-title" >ログイン</div> <el-form :model= "loginForm" > <el-form-item label= "メールアドレス" prop= "email" > <el-input type= "text" v-model= "loginForm.email" autocomplete= "off" ></el-input> </el-form-item> <el-form-item label= "パスワード" prop= "password" > <el-input type= "password" v-model= "loginForm.password" autocomplete= "off" ></el-input> </el-form-item> <el-button type= "primary" :loading= "checking" @click= "handleLogin" >ログイン</el-button> </el-form> </el-card> </div> </template> <script> // Lottieを使用 import Lottie from "@/components/Lottie.vue" // 動く顔のアニメーション import * as welcome from "@/assets/welcome.json" export default { name: 'app' , // 追加 components: { Lottie } , data () { return { checking: false , loginForm: { email: '' , password: '' , } } } , // 追加 computed: { welcomeLottie () { return { animationData: welcome } } } , methods: { // 追加 handleAnimation (anim) { this .anim = anim } , handleLogin() { this .checking = true } } } </script> <style> body { text-align: center; background-color: #E0EAF6; } .login-title { font-size: 32px; font-weight: 600; margin-bottom: 20px; } .login-card { max-width: 800px; margin: 200px auto 0; /* 動く顔の位置調整のため */ padding: 120px 0 20px; } /* 動くやつの位置調整 */ .login-lottie { position: absolute; top : 32px; right: 0; bottom: 0; left: 0; margin: 0 auto; } </style> ログインできた幸せを祝福する花火を追加 最後は画像のように一気に、Element UIとLottieを追加していきます。 // ./src/App.vue <template> <div id= "app" > <el-card class = "login-card" > <lottie class = "login-lottie" :options= "welcomeLottie" :height= "300" :width= "300" :animCreated= "handleAnimation" /> <div class = "login-title" >ログイン</div> <el-form :model= "loginForm" > <el-form-item label= "メールアドレス" prop= "email" > <el-input type= "text" v-model= "loginForm.email" autocomplete= "off" ></el-input> </el-form-item> <el-form-item label= "パスワード" prop= "password" > <el-input type= "password" v-model= "loginForm.password" autocomplete= "off" ></el-input> </el-form-item> <el-button type= "primary" :loading= "checking" @click= "handleLogin" >ログイン</el-button> </el-form> </el-card> <!-- ダイアログ追加 --> <el-dialog :visible.sync= "dialogTableVisible" > <div class = "login-title" >Welcome</div> <!-- 花火のアニメーション --> <lottie :options= "fireworksLottie" :height= "300" :width= "300" :animCreated= "handleAnimation" /> </el-dialog> </div> </template> <script> import Lottie from "@/components/Lottie.vue" import * as welcome from "@/assets/welcome.json" // 花火のアニメーション import * as fireworks from "@/assets/fireworks.json" export default { name: 'app' , components: { Lottie } , data () { return { checking: false , dialogTableVisible: false , loginForm: { email: '' , password: '' , } } } , computed: { welcomeLottie () { return { animationData: welcome } } , fireworksLottie () { return { animationData: fireworks } } } , methods: { handleAnimation (anim) { this .anim = anim } , handleLogin() { this .checking = true // 演出のためです...。追加しました。 setTimeout(() => { this .checking = false this .dialogTableVisible = true } , 1000) } } } </script> <style> body { text-align: center; background-color: #E0EAF6; } .login-title { font-size: 32px; font-weight: 600; margin-bottom: 20px; } .login-card { max-width: 800px; margin: 200px auto 0; padding: 120px 0 20px; } .login-lottie { position: absolute; top : 32px; right: 0; bottom: 0; left: 0; margin: 0 auto; } </style> ■ 使用したアニメーション lottiefiles.com lottiefiles.com 完成! 以下に追加したソースコードを記載します。 // ./src/App.vue <template> <div id= "app" > <el-card class = "login-card" > <lottie class = "login-lottie" :options= "welcomeLottie" :height= "300" :width= "300" :animCreated= "handleAnimation" /> <div class = "login-title" >ログイン</div> <el-form :model= "loginForm" > <el-form-item label= "メールアドレス" prop= "email" > <el-input type= "text" v-model= "loginForm.email" autocomplete= "off" ></el-input> </el-form-item> <el-form-item label= "パスワード" prop= "password" > <el-input type= "password" v-model= "loginForm.password" autocomplete= "off" ></el-input> </el-form-item> <el-button type= "primary" :loading= "checking" @click= "handleLogin" >ログイン</el-button> </el-form> </el-card> <el-dialog :visible.sync= "dialogTableVisible" > <div class = "login-title" >Welcome</div> <lottie :options= "fireworksLottie" :height= "300" :width= "300" :animCreated= "handleAnimation" /> </el-dialog> </div> </template> <script> import Lottie from "@/components/Lottie.vue" import * as welcome from "@/assets/welcome.json" import * as fireworks from "@/assets/fireworks.json" export default { name: 'app' , components: { Lottie } , data () { return { checking: false , dialogTableVisible: false , loginForm: { email: '' , password: '' , } } } , computed: { welcomeLottie () { return { animationData: welcome } } , fireworksLottie () { return { animationData: fireworks } } } , methods: { handleAnimation (anim) { this .anim = anim } , handleLogin() { this .checking = true // 演出のため setTimeout(() => { this .checking = false this .dialogTableVisible = true } , 1000) } } } </script> <style> body { text-align: center; background-color: #E0EAF6; } .login-title { font-size: 32px; font-weight: 600; margin-bottom: 20px; } .login-card { max-width: 800px; margin: 200px auto 0; padding: 120px 0 20px; } .login-lottie { position: absolute; top : 32px; right: 0; bottom: 0; left: 0; margin: 0 auto; } </style> // ./src/components/Lottie.vue <template> <div :style= "style" ref= "lavContainer" ></div> </template> <script> import lottie from 'lottie-web' ; export default { props: { options: { type: Object , required: true } , height: Number , width: Number , } , data () { return { style: { width: this .width ? `$ {this .width } px` : '100%' , height: this .height ? `$ {this .height } px` : '100%' , overflow: 'hidden' , margin: '0 auto' } } } , mounted () { this .anim = lottie.loadAnimation( { container: this .$refs.lavContainer, renderer: 'svg' , loop: this .options.loop !== false , autoplay: this .options.autoplay !== false , animationData: this .options.animationData. default , rendererSettings: this .options.rendererSettings } ); this .$emit( 'animCreated' , this .anim) } } </script> これでにくめない顔のログイン画面の、すごい祝福されるログイン画面ができました 😊 さいごに Element UIとLottieは導入が簡単だし、ライブラリが豊富なので、すぐにアプリケーションをリッチにすることができます! 今回のLottieの使い方は少し大げさ(笑)ですが、ボタンクリック時のインタラクションや、ローディング表示などで導入することで、UXも良くなりますし、なにより見た目のクオリティも格段に上げることができると思います。 ぜひ使ってみてください! また弊社ではデザインブログもやっています!(私も書いてます!) スマートキャンプのプロダクトデザインに込めた思いや、デザイン小話など書いていますので見ていただければうれしいです! note.mu ライター:葉栗 雄貴 / Haguri Yuki(Designer & Engineer)
アバター
こんにちは。エンジニアの米元です。 昨年12月に1人目の子供が産まれ、年始から1ヶ月間の育休をとりました。 2月に復帰して2ヶ月が経ち、ようやく少し落ち着いてきたので育児を通して取り組んだ事や、エンジニア・マネジメント経験が活かされた事を紹介しようと思います。 あくまで私のケースなので他の方には当てはらない事があるかもしれませんが、エンジニアだけでなくこれから子育てをする方の参考に少しでもなれば幸いです。 前提 そもそも育休とは 何のために取ったのか 育休に入る前にやったこと 目標設定 育休中にやったこと 妻とタスクの洗い出し 1on1 振り返り 効率化・自動化 購入した物 良かったもの 食器洗い乾燥機 洗濯乾燥機 大型冷蔵庫 非接触の赤外線温度計 リッチェル ひんやりしないおふろマット 微妙だったもの バランスボール 電動ハイローチェア アプリ・サービス系 タスク・スケジュール管理 Trello TimeTree 成長記録・共有 ぴよログ みてね メディア 買い物系 メルカリ ジモティー 地域のコミュニティ ネットスーパー 自宅環境の整備 GoogleHomeを活用 育児動線の整備 育休を終えて 目標の達成度合 復帰後 大変だったこと よかったこと まとめ 子育てはチームで取り組むプロジェクト 子供は不確実性の塊 前提 そもそも育休とは 育児・介護休業法に定められた制度で、 子が1歳(一定の場合は、最長で2歳)に達するまで(父母ともに育児休業を取得する場合は、子が1歳2か月に達するまでの間の1年間<パパ・ママ育休プラス>)、申出により育児休業の取得が可能 引用元: 育児休業制度とは|育児休業を取る|育てる男が、家族を変える。社会が動く。イクメンプロジェクト との事です。 休業中の収入に関しては 育児休業期間中、賃金が支払われないなど一定の要件を満たす場合には、「育児休業給付金」が支給され、休業開始時賃金の67%(休業開始から6か月経過後は50%)が支給されます。 育児休業給付金は非課税のため、所得税はかかりません(翌年度の住民税算定額にも含まれません)。 また、育児休業中の社会保険料は、労使ともに免除されます。給与所得が無ければ、雇用保険料も生じません。 その結果、手取り賃金で比べると休業前の最大約8割となります。 引用元: 育児休業制度とは|育児休業を取る|育てる男が、家族を変える。社会が動く。イクメンプロジェクト となっており、基本的には会社から賃金が出ない分を給付金でカバーする形になります。 ただ支給金額には上限があるので注意してください。 私の場合はまだリリース前だったので使ってないのですが、 こちらの記事 で紹介されている「育休シミュレーター」を使うと給付金の金額や支払いタイミングと 子供と過ごす時間 を計算出来るようなので、これから育休を検討される方は使ってみるとよいかもしれません。 何のために取ったのか 私も妻も実家が遠方なこともあり身近に頼れる人がいない事が主な理由でした。 生後1〜3ヶ月は昼夜関係無く2〜3時間おきに授乳する必要があり、まともに 寝る時間がほとんどとれません 。 最初の1ヶ月は妻の母が滞在して手伝ってくれる事になったので、昼間は私は仕事、妻は休みながら育児、義母が家事全般を行い、夜は妻が主に子供の面倒を見てくれました。生後2ヶ月目からは私が育休を取って昼と夜交代しながら家事と育児をすることにしました。 育休に入る前にやったこと 目標設定 育休期間を漫然と日々を過ごすと、ただ育児を頑張って終わりになってしまいそうだったので、育休期間で達成するべき目標を立てる事にしました。 当然ですが育休期間よりも復帰してからの方が長いので、育休期間自体ではなく復帰後の生活にフォーカスして以下の2つの目標を考えました。 効率化・自動化 仕組みや機械の力を使って家事/育児を可能な限り楽にする 冗長化 自分自身の家事/育児のスキルを妻と同等のレベルにする 1つ目は、復帰後は自分自身が時間があまり取れなくなる事や、日中ずっと子供の面倒を見ることになる妻の負担を少しでも減らすために、出来るだけ少ない時間で家事/育児をこなせる状態にしておくというもの。 2つ目は冗長化のイメージで、自分自身の家事/育児スキルを上げておくことで、妻が体調を崩したり用事があって外出するなどの時でも問題無く家庭のオペレーションが回る状態にするのが目的です。 目標を立てた事で目指す方向が固まり、いつまでに何をやるべきかの計画を立てやすくなりました。 育休中にやったこと 妻とタスクの洗い出し 目標を立てたものの、それを達成するために必要な事や子供関連の手続き・検診など育休期間中もやることがたくさんありました。 年末休みの間になんとなく生活リズムが分かってきたこともあり、年明けすぐに全体像を把握するためのタスクの洗い出しをすることにしました。 妻と2人で携帯用のホワイトボード( バタフライボード )にやることリストを書き出し、それぞれに優先度をつけておおまかなスケジュールを立てました。 タスクの種類としては 検診などスポットで発生するもの 普段の家事/育児などルーチン的なもの 効率化・自動化に必要な作業 の 大きく3つ があり、 1はスケジュールを決めて Trello と TimeTree(スケジュール管理・共有アプリ) に登録して管理。 2は冗長化の事を考え、料理が苦手な私があえて毎日朝食を作ることにしました。 普段の生活に活かせるように、時間をかけて凝ったものを作るというよりは冷蔵庫にあるもので手早く・そこそこ美味しいものが作れるスキルをつける事を目指しました。 他のルーチンタスクに関しては、明確に役割分担せずに出来る人がやる事にしました。 あまりガチガチに決めてしまうと疲れて出来ない時でも負担になってしまいそうだったのであえて緩いルールにしました。 3はどちらかというと自分自身がやりたい事だったので、私がメインとなって買う物や整備するものを洗い出しました。 1on1 夫婦なので普段から常にコミュニケーションをとっているとは言え育児に追われているとゆっくりお互いの考えを話す事もできず、いつの間にか我慢をしていたり、相手に言えてない事があります。 特に 不満や要望などは改めて話す時間をとらないと日々溜め込んだものがどこかで爆発しそう だったので、これもバタフライボードに書き出しながら話すことにしました。 私からは、「育休から復帰したあとも含めて少しでいいのでエンジニアとしての勉強時間をとりたい」という話をして、妻からは「時々1人で出かけたり夜の子供の世話を私にまかせてゆっくり寝たい」「育休期間に私にやってほしい事」などを話しました。 書き出したものを一緒に見ながら話す事で、 「私とあなた」のように相手に対して意識を向けるのではなく、「問題と私達」という二人で一緒に問題に対して向き合って話をする事が出来た と思います。 仕事の1on1でも同様の事をしていて、ホワイトボードやPC上で書き出しながら話をすると、客観的に事実と感情を整理しながら話を進められる事が多かったので仕事での経験をうまく応用できたと思います。 先ほどのタスクの話と合わせて育児や育休に対する互いの認識や期待値を調整ができたので、やって良かったと思います。 振り返り 隔週で合計2回の振返りを行いました。 タスクの進捗確認とスケジュール調整が主な内容で、KPT等のフォーマットは仕事っぽさが出すぎてしまうのであえて使いませんでした。 実際のタスクの進捗ですが、育休の途中で子供が発熱して1週間ほど入院してしまったこともあり、予定より出来なかった事が多かったです。 ただ復帰後に何をやればいいかが把握できた事や、どれくらいのペースで何が出来るかが具体的にわかったのでタスク管理と振返り自体はやって良かったなと思います。 効率化・自動化 家電から育児グッズから細かな改善までたくさんあるのですが、主なものをいくつか書いていきます。 購入した物 良かったもの 食器洗い乾燥機 家事の効率化・自動化の貢献度No.1 1日分の使った食器を入れて夜に回しておけば朝にはきれいに洗えて乾燥した状態になっているのでめちゃくちゃ楽。実は手洗いより水道代が安くなるので、育児中でなくても自炊する人は場所さえ確保できれば絶対に買った方がいいと言い切れるアイテムです。 洗濯乾燥機 これは以前から持っていたものの、家事の効率化・自動化には欠かせないので一応あげてます。 熱に弱いものは乾燥までは出来ないのですがタオルや下着などをガンガン洗って乾かせるので、全体で6割くらいの工数は減るイメージです。 大型冷蔵庫 以前は1〜2人用の小型の冷蔵庫を使っていたのですが、子供がいると買い物に行くのも料理を作るのも大変になるので大型のものを買いました。 容量が大幅に増えた事と、野菜の保存期間が長くなった事で作り置きや買い置きがしやすくなりました。 非接触の赤外線温度計 ミルクの調乳の時に使います。ミルクの粉を熱湯で溶かしてから水で割って人肌くらいまで温度を下げるのですが、手の感覚だけだと熱すぎるのかどうかが判断つきにくく時間もかかるので、温度計で定量的に測れるようにしました。 非接触なので洗う必要も無く一瞬で温度がわかるので便利です。1500円ほどの安いものがあったので購入しました。 リッチェル ひんやりしないおふろマット これだけ商品名指定ですが、発泡スチロールのような素材で軽くて汚れにくいので子供を風呂に入れる時にとても便利です。 www.richell.co.jp 微妙だったもの バランスボール 子供が泣き止まない時に、抱っこしながらバランスボールに乗ると泣き止むという話をネットで見て購入。 実際生後1.5ヶ月くらいまでは効果がありましたが、それ以降は全く泣き止まなくなりました・・・。 電動ハイローチェア バランスボールと違い自動でゆらゆら揺れてくれるので、自分達が手を離せることを期待して購入。 電動のものは数万円するので中古の物をメルカリで買いました。 1ヶ月ほどは効果がありましたが、子供が成長して狭くなったこともあり使わなくなりました。そろそろ手放すかもしれません。 アプリ・サービス系 タスク・スケジュール管理 Trello 主にスケジュールが決まってないタスクを管理するために利用。見た目とスペースを気にしなければ、ホワイトボードか黒板に書き込んだり付箋を貼ったりするほうが見える化出来て良さそうな気はしてます。 trello.com TimeTree 家族でのスケジュール管理・共有に便利。Googleカレンダーは家族で使うには少し見にくかったり使いづらいのですが、これはシンプルで見やすいUIのため使いやすくて妻も愛用しています。 timetreeapp.com 成長記録・共有 ぴよログ ミルクの量と時間、睡眠時間、うんちやおしっこの時間と回数の管理などに使用してます。一見少しごちゃついたUIですが、操作しやすくて成長の記録も残しやすいので便利です。 piyolog.strikingly.com みてね 夫婦だけでなく両家の親(子供の祖父母)にも写真や動画を簡単に共有できて、更に紙のアルバムも毎月作れます。UIもシンプルで使いやすく、子供の成長記録メディアとしては一番良いアプリだと思います。このアプリのおかげで双方の両親ともコミュニケーションを取る機会が自然と増えたので、その意味でも優れたサービスだと思っています。 mitene.us メディア 口コミやCGM系は質問に対して一般の方が書き込んでいるため情報の精度が高くありません。そのため回答ではなく自分と同じ質問をしている人がどれくらいいるかを見て、それが一般的なケースなのかどうかを判断するためだけに使いました(子供の肌荒れが大丈夫か等)。 そのため病気に関する質問など一定の信頼度が求められるものは一次情報を調べるようにしました。 買い物系 メルカリ 育児用品はハイローチェアやベビーカーなど、使う時期は限られているのに高価なものが多いです。メルカリだとものによっては半額以下の値段で買えて、使わなくなったらメルカリで売る事が出来るので育児をするようになってから利用頻度が増えました。 ジモティー 頻度は多くないですが、メルカリと合わせてジモティーも使ってます。ものによりますが、近所の人から送料無しで直接受け取れるのでうまく活用できると便利です。 地域のコミュニティ 意外とあなどれないのが自治体が運営しているコミュニティ。物々交換や情報交換が出来たりママ友を作ったりと、リアルでの交流ができるので長期的に見ると一番重要かもしれません。 ネットスーパー 便利に使ってますが、子供の状況次第で玄関まで行って受け取るのが大変な時もあり、事前の想定よりは使う頻度は多くないです。 自宅環境の整備 GoogleHomeを活用 今まであまり使っていませんでしたが、子供を抱えていると手が塞がるので声だけで操作できるGoogleHomeがとても便利です。 主にスマートリモコン(Nature Remo)をGoogleHomeと連携させてTV・エアコン・照明などを操作しています。 育児動線の整備 育児を効率的に行うための家具の配置、オムツ・タオル・お尻拭きなどのお世話グッズを複数箇所に配置するなど細かい調整を繰り返して最適な動線を作りました。 以上、効率化・自動化についての主なものの紹介でした。 育休中しかまとまって検証できる時間が無かったので、費用が発生するものでも極端に高いもの以外は失敗してもいいからどんどん試す方針にしていました。 育休を終えて 目標の達成度合 毎日てんやわんやだった育休を終えて、気になる目標の達成度合いですが以下のような感じです。 効率化・自動化 出来るだけの事はやったと思います。育児に関してはルーチン作業でも完全に人手を無くす事は難しいものが多く、効率化するよりは作業にかかるストレスを減らす方向で考えるのが良いかもしれません。例えばミルクの調乳で使った温度計は効率化というよりは深夜のぼーっとした頭でも間違いなく適温のミルクを作れるようにする等、いかに疲れない状況を作るかを意識しました。 冗長化 こちらはほぼ達成できたと思います。妻からも「安心して子供を預けて出かけられるようになった」「いつでも預けられる安心感があるので全部自分がやらなくてもいいという気持ちになって、精神的にかなり楽になった」とフィードバックをもらえました。個人的には料理のレパートリーはもっと増やしたかったな思いつつ、目標をクリアした事による達成感が得られて良かったです。 ただ子供はどんどん成長するので、時期によって必要な育児スキル(あやし方など)や気をつけるポイントが変わってきます。 最近は妻が側を離れると泣き出したり寝返りをしそうになったりと、前よりも更に目が離せなくなってきました。 サービスやプロダクトもフェーズが変わると課題が変わってくると思いますが、子供成長もそれと似ていて親自身も学んで成長しながら課題を解決知ていくことが必要だなと感じています。 復帰後 復帰後はフレックス制度を利用して、通常の始業時間より1〜2時間早く出社し就業時間より1時間ほど早く帰る生活を続けています。 帰ってから子供を風呂に入れて、寝かしつけまで終わったら仕事の続き、時間があれば筋トレや勉強をする毎日です。 大変だったこと 育休期間で一番大変だったのは子供が発熱して入院したことです。 小さい身体にいろんな検査をされて可愛そうだったのと、初めての事だったのでどうなるのかとても心配でした。 また、その時点で自宅はそれなりに効率化して育児に最適化された状態だったのですが、病院の環境が整ってない中でのミルクやオムツ替えは大変でした。 幸い1週間ほどで熱も下がり、無事に退院できたので3人で家に帰れた時は本当に嬉しかったです。 よかったこと 育休取れて良かったことはいくつかありますが、その中でも一番は子供の成長を間近で見られたことです。 特に生後間もない時期は変化が大きく毎日のように仕草が変わったりするので、この期間に一緒にいられた事は一生思い出になったと思います。 あとはがっつり育児に向きあった事で大変さがよくわかりました。 世の中のお母さん達が頑張っていることはもっと認知されていいと思います。 まとめ 育児全般を通してエンジニアとしての経験が役に立ったと考えています。 特に 効率化・自動化の習慣や考え方 1on1・チームビルディングなどマネジメントスキル は家庭でも使えるスキルなので、これからも活用していきたいと思います。 子育てはチームで取り組むプロジェクト 子育ては妻と2人でチームを組んで取り組むプロジェクトだと思っています。 目標や期待値をチームで摺合せ、クライアント(子供)の無茶振りに対して力を合わせて取り組み、時にはベビーシッターや家事代行サービスなど外部の力もうまく使いながらプロジェクトを成功させる。 そんなイメージで考えていくと大変な時でも前向きに取り組めるのかなと思います。 子供は不確実性の塊 子供がいると計画どおりにいかなかったり想像してなかった事が起きたりします。 バランスボールで泣き止まなくなった、ミルクを飲んでくれなくなった、何か家事をしようとした時に泣き出す、急に発熱して入院する、etc... イライラしてしまったり、しんどいと感じてしまう事も多いのですが、そんな時はアジャイル開発でよく言われる「変化を受け入れる」を思い出して、変化に対して前向きに取り組む事にしています。 また、子供が泣いてる時は何が原因かを考えて仮説を立てて検証し、結果をチーム(妻)に共有するなど、チームで学んで成長していく姿勢も大切だと思います。 育児に関しては子供が動き回るようになってからが本当に大変になると思うので、これからも妻とチームで協力して解決していきたいと思います!
アバター
最近髪型を変えました。スマートキャンプの今川( @ug23_ )です。 4月です。新卒の時期ですね。やがて令和の新卒と呼ばれる時期がくるでしょう。 自然と自分が新卒の頃に想いを馳せてしまいますね。 新卒の頃って雑用みたいな結構技術的には軽いタスクを振られて 俺ももっと技術的に難しい開発アイテムやりたい!!! とか、 面白くない、もっとコード書きたい という想いを持つ人もいるかもしれません。 でも、楽しい開発ばかりではないんですね。例えば サービス名変えるからこっからここまで直しといて! 的な、文言変更のような見方によっては雑用かもしれないと思うようなタスクをやることになったりします。 ※ 会社によっては、エンジニアに依頼しなくても直したい人が自分でプルリクエストを送って反映できるというところもあるようですね。すごい。 engineer.crowdworks.jp でも「文字列変えるだけじゃん??」と思うかもしれませんが、文言修正はエンジニアとして仕事を進めていく上での大切なことを学ぶチャンスにもとれます。技術的な困難抜きに、仕事の進め方、チームメンバーとしての動き方を学べます。 この記事では「技術的には易しい」が「いろんな人と協力する」必要のあるタスクをやるときに覚えておくとスムーズなことを新卒の自分に聞かせたい気持ちでまとめました。 例として前述の 文言修正 を挙げます。 前提としてWeb系企業での開発をイメージしており、「エンジニア」は主にWeb系ITエンジニアのことを指しています。 なぜやるのかを聞こう 関係者に早めに話そう どこが変わったのかお知らせしよう ついでにきれいにしちゃおう まとめ なぜやるのかを聞こう 軽い修正ならまだしも「こことこことここと…ここまで直してね!」と言われると気が滅入りますね。 「その規則性だったらここも直すんじゃないのかな…?指示漏れじゃないのかな? 「でも敢えて指摘されなかったんだし直さなくていいか」と思ってそのままにしておくと「指示漏れてたからここも直して!」とあとでやり直す 何度も修正を追加されて疲れてきたところで大事なところをミスる みたいな事になりそうです。どうせなら一発で終わらせたいですよね。二度手間は辛いです。 このタスクが発生した理由をちゃんと聞く ことをしましょう。 想像や推測ではなく、相手から聞いた理由なら確実です。 それだったらこうしたほうが楽だ、と気付ける ここの修正するのは難しいからこうしたい、と議論できる それが理由なら支持されていないけどここも直しておいたほうがいい/直さなくていいなどの判断ができる というメリットがあります。 迷ったときはやってしまって、ダメだったらあとでごめんなさいしてもとに戻すのも手です。「許可より謝罪を」です。3Mの社訓らしいです。うっかりやってみたら「ここも直してくれたんだ!ありがとう!」となるかも。 言われた指示を聞くだけでなく、その背景まで聞くと先回りして質問したり、リテイクも減ったりするんじゃないかと思います。 エンジニアなら迷ったときは安全側に倒して、お願いした人を捕まえて聞き出しましょう。わからなかったら捕まえた人を連れてさらに上の人に聞きにいきましょう。 文言変更する理由を聞いてそれでも納得できないときには「エンジニアも変数名で議論するしそれと同じことだ」と思うことにしましょう。 関係者に早めに話そう たとえば、文言修正なら、こんなひとたちが絡んでくるでしょう。 エンジニア 依頼者 デザイナー 役員陣・事業責任者(場合による) これら全員が一斉に同時に関わることはまれだとは思いますが、考えるだけでもこれだけいます。 依頼者とは何度かすり合わせをすると思います。デザイナーさんにはロゴやクリエイティブ等素材の修正依頼をするかもしれません。 文言修正が利用規約やプライバシーポリシーまで範囲を含んだり、文言変更が外部に影響する場合はバックオフィス・経営陣に伺いを立てる必要もあるかもしれません。 前の項でも似たような話でしたが、コミュニケーションの回数が多くなったり手戻りになったりすると時間もかかるしめんどくさい度が上がります。めんどくさいのはつらい。 そして作業依頼・レビュー依頼という形になってくると 相手側のボールになってしまう ので、忘れられたり、遅れたり…。 「こないだのあれいつできます?」というやりとり、やる方もやられる方もつらいですね。 つらい上に我々の完了が遅れていきます。出荷されない製品は価値を生まないのではやく世に出したい。 さっさと終わらせたいという立場に立つなら、早め早めに こういう作業をお願いすることになりそうです。余裕ありますか? このように修正しようと思っているけど問題ないかすり合わせしたいです なんてことを後々聞くことになるから時間をとると思います。たぶん。 というお願いを先回りして言っておきましょう。言っておくだけでもマシだと思います。 相手のためにも自分のためにも先に先に動いておきましょう。 どこが変わったのかお知らせしよう 道路の工事してる人が頻繁に写真を撮っているところをみたことがありませんか? 作業の前後でどうなったか、エビデンスが必要なんですね。 ITの世界の工事をする我々の作業にもエビデンスが必要です。 どこがどう変わったかがわかるように伝えておきましょう。 共有会等があり口頭で伝えられる場合でも変更箇所を伝えられるようにまとめておきましょう。 Gitの変更履歴だけだとエンジニアですらわからないし、他の人に伝えるなら見た目でわかりやすくしたいです。 スクリーンショットだけとってもいいですが、わかりやすくマークアップしていると変更点を伝えやすいです。 自分はSkitchを使うことが多いです。 Skitch - 撮る。描き込む。共有する Skitch 仕事効率化 無料 デザインツールやPowerPointなどを使ってBefore/Afterを載せるのもいいですね。 Excelにエビデンスを貼り付けていく まで必要ではないと思っていますが、わかりやすくする工夫はしたいです。 ついでにきれいにしちゃおう 文言変更とか、1年に1回しかいじらない部分の変更、とか直すのは簡単だけど変更が怖いとか「この作り、突貫工事で作ったんだろうな」というような雑さだったりして気が滅入ったりするかもしれません。 頻繁に変更されるところだと周りの人も「絶対直したほうがいい」などと、我慢できなくなった人が手をつけますが、たまにしか使われないところだと手付かずだったり。 ボーイスカウトルール というのがあります。 原義は「キャンプ場を離れる時は、来たときよりも綺麗にする」ということを指しますが、そこから転じてプログラマの世界では「チェックアウトしたときよりもコードをきれいにしよう」という意味になっています。ボブおじさんのことばが有名です。 myenigma.hatenablog.com 時間が許すなら、汚いと感じた部分を直すことをやってもいいと思います。汚い部分をリファクタリングするのはコードについて深く知れるチャンスなのでおトクです。 もちろん、必要のないリファクタリングをやってバグを仕込んだり、締切に間に合わないとせっかくのチャンスが無駄になってしまうのでバランスをとってください。諦めも大事です。 まとめ 伝えたいことをまとめるとこんな感じです。 自分でコントロールできない部分は早め早めに動きましょう 待ってても進まないので先んじて動いたり連絡したりしましょう やったことを伝えられるようにしておきましょう 余裕があったらきれいにしておきましょう 「サーバのコードをもっとゴリゴリ書きてえ」とか「画面のUIをもっといい感じに良い設計で書きたいんだ」とか、想いはあるかもしれませんがサービス運用してくるとどうしてもこうしたタスクは発生しがちです。 でも、こうしたタスクをうまく回せると周りからの「あいつめっちゃ仕事できる」感はどんどん増していくと思います。3年前の自分に教えてあげたい…。 新卒のみなさんも、とっくに新入の時期を超えたみなさんも参考になったら嬉しいです。 また、弊社でインターンをしていた学生が、Webエンジニアとして成長した話を書いてくれているので合わせて読んでいただくと、今後のエンジニアライフをイメージする手助けになるかと思います! ぜひ読んでみてください! tech.smartcamp.co.jp
アバター
最近社内でElixirをひっそり布教しようとしている、瀧川です。 弊社の一部プロダクトでは、gRPCでGolangアプリケーションを呼び出す構成をとっています。 それを説明するためにハンズオンをしたので、その一連の流れをこちらにもつらつら書いていきます! 内容は、以下の画像みたいなのを作っていこうと思います。 必要なコードは記事中に載せるのでコピペで動くはずなので、ぜひ実際にやってみてください! そもそもgRPCって? 実践 事前にインストールしてください リポジトリを作成しましょう さっそくProtocol Buffersで通信を定義しましょう GolangのgRPCサーバ作りましょう 定義からGolangのソースコードを生成 生成したGolangのインタフェースを実装したサーバを作成 動作確認 Railsを実装しましょう gRPC定義からRubyのソースコードを生成 呼び出すコードをControllerに実装 通しで実行してみましょう 振り返り そもそもgRPCって? Googleが開発している、RPC (Remote Procedure Call)を実現するためのプロトコルになります。 Protocol Buffersというシリアライズのフォーマットを用いて、 通信(関数・引数・返り値)を定義 する仕様となっています。 対応するプログラミング言語も豊富で、その定義をもとにクライアント(呼び出し側)・サーバ(呼び出される側)のソースコードの生成が可能で、安全で効率よくサーバ間通信を行うことができます。 今回の例に当てはめて簡単に表現すると「RailsからGolangのメソッドを普通に呼び出せる」といったニュアンスですね!(雑) 詳しくは以下リンクを参考にしてください。 公式サイト: Guides | gRPC 実践 事前にインストールしてください 以下をインストールしましょう。(versionは2019-03-27時点の筆者のもの) Ruby: 2.5.3 Golang: 1.11.4 protobuf: 3.7.0 リポジトリを作成しましょう アプリケーションとしては、RailsのJSON APIサーバと、GolangのgRPCサーバの2つになりますが、今回は単一リポジトリで実装していきましょう! ざっくりディレクトリ構成 grpc-sample ├ proto │ └ ProtocolBuffers定義 ├ server │ ├ Railsアプリ │ └ lib │   └ protoから自動生成 └ pinger   ├ Golangアプリ   └ lib     └ protoから自動生成 $ mkdir grpc-sample $ cd grpc-sample さっそくProtocol Buffersで通信を定義しましょう 肝であるProtocol Buffersで通信(関数・引数・返り値)を定義していきましょう。 「引数なしでテキストを返すPing関数」の定義が以下のようになります。 簡単ですね! grpc-sample/proto/pinger.proto syntax = "proto3" ; package pinger; service Pinger { rpc Ping(Empty) returns (Pong) {} } message Empty {} message Pong { string text = 1 ; } GolangのgRPCサーバ作りましょう ディレクトリを作成 # cd grpc-sample $ mkdir pinger $ mkdir pinger/lib $ cd pinger 必要ライブラリをインストール # gRPCサーバの実装に必要 $ go get -u google.golang.org/grpc # Protocol Buffersの定義からGolangのソースコードを生成に必要 $ go get -u github.com/golang/protobuf/protoc-gen-go 定義からGolangのソースコードを生成 いよいよ grpc-sample/proto/pinger.proto を元にGolangのインタフェースのソースコードを生成しましょう! 以下のコマンドによって grpc-sample/pinger/lib/pinger.pb.go が生成されます。 # cd grpc-sample $ protoc -I ./proto pinger.proto --go_out=plugins=grpc:./pinger/lib $ ls pinger/lib pinger.pb.go 生成したGolangのインタフェースを実装したサーバを作成 コードの中身としては、以下を実装しています。 生成されたインタフェース(Ping関数)を実装したStructを作成 生成された pinger.RegisterPingerServer 関数でgRPCサーバと実装したStructを紐づけ 5300番ポートでサーブ grpc-sample/pinger/server.go package main import ( "context" "log" "net" "./lib" "google.golang.org/grpc" ) func main() { listener, err := net.Listen( "tcp" , ":5300" ) if err != nil { log.Fatalf( "failed to listen: %v \n " , err) return } grpcSrv := grpc.NewServer() pinger.RegisterPingerServer(grpcSrv, &server{}) log.Printf( "Pinger service is running!" ) grpcSrv.Serve(listener) } type server struct {} func (s *server) Ping(ctx context.Context, req *pinger.Empty) (*pinger.Pong, error ) { pong := &pinger.Pong{ Text: "pong" , } return pong, nil } 実行してみましょう! # cd grpc-sample/pinger $ go run server.go # 2019/03/27 19:53:21 Pinger service is running! 動いてるみたいですね! 動作確認 ちゃんと動作してるか、Golangで呼び出してチェックしてみましょう。 package main import ( "context" "fmt" "os" "../lib" "google.golang.org/grpc" ) func main() { conn, err := grpc.Dial( "localhost:5300" , grpc.WithInsecure()) if err != nil { fmt.Fprintf(os.Stderr, "grpc.Dial: %v \n " , err) return } defer conn.Close() client := pinger.NewPingerClient(conn) req := &pinger.Empty{} pong, err := client.Ping(context.Background(), req) if err != nil { fmt.Fprintf(os.Stderr, "Ping: %v \n " , err) return } fmt.Fprintf(os.Stdout, "Pong: %s \n " , pong.Text) } 実行してみましょう! ※ 別ターミナル等で go run server.go を実行するの忘れずに! # cd grpc-sample/pinger $ go run examples/client.go # Pong: pong 大丈夫そうですね! これでgRPCのサーバ実装は終了です。 若干の おまじないコード はあるかと思いますが、とても簡単に実装はできました。 Railsを実装しましょう 今回はRails5のAPIモードで作成しようと思います。 また、DB(Active Record)は 用意が面倒なので スキップします。 Railsをインストール # cd grpc-sample $ mkdir server $ cd server $ bundle init $ echo 'gem "rails", ">=5.2.2.1"' >> Gemfile $ bundle install # -O でskip active record $ bundle exec rails new . --api -O $ bundle exec rails server # localhost:3000 にアクセスするとおなじみの画面がでますね! gRPC定義からRubyのソースコードを生成 必要なGemをインストールしましょう。 # cd grpc-sample/server # 呼び出し時に必要 $ echo "gem 'grpc'" >> Gemfile # 定義からのソースコード生成に必要 $ echo "gem 'grpc-tools'" >> Gemfile $ bundle install $ bundle exec grpc_tools_ruby_protoc -I ../proto --ruby_out=lib --grpc_out=lib ../proto/pinger.proto $ ls lib # pinger_pb.rb pinger_services_pb.rb tasks 生成されました! 呼び出すコードをControllerに実装 Stub.new でコネクションを貼って、あとはほぼオブジェクトへのメソッド呼び出しと同じように書けばOK。 grpc-sample/server/app/controllers/application_controller.rb require ' pinger_services_pb.rb ' require ' pinger_pb.rb ' class ApplicationController < ActionController :: API def ping pinger_stub = Pinger :: Pinger :: Stub .new( ' localhost:5300 ' , :this_channel_is_insecure ) pong = pinger_stub.ping( Pinger :: Empty .new) render json : { pong : pong.text} end end grpc-sample/server/config/routes Rails .application.routes.draw do get ' ping ' , to : ' application#ping ' end 通しで実行してみましょう Golang gRPCサーバ起動 # cd grpc-sample/pinger $ go run server.go # 2019/03/27 19:53:21 Pinger service is running! Rails JSON APIサーバ起動 # cd grpc-sample/server $ bundle exec rails server # Listening on tcp://localhost:3000 アクセスしてみると... ほしい結果が返ってきました! 振り返り 再掲となりますが、以下のようなファイル構成で構築しました。 grpc-sample ├ proto │ └ ProtocolBuffers定義 ├ server │ ├ Railsアプリ │ └ lib │   └ protoから自動生成 └ pinger   ├ Golangアプリ   └ lib     └ protoから自動生成 やった内容は以下の通りです。 proto ディレクトリにProtocol Buffersの仕様に則した通信(関数・引数・返り値)を定義 Ruby用のソースコードと、Golang用のソースコードを 1. のprotoファイルを元に生成 2. で生成されたGolangのインタフェース定義に則してgRPCサーバを実装 2. で生成されたRubyのgRPCクライアント定義に則して呼び出すコードを実装 シンプルに動かすだけなら、そこまで複雑でなく、コード量も少ないというのが伝わりましたか? APIのインタフェースを考えなくてよく、関数定義をすればよいというのは、開発をしていてもとても考える負荷が減って効率的だと感じます。 今後世の中で一層マイクロサービス化が進むと思いますが、その中でgRPCは重要な技術となると考えているので、みなさんトライしてみてください!
アバター
こんにちは、インフラエンジニアの入山です。 2018年10月のスマートキャンプ入社から、もう少しで6ヶ月になります。 前職はSIerで金融系インフラエンジニアを5年程しており、銀行システムの環境構築やテスト、システム管理、保守などを担当していました。 金融系インフラエンジニアだった前職では、オンプレミスなのはもちろんのこと、サーバーやストレージなどのハードウェアからミドルウェアまで、扱う製品のほとんどが自社ベンダー製だったため、一般的にWeb系エンジニアが使用している技術には全く触れたことがありませんでした。 また、大手で完全分業された中のインフラエンジニアだったたため、 プログラムについては目にする機会もありませんでした。 スマートキャンプに転職した現在は、AWSインフラをメインに、Vue.js Ruby on Railsでフロント〜サーバーサイドの開発まで幅広く担当しています。 そんな私が、Web系エンジニアに転職して触れた7つの技術について、SIerとの違いや触って感じたことなどを含めて紹介します! インフラ寄りの内容ではありますが、SIerからWeb系に転職を考えている方Web系エンジニアをこれから目指す方の参考になれば幸いです! Git tmux,byobu Docker Kubernetes (k8s) Amazon Web Services(AWS) Terraform Vue.js 最後に Git ファイル(ソースコードなど)の変更履歴(誰がどのファイルのどこをいつ変更したか)を記録・管理するための分散型バージョン管理システム リモート(サーバー上)とローカル(自分のPCなど)のリポジトリで別々に変更履歴を記録・管理できる 複数の変更履歴を一つに集約する機能も持つため、複数人での共同開発を効率的に行える GitHubなどのWebサービスと連携することで、チームメンバーとソースコードの共有やレビューなどが効率的に行える IT業界で働き始めて7年目を迎えようとしている私ですが、 今までバージョン管理ツールと呼ばれるものを使ったことがありませんでした。 前職での変更管理は、変更申請書や管理台帳(Excel)、作業手順書などで行っており、資産は自社ファイルサーバー内で作業日付や管理番号と対応したディレクトリで管理していました。 私はバージョン管理ツールを使ったことがないだけでなく、今までチーム開発の経験もなかったので、リポジトリやブランチの概念がわからなかったり、commitやpushをすることに慣れなかったりで、Gitでの管理に順応するまでに結構時間がかかりました。正直、Gitの複雑さは初心者には難しく、ある程度使えるようになるだけでもそれなりの学習が必要だと思います。しかし、使えるようなるとGitはとても便利なツールです。 GitHubと連携したソース共有やPR(PullRequest)レビューなども含めて、チーム開発には欠かすことのできないツールです! Git tmux,byobu 端末多重化(ターミナルマルチプレクサ)ソフトウェア 1つのターミナルウィンドウ上で複数のターミナルを操作することができる 仮想ターミナルの画面分割が可能 起動した仮想ターミナルのデタッチ(切り離し)/アタッチ(接続)が可能 エンジニアであれば必ず操作するターミナル。 サーバーをSSH接続で操作する場合などでターミナルは必須 で、複数のサーバーに接続して並行作業を行うことも多いと思います。 そんな時、 ターミナルウィンドウを別々に起動して作業していませんか? 私は前職で、数多くのターミナルウィンドウを並べて作業してました。 tmuxを使えば、1つのターミナルウィンドウで複数のターミナルを操作できるため、ターミナルウィンドウを複数起動する必要がなくなります。 また、tmux内のターミナルは、タブで切り替えが可能で 好きなだけ画面分割 できて、ターミナルの管理がとても楽になり、効率的に作業を行えるようになります。 Ubuntuでは、tmuxと同じような機能を持つ byobu がデフォルトで入っているため、サーバー上ではそちらを使うのも良いと思います。 GitHub - tmux/tmux: tmux source code http://byobu.co/ Docker コンテナ型の仮想環境を作成、管理、実行するためのソフトウェア OSやミドルウェアなどを含む仮想環境のシステム全体をイメージという単位で保存・管理できる 各種OSだけでなくOSSを含んだイメージがレジストリで公開されており、様々な仮想環境をすぐに利用できる 仮想環境の構成管理(ミドルウェアのインストールや各種環境設定など)をコードで行うことができる(Infrastructure as Code) SIerで触れた仮想化技術は、VMwareやHyper-Vなどの所謂仮想マシンと呼ばれるもので、高性能なサーバーの上に仮想ハードウェアを含む複数の仮想環境を動作させ、企業の物理サーバー台数や運用コストを削減することを主目的としたものでした。 それに対して、Dockerはコンテナと呼ばれるプロセス単位で仮想環境を実行するため、リソース使用量が少なく、PCでも動作させることができます。また、 仮想環境の構成をコードで管理できるため構成管理や共有が容易 で、Dockerさえ入っていれば異なる環境上で同じ構成の環境を動かすことができます。 開発では多くの現場で使われている技術で、 ローカルPCへの開発環境構築 などでよく利用されます。OSやOSSのイメージが豊富に公開されており、 様々な環境をすぐに動かすことができる のもとても便利です。 Empowering App Development for Developers | Docker Kubernetes (k8s) 複数の仮想コンテナを一元的に管理するコンテナオーケストレーションツールのデファクトスタンダード コンテナ化されたアプリケーションのデプロイ、スケーリング、管理などを自動化 コンテナベースのインフラを本番環境に実装するために必要な機能を提供する GCP、AWS、Azureなどがマネージドサービスを提供しており、クラウドサービスとの連携が可能 私が入社して一番始めに取り組んだのが、Kubernetes(以下、k8s)での新規プロダクトの基盤構築でした。前述したDockerの知識もほとんどなかったので、わからないことだらけで大変でした。私にとってWeb系転職の洗礼とも言えるk8sですが、入社からの6ヶ月間でもや書籍の情報がかなり増え、導入事例も多く目にするようになりました。 個人的にk8sで一番良いと思うところは、 インフラ(ホストOS)とアプリケーションを分離できる ことだと思います。というのも、アプリケーションに必要なものが全てコンテナで起動するため、コンテナを動作させる**ホストOSにはアプリケーションに関するものを一切導入する必要がなくなります。 これにより、ホストやミドルウェアなどのバージョンによる影響を受けなくなるだけでなく、ローカルのDockerで動作検証したものをそのまま本番運用することが可能になるため、効率的に開発が行えます。 マネージドサービスの提供も追い風となり近年急激に伸びている インフラ関連で今一番注目されている技術 です! Kubernetes Amazon Web Services(AWS) Amazonが提供するクラウドコンピューティングサービス レンタルサーバーやデータベースをはじめ、IoTシステムの構築、機械学習など、様々なサービスが利用できる ユーザーはサーバーなどの設備を一切用意する必要がなく、インターネットに接続できる環境があれば全て利用可能 必要なコストはサービス利用料のみで、個人でも気軽に利用できる(一年間の無料枠も有り) 前職で担当していた金融系のシステムでは、クラウドサービスでのシステム導入は少なく、 オンプレミス以外の選択肢はない 環境でした。 Web系への転職を決めるまではクラウドサービスを触ったことがなく、クラウドサービスに対する漠然とした 不信感や抵抗感 を持っていました。 そんな私が初めてAWSを使って環境構築をしたときは、オンプレミスとクラウドの構成や設定の差異に混乱しました。 しかし、一通り環境構築した後は、AWSでの環境構築の手軽さと早さに感動しました。オンプレミスでシステム導入をする場合は、 サーバーを触れる状態になるのに数ヶ月必要 なのが当たり前でした…。 わずか数分でサーバーが起動して、色んなサービスが手軽に使えて、スケールも柔軟で、個人でも手軽にサーバーが利用できるというのは とても感動しました 。 最近では政府機関や金融機関へのクラウド導入事例も増えており、オンプレミス運用をしている企業が減少しているため、 クラウドサービスに触れる機会はかなり多い と思います。 クラウドならアマゾン ウェブ サービス 【AWS 公式】 Terraform インフラリソースをコード(テンプレートファイル)で構築・管理するためのツール AWS・GCP・Azureを始め多くのクラウドプロバイダやDNSimple, Herokuなど多くのPaaSに対応 インスタンスやネットワークなどの低レイヤーから、DNS や SaaS など高レイヤーまで幅広く管理可能 コードの差分を判斷した上で最適な実行計画を練ってインフラを更新 前職でのインフラ構築は、設計書に基づいて手順書を作成し、手順書に従ってGUIやCLIで構築していくものでした。そのため、インフラをコードを使って構築・管理したことはなく、こういった技術の存在も知りませんでした。 プロダクト開発においてインフラは、同じような構成の環境が複数必要となるため、AWSでサーバーを立てて、データベースを作って…など、同じような内容の作業を各環境で繰り返し行う必要がありす。また、複数の環境を作るにはそれなりの工数が必要で、手作業では漏れやすい箇所もたくさんあります。 Terraformでは構築作業を コードで一括構築・管理できる ため、元となる構成のコードを一度書いてしまえば変数を変更するだけで、同じ構成の別の環境を構築できます。 インフラの構成をコードで管理できるため、インフラの構成や構築方法の属人化を防いだり、Githubで扱うことでバージョン管理やレビューをしたりできます。より簡単に扱えるようになり、インフラ変更作業に伴う事故リスクを減らすこともできます。 コードの書き方に慣れる必要はありますが、クラウドサービスのインフラ構成管理にとても便利なツールです。 Terraform by HashiCorp Vue.js WebアプリケーションのUI(ユーザーインターフェース)を構築するためのJavaScriptフレームワーク シンプルで自由度が高く、他のフレームワークと比べて軽量で速い SPA(Single Page Application)開発に使われることが多い 学習コストが低い 前職でプログラムを触ってなくインフラエンジニアの私が、 フロントエンド開発を行う日が来るとは正直思っていませんでした。 そんな私が現在開発中のプロダクトで初めて触ったのがVue.jsです。 JavaScriptすらわからない状態からスタートしたので、他のフレームワークと比べてどうかについては正直わかりませんが、日本語の公式ページが充実していて勉強しやすいと感じました。JavaScript HTML CSSをある程度勉強した上で、公式ページをしっかり読めばそれだけでもかなり理解度は上がると思います。 私の場合は、公式ページに加えて初心者向けの本を一冊読んだことで、大分理解できるようになりました。 コンポーネントライブラリを使うことで、デザイン性のある高機能なUIを簡単に実装できるのもフロント開発初心者としてはとっつきやすくてよかったと思います。 Vue.js 最後に SIerはレガシーな技術や文化が根強く残っていることが多いですが、Web系の企業ではモダンな技術や文化が積極的に活用されています! 今回は、SIer出身の私が系に転職して触れた技術の中から7つを紹介しましたが、これらの技術は Web開発では一般的に使われているもの ばかりです。 7つのWeb技術は共通して、 再利用性・利便性・安全性 を高めていて、開発効率向上には欠かせない技術です。 紹介した中にもし知らない技術があったら、是非触ってみることをおすすめします! 現在、インフラでは Amazon EKS 、フロントでは Atomic Design を中心に挑戦中です。 SIerからWeb系への転職は技術的なハードルが高いイメージがありますが、Web系では新しい技術が多く、それらの技術に触れることはとても楽しいので、悩んでいる方は恐れずに挑戦してみてください! ※ タイトルはここからとりました😙 7つの言語 7つの世界 作者: Bruce A. Tate 発売日: 2011/07/23 メディア: 単行本(ソフトカバー)
アバター
Terraform 0.12がbetaになりワクワクしていたら、それより前にTerraformのAWS Providerの2.0.0がリリースされていたことに気づいて焦る笹原です。 いきなりですが、AWSを使う際に、多要素認証(MFA)をかけるのは必須ですよね! また、IAM UserとしてログインするAWSアカウントから、Assume Roleして実際に作業するAWSアカウントに入ることも多くなっていると思います。 そういった環境だと、Assume RoleをするのにMFAを必須にすることもあると思います。 今回はそんなMFAをした上でAssume Roleする必要があるAWSアカウントでもTerraformを実行するために、aws-vaultを使った話をしようと思います! 背景 aws-vaultとは aws-vaultとTerraform aws-vaultの設定 Terraformの実行 終わり 背景 MFAをした上でAssume Roleする必要があるAWSアカウントでTerraformを実行することはTerraform上ではサポートされていません。 公式のドキュメントでMFAについて書いてあるものの、非推奨なTerraform Enterprise (Legacy)の機能です。 https://www.terraform.io/docs/enterprise-legacy/runs/multifactor-authentication.html また、AWS ProviderのIssueもOpenなまま議論されており、Providerに対してインタラクティブな認証を提供する予定はないと言われています。 Doesn't ask MFA token code when using assume_role with MFA required · Issue #2420 · terraform-providers/terraform-provider-aws · GitHub Issueの中で、MFA & Assume Roleな環境でTerraformを実行する方法が何個か紹介されています。 基本的には、AWS STSを利用してMFA & Assume Roleな環境への一時的なクレデンシャルを取得することになります。 そのうち、aws-vaultを使う方法が使いやすいなと感じたので紹介します! aws-vaultとは aws-vaultはAWSのAccess Keysをセキュアに保管するためのツールです。 github.com MacのKeychainなどOSが提供するセキュアなキーストアにAccess Key IDとSecret Access Keyを保存しておき、実際に利用するときには、一時的なクレデンシャルを取得した上でshellに渡してくれます。 一時的なクレデンシャルの取得はAWS STSを利用して行ってくれるのですが、MFAやAssume Roleにも対応してくれています! aws-vaultとTerraform 認証及びAssume Roleの手順を簡易的に図式化するとこんな感じです。 Access Keysの認証 ----> MFAの認証 ----> Assume Role 実際にMFA & Assume Roleな環境でaws-vaultとTerraformを利用するときに、それぞれの役割をどこまでにするかで2つ方法があります。 MFAまでをaws-vaultで、Assume RoleからはTerraformで行う Assume Roleまで全てaws-vaultで行う 今回はAssume Roleまで全てをaws-vaultで行うようにしてみます。 aws-vaultの設定 まず、aws-vaultで一時的なクレデンシャルを取得できるようにしましょう ~/.aws/config はこんな感じになります。 defaultがIAM Userの存在するアカウント、mfaがMFA & Assume Roleで入ることになるアカウントです。 [ default ] region = ap-northeast -1 [ profile mfa ] region = ap-northeast -1 mfa_serial = arn:aws:iam:: 000000000001 :mfa/sasahara role_arn = arn:aws:iam:: 000000000002 :role/Role source_profile = default aws-vaultを利用するので、 ~/.aws/credentials にAccess Keysを設定する必要はありません。 この状態でaws-vaultを使ってキーチェーンにAccess Keysを登録していきます。 $ aws-vault add default Enter Access Key ID: AKXXXXXXXXXXXXXXXXXX Enter Secret Access Key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Added credentials to profile " default " in vault これで、aws-vaultで一時的なクレデンシャルを取得する準備は整いました。 試しに、一時的なクレデンシャルを使ってawsコマンドを叩いてみましょう。 $ aws-vault exec mfa -- aws s3 ls Enter token for arn:aws:iam::000000000001:mfa/sasahara: 000000 ... 多要素認証のtokenの入力が求められるので入力すると、s3のバケット一覧が取得できますね! Terraformの実行 TerraformのAWS Providerに関して必要な設定は以下です。 実行時の環境変数からクレデンシャル情報を取得するため、profileなどの指定は必要ありません。 provider "aws" { region = "${local.aws_region}" version = "~> 1.60.0" } 実際に実行してみましょう。 $ aws-vault exec mfa -- terraform Enter token for arn:aws:iam::000000000001:mfa/sasahara: 000000 一時的なクレデンシャルの取得とshellへの受け渡しをaws-vaultがやってくれるので、簡単に実行できますね! 終わり 今回は、MFA & Assume Roleな環境でTerraformを実行することを目的としてaws-vaultを紹介しました。 aws-vaultはそれだけでなく、認証情報をセキュアに保管してくれるツールなので、Terraformを使わなくても入れて損はないと思います!
アバター
はじめまして。スマートキャンプでエンジニアマネージャーをしています林です。 私はエンジニアマネージャーをやっているのですが、 エンジニアではありません。 新卒の頃に1年程やっていたことがあるのですが、それからもう10年程たっていて、今となってはコードは全くかけません。 スキルセットは マーケター で、前職では医療機器のネット販売で年間数億の売上を上げていました。 スマートキャンプにはマーケターとして入社しましたが、マーケティングで成果を最大化するためにプロダクト改善に着手し、エンジニアに協力してもらっていたところマネージメントも担当するようになり、エンジニアマネージャーを務めるようになり今日に至ります。 お伝えしたい事の概要 私は現在、エンジニアのチームとWEBマーケティングのチーム、コンテンツマーケやニュース等記事作成のチームをみています。 またマーケターという仕事柄様々な部署と関わりながら仕事をしていますが、そういう立場からエンジニアをみると、 共通して凄いなと思う事 が多々あります。 しかしエンジニアのメンバーと話していると、みんな それが当然だと感じている様子で、多分凄さに気づいていません。 弊社のこの様子からすると、自分たちの美点に気づいていないエンジニアが世の中にはわんさかいる気がしています。 そこで今回は、私が エンジニアにたいして「すごい」と思っていることを共有 させていただこうと思います。 一人でも多くのエンジニアが自分自身の凄さに気づいて、より自信を持ってくれたら幸いです。 お伝えしたい事の概要 知らない事でも学習して実装してしまう ちゃんと詳細まで詰めて考え、実現できる システマチックに動いてくれる 正しく理解してくれる エンジニア業務以外でも学習能力がめちゃ高い 最後に 知らない事でも学習して実装してしまう エンジニアのこの要素は本当に凄いといつも思ってます。 私はもともと、優秀なエンジニアは知識が豊富で、 既に身につけた膨大な知識・スキルを使ってシステム開発をしている ものと考えていました。 マーケターの場合はその要素が強く、ある程度経験をつんでその引き出しを使って施策を考えていくというのが一般的なアプローチです。 しかし弊社のエンジニアの仕事の仕方をみていると、もちろん知識は豊富なのですが、何かを実装する時は 過去の知見にとらわれず、常に新たな最適な方法を模索 しています。 常に新しい知識を学びながら、これまで全く知らなかった事をキャッチアップしながら実装していっている事がわかったときは衝撃的でした。 しかも、きちんとコミットした納期を守ってきます。 知らない技術をキャッチアップしながら開発して、どうやって納期通りにアウトプットを出せるんだ! と、いつも魔法のようだと思います。 もちろん、マーケターの仕事でも未知の技術に挑戦することはあるのですが、エンジニアのように事あるごとにという頻度ではありません。 それは非常に難易度の高い事だと思いますので、常にやっている事は本当に凄い事です。この点は本当に誇りに思ってほしいです。 ちゃんと詳細まで詰めて考え、実現できる エンジニアの中にいるとそれが当たり前になってくるから不思議ですが、 世の中で細部まで詳細をつめて考えられている人は少ない 印象があります。 物事の実現のためには、それを実現するためのパーツが何かの全体像を把握し、そのパーツ1つ1つを完成させ、さらにそれを正しく組み立てていく必要があると思っています。 エンジニアはこれをするのが非常に得意な印象がありますが、一般的には、パーツが足りなくて実現できないケースや、パーツを作れないケース、それぞれのパーツについて詳しくしらないためにパーツの組み立て方、組み立てる順番を間違えるケースなどが多々あり、思ったように形にならない事がよくあります。 エンジニアは詳細をつめて考える習慣がるので、企画の会議などに参加してもらうと 議論が終わった後考慮漏れなどを的確に指摘してくれ議論を深めてくれる 事がよくあります。 こういった詳細をつめて形にする思考力は他の部署のメンバーと比較して非常に高いといつも感じるので、そこにも自信を持ってほしいです。 システマチックに動いてくれる この内容は当たり前の事のようですがエンジニア以外だとなかなかこれがうまく行かないケースがあります。 エンジニアの場合は、「こうする」と決めた場合その通りに運用してくれる事がほとんどですが、他部署のメンバーの場合は、 決めたことが履行されないとか、決めたとおりに履行されない 、という事がそこそこおこります。 決めた通りに動かないと、計画どおりに進まなかったり、解決するはずだった問題が解決されないなど色々と困る事がおきます。 またそういうメンバーに対してはその後のモニタリングが必要になるなど余計に工数がかかってしまうという問題もあります。 一方でエンジニアの場合は決めた事にたいして決めたように動いてくれれる事が多く、思ったとおりに物事が進む事が多いです。 この要素は、特に マネジメントする立場からすると非常に有り難い ことです。 正しく理解してくれる システマチックに動いてくれるのところで「決めたとおりに履行されない」というケースについて書きました。 上記のずれ原因として、「正しく理解する」という点の精度が原因の1つだと思っています。 こちらが要望を伝えた時に内容の理解に曖昧な点がある場合、エンジニアは つっこんで質問し、認識に相違がないかどうか確認した上で動いてくれる ケースが多いです。 一方で他の部署のメンバーだと個々が受け取った受け取り方で行動をするケースが多く、そこで伝え手と受け手で認識がずれてしまうため、決めたとおりに履行されないケースが起こっていると感じる事があります。 ここは伝え手の問題もあるので私も気をつけないといけないところですが、エンジニアの場合は 理解が不十分だと思った場合はエンジニアから質問をして意識合わせをしてくれる ケースが多く、認識違いが起こりづらい印象があります。 伝え手側から相手の理解度を把握するのなかなか難しいところもあるので、曖昧な点がある場合に質問で深掘ってくれるエンジニアにはいつも感謝をしています。 エンジニア業務以外でも学習能力がめちゃ高い 業務で常に学習しているエンジニアは、「学習能力」が非常に高い印象があります。 例えば、エンジニアの一人にロードバイクについて熱く語られた事があります。 自転車本体から、走り方から、メーカーから、詳細な部品から、 その部品のシェアをあまり知られていない日本企業がほぼ独占している話 など、ロードバイクについて幅広い視点でかなりの事を教えてもらいました。 まるで大学時代から部活でやってました、くらいの勢いだったのですが、その時彼は ロードバイクに興味をもってまだ1ヶ月程 という状況でした。 当時彼は重めのプロジェクトを動かしており、全く暇ではなかったので、いつの間にそんなに情報を仕入れたんだと驚愕しましたが、このようにエンジニアには、 とんでもない学習能力 を持っている事をよく見せつけられます。 この事例からも、エンジニアは日々の業務で「学習する」という能力自体を磨いてるものと感じています。 その能力においてかなりの競争力を持っていると思いますが、その能力はどんな仕事をするにも重要で活用できる能力だと思います。 その能力を持っている時点でどこでも通用する競争優位性をもっている と思うので、そこも誇りにもってほしいなと思います。 最後に 私はエンジニアでは無い立場でエンジニアのマネージャーをやらせていただいていて、エンジニア凄いな、羨ましいなと思うことが多々あります。 今回は凄いなと思うポイントを書かせていただきましたが、 エンジニアの方々は本当に素晴らしい能力を持っている方が多い と思います。 ただしエンジニアの中にいるとそれが当然になっていて気づけない事もあると思うので、このブログをきっかけに自分自身の素晴らしさに気づき、より自信をもって、よりよい開発をしてもらえたら嬉しいです。
アバター
こんにちは、スマートキャンプでエンジニアインターンをしている中村ノアです。 ついに今日が最終出社日となりました。 インターンを始めたのは去年の10月からですが、時間の流れの速さに戸惑うばかりです。 インターン中は新規サービスの開発を担当していました。 チームでのプロダクト開発で得られたスキル・経験 を改めて振り返ってみようと思います。 あっという間でした...!!! 自分自身について 成長したスキル Vue.jsを使ったフロントエンド開発スキル Ruby on Rails を使ったサーバサイド開発スキル 共通認識、チーム開発のための行動力 見積もりの勘、どれだけ正確に見積もりを出せるか 開発者のバイアスの自覚、答えはユーザーが持っているということ プロダクトの市場での立ち位置、競合との差別化への意識 ソフトウェア開発と不確実性、持続可能な開発への意識 まとめ 自分自身について まずはざっくりとしたプロフィールです。 19年卒(現在、学部の4年です) インターンを始める前までは趣味でフロントエンド開発をやっていました 4月からWeb系エンジニアとして就職することが決まっています。 しかし、それまでプログラミングは独学のみだったこともあり、チームでの開発もしてみたかったところで、偶然紹介していただいたスマートキャンプでエンジニア長期インターンをさせていただきました。 成長したスキル 当初はフロントエンド技術くらいしかやらない想定ではじめましたが、実際に今考えると、フロントエンド技術、バックエンド技術、働き方の軸で 圧倒的成長できた 実感があります。 Vue.jsを使ったフロントエンド開発スキル 担当しているサービスはVue.jsを使ったSPA構成になっています。 テンプレートはpug、スタイルはSCSSで書いています。 個人開発では触れないような一定規模以上のコードベースならではの、 プラグインの開発やコンポーネント設計、Store(Vuex)の実装 などいろんなことを経験できました。 趣味で勉強していたことを上手く実際の開発にも活かせたりしてよかったです。 Vue Pluginの自作 アプリケーションの Notification 機能をVue Pluginとして自作しました。 Notification もちろんOSSのライブラリも多くありましたが、どうしても開発中のサービスとトーン&マナーが合わなかったため独自のコンポーネントを実装する必要がありました。 開発にあたって既存のライブラリのコードリーディングも行い、良いところを取り入れつつ拡張性の高い・シンプルな Plugin が作れたと思います。 どのような呼び出し方(API)だと Plugin の使用者が楽かということを考える必要があり、普段とは異なる思考力も培うことができました。 また Vue Plugin という形で作ったので再利用性も高く、このまま1つのライブラリとして公開することも可能では!?と思えたことも印象深いです。 <transition-group> を使ったリッチなアニメーション 「動けば良い」というボーダーからさらに踏み込んで、どうすればユーザーが使いやすいかというところまで考えながら細かいところまで作り込むことができました。アニメーションもその1つです。 Vue.js の <transition-group> を使うことで、よりシームレスな表現ができました。 transition-group ユーザーの操作の「結果」だけを唐突に表示するのではなく「過程」も含めて認識してもらうことで、アフォーダンスを高めることができました。 ロード状態の管理と表示 ユーザーにとって最もストレスフルな瞬間の1つが「ロード中」であることは間違いありません。 担当したサービスでは大量のデータを取得する必要があるページが複数あり、ロード状態の管理とその表示が必要不可欠でした。 そこで状態管理のためのライブラリ vue-wait を導入し、API ごとにロードの状態を Vuex で統括できるよにしました。 ロード中の表示には vue-content-loader を使い、Facebook や Slack のようなロード表示を作りました。 Loading 表示 このようなロード表示は使いどころが難しいですが、単純なスピナー(くるくる回る表示)よりも体感時間がかなり短くなるので効果的だと感じました。 Ruby on Rails を使ったサーバサイド開発スキル Ruby on Rails 5 をAPIモードで使っており、レスポンスのJSONはjbuilderを使って生成しています。 個人開発では firebase を使ったいわゆるサーバレスな開発をしていたこともあり、サーバサイドの開発経験はほとんどありませんでした。 実際、Ruby on Rails はおろかRuby すら全く触ったことがないという有様でした。 チームの方針として、機能ごとにタスクを割り振る(サーバサイド・クライアントサイドまとめて)というやり方で分担しているため、 スマキャンでのインターンは未知の領域にチャレンジする良い機会 でした。 インターン当初は生まれたての子鹿のようにサーバサイドのコードに触れていましたが、今では苦手意識を持たずに開発できるようになりました。 サーバ側も作れるようになった結果、処理をサーバとクラアントどちらで行うか選ぶということができるようになりました。改めて振り返ってみて、これは大きな収穫だと感じます。 クラアントでは難しい処理もサーバでは簡単だったり、その逆もあり得るので、 状況に応じて最適な選択ができるというのは開発をする上での大きなアドバンテージ になります。 また、自分の守備範囲が広がることで出来ることも増え、対応力を磨くことができました。 共通認識、チーム開発のための行動力 担当サービスでは、ユーザーが3つの権限に分かれています。 権限によって必要とする機能も微妙に異なっており、どうやってコンポーネントを再利用すれば良いか頭を悩ませることが多いです。 なぜか僕の机の上に置いてあったAtomic Designの本を目にし、Atomic Designを導入すれば解決できるのでは...?!と思いました。 Atomic Design ~堅牢で使いやすいUIを効率良く設計する 作者: 五藤 佑典 発売日: 2018/04/25 メディア: 単行本(ソフトカバー) その思いをメンターに相談したところ、全社内エンジニアに向けて勉強会を開くことになりました。 内容は、Atomic Designの概要、それぞれのレイヤーの責務・制約、実際のコンポーネント設計への適用を話させてもらいました。 結果として、社員の賛同が得られ、プロダクトにもAtomic Designを導入することができました 🎉 勉強会のようす Atomic Designを実践してみて分かったこととして、「Atomic Designは厳格なルールではない」ということが挙げられます。 Atomic Designはあくまでコンポーネントを分割・分類する指標の1つであり、コンポーネントの粒度を表す共通言語を提供しているに過ぎません。 大事なことは、 Atomic Designが提供する用語に対する共通認識をチーム内で形成する ということだと感じました。 見積もりの勘、どれだけ正確に見積もりを出せるか 仕事としての開発と個人開発との最大の違いは、締め切りがあるということです。 仕事を頼まれた時には「いつまでに終わらせれば良いか」ということを意識するようになりました。 正確でない見積もりは、周りの人に迷惑をかけるだけでなく、自分の首をも絞めてしまいます。 僕自身、かなり楽観的な見積もりをしてしまいがちなので、工数の見積もりは意識して慎重に考えるようになりました。 この半年間の経験から、 自分の直感の1.5倍の時間 が実際の作業に必要で、最初の見積もりは当てにならないということが分かりました。 逆に、 自分の直感の1.5倍の時間 をあらかじめ想定しておくことである程度正確な見積もりを出せるというメソッドも得られましたが、これからは直感と現実のギャップを埋めていきたいです。 開発者のバイアスの自覚、答えはユーザーが持っているということ 幸いにもインターン中に、担当しているサービスの社内リリースと社外リリースの両方を体験できました。 この2つのイベントを経て、自分が作っているものの目的・意義を再確認する良い機会になりました。 リリースを機にサービスがより活発に使われるようになり、多くのフィードバックを貰いました。 エンジニアはサービスの内部構造まで知っているだけに、サービスの捉え方に少なからずバイアスがかかってしまいます。 ニュートラル視点からの評価をしてもらうことが良いものを作る際には必要不可欠 だと感じます。 開発中に想定していた使い方と、実際のユーザーの使い方が異なるということはよく起こります。その際に、ユーザーの考えに寄り添う姿勢が大事だと学びました。 サービスが複雑になるにつれて、作る側と使う側で認識を完全に一致させることは難しく、擦り合わせを行うためにも フィードバックと改善のサイクルを回さなくてはいけません。 今回担当したプロダクトは、社内にもユーザーがいるためフィードバックを貰いやすいという利点があり、ユーザー目線でのサービス開発をする上で大いに参考になりました。 プロダクトの市場での立ち位置、競合との差別化への意識 幕張メッセで開催された「営業支援EXPO」にスマートキャンプも出展しました! EXPOのようす 多種多様なサービスが一堂に会している様子は圧巻でした。 普段、会社の中で開発を行なっているだけだとあまり意識していませんでしたが、 自分が開発しているサービスにも競合が存在しているという当然の事実を再確認しました。 他のサービスと比較して、どのような点で優れていて、どのような点で改善の余地があるかということを客観的に捉える良い機会でした。 会場では実際に他社のサービスのデモに触れる機会があり、とても良い刺激を受けました。 ソフトウェア開発と不確実性、持続可能な開発への意識 「リファクタリングの日」を開催しました。 通常の業務中ではなかなか手が回らないシステムの改修をチーム全員でまとまって行おうという企画です。 イベント感を演出するために、オフィスから出て街へ繰り出し門前仲町のレンタルスペースで丸一日リファクタリングを行いました。 リファクタリングの日のようす ソフトウェア開発を続けていく中で不確実性は常にあり、最初は上手くいっていたやり方もいずれ変化を迫られるということを目の当たりにしました。 この日は Vuex の Store 周りを一気にリファクタリングし、より Vuex らしいやり方に統一するという作業をしました。 この改修作業を経て全体の見通しが良くなったと思いますが、もしかすると再びリファクタリングが必要になる日が来るのかもしれません。 新機能の開発を一旦ストップして現状のシステムを改修する ということは、開発スピードを落とさないためにも必要不可欠です。 数ヶ月に1度「リファクタリングの日」を設けるというやり方は、長期的な視点から考えてもとても良いやり方だと思いました。 実際このイベントのおかげで、システムだけでなく自分の気分もリフレッシュでき、普段の開発に一層集中して取り組むことができました。 まとめ 記事中に書いたこと以外にもたくさんの思い出があり、次のようなことも学べました🤗 チーム開発、プロダクト開発フローへの理解 GitとGitHub flowの理解 他の人にコードをレビューされる経験、他の人のコードをレビューする経験 「コードを憎んで人を憎まず」の精神 パフォーマンスへの意識 セキュリティ対策 ゲーム(スマブラ、ぷよぷよ、ボンバーマン、FIFA) スマートキャンプでエンジニアインターンをやってみようという決断は 大正解 だったな、と改めて思います。 サーバサイド・クライアントサイド両方の開発経験を積めただけでなく、チーム開発のノウハウやユーザビリティへの意識など、多くの学びを得られました。 また、スマキャンの掲げるバリューである 「SOCS」 (Smart thinking, Ownership, Collaboration, Speed)という考え方は僕の行動指針としてこの先変わらず在り続けるものだと感じています。 半年間という限られた時間でしたが、圧倒的な成長を遂げられたという実感があります。 スマートキャンプでのエンジニアインターンは、 「ものづくり」という総合格闘技をしたい人 におすすめです! 具体的には以下に列挙しておきます。 モダンなフロントエンド開発をしてみたい人 アットホームなチーム開発したい人 フロントエンドからバックエンドまで一気通貫で開発したい人 ユーザファーストなプロダクト開発に携わりたい人 改めてスマキャンの方々には感謝の気持ちでいっぱいです!本当にありがとうございました!!!
アバター
はじめに はじめまして。スマートキャンプでエンジニアをしています井上です。 皆さんパフォーマンス改善でうまくいかなかった経験や失敗した経験はないでしょうか? 今回は自分の経験からパフォーマンス改善に役立ちそうなポイントを5つご紹介したいと思います はじめに 1. 解決すべきパフォーマンスとは 2. パフォーマンス改善の目的を明確にする 目的 対象 対象 開発の指標 SEOのための指標 サービス改善のための指標 3. パフォーマンス改善と普通の開発プロセスとの違い 実装より計測のほうが時間がかかる 工数は長めに見積もる 4. ボトルネックを上手に知るためには インフラ的改善 バックエンド側改善 フロントエンド側改善 計測方法を知る 一般的なツール Railsの計測用のGem 5. 上手にパフォーマンス改善するには 最後に 1. 解決すべきパフォーマンスとは パフォーマンスにはいくつかのパターンが存在します。 この中で自分がどのパフォーマンスを改善するかを知ることはどの計測が必要かにつながる大事なポイントの1つです スパイクに対するパフォーマンス ⇒ 1分間などで大量のアクセスにどれだけ耐えられるかのパフォーマンスが求められます。 ストレスに対するパフォーマンス ⇒ アクセス数などが徐々に増えていった時にどれだけのアクセス数まで耐えられるかというパフォーマンスが求められます。 サービスに対するパフォーマンス ⇒ サービス・サービスの機能に対するパフォーマンス、すなわちUXやCVなどに必要なパフォーマンスが求められます 2. パフォーマンス改善の目的を明確にする 通常のタスクと同じように、パフォーマンス改善にも明確な理由が必要です。 スピード改善によりUXの向上、SEOの向上などそのページのどの部分を改善することでどんな効果があるのかを定義してパフォーマンス改善が何の目的でやっているかを明確にしましょう。 私は以下のようにまとめることが多いです。 目的 本来のパフォーマンス改善の目的を記載します。 対象 パフォーマンスを改善すべき対象ページ・機能 対象 db index、キャッシュなどの改善手段 目的: SEO向上のためのスピード改善 対象: 記事ページ 手段: レンダリングが遅い箇所をキャッシュ対応 開発の指標 パフォーマンス改善の指標は測定するツール・パフォーマンス改善の目的により異なります SEOのための指標 Google Page Speed Insight - Googleが提供しているページ速度の点数 サービス改善のための指標 New Relic APM Transactionやその内訳等 Chrome 開発者ツール Perfomenace、Networkタブなどでの計測時間 自分たちがなにを改善したいかによって、ここで採用するべきKPIは変わるでしょう。 3. パフォーマンス改善と普通の開発プロセスとの違い 推測するな計測せよ(Mesure, Don’t Guess) という言葉があるようにパフォーマンス改善作業のほとんどは計測です。 そのため、パフォーマンス改善と通常の開発を同じ考えでやるとハマりがちです。 実装より計測のほうが時間がかかる 大きく通常の開発と異なるのは、 実装の時間より計測のための時間の方が圧倒的に長くなる というところです。 改善しては計測を繰り返して効果がある手段を特定するためにほとんど時間を費やします。 工数は長めに見積もる 計測している過程で原因が想定と異なる場合、再度計測が必要になるなど意外と時間取られることが多いです。 想定工数 + 1,2日を目安に見積りしましょう。 4. ボトルネックを上手に知るためには どんなに正しいプロセスをたどってもボトルネックを正しく把握できなければ意味がありません。 また、ボトルネックはさまざまなツールから出したデータを元に仮説を立てて検証していく必要があります。 パフォーマンスの課題には下記のような問題と解決策があります。 インフラ的改善 HTTP2対応 HTTP pipeliningが有効な場合があります 圧縮対応 バックエンド側改善 単純に時間のかかる処理をリファクタリングする N+1問題 データベースのインデックスについて見直す フロントエンド側改善 JS, CSSを圧縮する レンダリングブロックの解消 画像のサイズ圧縮 WEBフォント表示・読み込みの非同期化 これ以外にもパフォーマンス問題はありますが、パフォーマンス問題がこの問題のどれに当たるのかを特定するのが計測の目的です。 問題の種類を把握しておくことで、1つの計測に失敗した場合の次のアクションが間違いにくくなります。 計測方法を知る これらを1つ1つ確認していると膨大な時間がかかります。 必要なのは計測ツールの特性を知り適切な計測を行い、それにより知ることができるデータが何かを把握することです。 私が計測でよく使うツールの一部をご紹介します。 一般的なツール NewRelic APM slow queryやactionごとの処理にかかった時間などが把握でき、どの処理が鈍重かを把握できます LightHouse Google Speed Insightでも採用されているツールでフロント側のボトルネックの計測に必要なデータが計測可能 Chromeの開発者ツール 主にフロント側のレンダリングの計測に使用する 前述の通り、これらで得られる値をKPIとして採用することもあるでしょう。 Railsの計測用のGem RailsではこれらのGemを利用できます。 rack-mini-profiler 発行されたSQLとそれぞれにかかっている時間が表示されます rack-lineprof controllerやviewを行単位で時間の計測が可能です bullet N+1問題の計測が可能です 5. 上手にパフォーマンス改善するには パフォーマンス改善でよくあるのが、 頑張って改善した割には効果が薄い・効果なかった などかと思います。 実際に改善するためには下記のような手法をよくとります。 1. 本番環境に近いデータ量・インフラ環境で計測する 本番のデータ量によってパフォーマンスが低下している問題や 本番環境の設定によりパフォーマンスが低下している場合、問題の特定が難しいためできる限り本番に近い状態で計測しましょう 2. さまざまな改善手段の仮説で計測する 改善や計測の手段が1つだけですと、パフォーマンス改善は難しく改善する際にフロントのみを計測し続けたが実はインフラだったなど回り道をしがちです。 インフラ面、バックエンド、フロントそれぞれパフォーマンス改善方法に特別なものは多くないため基礎的な改善手段は把握しておきましょう 最後に 今回はパフォーマンス改善に関する手法の基礎的な部分をまとめてみました。 パフォーマンス改善の取り組み方などで少しでも助けになれたらうれしいです。
アバター
BigQuery大好き瀧川です! 最近弊社ではVue.jsを使ってSPA(SinglePageApplication)!という構成がよく使われています。 普段の開発だとwebpack-dev-serverを利用していて、手軽だしホットリロードもサポートされているし特に不自由なく運用できています。 でも、例えばWebpackでのproductionビルドをテストしたい、生成された静的ページにアクセスしたいということがちょこちょこあるんですよね。 大体いままで静的ページのテストをするときにはPythonのSimpleHTTPServerを使っていたけど、SPAの場合はチョット工夫が必要だった...😣ということをメモとして書いておきます! そもそもSimpleHTTPServerって?🤔 SPAでSimpleHTTPServerを使ったときの問題点😥 解決 🤗 おわり 😚 そもそもSimpleHTTPServerって?🤔 俗に言うPython製のワンライナーWebサーバです。 Webサーバというとapacheやnginxが有名だと思いますが、今回のテスト等でちょっと動かすにはハードルが高いですよね。 このSimpleHTTPServerはPythonの標準でモジュールに組み込まれており、以下のようにワンライナーで実行することができます。 これで実行したディレクトリがWebサーバーにホスティングされるようになります。 とても簡単ですね! python2の場合 $ python -m SimpleHTTPServer Serving HTTP on 0.0.0.0 port 8000 ... python3の場合 $ python -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... SPAでSimpleHTTPServerを使ったときの問題点😥 SPAで開発する多くの場合にRouterライブラリを利用することと思います。 Vue.jsだとvue-router、Reactだとreact-routerあたりを使って、ページ遷移を実装しているのではないでしょうか。 そのときに問題になるのが URL です。 弊社ではVue.jsを使っているので、vue-routerの仕様を例に上げると以下のように記載されています。 vue-router のデフォルトは hash モード です - 完全な URL を hash を使ってシミュレートし、 URL が変更された時にページのリロードが起きません。 その hash を取り除くために、ページのリロード無しに URL 遷移を実現する history.pushState API を利用したルーターの history モード を使うことができます。 噛み砕くと、デフォルトでは http://localhost:8000/#/login のようにパスに # がついてしまうけど、HTML5の仕様を使ったhistoryモードだと http://localhost:8000/login のようにできますということですね。 ただこのhistoryモードで、SimpleHTTPServerを使うと... loginというファイルを探しにいってしまいますね... 解決 🤗 こちら参考にさせていただきました( https://gist.github.com/martijnvermaat/4bec9bcc37d965e43879 ) thx! SimpleHTTPServerをHookしてSPAのHistoryモードに対応した状態のサーバを起動するスクリプトが以下になります!(Python3を普段使いしているので両方書いてあります) SimpleHTTPServer for SPA そこまで複雑ではないですがロジックを簡単に説明すると、 GETリクエストを受けた際にURLのドメイン部以降を取り出して、それに対応するファイルが存在しなければindex.htmlにredirectする ようになっています。 SimpleHTTPServerでリクエストをさばく役目を持つ、SimpleHTTPRequestHandlerを継承して上記ロジックを間に挟んだHandlerを、BaseHTTPServerに渡すことで実現しています! おわり 😚 こちらのスクリプトをPATH通したりすぐ呼び出せるように整備しておけば、いざというときに役立つのではないでしょうか。 「SPA界だとこれでやるのが定石だろ」みたいなことがあれば教えていただけると嬉しいです!
アバター
メガリザードンYと同じ身長・体重なことに気づきました。スマートキャンプの今川です( @ug23_ )。 前編 の記事ではGatlingを使った負荷テストなどについてソースコードを載せつつ説明しました。 tech.smartcamp.co.jp 後編ではどちらかというとプロセス的な部分により着目してまとめていきます。 【ポイント6】ミスを防ごう 私は負荷テストで1件やらかしました。 本番環境に負荷テストをしてしまう という失態を。翌日に本番インスタンスの2台が過負荷で死んでいました。 幸いサービス提供には大きな問題はなかったものの冷や汗ものでした。 こういったミスが起きないように以後は以下のことに気をつけています。 負荷テスト環境にドメイン(本番のサブドメイン)をつけて明確に分け、試験中は負荷の向き先を変えない 負荷テスト環境のELBのインスタンス付け外しの場合にダブルチェックを実施する 負荷シナリオを作る際、負荷の増大量を急激にしない 負荷テスト環境は本番環境とVPCやセキュリティグループレベルで分けて同じようにアクセスできないようにするのが理想ですね。 【ポイント7】ボトルネックかどうかを確かめるために消してみる 前回の【ポイント2】でもお伝えしましたが、 負荷テストのゴール を決めてそれを満たすことを目的として試験を実施することを目的としました。 「この問題を修正したらパフォーマンスが○割改善するのでやらせてください!」などと交渉するためにも、 見えている課題がどれぐらいのボトルネックになっているのか を把握する必要があります。 一方で、負荷テスト環境ですべての仕様を満たすものを提供し続ける必要はありません。 負荷テスト環境にデプロイするブランチから ボトルネックになっている機能を消してみましょう 。 人気順に並べている部分が重い ⇒ ソートしないでだしてみる DBアクセスが多い部分がある ⇒ その部分まるっとコメントアウト データ量が多い ⇒ 0個で出してみる それによって 理想状態にするとどれぐらい改善するのか を把握するとその後の改善がやりやすくなります。 思い切ってやってみましょう。 間違って本番適用しないように…。 【ポイント8】誰でも使えるようにしよう さて、ここまで定期的に実施するポイントを説明してきましたが、環境を整備しても 誰かが負荷テスト担当になってしまうのはもったいない! と思います。 自分だけでなく、他のメンバーにも負荷テストをやってもらうようにすればチーム全体の力が伸びます。 リポジトリやAMIにしてすぐ使えるようにしておく 負荷ツール用のスクリプト 負荷テスト環境用のインスタンスイメージ これらを残しておきましょう。 スクリプトについてはコピペやちょっと編集するだけで使えるように、 インスタンスイメージは立ち上げてスクリプトを置けばすぐ負荷を与えられるように、 環境を立ち上げるのがすばやくなるようにしました。 頻繁に負荷テストが行われる場合には負荷テスト環境を毎回片付けずに残しておくのも手かもしれません。 残しておくインフラコストと毎回立ち上げ直す人的(あるいは心理的)コストを比較してみましょう。 具体的な手順を共有する スマートキャンプではesaを使ってエンジニア間での情報共有を行っています。 esa.io 解説エントリを作ってインスタンスの立ち上げ方からGatlingの設定方法、シナリオの組み方までざっくりですが書いておきました。 はじめてやるメンバーにも任せられる内容にしました。 チュートリアルを実施する ぶっちゃけ、 ドキュメントを書くだけ ではだれも使ってくれないんですよね。 実演会をやって実際に見せたり、やらせてみましょう。それだけでも心理的障壁がだいぶ取り払われます。 【ポイント9】開発プロセスに組み込もう 定期実施するといっても抜き打ちでやる必要はないでしょう。 ただし、折に触れてパフォーマンスを確認するために開発プロセスの中に負荷テストを組み込んで、それにフックできるようにしましょう。 人は忘れるという能力を持っています。だからこそ、なにかと一緒に組み合わせて思い出しましょう。 例えばこんな感じかなと思います。 依存ライブラリやミドルウェアのバージョンアップ時 テストフェーズ開始時 テレビ等メディアに出る予定ができた時 新機能のリリース前 もしかして○○に負荷かかるんじゃ…? 的な発想を頭の片隅にでもおいておきましょう。 まとめ 前後編にわけて9つのポイントを紹介しました。負荷テストは奥が深いです。 開発だけしている分には必要にならないスキルです。ただし、Webサービスとして提供するならいつか必須になるものです。 早めに環境を整えるに越したことはありません。その際にこれらの知識がお役にたてれば幸いです。 私は負荷テストを進めることで「このシステムに求められているのは秒間○リクエストを耐えうる性能なんだ」という明文化されていなかったビジネス的な要件が見えてきました。 テストというのは バグや瑕疵を見つけるだけでなくシステムのありようを形作り決定づけるもの でもあると思いました。 お読みいただきましてありがとうございます!
アバター
データエンジニアの瀧川です。 Redash使ってますか!? Redashが出てここ数年、加速度的に導入事例が聞かれるようになり、もうスタートアップのデファクトスタンダードとも言えるのではないでしょうか。 その導入事例の中でよく聞かれるのが、 全社的に Redashを公開して、だれでもデータにアクセスできるようにしたよという話です。 確かにRedashは、クエリの蓄積・共有が容易ですし、グラフやダッシュボード機能がそのまま営業活動に使えたりと利点はたくさんあります。 ただ、ここで問題になるのが、個人情報を始めとする 一般ユーザが見ることのできないデータ をどのように制御するかだと思います。 本記事では、弊社で使っている BigQueryだけ で、個人情報をマスキングしたデータを用意し、権限設定でデータへのアクセスを制御する方法を紹介しようと思います! 目標 方法 元テーブルからマスクテーブルへのコピーするクエリを作成 クエリを定期実行 サービスアカウントにアクセス権を付与 データセットの許可リストにサービスアカウントを追加 まとめ 目標 例として、 production データセットに users テーブルが存在し、カラムとして (id int64, email: string, created_at) を持つとします。(以下のような感じですね!) そのときに email カラムがマスキングされたテーブルだけ参照できる common-bigquery サービスアカウントを作成できればゴールとします。 (Redashへは、作成したサービスアカウントを登録すれば、個人情報にアクセスできない) 方法 以下の手順で実施していきます。 元テーブルからマスクテーブルへのコピーするクエリを作成 クエリを定期実行 サービスアカウントにアクセス権を付与 データセットの許可リストにサービスアカウントを追加 元テーブルからマスクテーブルへのコピーするクエリを作成 「マスキングしたいカラム以外をselect」ってどのように書いてますか? 単純にやるなら以下ですよね。 select id, created_at from production.users しかし、BigQueryには replace や except というステートメントが存在して、「hogehogeなカラムだけを除く、置換する」という表現をすることができます! これを使って上のSQLを書き直すと以下のようになります。 select * except(email) from production.users Standard SQL Query Syntax  |  BigQuery  |  Google Cloud 上記のステートメントと、UDF(User Defined Function)を使って、 production.users から email のドメイン以外をマスキングしてselectするクエリが以下になります! (UDFは複雑に見えますが、emailであれば@マークでsplitしてローカル部だけハッシュ化しているだけで、集計作業なんかでドメインを残したほうがいい場合のクエリですね) #standardSQL create temp function mask(str string, typ string) as ( case typ # emailはローカル部だけマスクし、ドメイン情報は残す when ' email ' then ( select format( ' %s@%s ' , to_hex(sha256(local)), domain) from ( select split(str, ' @ ' )[safe_offset( 0 )] as local , split(str, ' @ ' )[safe_offset( 1 )] as domain ) ) else to_hex(sha256(str)) end ); select * replace (mask(email, ' email ' ) as email) from production.users 実行するとこのようになります。 クエリを定期実行 作成したクエリを実行することでコピーは作成できますが、データを更新するため定期実行する必要があります。 実環境では定期ジョブ全般はDigdagを使ってますが、最近BigQueryに実装されたScheduledQuery(β)を使ってやってみようと思います。 Scheduling queries  |  BigQuery  |  Google Cloud WebUI上でクエリを作成し、「Save Scheduled Query」ボタンを押して、必要情報を入力したのが以下となります。 これで、1時間毎にデータが更新されるようにできました。今まで定期実行する仕組みをこちらで用意する必要があったのですが、とても便利ですね! (BigQueryはデータセット単位でしかアクセス制御できないので、 masked_production データセットを作成してそちらでマスキングされたテーブルを管理します) サービスアカウントにアクセス権を付与 残りの作業は、「masked_productionデータセット にのみ アクセスできるサービスアカウント」を作成するだけです。 これはGCPのWebConsoleで行うのですが、ここでかなりハマるのがGCPの権限です。 GCPの権限は日本語が難しく、どれをつけるとなにができるのか直感的にわかりにくいと感じています。 今回でいうと、「BigQuery データ閲覧者」が罠で、これを付与すると 無条件ですべてのデータセットが閲覧できる ようになってしまいます。 なので以下の画像の通り、「BigQueryジョブユーザー」だけ付与するようにしましょう! データセットの許可リストにサービスアカウントを追加 あとはBigQueryのWebUIから、 masked_production データセットの右の逆三角(▼)から「Share dataset」を押して、以下のように作成したサービスアカウントのEmailを「Can view」で追加すればすべて終了となります! まとめ この方法を使うと以下のメリットがありますね! 開発やインフラ構築がいらない SQLなので、マスキングの自由度高い GCPの権限で完結する ベストプラクティスとは言えないと思いますが、少しでもここで説明した機能やテクニックが参考になれば嬉しいです!
アバター