MySQL
kubernetes
Liberty
ibmcloudprivate

ICPにMySQLに接続するLibertyアプリケーションをデプロイ

お勉強のため、MySQLコンテナにアクセスするWebアプリケーション(WebSphere Liberty)コンテナを作成し、シングルノード構成のIBM Cloud Private CEにデプロイしてみたメモ。

IBM Cloud Private CEのインストールは以下の記事を参照。Kubernetes環境であればMinikubeでもGKEでも何でもよい。

AWSにIBM Cloud Private Community Editionをインストールする

MySQLコンテナの作成

以下を参考にする。

https://kubernetes.io/docs/tasks/run-application/run-single-instance-stateful-application/

PersistentVolume/PesistentVolumeClaimの作成

PVとしてはhostPathで使用するので、ディレクトリーを用意する。

root@myicp01:/export# pwd
/export
root@myicp01:/export# mkdir mysql
root@myicp01:/export# ls -l
total 16
drwxrwxrwx  2 root root 4096 Jul  5 01:25 logstash
drwxr-xr-x 15 root root 4096 Jul  9 04:41 MC_jenkins02
drwxr-xr-x  3 root root 4096 Jul  4 07:15 MC_microclimate02
drwxr-xr-x  2 root root 4096 Jul  9 06:51 mysql
root@myicp01:/export#

MySQLから使用するPesistentVolumeを作成する。

mysql-pv-volume.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
  name: mysql-pv-volume
  labels:
    type: local
spec:
  storageClassName: manual
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/export/mysql"
kubectl apply -f mysql-pv-volume.yaml

PesistentVolumeClaimを作成する。ns3というNamespaceに全てのリソースを作成したいので、metadatanamespace: ns3を記載している。この後のリソースも同様。なお、PVはNamespaceには作成できない。

mysql-pv-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim
  namespace: ns3
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
kubectl apply -f mysql-pv-claim.yaml

Secretの作成

MySQLのコンテナはrootパスワードを環境変数として渡す必要があるので、パスワードを保管するSecretを作成する。

パスワードをBase64でエンコードする。

$ echo -n "password" | base64
cGFzc3dvcmQ=

Secretを作成する。

mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
  namespace: ns3
type: Opaque
data:
  rootPassword: cGFzc3dvcmQ= # base64エンコードしたパスワード
kubectl apply -f mysql-secret.yaml

Deploymentの作成

MySQLのデプロイメントを作成する。先ほど作成したSecretから環境変数を定義している。

mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: ns3
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: rootPassword
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim
kubectl apply -f mysql-deployment.yaml

Serviceの作成

MySQLのServiceを作成する。アプリケーションはこのServiceに接続する。

mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: ns3
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
  clusterIP: None
kubectl apply -f mysql-service.yaml

MySQLコンテナの初期化

MySQLコンテナにユーザーを作成し、テーブルを作成し、初期データをロードする。
本来はJobで実行するのが望ましいかもしれない。ここではここではkubectl execで実施する。

MySQLのPodを確認する。

kubectl get po -n ns3

Podにログインする。

kubectl exec -it -n ns3 <Pod名> bash

mysqlクライアントを実行する。パスワードはSecretに定義したパスワードを入力する。

mysql -u root -p

mydbデータベースを作成する。

create database mydb;

libertyユーザーを作成する。パスワードはliberty

create user 'liberty' identified by 'liberty';

libertyユーザーにmydbへの全権限を付与する。

grant all privileges on mydb.* to 'liberty';

一度抜けてlibertyユーザーで入り直す。

exit;
mysql -u liberty -p

mydbにmemberテーブルを作成する。

use mydb;
DROP TABLE IF EXISTS member;
CREATE TABLE `member` (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
);

テストデータを挿入する。

INSERT INTO member (name) VALUES ("user1");
INSERT INTO member (name) VALUES ("user2");
exit;

Libertyコンテナのデプロイ

Secretの作成

MySQLに接続するユーザーのパスワードを格納するためのSecretを作成する。

パスワードをBase64でエンコードする。

$ echo -n "liberty" | base64
bGliZXJ0eQ==

Secretを作成する。

liberty-mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: liberty-mysql-secret
  namespace: ns3
type: Opaque
data:
  libertyPassword: bGliZXJ0eQ==
kubectl apply -f liberty-mysql-secret.yaml

ConfigMapの作成

パスワード以外のMySQLへの接続情報を格納したConfigMapを作成する。envではなくenvFromを使ってまとめて渡したいので、Keyには環境変数名を使用する。

liberty-mysql-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: liberty-mysql-config
  namespace: ns3
data:
  MYSQL_SERVERNAME: "mysql"
  MYSQL_PORTNUMBER: "3306"
  MYSQL_USER: "liberty"
kubectl apply -f liberty-mysql-config.yaml

Libertyコンテナイメージの作成

MySQLに接続して取得したデータを表示するアプリケーションを乗せたLibertyコンテナを作成する。

説明が面倒なので要点のみ記載。ローカルでMySQLを動かして稼働確認した方がよいでしょう。

Eclipseで「Dynamic Web Project」を作成し、以下のServletを作成。WARファイルとしてエクスポートする。

HelloServlet.java
package com.example;

import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

/**
 * Servlet implementation class HelloServlet
 */
@WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * Default constructor.
     */
    public HelloServlet() {
        // TODO Auto-generated method stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html; charset=SJIS");
        PrintWriter writer = response.getWriter();

        DataSource dataSource = null;
        Connection conn = null;

        try {
            Context context = new InitialContext();
            dataSource = (DataSource) context.lookup("jdbc/mydb");
        } catch (NamingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new ServletException(e);
        }

        try {
            conn = dataSource.getConnection();

            PreparedStatement statement = conn.prepareStatement("select name from member");
            ResultSet result = statement.executeQuery();
            while (result.next()) {
                writer.println(result.getString(1));
                System.out.println(result.getString(1));
            }

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                if (conn != null)
                    conn.close();
            } catch (SQLException e) {
            }
        }

        writer.append("Served at: ").append(request.getContextPath());
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
     *      response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

WARファイルをエクスポートしたディレクトリーに、MySQLのJDBCドライバーのjarファイルを配置する。

https://dev.mysql.com/downloads/connector/j/

このLibertyのserver.xmlを配置する。MySQLへの接続情報は環境変数で渡すようにする。

server.xml
<server description="new server">

    <!-- Enable features -->
    <featureManager>
        <feature>webProfile-7.0</feature>
        <feature>localConnector-1.0</feature>
    </featureManager>

    <!-- To access this server from a remote client add a host attribute to 
        the following element, e.g. host="*" -->
    <httpEndpoint host="*" httpPort="9080" httpsPort="9443"
        id="defaultHttpEndpoint" />

    <library id="MySQLLib">
        <fileset dir="resources/mysql" includes="*.jar" />
    </library>

    <dataSource jndiName="jdbc/mydb" transactional="false">
        <jdbcDriver libraryRef="MySQLLib" />
        <properties databaseName="mydb" serverName="${env.MYSQL_SERVERNAME}"
            portNumber="${env.MYSQL_PORTNUMBER}" user="${env.MYSQL_USER}" password="${env.MYSQL_PASSWORD}" />
    </dataSource>

    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true" />

    <applicationMonitor updateTrigger="mbean" />

</server>

同じディレクトリーにDockerfileを作成する。

FROM websphere-liberty:webProfile7
COPY server.xml /config/
RUN installUtility install --acceptLicense defaultServer
COPY mysql-connector-java-8.0.11.jar /config/resources/mysql/
COPY hellomysql.war /config/dropins/

このディレクトリーでDockerビルドを実行し、DockerHubにPushする。

docker build -t hellomysql:0.1 .
docker tag hellomysql:0.1 sotoiwa540/hellomysql:0.1
docker push sotoiwa540/hellomysql:0.1

Libertyコンテナのデプロイ

Libertyコンテナをデプロイする。

hellomysql-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hellomysql
  namespace: ns3
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: hellomysql
    spec:
      containers:
      - name: hello
        image: sotoiwa540/hellomysql:0.1
        imagePullPolicy: Always
        ports:
        - containerPort: 9080
        env:
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: liberty-mysql-secret
              key: libertyPassword
        envFrom:
        - configMapRef:
            name: liberty-mysql-config
kubectl apply -f hellomysql-deployment.yaml

Serviceの作成

Serviceを作成する。

hellomysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hellomysql
  namespace: ns3
spec:
  type: ClusterIP
  selector:
    app: hellomysql
  ports:
  - protocol: TCP
    port: 9080
kubectl apply -f hellomysql-service.yaml

Ingressの作成

Ingressを作成する。

hellomysql-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hellomysql
  namespace: ns3
  annotations:
    ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host:
    http:
      paths:
      - path: /hellomysql
        backend:
          serviceName: hellomysql
          servicePort: 9080
kubectl apply -f hellomysql-ingress.yaml

稼働確認

以下のURLにアクセスし、アプリケーションがMySQL内のデータを取得して表示することを確認する。

https://mycluster.icp/hellomysql/hellomysql/HelloServlet