- 投稿日:2020-10-16T23:20:22+09:00
docker runの[ i, t, d ]オプションの意味を調べた
はじめに
Dockerについて初心者の自分が
理解するのに難しかった部分を少しまとめます。
特に、コマンドオプションのところが難しかったので
少しでも自分と同じようなDocker初心者の方の理解の助けになればと思います!(^^)
間違った部分がありましたら是非お教えください!m(_ _)mDockerとは
これについては他にも沢山の記事があるので深く言及しませんが、
プロセスをコンテナ化するための仮想化ソフトウェアです。
Windowsの中に、Ubuntuも起動したい、そういった仮想化要望を叶えてくれます。主に、開発したものをデプロイする!って時に役立つようです。
本番環境はLinux!でも自分のPCはWindows!な時に、
Windows上に、「コンテナ」という箱を作り、その箱の中にLinuxを立ち上げて開発をする感じ。
もう少し具体的な使い方は、
Webサーバー用コンテナ、データベース用コンテナなどを作って開発をして
本番環境にそのままえーいっ!って使い方なのかな?複数人で開発する時に、
みんなの環境を合わせる意味でも便利なようです!以下の記事が丁寧で分かりやすかったです。
・Dockerとはどういったものなのか、めちゃくちゃ丁寧に説明してみる
・【連載】世界一わかりみが深いコンテナ & Docker入門Dockerの使い方
Dockerのインストール
まずは、Docker自体をインストールする必要があります。
公式サイト : https://docs.docker.com/get-docker/
こんなコンテナを作りたーい!って頼めば、そんなコンテナを作ってくれて、
コンテナ何個も作ったから管理してー!って頼めば複数のコンテナを管理してくれるやつです。
(管理は頼まずとも基本勝手に良い感じにしてくれます)Dockerをインストールしたら、
コマンドでDockerを操作することができるようになります。docker ~~~~みたいな感じでコマンドを打って、Dockerを操作します。
Linux以外?は、
「Docker Desktop For Windows/Mac」というGUIツールもありますね^^Docker Image
先ほど述べた、
こんなコンテナ作りたーい とは
こんなImageのコンテナ作りたーい ということで
具体的には
Ubuntuが入ったコンテナを作りたーい というようなことです。なので、コンテナを作るために、まずは「Image」を手に入れる必要があります。
これを「Docker Image」と言います。「 イメージ → コンテナ 」 ≒ 「 クラス → インスタンス 」
みたいな感じで考えれば、理解しやすいかもです。
Image群は基本的にここにあります。Docker Hub : https://hub.docker.com/
Docker Imageのリポジトリです。
GitHubのような感じと思えば良し。ここからImageを取ってきて、(必要があれば自分でカスタマイズして)
コンテナを作っていきます。docker pull someImageでImageをローカルに取ってくる。
docker push someImageでImageをサーバーにpushします。
既存のImageをカスタマイズして、自分でImageを作ることもできます。
「Dockerfile」というファイルにいろいろと設定を書いてdocker build -t imageName . // ※Dockerfile が存在する階層にいる想定でImageを作成します。
コンテナの作成・起動
Docker Imageを元に
実際のプロセスであるコンテナを作ります。docker create --name containerName someImageそして、プロセスを動かす。
docker start containerName/containerIDコンテナID(containerID)は
コンテナを作成した時に自動的に付けられます。コンテナ名(containerName)も指定しなければ自動的に付けられますが、
[ --name ] オプションを使って、自分で管理しやすい名前を付けるのが良いと思います。上記の、作成・起動という動作を一度に行ってくれるのが
docker run --name containerName someImageです。
コンテナの停止・削除
動いているコンテナを停止するのが
docker stop containerNameそして、停止しているコンテナ自体を削除するのが
docker rm containerName動作中のコンテナを強制削除するには、
docker rm -f containerNameとして、[ -f ] オプションを使います。
全体的に
「イメージ情報」を持ったクラスから
「実際のプロセス(コンテナ)」であるインスタンスを作って
Dockerが管理しているリストにコンテナを登録する
みたいに考えれば自分は理解しやすかったです。なので、
あくまでコンテナ自体はそれぞれ独立しています。
同じイメージから2つコンテナを作っても、それらはデータを共有してる訳ではないし、
コンテナを削除したら、そのコンテナで使っていたデータは基本的には消えます。
(データを永続化するには、Volume設定をする必要がある)Dockerコマンドのオプション
ここまでの内容は
様々な記事で書かれていて理解もしやすかったのですが、
自分が結構悩んだのがよく使うオプションの挙動?というか意味合い?でした。自分の中でも、これはこう書きましょう的な
ある種のおまじないのようになっていたので
少し踏み込んで調べてみて、ちょっとだけわかった気がしたので書いてみます。[ -i ][ -t ]オプション
これらのオプションは
docker run -it someImage /bin/bashのように使います。
( "/bin/bash" の部分は、コンテナ起動時に実行するメインコマンドを指定しています。)" -it " と書いているのは、" -i -t "と書いても良いです。
ただ、基本一緒に使うので短縮して書くのがスタンダードのようです。
それぞれ
[ -i ]オプション : interactive ( 対話型 )
[ -t ]オプション : tty ( 端末デバイス )
の意味です。[ -i ]オプション
シェルは何かしらの入力を受け取って処理をするコマンドプロセッサ?(何て言うの?)で、
対話型で入力を与えたい時は、標準入力からコマンドを与えて処理をしてもらいます。
(キーボード ≒ 標準入力 なので、キーボードでコマンドを打てばそれがシェルに届く)
なので、
コンテナの中で実行しているシェルに対話型で処理をしてもらうためには
コンテナの中で実行しているシェルと標準入力が繋がらないといけないです。
標準入力と繋がっていなければ、
コマンドを打っても、その入力はコンテナの中のシェルに届かず、
コンテナの中のシェルにコマンドを実行してもらうことができない状態になります。そうならないようにするためのオプションが [ -i ] オプションで、
「docker run で指定したコマンドの" /bin/bash "を対話型で使いたいから標準入力と繋いでね」
という意味のオプションなんだと思います。実際に、
1: docker run -it someImage 2: docker run -t someImage1と2で挙動を比較してみたらイメージしやすかったです。
2で起動した場合は、" ls " や " exit " などを入力しても何も起こりません。
入力したコマンドがコンテナ内のシェルに届いていないのがわかります。[ -t ]オプション
こちらは自分の中でも完全にしっくりきている訳ではないのですが、
おそらくこういうことなんじゃないかという推測です。。。" docker run " のコマンドを実行しているターミナルがあります。
で、
[ -t ]オプションを付けてコンテナを起動すると、
そのターミナルの中に、コンテナ用の疑似的なターミナルを用意する
ということだと思います。これも実際に、
1: docker run -it someImage 2: docker run -i someImage1と2を比較すれば、どういう状態かはすぐに体験できます。
2の場合、" ls " や " exit " などのコマンドを打てば、
それがコンテナ内のシェルに届いて、コマンドは実行されるのですが、
いつものようにターミナルを開いた時のような状態にはなっておらず、
ユーザー名やカレントディレクトリも出ていないし、タブによる入力補完なども機能しません。イメージ的には、
キーボードの入力がターミナルを介することなく、生のまま反映される感じ。。
ただ、これによって何か大きな問題があるのかと言われれば特にはわからず、
シェルと対話できるし、ただ単にターミナルの機能?が使えない分、不便なのかなという感じです。なので、[ -t ]オプションは
「普段のようにターミナルを使えないと不便だからコンテナ用に疑似ターミナル用意するね」
という意味のオプションなんだと思います。[ -d ]オプション
docker run -d someImageのように、runコマンドを実行する時に
[ -d ] オプションを使うことがあるのですが、
これはそのコンテナにアタッチせずにコンテナを起動するという意味です。
(アタッチしない = デタッチの"d")
つまりは、バックグラウンドでコンテナを起動するということです。このオプションで気になった点は、
バックグラウンドで実行してるのにコンテナがすぐに停止してしまうことでした。
なんとなく自分のイメージだと
バックグラウンドで実行したら裏で動き続けてくれるのかなと思ってたのですが、
単純にそういう訳でもなさそうでした。例えば
docker run -d someImage /bin/bashこれでコンテナを起動すると、
起動したコンテナは即座に停止します。これは指定した " /bin/bash "(メインコマンド) が起動直後に終了してしまうからで、
コンテナは、バックグラウンドで実行しようがしなかろうが、
メインコマンドの実行が終わると自動で停止する
という動作になっているからだと思います。なので、
バックグラウンドで実行させ続けたい時は
勝手に終了することのないコマンドを、コンテナに与える必要があるようです。先ほどの起動方法を
docker run -d -it someImage /bin/bash // ※この話に関しては[ -t ]オプションはあってもなくてもいいとすると、
" bin/bash " を対話型で実行するようになり、
メインコマンドが勝手に終了することがなくなり、
コンテナもバックグラウンドで動き続けるようになります。[ -d ]オプションで大事な点は、
「あくまでバックグラウンドで実行するだけでコンテナの挙動に差はないよ」
ということだと思いました。おわりに
完全にDockerの動きを理解できた訳ではありませんが、
よく使うオプションで
「とりあえずこのオプション付けておいたら良いんでしょ」
という状態からは少し抜け出せたかなと思います。同じようにDocker学び始めた方の理解の参考に
少しでもなれたら幸いです(^^)
- 投稿日:2020-10-16T23:04:59+09:00
OpenFOAM v8 Docker Image on Macのカスタマイズ
はじめに
OpenFOAMv8(Foundation版)をMacにインストールした後,イメージをカスタマイズする方法をここにまとめておきます。
インストールなどの詳細はこちらのページも参考になると思います。Dockerのインストール
Dockerサイトから,Docker Desktopをダウンロード,インストールします。ここでは特筆事項はありません・・・(楽でいいですよね)。
インストールが終了しましたら,Docker Desktopを起動します。こんな感じで右上にクジラマークが現れます。
OpenFOAMv8のインストール
FoundationにあるFoundationに従ってインストールします。
openfoam
というディレクトリを作成し,その中で,以下のコマンドを打ち込みます。本家から抜粋sudo curl --create-dirs -o /usr/local/bin/openfoam8-macos http://dl.openfoam.org/docker/openfoam8-macos sudo chmod 755 /usr/local/bin/openfoam8-macosOpenFOAMコンテナ
openfoam
のディレクトリ内で以下のコマンド(スクリプト)を実行することにより,OpenFOAMの環境が立ち上がります。openfoam8-macosこれでblockMeshやsolversが使えるようになっています。
イメージのカスタマイズ
まず,イメージのIDを確認します。
docker imagesこのImage IDをメモしておきます。その後,rootでImageを立ち上げます。
docker run -it -u=root <Image ID>スクリプトで立ち上げた場合は,aptなどでアプリをインストールしても,記録されません。が,rootで起動した場合はカスタマイズや各種設定変更はコンテナに記録されます。この状態で自分の所望のアプリをインストールします。
その後,コンテナを停止(ログアウト),Container IDを確認します。
docker ps -a変更を加えたコンテナをイメージにcommitさせます。
docker commit <container ID> <Repository>
Repository
ですがデフォルト設定を活かし,openfoam/openfoam8-graphical-macos
としておくのが良いです。ここでRepository
を自分の好きな名に変えることも可能ですが,その際は起動スクリプト/usr/local/bin/openfoam8-macos
内のDOCKER_IMAGESを修正する必要があります。たとえば,Repositoryをopenfoam8としたいのであれば,
exampledocker commit <container ID> openfoam8openfoam8-macosDOCKER_IMAGE='macos8'とします。
個人的には,
lv
,gmsh
,python3.8
,python3-pip(numpy, pandas, torch, tensorflow, scikit-learn)
は入れておくと便利かなと思います。1
以上でカスタマイズは終了です。お疲れさまでした。最後に
OpenFOAMのDockerイメージのカスタマイズ方法をまとめたサイト,ページが見当たらなかったこともあり,ここにまとめてみました。まだ,Dockerの使い方を理解できていない,使い切れていないこともあり,ベストなやり方であるかは分かりません・・・。より良いやり方があれば,ご指摘いただければ嬉しいです。
- 投稿日:2020-10-16T19:49:20+09:00
【Docker】php artisan migrate エラー(SQLSTATE[HY000] [2002])
環境:Mac
Laravel 6.x系
Mariadbphp artisan migrate 実行時に以下のエラーが表示される。
Illuminate\Database\QueryException : SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: nodename nor servname provided, or not known (SQL: select * from information_schema.tables where table_schema = database and table_name = migrations and table_type = 'BASE TABLE')上記エラーメッセージの一部をコピペして検索すると「.envファイル」修正の対策がでてきたが、
私の場合は、どれも設定済みであったので、解決には至らなかった。
その後も検索し続け、やっと解決したので備忘録として。
結果として、ごく初歩的なミスでした。。。以下簡単な流れ。
①コンテナに入る
②コンテナ内で「php artisan migrate」を実行
③違うエラーメッセージが表示される
④エラーメッセージの内容を確認し、訂正する==================================
①コンテナに入る
$ docker-compose exec app bash②コンテナ内で「php artisan migrate」を実行
[app] php artisan migrate③違うエラーメッセージが表示される
おそらく、記載ミス等の指摘がされている内容が表示されるかと思う。④エラーメッセージの内容を確認し、訂正する
③で表示されたエラーメッセージを読み取り、対象ファイルの修正を行う。私の例:エラーメッセージ内に「255」の記載あり。
migrationファイル内のtableのcreateで、以下のように記載していたのが原因
修正後、問題なく、php artisan migrate できた。。。#誤り(デフォルトで255のため不要) $table->string('title',255); #修正後 $table->string('title');恥ずかしいミスだが、これが誰かの役に立てば幸い!!!!!!
- 投稿日:2020-10-16T19:49:19+09:00
これだけ知っとけ!docker-composeスニペット集
はじめに
コピペで使える docker-compose 記載例集です。
使い方
- Mac, Windows 双方とも、空のディレクトリを作成する。
docker-compose.yml
という名前のファイルを新規作成する。- 記載例集に載っている記載例をコピー&ペーストして保存する。
- 上記フォルダをカレントディレクトリにして、Macならターミナル、WindowsならPowerShellかコマンドプロンプトを開く。
docker-compose up
を実行。Macでターミナルを開く方法
https://qiita.com/yamagh/items/02608e97be22c85cefaa 参照。
WindowsでPowerShellを開く方法
エクスプローラの「ファイル」メニューからPowerShellを開くことができる。
記載例集
MySQL
docker-compose.ymlversion: '3' services: # MySQL db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: mysql MYSQL_DATABASE: appdb volumes: - ./db/data:/var/lib/mysql ports: - 3306:3306バージョン指定は8.0にしていますが、5.5、5.7等でもOKです。
接続ID/Passwordは、root/mysqlになります。また、初期作成されるデータベース名がappdbになります。
変更したい場合は、environmentの各項目を修正してください。
localhost:3306
にアクセスすれば接続できます。
docker-compose.ymlがあるフォルダにdbフォルダが自動的に作成され、この中にMySQLのデータが入ります。
このフォルダの中身を消去するとDBを初期化できます(downしている時に消去してください)jupyterLab
docker-compose.ymlversion: '3' services: lab: image: jupyter/datascience-notebook environment: - JUPYTER_ENABLE_LAB=yes ports: - "8888:8888" volumes: - ./work:/home/jovyan/worknotebookよりもlabのほうが使い勝手が良いと思います。また、ホームにworkディレクトリをマウントさせています。
workの中にノートを作成していけば良いでしょう。gcc
たまにめっちゃCのソースをコンパイルしたいときがある。
docker-compose.ymlversion: "3" services: gcc: image: gcc tty: true volumes: - ./src:/src
tty: true
を書いておかないとすぐexitしてコンテナが終了してしまいます。
コンテナは起動だけしておいて、あとからゆっくりCLIでアタッチしましょう。終わりに
記載例集は順次増やしていく予定です。
- 投稿日:2020-10-16T19:01:30+09:00
高校生がAIでWebサイトの改善点を提案するサービスを作った話
まえがき
アクセシビリティーの観点からWebサイトを診断し、AIプラットフォームを利用して得た情報をもとにベストプラクティスを提案してくれるオープンソースのWebサービス「Visible」を開発しました。
- WebサイトURL: https://visi.dev
- GitHubリポジトリ(Starください!): https://github.com/visible/visible
GoogleのLighthouseなど、Webサイトの診断を行ってくれるサービスは以前からありましたが、診断だけではなく改善点の提案も行う新しいサービスになっています。また、アクセシビリティーに関する理解を深めてもらえるように工夫をした設計にしていたり、コマンドライン版ではスタンドアロンで実行可能なようになっています。
2020年度の「独創的アイデアと卓越した技術を持つ小中高生クリエータ支援プログラム」未踏ジュニアに採択されていて、技術・資金面で援助を頂いており、11月1日にYouTube Liveで最終成果報告会が行われる予定です!
機能の紹介
WebサイトのURLを入力することでページを診断し、改善点を自動で提案してくれます。
以下に提案する修正の一例を紹介します。
alt
属性
<img>
要素はスクリーンリーダーやSEOのクローラーに画像の内容を説明するためにalt
属性が提供されることが推奨されており、Google Cloud PlatformのVision APIを利用してキャプションを生成することで改善のヒントを提案します。
lang
属性Webページの言語が明示的に指定されていないと、言語情報を必要とするユーザーエージェントで問題があるためページの内容からTranslate APIから言語情報を検出し提案します。
色コントラスト比
もちろん機械学習を使わない改善提案も可能です。色コントラスト比が低いと色覚特性を持ったユーザーが使いづらいため、コントラストを上げる修正を提案します。
アクセシビリティーに関するベストプラクティスはW3CによってWCAGという名前で標準化されており、他にも標準に基づいたルールがいくつかあります。
提案の仕組み
診断プログラムの実行にはChromeをヘッドレスで実行できるPuppeteerを使っていて、チェックポイント(Rule)のインターフェイスを実装したプログラムを実行し、各ruleから返された値をもとにファイルの情報と紐付けて最終的に差分として表示できるようになっています。
全体はコア、プラグイン、アプリケーションの3つのコンポーネントから構成されていて、コアで公開されている最低限の実装とインターフェイスを実装する形でプラグインで実際の処理を書いています。プラグインの形式はESLintを参考にしています。
改善点の生成アルゴリズムやヘッドレスブラウザーもruleと同様にプラグインとして拡張可能なため、Google Cloud Platformに限らず別の方法を使うことも可能です。
使った技術
プロジェクトはTypeScriptで開発されています。Tech stacksは以下のとおりです。
コア部分
- Puppeteer - Chromeを使ったヘッドレスブラウザーです。診断プログラムの実行に使っています
- domhandler - HTMLのASTです
- PostCSS - プリプロセッサーとして知られていますが、stylelintを参考にASTとして使っています
Webバックエンド
- Clean Architecture - 4層にレイヤー分けした有名なバックエンドの設計方法です。
- TypeORM - TypeScript向けのORMです。
- Bull - Redisを使ったジョブキューのフレームワークです
- Apollo - Node.jsのGraphQL実装です
Webフロントエンド
- Next.js - SSR/SSG/LambdaをやってくれるReact向けのBFFです
- Apollo - Node.jsのGraphQL実装です
- Tailwind CSS - Utility-firstのCSSフレームワークです
- i18next - JS向けの国際化ライブラリです。
その他
- GitHub Actions - CI/CDに使っています
- Docker - Web版のデプロイに使っています
- Yargs - CLI版に使っているフレームワークです
- Lerna - JSのモノリポジトリのためのツールです
開発時のエピソード
Webアクセシビリティーという技術には前から興味があった一方で、僕自身は支援技術の利用を迫られたことがなく、正しいマークアップを心がけるモチベーションは専ら検索エンジンへの最適化くらいだったため、知識はあってもアクセシビリティーを欠いたWebサイトを作ってしまうことが多くあり、ESLintみたいに修正の方法も教えてくれるソフトウェアがあればいいと考えていました。
同時に、未踏ジュニアに応募できる年齢制限が17歳までで、当時僕はすでに17歳だったため最後にチャレンジしてみたいと考えていて、丁度いい機会にそれを作って応募してみようとプロトタイプを制作し始めました。
プロトタイプ
僕自身は中学生のときからプログラムを書いていてアルバイトだったりもしてるのでコードはかなり書いていましたが、それでも応募まで数ヶ月しかない状態でプロトタイプを仕上げるのは結構厳しく(正直採択後よりも応募段階のほうが忙しかったかもしれない)明確にゴールを設定する必要がありました。
応募自体にはプロトタイプは必須ではなくプロダクトの概要を書いた書類を送れば良かったものの、採択されるには僕自身が持っている技術を示して最後まで作り上げることができることを証明する必要がありました。逆に、それを示せればどこが面白いのかは伝わると判断し診断できる項目は最低限に絞り、「修正の提案」はバッサリ捨てて、「URLを入力したら診断結果が出てくる」ところまでをやることにしました。あとは脳内のふわふわした概念をノートに書いてドメインモデルに落とし込み、一番慣れている技術スタックでコードを書き始めました。
大体2ヶ月くらいで動くプロトタイプが出来上がり、無事書類選考も通過しました。その後は面接を受けることになっていて、オンラインでメンター陣にプロダクトに関する質問を受けました。正直何を言ったかよく覚えていませんが、確かプロダクト自体が将来的にどういう目標があるかみたいなことを訊かれて、漠然とした回答しかできなかったのは覚えています...。
採択後
採択後は、既にプロトタイプがあったためアジャイルで開発しました。未踏ジュニアでは定期的にメンタリングを受けられることになっていて、僕のプロジェクトでは週に一度メンターに進捗を報告しフィードバックを頂いていたので、だいたい各週でマイルストーンを設けてそれまでに小分けにした機能の開発を進めました。
ユーザーに使ってもらうまでのタスクの優先順位は完全に僕の勘で「これができるようになったぞ」って言ったときのインパクトが強い順でやっていましたが、今考えるとあんまりいい方法ではなかったかなと思います。ただ、それがあったおかげでビジネスロジックには拘ってもフレームワークに関する細かいところを弄りすぎることはなくてスピードは上がっていた気がします。
初ユーザーテスト
未踏ジュニアでは期間中、採択直後と中間時点と最後の3回登壇する機会があり(今年はオンラインでしたが)、一回目のプレゼンの機会がやってきたので既にあるプロトタイプ+αの段階のものを発表しました。
その際に、その時点のものをデプロイしたURLを共有し聴いていた方たちに実際に使っていただいたのですが、dockerの共有メモリの設定をミスっていたり非同期にするべきところを同期でレスポンスしたいたりして発表直後にサーバーがダウンしてしまい、期待していたほどのフィードバックは得られませんでした (トホホ〜)
A11yが専門の方々へのインタビュー
期間の四分の一を過ぎたあたりでメンター陣のご協力もあり某社のアクセシビリティーチームの方にフィードバックしていただく機会を頂けました。
アクセシビリティーの現場でどんなプロセスが行われているのかや、どんなツールを使っているか、チーム開発特有の問題を伺うことができ、このインタビューでストーリーラインを具体化してユーザー層を絞ることができました。タスク優先度もここらへんから明確になってきたと思います。
その後の改善
未踏ジュニアでの2回目の発表(もちろん負荷対策はしました...)やTwitterのフォロワーなどを活用して、実際に使ってもらいアンケートに答えてもらうというフィードバックループを小分けに回しました。
フィードバックは「How to create a good survey」と検索して上に出てきた良いとされる質問をパクってフィードバック用のフォームをGoogle Formsで作成したものと、Google Analyticsのタグを埋め込んだもので多面的に得られるようにしています。
特にユーザーがどの情報を欲しがっているかは重視していて、例えば未実装の機能にもURLを割り振ってそのURLにどれくらいトラフィックが発生したかで機能の優先度を付けることで開発に反映しました。
詰まったところ
すげーニッチな内容かもしれませんが、開発にあたって詰まったところのメモを書いておきます。
CSSのDOMから取れる情報とASTのマッピングができない
HTMLやCSSなどのソースコードはブラウザでパースされたあとにDOMに変換されてJavaScriptから利用可能になりますが、
getComputedStlye
などのメソッドから取得できるCSSの情報からは、どのファイルや宣言が適用されているのかわかりません。そこで、Google Chromeの開発者ツールのAPIである Chrome Devtools Protocol を使うことにしました。CDPは
CSS.styleSheetAdded
というイベントから読み込まれたスタイルシートの情報を取得できるため、問題検出時にNodeのIDと該当のCSSプロパティから対応するCSSファイルを探し出し、PostCSSのASTに変換して扱うことができました。Clean ArchitectureでORMをどこに置くか問題
書籍では「Interface adapter層はframework層が必要としているデータ形式に変換するレイヤー」と説明されており、そのためSQLクエリを発行する部分は interface adapter層、それを具体的なRDBMSで実行するのはframework層という扱いになっているのですが、ORMではこの2つのプロセスの境界が曖昧で、ググっても色んな人が全然違うことを言ってるっぽかったです。
TypeORMは(もちろん限度はありますが)どのRDBMSを使うかが抽象化されていて、最終的に
ormconfig
で決定するようになっているので、詳細について言及していないと割り切ってinterface adapter層でDALを実装することにしました。余談ですが、ドメインモデルをAPIの形に変換するpresenterもGraphQLの定義を直接扱うのではなくpresenter側で定義した型を使うことで依存の方向を守っています。
モノレポ(Yarn Workspace)でDockerをやるのが辛すぎる
マイクロサービスとかだとモジュール同士が依存することはあんまり無いのかと思いますが、フロントエンドとバックエンドで共有するパッケージがあるみたいなケースでYarn workspaceとDockerを使いたくなってしまうと、パッケージごとにlockfileを作れなかったり
node_modules
以下がシンボリックリンクになっているから単純にコピーしても動かなかったりして詰みます。今の所ごちゃごちゃなワークアラウンドを書いてなんとか動いています。Yarn v2 (berry) では
yarn workspace focus
という機能が提供されて、ほしいパッケージの依存だけをインストールして独立して動かせるようになっているっぽいので早く移行したいなと思っているのですが、Plug'n'Play周りが理解しきれていないためまだ手を付けられていません。styled-componentsが辛い
初めてちゃんとデザインシステム的なUIコンポーネントを作ったんですが、一つのコンポーネントが複数のバリアントを持ってるみたいなとき(下記)にstyled-componentsでやると可読性が最悪で最終的にTailwindに逃げました。
const Button = styled.button` font-size: 12px; ${ (props) => props.variant === 'primary' ? css` color: ${props.theme.primaryFgColor}; background-color: ${props.theme.primaryBgColor}; ` : css` color: ${props.theme.secondaryFgColor}; background-color: ${props.theme.secondaryBgColor} `; } `;詳細: https://qiita.com/rigarashi/private/5c97be5ed8fb15ea2d96
Utilify-firstをCSS-in-JSに輸入したstyled-systemとかxstyledというやつもあるっぽいんですが、テーマの型が静的に付かなかったので見送りました。
Puppeteerで並列処理
puppeteer-clusterというライブラリがいい感じっぽかったのですが、unmaintained気味なのと、pageのインスタンスをコールバックで受け取って簡単な処理をすることしかできず、例えばObservableに変換するみたいな今回のユースケースだと厳しかったのでやめました。
代わりに、いわゆるObject Poolingでbusyなインスタンスとそうでないインスタンスを管理して、暇そうなインスタンスに処理を投げるようにしました。こういうのは多分ワーカープロセスをforkしていくのがいいのかと思いますが、Puppeteerを起動した時点ですでにChromiumのプロセスと分かれるのであんまり意味がなかったのと、GraphQLのsubscriptionのソースになっているのがただのシングルトンで、redisとかを挟んでなくて同じプロセスで呼ばないとブラウザに伝えられないためです。
あとがき
蔑ろにされがちなアクセシビリティーで人々の気を引くにはどうすればいいか考えた結果、若干地雷っぽいタイトルになってしまったことをお赦しください。
また、web版等公開していますのでぜひお試しいただきフィードバックをお訊かせいただけると嬉しいです。
- WebサイトURL: https://visi.dev
- フィードバック: https://forms.gle/SvSkkKMs1NDbm4vn9
関連リンク
- GitHub (starください!!!): https://github.com/visible/visible
- Twitter: https://twitter.com/visible_hq
- 投稿日:2020-10-16T18:53:42+09:00
【Docker】Todoリスト作成時に詰まったところ
環境:Mac
Laravel 6.x系
Mariadb【Docker】dbコンテナに入り、データを見ると日本語が???で表示される
https://qiita.com/n_oshiumi/items/cfe91c60730f602b38eb
こちらの記事を参考にしましたが、私の場合は以下のエラーが発生し、解決できなかった。E: Unable to locate package language-pack-ja-base E: Unable to locate package language-pack-jaそこで上記エラーを検索しまくり、やっと以下の記事にたどり着いた。
https://www.atmarkit.co.jp/ait/articles/1806/28/news043.html結論としては、先に [apt update] をすることで、[language-pack-ja-base]らがインストール可能になる。
root # apt update root # apt-get install language-pack-ja-base language-pack-ja
- 投稿日:2020-10-16T18:53:42+09:00
【Docker】dbコンテナに入り、データを見ると日本語が???で表示される
環境:Mac
Laravel 6.x系
Mariadbhttps://qiita.com/n_oshiumi/items/cfe91c60730f602b38eb
こちらの記事を参考にしましたが、私の場合は以下のエラーが発生し、解決できなかった。E: Unable to locate package language-pack-ja-base E: Unable to locate package language-pack-jaそこで上記エラーを検索しまくり、やっと以下の記事にたどり着いた。
https://www.atmarkit.co.jp/ait/articles/1806/28/news043.html結論としては、先に [apt update] をすることで、[language-pack-ja-base]らがインストール可能になる。
root # apt update root # apt-get install language-pack-ja-base language-pack-ja
- 投稿日:2020-10-16T18:05:12+09:00
VSCodeのRemote ContainersでGoの開発環境を構築
はじめに
VSCodeの拡張機能である、Remote Containersを使って、Goの開発環境を構築したら快適だったので、紹介します。
尚、VSCodeおよび、Docker Desktopのインストール方法については割愛します。Remote Containers
Remote Containersとは、VSCodeのリモート開発機能のうち、Dockerに特化した拡張機能です。
他にもSSH経由でリモート接続する「Remote SSH」、WSL(Windows Subsystem for Linux)を使用してリモート接続する「Remote WSL」があるようです。ローカルのVSCodeからVSCodeServerを介して、Dockerコンテナにリモート接続することで、ローカル環境とは切り離された環境で開発をすることが可能です。
つまり、Docker上で、全てが完結するため、コード補完などの恩恵を受けつつ、ローカル環境を汚さなくて良い素晴らしい機能です。
※公式サイト
※2020年10月時点ではプレビュー版となります。インストール
VSCodeのExtentionsからRemote Containersを検索してインストールします。
初期設定
Ctrl + Shift + P
でメニューを出して、
Remote-Containers:Add Development Cotainers Configuration Files...
を選択します。
私の環境ではすでにdocker-compose.yml及びDockerFileで環境を構築しているため、そちらの設定を元に初期設定を行うことができます。
また、From a predefined container configuration definition...
を選択すると、あらかじめ用意されている、設定を使用することも可能です。
今回は、既存のdocker-composeファイルを使用して初期設定を行います。
.devcontainer
初期設定をすると
.devcontainer
フォルダが自動的に作成され、配下に
・devcontainer.json
・docker-compose.yml
が自動生成されます。
devcontainer.jsonが設定ファイルになります。ここで、現在のディレクトリ構造は以下のようになりました。
- .devcontainer ├ devcontainer.json └ docker-compose.yml - sample └ ソースコード - docker-compose.yml - docker-compose.dev.yml - Dockerfile - Dockerfile.dev自動生成されたファイルは以下。
devcontainer.json// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml. { "name": "Existing Docker Compose (Extend)", // Update the 'dockerComposeFile' list if you have more compose files or use different names. // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. "dockerComposeFile": [ "..\\docker-compose.dev.yml", "docker-compose.yml" ], // The 'service' property is the name of the service for the container that VS Code should // use. Update this value and .devcontainer/docker-compose.yml to the real service name. "service": "sample", // The optional 'workspaceFolder' property is the path VS Code should open by default when // connected. This is typically a file mount in .devcontainer/docker-compose.yml "workspaceFolder": "/workspace", // Set *default* container specific settings.json values on container create. "settings": { "terminal.integrated.shell.linux": null }, // Add the IDs of extensions you want installed when the container is created. "extensions": [] // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Uncomment the next line if you want start specific services in your Docker Compose config. // "runServices": [], // Uncomment the next line if you want to keep your containers running after VS Code shuts down. // "shutdownAction": "none", // Uncomment the next line to run commands after the container is created - for example installing curl. // "postCreateCommand": "apt-get update && apt-get install -y curl", // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. // "remoteUser": "vscode" }docker-compose.ymlversion: '3' services: # Update this to the name of the service you want to work with in your docker-compose.yml file sample: # If you want add a non-root user to your Dockerfile, you can use the "remoteUser" # property in devcontainer.json to cause VS Code its sub-processes (terminals, tasks, # debugging) to execute as the user. Uncomment the next line if you want the entire # container to run as this user instead. Note that, on Linux, you may need to # ensure the UID and GID of the container user you create matches your local user. # See https://aka.ms/vscode-remote/containers/non-root for details. # # user: vscode # Uncomment if you want to override the service's Dockerfile to one in the .devcontainer # folder. Note that the path of the Dockerfile and context is relative to the *primary* # docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile" # array). The sample below assumes your primary file is in the root of your project. # # build: # context: . # dockerfile: .devcontainer/Dockerfile volumes: # Update this to wherever you want VS Code to mount the folder of your project - .:/workspace:cached # Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker-compose for details. # - /var/run/docker.sock:/var/run/docker.sock # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. # cap_add: # - SYS_PTRACE # security_opt: # - seccomp:unconfined # Overrides default command so things don't shut down after the process ends. command: /bin/sh -c "while sleep 1000; do :; done"※一旦ファイルは生成されたものの、私は、docker-compose.ymlの2重管理になってしまうのが嫌だったので、.devcontainer配下のdocker-compose.ymlに全てを移しました。
Go向け設定
Goを開発するための設定を加えていきます。
extensions
ローカルにインストールしたVSCodeの拡張機能は、リモートコンテナ内部では使用できないので、コンテナ起動時に、自動的にインストールする拡張機能を設定します。
Goの拡張機能を入れます。この文字列は、拡張機能一覧で右クリックCopy Extension Id
から取得できます。devcontainer.json"extensions": [ "golang.go" ]settings
goplsを使用するので、githubを参考に、設定を追加します。
Gomod設定(GO111MODULE
)に関しては、1.13以降不要となりましたが、設定しないとimport文にエラーが出てしまうので、設定します。devcontainer.json"settings": { "terminal.integrated.shell.linux": "/bin/bash", "go.useLanguageServer": true, "[go]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, }, // Optional: Disable snippets, as they conflict with completion ranking. "editor.snippetSuggestions": "none", }, "[go.mod]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, }, }, "gopls": { // Add parameter placeholders when completing a function. "usePlaceholders": true, // If true, enable additional analyses with staticcheck. // Warning: This will significantly increase memory usage. "staticcheck": false, }, "go.toolsEnvVars":{ "GO111MODULE":"on" } }workspaceFolder
リモートコンテナ起動時のワークスペースを設定します。
docker-compose.yml側で、ボリュームを/go/src/
配下にマウントするので、ワークスペースもそれに合わせて変更します。devcontainer.json"workspaceFolder": "/go/src/sample",以上で設定は終了です。
起動
実際にコンテナを起動します。
Ctrl+Shift+P
から、Remote-Containers:Reopen in Container
を選択初回起動時では、ツール類が
/go/bin
にインストールされていないのでインストールします。goplsを使用するので、以下の通知からインストールするか、
go get -v golang.org/x/tools/gopls
を実行します
その他ツールに関しては、
Go:Install/Update Tools
からgocode
とgocode-gomod
以外をインストールします。
/go/bin
配下に全てインストールされていることが確認できました。ここまでで、コンテナ上で開発することができるようになります。
当然ながら、コンテナ単位での管理となるため、コンテナの削除を行うと、インストールしたものについては、消えるので、再設定が必要となります。デバッグ
コンテナ内でのデバッグも可能です。
マウントした、ソースコードと同じディレクトリに.vscode
フォルダを作成して、launch.json
を作成します。launch.json{ "version": "0.2.0", "configurations": [ { "name": "Remote", "type": "go", "request": "launch", "host": "localhost", "program": "${workspaceRoot}", "args": [] } ] }まとめ
最終的に作成したファイルを記載しておきます。何かのご参考になれば幸いです。
devcontianer.json{ "name": "Existing Docker Compose (Extend)", "dockerComposeFile": [ "docker-compose.yml" ], "service": "sample", "workspaceFolder": "/go/src/sample", "settings": { "terminal.integrated.shell.linux": "/bin/bash", "go.useLanguageServer": true, "[go]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, }, "editor.snippetSuggestions": "none", }, "[go.mod]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, }, }, "gopls": { "usePlaceholders": true, "staticcheck": false, }, "go.toolsEnvVars":{ "GO111MODULE":"on" } }, "extensions": [ "golang.go" ] }docker-compose.ymlversion: '3' services: # Update this to the name of the service you want to work with in your docker-compose.yml file sample: image: golang:1.14 volumes: - ./../:/go/src/:cached tty: true environment: - MYSQL_CONNECTION_STRING=${CONNECTION_STRING} networks: - default - db-network networks: db-network: external: true参考
- Go勉強(1) mac+VSCode+Go環境を設定:https://qiita.com/oruharo/items/545378eae5c707f717ed
- VS Code Dev Container Guidebook:https://techbookfest.org/product/4696850535809024?productVariantID=5428870601768960
- 投稿日:2020-10-16T17:44:47+09:00
docker-composeで Nuxt TypeScript + Vuetify 環境を構築
はじめに
普段Laravelを触ることが多く、イチからフロントエンドの環境をDockerで構築したことがなかったので挑戦してみました。
docker-composeでNuxt TypeScript+Vuetifyなフロントエンド環境を構築します。
最終的に、モノリポでバックエンドとしてlaravelも同じリポジトリに載せる予定です。
Dockerとdocker-composeがインストールされているものとします。実行環境
- macOS Catalina ver 10.15.6
- Docker ver 2.4.0
- docker-compose ver 1.27.4
ディレクトリ構成
docker-compose.yml
nuxt/
docker/
└ app/
└ Dockerfiledocker-compose
docker-compose.ymlversion: "3" services: app: build: context: ./ dockerfile: ./docker/app/Dockerfile ports: - "3000:3000" command: yarn run dev volumes: - ./nuxt:/nuxtDockerfile
FROM node:14.4.0-alpine ENV HOME=/nuxt \ LANG=C.UTF-8 \ TZ=Asia/Tokyo \ HOST=0.0.0.0 # Vuetifyのインストールに必要 RUN apk update && \ apk upgrade && \ apk add --no-cache make gcc g++ python WORKDIR /nuxt EXPOSE 3000ビルド
$ docker-compose buildNuxt、Vuetifyのインストール
$ docker-compose run --rm app yarn create nuxt-app nuxt ... ... create-nuxt-app v3.4.0 ✨ Generating Nuxt.js project in nuxt ? Project name: `nuxt` ? Programming language: TypeScript ? Package manager: Yarn ? UI framework: Vuetify.js ? Nuxt.js modules: Axios (<= スペースキーを押さないと選択されないので注意) ? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Testing framework: Jest ? Rendering mode: Single Page App ? Deployment target: Server (Node.js hosting) ? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Continuous integration: None ? Version control system: None各種質問が出てくるので、上記のようにUI frameworkの欄でVuetify.jsを選ぶ。
スペースを押さないと選択されない項目があるので注意。
インストールが終わるとnuxtディレクトリが以下のようになるnuxt
┣ .config/
┗ nuxt/
┣ asset/
┣ ...
┣ ...
┣ package.json
┗ yarn.locので、nuxt/nuxt/* をnuxt/に移動して以下のようにする
nuxt
┣ .config/
┣ asset/
┣ ...
┣ ...
┣ package.json
┗ yarn.loc起動
$ docker-compose up -dhttp://localhost:3000/
にアクセスして下の画像のように表示されればとりあえず完了!
tsconfig.jsonの設定などあるかもしれませんが、次はLaravelのSanctumとSPA認証の記事で書く予定…
- 投稿日:2020-10-16T15:47:17+09:00
DockerイメージをビルドしてECRにpushするshell script
いちいちビルドしてタグ付けしてecrにログインしてpushするのが面倒だったので、1コマンドでやってくれるスクリプト書きました。
普段そんなに書かないのでShell書くときのConventionみたいなの知らなくて、変な書き方してる部分もあるかも。
気が向いたらご指摘いただけると嬉しいです。書いたやつ
#!/bin/sh # Returns boolean indicates whether designated tagged-image exists. # arg1: repository name # arg2: tag name function image_exists() { image=$(docker image ls -a | grep $1 | grep $2) if [ "$image" ]; then return 0; else return 1; fi } # Builds new image. # arg1: tag in the form like ${repository}:${tag} # arg2: Path to Dockerfile function build_image() { docker build -t $1 -f $2 . } # Work on master branch git checkout master # Get latest master revision revision=$(git rev-parse --short HEAD) echo "Current master revision is ${revision}\n" # Set constants readonly repository=your-repository-name readonly ecr_repository=************.dkr.ecr.<region>.amazonaws.com readonly revised_repository=$repository:$revision readonly ecr_revised_repository=$ecr_repository/$revised_repository readonly path_to_dockerfile=<relative-path-to-dockerfile> echo "local-repository: ${repository} ecr-repository: ${ecr_repository} local-revised-repository: ${revised_repository} ecr-revised-repository: ${ecr_revised_repository}\n" # Build current source if revision not exists if image_exists $repository $revision; then # Do nothing. echo "local-revised-repository already exists. Skip build.\n" true; else echo "Start building local-revised-repository." build_image $revised_repository $path_to_dockerfile; fi # Generate ecr-repository-tagged image if not exists if image_exists $ecr_repository $revision; then # Do nothing. echo "ecr-revised-repository already exists. Skip tagging.\n" true; else docker tag $revised_repository $ecr_revised_repository; fi # 必要に応じてこのへんでAWSの認証情報を設定する # Login to ECR aws ecr get-login-password --region <region> \ | docker login --username AWS --password-stdin $ecr_repository # Push new image to ECR docker push $ecr_revised_repository echo "Done.\n"実装要件
- latestっていうタグ付けをすると、あとから見た時にいつのビルドなんだか分からなくなるので、リリース時点のgitリビジョンを利用する
- 注意点: リリースブランチのリビジョンを取るべきなので、必要に応じて
master
部分を変更してください- 指定されたリビジョンがタグ付けされたイメージがまだ存在していなければビルドする
- 存在していればビルドをスキップする
- 指定されたリビジョンがタグ付けされたECR向けのイメージがまだ存在していなければタグ付けする
- 存在していればタグ付けをスキップする
- ECRにログインする
- 注意点: ECRログインする前に、そのECRが存在するAWSアカウントを操作できる認証情報を取得しておくこと。
- profileがあるならそれを読み込む。
- 自分の場合、MFAで一時的な認証情報を取得する必要があるため、その部分を書いてある
- ビルドされたイメージをECRにpushする
おまけ: MFA認証
これはこれでめんどくさかったので、誰かの役に立てば。
# 標準入力からmfaコード入力を受け付ける read -p "Input mfa code: " mfaCode result=$(aws sts assume-role \ --role-arn arn:aws:iam::************:role/<role-name> \ --role-session-name <session-name> \ --serial-number <mfa-serial> \ --token-code $mfaCode --profile <profile-name>) export AWS_ACCESS_KEY_ID=$(echo $result | jq ".Credentials.AccessKeyId" -r) export AWS_SECRET_ACCESS_KEY=$(echo $result | jq ".Credentials.SecretAccessKey" -r) export AWS_SESSION_TOKEN=$(echo $result | jq ".Credentials.SessionToken" -r)
- 投稿日:2020-10-16T10:33:10+09:00
docker-compose upでコンテナが立ち上がらずエラーが出たときの話
前日まで使えていたのに、ある日いきなりdockerが立ち上がらなくなった。(PCアップデートしたせい??)
最初にでたのはこんな感じのエラー
Could not find ancestry-3.1.0 in any of the sources Run `bundle install` to install missing gems.上記のgemはgemfile.lockに載っているし、エラーが出るのはおかしいと思いつつも
docker-compose run web bundle install
してみたけど案の定同じエラーで動かない。検索してみるとこんな記事があった。
https://teratail.com/questions/291124
試しにdocker-compose build --no-cache
をやってみると次は以下のエラーがdb uses an image, skipping Building web Traceback (most recent call last): File "site-packages\docker\utils\build.py", line 96, in create_archive OSError: [Errno 22] Invalid argument: '\\ディレクトリ名\\¥アプリ名\\node_modules\\.bin\\acorn' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "docker-compose", line 3, in <module> File "compose\cli\main.py", line 67, in main File "compose\cli\main.py", line 126, in perform_command File "compose\cli\main.py", line 302, in build File "compose\project.py", line 468, in build File "compose\project.py", line 450, in build_service File "compose\service.py", line 1125, in build File "site-packages\docker\api\build.py", line 160, in build File "site-packages\docker\utils\build.py", line 31, in tar File "site-packages\docker\utils\build.py", line 100, in create_archive OSError: Can not read file in context: \\?\ディレクトリ名\アプリ名\node_modules\.bin\acorn [24060] Failed to execute script docker-compose上記のエラーで調べてみると、この記事にたどり着いた。
https://crieit.net/posts/docker-compose-build-Can-not-read-file
記載されている通り、.dockerignoreを作成しnode_modulesと記載すればdocker-compose buildが動くように!エラーで検索した際にdockerのバージョンの問題という記事も見つけたのですが、自分のバージョンと違うようだったので謎が深まりました...無事解決してよかったです。
https://github.com/docker/compose/issues/5888今回エラーが起きたバージョン
docker-compose version 1.27.4, build 40524192 Docker version Version: 19.03.13
- 投稿日:2020-10-16T02:12:37+09:00
Db2コンテナをGoで操作する
こんにちは。
今回はDockerで立てたDb2コンテナをGoで操作する方法について紹介します。
Db2コンテナの立て方や、セットアップ時にデータを挿入する方法については、以下の記事で紹介しておりますので、ぜひ参考にしてください。Db2のDBコンテナを立ててちょっとしたデータを挿入してみる
Db2/DBコンテナに初期テストデータを挿入した状態でセットアップ今回は、Db2コンテナをデータを挿入した状態で立ち上げ、Goでの実装を中心に紹介します。
コード類はこちらに載せています。
概要
Db2コンテナにデータは挿入できたけど、実際にそのデータを取得してきて操作したり、データを更新したりするにはどうするんだという方向けの内容となっています。
今回はGo言語でDb2からデータを取ってくる方法をご紹介しようと思います。
開発環境
- Win10 Enterprise
- docker (v19.03.13)
- ibmcom/db2 (v11.5.4.0)
- Go (v1.12.9)
- Git Bash (git version 2.27.0.windows.1)
前提
- ibmcomのDb2コンテナの立ち上げ方がある程度分かる
- Go言語がある程度わかる
Getting Started
開発環境はWindowsですが、MacでもLinuxでもできます。
今回は、Db2との疎通確認に重きを置いておりますので、API化などは行っていません。
単純にDb2からデータを取ってきて、コンソールに出力するだけのプログラムを書いていきます。(いつかGoでREST APIの実装も紹介します。)1. フォルダ構成の説明
まずはフォルダ構成を説明します。
projectproject ├─go | ├─model | | ├─user.go | | ├─tweet.go | | └─reply.go | └─main.go └─db ├─data | ├─users_insert.csv | ├─tweets_insert.csv | └─replys_insert.csv ├─sql | ├─users_create.sql | ├─tweets_create.sql | └─replys_create.sql ├─createschema.sh ├─Dockerfile └─env.list
- /go
- /model
- user.go ユーザーのDTOとDAO
- tweet.go ツイートのDTOとDAO
- reply.go
リプライのDTOとDAO- main.go
メインの関数
- /db: データベースをセットアップするフォルダ
- /data
初期にデータベースに登録するテストデータのフォルダ- /sql
テーブル作成するSQL文のフォルダ- createschema.sh
データベースセットアップ時に呼ばれるテーブル作成用スクリプト- Dockerfile
コンテナ定義- env.list
Db2コンテナ用の構成情報本当は、ドメイン駆動設計とかで、ユーザードメインとか、インフラストラクチャとか作ってカッコいい設計をしたいんですが、それはまたの機会ということで。
2. コンテナの立ち上げ
まずは
Dockerfile
を用いて、コンテナイメージをビルドします。
実行するコマンドは以下です。$ cd db $ docker build -t test-db:v1.0 .これで、コンテナイメージが出来上がるので、早速runしていきます。
$ docker run --name go-db --restart=always --detach --privileged=true -p 50000:50000 --env-file env.list test-db:v1.0詳しい説明はこちらで紹介しています。
ここで大事なのはポートを50000:50000でポートフォワーディングしていることです。
クライアントに公開している50000ポートはDBと接続する時に指定する必要があるので、覚えておきます。3. インポートするパッケージ
利用するパッケージ
* github.com/ibmdb/go_ibm_db
* github.com/pkg/errors3.1. go_ibm_db
基本的にGoでDb2を利用する際は、
github.com/ibmdb/go_ibm_db
というパッケージを利用します。以下のコマンドを叩きます。
$ go get github.com/ibmdb/go_ibm_db
またデータベースを操作するにあたって、SQLを操作するためのドライバが必要になります。
色々操作があるので順にやります。まず、落としてきた
github.com/ibmdb/go_ibm_db
を見に行きます。
おそらくGOPATH
配下に落とされていると思うので、こちらの階層を下ると、installer
というフォルダにぶち当たります。
このフォルダ内setup.go
がclidriverのダウンロードスクリプトになっています。$ cd PathToInstaller/installer $ go run setup.goこれでclidriverが
installer
配下にダウンロードできます。(パーミッションエラーが起きた方は、installerフォルダの権限を変えてみてください。)
結構時間がかかる気がします。無事落とせてこれた方は
PathToInstaller/installer/clidriver/bin
のパスを通す必要があるので、通しましょう。
これでgo_ibm_dbのセットアップは完了です。もし余計なパッケージを環境に落としたくないという方は、
go mod
でもできます。
しかしその場合も、sqlcli.h
は必要になりますので、インストールしてきたinstallerをプロジェクトにコピーしてきて、、シェルスクリプトなどで、clidriver/bin
のパスを通し、moduleを指定してビルドすることで実行ファイルを生成できます。3.2. errors
また、エラーの実装もするので、
errors
パッケージも落としましょう。$ go get github.com/pkg/errors
4. Goの実装
基本的に実装は本当に3で紹介した通りです。
main.goのmain関数を見ながら紹介します。まずこのコード
main.goconfig := "HOSTNAME=localhost;DATABASE=USERDB;PORT=50000;UID=db2inst1;PWD=password" conn, err := sql.Open("go_ibm_db", config) if err != nil { fmt.Printf("DBとの接続に失敗しました。%+v", err) } defer conn.Close()configにDB接続情報を格納します。HOSTNAMEとPORT以外はenv.listに乗せてある情報を使います。
その下のsql.Open
でDBとのコネクションを張ります。
一つ目の引数はドライバ名を指定します。今回はgo_ibm_db
です。
二つ目の引数はDB接続情報を指定します。エラーを取りうるので、エラー処理もかかせず行います。
コネクションは必ず終了する必要があるので、Goのプラクティスであるdefer
を使ってコネクションを閉じましょう。これでDb2コンテナとのコネクションが取得できました。
これを利用してデータを操作していきます。まずはユーザーを全件取得して、情報をユーザー構造体に格納し、インスタンスの配列を作っています。
main.gousers, err := model.GetAllUser(conn) if err != nil { fmt.Printf("取得に失敗 %+v", err) }ではユーザーDAOとDTOを定義しているuser.goを見ていきます。
user.go// User is users entity type User struct { id string name string mail string password string createdAt time.Time updatedAt time.Time } func (u *User) String() string { return fmt.Sprintf( "ユーザー名:%s", u.name, ) } // GetID returns user's id func (u *User) GetID() string { return u.id }ユーザー構造体はテーブル定義のカラムをフィールドに定義しています。
GetIDメソッドはユーザーのIDを取得するメソッドです。これは他のテーブルのクエリにIDを渡すためにユーザー構造体のフィールドがプライベートに指定されているため、書いています。
まぁここら辺は他の言語でも似たようなことやると思います。その下、ユーザー全件取得メソッドですが、
user.go// GetAllUser returns all user instances func GetAllUser(conn *sql.DB) ([]User, error) { selectAllUserQuery := `SELECT * FROM users` selectAllUserPstmt, err := conn.Prepare(selectAllUserQuery) if err != nil { return []User{}, errors.Wrapf(err, "ステートメントの作成に失敗しました") } var users []User rows, err := selectAllUserPstmt.Query() if err != nil { return []User{}, errors.Wrap(err, "クエリ実行に失敗") } for rows.Next() { var user User if err := rows.Scan( &user.id, &user.name, &user.mail, &user.password, &user.createdAt, &user.updatedAt, ); err != nil { return []User{}, errors.Wrap(err, "結果読み込み失敗") } users = append(users, user) } return users, nil }ここは色んな書き方があるんですが、Prepare()メソッドでステートメントを用意してから、queryを実行する方法で書きます。
これを実行すると、取れてきたレコードが
rows
に格納されます。
rows
はNextメソッドを持っていて、for文でそれぞれのレコードを回すことができます。
さらにrows.Scan()
にユーザーインスタンスの情報を渡してあげると、そこにレコードの情報を格納してくれます。これで、ユーザー情報をユーザーインスタンスに格納することができました。
ユーザーの配列を返します。それではmainに戻ります。
次からはユーザーインスタンスからIDを取ってきて、Tweetの
WHERE句
に渡して挙げて、ユーザーに紐づくレコードを取ってきています。
取ってきたtweetレコードからさらにIDを取ってきて、それに紐づくReplyを取得し出力、それをユーザーレコード分行うといった処理をしています。main.go// 件数少ないので3重for文で。 for _, user := range users { fmt.Println(user.String()) tweets, err := model.GetAllTweets(conn, user.GetID()) if err != nil { fmt.Printf("取得に失敗 %+v", err) } for _, tweet := range tweets { fmt.Println(tweet.String()) replys, err := model.GetAllReplys(conn, tweet.GetID()) if err != nil { fmt.Printf("取得に失敗", err) } for _, reply := range replys { fmt.Println(reply.String()) } } }
WHERE句
にIDを渡すためにはSQL文をSELECT * FROM Tweets WHERE user_id = ?
のように与えたいパラメータの箇所を?
とします。
パラメータ分第2引数を与えることで、WHERE句
をカスタムできます。書き方は、
rows, err := selectAllTweetPstmt.Query(userID)
このような形です。5. 実行結果
Windowsで実行すると、コンテナから値を受け取ってくる段階で、日本語箇所は文字化けして表示されてしまいます。
Db2で用いているコンテナがLinuxコンテナなので、文字コードがUTF-8のまま文字列が送られてくることに起因していると思われます。実行結果は以下のようになります。
ユーザー名:hoge ツイート本文:�����̓e�X�g�ł��B, 作成日:2020-10-09 12:00:00 +0900 JST リプライユーザー名:fugaaaa, リプライ本文:�e�X�g�m�F���܂����B, 作成日:2020-10-11 12:00:00 +0900 JST ----------------------- ユーザー名:fuga ツイート本文:�����̓e�X�g�ł��B, 作成日:2020-10-10 12:00:00 +0900 JST リプライユーザー名:hogeeee, リプライ本文:�e�X�g�m�F���܂����B, 作成日:2020-10-11 12:00:00 +0900 JST -----------------------まぁめっちゃ文字化けしてますね。
悲しいです。
このままだとあれなんで、Macで実行した結果も載せときます。ユーザー名:hoge ツイート本文:これはテストです。, 作成日:2020-10-09 12:00:00 +0900 JST リプライユーザー名:fugaaaa, リプライ本文:テスト確認しました。, 作成日:2020-10-11 12:00:00 +0900 JST ----------------------- ユーザー名:fuga ツイート本文:これはテストです。, 作成日:2020-10-10 12:00:00 +0900 JST リプライユーザー名:hogeeee, リプライ本文:テスト確認しました。, 作成日:2020-10-11 12:00:00 +0900 JST -----------------------こんな感じで、Db2から取得できています。
6. まとめ
文字コードの弊害がありながらも、GoでDb2コンテナに接続する手法を紹介しました。
これでAPI開発とか楽に行えますね。