Briswell Tech Blog

ブリスウェルのテックブログです

ブリスウェルで一緒に働きませんか? (採用関連情報)

image created with ImageFX

みなさん、こんにちは! Briswellの加藤です。

今日は、私たちの会社がどんなことをしているのか、そしてなぜそれが面白いのかについて、ちょっとお話ししたいと思います。

コンサルタントって何?エンジニアって何?いや、両方なんです!

コンサルタント」や「エンジニア」って聞くと、なんだか全く別の職業のようでですよね。

実は、採用プロセスでお会いする多くの候補者の方々もそう思っているようです。 「エンジニア”ではなく”、より上流のコンサルタントになりたいんです。」と。

でも、Briswellでは、この2つが実はすごく密接に関係しているということなんです。 私たちは、クライアント企業の経営課題を解決するために、ITの力を使います。 つまり、ビジネスの問題を理解し、それをテクノロジーで解決するんです。 カッコよく言えば、経営とITの架け橋になる、というわけです。 ビジネスの課題解決のために、ITを使うわけで、コンサルタントとエンジニアは地続きなのです。

image created with ImageFX

事業って、業務プロセスだけでなくデータも重要なんです。

ビッグデータ」とか「データ経営」って言葉、よく聞きますよね。 でも、実際にそれを使いこなせている企業って、実はそんなに多くないんです。 私たちは、企業が行っている事業を、業務プロセス(業務の手順)の観点だけでなく、事業から得られるデータの観点から再構築するお手伝いをしています。 持っているデータを分析してそこから新しいビジネスのヒントを見つけ出す、あるいは得られていないデータを取得するために取得方法を業務に埋め込むというお手伝いをしています。 例えば、お客様の行動パターンを分析して、新しい商品のアイデアに活かしたり、無駄な在庫を減らしたりするんです。 データを味方につければ、ビジネスはもっともっと面白くなる。そう信じて、日々奮闘しています。

セキュリティも忘れちゃいけません

デジタル化が進むと同時に、サイバー攻撃のリスクも高まっています。 でも、大丈夫。私たちはセキュリティのプロフェッショナルでもあるんです。 クライアントの大切な情報を守りながら、ビジネスを加速させる。 そんなバランスの取れたシステムづくりも、私たちの大切な仕事の一つです。

スピードを大切に

ビジネスの世界は本当に速く動いています。 そのスピードについていくために、私たちはいつも最新の開発手法を取り入れるように心がけています。 試行錯誤を繰り返しながら、どんどん良いものを作っていく。 そんなスピード感のある仕事ができるのも、Briswellの魅力の一つですね。

image created with ImageFX

さあ、一緒に未来を創ろう!

Briswellは、ITコンサルティング企業でもありますが、もっと広くエンジニアリング企業でありたいと思っています。 でも何より大切なのは、クライアントが成長していくために一緒に走るビジネスパートナーだということ。

私たちは、最新のテクノロジーと経営のノウハウを組み合わせて、クライアントの成功を全力でサポートしています。 そして、その過程で私たち自身も成長し続けているんです。

ブリスウェルを検討して頂いている皆さんには、こんな刺激的な環境で、自分の可能性を思う存分に試してほしいと思っています。 経営とITの両方を学べる環境は、そうそうありません。 ここでの経験は、きっと皆さんの人生の大きな財産になるはずです。 ビジネスの未来を一緒に創っていく。 そんなワクワクする挑戦に、あなたも参加してみませんか? 私たちと一緒に、新しいビジネスの世界を切り拓いていきましょう!

www.briswell.com

ImageFX Japanese young men and women who are willing to work in an IT company called Briswell. Smiling or laughing. Dressed in neat business casual, like with a jacket and a T-shirt, but not in a suit. With a mac or an iPad. Bright background.

採用応募者様向けの会社説明

https://www.briswell.com/wp-content/themes/wp01/img/cmn/recruit_b_01.jpg

1 ミッション

企業理念 | 株式会社ブリスウェル

わたしたち株式会社ブリスウェルは、「テクノロジーで新しい価値を創造し、より良い社会の実現に貢献する」ことをミッションに掲げています。

ここで言うテクノロジーと言うのは文字通り技術ですけれど、我々にとって技術は当然情報技術(IT)ですし、その中でもソフトウェア、更には業務システムと呼べるような領域に特化しています。 私たちは情報技術(IT)を活用し、企業の業績向上および企業価値の拡大に資するような仕事をしています。

2 会社概要

会社概要 | 株式会社ブリスウェル

  • 会社名: 株式会社ブリスウェル
  • 所在地: 東京都港区芝大門浜松町駅・大門駅が最寄り駅)
  • 設立: 2008年
  • 経営陣: 代表取締役 山口、取締役 加藤(共に創業時より)

3 事業内容

事業 | 株式会社ブリスウェル

当社はソフトウェア開発を核とし、クラウドサービス、AI技術の利用、ITエンジニア人材紹介など、技術関連の複数事業を展開しています。

主力は業務システムの開発ですが、関連するAWSMicrosoft Azureの導入支援、生成AI導入などが事業として大きくなって来ています。 ソフトウェア開発に関連する事業のみにフォーカスしていると言えます。

4 開発拠点

会社概要 | 株式会社ブリスウェル

ブリスウェルベトナム: ベトナムホーチミンシティに開発拠点があります。 日本の本社と協力してプロジェクトを進行しています。

5 経営メンバー

会社概要 | 株式会社ブリスウェル

代表の山口と取締役の加藤は、ITコンサルティング企業であるアクセンチュアで働いていました。

エンジニア出身ではないですが、今まで数多くの企業向けのITプロジェクトに事業面・技術面ともに関わってきました。

事業部や技術部のリーダー層は専門学校・大学・大学院でのITを初めとした工学のバックグラウンドを持つエンジニアが増えて来ており、より技術面にフォーカスした企業になってきているといえます。

6 プロジェクト実績

実績 | 株式会社ブリスウェル

創業当初は、情報通信やシステム開発、あるいはITコンサルティング業界といったようなITに親和性の高い企業のプロジェクトが多かったですし、その後はスタートアップ・ベンチャーの支援が増えた時期もありました。

現在では日本の基幹産業と呼べる業界の顧客が増えてきております。

業界としては、金融、不動産、製造業、建設、小売・卸売といった、ITによる業績へのインパクト・伸び代が大きく見込める企業に対してサービスが多くなっています。

業務領域としては、以前は企業の基幹業務、すなわち販売、生産、調達、会計周りのようなコア業務と言える領域を得意としていました。 最近ではフロント業務はECや営業支援へ、バック業務では物流や在庫などのプロジェクトが多くなっています。

またデータ分析基盤、データ連携機能などの業務フローをカバーしない、機能ベースでのプロジェクト増えてきています。

7 採用情報

採用情報 | 株式会社ブリスウェル

大きく分けてエンジニアすなわち技術領域の担当者と、コンサルタントすなわちビジネス領域の担当者とに大別しています。

エンジニアがプロダクトマネージャーあるいはテクニカルリードになっていく職種であるのに対して、コンサルタントはプロジェクトマネージャーになっていく職種と言えるかと思います。 どちらも、マインドセットとしてテクノロジーを活用してビジネス全体を変えていくと言うような意識を重視していたり、スキルセットとしてその実現に必要な確かな技術力・技術的知見を求めています。

エンジニア:

技術力を活かし、ビジネス全体を変革する意欲を持った人材を求めています。

最近の使用技術はNode.js/TypeScript、Python等。 クラウドインフラはAWSが多かったのですが、ChatGPT(AOAI)との親和性によりMicrosoft Azureの利用も拡大中です。

コンサルタント:

技術知識とともに、クライアント企業に対するソフトウェアソリューションの提供をマネージする役割を担います。

広い視点ではプロジェクト全体に対する深い理解と責任感が求められると言えますが、細かい役割という点では、クライアント向け・ベトナムの開発拠点向けの設計書・仕様書のドキュメンテーションというタスクまであります。

8 求める人物像

求める人物像 | 株式会社ブリスウェル

ブリスウェルのカルチャーとして、

  • リーダーシップ
  • チームワーク
  • スピード
  • 完璧主義
  • ダイナミズム

という観点を重視しています。

また、上記以外では、

  • 技術への深い理解
  • 新しい技術やトレンドに対する適応力
  • プロジェクトの成功へのコミットメント
  • 継続的な学習意欲

などを重視します。

9 社内の活躍するメンバー

インタビュー | 株式会社ブリスウェル

社内で活躍しているメンバーは、専門学校・大学や前職などでITのバックグランドを持っている方が多く、技術力を手放すことなくビジネス面での知見を積んでいると言えます。 一方で、未経験から入社しても、社内のナレッジや最新の技術を急速に吸収して活躍している社員が多いのもブリスウェルの強みです。

www.briswell.com

www.briswell.com

www.briswell.com

www.briswell.com

www.briswell.com

ブリスウェルは、1つの技術をずっと使い回すよりは、どちらかと言うと新しいトレンドを取り込んでいく傾向にあり、継続的な学習習慣がある人がカルチャーフィットしやすいです。 もう少し柔らかい言葉で言うと、新しい物好きで自分からどんどん勉強していくようなタイプが活躍しやすいと言ってもいいかもしれません。

ブリスウェルでは一緒に働く仲間を募集しています! www.briswell.com

Androidのアプリをテストする為に[Galaxy A53]のSimulatorを設定してみる。

みな様こんにちは。ブリスウェルのSonです。


最近、Android アプリのインターフェースをテストするために、Galaxy A53のエミュレーターを使いました。しかし、Android Studio には Pixel (Google) の仮想デバイスしかなく、Galaxy (Samsung) の仮想デバイスはありません。

この記事では、Samsung の仮想デバイスを設定する方法を説明します。


使用ツール
Android Studio

この記事では、MacbookAndroid Studioを使用して新しいシミュレーターを作成します。

WindowsAndroid StudioのUIなど違う可能性があるので、注意してください。


① [Galaxy A53]のスキンをダウンロードして保存する。

[Samsung developer]のホームページへ遷移するために下記のリンクをクリックする。

developer.samsung.com

画面上でGalaxy A53のスキンを検索し、ダウンロードします。自分のPCで保存してください。

PCにダウンロード後、解凍してください。


② [Galaxy A53]のエミュレーターを設定する

エミュレーター管理画面を開く。

[Galaxy A53]のエミュレーターを追加する為に、[+]のボタンをクリックする

解凍した[Galaxy A53]のスキンをインポートする為に、[Virtual Device Configuration]の画面でこのボタンをクリックする。

[Galaxy A53]のエミュレーターの設定する為に、[Galaxy A53]のスペックを確認します。

[Hardwave Profile Configuration]の画面で画面サイズなどの情報を入力する。

[Galaxy A53]のスキンをインポートする

下に添付した画像により、[Galaxy A53]のデバイスを作成できました。

[Next]のボタンをクリックして、次の画面にプラットホーム情報を[Galaxy A53]に選択する必要がある。入力したら、[Next]のボタンをクリックする。

次の画面でエミュレーターの名前を入力し、先に入力した情報をまた確認する。

[Finish]のボタンをクリックしたら、エミュレーターの作成が終わりました。

エミュレーターの起動する為に、三角形のアイコンをクリックする


これで、Samsungエミュレーターを作成する方法についての記事は終わりです。
質問や不明な点があれば、下にコメントしてください。
次の記事でお会いしましょう!

職業紹介責任者講習を受講しました

先日受講した「派遣元責任者講習」に続き、「職業紹介責任者講習」を受講しました。 労働者派遣事業者と同様に、職業紹介事業者にも責任者の選任が義務付けられており、本講習を通じて、職業紹介事業所における事業運営の適正化に資することを目的に行われるものです。

2019年4月からは講習後の理解度確認試験が必須となり、合格点に達した人のみが受講証明書の交付が可能となりました。 また、職業紹介事業においても新規許可の場合は3年、更新の場合は5年毎に本講習の受講が必須となります!

講習では、主に以下の内容がカバーされます。

1. 法律や規制

  • 職業紹介業に関する法律や規制の解説。労働者の権利や適正な取引に関する法的基準の理解が重要です。

  • 個人情報保護法や労働者派遣法など、職業紹介事業における個人情報や労働条件に関する法令の遵守方法。

2. 倫理規範とベストプラクティス

  • 職業紹介の倫理規範やベストプラクティスの理解。求職者やクライアント企業との適切な関係構築のための指針。

  • 労働市場における公正な競争環境の維持に向けた行動規範の確立。

3. 求人情報の管理と選考プロセス

  • 求人情報の正確な管理方法と掲載基準。虚偽や誤解を避け、適格な求職者の選考プロセスを確保するための手法。

  • 選考プロセスの公正さと透明性の確保。差別や偏見の排除を目指した選考方法の実践。

4. 労働者の権利と福利厚生

  • 労働者の権利や福利厚生に関する理解。労働条件の適正化と働く人々の福祉向上を目指した取り組み。

  • 労働者の安全や健康に配慮した職場環境の整備。労働者の生活やキャリアに対するサポート体制の構築。

以上が職業紹介責任者講習の主な内容です。内容に沿った事例紹介もあり非常にわかりやすい、且つ実際の業務を行う上でも参考になる有意義な講習でした。 講習の所要時間は派遣元責任者講習と大体同じ、7〜8時間程度となります。

一日中会議室を占領するため多忙な社内のメンバーには気が引けますが、職業紹介事業の適正な運営のため、そして候補者の方に信頼の置ける職業紹介事業者でいるため、また3年後も会議室を占領させてください!

派遣元責任者講習を受講しました

今日は弊社の「Tech」領域から少し離れて、労働者派遣事業者としての側面をお話しします。

かつて労働者派遣事業は「一般労働者派遣事業(許可制)」と「特定労働者派遣事業(届出制)」の2つに区別されていましたが、 この制度は2015年に廃止され、すべての労働者派遣事業が「許可制」となりました。 そして適正な雇用管理を確保する目的から、すべての労働派遣事業者には「労働者100人」に対し「1名以上の派遣元責任者」の選出が義務付けられています。

そしてこの「派遣元責任者」の要件のうちのひとつが、「3年以内に、派遣元責任者講習を受講していること」です。 今回はこの派遣元責任者講習では一体何を学ぶのか?を簡単にご紹介します。 ちなみにこの講習は全国のセミナー会場、またはオンラインで受講することができ、今回私はお昼時にお腹が鳴るのを恐れてオンライン受講を選択しました(腹時計がしっかりしていて毎日12時に鳴ります)。

この講習では、主に以下のような内容が取り扱われます。

1. 派遣労働法と労働基準法

  • 派遣労働法や労働基準法に基づく派遣労働者の権利と義務の解説。派遣元が法令を遵守し、適切な労働条件を提供するための基本的な知識の獲得。

  • 派遣契約や労働条件の明確化。派遣労働者との間で正確で公平な契約関係を築くための方法や手続きについての指導。

2. 労働者の安全と健康管理

  • 労働安全衛生法労働災害補償保険法に基づく労働者の安全と健康管理の重要性。派遣元が労働災害や健康リスクを予防し、労働環境を改善するための取り組み。

  • 労働災害の報告と対応手順。万が一の災害発生時に速やかな対応を行い、被害を最小限に抑えるための体制の構築。

3. 派遣元と派遣先との連携

  • 派遣元と派遣先との円滑なコミュニケーションの重要性。派遣元が派遣先と協力して、労働条件や業務内容の適切な調整を行う方法。

  • 派遣先での労働環境の確認と改善提案。派遣元が派遣労働者の安全と福祉を守るために、派遣先と連携して労働環境を改善する取り組み。

4. 法令遵守と倫理規範

  • 法令遵守と倫理規範の徹底。派遣元が適正な労働条件を提供し、公正な労働市場の形成に貢献するための指針。

  • 偽装請負や不当な労働条件の提供を防ぐための取り組み。派遣元が社会的責任を果たし、企業としての信頼性を高めるための方策。

以上が、「派遣元責任者講習」の一般的な内容になります。 これらの内容を理解し、実践することで、派遣元が適切な労働環境を提供し、派遣労働者の権利と福祉を守ることができます。

講習後、理解度チェックのための設問があり、解答・合格後晴れて有資格者の仲間入りです。 おめでとう!と言いたいところですが、この講習、新規許可の場合は3年、更新の場合は5年に一度受講の必要があり、一度でも受講しそびれたり、落ちたりしてしまうと責任者として認められないどころか適格事業者でない烙印を押されてしまうので注意です。 私もまた3年後このブログを読み返す日が来ることでしょう…!

AIサービス展示会!文書生成から動画検索まで #ITExpo2024

IT Expo 2024春の季節がやってきました。 ブリスウェルはここ数年は毎年春・秋と出展していまして、社内は準備の最終段階です。 今回はAIサービスを中心に展示する予定です。

展示内容その①・・・ AIサービス(文書生成、AI QA、動画検索)

今、空前の話題となっている生成AIを活用したサービスをはじめ、様々なAIサービスを展示します。

1. 報告文書の自動作成、文章生成

文書作成AI

当社のAIサービスは、システムに直接入力された報告のインプットデータと、過去の文書データを基に、AIを利用して報告文章を自動的に生成します。 これによって、従来の手作業による文書作成プロセスを大幅に改善できます。 手間と時間をかけてゼロから文書を作成する必要がなくなるだけでなく、人力では見落としがちな報告すべき内容もAIが網羅します。 これにより、全体的に品質の高い報告文書を短時間で作成することが可能となります。

2. 自動QA、過去の類似QA検索

自動QA、過去の類似QA検索

お客様からの問合せや、特定のテーマに関する反復的な質問に対して、過去の事例や類似のQAデータベースから最適な回答をAIが迅速に提供します。 これは、一貫性のある情報提供を実現し、高品質なカスタマーサービスを提供するための重要な手段です。 さらに、回答作業の労力を大幅に軽減することができます。これにより、お客様サポートスタッフはより複雑な問題解決に集中することができ、全体的な業務効率を向上させます。

3. 動画内ピンポイントシーン検索

動画検索AI
当社のAIサービスは大量の動画データから、ユーザーが指定したキーワードに基づいて目的のシーンをピンポイントで迅速に探し出すことが可能です。 これは、今までになかった新しい画像検索方法です。 従来の検索方法では、ユーザーは時間と労力をかけて動画を一つ一つ見ていく必要がありました。 しかし、このサービスにより、ユーザーはキーワードを入力するだけで瞬時に関連シーンを見つけることができます。 これにより、ユーザーは時間と労力を節約し、より効率的に情報を取得できます。

展示内容その②・・・ BW AI Gauge - ブリスウェル・AI・ゲージ 〜 アナログメーター読み取り

BW AI Gauge - ブリスウェル・AI・ゲージ

アナログメーター読み取りに革新をもたらすAI技術を搭載したソリューションです。 これにより、目視によるメーター検針作業の課題が解決され、メーター値の自動記録により誤記、記載漏れ、不正を防ぐことが可能となります。

この技術の一番の魅力は、既存のメーター設備をそのまま利用しながら、付属のカメラを取り付けるだけで検針のデジタル化を容易に実現できることです。 これにより、大規模な設備投資や長期的なメンテナンスを必要とせずに、現行のシステムをアップグレードできます。

さらに、WiFiカメラを使ってメーターを読み取ることができます。これにより、検針作業を迅速かつ正確に行うことが可能となり、人間が行う作業に比べて読み取りの誤差を大幅に減らすことができます。

また、このシステムは、複数地点のデータを一箇所で簡単に管理・監視できる機能も提供します。 これは特に、広範囲に分散した設備を持つ企業や施設にとって大きな利点となります。 様々な場所に設置されたメーターの値を一元的に把握できるため、データの分析や管理が大幅に効率化します。

ブリスウェル・AI・ゲージは、業務の効率化だけでなく、データの正確性向上にも寄与します。 これにより、企業はより迅速かつ正確な意思決定を行うことが可能となり、ビジネスの成長を加速させることができます。

展示内容その③・・・ AI画像解析

当社のAI画像解析は、お客様の活用シーンに応じたカスタマイズと、用途に応じたアプリケーションへの組み込みを実現します。 これにより、最小限のデータを使用して効率的に高品質なモデルを構築することが可能となります。 これは、企画段階から導入、そして運用後のフォローまで、お客様を全面的にサポートするという当社のコミットメントの一環です。

危険エリア侵入検知

危険エリア侵入検知

最新の物体検出技術を活用し、リアルタイムで特定エリアを監視して不正侵入を即座に検出します。 不正侵入があれば、当システムは管理者に速やかに通知が可能で、化学工場や建設現場でも確実な安全対策を提供します。

顔検出機能

顔検出機能

最先端のディープラーニング技術を用いて、顔認証や表情認識など、多岐にわたるソリューションをご提案します。 高精度な検出能力とリアルタイムの処理能力により、マーケティングからエンターテイメント分野まで幅広く応用可能です。

外観検査・異常検出

外観検査・異常検出

AI技術を使用し、画像中の異常を高精度で検出します。外部通知機能や監視UIの提供、教師あり・なし学習の両方をサポートします。 お客様のニーズに合わせて最適な手法を選択し、さらに柔軟にカスタマイズも可能です。 これらの機能により、お客様のビジネスの効率化と品質向上を実現します。

展示内容その④・・・ AWS導入コンサルティング

AWSテクノロジーパートナーとして、クラウド上でのシステム構築実績が多数あるため、様々なニーズに対応したAWS導入支援が可能です。

詳細はこちら。 www.briswell.com

展示内容その⑤・・・ 「アイカタ」中小企業・成長企業向け 受発注工程管理システム

「アイカタ」SMB・成長企業向け 受発注工程管理システム

「アイカタ」は、中小企業や成長を志向する企業向けの受発注工程管理システムです。 このシステムはパッケージ形式のシステムであり、低コストで導入が可能なのが最大の特長です。 また、シンプルな操作性を持ちつつも拡張性の高いクラウドサービスとして提供されており、このため様々な外部システムとのデータ連携が容易になっています。

ERP(基幹システム)

  • 生産管理
  • 在庫管理
  • 販売管理
  • 税制対応
  • API連携機能
  • カスタマイズ対応

受発注システム

  • 在庫管理
  • 顧客情報管理
  • 請求書処理
  • 支払い処理
  • 受注処理
  • リアルタイムな情報更新
  • カスタマイズ対応
  • 分析・レポート
  • 顧客サポートと問合せ管理
  • モバイルデバイス対応

詳細はこちら。 ai-cata.com

展示会概要

■ 展示会名 JAPAN IT WEEK 春 【AI・業務自動化展】

■ 開催日時 2024.4.24(水)〜26(金) 10:00~18:00 ※最終日のみ17:00終了

■ 会場 東京ビッグサイト 東ホール Google Map: maps.app.goo.gl

東京ビッグサイト公式サイトはこちら www.bigsight.jp

■ ブース番号 17-14

■ 入場方法 来場登録をお願い致します。 https://www.japan-it.jp/spring/ja-jp/register.html?code=0996212775874600-3GV

■ 相談予約 ご相談の予約も可能です。 以下からご予約下さい。 https://bit.ly/4aoc7id

AWS LightsailでWordpressのサーバーを作成してみる

みな様こんにちは。ブリスウェルのSonです。

最近、WordPressを使って案件を開発しました。サーバーへソースコードを速く反映できるサービスがないか調べてみました。AWSのLightsailというサービスを見つけました。

今回はAWSのLightsailを触ってみましたので、わかったことなど書いていきます。

I. AWS Lightsailとは

① 概要

Amazon Lightsail は、数クリックでウェブアプリケーションやウェブサイトを立ち上げられる、使いやすいクラウドリソースを提供します。

Lightsail は、インスタンス、コンテナ、データベース、ストレージなどの簡素化されたサービスを提供します。Lightsail では、WordPress、Joomla、LAMP などの事前に設定されたブループリントを使用して、ウェブサイトやアプリケーションを簡単に立ち上げることができます。

参照先:https://aws.amazon.com/jp/free/compute/lightsail/?nc1=h_ls

② メリット

  • インストール・導入が簡単
  • 料金がシンプルで計画が立てやすい

③ デメリット

  • 大規模な構成には向いていない
    リソース要件が高く大規模なアプリケーションには適していません。
    これは、CPUやRAMの制限があり、EC2などの他のAWSサービスと比較して柔軟性に欠けるためです。
  • セキュリティグループやIAMなどのような複雑なセキュリティを設定できません

II. Wordpressのサイトを作成してみる

① サイトの初期化

下記のリンクでLightsailコンソールにアクセスする。
https://lightsail.aws.amazon.com/ls/webapp/home/instances

サイトを作成する画面に移動するために、[Create instance]のボタンを押下する。

次の画面でWordpress型やサーバー名などを設定して、[Create instance]のボタンを押下する。

下の画像により、[Wordpress-test]のサーバーの作成が完了しました。

② サイトの管理画面にアクセス

サイトの稼働状況を確認する

サイトを作成したら、[Instances]一覧の画面で[Wordpress-test]サイトを押下する。次の画面の[Connect]タブに[Public IPv4 address]の項目がある。このIPは、[Wordpress-test]サイトのIPです。このIPにアクセスしてみます。

下の画像により、ウェブサイトは正常に動作しています。




サイトの管理エリアにアクセスする方法

管理エリアにアクセスために、ユーザー名とパスワードが必要です。
ユーザー名は[Default WordPress admin user name]の値です。

パスワードの取得するために、CloudShellでコマンドを実行する必要がある。
取得方は下の画像と通りです。

認証情報が分かりましたら、下記のリンクにアクセスする。
http://54.xx.xx.xxx/wp-login.php

ユーザーとパスワードを入力し、[Log In]のボタンを押下する。

下の画像により、ログインできました。



③ ローカル環境でサイトのDBにアクセスする方法

DB情報の取得する方法

LightsailのTerminalを起動するために、[Connect]タブで[Connect using SSH]のボタンを押下する。

[wp-config]ファイルを下記のコマンドで開く。

vi /opt/bitnami/wordpress/wp-config.php

上の画像により、DB情報の取得ができました。

ローカル環境でDBにアクセスする方法

ローカル環境からDBにアクセスするために、SSHのキーをダウンロードする必要があります。

次に、DB情報とサーバー情報とSSHのキーを入力する。入力する方法は下の画像と通りです。
入力したら、[Connect]のボタンを押下する。

ローカル環境からDBへアクセスすることが成功しました。

III. 最後に

AWS LightsailでWordPressのサイトを簡単に作成でき、コストも手頃です。
使いやすく信頼性が高い、小規模なプロジェクトに最適なサービスです。

最後まで読んでいただきありがとうございました。
疑問があれば、コメントをしてください。

巡回セールスマン問題

東京はようやく桜が咲きそうです。桜並木の散歩が楽しみです。

今回は、巡回セールスマン問題です。複数の地点を訪れる際の最短経路を見つける問題です。時間の制約があり効率化が求められる現代ではより良い解が必要とされますね。可能であれば、時間を気にせず気の向くままに好きな方向に進んでいきたいですが。

では、巡回セールスマン問題の解法をGoogle Maps APIを利用して確認してみましょう。

設計

画面上で、以下5地点を指定し、Google Maps APIを呼び出して最短経路を取得・確認できるようにします。

  • 出発地
  • 経由地1
  • 経由地2
  • 経由地3
  • 到着地

APIの構築

画面から呼び出すAPIPython(Flaskフレームワークを利用)で構築します。

1. モジュールのインポート
from flask import Flask, render_template, request, jsonify
import googlemaps
from datetime import datetime
import os
2. Google Maps APIを利用
google_maps_api_key = os.environ.get('GOOGLE_MAPS_API_KEY')
gmaps = googlemaps.Client(key=google_maps_api_key)
3. 初期表示用
@app.route('/')
def index():
    return render_template('index.html', google_maps_api_key=google_maps_api_key)
4. 経路計算API

画面からの入力(出発地、経由地、到着地)を受け取り、Google Maps Directions APIを呼び出して最適な経路を計算します。

@app.route('/compare_modes', methods=['GET'])
def compare_modes():
    # 出発地(origin)、到着地(destination)、経由地(waypoints)を取得
    origin = request.args.get('origin')
    destination = request.args.get('destination')
    waypoints = filter(None, [request.args.get(f'waypoint{i}') for i in range(1, 4)])
    optimize = request.args.get('optimize') == 'true'  # 経由地の最適化オプション
    waypoints_str = '|'.join(waypoints) if waypoints else None
    optimize_waypoints = optimize

    try:
        # Google Maps Directions APIを呼び出す
        directions_result = gmaps.directions(origin, # 出発地
                                              destination, # 到着地
                                              mode="driving",  # 移動手段:driving
                                              waypoints=waypoints_str, # 経由地
                                              optimize_waypoints=optimize_waypoints, #最適化オプション
                                              departure_time=datetime.now(),  # 出発時間:現在時刻
                                              traffic_model='best_guess')  # 交通状態を考慮した時間算出方法:正確に予測

        # 所要時間と距離を合計
        total_duration_seconds = sum(leg['duration']['value'] for leg in directions_result[0]['legs'])
        total_distance_meters = sum(leg['distance']['value'] for leg in directions_result[0]['legs'])

        # 所要時間の変換
        total_hours, remainder = divmod(total_duration_seconds, 3600)
        total_minutes = remainder // 60

        # 距離の変換
        total_distance_km = total_distance_meters / 1000

        # 所要時間と距離を整形
        duration_text = f"{total_hours}時間{total_minutes}分" if total_hours else f"{total_minutes}分"
        distance_text = f"{total_distance_km:.2f}km"

        results = {
            "route": [step["html_instructions"] for leg in directions_result[0]["legs"] for step in leg["steps"]],
            "duration": duration_text,
            "distance": distance_text
        }

        # ログファイルを初期化して出力
        with open('app.log', 'w') as f:
            f.write("")

        custom_print(duration=results['duration'], distance=results['distance'])
        
    except Exception as e:
        return jsonify({"error": str(e)}), 500

    return jsonify(results)
5. ログ表示API
@app.route('/logs')
def view_logs():
    with open('app.log', 'r') as f:
        content = f.read()

    return content

画面の構築

FlaskのHTMLテンプレートを構築します。

1. HTML
<body>
    <div id="map"></div> <!-- 地図表示用 -->
    <div class="form-row"> <!-- ユーザー入力用 -->
        <div class="form-group">
            <label for="start_location">出発地:</label>
            <input type="text" id="start_location" name="start_location" required>
        </div>
        <div class="form-group">
            <label for="waypoint1">経由地1:</label>
            <input type="text" id="waypoint1" name="waypoint1" required>
        </div>
        <div class="form-group">
            <label for="waypoint2">経由地2:</label>
            <input type="text" id="waypoint2" name="waypoint2" required>
        </div>
        <div class="form-group">
            <label for="waypoint3">経由地3:</label>
            <input type="text" id="waypoint3" name="waypoint3" required>
        </div>
        <div class="form-group">
            <label for="end_location">到着地:</label>
            <input type="text" id="end_location" name="end_location" required>
        </div>
    </div>
    <div>
        <div class="form-group">
            <label for="optimize_route">ルートを最適化する:</label>
            <input type="checkbox" id="optimize_route" name="optimize_route">
        </div>
        <div class="form-group">
            <button onclick="callEndpoint()">ルート確認</button>
            <button onclick="reloadPage()">初期化</button>
        </div>
    </div>
    <!-- ログ表示セクション -->
    <div id="log-content">
        <pre id="logs"></pre>
    </div>
</body>
2. Style
<style>
        /* 地図表示領域 */
        #map {
            width: 100%;
            height: 400px;
            margin-bottom: 20px;
        }
        /* フォームとボタンの配置 */
        #main-container {
            display: flex;
            flex-direction: row;
            justify-content: space-between;
            align-items: flex-start;
        }
        .form-row {
            display: flex;
            justify-content: space-between;
            flex-grow: 1;
        }
        .form-group {
            margin-bottom: 15px;
        }
        input[type="text"] {
            margin-top: 5px;
            width: 90%;
        }
        button {
            background-color: #4CAF50;
            color: white;
            padding: 10px 15px;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            margin-right: 10px;
        }
        button:hover {
            background-color: #45a049;
        }
        #log-content {
            font-size: 16px;
        }
</style>
3. Javascript
<script>
        var map; // Google Mapオブジェクト
        var clickCount = 0; // 地図クリック回数
        var directionsRenderer; // 経路表示を管理

        // 地図の初期設定と表示
        function initMap() {
            var mapProp = {
                center: new google.maps.LatLng(35.6895, 139.6917), // 地図の中心地点
                zoom: 10, // ズームレベル
            };
            map = new google.maps.Map(document.getElementById("map"), mapProp);
            directionsRenderer = new google.maps.DirectionsRenderer();
            directionsRenderer.setMap(map); // 経路を地図上に表示するための設定

            google.maps.event.addListener(map, 'click', function(event) {
                placeMarker(event.latLng);
            });
        }

        // クリックされた地点の座標を入力
        function placeMarker(location) {
            clickCount++;
            var latRounded = location.lat().toFixed(6); // 緯度を小数点以下6桁に丸める
            var lngRounded = location.lng().toFixed(6); // 経度を小数点以下6桁に丸める
            var locationRounded = latRounded + ',' + lngRounded; // 緯度・経度を結合

            // 最初のクリックは出発地、2〜4回目のクリックは経由地、5回目のクリックは到着地に設定
            if (clickCount === 1) {
                document.getElementById('start_location').value = locationRounded;
            } else if (clickCount >= 2 && clickCount <= 4) {
                document.getElementById('waypoint' + (clickCount - 1)).value = locationRounded;
            } else if (clickCount === 5) {
                document.getElementById('end_location').value = locationRounded;
                clickCount = 0;
            }
        }

        // 経路表示
        function displayRoute(origin, destination, waypoints, optimize) {
            directionsRenderer.setDirections({routes: []}); // 前の経路をクリア

            var directionsService = new google.maps.DirectionsService();
            var waypointArray = waypoints.filter(value => !!value).map(location => ({
                location: location,
                stopover: true
            }));

            var request = {
                origin: origin,
                destination: destination,
                waypoints: waypointArray,
                optimizeWaypoints: optimize, // 経路の最適化
                travelMode: 'DRIVING' // 移動手段はDRIVING
            };

            directionsService.route(request, function(result, status) {
                if (status == 'OK') {
                    directionsRenderer.setDirections(result);
                }
            });
        }

        // 経路計算
        function callEndpoint() {
            var startLocation = document.getElementById('start_location').value;
            var endLocation = document.getElementById('end_location').value;
            var waypoint1 = document.getElementById('waypoint1').value;
            var waypoint2 = document.getElementById('waypoint2').value;
            var waypoint3 = document.getElementById('waypoint3').value;
            var optimizeRoute = document.getElementById('optimize_route').checked;

            if (!startLocation || !endLocation || !waypoint1 || !waypoint2 || !waypoint3) {
                alert("すべて入力してください。");
                return;
            }

            // compare_modes API を呼び出す
            fetch(`/compare_modes?origin=${startLocation}&destination=${endLocation}&waypoint1=${waypoint1}&waypoint2=${waypoint2}&waypoint3=${waypoint3}&optimize=${optimizeRoute}`)
                .then(response => response.json())
                .then(data => {
                    loadLogs();
                })
                .catch(error => console.error('Error:', error));
            
            displayRoute(startLocation, endLocation, [waypoint1, waypoint2, waypoint3], optimizeRoute);
        }

        // ログ表示
        function loadLogs() {
            // logs API を呼び出す
            fetch('/logs')
            .then(response => response.text())
            .then(data => {
                document.getElementById('logs').textContent = data;
            })
            .catch(error => console.error('Error:', error));
        }

        // 初期化
        function reloadPage() {
            location.reload();
        }
</script>

デモ

  1. 地図を表示します。
  2. 地図上で「出発地」、「経由地」、「到着地」をクリックして各地点の緯度・経度を入力します。
  3. 「ルート確認」ボタンをクリックすると、指定した経由地順のルートを表示します。
  4. 「ルートを最適化する」チェックを入れると、経由地を効率的に巡る最短ルートを表示します。

最適化するときれいな一本線になりますね!

業務の効率化はとても大事です。
ただ人生は、寄り道してその場所で精一杯がんばり、次の寄り道をするのも大事かと思います。あの時の大失敗、今思い返すといい思い出ですよね。

Middyを使用することで、Lambda向けのMiddlewareの作成を簡易化する。

I. はじめに

こんにちは。
ブリスウェルのSonです。

Lambdaを使ってNode.jsアプリを開発する際、いくつかの困っていることに直面しています。

・複雑なエラー処理の実装
・認証や権限の実装
・安全な環境変数の管理
・パフォーマンス最適化

その改善策としてフレームワークを探していたところ、Middyが良さそうだと感じて、実際に使ってみた。

II. Middyとは

① 概要

AWS LambdaのためのスタイリッシュなNode.jsミドルウェアエンジンです。
Lambdaコードを整理し、重複を外し、ビジネスロジックに集中する。

② メリット

  • すぐに使用できる豊富な公式のミドルウェアとユーティリティと一緒に提供されています。
  • 最小のコアを保持することで、関数のサイズを小さくし、Cold Startをコントロールします。
  • 簡単に拡張できます。

③ 機能紹介

1. warmup

Lambdaのコールドスタートの問題を軽減するために使用されます。

使用例

const middy = require('@middy/core')
const warmup = require('@middy/warmup')

const isWarmingUp = (event) => event.isWarmingUp === true

const lambdaHandler = (event, context, cb) => {
  /* ... */
}

export const handler = middy()
  .use(warmup({ isWarmingUp }))
  .handler(lambdaHandler)

2. do-not-wait-for-empty-event-loop

タイムアウトが発生しないようにするために使用されます。例えば、データベースへの接続する時。

使用例

import middy from '@middy/core'
import doNotWaitForEmptyEventLoop from '@middy/do-not-wait-for-empty-event-loop'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(doNotWaitForEmptyEventLoop({ runOnError: true }))
  .handler(lambdaHandler)

3. http-json-body-parser

HTTPリクエストを自動的に解析し、JSON形式の本文をオブジェクトに変換する。

使用例

import middy from '@middy/core'
import httpJsonBodyParser from '@middy/http-json-body-parser'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(httpJsonBodyParser())
  .handler(lambdaHandler)

4. validator

自動的に着信イベントと送信レスポンスをカスタムスキーマに対して検証します。

注意のこと:
eventSchemaまたはresponseSchemaの少なくとも1つが必要です。

使用例

import middy from '@middy/core'
import validator from '@middy/validator'
import httpJsonBodyParser from '@middy/http-json-body-parser'
import { transpileSchema } from '@middy/validator/transpile'

const lambdaHandler = (event, context) => {
  /* ... */
}

const eventSchema = {
  type: 'object',
  required: ['body'],
  properties: {
    body: {
      type: 'object',
      required: ['name', 'email'],
      properties: {
        name: { type: 'string' },
        email: { type: 'string', format: 'email' }
      }
    }
  }
}

export const handler = middy()
  .use(httpJsonBodyParser())
  .use(
    validator({
      eventSchema: transpileSchema(eventSchema)
    })
  )
  .handler(lambdaHandler)

5. http-content-encoding

レスポンスのHTTP Content-Encodingヘッダーを設定し、レスポンス本文を圧縮します。

使用例

import middy from '@middy/core'
import httpContentNegotiation from '@middy/http-content-negotiation'
import httpContentEncoding from '@middy/http-content-encoding'
import { constants } from 'node:zlib'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(httpContentNegotiation())
  .use(httpContentEncoding({
    br: {
      params: {
        [constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT, // adjusted for UTF-8 text
        [constants.BROTLI_PARAM_QUALITY]: 7
      }
    },
    overridePreferredEncoding: ['br', 'gzip', 'deflate']
  })
  .handler(lambdaHandler)

6. http-cors

Cross-Originリクエストを実行するために必要なAccess-Control-Allow-OriginAccess-Control-Allow-Headers、および Access-Control-Allow-Credentialsを含むHTTP CORSヘッダーをレスポンスオブジェクトに設定するために使用されます。

使用例

import middy from '@middy/core'
import cors from '@middy/http-cors'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(cors())
  .handler(lambdaHandler)

7. secrets-manager

AWS Secrets Managerからパラメーターを取得して、関数ハンドラのcontextのオブジェクトにアサインされる。

注意のこと
・Lambdaは、secretsmanager:GetSecretValueのIAM権限を持っている必要があります。

使用例

import middy from '@middy/core'
import secretsManager from '@middy/secrets-manager'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(
    secretsManager({
      fetchData: {
        apiToken: 'dev/api_token'
      },
      awsClientOptions: {
        region: 'us-east-1'
      },
      setToContext: true
    })
  )
  .handler(lambdaHandler)

8. ssm

AWS Systems Manager Parameter Storeからパラメータを取得して、関数ハンドラのcontextのオブジェクトにアサインされる。

注意のこと
・Lambdaは、ssm:GetParametersのIAM権限を持っている必要があります。

使用例

import middy from '@middy/core'
import { getInternal } from '@middy/util'
import ssm from '@middy/ssm'

const lambdaHandler = (event, context) => {
  /* ... */
}

let globalDefaults = {}
export const handler = middy()
  .use(
    ssm({
      fetchData: {
        accessToken: '/dev/service_name/access_token',
        dbParams: '/dev/service_name/database/'
      },
      cacheExpiry: 15 * 60 * 1000,
      cacheKey: 'ssm-secrets'
    })
  )
  .before(async (request) => {
    const data = await getInternal(
      ['accessToken', 'dbParams', 'defaults'],
      request
    )
    Object.assign(request.context, data)
  })
  .handler(lambdaHandler)

9. sts

他のAWSサービスに接続する際に使用するSTSAWS Security Token Service)資格情報を取得することのようなシーンで使うことがあります。

注意のこと
sts:AssumeRoleのIAM権限が必要です。

使用例

import middy from '@middy/core'
import sts from '@middy/sts'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(
    sts({
      fetchData: {
        assumeRole: {
          RoleArn: '...',
          RoleSessionName: ''
        }
      }
    })
  )
  .handler(lambdaHandler)

III. 終わりに

AWS Lambda開発者が開発プロセスを効率化したいと考えている場合には最適なツールです。

Middyは軽量でモジュール化されたアプローチを採用しており、問題を分離し、重複を減らし、Lambda関数の主要なビジネスロジックに焦点を当てることができます。

AWS Lambdaの開発に強力で柔軟なミドルウェアフレームワークをお探しの場合は、ぜひMiddyをご検討ください。