はじめに
Nimの記事を24日にアップしようとしてたけど、リアル(仕事)が忙しくて記事が作成できませんでしたので、謹んでお詫び申し上げます。
Nimでの開発するにあたり、Webフレームワークでどんなものがあるのかを調べて見たらJesterというフレームワークがあったので触って見ました。
今回は環境構築と、画面表示までを試して見たのでJesterに興味がある方は是非一度、構築して試して見てください。
Jesterとは
JesterとはRubyのSinatraのようなNimの軽量Webフレームワークです。
https://github.com/dom96/jester
やること
今回はNimのチュートリアルにある以下のマイクロサービスを作っていきます。
https://github.com/nim-lang/Nim/wiki/Tutorial:-Creating-a-(micro)-service
作成したソースはこちら
https://github.com/nemui-fujiu/docker_compose_nim_jester
Docker-Composeの準備
構成はこんな感じ
.
├── README.md
├── docker-compose.yml
├── mysql
├── nginx
│ ├── Dockerfile
│ └── server.conf
├── nim
│ └── Dockerfile
└── work
今回はNim + Nginx + MySQL + Redisの環境を作るのでそれぞれ、必要なimageとDockerfileを用意します。
docker-composeファイルはこんな感じ
version: "3.4"
services:
nginx:
container_name: std_nginx
build:
context: ./nginx/
dockerfile: Dockerfile
links:
- app
volumes:
- ./work/nim_jester:/opt/nim_jester
ports:
- "9090:80"
app:
container_name: std_nim
build:
context: ./nim/
dockerfile: Dockerfile
tty: true
privileged: true
volumes:
- ./work/nim_jester:/opt/nim_jester
links:
- mysql
- redis
mysql:
image: mysql:latest
container_name: std_mysql
volumes:
- ./mysql:/var/lib/mysql
restart: always
environment:
- MYSQL_DATABASE=nim_db
- MYSQL_ROOT_PASSWORD=example
- MYSQL_USER=nim_app
- MYSQL_PASSWOR=nim_app_pw
- MYSQL_ALLOW_EMPTY_PASSWORD=yes
ports:
- "43306:3306"
redis:
image: redis:latest
container_name: std_redis
ports:
- "46379:6379"
DockerやDocker Composeに慣れている人にとっては難しくはないと思います。
今回Nimのソースを好きなエディタでいじれるように、workディレクトリ配下に来るようにしています。
NginxのDockerfileはこんな感じ
FROM nginx:latest
MAINTAINER satoshi.fujiu
ADD ./server.conf /etc/nginx/conf.d/default.conf
RUN apt-get update && apt-get install -y vim curl
WORKDIR /opt/nim_server
サーバ用のconfファイルを追加するのと、あとで操作しやすいようにライブラリを入れておきます。
server {
listen 80;
server_name localhost;
index index.html index.htm
charset utf-8;
# アクセスログの設定
access_log /var/log/nginx/app_access.log;
# エラーログの設定
error_log /var/log/nginx/app_error.log;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# Jester listens on port 5000 by default
proxy_pass http://app:5000;
}
client_max_body_size 8M;
#--------------------
# 静的ファイル設定
#--------------------
# 画像やCSSにはキャッシュの有効期限を設定&アクセスログOFF
location ~ \.(html|css|js|jpe?g|png|gif|svg|mpg|flv|swf)$ {
expires 10d;
access_log off;
break;
}
# .gitや.htaccessにはアクセス禁止
location ~ /\.(ht|git|svn) {
deny all;
}
# faviconへのアクセスは記録しない
location = /favicon.ico {
access_log off;
log_not_found off;
}
# robots.txtへのアクセスは記録しない
location = /robots.txt {
access_log off;
log_not_found off;
}
}
Jesterはデフォルトで5000ポートで起動するのでNginxからproxyを5000ポートに向けてください。
同じサーバ内であればlocalhostで問題ないのですが、今回はnginxとnimのアプリケーションを別にDockerコンテナとして立ち上げるので
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# Jester listens on port 5000 by default
proxy_pass http://app:5000; # <= コンテナをわけないならhttp://localhost:5000を指定する。
}
として、docker-composeで定義した通りにリンク先( http://app:5000 )を指定しておきます。
次にNimのDockerfileはこんな感じ
FROM nimlang/nim:latest
MAINTAINER satoshi.fujiu
RUN apt-get update && apt-get install -y vim curl
RUN nimble -y install jester
WORKDIR /opt/nim_jester
こちらもNginxとほとんど一緒ですが、今回WebフレームワークとしてJesterを使うので、nimbleコマンドでjesterをインストールしておきます。
これで準備完了したので起動して見ましょう。
$ docker-compose up -d --build
プロジェクトの構成
.
└── work
└── nim_jester
├── bin # コンパイル後のソースを配置する。
├── nim_jester.nimble # nimbleの設定
└── src # 実際のソース
├── nim_jester.nim
└── resources
├── conf
│ └── conf.json
└── templates
├── base.tmpl
└── home.tmpl
workディレクトリ配下にソースを作っていきます。
まずはプロジェクトの作成から始めましょう。
nimbleを使ってプロジェクトを作ります。
https://github.com/nim-lang/nimble
nimbleはNimのビルドツールで、パッケージ管理やタスク定義などができます。
タスクはNimScriptで記述します。
イメージとしてはJavaで言う所の、AntやMaven,Gradle
PHPでいうならcomposerとかって感じでしょうか。
今回は環境をDockerで作っているのでまずはDockerに入って作業をしましょう。
「docker ps」コマンドでコンテナを確認すると先ほどDocker Composeで作成したコンテナが並んでいるはずです。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1e72f858790d dockercomp_nginx "nginx -g 'daemon ..." About an hour ago Up About an hour 0.0.0.0:9090->80/tcp std_nginx
7919c797ba80 dockercomp_app "/bin/bash" About an hour ago Up About an hour std_nim
e6e7e27d7efd mysql:latest "docker-entrypoint..." About an hour ago Up About an hour 0.0.0.0:43306->3306/tcp std_mysql
e780d77338fe redis:latest "docker-entrypoint..." About an hour ago Up About an hour 0.0.0.0:46379->6379/tcp std_redis
std_nimがnimアプリケーション用のコンテナになるので、そこで作業します。
$ docker exec -it std_nim bash
/opt/nim_jester#
nimbleは入れてあるので、initコマンドでプロジェクトを作成します。
/opt/nim_jester# nimble init
Info: In order to initialise a new Nimble package, I will need to ask you
... some questions. Default values are shown in square brackets, press
... enter to use them.
Prompt: Package name? [nim_jester]
Answer:
Prompt: Initial version of package? [0.1.0]
Answer:
Prompt: Your name? [Anonymous]
Answer:
Prompt: Package description?
Answer: jester description
Prompt: Package license? [MIT]
Answer:
Prompt: Lowest supported Nim version? [0.17.2]
Answer:
Success: Nimble file created successfully
対話モードにて質問を受けるので適宜質問に回答すれば以下のようなファイルが作成されます。
/opt/nim_jester# ll
-rw-r--r-- 1 root root 165 Dec 30 09:33 nim_jester.nimble
中身を確認して見ましょう。
/opt/nim_jester# cat nim_jester.nimble
# Package
version = "0.1.0"
author = "Anonymous"
description = "jester description"
license = "MIT"
# Dependencies
requires "nim >= 0.17.2"
先ほど入力した値が入っていると思います。
今回はjesterを使ってアプリを作るので、jesterを追記しておきましょう。
合わせて、いくつか記載します。
/opt/nim_jester# vim nim_jester.nimble
# Package
version = "0.1.0"
author = "Anonymous"
description = "jester description"
license = "MIT"
# Dependencies
requires "nim >= 0.17.2"
requires "jester >= 0.2.0" #①
# build dir #②
srcDir = "src"
binDir = "bin"
bin = @["nim_jester"] #③
1.jesterの0.2.0を必須にする設定を記述
requires "jester >= 0.2.0"
2.ビルド用ディレクトリ定義でそれぞれソースディレクトリとコンパイル後ソースの出力先を定義する。
srcDir = "src"
binDir = "bin"
3.実行するmain関数を指定します。
今回はnim_jesterという名前で作っていこうと思っています。
bin = @["nim_jester"]
準備ができたらソースを書いていきましょう。
/opt/nim_jester# mkdir src
/opt/nim_jester# vim src/nim_jester.nim
import jester, posix, json, asyncdispatch, htmlgen, os, osproc, logging
let config_file_name = "bin/resources/conf/conf.json"
let conf = parseFile(config_file_name)
# Traditional logging to file. To use the more featureful journald you might
# use https://github.com/FedericoCeratto/nim-morelogging
let fl = newFileLogger(conf["log_fname"].str, fmtStr = "$datetime $levelname ")
fl.addHandler
# the "debug" and "info" macros from the logging module are not flushing the buffer
proc log_debug(args: varargs[string, `$`]) =
debug args
fl.file.flushFile()
proc log_info(args: varargs[string, `$`]) =
info args
fl.file.flushFile()
include resources/templates/base.tmpl
include resources/templates/home.tmpl
routes:
get "/":
resp base_page(generate_home_page())
get "/hello":
resp h1("Hello world test")
when isMainModule:
log_info "starting"
runForever()
こんな感じでソースを書きます。
コンフィグ設定の読み込み
プロジェクトカレントディレクトリからの相対パスを記述しています。
let config_file_name = "bin/resources/conf/conf.json"
コンフィグファイルをParseして情報を変数へ格納
let conf = parseFile(config_file_name)
コンフィグのログファイル名を取得してロガーの作成
let fl = newFileLogger(conf["log_fname"].str, fmtStr = "$datetime $levelname ")
fl.addHandler
ログの出力をわかりやすくするのにちょっとまとめとく。
proc log_debug(args: varargs[string, `$`]) =
debug args
fl.file.flushFile()
proc log_info(args: varargs[string, `$`]) =
info args
fl.file.flushFile()
画面表示用のテンプレートを読み込んでおく。
includeはソースのカレントディレクトリからのパスになります。
include resources/templates/base.tmpl
include resources/templates/home.tmpl
ルーティングの設定。
HTMLを生成してレスポンスを返すこともできるし、tmplの中身を返すこともできます。
実際には*.tmpl内で各Procの定義がしてあります。
routes:
get "/":
resp base_page(generate_home_page())
get "/hello":
resp h1("Hello world test")
Jesterの起動処理。
when isMainModule:
log_info "starting"
runForever()
次に、tmplファイルとconfファイルを作りましょう。
/opt/nim_jester# mkdir -p src/resources/templates
#? stdtmpl | standard
#proc base_page(content: string): string =
# result = ""
<!DOCTYPE html>
<html lang="en">
<head>
<title>nim_jester</title>
<link rel="shortcut icon" href=""/>
<meta charset="utf-8">
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
</style>
</head>
<body>
<div class="container">
${content}
</div>
</body>
</html>
#? stdtmpl | standard
#proc generate_home_page(): string =
# result = ""
<h5>Welcome to nim_jester</h5>
/opt/nim_jester# mkdir src/resources/conf
{
"log_fname": "/var/log/nim_jester.log",
}
これで一通りの準備が完了です。
それではビルドして見ましょう。
/opt/nim_jester# nimble build
Verifying dependencies for nim_jester@0.1.0
Info: Dependency on jester@>= 0.2.0 already satisfied
Verifying dependencies for jester@0.2.0
Building nim_jester/nim_jester using c backend
こんな感じでビルドが完了します。
実行ファイルが作られていることを確認しましょう。
/opt/nim_jester# ll bin/
total 1368
drwxr-xr-x 3 root root 96 Dec 30 11:18 ./
drwxr-xr-x 5 root root 160 Dec 30 11:16 ../
-rwxr-xr-x 1 root root 1398464 Dec 30 11:18 nim_jester*
これで実行ファイルの準備が整いました。
ただし、このまま起動するとconfファイルが読み込めないので以下のようなエラーになります。
これはsrc配下にconf.jsonを作っているのに、ソース内でbin配下を見ているためです。
/opt/nim_jester# bin/nim_jester
Traceback (most recent call last)
nim_jester.nim(16) nim_jester
json.nim(1234) parseFile
Error: unhandled exception: cannot read from file: bin/resources/conf/conf.json [IOError]
できればソースとコンパイル後のファイルについては別にしたいので、最初からプロジェクトディレクトリのsrc配下を参照しておいてもいいのですが、
せっかくなのでnimbleのタスクでファイルの移動もやってしまいましょう。
nimbleに新たにタスクを追加します。
/opt/nim_jester# vim nim_jester.nimble
# task
task serverstart, "start jester server":
rmDir "bin"
mkDir "bin/resources/conf"
exec "cp -rf src/resources/conf bin/resources/"
exec "nimble build"
exec "bin/nim_jester"
色々試行錯誤したのですが、結果こんな感じになりました。
もっと良い書き方があると思うのですが、パッとかけたのがこれだったので取り合えずこれでリソースファイルの移動と起動もついでに追記してしまいます。
※タスク名にハイフン(-)を入れると正常に動作しないので注意してください。
それでは実行して見ましょう。
/opt/nim_jester# nimble serverstart
Executing task serverstart in /opt/nim_jester/nim_jester.nimble
Verifying dependencies for nim_jester@0.1.0
Info: Dependency on jester@>= 0.2.0 already satisfied
Verifying dependencies for jester@0.2.0
Building nim_jester/nim_jester using c backend
そして、ブラウザから表示を確認する。
http://localhost:9090
正常に表示できることが確認できました!
あとはみなさん好き勝手にNimでのJesterライフをお楽しみください!