MinishiftでOpenShift Origin(OKD)を始めると、チュートリアルでNode.jsのアプリケーションをデプロイする例が登場します。
こちらですね。
Minishift Quickstart / Deploying a Sample Application
$ oc new-app https://github.com/openshift/nodejs-ex -l name=myapp
これを、もう少し自分で小さく小さく初めてみたいと思います。
環境
対象の環境は、こちら。
$ minishift status Minishift: Running Profile: minishift OpenShift: Running (openshift v3.9.0+71543b2-33) DiskUsage: 18% of 19G (Mounted On: /mnt/sda1) CacheUsage: 1.946 GB (used by oc binary, ISO or cached images) $ minishift version minishift v1.22.0+7163416
アプリケーション
アプリケーションは、小さく小さく作ってみます。Expressで、ホスト名を返すだけのアプリケーションを作成してみましょう。
Expressのインストール。
$ npm i --save express
バージョン。
"dependencies": { "express": "^4.16.3" }
src/hostname.js const express = require('express'); const app = express(); const os = require('os'); app.get('/hostname', (req, res) => res.send(`this server name = ${os.hostname()}`)); app.listen(8080);
「npm start」でこのサーバーが起動するように、package.jsonを指定しておきます。
"scripts": { "start": "node src/hostname.js" },
アプリケーションの名前は、「node-hostname」としています。
とりあえず、デプロイしてみる
このソースコードをGitリポジトリに登録して、デプロイしてみましょう。次のような感じで、デプロイできます。
$ oc new-app http://....(GitリポジトリのURL) $ oc new-app openshift/nodejs~http://....(GitリポジトリのURL) $ oc new-app http://....(GitリポジトリのURL) -i openshift/nodejs
Routeをexposeして
$ oc expose svc/node-hostname route "node-hostname" exposed
確認。
$ oc get route node-hostname NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD node-hostname node-hostname-myproject.192.168.99.100.nip.io node-hostname 8080-tcp None $ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-1-xg6cbk
OKそうです。
Podを増やしてみる
現状、このDeploymentConfigはひとつのみで動いてますが、
$ oc get dc node-hostname NAME REVISION DESIRED CURRENT TRIGGERED BY node-hostname 1 1 1 config,image(node-hostname:latest) $ oc get pod NAME READY STATUS RESTARTS AGE node-hostname-1-build 0/1 Completed 0 3m node-hostname-1-xg6cb 1/1 Running 0 2m
これをスケールアウトさせてみましょう。
Basic Deployment Operations / Manual Scaling
3つにしてみます。
$ oc scale dc node-hostname --replicas=3 deploymentconfig "node-hostname" scaled
3つになりましたね。
$ oc get dc node-hostname NAME REVISION DESIRED CURRENT TRIGGERED BY node-hostname 1 3 3 config,image(node-hostname:latest) $ oc get pod NAME READY STATUS RESTARTS AGE node-hostname-1-build 0/1 Completed 0 5m node-hostname-1-pjn9n 1/1 Running 0 35s node-hostname-1-ss7pz 1/1 Running 0 35s node-hostname-1-xg6cb 1/1 Running 0 4m
curlでアクセスした際にも、各Podに順々にアクセスされているようです。
$ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-1-ss7pz $ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-1-xg6cb $ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-1-pjn9n $ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-1-ss7pz $ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-1-xg6cb $ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-1-pjn9n
Pod数を減らす時には、「--replicas」を小さい値で指定すればよいです。
$ oc scale dc node-hostname --replicas=1
環境変数を設定してみる
今度は、実行時に使う、環境変数を設定してみましょう。「oc set env」を使うようです。
Managing Environment Variables
アプリケーションを、「ENV_VAR」という名前の環境変数を使うように修正して、デプロイしてみます。
let envVar = process.env.ENV_VAR; if (envVar === null || envVar === undefined) { envVar = 'default env-var'; } app.get('/hostname', (req, res) => res.send(`this server name = ${os.hostname()}, env = [${envVar}]`));
ビルド開始。
$ oc start-build node-hostname
デプロイされました。
$ oc get dc node-hostname NAME REVISION DESIRED CURRENT TRIGGERED BY node-hostname 2 3 3 config,image(node-hostname:latest)
確認。
$ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-2-gxr6r, env = [default env-var]
値が入っていません…と。出力されているのは、未設定時のデフォルト値です。
では、環境変数を設定してみます。
$ oc set env dc node-hostname ENV_VAR=myvalue deploymentconfig "node-hostname" updated
確認。
$ oc set env dc node-hostname --list # deploymentconfigs node-hostname, container node-hostname ENV_VAR=myvalue
すると、再度デプロイが行われ、値が設定されます。この時、DeploymentConfigのリビジョンも+1されます。
$ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-3-jlz5f, env = [myvalue]
環境変数を削除するには、環境変数名の後ろに「-」を付与します。
$ oc set env dc node-hostname ENV_VAR- deploymentconfig "node-hostname" updated
環境変数がなくなり
$ oc set env dc node-hostname --list # deploymentconfigs node-hostname, container node-hostname
アプリケーションも環境変数が設定されていないと認識するようになりました。
$ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-4-wrffz, env = [default env-var]
コンテナに引数を設定してみる
環境変数に加えて、今度は起動引数を設定してみましょう。
Basic Deployment Operations / Executing Commands Inside a Container
…これは、YAMLでないと設定できそうにない感じがします。
プログラムを、起動引数を使うように修正。
let envVar = process.env.ENV_VAR; if (envVar === null || envVar === undefined) { envVar = 'default env-var'; } let argVal; if (process.argv.length > 2) { argVal = process.argv[2]; } else { argVal = 'default arg-val'; } app.get('/hostname', (req, res) => res.send(`this server name = ${os.hostname()}, env = [${envVar}], arg = [${argVal}]`));
Gitリポジトリにcommit/pushして、ビルド。
$ oc start-build node-hostname
確認してみますが、当然ながらこの時点では引数はありません。
$ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-5-7dk5r, env = [default env-var], arg = [default arg-val]
で、YAMLなら設定できそうな感じがするので、「oc edit」で編集。
$ oc edit dc/node-hostname
この場合、「npm」から定義しなおさないといけないようです。spec / containersの配下の、args要素で指定します。
spec: containers: - image: ... imagePullPolicy: Always name: node-hostname ports: - containerPort: 8080 protocol: TCP resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File args: - npm - start - arg1
反映されました。
$ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-8-bmwjx, env = [default env-var], arg = [arg1]
ですがまあ、これは不正解な気がします。
Node.jsのS2Iの場合は、環境変数NODE_RUNを使うのが正解な気が。
$ oc set env dc node-hostname NPM_RUN='start arg1' deploymentconfig "node-hostname" updated $ curl node-hostname-myproject.192.168.99.100.nip.io/hostname this server name = node-hostname-6-5z2vd, env = [default env-var], arg = [arg1]
Node.jsのImageStream?
ところで、Node.jsでアプリケーションを作った時に公開されるポートは、どうやって決まってるんでしょうね?
今回は、Node.jsのver 8を使っています。これのS2I builderのDockerfileを見ると
https://github.com/sclorg/s2i-nodejs-container/blob/master/8/Dockerfile
8080ポートがEXPOSEされています。
EXPOSE 8080
これが元ってことでいいんでしょうか?
ビルドはこちら、
https://github.com/sclorg/s2i-nodejs-container/blob/master/8/s2i/bin/assemble
実行はこちらですね。
https://github.com/sclorg/s2i-nodejs-container/blob/master/8/s2i/bin/run
こういうのを見ると、使われている環境変数がわかってよいですね。
実行はs2i/bin/runを見ると
exec npm run -d $NPM_RUN
なのですが、この「NPM_RUN」環境変数が定義されているのは、Dockerfileになります。
ENV NODEJS_VERSION=8 \ NPM_RUN=start \ NAME=nodejs \ NPM_CONFIG_PREFIX=$HOME/.npm-global \ PATH=$HOME/node_modules/.bin/:$HOME/.npm-global/bin/:$PATH
先の、プログラムの引数を見ていたのは、ここになりますね。
なんとなく、読めるようになってきた気がします。
追記)
この裏取りの方法について、@nekopさんにエントリを書いていただきました。ありがとうございます。
YAMLで書こう
とまあ、ここまでYAMLで書いてきましたが、最後にYAMLにまとめてみたいと思います。
ImageStream。
node-hostname-is.yml
apiVersion: v1 kind: ImageStream metadata: name: node-hostname
作成。
$ oc create -f node-hostname-is.yml imagestream "node-hostname" created
BuildConfig。
node-hostname-bc.yml
apiVersion: v1 kind: BuildConfig metadata: name: node-hostname spec: triggers: - type: ConfigChange - imageChange: {} type: ImageChange source: git: uri: [GitリポジトリのURL] ref: master type: Git strategy: type: Source sourceStrategy: from: kind: ImageStreamTag name: nodejs:8 namespace: openshift output: to: kind: ImageStreamTag name: node-hostname:latest
作成。
$ oc create -f node-hostname-bc.yml buildconfig "node-hostname" created
DeploymentConfig。
node-hostname-dc.yml
apiVersion: v1 kind: DeploymentConfig metadata: name: node-hostname spec: template: metadata: labels: name: node-hostname deploymentconfig: node-hostname spec: containers: - name: node-hostname image: node-hostname:latest imagePullPolicy: Always ports: - containerPort: 8080 protocol: TCP env: - name: ENV_VAR value: my value # - name: NPM_RUN # value: start arg1 args: - npm - start - arg1 restartPolicy: Always replicas: 3 selector: name: node-hostname triggers: - type: ConfigChange - type: ImageChange imageChangeParams: automatic: true containerNames: - node-hostname from: kind: ImageStreamTag name: node-hostname:latest
作成。
$ oc create -f node-hostname-dc.yml deploymentconfig "node-hostname" created
Service。
node-hostname-svc.yml
apiVersion: v1 kind: Service metadata: name: node-hostname spec: ports: - name: 8080-tcp port: 8080 protocol: TCP targetPort: 8080 selector: deploymentconfig: node-hostname
作成。
$ oc create -f node-hostname-svc.yml service "node-hostname" created
Route。
node-hostname-route.yaml
apiVersion: v1 kind: Route metadata: name: node-hostname spec: port: targetPort: 8080-tcp to: kind: Service name: node-hostname
作成。
$ oc create -f node-hostname-route.yaml route "node-hostname" created
と、ocコマンドでやってきたことを、だいたい再現できましたと。
オマケ
YAMLを、全部まとめる場合はこんな感じに。
apiVersion: v1 kind: List items: ## ImageStream - apiVersion: v1 kind: ImageStream metadata: name: node-hostname ## BuildConfig - apiVersion: v1 kind: BuildConfig metadata: name: node-hostname spec: triggers: - type: ConfigChange - imageChange: {} type: ImageChange source: git: uri: http://192.168.0.2:18080/git/root/node-hostname.git ref: master type: Git strategy: type: Source sourceStrategy: from: kind: ImageStreamTag name: nodejs:8 namespace: openshift output: to: kind: ImageStreamTag name: node-hostname:latest ## DeploymentConfig - apiVersion: v1 kind: DeploymentConfig metadata: name: node-hostname spec: template: metadata: labels: name: node-hostname deploymentconfig: node-hostname spec: containers: - name: node-hostname image: node-hostname:latest imagePullPolicy: Always ports: - containerPort: 8080 protocol: TCP env: - name: ENV_VAR value: my value # - name: NPM_RUN # value: start arg1 args: - npm - start - arg1 restartPolicy: Always replicas: 3 selector: name: node-hostname triggers: - type: ConfigChange - type: ImageChange imageChangeParams: automatic: true containerNames: - node-hostname from: kind: ImageStreamTag name: node-hostname:latest ## Service - apiVersion: v1 kind: Service metadata: name: node-hostname spec: ports: - name: 8080-tcp port: 8080 protocol: TCP targetPort: 8080 selector: deploymentconfig: node-hostname ## Route - apiVersion: v1 kind: Route metadata: name: node-hostname spec: port: targetPort: 8080-tcp to: kind: Service name: node-hostname