TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

927

こんにちは、技術広報の yayawowo です。 今回は、 ラク ス初開催の 『Cloud Native TechCafe』 について、ご紹介させていただきます! テーマは、 『「 クラウド ネイティブ入門」を語る』 !! コンテナ化、CI/CD、 Kubernetes 等の入門内容を一緒に学びつつ、交流を深めてみませんか? TechCafeとは 「クラウドネイティブ入門」を語るCloud Native TechCafe 開催概要 主催出演者の紹介 語り合う内容 参加申込方法 終わりに TechCafeとは エンジニアと技術が交差する、憩いの場(カフェ)として ラク スが開催しているイベントになります。 年に数回程度の頻度で開催しており、現在はオンラインで実施しています。 ラク スでは類似イベントとして、 PHP TechCafe フロントエンドTechCafe がございます。 テーマをご確認の上、ご興味ある内容でしたら是非ご参加ください! 「 クラウド ネイティブ入門」を語るCloud Native TechCafe 開催概要 今回のテーマは「 クラウド ネイティブ入門」を語り合います。 【日 時】2022/6/22(水) 19:00~20:30 【会 場】オンライン(Zoom) 【参加費】無料 【主 催】 ラク ス ◆ こんな方におすすめ コンテナ/ Kubernetes を学びたい方 コンテナ/ Kubernetes の動向に興味、関心がある方 最新のインフラ技術トレンドをキャッチアップしたい方 他社の クラウド ネイティブ活用にご興味がある方 自社でインフラ構築を担っている方 クラウド ネイティブなインフラ領域に幅を広げてみたいエンジニアの方 などなど 申込枠としましては、 リスナー枠 をご準備しております! リスナー枠とは? 文字通り、聴くのみのご参加 マイク・カメラOFFでOK チャットや Twitter でのコメント参加、大歓迎 是非、コメントにて一緒に語り合いましょう! 主催出演者の紹介 ◆ 金本 宏司 2002年~ SIにてオンラインゲームのインフラ運用などを担当 2005年~ ツタヤオンライン入社  ECサイト 、メール配信基盤のインフラ全般を担当 2009年~ カカクコム入社  価格.com 、 食べログ などのインフラ全般を担当 2021年7月  ラク ス入社 現在は、インフラ開発部の部長を務める ◆ 見形 親久 受託開発の現場で様々な開発現場を経験した後、自社サービスに関わりたいと考え2016年 ラク スへ入社。 楽楽精算のアプリケーション開発、サービス運用を担当、2021年よりインフラ部門にてSREチームを担当。 現在はSREチームにて サブスクリプション 管理の基盤構築、サービス全般のDevOps推進を担当。 1981年生まれ 栃木県出身 趣味は、 スノーボード 、サウナ、呑み歩きです。 語り合う内容 当日のタイムテーブルと語り合う内容は、以下の通りです! ◆ タイムテーブル 時間 内容 19:00 開始・ご挨拶など 19:10~ 自己紹介 19:15~ ディスカッション開始 20:20 アンケートのお願い、締めのご挨拶 20:30 終了 ◆ 語り合う内容 当日のShowNoteは以下の通りです! hackmd.io ① クラウド ネイティブとは?  ・各社何と言っているか?  ・ Google が言う クラウド ネイティブ5つの原則  ・ マイクロソフト の説明詳細  ・ クラウド ネイティブにするために鍵になるのは?   などなど ②情報収集方法  ・書籍  ・勉強会  ・記事  ・ メールマガジン  などなど ③ クラウド ネイティブ関連の最新ニュース  ・気になったニュース(4~6月編) ※当日変更になる可能性がありますので、ご了承ください。 参加申込方法 イベントに参加したい!という方は、TECHPLAY 又は connpass 経由からお申込みをお願いします! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com 終わりに 「 クラウド ネイティブ入門」を語るCloud Native TechCafeの開催告知はいかがでしたでしょうか? 今回、 ラク スとしては初めて開催する内容となっております。 皆様がお楽しみいただけるよう、コンテンツ準備を進めておりますので、是非是非ご参加ください! 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
"リーダブルコード" ベトナム語 解説の第2回です。 ベトナム とのオフショア開発において可読性/保守性が高い開発が行えることを目的にして、 "リーダブルコード" やその他書籍、普段の経験を参照し、開発におけるテクニックをまとめました。 *1 この記事を ベトナム チームのメンバに読んでもらうことで、"リーダブルコード" の知識が日本チームと ベトナム チームの共通認識となり、コード品質が向上することを目的としています。 本稿は「ループとロジックの単 純化 」について解説します。 Đây là phần thứ 2 của loạt bài giải thích "Readable Code" bằng tiếng Việt. Trong bài post này, tôi sẽ tóm tắt nội dung của "Readable code" và giải thích bằng cả tiếng Nhật và tiếng Việt, để có thể sử dụng trong việc phát triển Offshore với Việt Nam. Khi team Nhật Bản và team Việt Nam đọc bài viết này, sẽ có cách hiểu chung về kiến ​​thức "Readable code" giữa các team và hướng tới mục đích là để nâng cao chất lượng program. Dự định tổng cộng là có 3 chương, và bài post này là Chương 2, giải thích về "Đơn giản hóa Loop and Logic". はじめに / Lời nói đầu この記事の使い方 / Cách sử dụng bài viết này チーム内の共通言語として / Như là ngôn ngữ chung trong team ベトナムでの学習資料として / Được xem như là tài liệu học tập ở Việt Nam リーダブルコードについて / Về Readable Code 1. 変数の使い方に関するテクニック / Các Technique liên quan đến cách sử dụng biến 1-1. Explaining/Summary Variables 1-2. Not Use Useless Variables 1-3. Shrink the Scope of Variables アクセス修飾子を変えていい? / Tôi có thể thay đổi access modifier không? 1-4. Specify the Type 2. 条件式に関するテクニック / 2. Các Technique liên quan đến biểu thức điều kiện 2-1. The Order of if/else Blocks 2-2. Strict Comparisons 2-3. Not Use Conditional Branch 3. ロジックの簡略化を行うテクニック / Các kỹ thuật thực hiện đơn giản hóa logic 3-1. Early Return 3-2. Minimize Nesting 4. 目的に合わせたコードにするテクニック / Techniques làm cho code đúng bản chất 4-1. Be General-Purpose Code 4-2. Simple is more important than Easy 4-3. Avoid Too Fat Code おわりに / Lời kết ベトナム メンバと理解する「 PHP リーダブルコード」 〜第2回 ループとロジックの単 純化 〜 ” PHP Readable code”hiểu được cùng với member Việt Nam 〜Chương 2 Đơn giản hóa Loops and Logic〜 はじめに / Lời nói đầu 先日はオフショア先である ベトナム のメンバ向けに、「表面的な改善」についてまとめました。 前回はコメントや 命名 などに関する内容でしたが、今回は実際に記載されているロジックに関する内容になります。 Hôm trước, tôi đã tổng hợp về "Cải thiện giao diện" của readable code, dành cho member VN là Offshore. Lần trước là về comment và naming, nhưng lần này là về logic được viết trong thực tế. これらのテクニックは、自分がコードを書く時だけでなく、既存のコードを読むときにも役に立ちます。 既存のコードがどうしてこのように記載されているのかを理解し、昔の実装者の意図を理解した上で修正を加えることで、保守性に優れたコードを維持することができます。 これらのテクニックを身につけて、保守性の高いコードを目指しましょう!! Những kỹ thuật này không chỉ hữu ích khi bạn viết code mà còn khi bạn đọc code hiện có. Bạn có thể duy trì good code có tính bảo trì, bằng cách hiểu tại sao code hiện tại được viết như thế này, hiểu ý đồ của những người implment hồi trước và sau đó thêm chỉnh sửa. Hãy cùng trang bị những kỹ thuật này và hướng tới code có tính bảo trì cao nhé!! ◆ 関連ブログはこちら tech-blog.rakus.co.jp この記事の使い方 / Cách sử dụng bài viết này チーム内の共通言語として / Như là ngôn ngữ chung trong team 各テクニックを日本語と ベトナム語 の両方で記述しています 各テクニックには、英語での名称をつけています 日本も ベトナム も使える英語のテクニック名があることで、フィードバック時に指し示しやすくなります 英語名は原著をもとに、簡易な単語を利用しています フィードバックや議論時に、この記事のリンクを貼るとさらに伝わりやすくなります Mô tả từng technique bằng cả tiếng Nhật và tiếng Việt Từng technique sẽ có đặt tên bằng tiếng Anh Khi có technique name tiếng Anh mà phía Nhật và Việt Nam đều có thể sử dụng, thì sẽ dễ dàng chỉ ra hơn khi feedback Tên tiếng anh sẽ dựa trên bản gốc và sử dụng từ đơn giản Khi feedback hoặc thảo luận, nếu gắn link của bài viết này thì sẽ dễ truyền đạt hơn Sample 1: [Japanese] この条件の場合、これ以上処理は行われません。そのため「3-1. Early Return」にしてください。 [Vietnamese] Trường hợp là điều kiện này, các xử lý hơn nữa sẽ không được thực hiện. Do đó, hãy chọn "3-1.Early Return". https://tech-blog.rakus.co.jp/entry/ReadableCodeWithVietnam02#3-2-Early-Return Sample 2: [Japanese] この変数にはintegerしか入らないはずですので、厳密な比較を使ってください。 詳しくはこちらの「2-2. Strict Comparisons」を参照してください。 [Vietnamese] Vì các biến này chỉ chứa các số nguyên, nên hãy sử dụng các phép so sánh nghiêm ngặt. Để biết thêm chi tiết, hãy tham khảo ”2-2. Strict Comparisons” ở đây. https://tech-blog.rakus.co.jp/entry/ReadableCodeWithVietnam02#2-4-Strict-Comparisons ベトナム での学習資料として / Được xem như là tài liệu học tập ở Việt Nam ベトナム語 に翻訳しているため、 ベトナム の方でも読みやすいはずです プロジェクトに参画した ベトナム メンバの学習資料として利用できます Vì được dịch sang tiếng Việt nên có lẽ sẽ dễ đọc ngay cả với người Việt Nam Có thể sử dụng làm tài liệu học tập cho các thành viên Việt Nam đã tham gia dự án リーダブルコードについて / Về Readable Code www.oreilly.co.jp The Art of Readable Code リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック Readable Code là Technique đơn giản và mang tính thực tiễn để viết code tốt hơn 有名なより良いコードを書くためのテクニックがまとめられた書籍です。上記の通り、日本語の翻訳本があります。 Đây là cuốn sách nổi tiếng và tổng hợp technique để viết code tốt hơn. Như đã nói ở trên là có bản dịch tiếng Nhật. 1. 変数の使い方に関するテクニック / Các Technique liên quan đến cách sử dụng biến 変数の使い方を変えることで、ロジックはとても読みやすく、バグが入りづらくなります。 Bằng việc thay đổi cách sử dụng các biến, thì logic sẽ trở nên rất dễ đọc và khó bị bug hơn. 1-1. Explaining/Summary Variables #変数 #命名 #可読性 #Biến #Naming #TínhDễĐọc 説明/要約変数 Các biến mô tả/ biến tóm tắt ややこしいコードを変数に入れてしまえば、そのコードが何を表しているのか変数名が説明してくれるため、すぐ理解ができるようになります。 (この時の変数名が値の説明になっていないと意味がないので注意してください。) Nếu bạn cho code dễ nhầm lẫn vào trong biến, vì tên biến sẽ giải thích là code đó đang biểu thị cái gì nên sẽ giúp ta hiểu được ngay lập tức. (Xin lưu ý rằng sẽ không có ý nghĩa gì nếu tên biến lúc này không giải thích được cho giá trị.) ❌ Bad Code <?php if ( explode ( "/" , $ url )[ LAST_KEY_NUMBER ] === "root" ) { ... } ✅ Good Code <?php $ userName = explode ( "/" , $ url )[ LAST_KEY_NUMBER ] ; if ( $ userName === "root" ) { ... } また、変数を使って処理の内容を要約してしまうことでも、読みやすさが向上します。 たとえば、以下のようにあまり大きくない処理でも、結果を変数に入れることによって何を行いたいのかが理解できるようになります。 Ngo ài ra, cũng có thể nâng cao tính dễ đọc bằng sử dụng biến để tóm tắt nội dung xử lý. Ví dụ, có thể hiểu những gì muốn làm bằng cách đặt kết quả vào một biến, ngay cả ở xử lý không quá lớn như bên dưới. ❌ Bad Code <?php if ( $ mail -> comments [ $ index ] -> userId === $ session -> userId ) { // User thì có thể edit comment. // ユーザはコメントを編集できる } ✅ Good Code <?php $ isWritableUserComment = ( $ mail -> comments [ $ index ] -> userId === $ session -> userId ) ; if ( $ isWritableUserComment ) { // User thì có thể edit comment. // ユーザはコメントを編集できる } 1-2. Not Use Useless Variables #変数 #EarlyReturn #ガード節 #Biến #GuardClause 役に立たない変数を使わない Không sử dụng các biến vô ích 前述した説明/要約変数は、特に説明せずとも伝わる処理で使う必要はありません。 例えば、以下のように変数にまとめなくてもわかるような処理には使用する必要はありません。 Đối với xử lý mà vẫn truyền đạt được ý nghĩa mà không cần bất kỳ giải thích nào thì không cần sử dụng các biến giải thích / biến tóm tắt đã nêu trên. Ví dụ ở xử lý hiểu được mà không cần tổng hợp vào biến như dưới đây thì không cần thiết phải sử dụng. ❌ Bad Code <?php $ userId = $ session -> user -> userId; $ updateUserId = $ userId ; ✅ Good Code <?php $ updateUserId = $ session -> user -> userId; // Truyền tải đầy đủ ý nghĩa 他にも、以下の $targetKey のような 中間結果 を記録するためだけに使う変数も不要なので削除しましょう。 このような中間結果を記録する変数は、コードの理解の助けにはなりません。 Ngo ài ra, biến sử dụng chỉ để ghi kết quả trung gian , như $targetKey bên dưới thì cũng không cần thiết nên hãy xóa đi. Các biến ghi lại kết quả trung gian như vậy không giúp ta hiểu được code. ❌ Bad Code <?php /** * Xóa user đã được chỉ định khỏi danh sách người gửi mail * メール送信者リストから指定されたユーザを削除する */ function removeUserFromToAddressList ( array $ addressList , int $ removedUserId ) { foreach ( $ addressList as $ key => $ address ) { if ( $ address [ "userId" ] === $ removedUserId ) { $ targetKey = $ key ; } } array_splice ( $ addressList , $ targetKey ) ; return $ addressList ; } 詳しくは後述しますが、以下のように Early Return を行うことで、 $targetKey を削除することができます。 Chi tiết sẽ được mô tả sau, nhưng bạn có thể xóa $targetKey bằng cách thực hiện Early Return như sau. ✅ Good Code <?php /** * Xóa user đã được chỉ định khỏi danh sách người gửi mail * メール送信者リストから指定されたユーザを削除する */ function removeUserFromToAddressList ( array $ addressList , int $ removedUserId ) { foreach ( $ addressList as $ key => $ address ) { if ( $ address [ "userId" ] === $ removedUserId ) { array_splice ( $ addressList , $ key ) ; return $ addressList ; // Early Return } } } 1-3. Shrink the Scope of Variables #変数 #スコープ #Biến #Scope 変数のスコープを狭くする Thu hẹp phạm vi của các biến このテクニックは非常に有効です。 このテクニックはコードの読みやすさを上げるだけでなく、度重なる変更に強くなり、バグを含みにくいコードを作ることができます。 Technique này rất hiệu quả. Kỹ thuật này không chỉ làm cho code của bạn dễ đọc hơn mà còn làm cho nó có khả năng chống lại các thay đổi lặp lại nhiều lần và làm cho code của bạn ít bug hơn. 変数のスコープを広くしてはいけません!!スコープが広く、寿命が長い変数は、以下のような問題を引き起こします。 変数の値がどこで変更されているかわからなくなる 間違って変数の値を上書きしてしまう危険がある 広い範囲でその変数の値を覚えながらコードを読まないといけなくなってしまう 影響範囲が多くなり、修正が難しくなる Không được phép mở rộng phạm vi của biến!! Biến có phạm vi rộng và tuổi thọ lâu sẽ gây ra các vấn đề như: Sẽ làm cho bạn không biết giá trị của biến đã được thay đổi ở đâu Có nguy cơ vô tình ghi đè nhầm lên giá trị của một biến Bạn vừa phải đọc code vừa nhớ giá trị của biến đó ở một phạm vi rộng. Phạm vi ảnh hưởng sẽ trở nên nhiều và làm cho việc chỉnh sửa trở nên khó khăn hơn. グローバル変数 を考えるとわかりやすいと思います。 グローバル変数 はどこでも読める一方で、どこでも書き換えることができてしまいます。 そのため、 グローバル変数 を使った処理を記述するときには、コード全体のどこで グローバル変数 の値が書き換わるかを調査してから使う必要があり、とても手間がかかります。 一方、スコープが狭い変数であれば、その変数が扱われている範囲をすぐ理解できるため、意図しない値の変更に怯えながら実装する必要はありません。 コードを読むときに考えることをできるだけ減らすためにも、その変数のスコープを狭くしましょう。 Tôi nghĩ sẽ dễ hiểu nếu bạn nghĩ về biến toàn cục (Global variables). Các biến toàn cục có thể đọc ở bất kỳ đâu, mặt khác có thể được viết lại ở bất kỳ đâu. Do đó, khi viết một xử lý đã sử dụng biến toàn cục, cần phải khảo sát xem giá trị của biến toàn cục được viết lại ở đâu trong toàn bộ đoạn code trước khi sử dụng, điều này rất mất thời gian. Mặt khác, nếu biến có phạm vi hẹp, thì bạn có thể dễ dàng hiểu được phạm vi mà biến đó đang được sử dụng do đó bạn không cần phải lo sợ trước những thay đổi ngo ài ý muốn của các giá trị. Hãy thu hẹp phạm vi của biến để giảm thiểu tối đa những suy nghĩ khi đọc code. 以下の例では、 foo() の内部で グローバル変数 $dirPath を書き換えていますが、元のコードではそれが見えません。 そのため、 グローバル変数 $dirPath が書き換えられていることに気づかず、実装者は違うファイルに書き込まれると勘違いして実装してしまいます。 Trong ví dụ sau, biến toàn cục $dirPath được viết lại bên trong foo() , nhưng bạn sẽ không thể nhìn thấy ở code gốc. Do đó, implementer sẽ không nhận thấy rằng biến toàn cục $dirPath đã được viết lại và sẽ implement với phán đoán sai rằng nó được ghi lại vào một file khác. ❌ ​Bad Code <?php global $ dirPath ; $ dirPath = "/usr/local/system/tmp" ; foo () ; $ filePath = $ dirPath . "/data.log" ; file_put_contents ( $ filePath , "user:Jonh login" ) ; // Tôi định ghi dữ liệu vào "/usr/local/system/tmp/data.log" // Trên thực tế, nó được viết vào "/tmp/data.log". // Bởi vì là đang viết lại $dirPath ở trong foo(). // // // "/usr/local/system/tmp/data.log" にテータを書き込んだつもりだが、 // 実際は "/tmp/data.log" に書き込まれてしまう。 // なぜなら、foo() 内で $dirPath を書き換えているから。 : : : function foo () { global $ dirPath ; $ dirPath = "/tmp" ; } $dirPath を グローバル変数 にせず、 foo() の引数にすることで、 $filePath が foo() で利用されていることを明確に示すことができます。 Bằng cách dùng $dirPath làm đối số của foo() mà không biến nó thành một biến toàn cục, có thể biểu thị rõ ràng việc $filePath đang được sử dụng trong foo() . ✅ Good Code <?php $ dirPath = "/usr/local/system/tmp" ; $ dirPath = foo ( $ dirPath ) ; // foo()で値が書き換えられていることがわかる/ Có thể hiểu rằng giá trị đã được viết lại ở $dirPath = foo($dirPath); // foo() $ filePath = $ dirPath . "/data.log" ; file_put_contents ( $ filePath , "user:Jonh login" ) ; : : : function foo ( $ dirPath ) { $ dirPath = "/tmp" ; return $ dirPath ; } グローバル変数 でなくても、クラス内の、メンバ変数がミニ グローバル変数 化してしまうことがあります。 以下の例の場合、 $status がミニ グローバル変数 になっており、どこで変更されているかわからない状態になってしまっています。 Ngay cả khi nó không phải là một biến toàn cục, một biến member của một class có thể trở thành một mini global variable. Trong ví dụ dưới đây, $status đang là một mini global variable và bạn sẽ không biết rằng nó đã được thay đổi ở đâu. ❌ Bad Code <?php class StatusLogic { private $ status ; private function updateStatus () { $ this -> status = "ACTIVE" ; $ this -> changeStatusToCorrect () ; return $ this -> status; } private function changeStatusToCorrect () { $ this -> status = "INACTIVE" ; // Xử lý sử dụng $status} // $status を使う処理 } } 特に問題がないのであれば、 $status をローカル変数にすることで、各変数のスコープが狭くなります。 Nếu không có vấn đề gì, việc dùng $status làm một biến cục bộ sẽ làm thu hẹp phạm vi của các biến. ✅ Good Code <?php class StatusLogic { private function updateStatus () { $ status = "ACTIVE" ; $ status = $ this -> changeStatusToCorrect ( $ status ) ; return $ status ; } private function changeStatusToCorrect ( string $ status ) { $ status = "INACTIVE" ; // Xử lý sử dụng $status // $status を使う処理 return $ status ; } } アクセス修飾子を変えていい? / Tôi có thể thay đổi access modifier không? #アクセス修飾子 #プロパティ #スコープ #AccessModifier #Property #Scope 以下のクラスでは、メンバ変数である $userName には private アクセス修飾子が指定されています。 Ở class sau, private access modifier được chỉ định cho biến member $userName . <?php class User { /** @var int User id */ private int $ userId ; /** @var string User Name */ private string $ userName ; // ★★★★★ public function __construct ( int $ userId , string $ userName ) { $ this -> userId = $ userId ; $ this -> userName = $ userName ; } } では、あなたが上司から、「このUserクラスの インスタンス から、 ユーザ名 を取得して表示するように」と指示された場合、どのように修正しますか?? この場合、以下のように アクセス修飾子を変更することは避けた方がいい でしょう。 Vì vậy, nếu sếp của bạn yêu cầu bạn "lấy tên user từ instance của class User này để hiển thị", bạn sẽ chỉnh sửa như thế nào ?? Trong trường hợp này, bạn nên tránh thay đổi access modifier như sau. ❌ Bad Code <?php { /** @var int User id */ private int $ userId ; /** @var string User Name */ public string $ userName ; // ❌ Thay đổi từ `private` thành `public`!! public function __construct ( int $ userId , string $ userName ) { $ this -> userId = $ userId ; $ this -> userName = $ userName ; } } // Xử lý khác // 他の処理 $ user = new User ( 1 , "Jonh" ) ; $ userName = $ user -> userName; // Vì đã đặt `public` nên có thể đọc được. print ( $ userName ) ; アクセス修飾子を private から public に変更すると、このクラスの インスタンス を使う全ての処理から変数の参照と変更ができてしまうため、変数のスコープがとても広くなってしまいます。 最初にこの User クラスを実装した人は、何かの理由があって $userName を private にしていたはずです。 安易にアクセス修飾子を変更せず、変数のスコープを広くしない修正方針を考えるべきです。 Nếu bạn thay đổi access modifier từ private thành public , thì phạm vi biến sẽ trở nên rất rộng vì có thể tham chiếu và thay đổi các biến từ tất cả các xử lý sử dụng các instance của class này. Người đã implement class user này lúc đầu chắc có lẽ đã đặt $userName thành private vì có lý do nào đó. Bạn nên suy nghĩ về phương châm fix mà không thay đổi access modifier và không mở rộng phạm vi của các biến. たとえば、以下のように getter() を作成することで、少なくとも他の処理から $userName の上書きは行われなくなります。 Ví dụ: bằng cách tạo getter() như sau, ít nhất xử lý khác sẽ không ghi đè lên $userName . ✅ Good Code <?php class User { private int $ userId ; private string $ userName ; public function __construct ( int $ userId , string $ userName ) { $ this -> userId = $ userId ; $ this -> userName = $ userName ; } public function getUserName () { return $ this -> userName; } } : : // Xử lý khác // 他の処理 $ user = new User ( 1 , "Jonh" ) ; $ userName = $ user -> getUserName () ; print ( $ userName ) ; とはいえ、場合によっては private な値を他の処理からは全く読み取って欲しくないコードもあります。 そのときは、その値を使う処理をクラス内に実装するなどの工夫が必要です。 どのような意図でそのコードが記載されているかを読み取って、適切な実装を考える ことが大切です。 Tuy nhiên, tùy trường hợp, cũng có code mà bạn không muốn đọc được tất cả các giá trị private từ xử lý khác. Trong trường hợp đó, cần phải công phu hơn chẳng hạn như implement xử lý sử dụng giá trị đó ở trong class. Điều quan trọng là phải đọc được code đó đang được mô tả với ý định như thế nào và suy nghĩ về cách implement phù hợp . 1-4. Specify the Type #型 #Type 型を指定する Chỉ định type さて、せっかく ラク スのブログで PHP で記事を書いているので、 PHP ならではの注意事項も記載しておきます。 PHP はバージョンアップするたびに、型に厳格になってきました。文字列型と数値型の足し算ができなくなったり、 count() 関数の引数に配列以外の値を指定するとエラーになるようになった仕様変更がこれにあたります。 これに伴って、変数に予期しない型の値が入っていると、思わぬエラーが生じる場合があります。 そのため、今後作成する PHP コードでは、各変数にどの型が入るかを意識し、違う型の値が入らないようにするべきでしょう。 Nhân tiện, tuy không có mô tả trong Readable code nhưng vì tôi đã viết một bài blog về PHP trên blog của Rakus, nên tôi sẽ mô tả thêm mục chú ý chỉ đối với PHP . Mỗi khi PHP được nâng cấp, nó trở nên khắt khe hơn về type. Đó là thay đổi specification như: không thể cộng string type với numeric type, hoặc xảy ra lỗi khi chỉ định một giá trị không phải là mảng làm đối số của hàm count() . Cùng với điều này, nếu thêm kiểu giá trị không mong muốn vào biến, thì sẽ có trường hợp phát sinh error không lường trước được. Do đó, trong code PHP mà bạn sẽ viết trong tương lai, bạn nên ý thức ở các biến sẽ chứa type nào và tránh đưa vào các giá trị có type khác . たとえば、以下のように $result に数値や文字列が入るようなコードは避けましょう。 Ví dụ: chúng ta nên tránh code có chứa numerical value hoặc character string ở $result như bên dưới. ❌ Bad Code <?php $ countRows = count ( $ rows ) ; if ( $ countRows > 0 ) { $ result = $ countRows ; } else { $ result = "ERROR: NO RESULT" ; } ✅ Good Code <?php $ countRows = count ( $ rows ) ; if ( $ countRows > 0 ) { $ result = $ countRows ; } else { $ result = 0 ; } 2. 条件式に関するテクニック / 2. Các Technique liên quan đến biểu thức điều kiện コード中に条件式があると考えることが増えるため読みにくくなってしまいます。 とはいえ、コードから条件式をなくすことはできませんので、できるだけわかりやすい記述をすることが大事です。 Nếu có các biểu thức điều kiện trong code thì nó khó đọc hơn vì bạn phải suy nghĩ nhiều. Tuy nhiên, biểu thức điều kiện không thể bị xóa khỏi code, vì vậy điều quan trọng là phải viết nó dễ hiểu nhất có thể. 2-1. The Order of if/else Blocks #if文 #値の比較 #CâuIf #SoSánhGiáTrị if/else 文の順番 Thứ tự của lệnh if/else 特に理由がある場合以外は、否定より肯定の条件を先に書きましょう。 否定条件が先にあると、否定条件が重要な処理に見えてしまいます。 (もちろん、否定条件の方が重要な処理であれば、否定条件をはじめに書いても問題ありません。) Trừ khi có lý do đặc biệt, còn lại hãy viết điều kiện khẳng định trước điều kiện phủ định. Nếu điều kiện phủ định có ở phía trước, thì sẽ cho rằng điều kiện phủ định là một xử lý quan trọng. (Tất nhiên, nếu điều kiện phủ định là 1 xử lý quan trọng, bạn có thể viết điều kiện phủ định ngay từ đầu mà không có bất kỳ vấn đề nào.) ❌ Bad Code <?php if ( $ aaa !== $ bbb ) { ... } else { ... } ✅ Good Code <?php if ( $ aaa === $ bbb ) { ... } else { ... } 2-2. Strict Comparisons #型 #PHP #値の比較 #Type #SoSánhGiáTrị 厳密な比較 So sánh chặt chẽ こちらは PHP ならではの注意事項です。 Đây là một số lưu ý chỉ có ở PHP . Tuy nó không được đề cập trong Readable code, nhưng tôi sẽ đề cập đến nó vì nó là một vấn đề lớn. 先ほども述べた通り、 PHP はバージョンアップするたびに、型に厳格になってきたため、変数に予期しない型の値が入っていると、思わぬエラーが生じる場合があります。 そのため、条件式でも厳格に型を指定した比較を使い、事故を減らすべきです。 Như đã đề cập trước đó, PHP đã trở nên chặt chẽ hơn về type mỗi khi nó được nâng cấp version, vì vậy nếu một biến chứa kiểu giá trị không mong đợi thì sẽ có trường hợp phát sinh error không lường trước được. Do đó, bạn cũng nên giảm thiểu các sự cố bằng cách sử dụng các phép so sánh đã được chỉ định type một cách chặt chẽ ngay cả ở biểu thức điều kiện. 以下のように、できるだけ厳格な比較を行いましょう。 Thực hiện phép so sánh nghiêm ngặt nhất có thể như bên dưới. ❌ Bad Code <?php if ( $ value == 0 ) { ... } if ( $ value != 0 ) { ... } ✅ Good Code <?php if ( $ value === 0 ) { ... } if ( $ value !== 0 ) { ... } 2-3. Not Use Conditional Branch #if文 #CâuIf 条件分岐を使わない Không sử dụng phân nhánh điều kiện. さて、ここまで条件式についてのテクニックを述べてきましたが、そもそも 条件分岐は少ない方が読みやすいコードになります 。 条件分岐はロジックを記述する上で必要不可欠ではありますが、あちこちに if 文が存在するコードはとても読みづらくなってしまいます。 Đến đây tuy tôi đã mô tả Technique về các biểu thức điều kiện, nhưng vốn dĩ phân nhánh điều kiện mà có ít nhánh thì càng dễ đọc . Mặc dù phân nhánh điều kiện cần thiết khi mô tả logic, nhưng code mà có tồn tại câu lệnh if ở chỗ này chỗ kia thì sẽ trở nên rất khó đọc. Do đó, mặc dù nó không có đề cập ở Readable code nhưng tôi sẽ đề cập đến việc giảm câu lệnh if. 条件分岐を減らすテクニックはいくつかありますが、ここでは1つだけ紹介します。 以下の例では、ユーザが画面から選択したチャットツールに、メッセージを投稿します。 ユーザは、"LINE"、"Slack"、"Zalo" のうち、いずれかのチャットへメッセージを投稿することができます。 Có một số Technique để giảm phân nhánh điều kiện, nhưng tôi chỉ giới thiệu ở đây một Technique. Ở ví dụ dưới đây,sẽ post message vào Chat tool mà user lựa chọn từ màn hình. User có thể post message lên bất kỳ Chat nào trong "LINE", "Slack" hoặc "Zalo". ❌ Bad Code <?php $ chatType = $ request -> get ( "chatType" ) ; $ postMessage = $ request -> get ( "postMessage" ) ; switch ( $ chatType ) { case CHAT_TYPE_LINE : $ lineAccount = getLineAccount () ; $ linePostResult = postMesseageToLine ( $ postMessage ) ; break ; case CHAT_TYPE_SLACK : $ slackAccount = getSlackAccount () ; $ slackPostResult = postMesseageToSlack ( $ postMessage ) ; break ; case CHAT_TYPE_ZALO : $ zaloAccount = getZALOAccount () ; $ zaroPostResult = postMesseageToZalo ( $ postMessage ) ; break ; } : : : // Phần hiển thị màn hình // 画面表示部分 switch ( $ chatType ) { case CHAT_TYPE_LINE : if ( $ linePostResult === "success" ) { displayMessage ( "Posted a message to LINE." ) ; } else { displayMessage ( "Error occurred when post a message to LINE." ) ; } break ; case CHAT_TYPE_SLACK : if ( $ linePostResult === "success" ) { displayMessage ( "Posted a message to Slack." ) ; } else { displayMessage ( "Error occurred when post a message to Slack." ) ; } break ; case CHAT_TYPE_ZALO : if ( $ linePostResult === "success" ) { displayMessage ( "Posted a message to Zalo." ) ; } else { displayMessage ( "Error occurred when post a message to Zalo." ) ; } break ; } 上記のように、各チャットの処理をswitch文(もしくはif分)で記載すると、処理が煩雑になってしまいます。 そのため、以下のように、 各チャットの処理を記載したクラスの インスタンス を用意することで、条件分岐を減らすことができます。 また、条件分岐内の処理も少なくすることができます。 Như đã đề cập ở trên, nếu mô tả xử lý các Chat bằng câu lệnh switch (hoặc if), thì xử lý sẽ trở nên phức tạp. Do đó, phân nhánh có điều kiện có thể được giảm bớt bằng cách chuẩn bị instance class có mô tả xử lý của mỗi Chat như bên dưới. Ngo ài ra, cũng có thể giảm bớt xử lý trong phân nhánh điều kiện. ✅ Good Code <?php $ chatType = $ request -> get ( "chatType" ) ; $ postMessage = $ request -> get ( "postMessage" ) ; switch ( $ chatType ) { case CHAT_TYPE_LINE : $ chat = new Line () ; break ; case CHAT_TYPE_SLACK : $ chat = new Slack () ; break ; case CHAT_TYPE_ZALO : $ chat = new Zalo () ; break ; } // Ở các class chat // có một method `post ()` để post message vào từng Chat // 各チャットのクラスには、 // それぞれのチャットへメッセージを投稿する `post()` メソッドがある $ postResult = $ chat -> post ( $ postMessage ) ; : : : // Phần hiển thị trên màn hình // 画面表示部分 if ( $ postResult === "success" ){ displayMessage ( "Posted a message to " . $ chat -> name ()) ; } else { displayMessage ( "Error occurred when post a message to " . $ chat -> name ()) ; } 3. ロジックの簡略化を行うテクニック / Các kỹ thuật thực hiện đơn giản hóa logic 難しいロジックを読み解く場合、脳にはそれだけ負荷がかかってしまいます。 できるだけロジックは簡単にし、誰でも読み解けるようにすることが重要です。 Khi đọc hiểu những logic khó, nó sẽ khi ến não bạn căng thẳng. Việc đơn giản hóa logic trong khả năng có thể, để ai đọc vào cũng có thể hiểu được là điều quan trọng. 3-1. Early Return #EarlyReturn #ガード節 #早期リターン #GuardClause #ReturnSớm 早くに return する Early Return 関数にて早くに処理を終了できる場合は、終了できる状態になったときに値を return しましょう。 もう他に処理を行う必要がないのに、開発者は return を見つけるまでその処理を読まなければいけなくなってしまいます。 Trường hợp có thể kết thúc xử lý sớm ở hàm, thì hãy return giá trị khi nó đã ở trạng thái có thể kết thúc. Mặc dù không cần thực hiện thêm các xử lý khác nhưng Developer phải đọc các xử lý đó đến khi tìm thấy return . ❌ Bad Code <?php function isValidInputedUser ( $ inputedUser ) : bool { $ result = true ; if ( $ inputedUser -> id <= 0 ) { $ result = false ; } else { ............... ............... ............... : : ............... ............... ............... } return $ result ; } 以下のように記載すれば、最後まで読まなくても、 id が0以下の場合は他になにもせず処理が終わることがすぐにわかります。 Nếu mô tả như sau, thì dù không đọc đến cuối, bạn có thể thấy ng ay rằng nếu id nhỏ hơn hoặc bằng 0, thì xử lý kết thúc mà không cần làm gì khác. ✅ Good Code <?php function isValidInputedUser ( $ inputedUser ) : bool { if ( $ inputedUser -> id <= 0 ) { return false ; } ............... ............... ............... : : ............... ............... ............... return $ result ; } また、このテクニックは、関数の内部だけでなく、ループ内でも有効です。 ループ内で処理を終了できる場合は、 break を利用しましょう。 Technique này không chỉ hữu ích ở bên trong hàm, mà còn ở bên trong loop. Trường hợp có thể kết thúc xử lý ở bên trong loop, thì hãy sử dụng break . ❌ Bad Code <?php // Lấy 1 recipe bạn chưa từng tạo // 作ったことがないレシピを1件取得する $ done = false ; foreach ( $ recipeList as $ recipe ) { if ( !$ done ) { if ( !$ recipe -> isMade ) { $ done = true ; $ suggestRecipe = $ recipe ; } } } return render ( "sugestOneRecipe.html" , $ suggestRecipe ) ; ✅ Good Code <?php // Lấy 1 recipe bạn chưa từng tạo // 作ったことがないレシピを1件取得する foreach ( $ recipeList as $ recipe ) { if ( !$ recipe -> isMade ) { $ suggestRecipe = $ recipe ; break ; // Early Return!! } } return render ( "sugestOneRecipe.html" , $ suggestRecipe ) ; 3-2. Minimize Nesting #ネスト #EarlyReturn #ガード節 #Nest #GuardClause ネストを浅くする Làm cho nest cạn đi 以下のようなコードは、とても読みにくく感じないでしょうか。 Bạn có cảm thấy code như bên dưới rất khó đọc không? ❌ Bad Code <?php if ( $ val < 1 ) { if ( $ val < 0.5 ) { if ( $ val < 0.05 ) { if ( $ val < 0.005 ) { if ( $ val < 0.0005 ) { : : 深いネストがあると、考えることが多くなるため読みにくいコードになります。できるだけネストは浅くしましょう。 個人差はあると思いますが、私は Level 3 のネストがあると解消できないかを考え、Level 4 になるとなんとしても避けようとします。 Việc có nest sâu sẽ làm cho code khó đọc vì nó khi ến bạn phải suy nghĩ rất nhiều. Hãy làm cho nest cạn nhất có thể. Tôi nghĩ rằng có những sự khác nhau với mỗi người, nhưng nếu có nest ở level 3, thì tôi suy nghĩ xem có thể loại bỏ nó được không và nếu là level 4 thì tôi sẽ cố gắng tránh nó bằng mọi cách. ネストを浅くするためには、先ほど紹介した Early Return が有効です。 以下のようにif文の条件を整理することで解消できます。 Để làm cho nest cạn thì phải ON Early Return đã tôi đã giới thiệu trên đây. Có thể xóa bằng việc điều chỉnh điều kiện của câu lệnh if như dưới đây. ❌ Bad Code <?php $ user = loadUser ( $ userId ) ; if ( $ user -> loadResult === "success" ) { if ( !$ user -> hasPermission ) { $ errorMessage = "ERROR: Permission." ; } else { $ errorMessage = "" ; } } else { $ errorMessage = "ERROR: Failed load." ; } $ display [ "user" ] = $ user ; $ display [ "errorMessage" ] = $ errorMessage ; return render ( "userInfo.html" , $ display ) ; 以下の例では、Early Return を利用することで、上記コードのネストを1 Level 減らすことができました。 Trong ví dụ dưới đây, chúng tôi có thể giảm bớt nest của code viết trên xuống 1 level bằng cách sử dụng Early Return. ✅ Good Code <?php $ user = loadUser ( $ userId ) ; $ display [ "user" ] = $ user ; if ( $ user -> loadResult !== "success" ) { $ display [ "errorMessage" ] = "ERROR: Failed load." ; return render ( "userInfo.html" , $ display ) ; } if ( !$ user -> hasPermission ) { $ display [ "errorMessage" ] = "ERROR: Permission." ; return render ( "userInfo.html" , $ display ) ; } $ display [ "errorMessage" ] = "" ; return render ( "userInfo.html" , $ display ) ; 4. 目的に合わせたコードにするテクニック / Techniques làm cho code đúng bản chất 基本的に、プログラムで行う処理は小さな問題に分割されている方が便利です。 例えば、大きな関数があり、その関数内で全ての処理を行っているとしたら、 なにか仕様変更があったときにはその関数内で行われている全ての処理をテストする必要があります。 また、大きな関数では、コードを読むときもこの関数では最終的に何がやりたいのか分かりづらくなっているでしょう。 そのため、保守性が高く読みやすいコードを書くためには、 その処理でやりたい本当の目的 を明確にして、 目的でない問題は他の処理に切り出す ことが必要です。 Về cơ bản, rất tiện lợi khi chia xử lý thực hiện trong program thành các vấn đề nhỏ. Ví dụ: nếu có một hàm lớn và đã thực hiện tất cả xử lý trong hàm đó, khi có sự thay đổi specification gì đó, thì cần phải test tất cả các xử lý được thực hiện trong hàm đó. Ngo ài ra, đối với hàm lớn, cả khi đọc code, bạn cũng bị khó biết được là cuối cùng bạn muốn làm gì trong hàm này đúng không. Do đó, để viết code có tính bảo trì cao và dễ đọc, hãy làm rõ vấn đề bản chất mà bạn muốn làm trong xử lý này , cần cắt các vấn đề không phải là bản chất sang xử lý khác . 4-1. Be General-Purpose Code 汎用コードにする Chọn code đa dụng #疎結合 #密結合 #独立したコード #責務 #外出し #LiênKếtLỏngLẻo #LiênKếtChặtChẽ #CodeĐộcLập #TráchNhiệm #OutputRaNgoài 処理を他の関数に切り出すとき、 できればその関数はアプリケーションに無関係な独立したコードになっている方がいいでしょう。 もし、そのコードがアプリケーションの仕様に依存していた場合は、 コードの内容を理解するためにアプリケーションの仕様を理解しておく必要がありますし、 アプリケーションの仕様が変更されたとき、そのコードも合わせて修正する必要があります。 また、独立したコードは、そのコードのみで実行することができるため、 テストも簡単に行うことができます。 切り出したコード自体はできるだけ他の処理と依存せず、 疎結合 な状態を保つことで、 保守性の高いコードになります。 Khi cắt xử lý sang hàm khác, nếu có thể thì hàm đó nên là code độc lập không liên quan đến application đúng không. Nếu code đó đã tồn tại trong specification của application, thì cần phải hiểu spec của application để hiểu nội dung code, và khi spec của application bị thay đổi, thì code đó cũng cần chỉnh sửa cho phù hợp. Ngo ài ra, code độc lập có thể thực thi chỉ với code đó, nên việc test cũng có thể thực hiện dễ dàng. Chính bản thân code đã cắt ra không phụ thuộc với xử lý khác hết mức có thể, và giữ trạng thái loosely coupling, nên sẽ trở thành code có tính bảo trì cao . 以下はアプリケーションへの依存性が高いコードです。 Dưới đây là code có tính phụ thuộc vào application cao . ❌ Bad Code <?php // Hiển thị recipe yêu thích của user // ユーザのお気に入りレシピを表示する $ favoriteRecipeName = getUserfavoriteRecipe () ; print "<p> { $ favoriteRecipeName } </p>" ; /** * Lấy recipe yêu thích của user * ユーザのお気に入りレシピを取得する * @return string Recipe name yêu thích / お気に入りレシピ名 */ function getUserfavoriteRecipe () : string { $ userId = $ _COOKIE [ "userId" ] ; // ★★★★★ $ user = new User ( $ userId ) ; return $ user -> favoritRecipe -> name ; } getUserfavoriteRecipe() の中に注目してください。 関数の中で cookie からユーザIDを取得しています。 しかし、これはアプリケーションのどこかで cookie にユーザIDを保存しているため実行できるのであって、 もし、 cookie にユーザIDが保存されていない場合はエラーになってしまいます。 つまり、「ユーザIDが cookie に保存されている」というアプリケーションの仕様に依存してしまっている状態です。 Hãy tập trung vào trong getUserfavoriteRecipe() . Đã lấy user ID từ cookie trong hàm. Tuy nhiên, điều này có thể thực thi vì đã lưu user ID vào cookie ở đâu đó trong application, nên nếu user ID không được lưu vào cookie thì sẽ bị lỗi Nói cách khác, là trạng thái phụ thuộc vào spec của application gọi là  "user ID được lưu trong cookie ". 処理を外出しする場合は、以下のように仕様から独立したつくりにするべきでしょう。 Khi going out xử lý, thì nên tạo độc lập từ spec như dưới đây nhỉ. ✅ Good Code <?php // Hiển thị recipe yêu thích của user // ユーザのお気に入りレシピを表示する $ userId = $ _COOKIE [ "userId" ] ; // ★★★★★ $ favoriteRecipeName = getUserfavoriteRecipe ( $ userId ) ; print "<p> { $ favoriteRecipeName } </p>" ; /** * Lấy recipe yêu thích của user * ユーザのお気に入りレシピを取得する * @param int $userId * @return string Recipe name yêu thích / お気に入りレシピ名 */ function getUserfavoriteRecipe ( int $ userId ) : string { $ user = new User ( $ userId ) ; return $ user -> favoritRecipe -> name ; } ユーザIDを関数の外で取得して引数で渡すことにより、 getUserfavoriteRecipe() は仕様から解放されました。 これで関数単体のテストも容易に作成することができます。 Do lấy user ID ở ngo ài hàm và truyền với đối số, nên getUserfavoriteRecipe() đã được giải phóng khỏi specification. Điều này làm cho có thể tạo unit test cho hàm dễ dàng. ただし、もちろんやりすぎはよくありません。 あまりに処理を分けすぎると、処理があちらこちらに飛んでしまい、処理の流れを追いづらくなります。 適切な粒度を判断して、処理を分けるようにしましょう。 Nhưng tất nhiên, làm quá mức là không tốt. Nếu chia xử lý quá nhiều, thì xử lý sẽ bay sang chỗ này chỗ kia, dẫn đến khó theo dõi luồng xử lý. Hãy phán đoán độ chi tiết phù hợp để phân chia xử lý. 4-2. Simple is more important than Easy Easy より Simple が大事 Simple quan trọng hơn Easy #SimpleMadeEasy #Simple #責務 #TráchNhiệm さて、この記事は日本語と ベトナム語 で書かれていますが、ここで少し英語の話をさせてください。 (翻訳チームのみなさん、ややこしくてすみません。。。) Bài viết này được viết bằng tiếng Nhật và tiếng Việt, nhưng chỗ này thì hãy để tôi nói bằng tiếng Anh một chút. (Xin lỗi vì team dịch vì hơi rắc rối một chút...) "Easy" と "Simple" の違いを考えたことはあるでしょうか? 日本語では両方とも「簡単」と訳せます。( ベトナム語 はどうでしょう?) しかし、英語でのこれらには微妙なニュアンスの違いがあります。 Bạn đã từng suy nghĩ về sự khác nhau giữa "Easy" và "Simple" đúng không? Trong tiếng Nhật thì cả 2 đều dịch là「Đơn giản」.(Tiếng Việt thì sao?) Tuy nhiên, có sự khác biệt nhỏ về sắc thái giữa các từ này trong tiếng Anh. このニュアンスの違いについて、 Clojure の作者である Rich Hickey が数年前に "Simple Made Easy" という発表をしています。 *2 *3 Về sự khác biệt sắc thái này, Rich Hickey là tác giả của Clojure đã có phát biểu gọi là "Simple Made Easy" trong vài năm trước. *4 *5 この発表によると、"Simple" の語源は ラテン語 の "simplex" であり、「1回折る」「1回編む」などの意味をもち、いずれも「単一の」という意味を含みます。 また、対義語は 「絡まった」「もつれている」といった意味を持つ "Complex" です。 Theo phát biểu này, nguồn gốc của từ "Simple" là "simplex" từ tiếng Latinh, có nghĩa là "gấp 1 lần", "đan 1 lần", v.v., tất cả đều bao hàm ý nghĩa là " của đơn nhất". Ngo ài ra, từ trái nghĩa là "Complex", có nghĩa là "vướng" hoặc "rối". 一方、"Easy" の語源は ラテン語 の "adjacens" と言われており、「近くにある」「身近な」「親しみのある」という意味をもち、 対義語は "Difficult" か "Hard" です。 Mặt khác, nguồn gốc của từ "Easy" được gọi là "adjacens" từ tiếng Latinh, có nghĩa là「Ở gần」「Gần gũi」「Thân thiện」, Từ trái nghĩa là "Difficult" hoặc "Hard". ここで大事なことは、"Easy" の「身近な / 親しみのある」という意味は、あくまで主観的、相対的な意味であり、 "Simple" の「単一の」という意味は客観的であるという点です。 Điều quan trọng ở đây là ý nghĩa "Gần gũi / Thân thiện" của từ "Easy" chỉ mang tính chủ quan và tương đối. Ý nghĩa của " của đơn nhất" trong "Simple" là điểm mang tính khách quan. たとえば、メールを送信する関数があるとします。 この関数はとても高機能で、引数に From アドレスや、To アドレスを指定できるだけでなく、 添付ファイルがある場合は添付ファイルのパスを引数に指定することで利用できるほか、 To アドレスを配列にすれば複数の To アドレスを指定でき、 さらに、テキストと HTML の本文文字列をそれぞれ引数に渡せばマルチパートメールにも送信できます。 Ví dụ: giả sử có một hàm gửi mail. Hàm này có tính năng rất cao , không chỉ có thể chỉ định From address và To address cho đối số mà còn nếu có file đính kèm, bạn còn có thể sử dụng khi chỉ định path của file đính kèm cho đối số, ngo ài ra nếu đặt To address là mảng, thì còn có thể chỉ định nhiều To address, thêm nữa, nếu truyền chuỗi body của text và HTML lần lượt cho đối số thì còn có thể gửi tới multipart mail. つまりこの関数は、あらゆるメールを "Easyに" 作成できる関数です。 Nói cách khác, hàm này là hàm có thể tạo tất cả các mail một cách "Easy" しかし、引数によってさまざまな条件があるため、この関数の中には非常に多くの if 文が含まれます。 (添付ファイルがあった場合の if 文、マルチパートメールを送る場合の if 文 etc...) Tuy nhiên, bao gồm rất nhiều câu if trong hàm này vì có các điều kiện khác nhau tùy thuộc vào đối số. (Câu if khi có file đính kèm, câu if khi gửi multipart mail, v.v.) そのため、この関数は "Complexである" とも言え、 下図の②にあたります。 Vì vậy, hàm này có thể gọi là "Complex", tương ứng với ② trong hình dưới đây. さて、この関数が存在するシステムで「 SMTP サーバを指定する」機能を実装することになったとします。 この場合、上記の "Easyな" 関数を修正することは非常に難しくなります。 なぜなら、条件分岐がたくさんあるため考慮事項が増えてしまい、テストも非常に難しくなってしまいます。 また、この関数を作成した本人は内容を理解できるかもしれませんが、 他の人が見ると条件分岐がとても多く読みづらいコードに見えてしまうことでしょう。 Bây giờ, giả sử quyết định implement chức năng "chỉ định SMTP server" trên system tồn tại hàm này. Trong trường hợp này, sẽ rất khó chỉnh sửa hàm có tính "Easy" viết trên. Lý do là vì có nhiều phân nhánh điều kiện nên mục cân nhắc tăng lên và việc test cũng trở nên rất khó khăn. Ngo ài ra, chính người tạo ra hàm này có lẽ có thể hiểu nội dung, nhưng mà nếu người khác xem , thì sẽ thấy rất nhiều nhánh điều kiện và có vẻ như là code khó đọc. この関数は、とある1場面ではとても便利で Easy かもしれませが、 Complex になっているため、拡張性がなく他の場面では Easy でなくなってしまいます。 Hàm này có thể rất tiện lợi và Easy trong 1 tình huống nào đó, nhưng mà vì nó là Complex, nên với tình huống khác không có tính mở rộng thì sẽ trở nên không Easy. この場合、Easy よりも、Simple であることを重視すべきです。 Trường hợp này, thì nên chú trọng là Simple hơn là Easy. Simple、つまり単純でわかりやすいことを心がけた場合、そのコードは拡張性に優れ、さらに他者にも読みやすくなります。 もちろん、Easy であることはいいことですが、Easy であることを求めたため、Complex になってしまっては意味がありません。 拡張性がなく、バグを生み出しやすいコードより、利用するために手間がかかるが単純でわかりやすいコードの方がずっと嬉しいです。 実装を行うときは、Easy より Simple を重視すべきです。 Simple, tức là nếu giữ suy nghĩ cố gắng để cho nó đơn giản và dễ hiểu, thì code đó sẽ có tính mở rộng, và cả người khác cũng dễ đọc hơn nữa. Tất nhiên, Easy là tốt, nhưng mà vì đã yêu cầu là Easy, nên nếu trở thành Complex thì không có ý nghĩa gì. So với code không có tính mở rộng, dễ sinh ra bug, thì dù mất thời gian công sức để sử dụng tôi vẫn thích code đơn giản dễ hiểu hơn. Khi thực hiện implement, nên chú trọng Simple hơn là Easy. 4-3. Avoid Too Fat Code 肥大化したコードを避ける Tránh code cồng kềnh #やりすぎ #仕様を減らす #LàmQuáNhiều #GiảmSpecifications コードは少なく短い方が保守性や可読性が高くなります。 これの解決方法は、不要な仕様を減らすことだけではありません。 そもそものコードを小さく関数やクラスに分割し、 他のコードから分離することで、事実上コードを短くすることができます。 他の処理から分離されていることが明確なコードは、 仕様の追加時やバグの修正時にも編集箇所が局所的になるため、修正や品質の担保が容易になります。 他の項目でも触れましたが、汎用的なコードを意識することや、処理を分離することなどを考えつつ、 コードの "重量" を意識し、できるだけ軽量なコードを書くよう心がけましょう! Code càng ngắn thì tính bảo trì và tính dễ đọc càng cao . Giải pháp cho điều này không chỉ là giảm các spec không cần thiết. Ngay từ đầu, nếu chia code nhỏ ra thành các hàm và class, và tách khỏi code khác, thì trên thực tế có thể rút ngắn code lại. Code mà rõ ràng được việc phân tách khỏi xử lý khác thì, vì cả khi thêm spec hoặc khi chỉnh sửa bug, những chỗ edit sẽ mang tính cục bộ , nên việc chỉnh sửa và đảm bảo chất lượng sẽ đơn giản hơn. Như tôi đã đề cập trong các mục khác, hãy cùng để tâm đến việc nhận thức đến code đa dụng, vừa suy nghĩ về việc phân tách xử lý, để tâm đến "trọng lượng" của code và cố gắng viết code nó càng nhẹ càng tốt! おわりに / Lời kết 先の記事でも触れましたが、日本と ベトナム では文化だけでなく、エンジニア向けのコンテンツ量も違います。 お互いに言語も違いますし、英語を使ったとしてもお互いの母国語ではないためどこまで意図が伝わるかは不確定です。 この記事を利用することで、 ベトナム との齟齬のない開発が行えることを願います。 Như tôi đã đề cập trong bài viết trước, không chỉ văn hóa mà lượng nội dung dành cho kỹ sư cũng khác nhau giữa Japan và Việt Nam. Ngôn ngữ khác nhau, và ngay cả khi bạn sử dụng tiếng Anh, cũng không chắc chắn ý đồ sẽ được truyền đạt vì nó không phải là ngôn ngữ mẹ đẻ của cả 2. Chúng tôi hy vọng rằng khi sử dụng bài viết này, sẽ có thể phát triển mà không có sự hiểu lầm với Việt Nam. RAKUS Vietnam Co.,Ltd Rakus Việt Nam đang tích cực tuy ển dụng kỹ sư ( Java , PHP ) và member QA. Bạn có muốn cùng tạo ra những sản phẩm SaaS với chất lượng như blog này không? Nếu bạn quan tâm, hãy ứng tuy ển nhé. ラク ス ベトナム では、エンジニア( Java , PHP )やQAメンバの採用を積極的に行っています。 このブログのような品質を意識した自社 SaaS プロダクトを一緒に作りませんか? ご興味ありましたら、是非応募をお願いします。 www.rakus.com.vn エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com *1 : 原版の翻訳や要約ではありませんのでご注意ください。 *2 : https://www.infoq.com/presentations/Simple-Made-Easy/ *3 : https://speakerdeck.com/takeru0757/simple-is-not-easy *4 : https://www.infoq.com/presentations/Simple-Made-Easy/ *5 : https://speakerdeck.com/takeru0757/simple-is-not-easy
アバター
技術広報の yayawowo です。 『仕様書』 と聞き、何の仕様書を思い浮かべますでしょうか? また、仕様書と設計書の違いを説明できるでしょうか? 今回は、仕様書とは何なのかをはじめ、種類や作成のポイントなどを分かりやすく解説したいと思います! システム開発 に携わる方は、是非ご参考になさってください。 仕様書とは 仕様書があると良いこと 設計書と何が違うの? 仕様書の種類 要求仕様書 機能仕様書 技術仕様書 テスト仕様書 良い仕様書とは 要求事項漏れがない 図表を使い、丁寧に説明している 機能の振る舞いを明確にしている おすすめ書籍 ラクスの特徴 終わりに ◆ 関連ブログも合わせてご確認ください! ・ 「要求を仕様化する技術・表現する技術」から学ぶ要求仕様書作成テクニック ・ プロジェクトマネジメント とは 【まとめ】 ・ PMBOK とは【まとめ】 ・ 要件定義 とは【まとめ】 ・ プロジェクトマネジメントTips 20選 ~現場から語るプロマネの極意~ 仕様書とは 仕様書とは、 システム開発 の仕様(機能/性能/特性/満たすべき要件など)をまとめた書類のことを言います。 簡単に言えば、「こんなシステム作りますよ」を説明した書類です! 仕様書があると良いこと 仕様書があることにより、以下を回避することが出来るといえます。 回避(例) ステークホルダー との認識違い 仕様漏れ 仕様変更による、 工数 増加 仕様変更による、スケジュール遅延 などなど 上記は一例に過ぎませんが、仕様書があることによるメリットは多くあります。 設計書と何が違うの? システム開発 を行う中で、 設計書 もよく聞く言葉だと思います。 大きな違いは以下2点です。 ①. 仕様書の内容に対しての実現方法(開発過程)が記載されている ②. 設計書は開発側が作成するものであるため、技術的内容となる 文字だけではわかりにくいかと思いますので、 ユーザーからオムライスを作ってほしいと依頼された場合を例に説明したいと思います! 図1.例)オムライス 仕様書(結果) オムライス 設計書(過程) オムライスを作る方法 ご飯を炊く 玉ねぎをみじん切りにする フライパンに火をつけ、サラダ油を入れる みじん切りした玉ねぎを炒める etc… いかがでしょうか? 少しは仕様書と設計書の違いをご理解いただければ幸いです! 仕様書の種類 仕様書にはいくつか種類があります。 こちらでは、代表的な4つに絞って説明したいと思います! 要求仕様書 要求仕様書とは、開発するサービスや製品が持つべき機能/特性/特徴などをまとめた書類です。 ユーザーが開発部隊に対して開発を依頼するものであり、具体的な技術要件を記載しているものではございません。 要求仕様書には、最低限必要な要素である 5W1H をまとめる必要があります。 WHEN(いつ) WHERE(どこで) WHO(だれが) WHAT(何を) WHY(なぜ) HOW(どのように) 機能仕様書 機能仕様書とは、前述した要求仕様書のニーズをどのようにしてシステムの機能で実現するかをまとめた書類です。 開発部隊の責任者であるPM(プロジェクトマネージャー)やSE( システムエンジニア )が、ユーザーの要望を ヒアリ ングし、作成します。 作成目的は、エンドユーザーである依頼者と、開発者である開発部隊で認識齟齬の発生を回避するためです。 機能仕様書を見ることで、 システム開発 にすぐ着手できるよう、丁寧に作成することが必要になります! 技術仕様書 技術仕様書とは、前述した機能仕様書にまとめた機能を システム開発 でどのように実現するかをまとめた書類です。 開発部隊のSE( システムエンジニア )、 プログラマー が相談しながら作成します。 認識齟齬がありながらの システム開発 はとても危険です。 技術仕様書を作成し、技術的な定義や手法をはっきりさせて開発を進めることが重要になります。 テスト仕様書 テスト仕様書とは、作成した要件定義書通りにシステムが開発/機能できているか、をテストするためにまとめた書類です。 どの機能を、どのテスト技法で動作確認するかを記載しております。 主に、 結合テスト や総合テスト工程をまとめております。 テスト計画書、テスト設計書が類似書類としてありますので、こちらも少し解説します。 テスト計画書 システム開発 におけるテスト方針(目的/範囲/人員/スケジュールなど)をまとめた書類 テスト設計書 テスト仕様書と扱いは同じであり、テスト設計仕様書と呼ぶこともある ◆ 関連ブログ tech-blog.rakus.co.jp 良い仕様書とは 要求事項漏れがない 仕様書は、開発者にとって システム開発 の軸となるものです。 その仕様書に要求事項の漏れがあった場合、どうなると思いますか? お察しの通り、開発スケジュールの遅延だけでなく、システム品質の低下やコスト増加を招くことになりかねません。 システム開発 で何をしてほしいか、を仕様書にしっかり落とし込むことが大変重要となります。 図表を使い、丁寧に説明している 仕様書を読むのは仕様書を書いた本人でなく、第 三者 になります。 誰が見ても分かりやすい資料にするには、図表を使った説明が効果的です。 しかし、ただ単に図表を使えば良いというものではありません。 想定する利用ユーザーが完成したシステムをどのように使うかを明確なストーリーや、イメージ図を使って開発者に伝える必要があります。 ワイヤーフレーム や画面遷移図、シーケンス図、開発前後の比較画像等を仕様書内に入れておくと良いかもしれません。 機能の振る舞いを明確にしている 要求事項の漏れがない仕様書も大事ですが、細部まで機能の振る舞いを明確に定義することも大変重要です。 開発納期に開発者から提供されたシステムを確認した際、 「あれ?この機能がないんだけど・・・」 このようなことが起きてしまうのは、大変危険です。 この場合、リリース延期や追加開発になることが想定されます。 仕様書には、機能の振る舞いを明確に定義することで、最終的な成果物の品質を向上させましょう。 おすすめ書籍 仕様書を初めて書く人におすすめの書籍をご紹介させていただきます! 学習の一助となれば幸いです。 『SE一年目のための仕様書の書き方』 「仕様書とは何か?」を知りたい方におすすめです。 手戻りが発生しない、分かりやすい仕様書を書きたい方に分かりやすく解説された1冊になります! 『速効メソッド ITエンジニアのためのビジネス文書作成術』 エンジニアだけでなく、ビジネスサイドの方にもオススメしたい書籍です。 具体的なケースやテンプレートもあり、直ぐに実践できる内容となっております。 『[入門+実践]要求を仕様化する技術・表現する技術 -仕様が書けていますか?』 「要求」とはなにか、「仕様」とはなにか、を具体的に解説してくれている一冊です。 要求仕様書作りの考え方や具体的プロセスを身につけるために、是非ご一読ください。 ラク スの特徴 上記は、当社の 開発プロセス になります。 仕様書を作成/よく活用するタイミングは、以下タイミングとなります。 要件定義 概要設計 詳細設計~実装~受入 ラク スでは中堅エンジニアはもちろんのこと、若手エンジニアでも要件定義や概要設計に関わる事が可能です。 若手エンジニアにとっては成長できる場をご準備しております! 少しでも当社にご興味をお持ちの方がおりましたら、まずは当社が主催する技術/採用イベントにご参加ください。 ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com お待ちしております! 終わりに 「仕様書」のまとめは、いかがでしたでしょうか? 仕様書は システム開発 を行う中で、見返すことが多い書類になります。 作成をする際は、情報の過不足がないものを作れるように頑張ってください! また今回初めて作る方は、書籍を読むことは大前提ですが、既存の仕様書を読み返すこともおすすめです。 先輩方の知恵を有効活用し、是非ご自身が作る仕様書を良いものにしていただけますと幸いです。 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
初めに 初めまして、sakekobaです。 普段インフラのお仕事をさせて頂いておりますが、ここ数年の間に「SRE」という言葉を聞くようになったように感じます。 SREについて知りたいけど難しいな…。と思っていたので、SREについて、出来るだけ分かりやすくまとめる事にチャレンジしました。 「SRE」とは分かりやすく言うと何なのか? SREの取り組みは、具体的に何をすればいいのか? どういう人がSREに向いているのか? という点について、まとめてみました。 目次 初めに 目次 「SRE」とは? 「SRE」の「信頼性」を定義する SLI SLA SLO 省力化する 信頼性向上のための活動を行う エラーバジェット ポストモーテム ラクスのSRE課について ラクスの取り組み どういう人がSREエンジニアに向いているのか? 「SRE」とは? 「SRE」をざっくり言うと「新しいサービス運用の考え方、そしてその役割」です。 SREとは「サイト・リライアビリティ・エンジニアリング」の頭文字をとった言葉で、日本語では「サイト信頼性エンジニアリング」となります。 サイト(サービス)の信頼性をエンジニアリングする(意訳ですが、しっかり作りこむ)ことを目的として、 システムエンジニア リングの知見からのアプローチで、システム管理、運用をより良くします。 SREの中で謳われている「信頼性(が高い)」とは、ユーザが期待通りにサービスを使える(障害等で使えないことが無い(少ない))状態のことです。 勿論、SREが広まる前もシステムの運用者は高い信頼性維持を目指していますが、システム運用に求められるスキルは、「サーバ・NW・ ミドルウェア 等の幅広い知識を以て安定運用に寄与すること」であるため、運用者自身が手でコマンドを打つ等で運用作業を行うことが多い状態でした。 そのため、例えば下記のような問題がありました。 手作業が多いと、それだけヒューマンエラーで障害のリスクがある サービスの規模が大きくなると作業が増大し、人手や時間といったコストをかける必要があり、不具合改修、サービス向上にコストを振り分けられない サービスの「開発者」と「運用者」が完全に分かれていることで、「ユーザの為により良い改善をどんどんリリースしたい」開発と、「安定稼働の為には、システムのリリース作業は最小限にしたい」と考える運用の間に大きな壁がありリリースのスピードが遅くなる などなど… これらの問題を解消する役割が、SREです。 例えば下記があります。 SREチームが「信頼性」を定義し、皆で同じ方向を向く 手作業が多いシステム管理・運用の分野に、SREが自動化を取り入れる その他、エラー許容の定義や障害発生時の事後検証等、信頼性向上のための活動を行う SREという言葉を提唱したのは Google 社ですが、その Google 社が、下記の資料を公開しています。 sre.google また、このSREについての資料の日本語版が オライリー 社より出版されています。 www.oreilly.co.jp SREは会社・サービスの課題によって様々なアプローチで信頼性の向上に取り組みます。 このブログでは、先に上げたSREの役割の例 信頼性の定義 省力化する 信頼性向上のための活動 を少し掘り下げて記載したいと思います。 「SRE」の「信頼性」を定義する SREのエンジニアチームには、「信頼性」を定義して関わる人を同じ方向に向ける役割があります。 そもそも「信頼性が高いサービス」とは何でしょうか。 24時間常に動作していて、応答が即座に返ってきて…と、思いつくものはあるかと思いますが、具体的にするとどうでしょう。 例えば・・・ 「 ECサイト で決済だけされて購入(在庫の確保)が遅い時があった」ら買えない場合もあり、怖くて使えないかもしれませんが、「暇つぶしの無料動画サイトで読み込みが遅い時があった」としても、怖くて使えない!とまでは思わないかもしれません。 また、同じ ECサイト だとしても、「お気に入りリスト」のような付属機能の更新が一時的に使えなかったとしても、購入機能ほど困らないかもしれません。 このように、ものによって全く違う期待に応える必要があるため、SREでは具体的に数字に落とし込むことで、関わる人が同じ方向を向けるようにします。 SREではこの信頼性の定義を、SLI 、 SLA 、SLOを用いて行います。 SLI サービス・レベル・インジケーターと読みます。 サービスレベルを測るため、 定量 的な指標を定義します。 たとえば、「早くサイトが表示される」という目標だけでは、「1秒で表示されるのは遅い?早い?」のように人によってバラバラな捉え方をしてしまいます。 その為「理想的なリク エス トの 応答時間 は0.5秒」のように指標となる 定量 的な項目を定義し、後述のSLOを測るために使います。 SLA サービス・レベル・アグ リーメント と読みます。 この言葉自体はSREに特化したものではない為、SREに関わらず、この用語を耳にされたことがあるかもしれません。 サービスを使用するユーザと「合意した(対外的に約束した)」サービスの品質のことです。 この「合意」はお金を払って契約しているサービスで、契約書に明記されている場合もあり、「契約違反では?」と言えてしまうようなものです。 絶対に守るべきラインとなるため、次に挙げる「SLO」より緩く設定されていることが一般的です。 SLO サービス・レベル・オブジェクティブと読みます。 内部的に設定したサービスレベルの目標値です。 関わる人の認識を合わせるためでもあるので、高すぎても、低すぎても問題があります。 高すぎると、常に目標達成のため原因追及をする羽目になります。 低すぎると、普段は良くても「(内々にはSLOの範囲だけど)いつもより悪いとユーザからクレームが来た」等の時に対応に困る羽目になります。 SLOの考え方については、 Google のブログに具体的に書かれた記事があります。 cloud.google.com 省力化する 冒頭で上げた問題の例に、手作業に伴う問題がありました。 手作業が多いと、それだけヒューマンエラーで障害のリスクがある サービスの規模が大きくなると作業が増大し、人手や時間といったコストをかける必要があり、不具合改修、サービス向上にコストを振り分けられない このような人が手で運用作業をすることに伴い発生する課題について、SREは、自動化を推進することで解消を図ります。 省力化はSREが担う役割の中でも重要なものの一つで、 Google のSREチームでは「SREが運用作業に充てて良い時間は業務の50%まで」とし、残りは運用作業の自動化や、サービスの開発などに振り分けるよう徹底しているほどだそうです。 この自動化できるのにしていない繰り返しの手作業は「トイル」と呼ばれています。 SREはこのトイルを見つけ出し、ソフトウェアエンジニアリングの知見で解消をすることで、ヒューマンエラーの低減、サービス向上のような作業への人的リソースの振り分けを行えるようにアプローチします。 SREのトイルの考え方、見つけ方についてより知りたい場合は、下記の記事が参考になると思います。 cloud.google.com 「運用を自動化しよう」というのは「SRE」だけでなく「DevOps」という言葉で聞いたことがある方も多いかと思います。 SREとDevOpsは相反するような考え方ではありません。 SREとDevOpsは、それぞれが独立した考え方ではなく、SREはより具体化し、自動化以外の要素も持ったものと言えるかと思います。 先に紹介したSREを提唱した Google の資料でも、下記のように記載されています。 DevOpsは、さまざまな組織、管理構造、および人員に対するいくつかのコアSRE原則の一般化と見なすことができます。(※日本語訳、本文は英語) Google - Site Reliability Engineering 信頼性向上のための活動を行う 信頼性の定義や自動化だけでなく、SREはその会社・サービスが持つ運用課題を様々な方法で解決に向かわせます。 例えば、冒頭で挙げた サービスの「開発者」と「運用者」が完全に分かれていることで、「ユーザの為により良い改善をどんどんリリースしたい」開発と、「安定稼働の為には、システムのリリース作業は最小限にしたい」と考える運用の間に大きな壁がありリリースのスピードが遅くなる という問題点については、SREでは「エラーバジェット」という考え方を用いてアプローチします。 エラーバジェット SREのアプローチとして、開発と運用のバランスを取るための方法です。 エラー等を許容する値を決めておき、エラーが許容量までであれば新機能の開発やリリースを優先してもよく、 閾値 を下回る場合は新規のリリースをとめて安定した運用を優先しなくてはならない、というルールを決めた運用方法です。 前述したSLOで、サービスレベルの目標値を決めておき、そのレベルまでのサービス品質の低下を許容するという考え方です。 この値を決め、ルールを設定することで開発と運用で同じ指標を共有出来、バランスを取ることが出来るようになります。 他にも、運用には障害対応がつきものです。 SREではこの障害を「ポストモーテム」という振り返り用の記録として残し、事後検証・共有し今後に活かそうという取り組みを行うこともあります。 ポストモーテム 障害・インシデントが発生した際、その内容、原因、対応などを文章化し、そこから再発防止や、ベストプ ラク ティスを学びます。 SREが行うポストモーテムが、一般的な障害報告等と違うところは「決して非難しない」という点です。 非難を排除することで、問題が隠されてしまうことを防いだり、本質的な改善の議論にフォーカスすることが出来ます。 信頼性向上のため、多岐にわたる役割が内包されているSREですが、会社、サービスごとに取り巻く状況や解決するべき課題に差があります。 なのでSREチームが置かれていたとしても、どの会社も同じことをしているとは限りません。 一律に「この作業をする」と言い切れないところに、SREという言葉を理解する難しさがあると感じます。 ラク スのSRE課について 弊社 ラク スにも、SRE課があります。 一例として、簡単にではありますが、弊社の取り組みを紹介させていただきます。 ラク スの取り組み ラク スのSRE課では、現在各プロダクトの運用の自動化やモニタリング基盤の刷新などを進めています。 とは言え、まだ出来上がったばかりのチームで小さく始めたところです。 事例ですと、 Kubernetes を利用した環境構築や運用のデプロイパイプラインの構築、モニタリングツールの導入支援といった業務を推進しています。 将来的にはこれらのノウハウを各プロダクトへ展開していきたいと考えています。 どういう人がSREエンジニアに向いているのか? 最後に、SREエンジニアになりたい場合、どのようなスキルや経験を積んでいるとより良いか、 ラク スのSRE課に求める人物像を聞いてみました。 目的を正しく把握して行動に移せる というのが一番重要なことだと思います。 SREとしてのプ ラク ティスを実践するための技術的なスキルも重要ですが、 業務的にDevと Ops 双方と協業する立場ですので、目的がブレないようにしつつ、 関係者を巻き込んで改善していく行動力が特に重要だと考えています。 ※スキルは一緒に学んでいけばよい、と考えています ご興味ありましたら、以下内容をご確認ください! career-recruit.rakus.co.jp エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
初めに 皆さんこんにちは。mosyoryです。 今回は Python のPyAutoGUIを使用してマウスやキーボードを操作する方法をご紹介します。 関数で使用する引数の全ての解説は行っていないので予めご了承ください。 初めに PyAutoGUIとは PyAutoGUIのインストール マウス操作 画面のサイズの確認 マウスカーソルの移動 マウスのドラッグ クリック キーボード操作 文字の入力 特殊キーの入力 キーの同時入力 「@」「^」「:」が入力できない場合 緊急停止させたい場合 メモを作成してみる 終わりに 参考サイト   PyAutoGUIとは PyAutoGUIとは、マウスやキーボードを操作する GUI 自動化の Python のライブラリです。 Windows , macOS , Linux で使用することができ、Python2, 3のどちらでも動作します。 pyautogui.readthedocs.io 今回は Windows 環境でPython3を使用した例を紹介します。 PyAutoGUIのインストール PyAutoGUIは コマンドプロンプト で下記を実行するだけで簡単にインストール出来ます。 pip install pyautogui 正しくインストール出来たか確認したい場合は「pip list」を実行してください。 PyAutoGUIとそのバージョンが表示されていれば、インストールが出来ています。 > pip list Package Version ----------------- -------- PyAutoGUI 0.9.53 ※pipは Python のパッケージ管理ツールです。  Python3.4以上なら標準で付属していますので別途インストールする必要はありません。 Python は既にインストールされている前提で進めますので、まだの方は Python 公式サイトからPython3.xのバージョンをインストールしてください。本記事ではPython3.9を使用します。 www.python.org Python のバージョンを確認したい方は、 コマンドプロンプト から「 python -V」を実行してください。 > python -V Python 3.9.1 マウス操作 まずは、 Python がPyAutoGUIを使えるようインポートしましょう。 import pyautogui 画面のサイズの確認 マウスの位置は、X/Y座標で表されます。 画面の左上を座標(0, 0)とし右に進むほどX座標、下に進むほどY座標が増加します。 画面が1920×1080の場合、右下の座標は(1919, 1079)になります。 マウス操作の際に座標を使用するため、最初に画面のサイズを確認しておくと良いでしょう。 print (pyautogui.size()) マウスカーソルの移動 カーソル移動は、moveTo()を使用します。引数に与えたX/Y座標にカーソルが移動します。 # 座標(500, 600)に移動 pyautogui.moveTo( 500 , 600 ) 座標のみを与えた場合は、 Python を実行して一瞬でカーソルが移動します。 人間が動かしている様にゆっくりと移動させたい場合は、durationで移動時間を指定しましょう。 # 3秒かけて座標(500, 600)に移動 pyautogui.moveTo( 500 , 600 , duration= 3 ) 今のマウスカーソルの位置から相対的に移動させたいときは、move()を使います。 # 2秒かけて現在のカーソルの位置から上に200移動 pyautogui.move( 0 , - 200 , duration= 2 ) マウスのドラッグ ドラッグには、dragTo(), またはdrag()を使用します。 カーソル移動で使用したmoveTo()と同様にdragTo()が指定した座標へ、drag()が現在地からの相対的なドラッグを行います。 ドラッグでは引数に座標や移動時間に加え、どのマウスボタンを押し続けるかを指定します。 ボタンはleft, middle, rightの3つです。 # 左ボタンを押しながら、2秒かけて座標(100, 300)にマウスをドラッグ pyautogui.dragTo( 100 , 300 , duration= 2 , button= "left" ) # 右ボタンを押しながら、3秒かけて右に50, 下に100の座標までマウスをドラッグ pyautogui.drag( 50 , 100 , duration= 3 , button= "right" ) 引数に秒数を指定しないとmoveTo()と同様に一瞬で移動し、ドラッグとして認識されないことがあります。 そのため、移動時間を引数に指定したうえで実行することをお勧めします。 クリック click()を使用することでクリックが出来ます。 引数にクリックをする座標、クリックするボタン、回数や間隔を指定出来ます。 何も指定しなかった場合は現在のカーソルの位置で左クリックを1回行います。 # 現在のカーソルの位置で左クリックを1回 pyautogui.click() # 座標(20, 100)で右クリックを1回 pyautogui.click( 20 , 100 , button= "right" ) # 現在のカーソルの位置で0.5秒の間隔で左クリックを3回 pyautogui.click(button= "left" , clicks= 3 , interval= 0.5 ) 座標を指定した場合、カーソルを移動させた後クリックを行います。 そのため下のコードは同じ結果となります。 # moveTo()でカーソルを移動させてクリック pyautogui.moveTo( 100 , 100 ) pyautogui.click() # click()でカーソルを移動させてクリック pyautogui.click( 100 , 100 ) キーボード操作 文字の入力 write()に文字列を与えることで入力を行います。 入力速度はとても速く50文字程度の文字列は約0.1秒で入力が終えます。 意図的に遅らせたいのであればintervalを指定することで次の文字を入力するまでの間隔を空けることができます。 write()は一文字のキーのみ入力できるため、EnterやShiftの様な特殊キーを押したい場合は次に紹介する関数を使用してください。 # 0.5秒の間隔でenterという文字列を入力 (Enterキーは押されない) pyautogui.write( "enter" , interval= 0.5 ) # 下記の文章も約0.1秒で入力が終えます pyautogui.write( "It was created using the Python module pyautogui." ) 特殊キーの入力 特殊キーを入力したい場合はpress()に、enterの様な予め決められている文字列を与える必要があります。 # Enterキーを入力 (enterという文字列は入力されない) pyautogui.press( "enter" ) 使用することができる特殊キーについては、こちらの一覧をご覧下さい。 pyautogui.readthedocs.io キーの同時入力 複数のキーを同時に入力する方法は2つあります。 1つ目は、keyDown()とkeyUp()を組み合わせる方法です。 keyDown()がキーを押してそのまま離さない動作、keyUp()が押していたキーを離す動作のイメージです。 ただkeyDown()をしても、そのキーが入力され続けるわけではないので注意してください。 # ctrlを押した状態で、aを押す。 pyautogui.keyDown( "ctrl" ) pyautogui.keyDown( "a" ) pyautogui.keyUp( "a" ) pyautogui.keyUp( "ctrl" ) 2つ目は、hotkey()を使用する方法です。 こちらは引数に指定したキーを順番に押し逆順で離していきます。 keyDown()とkeyUp()の場合だとキーの数が増えると行数が増えてしまいますが、hotkey()だと1行で書くことができます。 # shiftを押した状態でbとcを入力 pyautogui.hotkey( "shift" , "b" , "c" ) # 上の例をkeyDown(), keyUp()で書いた場合 pyautogui.keyDown( "shift" ) pyautogui.keyDown( "b" ) pyautogui.keyDown( "c" ) pyautogui.keyUp( "c" ) pyautogui.keyUp( "b" ) pyautogui.keyUp( "shift" ) 「@」「^」「:」が入力できない場合 キーボードのレイアウトによっては、特定の文字が意図したものとは違う入力がされる場合があります。 筆者は「@」「^」「:」を入力したら、Shiftも一緒に押した状態の「`」「~」「*」になってしまいました。 # 「@^:」を入力したつもりだが「`~*」が入力されている pyautogui.write( "@^:" ) 「@」「^」「:」を使用したい場合は、_pyautogui_win.pyというファイルを直接修正して回避する方法があります。 ※修正する場合は自己責任でお願い致します。 def _keyDown (key): (省略) needsShift = pyautogui.isShiftCharacter(key) # 以下3行を追加 if key == '@' : needsShift = False if key == '^' : needsShift = False if key == ':' : needsShift = False """ # OLD CODE: The new code relies on having all keys be loaded in keyboardMapping from the start. if key in keyboardMapping.keys(): (省略) if文でShiftは不要であるという処理を追加することで「@」「^」「:」が入力できるようになりました。 筆者は_pyautogui_win.pyが以下のパスにありましたが、 Python のインストール先によって異なるのでご自身の環境に合わせて探してみてください。 C:\Users\ユーザ名\AppData\Local\Programs\Python\Python39\Lib\site-packages\pyautogui\_pyautogui_win.py 緊急停止させたい場合 「何らかの理由で実行中のプログラムを停止させたいが、実行中の Python がマウスやキーボードを動かしているせいで停止ボタンを押せない」という様な時が来るかもしれません。そんな時はfail-safeモードを使用しましょう。 このモードはマウスカーソルを画面の一番左上(座標(0, 0)の場所)に移動させると起動しプログラムを停止させます。 PyAutoGUIがマウスやキーボードを操作してる最中でもマウス操作やキー入力は受け付けてくれるので、自分でマウスを画面左上まで動かしましょう。 メモを作成してみる 最後にメモ帳を開いて保存するまでの例をご紹介します。 4行目で設定しているpyautogui.PAUSEはPyAutoGUIが操作をした後の待機時間です。 これを設定しないとメモ帳の起動が完了する前に文字の入力が始まってしまうので、1秒間の待機時間を設定しています。 import pyautogui # 処理を実行するたびに1秒待機 pyautogui.PAUSE = 1.0 # 画面のサイズを取得 width, height = pyautogui.size() # スタートボタン付近にカーソルを移動しクリック pyautogui.moveTo( 5 , height- 5 , duration= 1 ) pyautogui.click() # メモ帳のアプリを検索し、エンターで起動 pyautogui.write( "memo" ) pyautogui.press( "enter" ) # 文字を入力 pyautogui.write( "It was created using the Python module pyautogui." ) # Ctrl+S で保存 pyautogui.hotkey( "ctrl" , "s" ) # ファイル名を入力し、エンターキーで保存 pyautogui.write( "Python.txt" , interval= 0.25 ) pyautogui.press( "enter" ) 終わりに Python のPyAutoGUIでマウスとキーボードを操作する方法を紹介しました。 単純な定型作業であれば、 Python に任せることでちょっとだけ楽な思いができるかもしれませんね。 今回はマウスでクリックする場所が事前に分かった状態で行っておりましたが、画像認識の機能を使うことでもう少し柔軟な操作が行えるようになります。 ご興味のある方は是非試してみてください。 参考サイト Welcome to PyAutoGUI’s documentation! — PyAutoGUI documentation Some characters are not working with German layout keyboard · Issue #46 · asweigart/pyautogui · GitHub   エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 https://rakus.hubspotpagebuilder.com/visit_engineer/ rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは。株式会社 ラク ス技術広報のlandhです。 ラク スには、現在(2022年6月時点)12種類の SaaS プロダクトが存在しています。 本記事では、当社の SaaS プロダクトを簡単にご紹介し、 SaaS プロダクトから ラク スがどのような企業なのかを読み解いていければと思います。 【目次】 ラクスのSaaSプロダクトラインナップ サービスの強み 12種類のSaaSプロダクトご紹介 楽楽精算 楽楽明細 楽楽電子保存 楽楽労務 楽楽勤怠 楽楽販売 楽テル メールディーラー チャットディーラーAI 配配メール ブラストメール ブラストエンジン ベスト・オブ・ブリード型での開発 SaaSプロダクトのマルチヒットメーカー 上場企業を5つ作れる規模のSaaSプロダクトを保有 募集ポジションについて SaaSプロダクト紹介 まとめ ラク スの SaaS プロダクトラインナップ ラク スでは、企業のさまざまな業務の効率化、付加価値化に貢献することを目指した クラウド サービス( SaaS )を提供しています。 SaaS プロダクトラインナップは、大きく分けると経費精算や販売管理などのバックオフィス業務と、またお客様からのお問い合わせ対応や マーケティング などのフロントオフィス業務を支援するサービスに分かれています。 その中で、バックオフィスを支援しているのは、 SaaS プロダクト名に「楽楽」と付いている、楽楽シリーズです。 直近では「楽楽 労務 」や「楽楽勤怠」をリリースしています。 フロントオフィスは、当社初の SaaS プロダクトである「メールディーラー」や「配配メール」をはじめとしたプロダクトがあります。 ラク スでは、これらの SaaS プロダクトを、約2年に1サービスのペースでリリースしており、現在、BtoBの SaaS プロダクトが12種類あります。 ※2022年5月時点 サービスの強み 画像引用元: ラクスについて | 株式会社ラクス 中途採用 ・自社で企画、開発、サポートしていて、使いやすさが常に進化 ・手厚い導入支援で、運用が軌道に乗るまでしっかりサポート ・解決できる課題が明確で、 工数 の軽減や対応品質向上などの導入効果が見えやすい ・利用者の満足度が高く、継続率が高い ・高度な IT技術 や知識がなくても導入できる  ・初期導入コストの抑制や導入期間の短縮 ・セキュリティ対応やシステムメンテナンスの手間いらず   引用元  https://www.rakus.co.jp/business/cloud/   12種類の SaaS プロダクトご紹介 楽楽精算 交通費・経費精算システム ≪公式サイト≫   www.rakurakuseisan.jp ≪内容≫  経費や交通費の申請~承認~精算を Webブラウザ 上で実現! 経理 部門の業務も効率化 ≪実現できること≫ • どこからでも Webブラウザ で申請、承認できて、作業時間が短縮 • 交通系ICカード を「ピッ」とするだけで交通費申請可能 • 自動仕訳や会計ソフト連携で 経理 業務の時間を削減 • 「乗換案内」内蔵で、交通費を自動計算 • 国内累計導入社数No.1! ※デロイト トーマツ ミック経済研究所「電帳法対応進む クラウド 型経費精算システム市場の実態と展望」(ミックITリポート2021年6月号: https://mic-r.co.jp/micit/ )より 楽楽明細 電子請求書発行システム ≪公式サイト≫   www.rakurakumeisai.jp ≪内容≫  請求書、納品書などの帳票を電子発行!印刷・封入作業をゼロにし、郵送コストも削減 ≪実現できること≫ • 請求書、支払明細書、納品書、領収書など、あらゆる帳票を電子化 • 請求データを取り込むだけで請求書を電子発行、お客様専用ページで公開 • 郵送費、印刷代、封筒代、紙代を0円にし、コスト削減 • 紙が必要な方には郵送代行も可能 楽楽電子保存 電子帳簿保存システム ≪公式サイト≫   www.rakurakudenshihozon.jp ≪内容≫  改正電帳法に対応!「楽楽明細」で受け取った請求書を全て電子保存! ≪実現できること≫ • 楽楽明細で受け取った請求書の保存&一元管理が可能 • メール添付で受け取った請求書などの帳票をアップロードし、管理できる機能リリースも現在検討中 楽楽 労務 従業員情報管理システム ≪公式サイト≫   www.rakurakuroumu.jp ≪内容≫  入社時に必要な従業員情報の収集や管理・更新の手間を削減! ≪実現できること≫ • 入社時に必要な従業員情報をWeb上で収集 • 電子契約で、 雇用契約 書などの入社書類をペーパーレス化 • 点在する従業員情報を一元管理 • マイナン バーなどの重要情報を安全に管理できる強固なセキュリティ体制 楽楽勤怠  勤怠管理システム ≪公式サイト≫   www.rakurakukintai.jp ≪内容≫  勤怠管理の"面倒くさい"から解放。有給・残業の管理も楽楽に! ≪実現できること≫ • 有給休暇の付与、取得、残数の把握を一括管理 • 残業時間をタ イムリ ーに簡単確認 • スマホ 打刻で外出先から打刻可能 • CSV 出力で給与計算システムと楽楽連携 楽楽販売 販売管理業務システム ≪公式サイト≫   www.rakurakuhanbai.jp ≪内容≫  販売管理のあらゆる業務をシステム化・自動化して効率的に! ≪実現できること≫ • 業務フローや課題にあわせたシステムを開発不要で構築 • 既存フローを変えずにシステム化が可能 • 画面のレイアウトや処理などを担当者様自身で設定可能 • ボタン1つで複数の処理を自動実行 • フォームからの問い合わせや注文メールの取り込みを自動化 楽テル コールセンター/ヘルプデスク向け CRM システム ≪公式サイト≫   www.rakutel.jp ≪内容≫  電話対応業務を効率化し、顧客対応品質・満足度を向上! ≪実現できること≫ • コールセンターを管理する CTI システムとのシームレスな連携で、着信時の顧客情報ポップアップやクリックトゥコールを実現 • 対応履歴の管理や上長への引継ぎがスムーズ • 問い合わせ対応標準テンプレート搭載 • 項目や画面の細やかなカスタマイズ機能が充実 メールディーラー 問い合わせ管理システム ≪公式サイト≫   www.maildealer.jp ≪内容≫  メールや電話、チャット、LINEからの問い合わせを一元管理。チーム内の情報共有が簡単になることで、お客様を待たせない対応を実現! ≪実現できること≫ • 国内で一番選ばれているメール管理システム • 継続利用率99%(当社お客様アンケート調べ) • 今、誰が、どのメールを返信中か把握できる • 誤送信防止機能が充実 チャットディーラーAI 社内向けAIチャットボット ≪公式サイト≫   www.chatdealer.jp ≪内容≫  社内問い合わせ対応を効率化するAIチャットボット。チャットボットの自動回答で、業務のムダを省き生産性の向上を実現! ≪実現できること≫ • 忙しい担当者に代わって、チャットボットが社内問い合わせに自動回答 • 情報を活用できる形に社内のナレッジを一元管理 • 従業員側も回答にたどり着きやすくなり満足度向上 • 400種類以上の社内用テンプレートを完備 • 学習済みの賢いAIを搭載しており、最初から高い回答精度を実現 配配メール メール マーケティング サービス ≪公式サイト≫   www.hai2mail.jp ≪内容≫  シンプルな操作と機能、手厚いサポートで、メールによる集客・販促活動を「もっと効果的に。もっと ラク に。」 ≪実現できること≫ • メール配信に特化したシンプルな操作画面でカンタンに配信 • 迷惑メール誤認を防ぐ複数の仕組みで受信ボックスへの高い到達率を維持 • 開封 /クリック計測、ABテスト配信など配信効果を改善する機能が充実 • 見込み顧客を 見える化 して、メール マーケティング の勝ちパターンを発見 ブラストメール 高速メール配信を低価格で実現 ≪公式サイト≫   blastmail.jp ≪内容≫  "初めての人でも使いやすいこと" を追求した、シンプルで簡単なメール配信システム ≪実現できること≫ • 圧倒的な配信パフォーマンスで高速かつ確実にメールをお届け • メール配信に特化!専門知識がなくても簡単メール配信 • 直観的に操作ができるHTMLエディターを標準搭載 • 開封 率・クリックカウントなどの効果測定も可能 • 11年連続顧客導入数シェア No.1!※ ※デロイト トーマツ ミック経済研究所「 クラウド 型eメール一斉配信サービスの市場動向」2021年 ブラストエンジン システム連携特化型のメール配信システム ≪公式サイト≫   blastengine.jp ≪内容≫   API 連携と SMTP リレーで、システムからのメール配信を簡単かつ確実に ≪実現できること≫ • メールサーバー管理は不要! クラウド 型のメール配信システム • 高速配信エンジン開発20年の実績で高いメール到達率を実現 • 豊富な API リファレンスを公開!無料トライアルで接続テストが可能 • 24時間365日の保守、安心のセキュリティ体制 • 専任スタッフが電話とメールで丁寧に問題解決までサポート ベスト・オブ・ブリード型での開発 ラク スは経営理念を「ITサービスで 企業の成長を 継続的に支援します。」としています。 この経営理念は SaaS プロダクトにも反映されており、ここまで13種類の SaaS をご紹介してきたプロダクトは、全てベスト・オブ・ブリード型で開発しています。 ベスト・オブ・ブリード型で提供していく理由としては、 「最適なタイミングで最適な技術を使ってプロダクトをお客様に届ける」ことを最も重視しているためです。 SaaS プロダクトの マルチヒット メーカー 画像引用元: ラクスについて | 株式会社ラクス 中途採用 ベスト・オブ・ブリード型で開発していった結果、成長性の高い SaaS プロダクトをいくつも展開できています。 例としては、上の図のように、売上シェア第1位のメールディーラー、楽楽明細、導入件数第1位の楽楽精算などが挙げられます。 上場企業を5つ作れる規模の SaaS プロダクトを 保有 それぞれ個別のマーケットにアプローチしているので、事業としてのバランスも良く、数字で見ても非常に事業が安定しています。 主な SaaS プロダクトを5つ集めるだけでも上場企業を5つ作れる規模のプロダクトを 保有 しています。 参照元 : 3年で2.4倍の売上高 ラクスのSaaS最強決算(2/7 ページ) - ITmedia ビジネスオンライン 大規模な SaaS プロダクトへの成長要因としては、以下の2つが考えられます。 ・ SaaS 型のプロダクトを展開しているため、一度導入すると離脱が起こりにくく、導入件数は増加している為。 ・ベスト・オブ・ブリード型の開発により、複数のお客様の課題に並列で取り組むことができる。 上記に加えて、現在それぞれの SaaS プロダクトへの導入数が年率で120%~130%で推移しながら成長しており、これは SaaS 企業として成長し続ける ラク スの特徴とも言えると思います。 今後も、企業のさまざまな業務の効率化、付加価値化に貢献するためにベスト・オブ・ブリード型で提供していく予定です。 募集ポジションについて 急成長中の ラク スでは、「日本を代表する SaaS 開発エンジニア集団へ」を目指し日々精進しており、採用も積極的に行っております。 様々なフェーズの SaaS プロダクトが存在しており、現在は募集も多いため、やりたいことに合わせて多様なキャリア設計が可能です。 SaaS プロダクト紹介 まとめ 当社の SaaS プロダクトから、ざっと簡単に説明させていただきましたが、 ラク スの様々な SaaS プロダクトへの理解は深まりましたでしょうか。 少しでも SaaS 開発を関わっている方のお役に立てていれば、幸いです。 当社にご興味持っていただけた方は、技術イベント・採用イベントも行っておりますので、是非のぞいてみてください! 最後までお読みいただきありがとうございました! ◆ 関連ブログ - ラクスの福利厚生をご紹介! - 【株式会社ラクス】SaaSプロダクト別の技術スタックを一挙公開! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに  はじめまして、 ラク スでインフラを担当しているftkenjです。 前回は 「DNSエラーってなに?」その原因と解決方法 を書かせていただきましたが、 今回は「自動化」、その中でもインフラエンジニアの視点に立って紹介させていただこうと思います。 はじめに 自動化とは? インフラで自動化といえば・・・ 主な使用例 自動化ツールの簡単な紹介 おわりに 参考文献 自動化とは?  人間による作業を削減できるテク ノロ ジー を使ってタスクを実行すること。 ただし、一口に「自動化」といっても様々なものがあります。 例を並べると以下になります。 IT 自動化 ビジネスの自動化 ビジネスプロセス自動化 ロボティック・プロセス・オートメーション 産業オートメーション 人工知能 機械学習 ディープラーニング  一番自動化でイメージしやすいのは「産業オートメーション」でしょうか。 工場などで製品の品質安定や人員の安全のためにロボットによる製作が該当します。 元々はこれがオートメーション(自動化)と呼ばれていたようですが、上記にもあるビジネスの自動化が出現したため混同しないよう「産業オートメーション」となったようです。  自動化をすることによって生産性の向上が期待できるのはもちろんのことですが、同時に信頼性の向上も期待できます。 機械で繰り返し同じ作業を行うため、テスト時点でほぼ問題ないとなっていればほぼ同じクオリティを出すことが可能です。 (ビジネスプロセス自動化、 ロボティック・プロセス・オートメーション 、産業オートメーションが特に恩恵が大きいのではないかと思います。) インフラで自動化といえば・・・  やはりサーバの構成管理となります。(上記でいうと「ITの自動化」にあたります) とはいえ、サーバの構成管理といっても範囲が広いため、もう少し細分化すると以下になります。 OSや ミドルウェア の設定ファイルの一元管理 サーバ構成のコード化 冪等性の担保  これらをツールを使用して管理と実行の自動化を行っていきます。 そうすることで属人化の防止、人為的ミスの削減、サーバの新規構築や設定変更時の作業時間短縮といったことが見込めます。  今まで物理サーバを サーバラックへマウント から サービスが提供できる状態 までおおよそ1日はかかると思います。 それを自動化すると数時間まで短縮させることも可能になります。 特に複数のサーバを構築するとなった場合は効果は目に見えて変わりますね。 主な使用例 構成管理 ・使用ツール: Git+Ansible 、 Git+Terraform 、 CloudFormation  サーバのMWやその設定ファイルの管理。 管理している側を正としておき、変更を行うときはGit側に行い常に冪等性を担保できる状態にしておきます。 AnsibleのymlファイルやtemplateなどをGitで管理していきます。 メンテナンス/リリース ・使用ツール: Git+Ansible+Rundeck  Rundeckなしでもansible-playbookコマンドでも管理対象サーバに変更を反映させることはできます。 ですが、Rundeckを使用することであらかじめ作成したジョブを実行させることが可能です。 また設定した時刻に自動実行させることもできます。 (つまり夜間作業でずっと起きている必要がなくなる!) 動作確認 ・使用ツール: Git+ Selenium  ブラウザ操作の動作確認などを自動化することが可能。 特に同じ動作確認を複数のサーバで実施するときに一括で行うことができ、基本的に起動後は完了するまで待つだけでよくなります。 Gitは、Ansible同様に Selenium の ソースコード を管理します。 自動化ツールの簡単な紹介 インフラの自動化で使用される主なツールをざっくりと紹介します。 Git   分散型 バージョン管理システム 。 今となってはメジャーなツールですね。 サーバ上のファイルを直接変更すると、事前にバックアップとしてコピーを取っていなかった場合、差分の確認もできず最悪もとの正常に状態に戻せないこともあります。 またコミットのコメントも残せるので、いつ・だれが・どういう変更を行ったかを追いやすいのもポイントです。 ただし、チーム内でルールを決めておかないと想定外の変更が入っていたり、リリースするブランチを間違えたりするので注意です。 Ansible   Agentlessなインフラ自動化ツール 。 手動で行っていた手順をプログラム化してサーバにプッシュすることが可能です。 ansible playbookは人間が読み取れる「 YAML 形式」で記述でき、様々なモジュールや実行したコマンドの結果で処理を分岐するなどある程度複雑なことも可能です。 デフォルトでは ssh で接続を行い、対象サーバにて上記のansible playbookの処理が行われます。 対象サーバもansible上で管理することができ、タグ付けでグループごとに実行するといったことも可能です。 Ansibleイメージ図 Terraform   マルチ クラウド 上のコンピュータやネットワークの構築を自動化するツール 。 IaC(Infrastructure as Code)を実現するために、開発されたツールです。 AWS だけでなくAzure、 GCP と特定のプロバイダーに限定されることなくインフラ構成をコード化することが可能となっています。 こちらもAnsibleと同様にコードをGitでバージョン管理することができます。 ただあくまで、インフラ構成までを管理することができます。 それより上のレイヤーについてはAnsibleを使用することになります。 AWS CloudFormation   AWS リソースのモデル化およびセットアップに役立つサービス 。 Terraformと異なり AWS でのみ利用可能です。(頭に AWS とついているので当たり前ですが。。。) AWS 特化のためリソース間の依存関係等については、いい感じに吸収してくれているようですが、コード化しきるまでが大変かつサービスの一部なので無料利用枠を超えてしまうと料金が発生してしまいます。 他にどれだけ AWS のサービスを使用しているかによりますが、どっぷり AWS に浸かっているのであればTerraformよりもこちらの方が良いかもしれませんね。 Rundeck   Agentlessなジョブ管理ツール 。 Rundeckを実行するサーバから対象サーバへ、 ssh が可能であれば使用することができます。 ジョブネットを作成することが可能であり、もしジョブAの実行に失敗したらジョブBは実行させずに終了させるなどが可能となっています。 また、cronと同じ要領であらかじめ指定した日時にジョブをスケジュール設定させることも可能です。 失敗時に通利することもできるので、定常作業であればジョブを作りこんでおけば夜間であってもコンソール画面に張り付いて作業を行う必要がなくなります。 夜間でなくても作業日時が決まっている場合スケジュール設定していれば、万が一作業のことを忘れてしまっても安心ですね。 Rundeckのイメージ Selenium   Web ブラウザの操作を自動化するための フレームワーク 。 対応ブラウザは Chrome 、 Firefox 、 Internet Explorer などがあり、それぞれのドライバーを実装することで各ブラウザに備わっている自動操作ライブラリを使用することができます。 しかし、ブラウザのバージョンは自動更新されますがドライバーはされないためドライバー管理ソフトウェアを使用する、もしくは 環境変数 にてドライバーを使用している場合は、その都度ドライバーを手動インストールしてくる必要があります。 とはいえ、何度も手でページ遷移を行うよりも Selenium で自動化されている方が断然楽になります。 おわりに  インフラの運用保守ではリリース作業などで深夜作業が多くなりがちです。 ですが、今回紹介した自動化ツールを使用することで、作業担当者が夜中ずっと起きてPCに張り付いている必要がなくなります。  それだけでなく、手動実行する場合であっても基本的にツールを実行するだけであり、誰が実行しても同じ結果となります。 そのため特定のメンバーしか作業ができない。。。といった属人化の防止にもつながります。 といいことばかり言っていますが、自動化するにはそれなりに労力と時間がかかるので一筋縄ではいきません。。。  既存環境を構成管理しようとすると、まずはどこまでの範囲を管理する必要があるのか、今までのリリース手順で自動化する場合、失敗したことを検知できるようエラーハンドリングを十分に行う必要があります。 エラーハンドリングが不十分な状態で自動化してしまうと、失敗したまま処理が進んでしまい取返しのつかないことになりかねないです。 そのためにも、しっかりと現状把握と自動化の準備を行ったうえで検証、動作確認、実装に望むことが大切です。 参考文献 自動化について 【初心者向け】Gitとは何なのか。基本用語やその仕組みをまとめています。 Ansible とは 5分で分かるTerraform(Infrastructure as Code) AWS CloudFormation とは ジョブスケジューラ「Rundeck」を試してみる Seleniumを理解する!初心者でも分かる歴史、特徴、基礎知識などを簡単に解説! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
皆様こんにちは。インフラエンジニアのryuhei55225です! 前回の記事 は【Kibana 入門】という形で「Elastic Stack」(Kibana+Logstash+Elasticsearch)を Linux サーバにインストールして、簡単なデータのグラフ化を紹介させて頂きました。 こちらの記事では、MWのインストール等のサーバ構築がメインとなってしまい、具体的なElasticsearchへのデータ投入やデータ検索・グラフ化に関しては紹介することが出来ておりませんでした。 (データ検索・グラフ化に関しては、完全に省略しておりました。。) ということで・・・ 今回の記事は「Kibana 基礎」という形で、実際前回構築したサーバのElasticsearchにLogstash経由でデータを投入して、Kibanaにてデータをグラフ化をしていく流れについて、具体的に紹介 していこうと思います。 今回の流れとしては、 まず、Logstashの機能について一般的なことを紹介 その後、実際に今回やってみた内容(データのグラフ化)を紹介 していきます。 なんで、Logstashについてある程度理解されている方は、「実際にやってみた」から見て頂ければと思います。 Logstashを用いたデータの収集・加工・転送 Inputプラグイン Filterプラグイン Outputプラグイン 実際にやってみた データの準備 Logstash設定ファイルの作成 設定の反映と確認 今回作成した設定ファイルについて Inputプラグイン Filterプラグイン Outputプラグイン Kibanaを用いたGUIでのElasticsearchデータ確認 KibanaによるElasticsearchデータのグラフ化 グラフの選択 対象のデータを選択 Y軸の設定 X軸の設定 最後に Logstashを用いたデータの収集・加工・転送 Logstashは、さまざまなデータソースからのログやデータを、収集・加工・転送するためのツールです。 前回の記事では、その中の機能の一部として、 CSV ファイルからデータを収集して、Elasticsearchにデータを転送する形を紹介させて頂きましたが、実際にLogstashで出来ることはたくさんありますので、もう少しその要素( コンポーネント )について見ていきたいと思います。 Logstashの設定は、大きく「Input」「Filter」「Output」に分けられます。 「Input」でデータを収集して、「Filter」で加工、「Output」で転送という流れです。 それぞれの プラグイン ごとに、様々なものと連携可能ですので、ざっくり プラグイン について紹介していこうと思います。 Input プラグイン Input プラグイン で対応しているデータの取得対象は数多くあります。 全て紹介することは出来ませんので、今回は前回しようした「file」をもう少し詳細に紹介していこうと思います。 その他にも、「Elasticsearch」を指定することで、Elasticsearchのデータを取得するなんてことも出来ます。 以下サイトに対応している プラグイン の記載がありますので、詳しくはやりたいことにあった プラグイン を調べてもらえればと思います。 www.elastic.co Filter プラグイン Filter プラグイン では、実際にデータの整形を実施していきます。 こちらも様々な プラグイン が存在しますが、今回は実際にInput プラグイン で指定した CSV ファイルを整形するにあたって、よく使う プラグイン や便利な プラグイン について紹介していきます。 こちらも、以下サイトに対応している プラグイン の記載がありますので、もっと勉強したい方はこちらも参考にして頂ければと思います。 www.elastic.co Output プラグイン 最後にOutput プラグイン です。 こちらはデータの出力先となり、様々な プラグイン に対応がありますが、「Elastic Stack」を構成している場合は、ほとんどElasticsearchになるかと思います。 今回も実際に CSV ファイルからInputしたデータを、Filterで整形して、ElasticsearchにOutputする形を紹介していきます。 www.elastic.co 実際にやってみた ということで、早速サンプルデータをLogstash経由でElasticsearchに投入して、Kibanaでグラフ化していく流れを紹介していきます。 データの準備 今回用意したデータは、以下のようにtest-serv01~50のサーバのディスク容量を使用量を残容量に分けて一週間に一度計測したデータを CSV ファイルにしています。 こちらのデータをElasticsearchに投入していこうと思います。 # cat /data/server_disk_check.csv 2021/10/01 00:00:00,test-serv01,DBディスク使用量(GB),51.890850067139 2021/10/01 00:00:00,test-serv01,残DBディスク容量(GB),55.363399505615 2021/10/01 00:00:00,test-serv02,DBディスク使用量(GB),38.047119140625 2021/10/01 00:00:00,test-serv02,残DBディスク容量(GB),49.211036682129 2021/10/01 00:00:00,test-serv03,DBディスク使用量(GB),40.542625427246 2021/10/01 00:00:00,test-serv03,残DBディスク容量(GB),46.715530395508 2021/10/01 00:00:00,test-serv04,DBディスク使用量(GB),37.850318908691 ・ ・ ・ 2021/10/01 00:00:00,test-serv49,DBディスク使用量(GB),24.080196380615 2021/10/01 00:00:00,test-serv49,残DBディスク容量(GB),103.170146942139 2021/10/01 00:00:00,test-serv50,DBディスク使用量(GB),3.631404876709 2021/10/01 00:00:00,test-serv50,残DBディスク容量(GB),83.626750946045 2021/10/08 00:00:00,test-serv01,DBディスク使用量(GB),51.983116149902 2021/10/08 00:00:00,test-serv01,残DBディスク容量(GB),55.271133422852 2021/10/08 00:00:00,test-serv02,DBディスク使用量(GB),38.138965606689 2021/10/08 00:00:00,test-serv02,残DBディスク容量(GB),49.119190216064 ・ ・ ・ 2022/05/27 00:00:00,test-serv49,DBディスク使用量(GB),26.486736297607 2022/05/27 00:00:00,test-serv49,残DBディスク容量(GB),100.763607025146 2022/05/27 00:00:00,test-serv50,DBディスク使用量(GB),13.513370513916 2022/05/27 00:00:00,test-serv50,残DBディスク容量(GB),73.745048522949 Logstash設定ファイルの作成 このデータをElasticsearchに投入するための設定ファイルを以下のように記載します。 これで、 CSV ファイルのデータをそのままElasticsearchに投入可能です。 # cat /etc/logstash/conf.d/server_disk_check.conf input { file { path => "/data/server_disk_check.csv" start_position => "beginning" type => "server_disk_check" } } filter { if [type] == "server_disk_check" { csv { columns => [ "date" , "host" , "item" , "number" ] separator => "," } date { match => [ "date" , "YYYY/MM/dd HH:mm:ss" ] } mutate { convert => { "number" => "float" } } } } output { if [type] == "server_disk_check" { elasticsearch { hosts => ["localhost:9200"] index => "%{type}" } } } 設定の反映と確認 上記ファイルを作成したら、Logstashのサービスを再起動して、Elasticsearchにインデックスが作成されていることを確認します。 # systemctl start logstash.service # curl -XGET 'http://localhost:9200/_cat/indices?' yellow open server_disk_check wKGD-LlKSPqZjHJK8UMa_Q 1 1 3500 0 730.9kb 730.9kb curl でElasticsearchの情報をとってきて、「server_disk_check」のインデックスが作成されていることを確認できれば、一旦OKです。 ※ここでElasticsearchにインデックスが作成されていない場合は、「/var/log/logstash/logstash-plain.log」を確認しましょう。  設定ファイルの書式間違い等であれば、こちらのログに何が間違えているか記載されていることが多いです。 今回作成した設定ファイルについて では、設定ファイル(/etc/logstash/conf.d/server_disk_check.conf)に話を戻して、詳しく設定内容について確認していきます。 Input プラグイン まずInputからですが、今回は CSV ファイルからのデータ取得ですので、file プラグイン を使用します。 「path」 ここで、読み取る CSV ファイルのパスを指定します。 当たり前ですが、ここで設定しないと、Logstashはどのファイルを読み取ればいいのか、判断つきません。 「start_position」 こちらは、Logstash起動時にファイルの行頭から読むのか末尾から読むのかを指定します。 今回のデータに関しては、タイムスタンプ(date)も同時に出力するので、どちらでも問題はないのですが、通常ファイルの最初からデータを作成するので、作成した順にデータの読み取るよう、「beginning」を指定します。 ※デフォルトは、「end」(末尾から読み取り)になっているようです。 「type」 Logstashで取得するデータが今回のものだけであれば、こちらの設定は不要になりますが、明示的に「type」を設定することで他のLogstashの設定と混ざらないようにします。 ここでは、分かりやすいように、ファイル名=インデックス名=「type」=「server_disk_check」とします。 ※もちろん、それぞれの名前を分けることも可能です。 Filter プラグイン 続いてfilterについて確認していきます。 流れ的にはInputした CSV ファイルからデータを取得して意図した形にデータを整形することになりますが、その前にInputで使用した「type」(server_disk_check)の条件分岐をしてあげます。 ※これを忘れると、他のインデックスに影響を与えてしまします。 「 csv 」 columnsにて カラム名 をしていきます。 見たままですが、今回は左から「タイムスタンプ、サーバ名、対象データ、値」となっておりますので、分かりやすいように 命名 していきます。 「date」 Elasticsearch で「timestamp」として使用するカラムの日時フォーマットを指定します。 CSV ファイルに記載したフォーマットをそのまま記載すればOKです。 「mutate」 CSV ファイルから取得したデータは、デフォルトで文字列として認識されて、Elasticsearchに出力されてしまいます。 そのため、数値については、convertを使用してfloat等にデータ型を変更する必要があります。 mutateについては、convert以外にも使用可能ですが、今回は省略させて頂きます。 Output プラグイン 最後にoutputです。ここは特に難しいことはしておらず、見たままかと思います。 今回は、「type」=インデックス名としたので、インデックスの指定を"%{type}"としています。 Kibanaを用いた GUI でのElasticsearchデータ確認 続いて、Elasticsearchに入れたデータを実際にKibanaで確認してグラフ化していきたいと思いますのが、 その前に、Elasticsearchにどのような形でデータが投入されているかKibanaの GUI 上で確認していこうと思います。 まずは、Kibanaに新しく作成したインデックスをElasticsearchに登録します。 メニューから、「Stack Management」→「Index patterns」と選択して、「Create index pattern」からインデックスをElasticsearchに登録していきます。 ※次の画面で出てくる「Time field」に関しては、「date」を選択してあげます。 インデックスの登録が完了しましたら、実際にどのような形でElasticsearchにデータが投入されているか確認していきます。 今回はKibanaのDiscover画面から確認していきます。 続いての画面で、 ①:今回作成したインデックスを選択 ②:対象期間を選択(今回は一年) ③:表示するカラムを選択※デフォルトだと全て表示されるため、見づらくなります を実施していくと、以下のような形で、実際にElasticsearchにデータが投入されていることが確認できます。 この時点でKibanaのFilterを駆使することで、見たいデータだけを選択することも可能ですが、 今回は「Visualize Library」からElasticsearchのデータをグラフ化する流れについて説明していきます。 KibanaによるElasticsearchデータのグラフ化 ようやくElasticsearchにデータが投入されていることが確認できましたので、ここから実際にグラフ化していきたいと思います。 グラフの選択 グラフ化に関しては、「Visualize Library」という部分から実施していきます。 上記、画面で「Create visualization」を選択して、作成するグラフを選択していきます。 今回は、単純にディスク容量の推移を確認するために、折れ線グラフを使用していきたいと思います。 以下のように、選択して最後に対象インデックスに今回作成したものを選択します。 続いて、 GUI 上からグラフの設定をしていくことで、以下のようなグラフを作成していきます。 今回は、サーバ毎のディスク使用量の推移をグラフ化していきます。 (あくまで参考ですので、自身で実施する際はこちらを参考に、色々設定をいじってみてください。) では、続いてグラフ作成の手順を紹介していきます。 対象のデータを選択 今回Elasticsearchには、「DBディスク使用量(GB)」と「残DBディスク容量(GB)」の2種類が存在しますので、 「DBディスク使用量(GB)」のみが対象になるように、フィルターしてあげます。 フィルターは画面左上の「Add filter」から以下のように設定してあげます。 Y軸の設定 画面右に表示されている設定の「data」から「Metrics」の設定をしていきます。 具体的に言うと、Y軸に何の値をとるか、です。 Aggregation 今回は、サーバ毎にデータが一つしかありませんので、設定はなんでも問題ありません。 もし、全サーバのディスク容量の合計を表示したいとかであれば、こちらを「Sum」にする必要があります。 ※その場合は、グラフの分割方法についても修正する必要があります。 Feild 今回は、実際の値(ディスク容量)を表示したいので、Elasticsearch内のカラム「number」を「Field」に選択します。 Custom label グラフのラベル(Y軸)に何を書くかです。 設定しなくてもグラフ化は可能ですが、設定しておくとグラフが見やすくなります。 X軸の設定 続いて、「Buckets」でX軸の設定をしていきます。 今回は、時系列のグラフを作成するので、X軸に日付を選択していきます。 具体的には、「X-axis」を以下のように設定します。 続いて、「Split series」でグラフの分割方法を選択していきます。 今回はサーバ毎に折れ線を分けたいと思いますので、「Sub aggregation」で「Terms」を選択して、 「Field」で分割する単位である「host.keyword」を選択します。 これで、グラフの設定は以上です。 ここまで設定すれば、初めに見せたグラフが表示されるはずです。 最後に 今回はグラフ化ということで紹介はここまでにさせて頂きますが、Kibanaでは様々なグラフが作成できますので、実際に触っていくと、「こんなこともできるのか」といった発見があるかと思います!! サーバ毎の推移をグラフ化しましたが、その他にもディスク使用容量の全体の合計値の推移や、単位時間当たりのディスク使用容量の最大値の取得等、データさえ入っていれば、だいたいのことは出来ると覆います。 一般的な監視ツール( Nagios 、Zabbix、etc...)でも簡単なグラフの作成は可能ですが、Kibanaに比べると対応しているグラフは少ないかと思います。 そんな時は一度Kibanaにデータを移行してあげれば、作成したグラフが簡単に取得できる、なんてこともあるかと思います。 ということで、今回の紹介は以上にさせて頂きたいと思います。 次回も書くことがあれば、「応用編」としてElasticsearchやLogstashの便利な使い方について紹介していこうと思います。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは、弊社サービスのインフラを運用している id:keijiu (ijikeman)です。 今回は、 「Ansible実行を1/3に高速化した話」 を記載します。 目次 目次 背景 1. 実行時間の把握 1-1. Callbackプラグイン[profile_tasks]を有効にする 1-2. CallBack Plugin有効化の確認 2. Ansibleコードチューニング 2-1. 各タスク実行時間の確認 2-2. パッケージインストール処理の見直し 2-2-1. コードの確認 2-2-2. コードの修正 2-3. 動的書き換えの見直し 2-3-1. コードの確認 2-3-2. コードの修正 3. Ansible実行環境チューニング 3-1. 並列実行数の変更 3-2. Pipelining Pipeliningの有効化 Pipelining有効化の確認 3-3. サードパーティ製 Strategy Plugin (Mitogen for Ansible) Mitogen利用時に気付いた点 Mitogenの利用方法 3-4. 上記実行環境毎の実行速度の計測比較結果 実行速度検証総括 4. 高速化の結果 背景 3年ほど前よりAnsible構成管理を推進を開始し、ようやく ラク スの各商材のAnsible化の割合が大幅に増えました。 構成管理対象サーバの増加と構成管理範囲が増えるに伴い、Ansibleの実行完了までの時間が大幅に増えていきました。 また、インフラメンバー各自がAnsibleコードを記載し、テスト実行(--check)等をする回数も増えこの「実行時間の長時間化」が問題となりました。 この問題の改善にあたり、調査・検証し、実際に実行時間を大幅に削減した内容を公開させていただきます。 1. 実行時間の把握 まずはAnsibleの全体の実行時間や各タスク毎の実行時間の把握を行い、時間がかかっている処理がどの処理なのかを把握することが重要です。 各タスクの中で時間がかかりすぎているものを把握することで、処理や環境の見直しによって改善できるものがないかを確認します。 1-1. Callback プラグイン [profile_tasks]を有効にする 各タスクの実行に掛かった実行時間を表示するために、callback プラグイン profile_tasksを有効化します。 $ /etc/ansible/ansible.cfg --- [defaults] callback_whitelist = profile_tasks --- もしくは 実行時にANSIBLE_CALLBACK_WHITELIST='profile_tasks'を設定 ANSIBLE_CALLBACK_WHITELIST='profile_tasks' ansible-playbook ... 1-2. CallBack Plugin有効化の確認 下記の様に各タスクの実行時間が表示され、実行完了後には時間がかかったタスクのTOP20までがリスト表示される様になります。 ... RUNNING HANDLER [os : Install Epel.repo] ***************************************** Sunday 05 June 2022 19:33:32 -0400 (0:00:01.327) 0:00:01.356 ********** skipping: [192.168.0.1] PLAY RECAP ********************************************************************* 192.168.0.1 : ok=34 changed=1 unreachable=0 failed=0 skipped=3 rescued=0 ignored=0 Thursday 05 May 2022 21:09:47 -0400 (0:00:00.058) 0:00:14.804 ********** =============================================================================== os : Install Epel.repo ------------------------------------------ 1.66s os : Install python module for SELinux -------------------------- 1.12s Gathering Facts ------------------------------------------------- 1.07s 2. Ansibleコードチューニング 2-1. 各タスク実行時間の確認 では、実行時間の把握ができるようになったところで、実際のPlaybookを実行しました。 未セットアップの CentOS に対するPlaybookの実行結果は以下のようになりました。(一部伏字) 圧倒的にパッケージのインストール時間が大半でしたので、まずはパッケージインストール処理のコードを確認してみます。 すると 約6割がパッケージインストール処理 でした。 Tuesday 07 June 2022 01:14:14 -0400 (0:00:03.472) 0:05:20.110 ********** common : Install Packages --------------------------------------------- 190.71s common : Copy Scripts ------------------------------------------------- 14.21s os : Server Reboot ------------------------------------------------------- 11.57s os : Server Reboot ------------------------------------------------------- 9.78s os : Install python module ----------------------------------------------- 8.90s os : Install NetworkManager -------------------------------------------- 6.51s ... 2-2. パッケージインストール処理の見直し 2-2-1. コードの確認 実コードは記載できませんが、サンプルコードで説明すると以下の様な処理になっていました。 - set_fact: INSTALL_PACKAGES: - NAME: 'httpd' REPO: 'appstream' - NAME: 'httpd-devel' REPO: 'appstream' - NAME: 'httpd-tools' REPO: 'appstream' - NAME: 'httpd-manual' REPO: 'appstream' - name: Install Packages yum: name: "{{ item.NAME }}" state: 'installed' enablerepo: '{{ item.REPO }}' with_items: "{{ INSTALL_PACKAGES }}" 実際にこのコードを実行すると以下のようになります。 パッケージを1つずつインストールしている為、パッケージ対象が増えれば増えるほど毎回 yum moduleが呼び出され時間がかかる実装になっています。 --- 実行ログ TASK [test : Install Packages] ************************************************ Friday 06 May 2022 01:34:02 -0400 (0:00:00.024) 0:00:07.367 ************ changed: [192.168.0.1] => (item={'NAME': 'httpd', 'REPO': 'appstream'}) changed: [192.168.0.1] => (item={'NAME': 'httpd-devel', 'REPO': 'appstream'}) changed: [192.168.0.1] => (item={'NAME': 'httpd-tools', 'REPO': 'appstream'}) changed: [192.168.0.1] => (item={'NAME': 'httpd-manual', 'REPO': 'appstream'}) ... 2-2-2. コードの修正 以下ansible公式 ansible.builtin.yum module – Manages packages with the yum package manager — Ansible Documentation の yum moduleのサンプルコードにもあるようにパッケージ名は配列にて渡すことができます。 - name: Install a list of packages with a list variable yum: name: "{{ packages }}" vars: packages: - httpd - httpd-tools 以下様に書き換えを行う 「パッケージ名をリスト形式に変更する」 ことで、 yum モジュールの実行回数を大幅に削減することができます。 実際にこのサンプルコードでも実行時間を約半分にすることができました。 - set_fact: INSTALL_PACKAGES: - NAME: - 'httpd' - 'httpd-devel' - 'httpd-tools' - 'httpd-manual' REPO: 'appstream' --- 実行ログ TASK [test : Install Packages] ************************************************ Friday 06 May 2022 01:36:19 -0400 (0:00:00.023) 0:00:03.246 ************ changed: [192.168.0.1] => (item={'NAME': ['httpd', 'httpd-devel', 'httpd-tools', 'httpd-manual'], 'REPO': 'appstream'}) 2-3. 動的書き換えの見直し ラク スで使っているansibleコードテンプレートでは、汎用的な処理を別のタスクファイルにして、 「include_role」や「include_tasks」で動的に読み込み書き換えることで、コードの再利用性を高める取り組みを行っております。 その為、繰り返し処理を行うと毎回コードの動的書き換えと実行が行われる為、処理時間が伸びていきます。 2-3-1. コードの確認 以下のように汎用的な処理を別のロールに集めており $ roles/libraries/tasks/copy.yml --- - name: FILE COPY copy: src: "{{ item.SRC }}" dest: "{{ item.DEST }}" owner: "{{ item.OWNER }}" group: "{{ item.GROUP }}" mode: "{{ item.MODE }}" 呼び出し元からinclude_role等で呼び出して利用しています。 $ roles/test/tasks/main.yml --- - name: Copy files include_role: name: libraries tasks_from: copy.yml with_items: - { SRC: 'test1', DEST: '/tmp/test1', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test2', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test3', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test4', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test5', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test6', OWNER: 'root', GROUP: 'root', MODE: '0644' } 実行すると、毎回copy.ymlが呼ばれる毎に処理が行われる為、毎回TASKが呼ばれます。 TASK [Copy files] ******************** Monday 06 June 2022 02:00:31 -0400 (0:00:01.356) 0:00:01.386 TASK [libraries : FILE COPY] ********* Monday 06 June 2022 02:00:31 -0400 (0:00:00.087) 0:00:01.474 changed: [192.168.0.1] TASK [libraries : FILE COPY] ********** Monday 06 June 2022 02:00:33 -0400 (0:00:01.099) 0:00:02.573 changed: [192.168.0.1] TASK [libraries : FILE COPY] ********** Monday 06 June 2022 02:00:33 -0400 (0:00:00.910) 0:00:03.484 changed: [192.168.0.1] TASK [libraries : FILE COPY] ********** Monday 06 June 2022 02:00:34 -0400 (0:00:00.869) 0:00:04.354 changed: [192.168.0.1] TASK [libraries : FILE COPY] *********** Monday 06 June 2022 02:00:35 -0400 (0:00:00.889) 0:00:05.243 changed: [192.168.0.1] TASK [libraries : FILE COPY] ********* Monday 06 June 2022 02:00:36 -0400 (0:00:00.854) 0:00:06.098 changed: [192.168.0.1] PLAY RECAP ********************************************* 192.168.0.1 : ok=7 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Monday 06 June 2022 02:00:37 -0400 (0:00:00.830) 0:00:06.928 *********** =================================================== Gathering Facts --------------- 1.36s libraries : FILE COPY ---------- 1.10s libraries : FILE COPY ---------- 0.91s libraries : FILE COPY ---------- 0.89s libraries : FILE COPY ---------- 0.87s libraries : FILE COPY ---------- 0.85s libraries : FILE COPY ---------- 0.83s Copy files --------------------- 0.09s 2-3-2. コードの修正 動的書き換え(include_*)を使わずに静的処理に変更します。 $ roles/test/tasks/main.yml --- - name: Copy files copy: src: "{{ item.SRC }}" dest: "{{ item.DEST }}" owner: "{{ item.OWNER }}" group: "{{ item.GROUP }}" mode: "{{ item.MODE }}" with_items: - { SRC: 'test1', DEST: '/tmp/test1', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test2', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test3', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test4', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test5', OWNER: 'root', GROUP: 'root', MODE: '0644' } - { SRC: 'test1', DEST: '/tmp/test6', OWNER: 'root', GROUP: 'root', MODE: '0644' } Ansibleを実行するとわずかですが、実行時間が短くなりました。 書き換えを行う回数が増えるほど効果は高くなりますが、変更による効果はそれほど高くない為、「コードの再利用性」とどちらがよいかはで使い分ける必要がありそうです。 TASK [test : Copy files] *********************************** Monday 06 June 2022 02:01:26 -0400 (0:00:01.413) 0:00:01.437 *********** changed: [192.168.0.1] => (item={'SRC': 'test1', 'DEST': '/tmp/test1', 'OWNER': 'root', 'GROUP': 'root', 'MODE': '0644'}) changed: [192.168.0.1] => (item={'SRC': 'test1', 'DEST': '/tmp/test2', 'OWNER': 'root', 'GROUP': 'root', 'MODE': '0644'}) changed: [192.168.0.1] => (item={'SRC': 'test1', 'DEST': '/tmp/test3', 'OWNER': 'root', 'GROUP': 'root', 'MODE': '0644'}) changed: [192.168.0.1] => (item={'SRC': 'test1', 'DEST': '/tmp/test4', 'OWNER': 'root', 'GROUP': 'root', 'MODE': '0644'}) changed: [192.168.0.1] => (item={'SRC': 'test1', 'DEST': '/tmp/test5', 'OWNER': 'root', 'GROUP': 'root', 'MODE': '0644'}) changed: [192.168.0.1] => (item={'SRC': 'test1', 'DEST': '/tmp/test6', 'OWNER': 'root', 'GROUP': 'root', 'MODE': '0644'}) PLAY RECAP ********************************************************************* 192.168.0.1 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Monday 06 June 2022 02:01:31 -0400 (0:00:05.252) 0:00:06.689 *********** =================================================== test : Copy files --------------------5.25s Gathering Facts -------------------1.41s 3. Ansible実行環境チューニング 3-1. 並列実行数の変更 Ansibleの標準設定では並列で実行するターゲット数が"5"と設定されている為、多くのターゲットに対して実行する場合に時間がかかります。 その為、並列実行数の変更を行います。 FORKS設定はAnsibleの子プロセスを増やす設定の為、増やせば増やすほどCPUへの負荷がかかります。 各CPUの使用率を確認し、不足しているようであればCPU数を増やすことを検討してください。 $ /etc/ansible/ansible.cfg --- [defaults] forks = 5 もしくは 実行時にANSIBLE_FORKS=NUMを設定 ANSIBLE_FORKS=20 ansible-playbook ... 3-2. Pipelining ansibleのデフォルト設定の場合、各タスクを実行する度に以下の例の様にターゲット側の"~{ansible実行ユーザのホーム ディレクト リ}/.ansible/"に python プログラムの展開を行いAnsibleを実行しています。 その為、ターゲットへのデータ転送時間とこれらのファイルの書き込み等により、各タスクの実行に時間がかかります。 /home/ansible_user/.ansible/ansible-tmp-1651804170.1883245-2652826-75480101479031/ 合計 120 drwx------. 2 root root 31 5月 5 22:28 . drwx------. 3 root root 68 5月 5 22:28 .. -rw-------. 1 root root 119155 5月 5 22:28 AnsiballZ_stat.py Pipeliningの有効化 Pipeliningを有効化することで、 ssh のpipeを経由してansibleを実行することで高速化を行うことができます。 $ /etc/ansible/ansible.cfg --- [ssh_connection] pipelining = True ※設定を記載するセクションは[defaults]ではなく[ ssh _connection]であることに注意してください もしくは 実行時にANSIBLE_PIPELINING=True か ANSIBLE_PIPELINING=1を設定 ANSIBLE_PIPELINING=1 ansible-playbook ... 公式ドキュメント Ansible Configuration Settings — Ansible Documentation にもある通り、 requrettyが設定されているユーザでansibleを実行する場合は、sudo設定にて無効にする必要がある為注意が必要です。 Pipelining有効化の確認 Ansible実行時にPipeliningが有効になっているかは、-vvvオプションを付けることで確認することができます。 以下の例の様に "Pipelining is enabled" と出力されていることを確認してください。 ... Pipelining is enabled. <192.168.0.1> ESTABLISH SSH CONNECTION FOR USER: ansible_user ... 3-3. サードパーティ 製 Strategy Plugin (Mitogen for Ansible) Mitogen — Mitogen Documentation Other Toolsとして公式で紹介されているMitogen for Ansibleを使うことで、上記Pipeliningよりも実行速度の高速化を行うことができます。 Mitogenは 実行コードのRAMへのキャッシュ pipelineと同様ターゲットへの書き込み削減 従来のPreforkモデルに加えて、Thread化によるさらなる並列化・高速化 ネットワークコネクションの再利用等 によりAnsibleの実行を高速化することができます。 Mitogen利用時に気付いた点 Mitogen利用時に以下の事象が確認できましたので、利用するには事前に検証が必要です。 現行v0.2.9ではターゲットサーバ側に"/usr/bin/ python "が必要(/usr/bin/python3ではダメ) ansible 2.10以降には未対応 (2022/06/13時点でAnsible v2.10以上に対応するv0.3.xは未リリース 2022/06/13追記) ターゲットの python 環境によっては動かないコードがある include_roleは未対応 CPU使用率がPipeliningよりかなり高い メモリの使用率が高い Pipeliningとの併用はできない どなたかv2.10以降でmitogen for Ansibleが動くよというコメントをいただきましたが、公式ニュースに記載されているように v0.2.xは2.10未満 v0.3.x系は2.10以上 をサポートという風にバージョンが分かれるようです(2022/06/13追記) Release Notes — Mitogen Documentation Mitogenの利用方法 ■Ansible実行環境にMitogenを設置 $ curl -kL -o https://networkgenomics.com/try/mitogen-0.2.9.tar.gz $ tar zxvf mitogen-0.2.9.tar.gz -C /opt/ ■Strategy プラグイン としてmitogenを設定 $ ansible.cfg --- [defaults] strategy_plugins = /opt/mitogen-0.2.9/ansible_mitogen/plugins/strategy ■各playbook毎にstrategyを設定してmitogenを有効化します。(ansible.cfgで全体に適用してもよい) $ playbook.yml --- - name: Test Playbook strategy: mitogen_linear hosts: test-servers ... あるPlaybookの実行時間の差を確認したところ、以下様にかなりの差がありました。 Default状態 ... 0:00:22.399 Pipelining ... 0:00:17.744 Mitogen for Ansible ... 0:00:09.333 3-4. 上記実行環境毎の実行速度の計測比較結果 検証としてターゲットサーバ20台に対し、 ディレクト リ10個作成する処理を行いました。 それぞれの環境毎に5回実行し、一番早い時間と一番遅い時間を除く3回の平均時間を記しています。 [検証結果] AnsibleサーバCPU数 FORK数 CPU使用率 平均実行時間 コメント [Default] 1 5 20 - 40% 01:39 1 10 20 - 70% 01:05 1 20 30 - 100% 00:42 CPUが100%となる場合があり処理待ちが発生 2 5 10 - 20% 01:36 1 vCPUでもCPUに余裕があった為、CPU追加効果なし 2 10 20 - 40% 01:01 1 vCPUでもCPUに余裕があった為、CPU追加効果なし 2 20 15 - 80% 00:36 CPU 100%が解消され処理時間が短縮 [Pipelining] 1 5 30 - 40% 00:40 Pipeliningの効果が高いが若干CPU使用率が上昇 1 10 30 - 70% 00:28 Pipeliningの効果が高いが若干CPU使用率が上昇 1 20 30 - 100% 00:21 Pipeliningの効果が高いが若干CPU使用率が上昇 2 5 15 - 30% 00:38 1 vCPUでもCPUに余裕があった為、CPU追加効果なし 2 10 20 - 40% 00:26 1 vCPUでもCPUに余裕があった為、CPU追加効果なし 2 20 30 - 50% 00:17 CPU 100%が解消され処理時間が短縮 [Mitoge] 1 5 100% 00:18 Pipeliningよりさらに効果が高いがCPU使用率がかなり上昇 1 10 100% 00:16 Pipeliningよりさらに効果が高いがCPU使用率がかなり上昇 1 20 100% 00:16 Pipeliningよりさらに効果が高いがCPU使用率がかなり上昇 2 5 25 - 50% 00:19 CPUに余裕がでたが、CPU追加効果なし 2 10 40 - 60% 00:16 CPUに余裕がでたが、CPU追加効果なし 2 20 30 - 70% 00:14 SWAPが発生 2+2GBメモリ 20 50% 00:14 メモリを追加することでSWAPが解消 実行速度検証総括 Pipelining Ansible標準機能として実装されている為、手軽に利用が可能で効果が高い為、標準で利用できるようにしておくとよい。 Ansible実行側のCPU使用率の上昇が若干見られるが、実行環境の大幅な見直しは必要ない。 並列実行数(ANSIBL_FORKS) 並列実行数を上げて実行する場合はAnsible実行側のCPU使用率を確認して引き上げる必要がある。 Mitogen for Ansible CPUの使用率が高い為、CPUの割り当て数に注意が必要。 並列実行数を増やすAnsible実行サーバのメモリ使用量が上がる為、メモリの割り当てに注意が必要。 初期導入作業が必要だが、Pipelineよりも効果が高い。 4. 高速化の結果 最終的に Mitogen Plugin及び上記コードの改修等を進め 以下の様に 1/3に高速化することができました!! ■改修前 Tuesday 07 June 2022 01:14:14 -0400 (0:00:03.472) 0:05:20.110 ********** ■改修後 Tuesday 07 June 2022 01:04:01 -0400 (0:00:02.327) 0:01:34.366 ********** 終わり エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに はじめまして。knng1です。 本記事では、AnsibleのRoleの使用方法についてまとめていきたいと思います。 Ansibleを実務で使用する場合、基本的にはRole化されているところが多いと思いますので、 これからAnsibleを触る方 最近Ansibleの勉強を始めた方 の参考になれば幸いです。 はじめに 1.Ansible とは 2.Ansible 使用例 ディレクトリ sample.yml 実行ログ 3.Roleについて ディレクトリ sample-role.yml roles/yum/tasks/main.yml roles/template/tasks/main.yml roles/template/handlers/main.yml 実行ログ 4.その他の機能について defaults (roles/<Role名>/defaults/main.yml) サンプル vars (roles/<Role名>/vars/main.yml) files (roles/<Role名>/files/main.yml) meta (roles/<Role名>/meta/main.yml) 5.Ansible Role おまけ ansible-galaxy init 1.Ansible とは Ansibleは RedHat 社が開発する OSS の構成管理ツールです。 あらかじめ記述したPlaybookファイル通りにソフトウェアのインストールや設定を自動で行うことができます。 管理対象機器には SSH プロトコル を使用するため、エージェントレスでの導入が可能です。 新規に構築するシステムはもちろん、すでに数十台、数百台を管理しているようなシステムでも容易に導入ができます。 詳細はAnsibleを説明している 公式ドキュメント をご参照ください。 2.Ansible 使用例 今回は apache のインストール、confファイルの配布、サービスの再起動を行う簡単なPlaybookを作成しました。 ※Playbookの詳細については こちら ディレクト リ ├── ansible.cfg ├── sample.yml ├── templates │ └── httpd.conf.j2 └── test-hosts sample.yml [root@AnsibleServer ansible]# cat sample.yml --- - hosts: test-server gather_facts: false tasks: - name: install apache yum: name: httpd state: installed - name: setup apache template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf - name: restart apache service: name: httpd state: restarted enabled: yes 実行ログ Playbookを実行すると下記のようなログが出力されます。 changedが表示されていれば、taskの実行によってターゲットのサーバに変更が加えられたことを示しています。 [root@AnsibleServer ansible]# ansible-playbook -i test-hosts sample.yml PLAY [test-server] ************************************************************************************************************************************************************************************************** TASK [install apache] *********************************************************************************************************************************************************************************************** changed: [test-server] TASK [setup apache] ************************************************************************************************************************************************************************************************* changed: [test-server] TASK [restart apache] *********************************************************************************************************************************************************************************************** changed: [test-server] PLAY RECAP ********************************************************************************************************************************************************************************************************** test-server : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 3.Roleについて AnsibleのRoleは処理毎にグループ化して分割する機能です。 roles アトリビュート で対象のグループ(Role)を呼び出すと「roles/<Role名>/tasks/main.yml」が読み込まれます。 ※「main.yml」以外の名前ではincludeやimportをしないと読み込んでもらえないのでご注意ください。 また、rolesを使用している場合、handlersを使用することができます。 handlersは「roles/<role名>/tasks/main.yml」内の対象のタスクがchangedステータスで終了した場合に実行される処理です。 ここで実行したい処理は「roles/<role名>/handlers/main.yml」に記述し、tasks配下のmain.ymlで「notify: <タスク名>」を記述して呼び出します。 コンフィグファイルの配布等、サーバに変更が加わった場合のみサービスを再起動したい場合にとても有用な機能です。 サンプルPlaybookをrole化して分割すると、以下のような感じになります。 ディレクト リ ├── roles │ ├── template │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ └── main.yml │ │ └── templates │ │ └── httpd.conf.j2 │ └── yum │ └── tasks │ └── main.yml ├── sample-role.yml └── test-hosts sample-role.yml - hosts: test-server gather_facts: false roles: - yum - template roles/ yum /tasks/main.yml --- - name: install apache yum: name: httpd state: installed roles/template/tasks/main.yml --- - name: setup apache template: src: httpd.conf.j2 dest: /etc/httpd/conf/httpd.conf notify: restart apache roles/template/handlers/main.yml --- - name: restart apache service: name: httpd state: restarted enabled: yes 実行ログ [root@AnsibleServer ansible]# ansible-playbook -i test-hosts sample-role.yml PLAY [test-server] ************************************************************************************************************************************************************************************************** TASK [yum : install apache] ***************************************************************************************************************************************************************************************** changed: [test-server] TASK [template : setup apache] ************************************************************************************************************************************************************************************** changed: [test-server] RUNNING HANDLER [template : restart apache] ************************************************************************************************************************************************************************* changed: [test-server] PLAY RECAP ********************************************************************************************************************************************************************************************************** test-server : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 4.その他の機能について AnsibleのRoleには前述した「tasks」、「handlers」、「templates」以外にもいろいろな機能があります。 便利に使用できるものが多いので、簡単に説明させていただきます。 defaults (roles/<Role名>/defaults/main.yml) 対象のAnsibleのRoleは、デフォルト変数を定義する機能です。 その他の変数ファイルで定義された場合、ここで定義された変数は上書きされます。 記述方法はその他変数ファイルと同様です。 サンプル --- ansible_user: root package: - { state: installed , name: httpd } - { state: installed , name: tomcat } vars (roles/<Role名>/vars/main.yml) その名の通り、変数を定義する機能です。 ここで定義する変数は最後に読み込まれるため、「defaults」はもちろん「group_vars」や「host_vars」の変数も上書きます。 デフォルト変数はともかく、変数ファイルが複数個所にまたがって配置されると管理が煩雑になるため、 個人的には特別な理由がない限りは「group_vars」や「host_vars」を使用した方が管理しやすいと思います。 files (roles/<Role名>/files/main.yml) Copyモジュールを使用する際にファイルを格納する ディレクト リです。 ここに格納されたファイルはファイル名のみで呼び出し可能です。 meta (roles/<Role名>/meta/main.yml) Ansibleの各Roleの依存関係を定義するファイルです。 「 yum 」のmeta/main.ymlで「template」を指定した場合、下記のように「template」⇒「 yum 」の順に実行されてしまいますので注意が必要です。 [root@AnsibleServer ansible]# ansible-playbook -i test-hosts sample-role.yml PLAY [test-server] ************************************************************************************************************************************************************************************************** TASK [template : setup apache] ************************************************************************************************************************************************************************************** changed: [test-server] TASK [yum : install apache] ***************************************************************************************************************************************************************************************** changed: [test-server] RUNNING HANDLER [template : restart apache] ************************************************************************************************************************************************************************* changed: [test-server] PLAY RECAP ********************************************************************************************************************************************************************************************************** test-server : ok=3 changed=3 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 5.Ansible Role おまけ ansible-galaxy init AnsibleのRoleの各 ディレクト リは「ansible-galaxy init <Role名>」で簡単に生成できます。 新規でAnsibleのRoleを作成する際に利用してみてください。 [root@AnsibleServer ansible]# ansible-galaxy init roles/test - Role roles/test was created successfully [root@AnsibleServer ansible]# [root@AnsibleServer ansible]# ll roles/test total 4 drwxr-xr-x 1 root root 512 May 29 11:42 defaults drwxr-xr-x 1 root root 512 May 29 11:42 files drwxr-xr-x 1 root root 512 May 29 11:42 handlers drwxr-xr-x 1 root root 512 May 29 11:42 meta -rw-r--r-- 1 root root 1328 May 29 11:42 README.md drwxr-xr-x 1 root root 512 May 29 11:42 tasks drwxr-xr-x 1 root root 512 May 29 11:42 templates drwxr-xr-x 1 root root 512 May 29 11:42 tests drwxr-xr-x 1 root root 512 May 29 11:42 vars エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに JUnitとSpockについて ライフサイクルの定義 テストクラス データドリブンテスト まとめ はじめに こんにちは、ryrkssです。 今回は、今のプロジェクトのテスト フレームワーク に JUnit と Spock が使われているので、 簡単なテストコードでどう書き方が違うのか比較してみようと思います。 また、それぞれ各テストで出てくる アノテーション や検証メソッドについての説明は割愛します。 本記事で使う JUnit は JUnit5 です。 以後 JUnit が多発しますが全て JUnit5 として捉えていただければ幸いです。 JUnit とSpockについて みなさんご存知の方は多いと思いますが「知らないよ」という方に向けてちょっとだけまとめます。 JUnit Java のテスト フレームワーク といえば真っ先に思い浮かぶ有名なテスト フレームワーク です。 Java エンジニアであれば全ての人が通る道ではないでしょうか。 Spock Groovy で動作する Java のテスト フレームワーク です。 Java とは違う言語で書くので「違う言語覚えないといけないのか」と思う人もいるかもしれませんが、 Java とほぼ同じ感覚でコーディングできます。 何よりデータドリブンテストが JUnit と比べて非常に書きやすく可読性も高いです。 あとは、MockAPIが標準で入っていることも嬉しい人には嬉しいと思います。 ※私は使い慣れている Mokito を使っちゃってますが。 JUnit と Spock は共存できるので、是非どちらも試してみてください。 それでは、 JUnit と Spock のテストコードの書き方を比べてみます。 ライフサイクルの定義 良く使いますが JUnit は アノテーション 、 Spock はメソッド名で定義します。 JUnit アノテーション で実行タイミングを表現します。 // 各テストメソッドが実行される前に都度実行 @BeforeEach void init() {} // 各テストメソッドが実行された後に都度実行 @AfterEach void cleanup() {} // テストクラスが実行される前に1度だけ実行 @BeforeAll static void initAll() {} // テストクラスが実行された後に1度だけ実行 @AfterAll static void cleanupAll() {} Spock メソッド名で実行タイミングを表現します。 // 各テストメソッドが実行される前に都度実行 def setup() {} // 各テストメソッドが実行された後に都度実行 def cleanup() {} // テストクラスが実行される前に1度だけ実行 def setupSpec() {} // テストクラスが実行された後に1度だけ実行 def cleanupSpec() {} Spock のメソッド名だけで判別してくれるのは、コードがスッキリして良いですよね。 JUnit はお馴染みの アノテーション 定義です。 見慣れすぎて何も思わないです。 テストクラス JUnit @Test @DisplayName( "文字列結合" ) void combineStringsTest() { var expected = "テスト" ; var actual = sut.combineStrings( "テ" , "ス" , "ト" ); assertEquals(expected, actual); } Spock def "文字列結合" () { setup : def expected = "テスト" when : def actual = sut.combineStrings( "テ" , "ス" , "ト" ) then : actual == expected } 単純なテストを書きましたが、結構違いますね。 JUnit と Spock で目に付く違いは、ブロックの定義です。 JUnit はコードを記述しているだけですが、 Spock には setup: や when: など 記載しているもの以外にもそれぞれのフェーズに応じたブロックがあります。 単なる分かりやすくするためだけのものではないので、ちゃんと状況に応じた使い分けをしましょう。 また、こう見ると JUnit は アノテーション @Test を毎回つけなければならないのが冗長に感じます。 検証メソッドについても JUnit はメソッドを使用する必要がありますが、 Spock は等価 演算子 を使うだけで事足ります。 こちらは比較的万能で配列やリスト、数値なども等価 演算子 で検証できます。 データドリブンテスト こちらもよく使います。 同じようなテストを1つ1つメソッドで分けるのは、テストケースが見えにくく可読性が落ちてしまいますよね。 JUnit は ParameterizedTest で検索すると出てくると思いますが、 渡している値と想定結果の関係性が分かりにくく、同時に書きにくいと感じてしまいました。 その反面 Spock は非常に書きやすく、見やすいので実装している時にパターンを洗い出しやすいです。 またレビュー時もテストケースを考えずとも頭に入ってくる感覚で個人的には中毒になるレベルです。 百聞は一見に如かずということで、早速比較してみたいと思います。 JUnit 例として、3種類の書き方を記載します。 まだパラメータが少ない且つ、型を同一にしているので幾分見え方がましですが、 ②、③についてはパラメータ数が多くなる、色々な型が混じるとなると 本当に何のテストをしているのか分からなくなってきますね…。 /* ① 単一パラメータのみを定義する書き方 引数で定義した型に暗黙的に変換される(サポートされている型は要確認) */ @ParameterizedTest @ValueSource(strings = { " テスト " , " テスト" , "テスト " }) void trimTest1(String target) { var expected = "テスト" ; var actual = sut.trim(target); assertEquals(expected, actual); } /* ② csv形式でパラメータを定義できる書き方 デリミタを変更したり外部ファイルを指定することもできる 例は文字列だけだが他の型も定義できて、引数で定義したそれぞれの型に暗黙的に変換される */ @ParameterizedTest @CsvSource ({ " テスト , テスト" , " テスト, テスト" , "テスト , テスト" , }) void trimTest2(String target, String expected) { var actual = sut.trim(target); assertEquals(expected, actual); } /* ③ 別メソッドにパラメータを定義できる書き方 メソッドで引数を定義するので独自の型をパラメータで与えることができたり、 ただのメソッドなので最終的に返り値の形にしてあげれば何でもできるイメージ */ @ParameterizedTest @MethodSource( "trimArguments" ) void trimTest3(String target, String expected) { var actual = sut.trim(target); assertEquals(expected, actual); } static Stream<Arguments> trimArguments() { return Stream.of( Arguments.of( " テスト " , "テスト" ), Arguments.of( " テスト" , "テスト" ), Arguments.of( "テスト " , "テスト" ), Arguments.of( null , "" ) ) } Spock 例として2種類ありますが、どちらもメソッドに @Unroll をつけて、 where: にパラメータを定義していくだけとなります。 また、文字列だけを扱うテストになってますが、勿論他の型が混じっても問題ありません。 簡単なものしか記載していませんが、 where は色々な使い方ができるようなので興味のある方は調べてみてください。 /* ①データテーブルを使用した書き方 1行目がテスト内の変数にマッピングされ、2行目以降がその変数に入れるデータパターン || をつけることで検証用のデータと想定結果を視覚的に分かりやすくできる */ @Unroll( "文字列前後空白除去 #title" ) def "trimTest1" () { when : def actual = sut.trim(target) then : actual == expected where : title | target || expected "前後空白" | " テスト " || "テスト" "前空白" | " テスト" || "テスト" "後空白" | "テスト " || "テスト" "null" | null || "" } /* ②データパイプを使用した書き方 左シフト演算子で変数とデータ部を接続 */ @Unroll( "文字列前後空白除去 #title" ) def "trimTest2" () { when : def actual = sut.trim(target) then : actual == expected where : [title, target, expected] << [ [ "前後空白" , " テスト " , "テスト" ], [ "前空白" , " テスト" , "テスト" ], [ "後空白" , "テスト " , "テスト" ], [ "null" , null , "" ] ] } まとめ いかがでしたでしょうか? 簡単な例しか出せておらずイメージがつきにくいかもしれませんが、 テストの可読性・書きやすさだけでいうと JUnit よりも Spock の方が印象として良いのかなと私は感じました。 現在のチームでも新しく作るテストクラスは大体 Spock で作ってたりしてます。 ただし、 JUnit にしかできないこともあります。 例えば、 JUnit のネスト( @Nested )を使ってテストをまとめることが Spock ではできません。 テストも色々な書き方があって奥が深いですね〜 テストの実装はベストプ ラク ティスをあまり深く考えてこなかったのですが、 しっかりと可読性や何が最良なコーディングかを考えていかなくてはいけないな、と感じております。 全体を通してかなり Spock よりの記事になってしまいましたが、 結論 JUnit も Spock もどちらも使いやすいです。 最後に...冒頭でも書かせていただきましたが JUnit と Spock は共存できるので 是非実際にそれぞれでテストを書いてみてください! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに Gather.Townとは? 導入しようと思ったきっかけ オフィスの作り方 どう使ったか まとめ・感想 はじめに こんにちは。新卒3年目hy094です。 最近はスパイのアニメにハマっています。ファミリーのやつです。 今回は Gather.Town を導入したらリモートワーク中の開発速度が向上した話をしたいと思います。 Zoomが2人の通話でも制限時間が40分になってしまい、代替ツールを探している方もいるかと思いますので、少しでも判断材料になれば嬉しいです。 Gather.Townとは? 一言で言うと "Pokemon x Zoom thing" ( Notion – The all-in-one workspace for your notes, tasks, wikis, and databases. ) です。 ポケモン とZoomを掛け合わせたかのようなツールで、ゲーム好きの人には親しみやすくかつZoomに近い使い心地で利用できます。(使い方によってはZoomを上回る利便性すら秘めていると思います。) 表題では「リモートオフィス」と表現しましたが、これは私たちの使い方がリモートオフィスに近かったためです。Gather.Townはオフィスに限らず様々な用途に活用できます。 一例として、社内サークルでの活用事例です。 Gather.Townで社内サークル活動の一部をオンライン化しました - Qiita 導入しようと思ったきっかけ 弊社は基本オフィスワークで、緊急事態宣言やまんえん防止等重点措置などが発令されている場合のみ完全リモートワークになる運用でした。 (ここしばらくは落ち着いており出社していますが、現在もおそらく同様です) 皆さんもご存知の通り、発令したり解除したりが繰り返されていたため、私たちも出社とリモートを繰り返していました。 そこで出社した際にリモートの仕事がどうだったか新卒入社の後輩に聞いたところ、 「気軽に相談しにくい」 といった声がありました。 出社時は隣にちょっと声をかけるだけのところを、チャットで連絡→返信待つ→ZoomURLを共有→入室待つ→相談といった流れで、実装で少し詰まったところを聞くだけで15分〜30分かかるといった状態です。 そこで丁度上記の社内サークルでの導入事例を見て、同じ空間にいれば声をかけやすいのでは?と導入してみることにしてみたのです。 オフィスの作り方 導入方法については再掲になりますが、弊社の昨年の アドベントカレンダー に記載があるのでそちらを参照してください。 Gather.Townで社内サークル活動の一部をオンライン化しました - Qiita ここでは、オフィスの作成方法だけお伝えします。 基本は上記の記事の通りで大丈夫なんですが、「Explore social experiences」でそのままテンプレートが選べなくなっていたので、テンプレートまでの道のりも示しておきます。 一番左の「Set up an office or team social」を選択 左の「Remote Office」を選択 オフィスを改造したり増築する予定がない方はこのまま「Confirm Selection」を押してください。 ※「Confirm Selection」で作成されるspaceに床や壁を追加しようとすると、デフォルトでついている背景など全て消え去ってしまうためです。(一度space作り直しました) ※まだ壁と床の追加はベータ版のようなので、いずれ編集できるようになるかもしれません 壁や床を編集しようとすると以下のような警告が出て・・・ Yesを押すと真っ白になってしまいます! 「Confirm Selection」がある画面の左下に「See other space」というボタンがあるので、それを選択するとテンプレート選択画面に飛べます。 あとは先程の記事の通り作成していただければ問題なく部屋の改築が可能です。 どう使ったか オフィスとしてGather.Townの特性を最大限活かすための使い方はズバリ 「常時ログインし座っておくこと」 です。 他の作業をやっている時も裏で起動しておき、キャ ラク ターを机の前(椅子の上)に置いておきます。 見た目で「出社してる感」があるのはもちろん、通知機能(ログイン中でGather.Townが非アクティブな人に通知を送ることができる)などを使うことで、オフィスでは普通にやっている「すみません、今お時間いいですか?」をリモートで実現することができました。 実際の作業中の風景↓ (ちなみに、後ろ向いてますが青いのが僕です。 ポケモン のアクア団をイメージしました。) また、逆にチャットで悩んでる雰囲気の人を見つけ、教える側の人が突撃することも容易になりました。 相談がしやすくなったことで一人で悩む時間が格段に減り、結果的に開発がスムーズに進むようになりました。 ただ、一つ大きな問題があり・・・ Gather.Townはそこそこ重く、それを常時起動とするとそれなりにメモリを喰ってしまうことです。 導入の際はマシンスペックやコミュニケーションへの課題感と相談していただければと思います。 まとめ・感想 まず、導入して本当によかったです。 オフィスワークと遜色ないレベルでコミュニケーションが取りやすくなり、開発もスムーズに進むので導入前に比べて楽しく開発できました。 また、Gather.Townの導入は当時2年目の私が提案し、導入に至っています。 若手も色々と提案でき、それをやってみることができる環境は個人的にはとても嬉しいです。 Gather.Townには他にもたくさんメリット/デメリットがありましたが、そちらは別途「 ラクス フロントエンドチーム|note 」の方にまとめました! 是非ご確認ください。 note.com エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは、技術広報の yayawowo です。 システム開発 で重要な『開発とテスト』について、 エンジニアの体験談やTipsを覗いてみませんか? 延べ450名以上の参加申込をいただいた、 『開発×テスト LT会』全2開催分の資料をまとめて紹介します! イベント詳細はこちらをご確認ください! ・ 開発×テスト LT会 ・ 開発×テスト LT会 - vol.2 開発×テスト Tips 11選 絶対にテストコードを書かないチームのテストルール 意外とカンタン!?テストコードの改善から始めるシステム開発の効率化 テスト嫌いなプログラマがテストを語る ISTQB/JSTQBシラバスから学ぶAgileTesting 単体テストゼロからテスト文化を醸成させた話 少しずつ手厚くして不具合や仕様漏れを防ぐために CircleCIでtiming dataに基づいたテスト分割をDartで利用できるようにした話 ソフトウェアエンジニアが品質保証を学んでわかったこと 個人開発でテストを書いてみて良かったこと&伸びしろ UTアンチパターン インシデントゼロを支える技術 最後に 開発×テスト Tips 11選 絶対にテストコードを書かないチームのテストルール 発表者: ヒグ! さん speakerdeck.com ◆ 発表内容 ・テストコードのいいところ  →テストを自動化して効率化  → 単体テスト を入れることでコードの品質を評価  →修正して再テストが容易 ・手動テスト/自動テストのメリット/デメリット ・自動テストデータの扱いやすさ、手動テストの柔軟性  →双方の長所を活かして生産性向上に繋げていきたい 意外とカンタン!?テストコードの改善から始める システム開発 の効率化 発表者: 面川泰明 さん speakerdeck.com ◆ 発表内容 ・テストやっててつらいことはありませんか?  →考慮漏れしたテストケースで不具合を発見できない  →テスト用のドキュメントをいちいち作成/更新するのが面倒 ・今回取り組んだ内容  ① テストコードは適切にケース分けする  ② あえてDRYをやめ、べた書きする ・続けて良かったこと  エンジニア:テストケースの考慮漏れに気づきやすくなった  テスター :テスト用のドキュメントを作成/更新する手間が削減された テスト嫌いな プログラマ がテストを語る 発表者: 白柳隆司 さん speakerdeck.com ◆ 発表内容 ・テストが「嫌い」な プログラマ は多いはず  →おおっぴらに「嫌い」とも言えない空気感 ・「嫌い」だから遠ざけていいわけではない ・「嫌い」の理由を見つめる  →そこから出てくる視点を活用する ISTQB/ JSTQB シラバス から学ぶAgileTesting 発表者: Daiki Tanoguchi さん speakerdeck.com ◆ 発表内容 ・品質はチームのもの ・リスク評価は「相対見積もり」でできる ・探索的テストはセッションベースドかつテストチャーターを用いて行おう ・要求エンジニアリングの技法を使ってテストを考えよう 単体テスト ゼロからテスト文化を醸成させた話 発表者: south さん speakerdeck.com ◆ 発表内容 ・やったこと  →ライブラリ導入(Jest/Testing Library)  →ドキュメント(ライブラリの主要な API /テスト観点/サンプルコード)  →モック関数を用意  →読書&発表  → ペアプロ ・モブプロ ・ライブラリを導入して終わりではなくテストが書きやすい環境を整備 ・テストは慣れるしかない!が段階的に慣れていくための支援をしよう 少しずつ手厚くして不具合や仕様漏れを防ぐために 発表者: fumiyasac さん 少しずつ手厚くして不具合や仕様漏れを防ぐために from Fumiya Sakai www.slideshare.net ◆ 発表内容 ・少しずつUnitTestを手厚くして複雑になっても仕様を読み取れる様にする  ① 仕様を良い意味で疑いながら必要な観点を洗い出す  ② UnitTestがあると以前の仕様の確認もできる  ③ 言葉だけでは紛らわしい部分にはテストを手厚く書く様にする CircleCIでtiming dataに基づいたテスト分割を Dart で利用できるようにした話 発表者: operandoOS さん speakerdeck.com ◆ 発表内容 ・ Dart testsでCircleCIのtiming dataを利用して良い感じにテスト分割/並行実行したい…!  ① Dart testの結果を Junit XML で出力する  ② 出力した Junit XML をテスト メタデータ としてCircleCIに認識させる  ③ テスト実行のコマンドを書き換える ・"No timing found for <ファイル>"というエラーが出た場合  原因:timing dataのfile keyの値がnull  対応  ・ dart -junitreportの内部で利用している dart -testreportライブラリの修正  ・ dart -junitreportで Junit XML 出力する際、testcase elementにfile attributeの追加  ・file attributeに記載されたfile pathをCI上で sed コマンドを利用して書き換え ソフトウェアエンジニアが品質保証を学んでわかったこと 発表者: hisaichi5518 さん speakerdeck.com ◆ 発表内容 ・品質を定義するのが重要 ・プロダクト品質を保証するには、品質を作りこむことが大事 ・品質を作り込むには、チーム全員が 開発プロセス をすべてフェーズでテストをする=Holistic testing ・テストとは、 開発プロセス のフェーズで出来た成果物に対してチーム全員が想定したものか確認すること 個人開発でテストを書いてみて良かったこと&伸びしろ 発表者: あきお さん akio-blogger.blogspot.com ◆ 発表内容 ・なんで個人開発でテストを書こうと思ったのか?  →個人開発では0からテストを書くことができる  →個人開発だから自由に技術選定ができる  →若干仕様が複雑な部分がある ・良かったこと  →テストが開発記録代わりになる  →最初以外はテストのメンテコストを感じない  →少し時間はかかるけどチャレンジングなことができる UT アンチパターン 発表者: ryo07 さん UTアンチパターン from ryoheiseki1 www.slideshare.net ◆ 発表内容 ・ アンチパターン  ① 「そもそもテストが存在しない!」  ② 「テスト名が雑!」  ③ 「mock, verify に any を使うな!」  ④ 「テストの中に実装コードを混ぜるな!」  ⑤ 「なにもアサートしていない」  ⑥ 「Null チェックが甘い」 インシデントゼロを支える技術 発表者: Yuji Yamaguchi さん speakerdeck.com ◆ 発表内容 ・フロントエンドテストのつらみ  → 単体テスト は問題ないのに手戻りや結合バグが多くなりがち ・testing-libraryの導入で良かったこと/難しかったこと ・testing-libraryを使うと、実態に近い状態でテストをすることができる ・全体仕様を把握するための材料として、テストケースを作成するのもオススメ ・ 単体テスト よりもコストがかかるので、やりすぎないように注意が必要 最後に 『開発×テスト LT会』のまとめはいかがでしたでしょうか? 参考になる システム開発 /テストのTIpsが1つでもありましたら、幸いです。 また、『開発×テスト LT会 vol.3』が7/20(水)19:00~、開催を予定しております! rakus.connpass.com 登壇/視聴ともに募集をしておりますので、是非お気軽にご参加ください! 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
4/16に ラク スに入社しました、技術広報の飯野です。 入社して1ヶ月と少し経ちましたので入社エントリを書いてみることにしました。 本投稿は社外の方はもちろんですが社内メンバーにも自己紹介の絶好の機会ですので、社内外を問わず読み物としてお楽しみいただければと思います。 「技術広報って何をしているの?」「なぜエンジニアから技術広報に?」という方から「 ラク スってどんな会社か知りたい!」という方にまで幅広く読んでいただければ幸いです。 目次 経歴 転職のきっかけ 入社の決め手 ラクスの技術広報とは 入社後やっていること 社内の雰囲気 入社後の課題感 まとめ 経歴 大学 文学部日本文学科を卒業しており、生粋の文系です。 入社1社目の常駐先で「好きな古文は 源氏物語 です」と言ったところ、大層変わった子扱いをされていました。(そんなに変わった人間ではないです。) 1社目 前述の中小 SIer に新卒で入社し、SES(客先常駐)の形態で約4年働いていました。 Web広告代理店、宝くじ、 クラウド サービス、ネット証券等の現場でエンジニア経験を積ませていただきました。 SESあるあるかと思いますが(6年前の情報なので今とは違うかもしれません)、 常駐先によって待遇がかなり変わること 完全な ウォーターフォール だと一部工程にしか携われず自分が何を作っているのかすらよく分からない現場もあったこと 会社同士の契約のため来月自分がどこに居ることになるかすら分からなかったこと 等々、働き方について疑問を抱くようになり転職を決めました。 ただし、いろいろな現場に行って様々な業種のプロダクトを社外のエンジニアと毎回違う言語や フレームワーク で開発していたことは、間違いなくわたしのエンジニアスキルの土台になっています。 2社目 Web広告代理店で約6年働いていました。 自社のエンジニアと自社システムを作りたかった(エンジニアが99%内製でした)のと、入社時から新規プロダクトの開発に従事できるということで入社を決めました。 上述の、新規プロダクトであるクライアントが使用する広告発注システムの構築や、営業の売上と粗利をデイリーで管理するデータ分析基盤の実装、計上ツールの保守等、様々なプロダクトに携わりました。 また、この会社で アジャイル について学び、 スクラム 開発を実践していました。 エンジニア リングマ ネージャー(一般用語で言うところの課長レイヤーにあたるかと思います)兼開発チームのエンジニアを約5年、+ スクラム マスターというやりたがりが溢れ出たロールを担当していました。 2020年2月からはフルリモートで働いていました。 上記の通り、 ラク スが3社目で転職経験は2回です。 転職のきっかけ 前職はとても居心地のよい会社で、働きやすく、かつ人もよく、とても恵まれた環境であったと言えます。 「やりたい」と声を上げれば大抵のことはやらせてもらえましたし、価値が見出せないものやあまりにも短納期な要望は「やりたくない」と正直に言っていました。 (※ここは個人の性格によるところが大いにあります、全員が全員そう言っていたわけではないです。) 若い時からどんどんチャレンジさせてもらえて、新卒入社2年目で課長職になっている社員もいました。 実成果云々ではなく「やりたい」と言った社員へ挑戦機会を多く与える、といった風土です。(もちろん素質があればこそです。) そんな恵まれた環境に身を置いていたわけですが、だんだんと自分の市場価値について考えるようになりました。 立場上裁量も大きく、言い方がよくないかもしれませんが大体のことは言えばなんとかなっていた感覚があり、周りのメンバーからも「飯野が言うなら」といった空気を感じていました。 また、入社してからずっと同じプロダクト/チームに携わっていたため、他の環境に行った際にどのくらい太刀打ちできるものなのかが全く分からず、変化を求めている状態が続いていました。 とは言え「絶対に転職したい」という強い願望はなく、とりあえず手っ取り早く市場価値を知りたかったので、 ヘッドハンティング 型の転職サイトに登録しました。 ありがたいことにスカウトはそれなりにいただいていたのですが、同じようなポジションで同じような働き方なのであれば無理に会社を離れる必要はないと思っており、求人情報を見るだけの日々を過ごしていました。 そして転職サイト登録後半年を経過した頃、「技術広報」というポジションで ラク スからスカウトをいただきました。 入社の決め手 そもそもスカウトを貰うまで「技術広報」というポジションをよく知らなかったわたし。 カジュアル面談で開発部の組織体制や技術広報の業務内容について話を聞き、 これまで培ってきたエンジニアリングを土台に新しい分野で活躍できること 横断組織のため関わりを持つ社員がとても多いこと 対外的な組織 ブランディング に携われること に心惹かれて選考に進みました。 もともとコミュニケーションを取りながらチームで成果を上げることが好きだったので、より多くの人と関わり、プロダクトやチームを横断して組織のために働ける環境が魅力でした。 また、エンジニアから完全に離れることには不安もあったので、オファー面談の際に「もし技術広報が合わなければエンジニアでも」と言ってもらえたのも大きかったです。 ということで、転職活動は完全受け身で ラク ス1社のみエントリーし、ご縁がありまして入社に至りました。 内定をいただいてからも採用課の方も含め何度も話を聞いていただき(週1の頻度で電話 or Web面談で連絡を取り、オファー面談の際にも「現場のエンジニアと話したい」というわがままも聞いてもらいました)会社の制度面についてや、どういった人がいるのか、どういった人がカルチャーマッチしているか、という話を根掘り葉掘り教えていただきました。 納得いくまで悩む時間とサポートをくださったことで、安心して入社を決めることができました。 転職活動をほとんど行っていないので他社と比較しづらいですが、かなり手厚い方なのではないかと思います。 ラク スの技術広報とは 前述の通りですが、 ラク スの技術広報は 開発組織の ブランディング 、認知拡大 を行うことで、最終的には開発組織の 採用強化 を図ることを目的としています。 具体的な業務内容としては、 ラク ステックブログの運営 社外向け技術イベントの企画・運営 外部イベントの発掘・情報収集 上記取り組みによる集計作業・分析 の4点が挙げられます。 もちろん技術広報だけで全てを賄うことはできないので、現場のエンジニア/デザイナー、マネージャーの皆さまの力をお借りして活動を行っています。 より具体的な活動内容は こちらの投稿 をぜひご覧ください。 tech-blog.rakus.co.jp 入社後やっていること 出勤 2年ぶりのオフィス出勤なので、まずは通勤に慣れるところから始めましたw (※ ラク スは対面でのコミュニケーションを重視しており、緊急事態宣言等の要請がない限りは基本的にオフィス出勤となります。) 始めは電車に乗るのも緊張していたのですが、転職したてのわたしにとって対面コミュニケーションは関係構築をするにあたりかなり有効に働いていると思います。 もちろんリモートでも関係構築は可能です。 が、やはりリモートに比べてコミュニケーション量(雑談含む)が全く違います。 困ったときの「ちょっといいですか?」も2秒でできます、すごい。 「あの席にいるあの人にXXについて聞きたい」も歩いて行って5秒でできます、すごい。 業務外だけど誰かに話したいくだらないこともすぐに言えます、すごい。 リモートでは必須と思われる「場作りをしてから雑談をする」という必要性がなくなり、気軽にコミュニケーションが取れるようになりました。 副次的な効果としては、圧倒的に運動量が増えなんと体重も減りました。歩くの大事。 イベント企画・運営 わたしは4月中旬入社のため、入社時点で今期の計画はほぼ出来上がっている状態でした。 が、社外向け技術イベントを追加するにあたり、わたしも入社1週目からさっそく企画に携わることとなりました。 6月〜8月にかけて行われるLT会のうち、6本のイベント企画をさせていただいております。 「初めはお試しと思ってとりあえずやってみよう」というご厚意によりたくさん採択していただいて、嬉しいやら緊張やらです。 直近ですと6月中旬に「 アジャイルについてゆるく語りたい! 」(LT会)を開催予定です。 アジャイル 開発での取り組みやプロダクトの開発手法について聞いてみたい!という方はぜひご視聴ください! rakus.connpass.com 集計作業の自動化 技術広報はブログやイベント等の数値を扱っており、改善サイクルを回すために集計作業が必須です。 集計作業を行うにあたり、これまでは手作業でやっていた定型業務を少しずつ自動化しています。 「集計が速くなった!楽になって嬉しい!」と言ってもらえるとにんまりします。(これまでのエンジニア経験も活かせていますね!) チームビルディング 入社後は毎日1on1の時間を30分取っていただいていて、雑談や現状困っていること等をチームのリーダーと話しています。 入社3日目の1on1で「メンバーも増えましたし(自分のことですが)チームビルディングのワークがやりたいです」と言ったところ「どうぞどうぞ」と快諾していただいたのでさっそくやってみました。 技術広報チームではチームビルディングの経験があるメンバーは少なかったので、まずはチームビルディングの目的と効果について認識合わせを行いました。 チームビルディングを行うことで、 コミュニケーションの円滑化 心理的 安全性が高くなる チームに一体感が生まれる=生産性が向上 といった効果を期待できる、という話をしました。 ドラッカー 風エクササイズ 技術広報チームの初ワークには前職でも行っていた ドラッカー 風エクササイズを採用しました。 「 ドラッカー 風エクササイズ」とは、 アジャイルサムライ の著者であるJonathan Rasmusson氏が書籍やブログで紹介しているチームビルディングの手法 4つの質問にチーム全員が答えることで、相互理解の促進と期待値のすりあわせという効果があり、特にプロジェクトの開始時や新メンバーを迎えるときに効果的 と言われているワークです。 主にソフトウェア開発をしているチームで取り組むことが多い(と思われる)ので、開発外のチームで行ったのは初めてでした。 ドラッカー 風エクササイズでは参加メンバーの得意なことや大切にしている価値観を知ることができるので、自己開示を目的としているワークの場合に有効な選択肢だと思います。 実際にやってみて、参加メンバーからの感想も貰いました。 期待値の擦り合わせは良かった。 期待値の得点づけで4-5が多いあたり、対人の気遣いができるメンバー達なのだなと思った反面、 心理的 安全性はまだ低いのかなとも考えた。 一緒に仕事をする上で、チームの得意・不得意、価値観などが詳細にきけて良かったです。 今後仕事をする上で、期待値の相互理解ができたことはとても良いと思いました。 関わっている期間が長い人/短い人とで、期待値の変化が起こると思うので、期が変わる頃にもやってみたいなーと思いました。 改めてコミュニケーション、自己客観視の機会が持ててよかったと思う。 チームの現状やメンバーの得意なことが大きく伝わってきて理解が深まった。 自分へのコメントがいただけることで、自分の位置や目指していく方向わかりやすくなったと思う。 今後いい意味で変化できたらと思いました! プロダクト開発のチーム以外で行ったのは初めてでしたが、ポジティブなFBが多く貰えました。 「 心理的 安全性はまだ低いのかな」という意見もあり、メンバーが増員したばかりということももちろんあるでしょうし、まだお互いに気は遣っている状態ということがこういったワークを通して明るみになった点もよかったと思います。 社内の雰囲気 情報共有の文化 チャット上での交流がとても活発に行われていて、社外の技術イベントのレポートやおすすめの記事、ネットニュース、美味しいランチ情報が流れてくるチャンネルもあります。 (※ ラク スの開発部門では「Mattermost」をチャットツールとして採用しています。) こういった情報発信ってリアクションがないと切ないですよね。 わたしも技術広報活動の一環で社内報を流す機会が多いのですが、絵文字で反応をいただけるととてもありがたいです! また、有志での集まりも盛んな印象です。 技術広報主導ではない社外向けイベントがあったり、読書会が開かれたりと、主体性を持つ社員によってチームを横断した活動が行われています。 わたしも読書会に参加予定なので楽しみです! フラットで柔軟な社員が多い 技術広報では社外向け技術イベントの企画・運営をしていますが、その一環で社内のエンジニアやデザイナーに登壇依頼をする機会があります。 わたしも入社してから何名かの方に「登壇いかがですか?」と声を掛けさせていただきましたが、「お誘いありがとうございます!」「発表内容考えておきます!」とさくっと快諾いただけることが多く感動しました。 社内でこういったアウトプットの文化を作るのはすごく難しいと思います。 前職でも「ブログ書いてよ」と言うのも言われるのもなんだかハードルが高いなと感じていました。(実際にはそんなことはないのですが。) ラク スでは年間のブログ計画を技術広報と開発部内で連携して作成しています。 この計画ブログだけでも大変にありがたいのですが「LT会に向けてブログを執筆したのでアップしてほしい」「連載物を書きたい」等の飛び込み執筆者がいらっしゃいます。 強制ではなくこういった形でブログを書いてくださる方が増えると、日頃のインプットを気軽にアウトプットする流れが根付きそうですね。 技術広報としてもそういった社員のアウトプットの場をどんどん増やしていければと思っています。 入社後の課題感 人力の作業を自動化したい 集計作業や管理業務にはまだまだ手作業が多い状況です。 なんでもかんでも自動化すればいいってものでもないですが、完全にルーチン化していて人がやる必要のない作業はなるべく人力を減らして、より企画や分析等のクリエイティブな箇所に注力していきたいですね。 技術広報として会社のことをもっと知りたい まだ入社して日が浅いこともありますが、会社に関するインプットの日々が続いています。 プロダクトも社員も多く、それぞれのプロダクト毎の技術スタックや開発手法、日々の取り組み等まだまだ分からないことだらけです。 が、技術広報は横断組織のため関わる社員は本当に多いです! 前職までは業務で関わりのあるエンジニアやデザイナーはかなり限られていたので、横断組織ならではの特色だと思います。 日々少しずつですが、関わりを通してプロダクトや組織毎の特色を学び、組織 ブランディング に活かせるような企画に繋げていきたいです。 ラク ス社員の皆さま、今後ともよろしくお願いいたします! まとめ 以上、エンジニアから技術広報に ジョブチェンジ した話でした。 入社して一ヶ月と少しですが、すでにいろいろな機会をいただけているなと改めてふりかえることができました。 まだまだ技術広報としては ひよっこ ですが、組織の認知拡大目指して邁進します! 最後までお読みいただき、ありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
技術広報の yayawowo です。 いつも ラク スのエンジニアブログをお読みいただき、ありがとうございます! 今回は2022年度最初の ラク スMeetup、 『 技術刷新の課題取り組み - 共通基盤/k8s/技術ロードマップ - 』 の発表内容について紹介させていただきます! テーマは『先行技術検証の事例』です。 当社の技術課題解決に向け、新技術の導入検証に取り組む専門組織のマネージャー/メンバーたちが登壇させていただきました! なお、本イベントは以下のような方にオススメとなっております。 ◆ こんな方にオススメ! ・時代の変化/ビジネス要求に合わせて システム開発 したい ・5年10年続いたサービスをさらにこれから10年維持したい方 ・ ラク スの技術ロードマップ運用が気になる ・現実的な技術検証の取り組みを知りたい ・技術検証組織に興味がある ・ SaaS 開発に携わるエンジニアの話が聞きたい ・当社で働くことに興味がある 発表の紹介 横断部門としての取り組み紹介(研究開発、共通基盤開発) Kubernetes導入に備えたGitOpsなCI/CDを構築する サービスを陳腐化させない組織だった技術刷新  ラクスのエンジニアと話をしてみたい方へ 終わりに 発表の紹介 それではここから各発表内容と資料を共有させていただきます! イベントの詳細は以下をご確認ください。 rakus.connpass.com 横断部門としての取り組み紹介(研究開発、共通基盤開発) 登壇:堀内 泰秀 [所属:技術推進課 マネージャー] speakerdeck.com まずは、技術推進課マネージャーの堀内からの組織についてご紹介させていただきました! 術推進課は技術的な横断部門として活動しており、各サービス開発部門が手をつけられない領域にチャレンジする組織となっています。 例えば サービス開発のチームでは取り組めないテク ノロ ジー の研究開発 各サービスを横断する機能の開発 開発部門だけでなくビジネス部門への技術情報提供 など様々です。 本発表では、以下ポイントについてお話しさせていただきました。 技術推進課の取り組み 研究開発成果の一例のご紹介 研究開発を組織化して行う意義 組織が抱える課題  Kubernetes 導入に備えたGitOpsなCI/CDを構築する 登壇:岡本 拓也 [所属:技術推進課] speakerdeck.com 続いて、岡本より検証事例をご紹介しました! ラク スのサービスのほとんどはオンプレミスで構築し運用していますが、コンテナ技術を生かした開発の需要の高まりやECSで運用しているサービスのオンプレミスへの移行に備え、 Kubernetes での運用を視野に入れています。 そのため、 Kubernetes 導入に備え従来のCI/CDをIaCの特徴を活かしたものに変化させる必要があります。 今回はGitOpsを採用して ラク スの技術スタックに沿った Kubernetes ネイティブなCI/CDを考案、構築したお話をさせていただきました。 ◆ 関連ブログ tech-blog.rakus.co.jp サービスを陳腐化させない組織だった技術刷新 登壇:鈴木 勇 [所属:技術推進課 speakerdeck.com 最後は、鈴木の発表です。 数年どころか、数ヶ月も持たずに消えていくシステムがある一方、上手く軌道に乗ると10年以上運用されるものもあります。 ラク スでは長く続いているサービスを複数抱えていますが、長く続くサービスの悩みといえば「技術的負債」と「仕組みの陳腐化」です。 今回は仕組みの陳腐化に対してどのような取り組みを行っているのかをご紹介しました。 長く続くサービスにありがちな「システム面では陳腐化しているが、ビジネス面では問題となっていない」というよくあるシチュエーションに対してどのようなアプローチを行っているのかという話をさせていただきました。 ちなみに予想通りか、期待外れか、 銀の弾丸 になるような話ではなく地道な努力となります。 ◆ 関連ブログ tech-blog.rakus.co.jp   ラク スのエンジニアと話をしてみたい方へ 今回登壇した技術推進課では、東京/大阪開発拠点にて 『プロダクト共通基盤開発チーム(立ち上げメンバー)』 を積極的に募集しております! ラク スのプロダクトを複数契約されているお客様に、複数契約ならでの付加価値を提供するため開発チームです。 立ち上げ組織のため、ご経験に応じて下記ポジションをお任せしたいと考えております。 エンジニア リングマ ネージャー PdM(プロダクトマネージャー) 開発エンジニア ◆ 東京開発拠点 career-recruit.rakus.co.jp ◆ 大阪開発拠点 career-recruit.rakus.co.jp career-recruit.rakus.co.jp 「まだ応募する段階では…」 という方は、是非 カジュアル面談 もご検討ください! 【こんな方におすすめ】 ポジションが経験にマッチするか確認したい 働き方/環境・体制/事業・プロダクト/文化/制度を詳しく知りたい 応募前に選考の概要を聞きたい(人物像、基準など) エンジニア・デザイナーの人となりを知りたい 以下申込フォームとなります。 rakus.hubspotpagebuilder.com 「イベントで登壇していた●●さんとお話をしたい・・・」 などご要望がありましたらその旨をご記入の上、お申込みください! お気軽にどうぞ 😊 終わりに 『技術刷新の課題取り組み - 共通基盤/ k8s /技術ロードマップ -』はいかがでしたでしょうか? 今回初めて、技術課題解決に向けて新技術の導入検証に取り組む専門組織をご紹介させていただきました! 実際、検証からサービスへの反映までは3~4年ほどの期間がかかります。 そんな技術ロードマップを検討、技術検証をしてくれる当社の技術推進課は当社にとって重要なポジションとなっております。 また、採用を強化しているサービス共通基盤開発も大変大きな役割を担っていきます。 そんな重要組織の一員として一緒に働いてくれる方を大大募集中でございます! 今回ご紹介した3名の発表を通じ、大規模 SaaS の開発に携わっている方や、同様の課題と向き合っている方の一助となれば幸いです。 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに こんにちは。mst_78710と申します。 今回は、初見では発音の仕方が分からない" kubernetes "について書いてみようと思います。 今となってはご存じの方も多いと思いますが、発音は「クバネティス」や「クーベネティス」でいいようです。 また「 k8s 」とも表現され、「k + 8 文字(ubernete) + s」から取ったとのことで、なかなかユニークな表現ですね。 さて、その kubernetes ですが、内容が膨大でブログの一記事では到底収まりきらないです。 なので、今回は kubernetes の概要と特徴の一部(主にPod)について、一例を交えながら説明できればと思います。 この記事のターゲットとしては、以下のような方が対象になります。 ・ kubernetes って聞いたことはあるけど詳しくは知らない ・ちょっと調べてみたけど複雑でイメージがわかない 少しでも kubernetes についての理解の補助になれば幸いです。 それではいきましょう。 目次 はじめに 目次 これまでとどう違うのか kubernetesとは 宣言的な構成管理と自動化を促進 コンテナ化されたワークロードやサービスを管理する ポータブルで拡張性のある Kubernetesの特徴 具体例 まとめ 参考情報 これまでとどう違うのか まず、これまでとどう違うかをざっくりと理解するため、 一般的なサーバと kubernetes をすごく簡略化した図にしてみました。 一般的なサーバの方は説明するまでもないかと思います。 kubernetes 側で"master"と"node"という言葉がありますが、仮想サーバを表しています。( AWS 環境であれば、EC2になります。) masterでは、 kubernetes を動かすために必要な機能が稼働します。 詳細な機能を書いてしまうと長くなってしまうので、公式にお任せします。 Kubernetesのコンポーネント | Kubernetes 一方のnodeでは、アプリケーションが"Pod"という枠で稼働します。 こちらは後ほど説明します。 簡単なまとめとして、 kubernetes ではmaster上で稼働する管理機能を利用して、node上でアプリを稼働させることになりそうですね。 kubernetes とは 公式でさらっと書かれているこの一言を掘り下げて、 kubernetes の概要を説明していこうと思います。 Kubernetes は、宣言的な構成管理と自動化を促進し、コンテナ化されたワークロードやサービスを管理するための、ポータブルで拡張性のある オープンソース のプラットフォームです。 Kubernetesとは何か? | Kubernetes わかったようで結局よくわからない感じですね。分解してみましょう。 宣言的な構成管理と自動化を促進 構成管理と自動化はお馴染みかもしれないですが、"宣言的"という言葉はそれほど使わないかもしれないですね。 これまでのサーバ構築であれば、一般的にはサーバを構築して ミドルウェア やアプリケーションなどのインストール、設定をしたうえで稼働させていたと思います。 kubernetes では、 マニフェスト と呼ばれるファイルを作成し、それを kubernetes に読み込ませることで ミドルウェア などを設定・実行させることができます。 この「 マニフェスト 」ですが、英単語の意味としては以下のような意味があります。 明らかにする、明示する manifestの意味・使い方・読み方|英辞郎 on the WEB つまり、 kubernetes で稼働させるアプリや ミドルウェア の設定などを明示して(① マニフェスト 作成)、 kubernetes に「明示した通りに稼働させて」と宣言すると(② マニフェスト 実行)、 kubernetes 上で想定通りにアプリや ミドルウェア を稼働させることができる(③Pod作成)ということです。 コマンド実行したり、 シェルスクリプト を流して結果確認などして設定していたのが、 マニフェスト というファイルを作ればアプリや ミドルウェア を効率的に設定して稼働させることができるようになる、ということになります。 図にすると以下のようになります。 ① マニフェスト 作成 ここでは、nginxのPodを2個作成するという マニフェスト を作成しています。 (記載している内容は例としているだけなので、実際には正しい記載方法があります。) ② マニフェスト 実行 kubernnetesに マニフェスト 通りに稼働させるよう宣言します。 ③Pod生成 マニフェスト に記載された内容でPodを生成します。 Podの設定を変更したければ、 マニフェスト を編集して kubernetes に宣言すると変更内容が反映されます。 マニフェスト をバージョン管理することで、戻したい内容の マニフェスト を実行すれば kubernetes が即座に自動でその通りに再作成してくれるので、万が一トラブルがあった際にも最小限の被害で対応できそうですね。 コンテナ化されたワークロードやサービスを管理する ワークロードという言葉は kubernetes ではどういう意味で使われるのか、調べてみます。 ワークロードとは、 Kubernetes 上で実行中のアプリケーションです。ワークロードが1つの コンポーネント からなる場合でも、複数の コンポーネント が協調して動作する場合でも、 Kubernetes ではそれらはPodの集合として実行されます。 ワークロード | Kubernetes kubernetes の世界ではワークロードという言葉を使っていますが、いわゆるアプリケーションのことを表していて、アプリケーションは先ほど出てきた"Pod"として管理します。 "複数の コンポーネント が協調して動作する場合でも"という言葉について少し補足すると、 同じPodでも、アプリケーションとして稼働させるもの以外に、LBの役割やcronのjob実行などで稼働させることも可能です。 ものすごくざっくり言うと、 kubernetes では"Pod"という形式にすることで様々な機能を持ったコンテナをまとめて管理できる、ということになりそうです。 管理というと曖昧になってしまうので、例えばPodの状態確認とするとわかりやすくなるかもしれません。 上記でNginxのPodを作成したので、その状態確認を実施すると以下のようになります。 ※ kubernetes 用の コマンドライン ツール"kubectl"で確認します。 $ kubectl get pod NAME READY STATUS RESTARTS AGE Nginx1 1/1 Running 0 2m Nginx2 1/1 Running 0 2m このように作成したPodが一覧で表示され、状態確認ができます。 項目 詳細 READY 準備が完了しているかどうかを表します。1/1となっているので正常稼働しています。 STATUS Podの作成状況を表します。Runningとなっているので、正常稼働しています。 RESTART Podが再起動した回数を表示します。 AGE 作成されてどれくらい時間が経過したかを表します。上記では、2分前に作成されたことがわかります。 ここで他のPodも作成してみることにします。 LBとcronのPodを作成する マニフェスト を新しく作り、実行したとします。 $ kubectl get pod NAME READY STATUS RESTARTS AGE Nginx1 1/1 Running 0 5m Nginx2 1/1 Running 0 5m lb1 1/1 Running 0 3m cronjob1 1/1 Completed 0 1m LBのPodが新たに作成されていることが分かります。 また、cronのPodも新たに作成されていますが、STATUSが他のPodとは異なっています。 定期実行の設定がされたPodは、指定した実行時間にのみ起動されます。 Completedの場合はcron実行されて処理が終わった状態を示しています。 このように、 kubernetes ではPodという単位にすることで、様々な機能をまとめて管理することが可能となります。 ポータブルで拡張性のある こちらは上記の"宣言的"という言葉にも少し関連してきます。 例えば、上記で例に挙げたとおり「このアプリケーションをPod2個で稼働させて」と kubernetes に宣言すると、その通りに稼働します。 稼働させているうちにさらに2個Podが必要になった場合、「このアプリケーションをPod4個で稼働させて」と宣言すると、 即座にPodが2個追加され、4個のPodが稼働するようになります。 Pod追加時に、 kubernetes がこの時点でのnodeのリソース状態を確認し、よりリソースの少ないnodeを選んで、 全体のnodeのリソースが均等になるようにPodを作成してくれます。 さらに、運用上でPodを毎日再起動する必要があったりすると、その都度各nodeに分散させてPodを再作成します。 また、Pod自体のリソース(メモリなど)も指定することができ、同様に宣言すれば kubernetes が変更してくれます。 このように、 マニフェスト を実行することで、Podを簡単に稼働させることができたり、node上でのPod配置もその時に応じて変わったり、リソースの拡張や縮小も可能になります。 "ポータブルで拡張性のある"という言葉の持つイメージが湧いてきたかと思います。 Kubernetes の特徴 kubernetes の特徴について、公式の以下のリンクを確認してみます。 Kubernetesとは何か? | Kubernetes サービス ディスカバリー と負荷分散 ストレージ オーケストレーション 自動化されたロールアウトと ロールバック 自動ビンパッキング 自己修復 機密情報と構成管理 今回は、上記で説明した内容とも関わってくる"自動化されたロールアウトと ロールバック "と"自己修復"について見てみましょう。 自動化されたロールアウトと ロールバック Kubernetes を使うとデプロイしたコンテナのあるべき状態を記述することができ、制御されたスピードで実際の状態をあるべき状態に変更することができます。例えば、アプリケーションのデプロイのために、新しいコンテナの作成や既存コンテナの削除、新しいコンテナにあらゆるリソースを適用する作業を、 Kubernetes で自動化できます。 自己修復 Kubernetes は、処理が失敗したコンテナを再起動し、コンテナを入れ替え、定義したヘルスチェックに応答しないコンテナを強制終了します。処理の準備ができるまでは、クライアントに通知しません。 Podについて自動作成したりできるのは、何となくイメージがついたかと思います。 ただ、 マニフェスト に記載して宣言すればPodが作成されるようだけど、実際はどのように動いているのかという疑問が浮かんでくると思います。 具体例 AWS 環境を例として、Podを作成するまでの段取りも含めて見てみましょう。 ①dockerfileからdocker imageを作成 →自動で色々やってくれる kubernetes ですが、元となるものはやはり必要です。なので、稼働させたいdocker imageを作成します。 ②作成したdocker imageをECRにpushして格納 →ECRはdocker imageの保存場所です。 ③格納したdocker imageを利用するよう マニフェスト に記載して、 kubernetes に宣言 → マニフェスト にECRのどの リポジトリ にあるどの名前のdocker imageを利用するのかを明示して、 kubernetes に宣言します。 ④ kubernetes が マニフェスト に従って、ECRに格納しているdocker imageをpullしPodを作成 → マニフェスト に記載されたdocker imageを利用して、Podを作成します。 マニフェスト にどのdocker imageを使うのか明示しておくことで、 kubernetes は指定されたdockerを利用し、 マニフェスト に記載された通りに動作させようとしてくれます。 この時に、"自動化されたロールアウトと ロールバック "が効果を発揮します。 新規作成したい時や変更を加えたい時など、対象のdocker imageで新しいPodを作成しつつ、古いPodがあれば自動で削除してくれます。 また、稼働させていたPodに何らかのトラブルが発生し、正常に稼働できなくなったとしても、 kubernetes の自己修復機能によって、問題のあるPodを削除して、同じdocker imageで新しいPodを作成してくれます。 kubernetes の特徴である"自動化されたロールアウトと ロールバック "と"自己修復"を利用してうまく運用すれば、比較的障害に強いシステムを作ることができそうですね。 まとめ "宣言的な構成管理と自動化を促進し、コンテナ化されたワークロードやサービスを管理するための、ポータブルで拡張性のある オープンソース のプラットフォーム"という言葉の意味や kubernetes の特徴やPodについて、少しでもイメージできたでしょうか? 今回の内容はほんの一部分なので kubernetes を理解したということにはならないのですが、何かのお役に立てば幸いです。 最後までお読みいただき、ありがとうございました。 参考情報 Kubernetesドキュメント | Kubernetes KubernetesのWorkloadsリソース(その1) | Think IT(シンクイット) エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに 本記事では Python ライブラリ「Keras」を用いたレビューデータの2値分類用の ニューラルネットワーク モデルの作成についてまとめます。Kerasについてはインターネット上で多くの情報を手に入れられますが、本記事ではKerasによるモデルの作成に加えて、パラメータ最適化 フレームワーク 「Optuna」を用いた一部パラメータの自動最適化の試みについても紹介します。 はじめに Kerasとは Optunaとは Kerasによるニューラルネットワークモデルの作成の流れ Pythonのバージョンと利用ライブラリ データの入手 実行ディレクトリの整備 データの前処理 文字列の整形 データの読み込みと分割 文字列のベクトル化 Kerasによるニューラルネットワークモデルの定義 モデル作成・訓練・評価実行用クラスの定義 Optunaによる最適なパラメータの探索 最適なパラーメータの探索とニューラルネットワークモデルの作成・訓練の実施 作成したニューラルネットワークモデルの利用 結果と考察 全体スクリプト Kerasとは Kerasは オープンソース の ニューラルネットワーク /深層学習ライブラリです。 ニューラルネットワーク モデルを作成するためのライブラリにはKeras以外にも、TensorFlowやPytorchなどいくつか種類がありますが、Kerasは簡潔な記述で ニューラルネットワーク を作成できるといわれています。実際にKerasでは十数行で ニューラルネットワーク モデル周りの スクリプト を記述できます。 Keras公式サイト(日本語) keras.io Optunaとは Outunaはハイパーパラメータ自動最適化 フレームワーク です。専門用語を使わずに簡単に説明すると、Optunaは「人が自ら調整する必要があった 機械学習 モデル等のパラメータの最適値を自動的に見つけてくれるツール」です。 ニューラルネットワーク モデルでは隠れ層のユニット数など一部のパラメータの最適化を人が行うことになりますが、Optunaを使うとそれらのパラメータの最適化を自動化できます。 www.preferred.jp   Kerasによる ニューラルネットワーク モデルの作成の流れ 今回は以下の流れで ニューラルネットワーク モデルを作成します。      Python のバージョンと利用ライブラリ 利用した Python のバージョンは「 Python 3.9.9 」です。 今回の ニューラルネットワーク モデル作成に際し、以下の外部ライブラリを利用しています。 KerasはTensorflowのtensorflow.kerasモジュールを通して利用します。 pandas cleantext scikit-learn tensorflow optuna データの入手 今回は英語のレビュー投稿に対し、内容が肯定的か否定的かを予測する ニューラルネットワーク モデルの作成を試みます。そのために必要なデータをKaggleから入手することにします。 Kaggleについて詳しくは公式サイトや紹介記事を参照して下さい。 www.kaggle.com ai-kenkyujo.com 今回は、こちらのTrip Advisor Hotel Reviewsという英語のホテルのレビュー投稿のデータを使います。 www.kaggle.com このデータは行毎にレビュー本文とレーティング(肯定的か否定的かの指標になります)がセットになっている形式のため、モデルの訓練用として使いやすいと思います。      *Alam, M. H., Ryu, W.-J., Lee, S., 2016. Joint multi-grain topic sentiment: modeling semantic aspects for online reviews. Information Sciences 339, 206–223. ※ レビュー本文を見てみると文法的に?な英文が結構ありますが、今回はそのまま使うことにします。(公開用に匿名化処理をした影響なのかもしれません。) 実行 ディレクト リの整備 予め以下のような構造で実行 ディレクト リを整備しておきます。実行用のpyファイル、ダウンロードしたレビューの csv ファイル、Tensorboardに表示するデータの保存先となるフォルダを配置します。     データの前処理 レビューデータはそのまま使わず、文字列の整形やデータの分割、文字列のベクトル化といった前処理を行います。まずはそのための関数をいくつか定義します。 文字列の整形 レビュー本文は英語のため、それほど複雑な処理は必要ないと思います。今回は、 Python の前処理ライブラリのclean-textと組み込み関数のみで前処理を実施します。 Clean-text pypi.org ※ インストール時にはハイフンありのものを指定してください。「cleantext」という別のライブラリもあるので注意してください。    pip install clean-text ※  Python の スクリプト を掲載する際には、なるべく説明のコメントを入れ、関数には Google スタイルのdocstringライクな説明をつけるようにします。 def normalize_doc (text, no_digits= False , no_numbers= False , to_ascii= False ): """訓練データ正規化 訓練用データ(テキスト)を正規化します。 Args: text (str): 正規化したいテキスト no_digits (bool, optional): 桁数を維持したまま全ての数字を0に変換するかどうか(123は000に、5678は0000に変換されます) no_numbers (bool, optional): 桁数を残さず数字を全て指定した文字列(今回は0に設定しています)に変換するかどうか(123は0に5678も0に変換されます) to_ascii (bool, optional): 非アスキー文字をアスキー文字に変換するかどうか (日本語は非アスキー文字なため、日本語を含むテキストにはFalseを設定) Returns: str: 正規化済テキスト """ # テキストの最初と最後の余分なスペースを除去します。 text = text.strip() # 正規化用ライブラリを使ってデータを正規化します。ここではunicode errorの除去とアルファベットの小文字化のみを行っています。 text = cleantext.clean(text, no_digits=no_digits, to_ascii=to_ascii, no_numbers=no_numbers, replace_with_number= "0" ) return text データの読み込みと分割 CSV ファイルを読み込み、レーティングのつけ直しとデータ数の学習用と評価用への分割を行います。 レーティングのつけ直しでは、今回の予測を「レビューがポジティブかネガティブか」の2値予測(2値分類)とするため、レーティング1と2を「0」(ネガティブ)、レーティング4と5は「1」(ポジティブ)につけ直します。どちらとも取れそうなレーティング3は除外することにします。 また、データを学習用とモデルの評価用に分割する処理も行います。このとき、ポジティブレビューとネガティブレビューそれぞれで学習用と評価用に分割し、その後に学習用のポジティブレビューとネガティブレビュー同士、評価用のポジティブレビューとネガティブレビュー同士を結合します。 (全体の8割を学習用とした場合、すべてのネガティブレビューが学習用となり、評価用にはポジティブレビューしか含まれなくなることを防ぐためです。2番目の図で示す処理を行います。)      def load_data (path): """訓練データ、正解ラベルの加工と訓練用。評価用への分割 CSVファイルから訓練データと正解ラベルを取り出し、正解ラベルの2値への変換と データの学習用と評価用への分割を行います。 訓練用データのデータフレームと評価用データのデータフレームを返します。 Args: path (str): レビュー本文とレーティングのCSVファイルのパス :Returns: pandas.DataFrame: 学習データのデータフレーム pandas.DataFrame: 評価用ラベルのデータフレーム """ # レビュー本文とレーティングのCSVファイルをデータフレーム形式で読み込みます。 review_df = pd.read_csv(path, header= 0 ) # データフレームの列を参照するときに便利なので列ラベルを取得しておきます。 columns = review_df.columns # レビューを「ポジティブ」、「ネガティブ」の2値で予測するため、レーティング1-2は0(ネガティブ)に、4-5は1(ポジティブ)に変換します。 # 今回中間の3は無視します。(3はポジティブともネガティブともとれる場合がありそうなため) mapping = { 1 : 0 , 2 : 0 , 3 : 3 , 4 : 1 , 5 : 1 } review_df[columns[ 1 ]] = review_df[columns[ 1 ]].map(mapping) # ネガティブレビューのみでフィルターをかけます。 negative_review_df = review_df[review_df[columns[ 1 ]] == 0 ] # ポジティブレビューのみでフィルターをかけます。 positive_review_df = review_df[review_df[columns[ 1 ]] == 1 ] # ネガティブレビュー、ポジティブレビューそれぞれを学習用と評価用に分割します。今回は学習用8割、評価用2割に分割します。 # 分割にはsklearnのtrain_test_splitを利用します。 # レビュー本文の前処理を同時に行うためにいったんデータをリスト形式に変えています。 xn = [normalize_doc(review_body) for review_body in negative_review_df[columns[ 0 ]]] yn = list (negative_review_df[columns[ 1 ]]) xp = [normalize_doc(review_body) for review_body in positive_review_df[columns[ 0 ]]] yp = list (positive_review_df[columns[ 1 ]]) xn_train, xn_test, yn_train, yn_test = train_test_split( xn, yn, test_size= 0.2 , random_state= 42 , ) xp_train, xp_test, yp_train, yp_test = train_test_split( xp, yp, test_size= 0.2 , random_state= 42 , ) # ネガティブレビューとポジティブレビューデータを結合し、学習用データのデータフレームと評価用データのデータフレームを作成します。 x_train = xn_train + xp_train x_test = xn_test + xp_test y_train = yn_train + yp_train y_test = yn_test + yp_test # 学習用データのデータフレーム train = list () for x, y in zip (x_train, y_train): train.append([x, y]) train_df = pd.DataFrame(train, columns=[ "Review" , "Rating" ]) # 評価用データのデータフレーム test = list () for x, y in zip (x_test, y_test): test.append([x, y]) test_df = pd.DataFrame(test, columns=[ "Review" , "Rating" ]) # 関数の戻り値としてデータフレームをセットする際に「.sample(frac =1).reset_index(drop=True)」でデータをシャッフルしインデックスをふり直します。 return train_df.sample(frac= 1 ).reset_index(drop= True ), test_df.sample(frac= 1 ).reset_index(drop= True ) 文字列のベクトル化 モデルの訓練の際、テキストデータをそのまま入力とすることができません。 ベクトル化というテキストを数値列に変換する作業が必要になります。そこで今回は「TF-IDF」というテキスト内の単語の重要性を反映した数値列によるベクトル化を行ってみます。 また、今回は「 ストップワード 」という頻度単語のフィルタを使わない代わりに、SklearnのTfidfVectorizerのパラメータの「max_df」を設定し、重要ではない単語の除去を試みます。 Tf-idfについて atmarkit.itmedia.co.jp Sklearnの公式ドキュメント scikit-learn.org def get_vectorizer (corpus_x_train, corpus_x_test): """テキストのTF-IDFによるベクトル化 レビュー本文データをtfidfによってベクトル化します。ベクトライザー(ベクトルへの変換器)はデータのうち モデルの訓練に使うデータのみで訓練します。評価用データは学習データで訓練したベクトライザーを使いベクトル化します。 Args: corpus_x_train (iterator): データのうち訓練用に分割した部分 corpus_x_test (iterator): データのうち評価用に分割した部分 Returns: sklearn.feature_extraction.text.TfidfVectorizer: 訓練後のTF-IDFベクトライザー ndarray: ベクトル化の対象となった語彙のリスト (ndarray of str objects) ndarray: ベクトル化した訓練用データ ndarray: ベクトル化した評価用データ ベクトライザー、ベクトル化の対象となった語彙のリスト、ベクトル化した訓練用データ、ベクトル化した評価用データ """ # ベクトライザーを作成します。max_dfの値を設定すると指定した値の割合以上の文書に含まれる語彙はベクトル化の対象とならなくなります。 # max_dfの値を0.3に設定していますが、データの実情に合わせて調整が必要です vectorizer = TfidfVectorizer(max_df= 0.3 ) # 訓練用データを使ってベクトライザーを訓練し、同時に訓練用データのベクトル化も行います。 vec_corpus_x_train = vectorizer.fit_transform(corpus_x_train) # 訓練したベクトライザーで評価用データのベクトル化を行います。 vec_corpus_x_test = vectorizer.transform(corpus_x_test) return vectorizer, vectorizer.get_feature_names_out(), vec_corpus_x_train.toarray(), vec_corpus_x_test.toarray() Kerasによる ニューラルネットワーク モデルの定義 データの前処理用関数の定義が終わったので、Kerasを使い ニューラルネットワーク モデルを定義していきます。 今回は次のような ニューラルネットワーク モデルを定義していきます。     ※ 中間層が2層ある ニューラルネットワーク モデルは深層学習モデルともみなせますが、本記事では「 ニューラルネットワーク モデル」と呼ぶことにします。 テキスト(今回はレビュー本文)のベクトルデータを入力とし、入力層→中間層1→中間層2→出力層と処理を行っていきます。 今回はKerasのFunctionalモデルを使いこのモデルを定義します。Kerasでは、モデルの定義に「Functional API 」と「Sequentialモデル」が使えますが、複数入力、複数出力をするような複数なモデルを定義する際にはFuncitional API を使います。(今回はベクトル1つを入力とするので複数入力ではありませんが、将来的な発展の可能性も考えて、Functional API を選んでいます。) KerasはTensorflowのtensorflow.kerasモジュールからインポートして使います。 keras.io keras.io Kerasでモデルを定義する際は、keras.layers.Inputやkeras.layers.Denseを使い各層のユニット数や活性化関数を定義していきます。 上の図では省略していますが、中間層1と中間層2の間、中間層2と出力層の間にDropout層(keras.layers.Dropout)を追加し 過学習 対策をします。(上の図では省略) deepage.net 最後にkeras.Modelのcompileメソッドを使い、モデルの損失関数や オプティマ イザを指定します。 def create_model (n_vocab, unit): """Kerasによるニューラルネットワークの定義 KerasのFuncitional APIを使いニューラルネットワークモデルを定義します。 Args: n_vocab (int): 入力として渡すデータの次元数です。入力層のユニット数となります。(ベクトライザーから取得する語彙のリスト内の要素数とします。) unit (int): 中間層のユニット数です。任意の数を設定できますが、今回はOptunaによる最適な値(整数)を自動で設定します。 Returns: tensorflow.keras.Model: ニューラルネットワークモデル(オブジェクト) """ # 入力層を追加します。ユニット数はvocabと同じです。 input_ = keras.layers.Input(shape=(n_vocab,)) # 中間層(全結合層)とドロップアウト層を追加します。ユニット数は引数unitで指定します。 x = keras.layers.Dense(unit, activation= "relu" )(input_) x = keras.layers.Dropout( 0.8 )(x) # 2層目の中間層(全結合層)とドロップアウト層を追加します。ユニット数は引数unitで指定します。 x = keras.layers.Dense(unit, activation= "relu" )(x) x = keras.layers.Dropout( 0.5 )(x) # 出力層を追加します。 ソフトマックス関数を使う2値分類のためユニット数は2です。 output = keras.layers.Dense( 2 , activation= "softmax" )(x) # モデル(オブジェクト)を作成します。 model = keras.Model(inputs= input , outputs=output) # パラメータの更新や損失関数、評価関数に何を使うかを設定します。変更する必要はないかと思います。 model.compile( optimizer= "adam" , loss= "sparse_categorical_crossentropy" , metrics=[ "accuracy" ] ) # モデルの概要を出力します。 print (model.summary()) return model モデル作成・訓練・評価実行用クラスの定義 Kerasによる ニューラルネットワーク モデルの定義ができたので、次に「モデル(のオブジェクト)を作成し、そのモデルの訓練を行うクラス」を作成します。 これまでに作成した前処理関数を使い、学習データを整え、 ニューラルネットワーク もですの作成、訓練から評価までを行うクラスとしてみます。 class NnModel : """ニューラルネットワーク作成・訓練・評価用クラス ニューラルネットワークモデルオブジェクトの作成、訓練、評価を行うためのクラスです。 Attributes: vec_x_train (ndarray): ベクトル化済の訓練用データ x_test (pandas.Series): ベクトル化前の評価用データ vec_x_test (ndarray): ベクトル化済の評価用データ y_train (pandas.Series): モデルの訓練時に使う正解ラベル y_test (pandas.Series): モデルの評価時に使う正解ラベル model (tensorflow.keras.Model): model_fit実行時に作成されるニューラルネットワークオブジェクト """ def __init__ (self, path): # CSVファイルから訓練データと正解データを取得します。 train_df, test_df = load_data(path) columns = train_df.columns x_train = train_df[columns[ 0 ]] self.x_test = test_df[columns[ 0 ]] self.y_train = train_df[columns[ 1 ]] self.y_test = test_df[columns[ 1 ]] # 訓練データをベクトル化します。 self.vectorizer, self.vocab, self.vec_x_train, self.vec_x_test = get_vectorizer(x_train, self.x_test) # 後で訓練後のモデルを利用する際に必要になるので、ベクトライザーのオブジェクトをpickleファイルに保存しておきます。 with open ( "my_vectorizer.pickle" , "wb" ) as f: pickle.dump(self.vectorizer, f) def model_fit (self, unit, logdir, validation_split= 0.2 , epochs= 20 , batch_size= 32 ): """ニューラルネットワークモデル (tensorflow.keras.Model)の作成と用意した訓練データでの訓練 ニューラルネットワークモデルオブジェクトを作成し、用意した訓練データで訓練します。 Optunaとの連携のため、ニューラルネットワークオブジェクトはこのメソッドで作成します。 Args: unit (int): 中間層のユニット数です。今回はoptunaによって最適な値を設定します。 logdir (str): 訓練時の学習ログを保存するフォルダを指定します。(tensorboardを使う場合にこのフォルダ内のログを参照します。) validation_split (float): 訓練時(keras.Modelのfitメソッド実行時)の各エポックで検証用に使う訓練データの割合を指定します。 epochs (int): 訓練データを繰り返して学習させる回数を指定します。 batch_size (int): 何件の訓練データ毎にパラメータの更新を行うかを指定します。 Returns: float: モデルの評価用データでの損失値 """ # ニューラルネットワークモデル(オブジェクト)を作成します。 self.model = create_model( len (self.vocab), unit) # 学習の進行状況の可視化や精度が向上しなくなった際の学習の早期終了を行う関数をコールバックにまとめます。 tensorboard = keras.callbacks.TensorBoard(log_dir=logdir) early_stopping = keras.callbacks.EarlyStopping(monitor= 'val_loss' , patience= 3 ) model_name = "my_neural_model.h5" modelCheckpoint = keras.callbacks.ModelCheckpoint( filepath=model_name, monitor= 'val_loss' , verbose= 1 , save_best_only= True , save_weights_only= False , mode= 'min' , period= 1 ) callbacks = [tensorboard, early_stopping, modelCheckpoint] # モデルの訓練を実施します。 self.model.fit( self.vec_x_train, self.y_train, validation_split=validation_split, epochs=epochs, batch_size=batch_size, callbacks=callbacks # 上で定義したコールバック関数のリストを指定します。 ) # my_neural_model.h5に保存されている(各エポックのうち最も精度の良かった)モデルを読み込んでテスト用データで精度を算出します。 model = keras.models.load_model(model_name) pred = model.evaluate(self.vec_x_test, self.y_test) # ニューラルネットワークモデルの隠れ層の最適ユニット数の推定のため、今回はテストデータでの損失値を学習メソッドの戻り値としておきます。 return pred[ 0 ] def model_predict (self): """ニューラルネットワークモデルの性能評価 train_test_splitで分割したデータのうち評価用のデータを使い、 model_fitで訓練したモデルの性能を評価します。 Returns: list: モデルの予測結果のリスト(予測ラベルのリスト) """ # (keras.Modelの)predictメソッドに評価用のデータを渡します。 y_pred = self.model.predict(self.vec_x_test) # 予測結果のうち、「1」(あり)、「0」(なし)のうち予測数値が高かった方をリストに追加します。 y_pred = [p.argmax() for p in y_pred] # sklearnのスコア算出用の各関数に予測ラベルと正解ラベルを渡しモデルの性能を評価します。 print ( "result" ,f "accuracy: {accuracy_score(self.y_test, y_pred)}" , f "precision: {precision_score(self.y_test, y_pred)}" , f "recall: {recall_score(self.y_test, y_pred)}" , sep= " \r\n " ) return y_pred Optunaによる最適なパラメータの探索 Kerasを使った ニューラルネットワーク モデルの作成から評価までを行うクラスが定義できたので、実行して結果を得ることもできるのですが、今回は中間層の最適なユニット数を「Optuna」を使って自動最適化してみます。 Optunaを使う際には、最適化したいパラメータとそのデータ型、ある値が最適かどうかを判断するための指標に何を使うかを指定します。今回は: 最適化したいパラメータとそのデータ型: 中間層のユニット数 整数(int)型 最適かどうかの指標: 指定したパラメータでのモデル訓練後の損失値 という条件でパラメータの自動最適化を実施します。 keras.io keras.io def objective (trial): """Optunaでパラメータ推定を行う際の処理内容 Optunaでパラメータ推定を行うための関数です。 目的のパラメータを変更した際の精度の指標となる数値が戻り値となるようにしておく必要があります。 (値が改善すればより良いパラーメータ値とみなします。) Args: trial: OptunaのStudyオブジェクトが設定します。 :Returns: float: Optunaがパラメータを設定した作成したモデルの損失値(精度指標) """ # 今回はニューラルネットワークモデルの中間層のユニット数の自動推定を試みます。 # 指定した数値(今回は2-30)の間で最も精度が高くなる数値を最適なユニット数とみなします。 # 推定する数値は後で参照するために"unit"と指定しておきます。 unit = trial.suggest_int( "unit" , 2 , 30 ) loss = nn_model.model_fit(unit, r"logs" , epochs= 100 ) return loss 最適なパラーメータの探索と ニューラルネットワーク モデルの作成・訓練の実施 これまで定義してきた関数とクラス、Optunaを使い、最適なユニット数の探索と最適なユニット数を設定したモデルの作成を行います。 処理の流れは次のようになります。 ニューラルネットワーク モデル作成用オブジェクトの作成(学習/評価用データの前処理) OptunaのStudyオブジェクトを作成、最適なユニット数を探索 探索完了後にStudyオブジェクトからユニット数の数値を取得 ニューラルネットワーク モデル作成用オブジェクト(keras.Model)のモデル作成/訓練メソッド(fit)に最適なユニット数を渡してモデルを作成/訓練 評価用データでモデルを評価 # ニューラルネットワークモデル作成用オブジェクトを作成します。 target_data = r"tripadvisor_hotel_reviews.csv" nn_model = NnModel(target_data) def main (): """実行用関数 これまでに作成してきた関数とクラスを使い最適な中間層ユニット数の推定から モデルの定義、訓練、評価までを実行します。 Returns: None """ # oputunaで最適なユニット数を見つけます。最後にBest unitとして出力されます。 # directionは損失関数の場合はminimizeを指定します。(正解率を精度指標とする場合にはmaxizeを指定します。) study = optuna.create_study(direction= "minimize" ) # 最適なユニット数を見つけるために何度試行するかを指定し、自動推定を実行します。 study.optimize(objective, n_trials= 10 ) # 最適なユニット数はこの変数から best_params["unit"] とすると取得できます。ディレクトリ型の参照と同じです。 best_params = study.best_params print (f "Best unit: {best_params}" ) # 最適な数値を中間層のユニット数に指定し、モデルの定義と訓練、評価を行います。 nn_model = NnModel(target_data) nn_model.model_fit(best_params[ "unit" ], r"logs" , epochs= 100 ) nn_model.model_predict() if __name__ == "__main__" : main() 今回作成したモデルの評価結果は以下のようになりました。      Accuracy: 0.9555      Precision: 0.9609      Recall: 0.9861 かなり良い数値ですが、Tensorboardでモデル損失のグラフを確認してみると、2エポック以降減少が見られません。対策はしていましたが 過学習 をしてしまったようです。 (濃い青の線が学習データでの損失値、水色の線が評価用データでの損失値)      (Tensorboardでは学習データでモデルを繰り返し訓練する中で、どのように性能が変化していったかを確認することができます。) (model_fitメソッドのtensorboard = keras.callbacks.TensorBoard(log_dir=logdir)の行でtensorboardの設定を行っています。) www.tensorflow.org deepage.net 作成した ニューラルネットワーク モデルの利用 最後に作成したモデルを使って、未知のデータでの予測を行うための関数を作成してみます。 モデル作成時に使った ベクトラ イザーと保存した ニューラルネットワーク モデルを読み込み、それらに予測を行いたいデータ(テキスト)を渡して予測結果の確率を出力します。 保存したモデルは、tensorflow.keras.models.load_modelで読み込みます。 def model_load_predict (x, model_h5, vec_pickle): """ニューラルネットワークモデルを利用した未知データに対する予測用関数 作成・保存したニューラルネットワークモデルを利用して、未知データの予測を行います。 Args: x (str): テキストデータ(レビュー本文と同じ形式) model_h5 (str): 作成したニューラルネットワークモデルのh5ファイルのパス vec_pickle (str): 保存したベクトライザーのpickleファイルのパス Returns: int: 予測ラベルの整数値(入力したテキストをネガティブと予測した場合は0,、ポジティブと予測した場合は1) """ # モデルとベクトライザーを読み込みます。 model = keras.models.load_model(model_h5) with open (vec_pickle, 'rb' ) as f: vectorizer = pickle.load(f) # 入力データの正規化とベクトル化を行います。 x = normalize_doc(x) vec_x = vectorizer.transform([x]).toarray() # 入力データに対して予測を行い、結果を出力します。 pred = model.predict(vec_x) print ( "== Result ==" ) print ( "Positive" ) if pred[ 0 ][ 1 ] > pred[ 0 ][ 0 ] else print ( "Negative" ) print (f "Positive: {pred[0][1]}, Negative: {pred[0][0]}" ) return pred.argmax() if __name__ == "__main__" : x = input ( ">>>" ) model_h5 = "my_neural_model.h5" vec_pickle = "my_vectorizer.pickle" res = model_load_predict(x, model_h5, vec_pickle) print ( "予測ラベル: " , res) 関数を実行し「>>>」と表示されたら、ネガティブかポジティブかを予測したいテキストを入力します。テキスト入力後に「enter」を押すと予測結果が出力されます。(予測結果の前に警告が表示されることがありますが、今回は無視します。)           結果と考察 KerasとOptunaを使い、実際に予測を行うことができる ニューラルネットワーク モデルを作成することができました。 このモデルは評価結果の数値を見ると悪くないように見えるのですが、未知のレビューデータに対する予測性能が高いとは言えません。一目でポジティブとわかる内容のレビューに対して、99%ネガティブという予測をすることもありました。 今回の反省点としては、 データの質が良くなかった。   → 今回は文法的に怪しい英文もそのまま使いました。これが良くなかったのかもしれません。   → 非常に長いレビュー本文を持つレコードが含まれていましたが、この手のレコードは「いい点もあった。が、しかし…」という流れでポジティブな側面とネガティブな側面を持っている可能性があり、どちらかに分類するには容易ではないのかもしれません。 データが足りなかった。   → 2万レコードでは足りなかったのかもしれません。 ベクトル化の方法が良くなかった。   → TF-IDFよりも単語の意味を反映するとされる分散表現の方がよかったのかもしれません。 別のハイパーパラメータもOptunaで最適化すべきだった。   → ユニット数以外にも学習率やバッチサイズも最適な値をOptunaで設定するとよかったのかもしれません。 モデルの アーキテクチャ が適切でなかった。   → 今回のような ニューラルネットワーク モデルよりもリカレント ニューラルネットワーク モデルの方がテキスト分類には適しているのかも知れません。 といったことが考えられます。このモデルを改善する際や新たなモデルを作成する際には、上記の反省点を踏まえる必要があると感じています。 特にデータに関しては、大量のデータが必要になるため、ある試行でうまくいかなかった際に、簡単に代替のデータを用意できるとは限りません。 自前でデータを用意する際には、最初から質の高いデータが揃うようにデータ収集作業を行う必要があると思います。 全体 スクリプト 最後に、今回の ニューラルネットワーク モデル作成の試みに関わる スクリプト 全体をまとめておきます。 テキストデータの分類であれば、少しの編集でいろいろなデータを扱えると思います。ぜひ、ご自身のデータで予測モデルを作成してみてください。 # coding: utf-8 import pandas as pd import pickle import cleantext import optuna from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, precision_score, recall_score from tensorflow import keras def normalize_doc (text, no_digits= False , no_numbers= False , to_ascii= False ): text = text.strip() text = cleantext.clean(text, no_digits=no_digits, to_ascii=to_ascii, no_numbers=no_numbers, replace_with_number= "0" ) return text def load_data (path): review_df = pd.read_csv(path, header= 0 ) columns = review_df.columns mapping = { 1 : 0 , 2 : 0 , 3 : 3 , 4 : 1 , 5 : 1 } review_df[columns[ 1 ]] = review_df[columns[ 1 ]].map(mapping) negative_review_df = review_df[review_df[columns[ 1 ]] == 0 ] positive_review_df = review_df[review_df[columns[ 1 ]] == 1 ] xn = [normalize_doc(review_body) for review_body in negative_review_df[columns[ 0 ]]] yn = list (negative_review_df[columns[ 1 ]]) xp = [normalize_doc(review_body) for review_body in positive_review_df[columns[ 0 ]]] yp = list (positive_review_df[columns[ 1 ]]) xn_train, xn_test, yn_train, yn_test = train_test_split( xn, yn, test_size= 0.2 , random_state= 42 , ) xp_train, xp_test, yp_train, yp_test = train_test_split( xp, yp, test_size= 0.2 , random_state= 42 , ) x_train = xn_train + xp_train x_test = xn_test + xp_test y_train = yn_train + yp_train y_test = yn_test + yp_test train = list () for x, y in zip (x_train, y_train): train.append([x, y]) train_df = pd.DataFrame(train, columns=[ "Review" , "Rating" ]) test = list () for x, y in zip (x_test, y_test): test.append([x, y]) test_df = pd.DataFrame(test, columns=[ "Review" , "Rating" ]) return train_df.sample(frac= 1 ).reset_index(drop= True ), test_df.sample(frac= 1 ).reset_index(drop= True ) def get_vectorizer (corpus_x_train, corpus_x_test): vectorizer = TfidfVectorizer(max_df= 0.3 ) vec_corpus_x_train = vectorizer.fit_transform(corpus_x_train) vec_corpus_x_test = vectorizer.transform(corpus_x_test) return vectorizer, vectorizer.get_feature_names_out(), vec_corpus_x_train.toarray(), vec_corpus_x_test.toarray() def create_model (n_vocab, unit): input_ = keras.layers.Input(shape=(n_vocab,)) x = keras.layers.Dense(unit, activation= "relu" )(input_) x = keras.layers.Dropout( 0.8 )(x) x = keras.layers.Dense(unit, activation= "relu" )(x) x = keras.layers.Dropout( 0.5 )(x) output = keras.layers.Dense( 2 , activation= "softmax" )(x) model = keras.Model(inputs= input , outputs=output) model.compile( optimizer= "adam" , loss= "sparse_categorical_crossentropy" , metrics=[ "accuracy" ] ) print (model.summary()) return model class NnModel : def __init__ (self, path): train_df, test_df = load_data(path) columns = train_df.columns x_train = train_df[columns[ 0 ]] self.x_test = test_df[columns[ 0 ]] self.y_train = train_df[columns[ 1 ]] self.y_test = test_df[columns[ 1 ]] self.vectorizer, self.vocab, self.vec_x_train, self.vec_x_test = get_vectorizer(x_train, self.x_test) with open ( "my_vectorizer.pickle" , "wb" ) as f: pickle.dump(self.vectorizer, f) def model_fit (self, unit, logdir, validation_split= 0.2 , epochs= 20 , batch_size= 32 ): self.model = create_model( len (self.vocab), unit) tensorboard = keras.callbacks.TensorBoard(log_dir=logdir) early_stopping = keras.callbacks.EarlyStopping(monitor= 'val_loss' , patience= 3 ) model_name = "my_neural_model.h5" modelCheckpoint = keras.callbacks.ModelCheckpoint( filepath=model_name, monitor= 'val_loss' , verbose= 1 , save_best_only= True , save_weights_only= False , mode= 'min' , period= 1 ) callbacks = [tensorboard, early_stopping, modelCheckpoint] self.model.fit( self.vec_x_train, self.y_train, validation_split=validation_split, epochs=epochs, batch_size=batch_size, callbacks=callbacks ) model = keras.models.load_model(model_name) pred = model.evaluate(self.vec_x_test, self.y_test) return pred[ 0 ] def model_predict (self): y_pred = self.model.predict(self.vec_x_test) y_pred = [p.argmax() for p in y_pred] print ( "result" , f "accuracy: {accuracy_score(self.y_test, y_pred)}" , f "precision: {precision_score(self.y_test, y_pred)}" , f "recall: {recall_score(self.y_test, y_pred)}" , sep= " \r\n " ) def objective (trial): unit = trial.suggest_int( "unit" , 2 , 30 ) loss = nn_model.model_fit(unit, r"logs" , epochs= 100 ) return loss target_data = r"tripadvisor_hotel_reviews.csv" nn_model = NnModel(target_data) def main (): study = optuna.create_study(direction= "minimize" ) study.optimize(objective, n_trials= 10 ) best_params = study.best_params print (f "Best unit: {best_params}" ) nn_model = NnModel(target_data) nn_model.model_fit(best_params[ "unit" ], r"logs" , epochs= 100 ) nn_model.model_predict() def model_load_predict (x, model_h5, vec_pickle): model = keras.models.load_model(model_h5) with open (vec_pickle, 'rb' ) as f: vectorizer = pickle.load(f) x = normalize_doc(x) vec_x = vectorizer.transform([x]).toarray() pred = model.predict(vec_x) print ( "== Result ==" ) print ( "Positive" ) if pred[ 0 ][ 1 ] > pred[ 0 ][ 0 ] else print ( "Negative" ) print (f "Positive: {pred[0][1]}, Negative: {pred[0][0]}" ) return pred.argmax() if __name__ == "__main__" : main() x = input ( ">>>" ) model_h5 = "my_neural_model.h5" vec_pickle = "my_vectorizer.pickle" res = model_load_predict(x, model_h5, vec_pickle) print (res) エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
【目次】 PdM とは PM(プロジェクトマネージャー)との違い PdM/PMの役割表 PdM に必要なスキル おすすめの書籍/勉強会 ラクスのPdMとして活躍してみませんか? 終わりに 技術広報の yayawowo です。 昨今、PdM(プロダクトマネージャー)という言葉を耳にすることが増えたのではないでしょうか? しかしながら… 「実際何をしている人かわからない!」 「PdMを目指しているけど、必要なスキルって…?」 と不明点もあると思います。 本記事では、PdM(プロダクトマネージャー)の基本や必要なスキル等を紹介させていただきます。 ◆ 関連ブログも合わせてご確認ください! ・ プロジェクトマネジメント とは 【まとめ】 ・ PMBOK とは【まとめ】 ・ 要件定義 とは【まとめ】 ・ プロジェクトマネジメントTips 20選 ~現場から語るプロマネの極意~ PdM とは PdMとは、プロダクトマネージャーのことを指します。 経営方針や企業戦略、ビジネスサイド側の販売計画に基づく顧客要求の分析、要求分析/定義を行い、「プロダクト(製品・商品・サービス)」を成功に導く責任者をPdMと呼びます。 またプロダクトとは、一般的にお客様へ販売する製品のことを言いますが、ソフトウェア・データ等も含まれます。 元々、製造業で使われていた言葉でしたが、昨今はIT/Web業界にまで派生し、注目を集めています。 当社PdM(プロダクトマネージャー)は、製品企画を行う「製品力会議」から参加します。 「製品力会議」では、ビジネスサイドの部長やカスタマーサクセスと顔を合わせるため、開発ロードマップにPdM(プロダクトマネージャー)としての声を反映させることができます!   また、PdM(プロダクトマネージャー)の類似職種として、PM(プロジェクトマネージャー)があります。 以下にて、職種の違いを解説します。 PM(プロジェクトマネージャー)との違い PM(プロジェクトマネジャー)とは、期間が定められたプロジェクトの達成/成功に向けて進捗、品質、コストを管理しコン トロール する人のことを指します。 担当プロジェクトに対し、責任をもって管理することが求められる職種です。 プロジェクトマネジメントを解説している記事も是非ご参考ください。 tech-blog.rakus.co.jp PdM/PMの役割表 職種 役割 PdM 経営方針や企業戦略、ビジネスサイド側の販売計画に基づく顧客要求の分析、要求分析/定義を行い、「プロダクト(製品・商品・サービス)」を成功に導く責任を担う PM 期間が定められたプロジェクトの達成/成功に向けて進捗、品質、コストの管理責任を担う PdM、PMとの役割が異なることをご理解いただけたでしょうか? PdM に必要なスキル ◆ マネジメント面 ・ コミュニケーション力 ・ 進捗管理 能力 ・ 課題解決力 プロダクトマネージャーという名前の通り、マネジメント面でのスキルは重要です。 上記3点は、特に必要となるスキルセットとなります。 ◆ ビジネス面 ・ マーケティング 力 ・ 業界知識 市場調査を行い、分析することで市場ニーズを把握することが必要です。 プロダクトを開発にするにしても、市場のニーズに合っていないものを開発するのは会社としてもメリットがありません。 マーケティング 力と業界知識は、PdM(プロダクトマネージャー)にとって大切なスキルと言えるでしょう。 ◆ テク ノロ ジー 面 ・ UI/UXデザイン ・ 開発スキル お客様にシステムを長く使ってもらうには、使いやすいデザイン、構成となっているのが大切です。 お客様に選ばれるプロダクトにするためには、UI/UXのスキルを深めておくことが大切となります。 また、デザイン性だけでなくプロダクト設計も同様のことが言えますので、ある程度の開発スキルがあることは好ましいと思います。 おすすめの書籍/勉強会 おすすめの書籍と勉強会をご紹介いたします! ◆ 書籍 PdM(プロダクトマネージャー)におすすめの書籍を並べてみました。 学習の一助となれば幸いです。 『 プロダクトマネジメント のすべて 事業戦略・IT開発・UXデザイン・ マーケティング からチーム・組織運営まで』 タイトルの通り、まさに プロダクトマネジメント の決定版といえる書籍です。 180点超の図版で分かりやすく解析しています。 また、 ケーススタディ もついているので、実践的に学ぶことができます。 『INSPIRED 熱狂させる製品を生み出す プロダクトマネジメント 』 シリコンバレー で実施している プロダクトマネジメント の手法がまとめられている書籍です。 例えば、 Google 、 Apple 、 Facebook 、 Netflix 、Tesla、 Amazon 等の手法を学ぶことが出来ます。 『 プロダクトマネジメント ―ビルドトラップを避け顧客に価値を届ける』 プロダクトマネジメント で必要な情報が一通り書かれている一冊です。 マネジメント手法において必要な戦略や、組織、プロセスについてを論じています。 ◆ 勉強会 当社主催の技術イベントに「PdM TIps LT会」がございます。 rakus.connpass.com 2021年3月に開催した際は、以下発表テーマがありました。 【発表テーマ】 ・ アラフィフの開発者が プロダクトマネジメント をはじめてみた ・ ジョブ理論とプロダクト開発 ・ oVice着想から初有料ユーザーを獲得するまでのPMビハインドストーリー ・ PMが「開発を巻き込んだ施策相談会」で工夫したポイント ・ 私たち“プロダクト主導組織”になれてるかな?を問う自問自答Tips ・ LabTech企業の初代PMになってやってきたこととこれから ・ PdM忙しい問題どうします? -80点以上のプロダクトを目指したら緩和されてきた話- ・ PjM→PdMになった1年目の失敗とTips vol.2も今年の夏ごろに開催を予定しております。 ご興味ありましたら connpass 又は TECH PLAY を定期的にご確認いただけますと幸いです! ラク スのPdMとして活躍してみませんか? 当社 ラク スでは、プロダクトマネージャーを積極的に募集しております! PdM(プロダクトマネージャー)組織は2021年8月からスタートし、サービス全体の視点を持って働ける環境が備わっております。 ビジネスサイドを通じてお客様の課題把握、プロダクトの開発部隊へ抜けもれなく効率的に要求を落とし込み、開発側へ連携するために当社ではPdMというポジションを置いております。 そのため、当社PdMは以下スキルを重要視しております。 Webサービス で開発知識 RFP 、要求仕様策定の実務経験 ステークホルダー との折衝・調整経験 そんな ラク スのPdM特徴は以下の通りです! ラク スのPdM 開発スタイル ベスト・オブ・ブリード (異なるフェーズのマルチプロダクト) 組織 プロダクト単位でコミット 案件 顧客課題・案件単位のコミット 当社PdM(プロダクトマネージャー)の面白さは、挑戦の裁量と規模です。 当社最大のプロダクトについてサービス全体の視点を持って動けること 新しいチームで、PdMの役割そのものを立ち上げていけること 上記の内容にピンっ!ときた方は、以下の求人内容を是非ご確認ください! career-recruit.rakus.co.jp 「まずは、会社説明を聞いてみたい!」というのも大歓迎です。 以下フォームより申し込みをお願いいたします! rakus.hubspotpagebuilder.com 終わりに PdM(プロダクトマネージャー)のまとめ記事はいかがでしたでしょうか? 年々、社会全体としてPdMポジションは重要となってきております。 今後PdMを目指す方にとって、本記事の内容が少しでもお役に立てれば幸いです! もし当社PdMに少しでもご興味をお持ちになりましたら、定期的に行っているイベントもございます。 是非ご参加ください! ◆ イベント情報 < connpass > rakus.connpass.com < TECH PLAY > techplay.jp PdM Tips LT会 - vol.2の開催が決定! ご興味ありましたら以下より、ご登壇/ご視聴の申込をお願いいたします! techplay.jp rakus.connpass.com お気軽にどうぞ。 最後までお読みいただきありがとうございました! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
こんにちは、弊社サービスのインフラを運用している id:keijiu (ijikeman)です。 今回は、「Knative on minikubeでServing & Eventing Helloworldを動かすまで(構築編)」を記載します。 [対象読者] Knative Eventing環境を自分で作ってみたい人 Knative EventingでサンプルコードHelloworldを動かしたい人 Knative Eventingの構成を理解したい人 [記事を読んでわかること] knative Cli [kn] を使ってminikube上でKnative Eventingを構築 [構築環境情報] CPU 4 Memory 4GB OS Alma Linux 8.5 minikube version 1.23.4 minikube Driver docker Knative v1.4.0 目次 目次 1. docker-ceのセットアップ docker-ceリポジトリのセットアップ docker-ceのインストールと起動 docker-ceの起動を確認 2. minikube CLIのセットアップ minikube CLIコマンドのセットアップ minikubeの動作確認 minikube用アカウントを用意する k8sユーザに変更し、minikubeクラスタを構築する 3. Knativeのセットアップ kubectlコマンドの設置 knative Cli(kn) + Knative quickstart pluginによる構築 knativeのバージョン指定 kn(Knative cli)の設置 Knative Quickstart Pluginの設置 Knative(Serving + Eventing)のセットアップ Knative環境の動作状況を確認する 1. docker-ceのセットアップ minikubeを動作させる為にはいくつかのDriverをあらかじめセットアップしておく必要があります。 参考: Drivers | minikube から今回はdocker-ceを選択します。 Docker-CEのセットアップは以下の情報を元にセットアップを行います。 Docker CE の入手(CentOS 向け) — Docker-docs-ja 20.10 ドキュメント docker-ce リポジトリ のセットアップ # sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2 # sudo yum-config-manager \ --add-repo \ https://download.docker.com/linux/centos/docker-ce.repo docker-ceのインストールと起動 # sudo yum install -y docker-ce # sudo systemctl enable docker # sudo systemctl start docker docker-ceの起動を確認 # sudo systemctl status docker --- ● docker.service - Docker Application Container Engine Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled) Active: active (running) since Wed 2022-05-25 23:56:00 EDT; 2s ago Docs: https://docs.docker.com Main PID: 26493 (dockerd) Tasks: 9 Memory: 35.2M CGroup: /system.slice/docker.service └─26493 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --- 2. minikube CLI のセットアップ 参考: minikube start | minikube 今回は RPM を使ってセットアップします。 ※ご自身の環境に合わせて選択肢を変更してください。 minikube CLI コマンドのセットアップ # sudo curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-latest.x86_64.rpm # sudo rpm -Uvh minikube-latest.x86_64.rpm minikubeの動作確認 以下の様にroot権限のアカウントではminikubeは起動できませんので専用アカウントを用意します。 確認の為にminikube Driverにdockerを指定してminikube clusterを構築します。 # sudo minikube start --driver=docexitker --- * Rocky 8.5 (amd64) 上の minikube v1.25.2 * ユーザーの設定に基づいて docker ドライバーを使用します * 「docker」ドライバーは root 権限で使用すべきではありません。 * If you are running minikube within a VM, consider using --driver=none: * https://minikube.sigs.k8s.io/docs/reference/drivers/none/ X DRV_AS_ROOT が原因で終了します: 「docker」ドライバーは root 権限で使用すべきではありません。 --- minikube用アカウントを用意する minikube構築用に k8s グループとユーザを作成し、dockerグループに付属させます。 # groupadd k8s # useradd k8s -g k8s -s /bin/bash -d /home/k8s # sudo usermod -aG docker k8s && newgrp docker dockerグループに付属していることを確認 # egrep docker /etc/group --- docker:x:987:k8s --- k8s ユーザに変更し、minikube クラスタ を構築する # su - k8s -c 'minikube start --driver=docker' --- * Rocky 8.5 (amd64) 上の minikube v1.25.2 * 既存のプロファイルを元に、docker ドライバーを使用します * minikube クラスター中のコントロールプレーンの minikube ノードを起動しています * ベースイメージを取得しています... * docker 「 minikube 」 container がありません。再生成します。 * docker container (CPUs=2, Memory=2200MB) を作成しています... * Docker 20.10.12 で Kubernetes v1.23.3 を準備しています... - kubelet.housekeeping-interval=5m - 証明書と鍵を作成しています... - コントロールプレーンを起動しています... - RBAC のルールを設定中です... * Kubernetes コンポーネントを検証しています... - gcr.io/k8s-minikube/storage-provisioner:v5 イメージを使用しています * 有効なアドオン: storage-provisioner, default-storageclass * kubectl が見つかりません。kubectl が必要な場合、'minikube kubectl -- get pods -A' を試してください * 完了しました! kubectl が「"minikube"」クラスタと「"default"」ネームスペースを使用するよう構成されました --- minikube clusterの作成が可能であることが確認できましたので、作成したminikube clusterを削除しておきます # su - k8s -c 'minikube delete' --- * docker の「minikube」を削除しています... * コンテナー「minikube」を削除しています... * /home/k8s/.minikube/machines/minikube を削除しています... * クラスター「minikube」の全てのトレースを削除しました。 --- 3. Knativeのセットアップ ここまでこればKnative環境を構築するのは簡単です。 Knative環境の構築方法は以下2種類あります。 knative Cli (kn) + Knative quickstart plugin こちらはKnative ServingとKnative Eventingの両方をセットアップしてくれます。 動作が保証された状態でセットアップできることから、環境構築に手間をかけたくない。とりあえずKnative触ってみたいといった人に向いています。 kubectl + YAML こちらはKnativeのInstallページに沿ってKnativeのBrokerやTriggerなど各 コンポーネント をセットアップしていきます。 Knative Serving環境だけ作りたい人や、BrokerやTrigger等の コンポーネント において複数種のソリューションの選択が可能ですので、上記基本セットアップ以外の環境で動かしたい人に向いています。 kubectlコマンドの設置 どちらの構築方法を選択してもkubectlコマンドの設置が必要となりますので、実施します。 # sudo curl -L -o /usr/local/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" # sudo chmod +x /usr/local/bin/kubectl knative Cli (kn) + Knative quickstart pluginによる構築 公式サイトに書かれている方法構築していきます。 Install Knative using quickstart - Knative knativeのバージョン指定 Knativeのバージョンを指定します。 KNATIVE_VERSION='1.4.0' kn(Knative cli )の設置 上記指定したバージョンにてknコマンドを設置します。 # sudo curl -LO https://github.com/knative/client/releases/download/knative-v${KNATIVE_VERSION}/kn-linux-amd64 # sudo mv kn-linux-amd64 /usr/local/bin/kn # sudo chmod +x /usr/local/bin/kn Knative Quickstart Pluginの設置 次にquickstart plugin用コマンドを設置します。 # sudo curl -LO https://github.com/knative-sandbox/kn-plugin-quickstart/releases/download/knative-v${KNATIVE_VERSION}/kn-quickstart-linux-amd64 # sudo mv kn-quickstart-linux-amd64 /usr/local/bin/kn-quickstart # sudo chmod +x /usr/local/bin/kn-quickstart Knative(Serving + Eventing)のセットアップ 一旦 k8s ユーザに切り替えます。 # su - k8s いよいよKnativeのセットアップを行います。 下記ログより minikube clusterの構築 kubectlコマンドの参照先をknative クラスタ を参照させる Knative Servingのセットアップ Kourier Netowrk Layerのセットアップ Knative Eventingのセットアップ をそれぞれ実施しています。 k8s$ kn quickstart minikube --- Running Knative Quickstart using Minikube Minikube version is: v1.25.2 ☸ Creating Minikube cluster... Using the standard minikube driver for your system If you wish to use a different driver, please configure minikube using minikube config set driver <your-driver> * Rocky 8.5 (amd64) 上の [knative] minikube v1.25.2 ! Specified Kubernetes version 1.23.4 is newer than the newest supported version: v1.23.4-rc.0 * docker ドライバーが自動的に選択されました ! あなたの cgroup ではメモリーの設定ができません。 - 追加情報: https://docs.docker.com/engine/install/linux-postinstall/#your-kernel-does-not-support-cgroup-swap-limit-capabilities X 要求された 3072MiB のメモリー割当は、システムのオーバーヘッド (合計システムメモリー: 3684MiB) に十分な空きを残しません。安定性の問題に直面するかも知れません。 * 提案: Start minikube with less memory allocated: 'minikube start --memory=3072mb' * knative クラスター中のコントロールプレーンの knative ノードを起動しています * ベースイメージを取得しています... * Kubernetes v1.23.4 のダウンロードの準備をしています > preloaded-images-k8s-v17-v1...: 505.67 MiB / 505.67 MiB 100.00% 35.55 Mi * docker container (CPUs=3, Memory=3072MB) を作成しています... * Docker 20.10.12 で Kubernetes v1.23.4 を準備しています... - kubelet.housekeeping-interval=5m - 証明書と鍵を作成しています... - コントロールプレーンを起動しています... - RBAC のルールを設定中です... * Kubernetes コンポーネントを検証しています... - gcr.io/k8s-minikube/storage-provisioner:v5 イメージを使用しています * 有効なアドオン: default-storageclass, storage-provisioner * 完了しました! kubectl が「"knative"」クラスタと「"default"」ネームスペースを使用するよう構成されました 🍿 Installing Knative Serving v1.4.0 ... CRDs installed... Core installed... Finished installing Knative Serving 🕸️ Installing Kourier networking layer v1.4.0 ... Kourier installed... Ingress patched... Finished installing Kourier Networking layer 🕸 Configuring Kourier for Minikube... Domain DNS set up... Minikube tunnel... Finished configuring Kourier 🔥 Installing Knative Eventing v1.4.0 ... CRDs installed... Core installed... In-memory channel installed... Mt-channel broker installed... Example broker installed... Finished installing Knative Eventing 🚀 Knative install took: 3m25s 🎉 Now have some fun with Serverless and Event Driven Apps! --- Knative環境の動作状況を確認する まずは、namespace情報を確認します knative-eventing, knative-serving, kourier-systemができていることが確認できます。 ※kube-node-lease, kube-public, kube-systemはminikubeのnamespaceです。 k8s$ kubectl get namespaces NAME STATUS AGE default Active 9m26s knative-eventing Active 7m27s knative-serving Active 8m26s kourier-system Active 8m1s kube-node-lease Active 9m28s kube-public Active 9m28s kube-system Active 9m28s 次に各deploymentの状況を確認します。全てREADY 1/1となっているか確認します。 ※pingsourceは0/0で問題ありません。 knative-eventingの コンポーネント です。 ChannelはIn Memory Channel(imc-*)が選択されています。 BrokerはMT-Channel-based(mt-broker-*)が選択されています。 $ kubectl get deployment --namespace knative-eventing --- NAME READY UP-TO-DATE AVAILABLE AGE eventing-controller 1/1 1 1 13m eventing-webhook 1/1 1 1 13m imc-controller 1/1 1 1 12m imc-dispatcher 1/1 1 1 12m mt-broker-controller 1/1 1 1 12m mt-broker-filter 1/1 1 1 12m mt-broker-ingress 1/1 1 1 12m pingsource-mt-adapter 0/0 0 0 13m --- knative-servingの コンポーネント です。 networkにkourier network layerが選択されています。 $ kubectl get deployment --namespace knative-serving --- NAME READY UP-TO-DATE AVAILABLE AGE activator 1/1 1 1 16m autoscaler 1/1 1 1 16m controller 1/1 1 1 16m domain-mapping 1/1 1 1 16m domainmapping-webhook 1/1 1 1 16m net-kourier-controller 1/1 1 1 16m webhook 1/1 1 1 16m --- $ kubectl get deployment --namespace kourier-system --- NAME READY UP-TO-DATE AVAILABLE AGE 3scale-kourier-gateway 1/1 1 1 16m --- 環境構築は以上で終了です。 続はいよいよ 構築したKnative on minikubeでServing & Eventing Helloworldを動かすまで(実行編) にて Knative ServingのHelloworldの動作確認 Knative Eventing Helloworldの動作確認 Knative Serving + Eventingを組み合わせたHelloworld を行います。お楽しみに!! エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com
アバター
はじめに こんにちは、 id:FM_Harmony です。 ラク スの一部サービスでは iOS 向けのアプリを提供していますが、 今回はそれに関連して、 iOS アプリ開発 のCI/CDについてまとめてみました。 これから iOS アプリ開発 のプロジェクトを始められる方、 既にあるプロジェクトにCI/CDを導入する方の参考になれば幸いです。 なお、「CI/CDとは?」といった部分に関しては、過去の記事に詳しいものがあるため、 そちらをご一読いただいてからこの記事を読まれますと、より理解が深まるかと思います。 tech-blog.rakus.co.jp 目次 はじめに 目次 Bitriseとは fastlaneとは 実例紹介 CI / CD環境について CIの取り組み CI戦略 具体例 CDの取り組み CD戦略 具体例 おわりに Bitriseとは 公式サイトの説明 によればBitriseとは、 Bitriseは、モバイルアプリ開発(iOS、Android、React Native、Flutterなど)に主に焦点を当てたサービスとしての継続的インテグレーションおよびデリバリー(CI / CD)プラットフォーム(PaaS)です。 これは、ソフトウェアプロジェクトの開発と自動化を支援するツールとサービスのコレクションです。 とのことです。 つまり、 スマートフォン 向けアプリでCI/CDを行うための環境を提供してくれるサービス という事です。 他にもCI/CD環境を提供するサービスはありますが、コスト面やワークフロー *1 の作り易さから、 Bitriseの採用に至っています。 ラク スにおけるBitriseの利用についても、過去の記事がありますので、 よろしければ併せてご一読ください。 tech-blog.rakus.co.jp fastlaneとは こちらも 公式サイトの説明 によれば、fastlaneとは、 fastlane is the easiest way to automate beta deployments and releases for your iOS and Android apps. It handles all tedious tasks, like generating screenshots, dealing with code signing, and releasing your application. とのことです。 つまり、 スマートフォン 向けアプリのリリースに関するタスクを自動化するためのツール という事です。 リリースだけでなく、自動テストやレポーティングといった機能も備えています。 BitriseだけでもCI/CDは行えるのですが、Bitriseがトラブルで利用できないときや、 コミット前にローカルでテストを動かしたいときを考え、 ビルドツールとしてfastlaneを使う ビルド環境としてBitriseを使う というように使い分けをしています。 実例紹介 CI / CD環境について 以下は、ある スマートフォン 向け アプリ開発 におけるCI / CDの環境構成の例になります。 図1:CI/CD環境 プロジェクトは社内のGitLabで管理しており、開発者がコードのプッシュ等を行うと、 Bitriseでビルド用の 仮想マシン が作成されます。 その 仮想マシン にプロジェクトの ソースコード をクローンして、 fastlaneでのテストやビルドを行うといった構成です。 ビルドの終了時にはGitLabのPipelineに成功/失敗が通知されますが、 同時に社内のチャットツール(mattermost)にも通知されるようにしています。 これにより、開発者がGitLabを確認せずともCIの完了を把握できるようにしています。 図2:チャットツールへの通知例 では、具体的にCI / CDとしてどういったことを行っているか紹介します。 CIの取り組み CI戦略 CIとしては、例えばfastlaneで以下を実行しています。 SwiftLint による静的解析 XCTest による ユニットテスト ユニットテスト については、 Xcov でその カバレッジ を集計するようにしています。 基本的にはGitLabのマージリク エス ト(MR)が作成、MRへコミットがプッシュされたタイミングで、 上記を行うようにしています。 というのも、2022年5月現在で利用しているBitriseのプランは、 同時に1ワークフローのみ実行可能なものであるためです。 Bitriseを利用しているプロジェクトはいくつか存在し、プッシュされるたびにワークフローを実行すると、 他プロジェクトのワークフローが待ち状態になることがあります。 それを避けるため、ワークフローの実行は必要最小限にしています。 ただし、masterブランチやリリース用のブランチは デグレード が発生した場合に速やかに修正する必要があるため、 コミットのプッシュごとにCIを実行しています。 図3:GitのブランチとCI戦略の関係 具体例 では、CIの実行例を見てみましょう。 CIが行われたとき、fastlaneにより実行結果のレポートが作成されます。 これにより、CIが失敗したときにその失敗理由を確認することが出来ます。 また、CIが失敗した際は Danger により、失敗した箇所がMRにコメントされます。 fastlaneにより作成されたレポートはBitriseに保存されるのですが、Dangerも利用することで、 原因が簡単に分かるものは、MRのコメントを基にコードを修正する 詳細な原因を知りたいものは、Bitriseに保存されたレポートを基にコードを修正する というようにでき、簡単なものであればBitriseとGitLabを行ったり来たりしなくても済むようにしています。 図4:Dangerによる静的解析のコメント例 さらに、 ユニットテスト の カバレッジ もDangerによるコメントを行っています。 これにより、 ここはカバーできているのでOK ここはこういった理由でカバーしなくてもOK といった情報を、MRのコメントでレビュワーに伝えています。 図5:Dangerによる カバレッジ のコメント例 CDの取り組み CD戦略 CDとしては、例えばfastlaneで以下を行っています。 アプリのバージョン番号変更 Test Flightへのアプリアップロード ただし、以下の理由により必要なタイミングでのみ実行出来た方が都合がよいため、 CIと違って自動実行ではなく、手動でBitriseのワークフローを実行しています。 バージョン番号変更はファイルのコミットを伴う アップロードに伴って変更されたビルド番号も含めた状態で、プロジェクトにタグを打つ運用になっている また、事前にリリース時期の社内調整が必要なことがほとんどであるため、 アップロードしたアプリの公開も手動で行っています。 具体例 こちらも具体例を見てみましょう。 あるプロジェクトではそのバージョンの開発が完了すると、 成果物がコミットされたmasterブランチをreleaseブランチへマージし、 バージョン番号を変更するという運用を行っています。 バージョン番号変更においては、 fastlane-plugin-versioning を利用しています。 以前はInfo.plistに設定された値を修正してバージョン番号を更新していたのですが、 Xcode 11よりBuild Settingに設定を行うようになりました。 fastlane-plugin-versioningはバージョン番号を渡すと、その辺りを考慮して適切に設定値を更新してくれるため、 バージョン番号変更に利用しています。 その後、プロジェクト全体の 結合テスト 等を経てリリース準備が完了すると、 releaseブランチでビルドしたアプリを署名してTest Flightへアップロードし、審査に提出しています。 このアプリビルドやアップロードについては、fastlaneで用意された機能を利用しています。 具体的には、 ビルド: gym 署名: match アップロード: Pilot を利用するといった構成です。 また、アップロードに App Store Connect API を利用することで、 2段階認証を行うことなくBitriseからのアプリアップロードを行えるようにしています。 おわりに いかがでしたでしょうか。 個人的な感想になりますが、CI/CD環境の整備により、 実装コードに対する安心感が向上する アプリビルドやアップロードの間もローカルマシンで別のことを行える といった点で、それまでと比べて開発環境が改善されたと感じています。 今回の記事が iOS アプリでCI/CDを行う際の一案として、みなさまの参考になれば幸いです。 エンジニア 中途採用 サイト ラク スでは、エンジニア・デザイナーの 中途採用 を積極的に行っております! ご興味ありましたら是非ご確認をお願いします。 https://career-recruit.rakus.co.jp/career_engineer/ カジュアル面談お申込みフォーム どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。 以下フォームよりお申込みください。 rakus.hubspotpagebuilder.com ラク スDevelopers登録フォーム https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/ イベント情報 会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください! ◆TECH PLAY techplay.jp ◆connpass rakus.connpass.com *1 : Jenkinsのジョブのようなもの
アバター