CLOVER🍀

That was when it all began.

OKD/Minishift上に、Node.jsとMySQL(Template)を使ったアプリケーションを作る

前回、OKD(OpenShift Origin)上にNode.jsとMySQLのImageを使ったアプリケーションをデプロイしてみました。

OKD/Minishift上に、Node.jsとMySQL(Image)を使ったアプリケーションを作る - CLOVER🍀

今度は、MySQLをTemplateを使ってデプロイして、Node.jsで作成したアプリケーションと組み合わせて動かしてみます。

環境

今回の環境は、こちら。

## Minishift
$ minishift version
minishift v1.23.0+91235ee


## OKD
$ oc version
oc v3.10.0+dd10d17
kubernetes v1.10.0+b81c8f8
features: Basic-Auth GSSAPI Kerberos SPNEGO

Server https://xxx.xxx.xx.xxx:8443
openshift v3.10.0+ddf284b-16
kubernetes v1.10.0+b81c8f8

OKD自体のバージョンも、3.10.0です。

MySQLのTemplateを使う

このエントリでは、以下のエントリで作成したNode.jsのソースコードを少し修正して、OKDのMySQL Templateを使ってアプリケーションを作ってみます。

OKD/Minishift上に、Node.jsとMySQL(Image)を使ったアプリケーションを作る - CLOVER🍀

MySQLのTemplateについては、こちら。

MySQL

Creating a Database Service from a Template

今回は、MySQLのEphemeralなTemplateを使ってみます。デフォルトではOKDに入っていないので、こちらから取得。
https://github.com/openshift/origin/blob/v3.10.0/examples/db-templates/mysql-ephemeral-template.json

取り込み先は、自namespaceとしました。

$ oc create -f https://raw.githubusercontent.com/openshift/origin/v3.10.0/examples/db-templates/mysql-ephemeral-template.json
template.template.openshift.io "mysql-ephemeral" created

Templateでどのようなパラメータが使えるかは、「oc process」で確認できます。

$ oc process --parameters mysql-ephemeral
NAME                    DESCRIPTION                                                             GENERATOR           VALUE
MEMORY_LIMIT            Maximum amount of memory the container can use.                                             512Mi
NAMESPACE               The OpenShift Namespace where the ImageStream resides.                                      openshift
DATABASE_SERVICE_NAME   The name of the OpenShift Service exposed for the database.                                 mysql
MYSQL_USER              Username for MySQL user that will be used for accessing the database.   expression          user[A-Z0-9]{3}
MYSQL_PASSWORD          Password for the MySQL connection user.                                 expression          [a-zA-Z0-9]{16}
MYSQL_ROOT_PASSWORD     Password for the MySQL root user.                                       expression          [a-zA-Z0-9]{16}
MYSQL_DATABASE          Name of the MySQL database accessed.                                                        sampledb
MYSQL_VERSION           Version of MySQL image to be used (5.5, 5.6, 5.7, or latest).                               5.7

もとのJSONファイル(もしくはYAMLファイルなど)から、直接確認してもよいでしょう。

$ oc process --parameters -f https://raw.githubusercontent.com/openshift/origin/v3.10.0/examples/db-templates/mysql-ephemeral-template.json
NAME                    DESCRIPTION                                                             GENERATOR           VALUE
MEMORY_LIMIT            Maximum amount of memory the container can use.                                             512Mi
NAMESPACE               The OpenShift Namespace where the ImageStream resides.                                      openshift
DATABASE_SERVICE_NAME   The name of the OpenShift Service exposed for the database.                                 mysql
MYSQL_USER              Username for MySQL user that will be used for accessing the database.   expression          user[A-Z0-9]{3}
MYSQL_PASSWORD          Password for the MySQL connection user.                                 expression          [a-zA-Z0-9]{16}
MYSQL_ROOT_PASSWORD     Password for the MySQL root user.                                       expression          [a-zA-Z0-9]{16}
MYSQL_DATABASE          Name of the MySQL database accessed.                                                        sampledb
MYSQL_VERSION           Version of MySQL image to be used (5.5, 5.6, 5.7, or latest).                               5.7

「oc describe」でもOKです。

$ oc describe template mysql-ephemeral
Name:		mysql-ephemeral
Namespace:	myproject
Created:	2 minutes ago
Labels:		<none>
Description:	MySQL database service, without persistent storage. For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/5.7/root/usr/share/container-scripts/mysql/README.md.
		
		WARNING: Any data stored will be lost upon pod destruction. Only use this template for testing
Annotations:	iconClass=icon-mysql-database
		openshift.io/display-name=MySQL (Ephemeral)
		openshift.io/documentation-url=https://docs.openshift.org/latest/using_images/db_images/mysql.html
		openshift.io/long-description=This template provides a standalone MySQL server with a database created.  The database is not stored on persistent storage, so any restart of the service will result in all data being lost.  The database name, username, and password are chosen via parameters when provisioning this service.
		openshift.io/provider-display-name=Red Hat, Inc.
		openshift.io/support-url=https://access.redhat.com
		tags=database,mysql

Parameters:		 
    Name:		MEMORY_LIMIT
    Display Name:	Memory Limit
    Description:	Maximum amount of memory the container can use.
    Required:		true
    Value:		512Mi

    Name:		NAMESPACE
    Display Name:	Namespace
    Description:	The OpenShift Namespace where the ImageStream resides.
    Required:		false
    Value:		openshift

    Name:		DATABASE_SERVICE_NAME
    Display Name:	Database Service Name
    Description:	The name of the OpenShift Service exposed for the database.
    Required:		true
    Value:		mysql

    Name:		MYSQL_USER
    Display Name:	MySQL Connection Username
    Description:	Username for MySQL user that will be used for accessing the database.
    Required:		true
    Generated:		expression
    From:		user[A-Z0-9]{3}

    Name:		MYSQL_PASSWORD
    Display Name:	MySQL Connection Password
    Description:	Password for the MySQL connection user.
    Required:		true
    Generated:		expression
    From:		[a-zA-Z0-9]{16}

    Name:		MYSQL_ROOT_PASSWORD
    Display Name:	MySQL root user Password
    Description:	Password for the MySQL root user.
    Required:		true
    Generated:		expression
    From:		[a-zA-Z0-9]{16}

    Name:		MYSQL_DATABASE
    Display Name:	MySQL Database Name
    Description:	Name of the MySQL database accessed.
    Required:		true
    Value:		sampledb

    Name:		MYSQL_VERSION
    Display Name:	Version of MySQL Image
    Description:	Version of MySQL image to be used (5.5, 5.6, 5.7, or latest).
    Required:		true
    Value:		5.7


Object Labels:	template=mysql-ephemeral-template

Message:	The following service(s) have been created in your project: ${DATABASE_SERVICE_NAME}.
		
		       Username: ${MYSQL_USER}
		       Password: ${MYSQL_PASSWORD}
		  Database Name: ${MYSQL_DATABASE}
		 Connection URL: mysql://${DATABASE_SERVICE_NAME}:3306/
		
		For more information about using this template, including OpenShift considerations, see https://github.com/sclorg/mysql-container/blob/master/5.7/root/usr/share/container-scripts/mysql/README.md.

Objects:		 
    Secret		${DATABASE_SERVICE_NAME}
    Service		${DATABASE_SERVICE_NAME}
    DeploymentConfig	${DATABASE_SERVICE_NAME}

Template?

と、ここまでTemplateというものが、なんなのかに触れないまま進んできました。

TemplateについてのOKDのドキュメントは、こちら。

Templates

Templateというのは、以下の説明にあるようにOKDによって作成されるオブジェクト(のリスト)を、パラメータ化して定義したものです。

A template describes a set of objects that can be parameterized and processed to produce a list of objects for creation by OKD. A template can be processed to create anything you have permission to create within a project, for example services, build configurations, and deployment configurations. A template may also define a set of labels to apply to every object defined in the template.

https://docs.okd.io/3.10/dev_guide/templates.html

ServiceやBuildConfig、DeploymentConfigなどを作成したり、各オブジェクトに付与するラベルを事前に定義したりできます。これをテンプレートにして、
「oc new-app」でパラメータを設定して実行すると、定義してあるオブジェクト群が一式できあがります、と。

例えば、先のMySQL Ephemeralについて言うと
https://github.com/openshift/origin/blob/v3.10.0/examples/db-templates/mysql-ephemeral-template.json

Secret、Service、DeploymentConfigが定義されており、先程「oc process」などで見たパラメータを使ってテンプレートとして構成されています。

MySQLをデプロイする

それでは、まずはMySQLをデプロイしてみましょう。以下のコマンドで、MySQLをデプロイします。名前は、「simple-mysql」としました。

$ oc new-app mysql-ephemeral --name=simple-mysql -p MYSQL_USER=kazuhira -p MYSQL_PASSWORD=password -p MYSQL_DATABASE=practice -p MYSQL_VERSION=5.7 -p DATABASE_SERVICE_NAME=simple-mysql

Secretは、こんな感じになっています。

$ oc get secret/simple-mysql -o yaml
apiVersion: v1
data:
  database-name: cHJhY3RpY2U=
  database-password: cGFzc3dvcmQ=
  database-root-password: dmZkR3BUdE5yQWZqUzhCTw==
  database-user: a2F6dWhpcmE=
kind: Secret
metadata:
  annotations:
    openshift.io/generated-by: OpenShiftNewApp
    template.openshift.io/expose-database_name: '{.data[''database-name'']}'
    template.openshift.io/expose-password: '{.data[''database-password'']}'
    template.openshift.io/expose-root_password: '{.data[''database-root-password'']}'
    template.openshift.io/expose-username: '{.data[''database-user'']}'
  creationTimestamp: 2018-09-01T10:23:34Z
  labels:
    app: simple-mysql
    template: mysql-ephemeral-template
  name: simple-mysql
  namespace: myproject
  resourceVersion: "25403"
  selfLink: /api/v1/namespaces/myproject/secrets/simple-mysql
  uid: 14cbcec3-add1-11e8-9b07-5254000c2420
type: Opaque

Node.jsのアプリケーションをデプロイする

アプリケーションは、こちらのエントリとほぼ同じものを使います。

OKD/Minishift上に、Node.jsとMySQL(Image)を使ったアプリケーションを作る - CLOVER🍀

アプリケーションのデプロイ。名前は、「node-mysql」としています。

$ oc new-app [GitリポジトリのURL]

環境変数は、Secretから取り込むことにします。

$ oc set env --from=secret/simple-mysql dc/node-mysql

このSecretとTemplateのMySQLから作成したServiceによる環境変数を使うソースコードは、こちら。
src/server.js

const express = require('express');
const mysql = require('promise-mysql');

const os = require('os');

const app = express();

app.get('/select-now', async (req, res) => {
    const connection = await mysql.createConnection({
        host: process.env.SIMPLE_MYSQL_SERVICE_HOST,
        port: process.env.SIMPLE_MYSQL_SERVICE_PORT,
        user: process.env.DATABASE_USER,
        password: process.env.DATABASE_PASSWORD,
        database: process.env.DATABASE_NAME
    });

    const rows = await connection.query('select now() as message');

    res.send(`${rows[0]['message']} from ${os.hostname()}`);
});

app.listen(8080);

あとは、Routeを作成。

$ oc expose svc/node-mysql

スケールアウト。

$ oc scale dc/node-mysql --replicas=3

確認。アクセスごとに、ホスト名が切り替わっていきます。

$ curl node-mysql-myproject.192.168.42.215.nip.io/select-now
Sat Sep 01 2018 10:48:37 GMT+0000 (UTC) from node-mysql-2-nhzzm

アプリケーションに対して、Secretからの環境変数はこんな感じに入っています。

$ oc get dc/node-mysql -o jsonpath='{.spec.template.spec.containers[0].env}' 

出力結果(ちょっと整形)。

[
  map
    [name:DATABASE_ROOT_PASSWORD
      valueFrom:map[secretKeyRef:map[name:simple-mysql key:database-root-password]]
    ]
  map
    [name:DATABASE_USER
      valueFrom:map[secretKeyRef:map[name:simple-mysql key:database-user]]
    ]
  map
    [name:DATABASE_NAME
      valueFrom:map[secretKeyRef:map[name:simple-mysql key:database-name]]
    ]
  map
    [name:DATABASE_PASSWORD
      valueFrom:map[secretKeyRef:map[name:simple-mysql key:database-password]]
    ]
]

Secretの内容が、環境変数にちょっと名前を変えて入ったみたいですね。

これで、やりたいことはできました、と。

Web Consoleで一括でSecretを取り込んだ場合

こういう定義になるようです。

$ oc get dc/node-mysql -o jsonpath='{.spec.template.spec.containers[0].envFrom}' 
[map[secretRef:map[name:simple-mysql]]]

YAMLだと、こんな感じ。

spec:
  ...
  template:
    metadata:
      ...
    spec:
      containers:
      - envFrom:
        - secretRef:
            name: simple-mysql

ただ、この方法で取り込むと、環境変数名としては微妙な名前のまま取り込まれてしまうものがあるので、その点には注意です。

例えば、今回のMySQL Ephemeralから取り込んだSecretは、こんな感じに環境変数に展開されます。

$ oc rsh dc/node-mysql
sh-4.2$ env | grep database
database-root-password=xxxxx
database-name=xxxxx
database-user=xxxxx
database-password=xxxxx

さっきの例みたいな、「DATABASE_USER」とかとはちょっと違いますね。

YAMLで書こう

最後に、ここまでの定義をYAMLで書いてみようと思います。

が、Templateの部分って、これをYAML化するんだったら、それぞれ個別に定義するに他ならない気が…。

というわけで、Node.jsのアプリケーションの方だけ。

application-resources.yml

---
## ImageStream
apiVersion: v1
kind: ImageStream
metadata:
  labels:
    app: node-mysql
  name: node-mysql

---
## BuildConfig
apiVersion: v1
kind: BuildConfig
metadata:
  labels:
    app: node-mysql
  name: node-mysql
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-mysql:latest

---
## DeploymentConfig
apiVersion: v1
kind: DeploymentConfig
metadata:
  labels:
    app: node-mysql
  name: node-mysql
spec:
  replicas: 3
  selector:
    app: node-mysql
    deploymentconfig: node-mysql
  template:
    metadata:
      labels:
        app: node-mysql
        deploymentconfig: node-mysql
    spec:
      containers:
      - name: node-mysql
        image: node-mysql:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          protocol: TCP
        env:
        - name: DATABASE_USER
          valueFrom:
            secretKeyRef:
              key: database-user
              name: simple-mysql
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
              key: database-password
              name: simple-mysql
        - name: DATABASE_NAME
          valueFrom:
            secretKeyRef:
              key: database-name
              name: simple-mysql
      restartPolicy: Always
  triggers:
  - type: ConfigChange
  - type: ImageChange
    imageChangeParams:
      automatic: true
      containerNames:
      - node-mysql
      from:
        kind: ImageStreamTag
        name: node-mysql:latest

---
## Service
apiVersion: v1
kind: Service
metadata:
  labels:
    app: node-mysql
  name: node-mysql
spec:
  ports:
  - name: 8080-tcp
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: node-mysql
    deploymentconfig: node-mysql

---
## Route
apiVersion: v1
kind: Route
metadata:
  labels:
    app: node-mysql
  name: node-mysql
spec:
  port:
    targetPort: 8080-tcp
  to:
    kind: Service
    name: node-mysql

ほぼ前回のエントリと同じ内容なのですが、環境変数の定義だけが違いますね。

    spec:
      containers:
      - name: node-mysql
        image: node-mysql:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
          protocol: TCP
        env:
        - name: DATABASE_USER
          valueFrom:
            secretKeyRef:
              key: database-user
              name: simple-mysql
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
              key: database-password
              name: simple-mysql
        - name: DATABASE_NAME
          valueFrom:
            secretKeyRef:
              key: database-name
              name: simple-mysql

こんな感じで。