
Futureアドベントカレンダー6日目です。昨日は @shun_shushu さんでした。
マイクロサービスまではいかなくても、gRPCなり、Swaggerなりを使って、リッチなSPAのフロントエンドと、いくつかのプロセスに分割されたバックエンドでサービスを開発したい、というニーズはあると思いますので、今までやってきた開発の反省・良かったところを踏まえて、次やるなら絶対にこうする・実際にこうし始めた!というDocker活用案です。
フロント、バックエンドのサービスを種類ごとに書いています。好きなフロントエンドと、好きなバックエンドのレシピを組み合わせて、オリジナルのdocker-compose.ymlを作る、という感じで読んでいただけるように書いています。対象言語とかも増やしたいので、この記事自体、検証結果を受けてどんどん変わっていく予定です。
ソースコードは次のリポジトリに置いておきます。本エントリーでは環境構築周りのみの説明にフォーカスするため、各サービスの実装の詳細までは踏み込みません。気になるところがあればソースを見ていただければと思います。
大前提の整理
フロント寄りの人もいれば、バックエンド寄りな人もいると思うので、僕のバイアスも入っているとは思いますが、開発環境を整備するために、ウェブサービスの2018年末時点の状況を整理しておきましょう。
ウェブの開発はホットリロードが必須
昔と違って、ウェブフロントエンドの開発のためのライブラリとか開発ツールとかもかなり複雑になってきています。また、コード量も大きくなってきており、大量のライブラリを使って開発をしていきます。フロントエンド開発界隈もそれに応じてツールを改善しており、変換結果をメモリ上にキャッシュしつつ、ソースコードの変更を監視して、変更したファイルをすばやく反映してビルドし、ページを開いているブラウザをリロードするというホットリロード、より進んだ例では、ブラウザ上の特定のモジュールのみを置き換えてさらに素早く更新するホットモジュールリプレースメントなどもあります。
このホットリロードや、ホットモジュールリプレースメントには、Node.js上で動く開発サーバというものを動かすのが一般的です。webpack-dev-serverとかその手のやつです。
たとえサーバーをGoとか他の言語で作るにしても、開発環境の整備をするときは、このフロントエンドの開発速度にブレーキをかけることはしてはいけません。
シングルページアプリケーション
React、Vue、Angularでフロントエンド開発の景色がだいぶ変わりました。jQueryで整合性を保ちつつDOMの状態管理をするよりも、これらのフレームワークに任せてしまう方が楽です。そんため、今時であれば、シングルページアプリケーションを避けて作ろうとする方が逆に大変なのではないか、と思います。因果関係は逆転しますが、シングルページアプリケーションでやるのが当然となってきていると思います。
シングルページアプリケーションは、フロントエンド上でURL(として見えるブラウザのアドレスバー)を書き換えつつも、ページリセットをしないで動きます。で、そのタイミングでリロードをかけると、サーバー側でそのページに対応してHTMLを返すようになっていない場合にエラーになってしまいます。シングルページアプリケーションを実装する場合は、サーバー側の協力も不可欠です(URLにハッシュ#とか、クエリーの?が入るダサいシングルページアプリケーションはこの世に存在しないものとします)。
サーバーサイドレンダリング
フロントエンドがReact、Vueの場合は、Next.js、Nuxt.jsを使うと、勝手にサーバーサイドレンダリングが付いてきます。今時はJS/CSSがヘビーなため、そのままだと、必要なリソースが読み込み終わるまで時間がかかりますが、初回表示はサーバー側でHTML/CSSを作って返すために最初に見える表示が早い、というものです。残念ながら、Angularはちょっと大変そうです。
わざわざ手間暇かけてサーバーサイドレンダリングに対応するのは、悪手であるな、とは未だに思っていますが、せっかく勝手に付いてきてパフォーマンスも良いのであれば使わない手はありません。
サーバーサイドレンダリングを効率よく行うには、サーバー側にJavaScriptエンジンが必要です。99%のケースで、これはNode.jsのことを意味しています。ごく稀に、duktapeという軽量なJSエンジンのラッパーライブラリを利用したGoのこともあります。
静的ファイル配信とセキュリティ
フロントエンドの開発サーバーと、APIサーバーを別に起動すると、ローカルでは別ポートになります。ブラウザからみると、ポートが違えば別サーバー(正確には別オリジン)です。別オリジンのウェブサイトからのAPIアクセスは、サーバーリソースの窃盗になりえるため、どのオリジンからのアクセスを明示しないとアクセスを遮断する、という機能がブラウザにはあります。CORS(クロスオリジンリソースシェアリング)というやつです。
みなさん、Real World HTTPはお手元にお持ちですよね?詳しくはP275から書かれています。
よくあるウェブサービスのローカル開発だと、このCORSの設定を開発環境だけ有効にするオプションとかがあったりします。
コンテナ
今時のシステム開発はコンテナというブロックを作って、それを組み合わせて使うのが当たり前になりつつあります。コンテナは、使うOSやら環境やらによって技術的詳細は異なりますが、擬似的なOS環境を作り、同じサービスであってもたくさんのOSのインスタンスが協力して動作するような構成を作ります。
LinuxであればcgroupなどのOS機能を使って仮想OSを作りますし、macOSやWindowsであれば、OSの提供するハードウェア仮想化機能を利用して仮想PCを動かしてLinuxを動かし、その上で仮想OSをたくさん作ります。GKEであればgVisorというサンドボックスミドルウェアの中でアプリを動かしているそうです。AWSも何かRust製のやつを発表していましたね。
みなさん、Goならわかるシステムプログラミングはお手元にお持ちですよね?詳しくはP303から書かれています。
コンテナのセオリーは次の2つです
- 1つのコンテナには1つのプロセス
- 環境変数でシステムの動作がコントロールできるようにコンテナイメージを作成する
コンテナを協調させて動かすには、docker-compose、Kubernetesなどを使います。ローカルだとdocker-composeが便利かと思います。
デプロイだけではなく、開発環境をコンテナで用意するということもあります。
monorepo
マイクロサービス同様に、ツールを細かくプラグインとして分割して切り出し、柔軟性を上げつつ、コアをシンプルに保とう、というのがJavaScript界隈では活発に行われています。その細かく分ける部品数が大量になった時のソフトウェアの構成の手法として広まっているのが、monorepoです。Babelが行なっているやつですね。
もちろん、monorepoにもPros/Consいろいろあります。monorepoを複数サービス入れちゃうのか、単体サービスの構成部品かによっても変わってくるでしょう。現時点でのスナップショットが簡単に手に入る点はメリットで、1つのサービスだけを入れる場合にはConsはほぼないと思います。
複数サービスを1リポジトリに入れると、共通ライブラリ的なものの非互換の更新をどうするか、みたいなのが話題になりますが、どうせ複数リポジトリに分けたとしても、共通ライブラリの中でブランチが生えまくって収集がつかなくなってひどいことになっていくのはすでに経験済みです。無理やりにでも1つにして、非互換をしたくなったらコードをコピーしてv2を作る!ぐらいの気持ちが良いのかなって思っています。マイクロサービスの、ROIは下がってもスループットは死守する、という考えはmonorepoでも同様かと思います。
クラウドサービスの利用
近頃はクラウドサービスを使って開発することが増えています。
例えば、AWSのRDSのように、ローカルのPostgreSQLで代用できるものだったり、エミュレーターが提供されているGCPのDatastoreだったりであれば、ローカル開発環境の構築は難しくはないでしょう。しかし、そういうのが提供されていないサービスの場合、そこだけはリモートで行うなり、クラウドサービスをバイパスするような設定を入れたりする必要があるかもしれません。
最終系
というわけで、最終的に目指す姿を紹介していきます。
フォルダ構成
フォルダ構成はフロントもバックエンドも全部含むmonorepoにします。
+- containers
| +- angular-frontend
| +- golang-api-server
| :
+- docker-compose.yml // 開発環境用
+- docker-compose.prod.yml // 本番環境のイメージテスト用
必要な環境はすべてdocker-compose1つで揃うようにします。
# 開発環境起動
$ docker-compose up
# 本番環境のイメージをビルド
$ docker-compose -f docker-compose.prod.yml build
# 本番環境のイメージをテスト起動
$ docker-compose -f docker-compose.prod.yml up
開発環境
開発環境はローカルのソースコードをDockerコンテナに共有し、実際のビルドはそちらで行います。すべての環境がdocker-composeコマンド1つで行えます。
同一マシンでの実行であっても、Dockerは、仮想PCを実行してLinuxを動かし(mac/Windowsの場合)、その中で実際の処理を行います。docker buildコマンドを叩くと、ローカルのファイルをこの仮想PC上にコピーし、そこで必要なファイルをADDコマンドなどで追加しつつ、イメージを作成していきます。
コンテナは箱の中で動くイメージがありますが、ボリュームという機能を使えば、ローカルの作業フォルダをそのままコンテナ内部にミラーすることができます。ソースコードをコンテナ内部でビルドし、成果物をまたローカルの作業フォルダに戻すこともできます。
今回はサーバーとして起動するので、成果物を取り出すことはありませんが、ローカルのファイルでソースコードを編集しつつ、実行はDocker内のLinuxで行います。ちなみに、AWSとかでDockerを動かせば、ビルドを外部マシンで行わせることもできます。
フロントエンドは必ず、Dev Serverを稼働させ、APIサーバーへのアクセスはそのプロキシ機能を使って後方のAPIサーバーに流すようにします。そうすると、ブラウザからはフロントもバックも、つねに1つのURLでのアクセスになります。CORSとか気にしなくてもよくなります。
注意点としては、最初にローカルのファイルをまとめてコピーしてしまうので、node_modulesとか、中間ファイルのフォルダがあると、docker buildに余計に時間がかかってしまいます。.dockerignoreというファイルを作り、dockerのサーバーに送信しないファイルを記述しておくと良いでしょう。
デプロイ用イメージのローカルテスト
本番環境はECSなり、EKSなり、GCEなり、GKEなり、AKSなりで公開していくとは思いますが、そのイメージ自体はローカルでビルドしてテストもしますよね?それらをdocker-composeでぱっと起動して試すのが良いかと思います。minikubeでローカルのKubernetesでも良いかもしれません(僕はまだ触ったことがないけど、きっとできそう)。
フロントがNext.jsなどのSSRの場合、Node.jsが必要なので、Node.jsを含むコンテナをフロントにおき、このコンテナにすべてを任せます。SSRではなく、静的HTMLが生成される場合は、Nginxと一緒にコンテナにして、静的ファイルを配信します。
裏のサーバーへのアクセスは、開発環境同様に、フロントのコンテナのプロキシ経由にします。フロントが静的HTMLのみの場合でも、裏のメインのサービスにHTMLとJSをバンドルさせ、静的HTML配信設定を行うよりは、開発環境と同様の役責のみを持たせる方がわかりやすいでしょう。ただし、バックエンドがLambda上のExpressで・・・とか、Goのバイナリにバンドルさせたいとか、GAEで・・・という場合は、1つにまとめる必要があるでしょう。
フロントエンド周りのDocker設定
まずはフロントエンドから説明していきます。
Angular
開発環境
まず開発環境ですが、専用のイメージは作らないで、nodeのコンテナイメージをそのまま使ってホットリロードを有効にします。working_dirと同じ名前のところに、ローカルのフロント
APIサーバーなどがあるなら、その名前(servicesの直下の項目名)をlinksに入れていきます。
node_modulesなどのようにぶくぶく太っていくものはvolumesを別途作って割り当てます。
version: '3'
volumes:
node_modules:
services:
angular-frontend:
image: node:lts-slim
command: bash -c "npm install && npm run start"
volumes:
- ./containers/angular-frontend:/front
- node_modules:/front/node_modules
working_dir: /front
ports:
- "4200:4200"
links:
- golang-api-server # APIとか依存するものを書く
Angularのdev-serverは、localhostで起動してしまって、外の世界からアクセスできない(コンテナの外からも)ので、scriptsのstartは修正が必要です。ちょっと前の全体構成の図に書いたように、ブラウザのアクセスはまずこのコンテナが受けて後ろにプロキシするので、それも設定します。
"scripts": {
// --hostと--proxy-configを追加
"start": "ng serve --host 0.0.0.0 --proxy-config proxy.json"
}
Proxyの内容はこんな感じです。ホスト名はdocker-compose.ymlのlinksの名前ですね。/api以下のアクセスは後ろのサービスにフォワードされます。ここでは使っていませんが、URLのリライトもできます。
{
"/api": {
"target": "http://golang-api-server:8080"
}
}
この状態で起動すると(実際にはGo側のサービスがまだ書かれてないので動かないですが)、ローカルのangular-frontend以下のソースを編集するたびに、コンテナ内部でビルドが行われます。ブラウザで表示していると、リロードが行われます。快適ですね!
本番用イメージ
プロダクション用のイメージを起動するdocker-composeファイルはこんな感じです。こちらは単にローカルのDockerfileを読み込んでポートアクセスするだけなのでシンプルです。
version: '3'
services:
angular-frontend:
build:
context: ./angular-frontend
ports:
- "4200:80"
links:
- golang-api-server
本番環境のDockerfileはこんな感じです。マルチステージビルドですが、ビルド側はnode-sassがあってgypとC++コンパイラが必要なのでちょっと複雑です。うまくいけば、ビルド済みバイナリが落ちてきてビルドしなくて済むかもなので、一応コメントアウトしています。実行側は完全にビルドが終わった静的ファイルのみなのでNginxのイメージを使っています。
FROM node:lts-slim as builder
WORKDIR /work
#RUN apt-get update \
# && apt-get install --no-install-recommends -y python build-essential unzip \
# && apt-get clean
#RUN npm install -g node-gyp
ADD package.json .
ADD package-lock.json .
RUN npm ci
ADD angular.json .
ADD tsconfig.json .
ADD src src
ADD libs libs
RUN npm run build
FROM nginx:stable-alpine as runner
RUN apk add --no-cache bash sed coreutils
ADD ./nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /work/dist/env-config public
EXPOSE 80
ENTRYPOINT ["bash", "-c", "nginx -g \"daemon off;\""]
シングルページアプリケーションで必要なのはtry_filesですね。すべてのリクエストが一旦このコンテナにくるので、裏に流す必要があるリクエストはProxyします。Proxy先のホスト名はdocker-compose.prod.ymlのlinksの名前です。
server {
listen 80;
server_name localhost;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
server_tokens off;
location / {
root /public;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}
location /api/ {
proxy_pass http://golang-api-server:8080;
}
}
こちらの環境は最低限のリソースで起動しますが、ホットリロードはしません。
なお、コンテナの場合は環境変数で動作を変えられるようにするのがセオリーです。ですが、フロントエンド周りではビルド時のオプションでビルドする結果の成果物を作り分ける方法、実行時にサーバーに設定を取りに行く方法がよく出てきます。Angularのデプロイの話を調べると、環境変数で切り替えたいけどダメだったという話がよく出てきます。この方法については目処は立っているので、Node.jsのアドベントカレンダーの時にでも別途紹介します(長さが二倍になってしまうので)。
Next.js
ReactやVue.jsをSSRせずに使うのであれば、Angularの説明の応用でいけるでしょう。今回はサーバーサイドレンダリングを行うNext.jsについて説明します。Next.jsの場合、ビルドしてもNode.jsが必要になります。
2019年版: 脱Babel!フロント/JS開発をTypeScriptに移行するための環境整備マニュアルのエントリーの通り、TypeScriptを積極的に使って行く方向なので、本エントリーでもTypeScriptを使っていきます。Next.jsのプロジェクトの基本構成はそれに準拠します(あとでそちらにExpressの話は追記するかも)
nextコマンドを使う方法と、自前のExpress.jsのサーバーにnextの機能を組み込む方法があります。Angularと異なり、nextコマンド単体ではプロキシを設定する機能はありません。BFFとしてNext.jsを使い、HTTPハンドラを組み込むのであればExpress.jsが必要となります。とりあえず、Express.jsを使う方向で実装しておく方が良いでしょう。
+- pages // フロントのページ
+- src // フロントのページ以外の要素(コンポーネントとか)
+- server // サーバーコード
| +- server.ts // サーバー用のエントリーポイント
| +- tsconfig.json // サーバー用のビルド設定
+- package.json // いつもの
+- tsconfig.js // フロントのビルド設定
+- next.config.js // Next.jsの設定
設定ファイルが2つありますね。ビルド時にはフロントだけじゃなくて、ついでにサーバーもビルドされるようにします。
"scripts": {
"postbuild": ""postbuild": "tsc -p server""
}
サーバー側ではいくつかフロントの設定を上書きします。設定については2019年版: 脱Babel!フロント/JS開発をTypeScriptに移行するための環境整備マニュアルのCLIツールの説明とほぼ同じです。
{
"extends": "../tsconfig.json",
"compilerOptions": {
"target": "es2017",
"outDir": "../dist/server",
"declaration": false,
"module": "commonjs",
"noEmit": false
},
"include": ["."]
}
次のコードがnextコマンド同等の最低限のExpressのコードに、プロキシ設定を足したコードです。今回も、/api以下はgolang-api-serverにフォワードします。
import * as express from "express";
import * as next from "next";
import * as proxy from "http-proxy-middleware";
import { IncomingMessage, ServerResponse } from "http";
const dev = process.env.NODE_ENV !== "production";
const PORT = process.env.PORT || 3000;
const app = next({ dir: ".", dev });
const handle = app.getRequestHandler();
const main = async () => {
await app.prepare();
const server = express();
// Proxy Setting
const options = {
target: "http://golang-api-server:8080",
changeOrigin: false,
ws: false // proxy websockets
};
server.use("/api", proxy(options));
// Next.js Handling
server.get("*", (req: IncomingMessage, res: ServerResponse) => {
return handle(req, res);
});
server.listen(PORT, (err: Error) => {
if (err) {
throw err;
}
console.log(`> Ready on http://localhost:${PORT}`);
});
};
main();
もちろん、BFFとしていろいろロジックを足すのも良いでしょう。passportを使って認証のコードを組み込んでもいいし、Express.jsのイベントハンドラを自分で足して、中からisomorphic-unfetchなりaxiosで後方のサービス群を使って自分でリクエストを飛ばして、結果を集約するとかやると「俺/私、BFF開発しているぜ」と自慢できます。
実行に必要なパッケージも入れます。
$ npm install --save express http-proxy-middleware
$ npm install --save-dev @types/express @types/http-proxy-middleware
開発環境
Next.jsを非production環境で実行すると、勝手にフロント関連のコードはホットモジュールリプレースメントになってくれて便利ですが、カスタムのExpressサーバー部分は手動の再起動が必要です。nodemonを使ってサーバー部分もホットデプロイされるようにしましょう。ts-nodeコマンドを使えば、ExpressのコードをTypeScriptで書くことができます。
{
"watch": [
"server.ts",
"server/**/*.ts",
"next.config.js"
],
"execMap": {
"ts": "ts-node --compilerOptions '{\"module\":\"commonjs\"}'"
}
}
必要なコマンドをインストールします。
$ npm install --save-dev nodemon ts-node
"scripts": {
"dev": "nodemon server/server.ts"
}
docker-compose化します。
version: '3'
volumes:
node_modules:
services:
nextjs-frontend:
image: node:lts-slim
command: bash -c "npm install && npm run dev"
volumes:
- ./containers/nextjs-frontend:/front
- node_modules:/front/node_modules
working_dir: /front
ports:
- "3000:3000"
links:
- golang-api-server # APIとか依存するものを書く
これでdocker-compose upでNext.jsのフロントエンドが起動して、フロントのホットモジュールリプレースメント、サーバーのホットリロードが有効になります。ただし、npm installで新しいパッケージの追加までは今は見ていないので、その場合だけ再起動が必要です。
本番イメージ
こちらも、Angular同様、node-sassを使っているので、念のためコメント外せばコンパイラとnode-gypが使えるようにしています。本来はrunnerの方もそういうのが必要ですが、それはまた必要になったら考えます。これでマルチステージビルドで作成できます。
FROM node:lts-slim as builder
WORKDIR /work
#RUN apt-get update \
# && apt-get install --no-install-recommends -y python build-essential unzip \
# && apt-get clean
#RUN npm install -g node-gyp
ADD package.json .
ADD package-lock.json .
RUN npm ci
ADD next.config.js .
ADD .babelrc .
ADD tsconfig.json .
ADD src src
ADD pages pages
ADD server server
RUN npm run build
FROM node:lts-alpine as runner
COPY --from=builder /work/.next .next
COPY --from=builder /work/dist dist
ADD package.json .
ADD package-lock.json .
RUN npm ci --production
EXPOSE 3000
ENTRYPOINT ["node", "dist/server/server.js"]
イメージ確認用のdocker-composeも特段難しいことはありません。環境変数ぐらいですかね。
version: '3'
services:
nextjs-frontend:
build:
context: ./containers/nextjs-frontend
ports:
- "3000:3000"
links:
- golang-api-server
environment:
- NODE_ENV=production
なお、今回のコードの場合は必ずExpress.jsのProxyのミドルウェアを経由してしまうので、ブラウザからのアクセスで後方のウェブサービスに投げる場合はリバースプロキシであらかじめ振り分けてあげればNode.jsの負担が減ります。リバースプロキシはURLのパスを見て振り分けているだけですので、本番環境の場合はAWSとかGCPとかのロードバランサーを使ってあげると良いでしょう。
バックエンドサーバのDocker設定
Go
開発環境
開発環境の方は、以下のエントリーに書かれている内容がすべてですね。
1つのコマンドを実行するだけであれば、わざわざDockerfileを作らなくてもいいのですが、go getと、freshコマンドの2つを実行する&freshは何度も継続実行するのであれば、Dockerfileが必要です。たいした手間ではないですが作っておきます。
FROM golang:1.11.1
WORKDIR /src
COPY . .
ENV GO111MODULE=on
RUN go get github.com/pilu/fresh
CMD ["fresh"]
これを起動するためのdocker-compose.ymlは次の通りです。
version: '3'
services:
golang-api-server:
build:
dockerfile: Dockerfile.dev
context: ./containers/golang-api-server
command: fresh
expose:
- "8080"
ports:
- "8080:8080"
volumes:
- ./containers/golang-api-server:/src
本番イメージ
こちらも、こちらのエントリーにまとめている内容が詳細説明になります。CGO対応でマルチステージビルド
するDockerfileになっています。多少、go modules用に書き換えました。最終成果物の実行ファイルとCGOで必要なランタイム
FROM alpine:latest as builder
RUN apk add --no-cache go \
git \
binutils-gold \
curl \
g++ \
gcc \
gnupg \
libgcc \
linux-headers \
make \
python
ADD . /main
WORKDIR /main
ENV GO111MODULE=on
RUN go build
FROM alpine:latest as runner
RUN apk add --no-cache libc6-compat
COPY --from=builder ./main/main ./
ENTRYPOINT [ "./main" ]
これを使うdocker-composeファイルは次の通りです。
version: '3'
services:
golang-api-server:
build:
context: ./containers/golang-api-server
expose:
- "8080"
ports:
- "8080:8080"
Java (SpringBoot)
OpenJDK11とSpringBoot2でもやってみます。
開発環境
SpringBootは簡単にホットリロードができます。まず、dependenciesの中に、Spring Boot DevToolsを追加します。これはjar起動の時は無効になりますが、gradlewコマンドで起動したときにライブリロードができるようになります。ただし、他の言語のように1コマンドではできず、2コマンドが必要です。
dependencies {
runtimeOnly('org.springframework.boot:spring-boot-devtools')
}
これが開発環境用のdocker-composeです。build –continuousが自動ビルドになります。devtoolsが入っていると、bootRunの部分が再起動を行います。この2つを組み合わせなければなりません。
version: '3'
volumes:
node_modules:
gradle:
java_server_build:
services:
java-api-server:
image: openjdk:11-jdk-slim-sid
expose:
- "8081"
ports:
- "8081:8081"
volumes:
- ./containers/java-api-server:/src
- gradle:/root/.gradle
- java_server_build:/root/build
working_dir: /src
command: bash -c "./gradlew build --continuous & ./gradlew bootRun"
これで、ソースの変更に対してライブリロードが可能です。ブラウザの再起動は主導で行う必要があります。
本番環境
とりあえず簡単バージョンです。Jarの名前にバージョンが入ってしまうのはバッチでビルドするときに不便なので、固定名になるようにします。
bootJar {
archiveName = 'fortune.jar'
}
JDKでビルドして、JREで動かします。OpenJDK11だけはなぜかAlpine版が存在しなかったので、slimを使っています。
FROM openjdk:11.0.1-jdk-slim-sid as builder
WORKDIR /work
ADD build.gradle .
ADD gradlew .
ADD settings.gradle .
ADD gradle gradle
RUN ./gradlew bootJar 2> /dev/null; exit 0
ADD src src
RUN ./gradlew bootJar
FROM openjdk:11.0.1-jre-slim-sid as runner
COPY --from=builder /work/build/libs/fortune.jar .
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "fortune.jar"]
ビルドはいつもの通りです。
version: '3'
services:
java-api-server:
build:
context: ./containers/java-api-server
expose:
- "8081"
ports:
- "8081:8081"
実際には、JDK9以降はjlinkを作ってバイナリサイズを小さくするのがベストプラクティスなようです。
Python (uvicorn + starlette)
Pythonでもやってみます。techempowerで最近上位に入っているStarletteで試してみます。Starletteは、これまでのPythonのフレームワークがよく使っていたウェブのPython共通の通信規格であるWSGIを、Python3で導入された非同期を使って拡張したASGIという規格を使っています。本節では、ASGIサーバーとして、uvicornを使います。
なお、本番環境の方はまだ作成中です。
開発環境
pipenvを使うのでインストールしておいてください。Ubuntuに入っていたのがPython 3.6なので、その前提で行います。もし違っていたら、Pythonのイメージのバージョンを変えてください。
# 環境作成
$ pipenv install
# 必要なライブラリの追加
$ pipenv install starlette uvicorn ujson
ujsonは高速なjsonパーサーです。なくても大丈夫です。
サーバー実装はシンプルです。
from starlette.applications import Starlette
from starlette.responses import UJSONResponse
import uvicorn
import random
app = Starlette()
fortunes = [
"大吉",
"吉",
"中吉",
"小吉",
"末吉",
"凶",
"大凶",
]
@app.route('/api/fortune')
async def homepage(request):
return UJSONResponse({'result': random.choice(fortunes)})
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8000)
npmコマンドみたいにpipenvを起点としてコマンド実行ができるので、Pipfileにスクリプトを追加します。–hostを付けないと、コンテナ外部から使えないので要注意です。–debugをつけると、ホットリロードが有効になります。
[scripts]
dev = "uvicorn --host=0.0.0.0 --debug app:app"
インストールにはもろもろのセットアップが必要で、docker-composeのcmdやらentrypointでは間に合わないため、開発環境用のDockerfileを作ります。
FROM python:3.6-alpine
RUN pip install pipenv
RUN apk add --no-cache build-base
WORKDIR /src
COPY . .
RUN pipenv install
ENTRYPOINT ["pipenv", "run", "dev"]
.venvをビルドコンテキストに含めないように設定を追加します。
.venv
最後にdocker-compose.ymlで起動するように設定します。これでサーバーが起動し、ホットリロードも有効になります。
version: '3'
services:
python-api-server:
build:
dockerfile: Dockerfile.dev
context: ./containers/python-api-server
expose:
- "8000"
ports:
- "8000:8000"
volumes:
- ./containers/python-api-server:/src
本番環境
Nginxも使って設定しますが、ちょっと一週間忙しいので、後日追加予定です。基本的な作戦としてはこんな感じで考えています。
- volumeでuvicornとnginx間でUnixドメインソケットを共有
- nginxコンテナとuvicorn+アプリのコンテナを作って、Unixドメインソケット経由でつなげる
ストレージ周りのDocker設定
ストレージ周りは選択肢が多すぎて、まだ手順化できていないので、基本方針案だけ書いておきます。
ストレージ周りは、PostgreSQLだろうと、MySQLだろうと、気軽にDockerでイメージをダウンロードしてコンテナで起動できます。ですが、マスターや、テストデータなどの初期データのインポートをどうするかはいくつか作戦が考えられます。それぞれ、Pros/Consがあります。
- コンテナの初期化用のエントリーポイントを利用
- 公式のDockerコンテナは
/docker-entrypoint-initdb.d
フォルダ以下にシェルスクリプトなどを置いておくと、起動前にそこのsqlやら.shやら(コンテナの種類によって異なる)を実行します。MySQL、PostgreSQL、MongoDBあたりがサポートしています。
- 公式のDockerコンテナは
- バッチ処理を行うコンテナを作り、docker-compose.ymlで一緒に起動する(インポートが終わったらすぐ終了)
- linksを設定したとしても、docker-composeはlinksは依存先のDBが起動し終わる前に次のバッチのコンテナをすばやく起動してしまうため、ループを回しつつ、DBの準備を完了を待ってコードを書かなければなりません。
- docker-compose.ymlには入れないで、外部からツールを使って入れる
- 外部ツールは文字通り、開発環境のOS用のツールを使ったりもできますし、phpMyAdminのようなウェブサービスをコンテナで動かして使う方法もあります。
- ただし、docker-composeでストレージを含めてすべてのサービスを完全な形で再現する、というのは実現できない。
どのようなデータを入れるかについても、いくつか選択肢があります。
- 初期データもダンプでまとめていれる
- 速度や再現性では一番良いと思われる。各DBイメージはファイルの保存先がREADMEに書かれているので、そのフォルダをあらかじめボリュームに切り出しておき、マウントだけすればインポートもいらない
- スキーマとデータを分けて入れるか
- マイグレーションツールを利用してバージョン管理されたスキーマを入れる方がバージョン管理の面では良いでしょう。
- マスターデータではなく、トランザクション系のテーブルの場合は、スキーマだけ入れれば良いこともある
- Elasticsearchを検索インデックスとして使っているが、マスターのデータがRDBという場合、RDBから取り出して、インデックスを作り直す機能をアプリケーションが持っていた方が、古いデータを気軽に決してリセットしたり、スキーマ変更の再の再生成が捗るでしょう。
インポートのためのツールも、データベース固有のツールを使う手もありますし、ORMとかでいろいろ便利なコードが作られているのであれば、本番コードとインピーダンスミスマッチがなくなるためにチームによっては望ましいとなるかもしれません。
まとめ
いろいろな言語、フレームワーク、ストレージなどが絡んでくるのですが、コンテナという単位で見ていけば怖くはなくなってきますよね。とりあえず、React (Next.js)、Angular、Go/Pythonの4例を紹介しました。今後もExpress.jsのサーバー、Python (Pyramid? Django)の例など、気が向いたら増やしていきます。また、ストレージ周りも具体的なコードにしていきたいです。
なお、現在は「開発環境用」「イメージ確認用」としていますが、「自分が手を加えたいソースコードのところだけはホットデプロイできるように開発モードで起動して、それ以外は省メモリに本番ビルドを使ったイメージを起動」みたいなハイブリッドができてもいいのかなという気がします。単なるymlファイルなので、そういうのを生成するヘルパーツールがあると良いかな、と思いました。
明日は @hichika さんです。
- Golangでの日付のフォーマット指定の方法について
- revelでHello World
- golang の net/http で basic auth
- AWS SDK for Go を使ってS3にファイルをアップロードする
- Goでスレッドセーフなシングルトン
- GolangでSHA-256ハッシュ
- もう目移り。ちょっち迷っている
- テーブルのカラムの値をnullかゼロ値かを判別する
- Go 言語に関するブックマーク
- Goでオブジェクト指向っぽく書く
- Revel templatesを使ったサンプルアプリケーション
- GoでRabbitMQに接続してみる
- CrystalとGoでHTTPサーバのベンチマーク
- Go言語のtestで、json apiのレスポンス検証を行う
- Chefのコマンド一発でサービスを作れる状態へ(CentOS6.6)
- gopherだからgoでgopherしよう
- Go Conference 2015 summer
- gbを知ったのでgojiを使ったWEBアプリケーションプロジェクトを管理してみた
- 「Docker」と新コンテナランタイム「rkt」をサクっと比較してみる
- 開発者から見た UNIX 哲学とコマンドラインツールと Go言語
- Go言語でAmazon Linux AMI向けのビルド
- SpheroをGo言語から制御して遊んでみた
- AWSでnginx+golangが動く環境を作る
- GoのBuild Constraintsに関するメモ
- Go言語(Go-Json-Rest)でAPIサーバーを立てるときのCORS設定 (Basic認証機能付きも)
- 「連結されたgzipを1行ずつ見る」をgolangでやったらハマった
- Goでwebサーバー作るときに考えたこと
- ginのBindingとValidationについての調査メモ
- iOSアプリのライセンス一覧のplistを半自動生成して設定画面にキレイに表示するプログラムをGo言語で作った
- Goのfor rangeで思った値が取れなかった話
- GoのWEBアプリケーション運用について
- Go言語(Go-Json-Rest)のCORSでのハマり。Safariだけでハマった話。
- deploy the MVMs of gae / go
- GolangでAPIを叩く
- Goji のアプリケーションサーバーを Circus で管理する
- 画像処理で写真をアニメ風に変換する
- gvmでgoをバージョン指定で簡単インストール
- golang の進捗どうですか
- Sensuでネットワーク監視やってみた
- Web Frameworkとして検討する言語とフレームワーク
- Golangで簡単にログを吐くことを考える
- Golangで簡単に一時ファイルを吐くことを考える
- go install時にno install location for directory ○○○ outside GOPATHのエラーが出る件について
- BigQuery の課金仕様と注意点をまとめてみた(2015-07 時点)
- 社内リポジトリのサブディレクトリをgo getするにはrepo名に.gitをつける
- gomobileでiOS用のライブラリをビルドするまで
- Gobotを使ってみよう
- Golangで、ResponseWriterを使ってHTTPレスポンスを組み立てる際にハマったこと
- Go の expvar パッケージを使ってアプリケーションのメトリクスを公開する
- Golang開発環境 3分クッキング
- ipから都市情報を取得する
- 文章を非可逆圧縮して容量削減を図る
- 多重起動を禁止する
- Create,NewFile,Open,OpenFileの違い
- Goで64bitと32bitの実行ファイルを同一Windows機で作成するために講じたこと
- やはり俺のgolangがCPUを一つしか使わないのはまちがっている。
- Windowsでtail -f
- x/net/context の実装パターン
- Git.io 短縮 URL を golang コードで取得してみる
- 任意のコマンドをWebサーバー化する
- はじめての Go 言語 (on Windows) その10
- Go言語でCSVの読み書き(sjis、euc、utf8対応)
- Go言語の文字列、バイト、ルーンと文字(翻訳)
- Golangのgormで、PrimaryKeyにUUIDを使う
- tcnksm/gcli を使った golang によるコマンドライン・ツール開発について
- Go言語でプロセス間同期処理
- Golangのgormで、カスタマイズしたtime.Timeに差し変える
- Go言語は空文字に対してstrings.splitを掛けると1要素の配列を返す
- goを1.5にアップデートして1.4とベンチを取る
- runtime.Caller(1)をなめて扱ったら危険かもしれない
- GoでCLIツール作るのに便利そうなパッケージを集めてみました
- Go Mockでインタフェースのモックを作ってテストする #golang
- Goのchannelの送受信用の型について
- そろそろ真面目に Golang 開発環境について考える — GOPATH 汚染問題
- 【Go】 go fmt でコード整形
- Golang による文字エンコーディング変換
- CircleCIでService Account使ってGAE/goへdeployする
- Javaばかり書いている僕がGo言語の勉強で参考になったサイトまとめ
- Go言語の作業ディレクトリをドキュメントに従って作り直してみた
- Go言語のドキュメントに従って簡単なライブラリとテストコードを作ってみた
- 文章からダジャレのみを抜き出すコマンドを作ってみた
- Go言語の自作パッケージに説明をつけてみる その2
- Go言語 http POSTする際のContent-Type
- 【Go】import 書き方まとめ
- go言語でベンチマーク
- Goでビルドバージョン情報を参照できるようにする(Go1.5)
- Golang の文字列連結はどちらが速い?
- そろそろ真面目に Golang 開発環境について考える — Internal Packages と Vendoring
- GoとRedisでアンテナサイトを作ってみた
- Goのバッチで統計を取得するAPIを用意しておくと便利
- Go言語の練習でTODOコマンドを作ってみた
- golangでhttpを監視するscriptを書いて結果をslackにpostする
- Mithril+golang Gin を試す
- モンティ・ホール問題をGoでやってみる
- Go言語でDIを試す
- 文中の人名をイニシャルに変換して匿名化をはかる
- golang+kocha で Web アプリケーションを作ってみて
- Docker の WebUI 作りました
- Alfred Qiita WorkflowをGoで書き直した
- gbでginをrunする
- Go言語版word2vecを作ってみた
- aws-sdk-goを使ってGoからAWS SQSを使う
- 楽ができるGolangのライブラリ達
- コンソールでGithubのReadme.mdを取得するコマンドを作ってみた
- char2vecを作ってみた
- 【Go】print系関数の違い
- Go言語のChannelは送信時にもブロックする
- Goのforとgoroutineでやりがちなミスとたった一つの冴えたgo vetと
- Dockerで独自レポジトリからgo getするお
- goからiOSまで一人でアプリ開発をしてたらいつの間にかマインクラフトエンジニアになった話
- Go 1.5をRaspberry Pi 2で使う
- Goで作ったプログラムでシグナルを受け取る
- 【Go】fmt.Print とビルトイン関数の print の違い
- cgoでMacのスリープログを勝手にとって作業時間の可視化してみた
- GAE/GoのRPCにフックするライブラリaespyを作った
- struct にアノテーションつけてたら go vet . すべき
- Goで多重連想配列
- AppEngine/PythonでDjangaeを試した
- gopherjs + electron テスト
- ISUCON5予選でスコア34000を出す方法
- Go言語の練習用にTwitterのOAuth認証をフルスクラッチしてみた
- Go言語でBigQueryのクエリを実行してみる
- GolangとPEGで作る言語処理系 vol.1
- Go(goquery)でQiitaのスクレイピングライブラリをつくった
- GoでJSONとYAMLを相互変換するツールを作った
- golangでスクレイピングをする準備
- Goroutine の同時実行数を制限する
- github でホストされている go アプリを fork して PR する時には、まず go get から始めよう
- Go言語のflagパッケージより便利というoptargを使ってみる
- Go言語の開発環境セットアップ
- PHP、Node.js、Go のミドルウェアに関する調査
- golangでnestしたstructの初期化がツライ
- go言語のslice操作をまとめてみた(shiftしたりpushしたり)
- sliceのシャッフル
- golangでi18n
- Go言語でチャネルとselect
- GoのEnumイディオム
- Go速習会
- Golangで自分自身で定義したパッケージをインポートする方法あれこれ
- RPG ツクール MV のゲームをブラウザ上でテストする
- ブラウザ上で動作する[開発|実行|チュートリアル|デモ]環境を作成できるCodePicnic
- 今後イケそうなデスクトップGUIフレームワーク
- おい、peco もいいけど fzf 使えよ
- Goで外部コマンドをパイプして実行する
- [Go] ビルドツール gb を使ってみた
- Go言語で簡単にHTTPリクエストを送ってJSONをパースするサンプル
- Go1.5でHTTP2サーバーとクライアント
- GoでXMLをパースする
- [Go] HerokuにRevelでつくったアプリをデプロイする
- [golang] struct の初期化で missing type in composite literal が出た場合の対処
- RubyからGoの関数をつかう → はやい
- UnixListener.Closeでソケットファイルが消えて困っている
- Goフレームワークのパフォーマンス比較
- Goでお手軽に行列の積を爆速並列計算
- Go で UTF-8 の文字列を扱う
- GroovyからGoの関数を使う→はやい
- loggingについて話そう
- GO 1.5 と C++ を SWIG でブリッジさせる方法
- golangでシグナルを拾ってgracefulにgoroutineを停めたい・改
- PHP から Go の関数を呼び出す
- goでDotenvを使用する
- golangでImageMagickを触りたい
- Go でパラメタライズドテストをする
- まだAWSのみに頼って生きてるの?複数のクラウド利用で、大幅コストダウンした話
- 【緩募】Golangでスライスの型のキャストをする方法が知りたい
- ATOM で Go
- GoでJsonファイルを読み込んで構造体として扱う。
- Go付属のツールでオレオレ証明書を生成する
- オブジェクト指向言語としてGolangをやろうとするとハマること
- オブジェクト指向言語としてGolangをやろうとするとハマる点を整理してみる
- “Swift” vs “Swift その2” (vs Go) 戦力比較
- 全くの素人がISUCON5本戦に参加しました
- GoのGUIライブラリshinyを試す #golang
- Goで連鎖性かつスタック指向の言語を実装する
- Tour of Go (Go言語基礎文法最速マスター)日本語版を更新しました #golang
- GoのChannelを使いこなせるようになるための手引
- Go http.RoundTripper 実装ガイド
- Go言語でRedshiftとつなぐ(というかただのPostgreSQL)
- Go言語で REST API + マイクロサービス
- すごく長い行を読む場合の注意点
- golang.org/x/net/websocketの使い方とkuiperbelt
- soracom-sdk-go ベータ版でいろいろ試す
- Google App Engineのリージョン別レイテンシ
- Zaif取引所(Bitcoin/Monacoin)のAPIをGolangから使う
- Goでフィルタコマンドを怠惰に書く
- goのmgoでfindするときの処理
- GolangのOpenGL事情(WebGLも含むよ)
- lazyなGoやリスト演算なGoについて
- Go と reflect と generate
- AWS SDK for Go の基本とRoute53へのHostedZone作成とレコード作成
- Go+Webアプリケーション+CircleCIで静的解析・テスト・バイナリリリースを効率良く行なう
- 外部APIを使うController用のTest Doubleライブラリを書いた
- Exifの回転情報をピクセル情報に反映する
- Go を レンタルサーバーで動かす方法
- GoでHTTP負荷テストフレームワークをつくった
- 1行もGo書いたこと無いけどGo Conference Winter 2015に参加したので資料をまとめた
- 簡易リバースプロキシ「ichigo」でWebアプリ開発が捗る話
- glide – パッケージ管理のお困りの方へ –
- Golangで静的ファイルをバイナリに含めるライブラリを書いてみた
- GoからBLE制御でPLAYBULB Candleの操作をしてみる
- Goのjson.Decoderの使い方
- Go言語でバイトニックソート実装してみた
- Go言語入門 パッケージの作成、便利ツール、interfaceとは。
- PythonのABC – 抽象クラスとダック・タイピング
- 初心者がごるーちんでハマるところ
- GoのMemcacheパッケージ比較
- Go開発向けVim設定
- Go言語に1日で入門したときの頭の使い方の話
- go の regexp が遅いと知らずに regexp で form に fillinするモジュールを書いた
- GoとDockerとMicroservices 最初の一歩
- OS XのネイティブHypervisorを使うxhyveと、ネイティブDockerを立ち上げるdocker-machine-driver-xhyveを作った話
- 手間をかけずに形態素解析を Elasticsearch Service (on AWS) で用意する
- Goで型を挿げ替え可能なデータ構造ライブラリを作る
- Go Web Frameworks 比較
- golangでgitのサブコマンドを作ろう
- golangでrecoverしたときの戻り値
- GAE/GoでSlackの’/’(スラッシュ)コマンドサーバーを立てる
- Goでのシリアル通信でハマった事
- go-swaggerの紹介
- gomobileで日本語フォントを扱ってみる
- Vim で引き籠る
- flag 並にシンプルでより強力な CLI パーサ kingpin の紹介
- GoでGitHub通知をデスクトップ通知として取得するアプリを書いた
- Go言語でRICOH THETA API v2を叩いてみた
- GoでBLEセンサーデータをBeamする
- GunosyでのGoパッケージ構成の歴史
- Packer プラグインの書き方(Provisionerを例に)
- Goでパケットキャプチャを実践してみる
- プロダクト開発でのGoのテストとモック活用事例
- vimでGoのシンタックスチェックをquickrun+vimprocで非同期に行う
- GAE/Goでもgoroutine使おうぜ!というハナシ
- ChromebookでGo!(ChromebookでGo言語を楽しんでみる)
- 本物の golang を… 本物の Gopher を、お見せしますよ。
- コネクションプールのチューニング
- Go、Scala、Clojureで開発する3社が集結!!
- GoとAlefとLimbo
- sidekiq用のmackerelプラグイン書きました
- Goだからこそ許される3つの作法
- Go言語で実現するフォースの覚醒 (Gobotで脳波をなんとかする)
- GoでShared Libraryをビルドしてみた(簡単ドキュメント指向DB)
- はてブ ホットエントリーを一覧表示してくれるCLIツールを書いた
- GoでHTTPサーバを立てる
- 最速という噂のFlatbuffersの速度のヒミツと、導入方法の紹介(Go)
- Go言語でサーボモータをキュイキュイ動かす
- emacsでGoの環境構築をやる
- z.shをGoで書き換え(ようとしてい)る話とGoの学びかた
- GoLearnについて
- GAE/GoとGojiの組み合わせでテストを書く
- Goで3Dモデル変換してプレビュー
- slackのログをAWS(DynamoDB)にためて、検索できるようにした
- vscode-go/READMEとdelve/Buildingの日本語訳
- あるとちょっと便利なコマンドラインツール3つ
- Concurrencyについてm9(^Д^)プギャーられないための基礎知識
- ターミナル画面を勝手に共有して他人の作業を覗いてみる
- Go最後の秘宝「GUI」を探しに行く
- [golang]RevelでCSRFの対策
- Goで小さなScheme、Gigueを実装しました
- Google Cloud Monitoringのカスタムメトリクスを使ってTask Queueを監視したいズラ๑◉ﻌ◉๑
- 気楽にDynamoDBを使おう
- gonp〜Goによるdiffのアルゴリズム実装〜
- Kami – process monitor. inspired by god.rb
- nanogui.goの進捗を記録するスレ
- Goでデーモンを作るにはどうするのが良い?
- IntelliJ で Go ファイル保存時に整形する
- Go + QML + QChart.js で素敵なチャートを表示する
- GoでRESTfulなWebアプリを作りたい (go-restfulを触ってみる)
- importによってパッケージを拡張する方法(プラグイン)
- golang で AB x CD / E – F * G * H = 2016 になる全パターン洗い出し
- go 1.6でhttp.Clientが自動的にhttp/2を使わないようにする方法
- ゲームにおける正しいアイテムドロップ抽選実装
- go-redis/redis のクライアントが PubSub で共用できることを確認した
- 値渡し、ポインタ渡し、参照渡しの違い
- golang coreutils 探しメモ
- 重みをつけてランダムに何か出したい
- iOSエンジニアがGoを使ってみての所感
- CLIのWebsocketクライアントを作ってみた
- GoでhttpリクエストにHostを設定するにはreq.Headerではなくreq.Hostを使う
- Goで帳票をPDFに作成するライブラリ。 請求書などの複雑なフォーマットにも対応
- Web Application周りのテスト方法
- はじめてのGo言語 – インストール〜nginx導入まで –
- Go言語でstructをmapに変換する
- ユニットテストが書きやすい設計〜自家製モックを添えて〜
- Go言語でMongoDB使う ②埋め込みとの相性
- go testの並列(-cpuと-parallel)がなんの事だったか忘れた時のメモ #golang
- database/sql の Rows.Scan で不要なカラムデータを読み捨てる
- Go で簡単に Excelを作成するライブラリ。 色、罫線、網掛けを事前定義済
- 複雑なPDFをテキストファイルから生成。Windows,Mac,Linuxの実行ファイルなので、どの言語でも使用可能
- GoのgRPCでRemote IPを取得する方法
- 複雑なExcelをテキストファイルから生成。Windows,Mac,Linuxの実行ファイルなので、どの言語でも使用可能
- Go 1.6 でポインタをcgoの関数へ渡す際の注意点
- Go1.6でポインタをcgoの関数へ渡す際に発生するcgoCheckPointerを回避する方法
- go言語でゲーム
- 複数のgroutineが連携する通信オブジェクトの設計方針
- GoでANSIエスケープコードを扱うライブラリを作った(色付け・カーソル移動等)
- CentOS6.5にGoをインストールする
- Go で “broken pipe” を無視したい僕達がハマる罠
- iOSアプリの新規レビューを通知してくれるSlack Button Appを作った~手順と申請手続き
- Go で interface {} の中身がポインタならその参照先を取得する
- Go1.6の新機能
- golangの AriticleExtractor “GoOse” を試してみる
- [翻訳+α] Go言語の設定ファイルライブラリ Viper
- net/httpでポート443のHTTPSサーバーを立ち上げるまで
- テキストをカジュアルに暗号化したい
- GolangのGin/bindataでシングルバイナリを試してみた(+React)
- Ruby,Golang,ES2015比較チートシート
- 概観からGoのWebFrameworkを選ぶ(2016/02)
- Go の DB アクセス用のパッケージを作った
- golangの静的解析,カバレッジ解析,バイナリリリースをCircleCIで簡単に整える
- 異なる環境間で private network を構築して接続する
- vim-go-ideでさくっとGolang開発を始める
- ひとりぼっちのサービス開発における技術選択の遷移と戦略
- Golangでparserをつくって、SQLを解析してみよう!
- [golang]GoでbitFlyerのAPIへアクセスしてみた
- Goのfor文で関数のリストを作ろうとして嵌った話
- golang コーディングテストで使うメソッド・テクニックまとめ
- Go のバッファ付きチャネル close の挙動
- Golang: nil Pointer Receiverの話
- 企業動向にアンテナを張るためにQiita Organizationのキュレーションサイトを作った。
- 何も考えずMac上のGit diffでExcelの差分を見れるようにする
- Go言語でプリント文デバッグするときのTips
- HomebrewでGo 1.6を入れる
- ElectronのスタンドアロンMacアプリに無理やりGoのHTTPサーバーを仕込む
- golangでcrontabからドキュメントを作成するコマンドラインツール crondocを作った
- ファイルベースのQiitaクライアントqiitactlを作った話
- Google App Engine SDK for Goを使ってGAE上でアプリを動かすまで
- goroutine 使い方まとめ
- Go言語でGoogle Cloud Vision APIを使う
- GolangでPostgreSQLのJSONデータ型を読み書きする
- Go言語の可変長配列(slice)を使う上での注意
- Go言語で作った実行ファイルを小さくしよう!
- goでスクレイピングするのにgoquery + bluemonday が最強な件
- Go 1.6 templateパッケージ新機能
- go言語でhttpモックサーバを作る方法(2016/02時点)
- RaspberryPi1(2とzeroも)で動かすgolang製アプリをクロスコンパイル(onMac)
- GoでSpreadsheetを操作するパッケージを作った
- VisualStudioCodeでGAE/Goの環境設定
- Golang開発環境構築 in MAC
- GoでGCEからGCSにファイルを書き込む/読み込む
- 10分で終わるGo言語の開発環境構築
- Go言語のスクレイピング系ライブラリまとめ
- vagrant、ansibleでgoの開発環境を構築してみた(所要時間:15分)
- Go+goqueryでGithubRankingにWebスクレイピングを試みる
- Ginの Middleware と HandlerFunc でデータの受け渡し
- vim-goをインストールしてみた(所要時間:15分)
- GoogleAppEngine/Goのインストールから使いやすくするとこまでをさくっと 後編
- ざっくりGoの文法まとめ
- Dockerのbusybox内でgolangのtimeを使ったらエラーになった
- Golangで外部コマンドを実行する方法まとめ
- golangでlogを標準出力とテキストファイルの2箇所の出力する
- ズンドコキヨシまとめ
- Go製のData Shipper Platform「Beats」が良さそうなので紹介してみる
- Go の echo ってWebサーバーでサクッと REST しちゃう
- Go 言語 1つの構造体に複数の validation を適応する
- SensorBeeとは何か?
- あえて aws-sdk-go で dynamoDB を使うときの基本操作
- WebRTCの勉強でむりやりGo使ってみた!
- gometalinter で楽々 lint
- Golangで標準入力がパイプで渡されたものか判定する
- Goのプログラムでどういうリクエストを飛ばしているのか見たい
- Go言語をyumで入れる
- [2016年3月時点]Mac用Electronバイナリに署名してApp Storeにアップするためのメモ
- microservicesの調査に向けて〜Golang製のzipking tracerのgo-kitを利用してみる
- GAE/Go 旧appengineパッケージから新appengineパッケージ(google.golang.org/appengine)への移行ガイド
- nginx+circus+gojiによるgolang webアプリケーションの動作環境構築
- GoでJSONの一部分を利用者が定義した構造体に読み込める便利な手法を見つけた
- CentOSにGo言語をインストール
- sliceの重複チェックを高速化
- go言語初心者が図を書きながらgoroutineやgo channelを理解する(Part1)
- Golang GinでReact.jsのサーバサイドレンダリングを試してみた
- 大谷翔平の打席になったら自動でチャンネルを変える
- CSV から構造化された JSON を生成するツールを Go で作った
- コードレビューに大事な褒め言葉
- 2016年から始める Google App Engine for Go
- 【取得できた!!】IntelliJ IDEAのオープンソースライセンスでUltimate版を無料で使いたい!
- Golang (heroku) で LINE Bot 作ってみる
- GAE/GoでLINE Bot Trial
- LINE BOT をgoで作ってみたよ
- ゴルーチンで発生したエラーの返し方
- cgoを使ったCとGoのリンクの裏側 (1)
- [2016年春版] 静的サイトジェネレーターHugo導入Tips ①インストールとサイトの初期作成
- atomicパッケージが必要な理由と使い方
- go言語初心者が図を書きながらgoroutineやgo channelを理解する(Part2)
- Google App Engine for GoでOAuth2による認証を用意する
- Facebook Bot API + Go でオウム作ってみた
- go言語でのマイクロサービスフレームワークの雑な比較メモ
- Go でコメントアウトせずに素早く複数行スキップさせる
- Angular2 Tour of Heroes with Golang (w/o npm)
- GAE/Go urlfetch.ClientでGetしたらタイムアウトエラー「urlfetch: DEADLINE_EXCEEDED」の解決方法
- phonyでテスト用データを超絶簡単に作る
- Go言語のHello Worldを3分で試す
- Go Conference 2016 Spring
- GoのWebフレームワークのGoji (goji.io) をGAEで動かす
- Go製のフレームワークechoを使ってJSONを返すWebサーバーを作り、GoogleAppEngineで動かす
- Golangで、テニスの大会のエントリーページから、大会情報をクローリングしてくるサンプル
- Arukas.io CLI、まだ β なのに便利だった
- あなたのサーバは本当に安全ですか?今もっともイケてる脆弱性検知ツールVulsを使ってみた
- Goでのログ出力に標準logとcologを使う
- jsonpbはencoding/jsonより5倍遅い
- 他言語から来た人がGoを使い始めてすぐハマったこととその答え
- echoのAPIサーバ実装とエラーハンドリングの落とし穴
- ディープラーニングで「顔が似ているAV女優を教えてくれるbot」を構築
- [golang]GoのSDKで手軽にLINE BOT APIを試してみた
- 「Go言語によるWebアプリケーション開発」でハマったところ
- インタフェースを埋込む #golang
- Go言語のエラーハンドリングについて
- 愛が生まれた
- なんで愛が生まれるのか
- Goで値が文字列のJSONを構造体にパースする
- GolangのフレームワークEchoの話とHelloWorldサンプルを読み解くだけ
- UnityのネイティブプラグインをGoで書く #golang #unity
- Go製WAFのuconを触ってみた
- Terraform for さくらのクラウド
- Negroni + Gorilla/Muxで簡単なサーバーアプリケーション作る
- Goフレームワーク vs PHPフレームワーク
- Go言語で東京メトロAPIを叩く
- Cloud9でGAE/Goの環境構築
- GAE/Goで形態素解析してみた
- MySQLでのトランザクション処理をGolang+dbrで実現してゆく話
- コマンドラインからググれてもいいと思ったので作った
- Kubernetes の Go API クライアントライブラリを使って Kubernetes を直接操作する
- cgoを使ったCとGoのリンクの裏側 (2)
- Terraform for さくらのクラウド スタートガイド(第1回)
- Go言語のエラーハンドリングについて ~panic編~
- Lambda with Apex: Terraformを使った管理
- Amazon Linux に GoLang をインストールし、NginxのWebサーバを構築 & 簡単なHTTPレスポンスを受けるまで
- Gin(Golang)におけるHTMLテンプレート記述方法
- Amazon Linux に GoLang (1.5) をインストール & Gin、MongoDB (3.2) を導入し、APIサーバ を10分で作る
- Go言語の並行処理デザインパターン by Rob Pike 前編
- Go言語の並行処理デザインパターン by Rob Pike 後編
- go1.7 の気になるところを試した
- contextの使い方
- Windows に インストーラを使って GoLang を インストールするときの注意点
- HUGOで作れるCMSっぽいパーツ:関連記事・目次・JSON-LDなど
- Goのjson.Unmarshalで値にJSONのnumber、string両方の可能性がある場合
- GAE/Go+WebPushAPI+FCMでPush通知
- Hugoで新規記事を作成するときのTips的なメモ
- Go + WebDriver でブラウザ操作を自動化する
- Goでjqっぽくinterface{}の中を探索する
- go+ginでローカルで作ったWebアプリをGoogleAppEngineに載せる時に注意することまとめ
- Go+gin+dbrでセッション管理(ログイン・ログアウト)をする話+HTMLテンプレートにセッション情報を埋め込んで制御する
- Apexを使ってGoでlambdaを動かす
- labstack/echoの2.0がreleaseされてたのでAppEngineで動かしてみる
- Go+gin on GAEでBlobstoreを使ってCloudStorageにファイルをアップする話(公式ドキュメント解説を添えて)
- golangでURLをパース
- Goでパスワード入力を読み取る(Windows/Mac/Linux対応)
- Go言語による高速な接尾辞配列の実装(SA-ISの実装)
- Go で作ったゲームを Android/iOS 対応させた話 (1)
- UnityのセーブデータをGoogleAppEngine/Go(with Datastore)に保存したり読み込んだりする話
- ターミナルでスライドを再生するコマンド『pera』を作った。
- Docker Compose で Go の開発環境をサクッと作る
- GoogleスプレッドシートでGopherを描く with Go
- GAE/Go+ginでHTTPリクエストも含めてEnd to Endなテストをする話
- Github on BigQueryを使ってgoでよく使われるpackageを調べる
- Go製のフレームワークechoをHerokuで動かす
- Crystal 言語の 2016年を振り返ってみて
- Go でコードカバレッジを取得する
- 高速でダウンロードできるツールを Go で実装した。
- goでmicroserviceをつくる
- GoでGitHub Issuesのビューアを作った
- goimportsのスキャン対象から特定ディレクトリを除外する
- NHK番組表API を使ってアニメの音楽番組をslackに通知する
- (比較的)標準化をした Vuls/VulsRepo の導入
- NATSについて調べて動作を試してみた
- golang で Evernote のノートを Jekyll 用の HTML に書き出すツールを書いた
- 集合知で各言語、ライブラリ、フレームワーク、DB、アーキテクチャの適材適所をみんなでまとめたら面白いんじゃないか?
- goroutine と channel を使った並行処理のパターン
- Filebeatで複数行を扱うMultiline設定まとめ
- あなたのWebサービスでLDAPユーザ認証を実現するgo-ldapc
- golangで設定記述にTOMLを使う
- Termuxでお手軽ポケットlinux開発環境(例えばnvim+golangが手元に)
- Ubuntu 16.04 に GoLang 1.6 と Gin Web Framework をインストールし、HTMLテンプレートを読み込むまで
- Gin Web Framework と GORM を使って簡単なウェブアプリを作ってみる
- マイクロサービスのフレームワークまとめ [不定期更新](2017/02/25更新)
- WindowsのDLLをGoで作る
- Google Sheets API v4 と サービスアカウントと Go でスプレッドシートを操作する
- textqlが超便利
- CLI作成支援パッケージ Cobra を使い、Go 言語でコマンドラインツールを作ってみる
- データのバージョン管理が可能な分散データベースNomsをさわってみる
- Windows 使いの為の GAE/Go 開発環境構築
- go言語のwebフレームワークechoとpythonのwebフレームワークflaskの速度を比較してみた
- MacでGo開発環境構築
- golangの関数をまとめてみた。
- Go言語でサーバのGraceful Shutdown
- golang メソッドについてまとめてみた。
- #golang CodeReviewComments 日本語翻訳
- IntelliJ CE + Delve で Go 言語のデバッグ環境構築(Mac)
- golangではスタックとヒープを気にする必要が無い
- Go 言語で Websocket を使ったアプリケーションをつくる
- Google App Engine for Go のローカルサーバでデバッグをする #golang #GAE
- Go言語でランダムな文字列を生成する方法の比較
- Heroku への Go 言語製アプリケーションのデプロイと依存パッケージ管理方法の比較
- Docker for Macで開発環境がほんとに早くなったのか?(心の中のモヤ×100がモヤ×99になった)
- GoでAWS SDKを叩くCLIツールを作ってリリースするまでの流れ(aws-sdk-go+cobra+viper+gox+ghr)
- GAE/Goで本番のDatastoreをローカル環境に持ってくる 2016
- golang.org/x/time/rateで速度制限を行う
- インタフェースは型名を公開しなくても実装できる #golang
- ChatGopsで本物のGopherを、すぐそばに…
- 終了したことを他の複数のゴルーチンに伝えるのにチャネルのcloseを使う #golang
- Go で WebSocket を簡単に扱えるパッケージを作った
- else ifにも代入文が書ける #golang
- fmt.Formatterを実装して%vや%+vをカスタマイズしたり、%3🍺みたいな書式をつくってみよう #golang
- Go 言語の http パッケージにある Handle とか Handler とか HandleFunc とか HandlerFunc とかよくわからないままとりあえずイディオムとして使ってたのでちゃんと理解したメモ
- Go言語+Ginで相関項目チェックを実装する(テストコード含む)
- golangで部分文字列を取り出す
- Golang + Echo + MySQL まとめ
- Macでjupyter XXX (iruby, R, nodes, go) インストール
- logmon-goで簡単ログ監視
- Go 言語で JWT を使って認証と認可を低コストに
- Docker+AlpineLinux3.4+Go1.7+Nginxでfreshを使って自動ビルドする開発環境を作る
- Go 言語で Excel ファイル内の文字列を自力で置換する
- golangのioutilべしべし叩いてみた
- Golangのbundleコマンド
- Travis CI から複数ファイルを GitHub Releases にアップロードする
- gRPCを使った簡易的なマイクロサービスを作ってみた
- 競技プログラミングで使うGo言語基礎
- Go 言語 reflect チートシート
- 自宅の使用電力を見える化してみた
- Go WEBフレームワークEchoでmiddlewareを自作する
- gocsvを使ってラクにCSVファイルをつくる
- 全く新しい銀行を1から作るために必要な技術 (Monzo公式ブログより)
- Go 1.7のホスト名前解決の仕様変更
- LINE Developer Trialを GAE/Go で始める #linedevday
- スクレイピング結果をJSONで返すAPIをGAE/Goで作る。
- DatastoreからGetした時に余計なPropertyがある場合エラーになるが無視してもいい
- GoでSlackへ通知してみた
- golangでunix domain socket経由で通信するechoサーバーを書いてみた
- golangにgoto文が存在する理由
- [検証中]更新のあったファイルだけgo testを走らせテストを高速化する #golang #gae
- LINE NotifyをGoで送る #golang #linedevday
- golangで書くunix domain socketを使ったserver/clientのサンプルコード
- Goのpathとfilepathでは動作が異なる Windowsでも正しくパスを扱う
- tmux 上でバッテリーの残量が一目でわかるコマンドを Go で作った
- GoのJSON API ServerでゆるふわJSON responseを返す方法
- GAE/Go で公開鍵暗号を使う JWT 認証サーバーを作った
- Golangでエラー時にスタックトレースを表示する
- Golangでinterface{}を使ったJSONのパース
- Go言語でTodoistのCLIクライアントを作ってみた
- HerokuとGoでLINEの Messaging API環境を作ってみた
- Go のバイナリには -ldflags ‘-w -s’ でコンパイルしてもたくさんパスが埋め込まれていた
- 独自のfmt.Formatterを実装する
- Golang製DBマイグレーションツールgoose + MySQLを試してみた
- Go(golang)+HerokuでLINE Messaging APIに触れてみたよ!
- TwitterはGoでゴー。
- Go+GAE+Cloud Datastoreで簡単なREST APIを構築
- Firebase Realtime Database を Rest API と Golang でいじってみる
- Go言語でハマったNつのこと
- Go言語でJSONに泣かないためのコーディングパターン
- Firebase Notifications からメッセージを送ってみる iOS10版
- GAE/Go (echoフレームワーク)で Line Message API 使って Bot を作る。
- testeratorを使ってgae/goのunit testを高速化する
- Go で Neo Cowsay を作った
- context.Contextでリクエストスコープな値を持ち回す
- echoのv3にアップデートした時に修正した箇所
- オレオレGo入門2016
- 初心者が2016のプログラミングで思うこと
- Go言語でJSONの生成を構造体無しでJavaScript並に簡単に生成できる方法
- JSONをインタラクティブに掘り下げるコマンド jid
- go get しようとしたら SSL protocol error エラーが出るとき
- Go言語でS3にアップロードする時速度制限をかける
- PHPerに贈る、GoでWebサーバー開発環境構築
- GoでのWebアプリケーションの組み立て方
- GolangでGetWildの’89バージョンを演奏する
- boto3からaws-sdk-goにしただけでCPU使用率が75%->10%になった
- GoでSSHサーバにラインエディタが欲しいなら golang.org/x/crypto/ssh/terminal
- Golangでテストしやすいコードをかく
- 冷蔵庫の余った食材からレシピを提案するボットをリリースしました
- 新しく入ったReverse Binding機能を使ってPure GoでAndroidアプリを作ってみる #golang #gomobile
- 社内のバックエンド開発にgRPCを導入してみた
- PocketCHIPでGoのGUIアプリを動かしてみる
- 早明浦ダムの貯水率を見れるコマンド作った
- gojaを使ってGoでJavaScriptの実行を試してみた
- go言語のデバッガ(delveとgdb)とcore dump
- APIライブラリをgo-json(改)で楽に実装する
- VisualStudioCodeでGo言語開発環境を作ってみた
- Echoはver.3で結局のところ何が変わったのか?
- Go言語でファイルアップロードを使ってみる
- macの初期ターミナルでも画像を表示したい
- go-bindataで実行バイナリにWeb UIをバンドルする〜slackboardの場合〜
- 実践的なGAE/Goの構成について #golang #gcpja
- Go!プリンセスプリキュアのGo言語実装「GoPrecure」
- init関数のふしぎ #golang
- Goの構造体の研究
- Goを学びたての人が誤解しがちなtypeと構造体について #golang
- RubyistがGoに入門してDBマイグレーションツールを作った
- Goで真面目にコレクション操作ライブラリを作ってみた
- GoからCのライブラリを呼ぶ
- gorenameをライブラリとして使う #golang
- GitHubで自動生成コードをDiffに表示しない方法
- CとGolangの境界
- LINEBOT SDK GOを使って月曜日のたわわBOTを作った。
- git管理ファイルを透過的に暗号化するgit用フィルタを作った
- GetWildの信号強度を解析し、音の大きさを可視化する
- 秒間500件以上のLOGフローアーキテクチャ
- Go でツール書くときの Makefile 晒す
- GolangでKey-Valueリソースのキャッシュと分散合意によるコミットをする
- GAE/Go で巨大な Zip を扱う
- golangの開発で使っている色々
- Hacker Newsを一覧表示してくれるCLIツールを書いた
- Golangのconst識別子iotaの紹介
- Go言語をMacにインストールしてみる(goenv + direnv + glide)

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。