MySQL
kubernetes
Liberty
ibmcloudprivate

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

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

IBM Cloud Private CEのインストールは以下の記事を参照。Kubernetes環境であればIKS(IBM Cloud Kubernetes Service)でもMinikubeでもDocker for Macでも何でもよいはず。

AWSにIBM Cloud Private-CE 2.1.0.3をインストールする

AWSにIBM Cloud Private-CE 3.1をインストールする

ClusterImagePolicyの作成

ICP 3.1ではデフォルトではイメージをPullできるレジストリーが制限されているため、DockerHubからのイメージのPullを許可する。ICP 2.1.0.3では不要。

コンテナー・イメージ・セキュリティーの適用

allow-dockerhub-image-policy.yaml
apiVersion: securityenforcement.admission.cloud.ibm.com/v1beta1
kind: ClusterImagePolicy
metadata:
  name: allow-dockerhub-image-policy
spec:
  repositories:
  # Docker hub Container Registry
  - name: "docker.io/*"
    policy:

Namespaceの作成

今回のアプリケーションはsugiというNamespaceに作成することにする。そのNamespaceを作成する。

kubectl create ns sugi

(オプション)
kubectlが操作するデフォルトのNamespaceを作成したNamespaceに変えておく。

ubuntu@icp31-single:~$ kubectl config current-context
mycluster-context
ubuntu@icp31-single:~$ kubectl config set-context mycluster-context --namespace=sugi
Context "mycluster-context" modified.
ubuntu@icp31-single:~$

MySQLコンテナのデプロイ

以下を参考にする。

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

PV/PVCの作成

今回は簡単に試すためPVとしてはhostPathで使用するので、ディレクトリーを用意する。NFSが利用できればその方が望ましい。

root@icp31-single:~# mkdir -p /data/mysql
root@icp31-single:~# ls -l /data
total 4
drwxr-xr-x 2 root root 4096 Oct 23 10:45 mysql
root@icp31-single:~#

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

mysql-pv.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
  name: mysql-pv
spec:
  storageClassName: mysql
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: "/data/mysql"
kubectl apply -f mysql-pv.yaml

PesistentVolumeClaimを作成する。なお、PVは全てのNamespaceで共通のリソースであるが、PVCはNamespace毎に作成するリソースである。

mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pvc
spec:
  storageClassName: mysql
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
kubectl apply -f mysql-pvc.yaml -n sugi

PVCがBoundになったことを確認しておく。

ubuntu@icp31-single:~$ kubectl get pvc -n sugi
NAME        STATUS    VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mysql-pvc   Bound     mysql-pv   10Gi       RWO            mysql          1m
ubuntu@icp31-single:~$

Secretの作成

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

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

ubuntu@icp31-single:~$ echo -n "password" | base64
cGFzc3dvcmQ=
ubuntu@icp31-single:~$

Secretを作成する。

mysql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  root-password: cGFzc3dvcmQ=
kubectl apply -f mysql-secret.yaml -n sugi

Deploymentの作成

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

mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - image: mysql:5.7
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-secret
              key: root-password
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pvc
kubectl apply -f mysql-deployment.yaml -n sugi

Podが稼働したことを確認する。

ubuntu@icp31-single:~$ kubectl get po -n sugi
NAME                     READY     STATUS    RESTARTS   AGE
mysql-7c47657796-b7x48   1/1       Running   0          41s
ubuntu@icp31-single:~$

Serviceの作成

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

mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
spec:
  ports:
  - port: 3306
  selector:
    app: mysql
kubectl apply -f mysql-service.yaml -n sugi

MySQLコンテナの初期化

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

(追記)
Jobでやる場合は以下の記事を参照。
Kubernetes JobでMySQLへデータをロードする

(補足)
環境変数MYSQL_DATABASEや、MYSQL_USERMYSQL_PASSWORDにより、コンテナの初回起動時にデータベースを1つ作成したり、ユーザーをひとり作ることも可能。また、/docker-entrypoint-initdb.d/ディレクトリーにスクリプトを配置して、コンテナの初回起動時に初期化処理を行う事も可能。

2.5.8.2 More Topics on Deploying MySQL Server with Docker

MySQLのPodを確認する。

kubectl get po -n sugi

Podにログインする。

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

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

mysql -u root -p

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

create database mydb;

libertyユーザーを作成する。

create user 'liberty' identified by 'liberty';

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

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

一度抜けてlibertyユーザーで入り直す。パスワードはliberty

quit;
mysql -u liberty -p

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

use mydb;
DROP TABLE IF EXISTS member;
CREATE TABLE `member` (
  id int NOT NULL AUTO_INCREMENT,
  name varchar(255) 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'),('user2'),('user3');
INSERT INTO member (name) VALUES ('user4'),('user5'),('user6');

データを確認。

SELECT * from member;

(実行例)

mysql> SELECT * from member;
+----+-------+---------------------+---------------------+
| id | name  | created             | updated             |
+----+-------+---------------------+---------------------+
|  1 | user1 | 2018-10-23 11:18:48 | 2018-10-23 11:18:48 |
|  2 | user2 | 2018-10-23 11:18:48 | 2018-10-23 11:18:48 |
|  3 | user3 | 2018-10-23 11:18:48 | 2018-10-23 11:18:48 |
|  4 | user4 | 2018-10-23 11:18:48 | 2018-10-23 11:18:48 |
|  5 | user5 | 2018-10-23 11:18:48 | 2018-10-23 11:18:48 |
|  6 | user6 | 2018-10-23 11:18:48 | 2018-10-23 11:18:48 |
+----+-------+---------------------+---------------------+
6 rows in set (0.00 sec)

mysql>

終了する。

quit;
exit

Libertyコンテナのデプロイ

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

アプリケーションの作成

簡単なWebアプリケーションを作成する。以下のコマンドでWarファイルを抽出して先に進んでもよい。

docker run --rm -v $(pwd):/data sotoiwa540/hellomysql:1.0 cp /config/dropins/hellomysql.war /data/

アプリケーションの作成は本筋から外れるため、ここでは要点のみ記載。

Eclipseで「Dynamic Web Project」を作成し、以下のServletを作成する。

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);
    }

}

MySQLのJDBCドライバーのjarファイルをダウンロードして、Libertyの${server.config.dir}/resources/mysqlディレクトリーに配置する。

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

Libertyのserver.xmlは以下のようになる。MySQLへの接続情報は環境変数で渡すようにする。ローカルで稼働確認する際はserver.envを使って環境変数を渡すとよい。

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="${server.config.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>

ローカルで稼働確認する場合、以下のコマンドでDockerでMySQLを起動することができる。

docker run --name mysql -e MYSQL_ROOT_PASSWORD=password -d -p 3306:3306 mysql:5.7

起動後は以下のコマンドでコンテナの中に入り、先ほどと同様にユーザーやデータを作る。

docker exec -it mysql bash

Libertyイメージのビルド

Dockerfileを作成する。

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

同じディレクトリーに必要なファイルを配置する。

$ tree .
.
├── Dockerfile
├── hellomysql.war
├── mysql-connector-java-8.0.13.jar
└── server.xml

0 directories, 4 files
$

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

docker build -t sotoiwa540/hellomysql:1.0 .
docker push sotoiwa540/hellomysql:1.0

Secretの作成

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

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

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

Secretを作成する。

liberty-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: liberty-secret
type: Opaque
data:
  liberty-password: bGliZXJ0eQ==
kubectl apply -f liberty-secret.yaml -n sugi

ConfigMapの作成

パスワード以外のMySQLへの接続情報を格納したConfigMapを作成する。

liberty-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: liberty-config
data:
  mysql-servername: "mysql"
  mysql-portnumber: "3306"
  mysql-user: "liberty"
kubectl apply -f liberty-config.yaml -n sugi

Libertyコンテナのデプロイ

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

liberty-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: liberty
spec:
  selector:
    matchLabels:
      app: liberty
  replicas: 1
  template:
    metadata:
      labels:
        app: liberty
    spec:
      containers:
      - name: liberty
        image: sotoiwa540/hellomysql:1.0
        imagePullPolicy: Always
        ports:
        - containerPort: 9080
        env:
        - name: MYSQL_SERVERNAME
          valueFrom:
            configMapKeyRef:
              name: liberty-config
              key: mysql-servername
        - name: MYSQL_PORTNUMBER
          valueFrom:
            configMapKeyRef:
              name: liberty-config
              key: mysql-portnumber
        - name: MYSQL_USER
          valueFrom:
            configMapKeyRef:
              name: liberty-config
              key: mysql-user
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              name: liberty-secret
              key: liberty-password
kubectl apply -f liberty-deployment.yaml -n sugi

Podが稼働したことを確認する。

ubuntu@icp31-single:~/hellomysql$ kubectl get po -n sugi
NAME                       READY     STATUS      RESTARTS   AGE
liberty-74fd798747-pwghp   1/1       Running     0          11s
mysql-7c47657796-bkc67     1/1       Running     0          4m
mysql-init-b9kqc           0/1       Completed   0          3m
ubuntu@icp31-single:~/hellomysql$

Serviceの作成

Serviceを作成する。

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

Ingressの作成

Ingressを作成する。
Minikubeの場合はアノテーションがnginx.ingress.kubernetes.io/rewrite-target:となるかもしれない。

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

稼働確認

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

https://<ICPのProxyのVIP>/liberty/hellomysql/HelloServlet

image.png

ログを確認する。

ubuntu@icp31-single:~/hellomysql$ kubectl logs liberty-74fd798747-pwghp -n sugi
Launching defaultServer (WebSphere Application Server 18.0.0.3/wlp-1.0.22.cl180320180905-2337) on IBM J9 VM, version 8.0.5.22 - pxa6480sr5fp22-20180919_01(SR5 FP22) (en_US)
[AUDIT   ] CWWKE0001I: The server defaultServer has been launched.
[AUDIT   ] CWWKE0100I: This product is licensed for development, and limited production use. The full license terms can be viewed here: https://public.dhe.ibm.com/ibmdl/export/pub/software/websphere/wasdev/license/base_ilan/ilan/18.0.0.3/lafiles/en.html
[AUDIT   ] CWWKG0093A: Processing configuration drop-ins resource: /opt/ibm/wlp/usr/servers/defaultServer/configDropins/defaults/keystore.xml
[AUDIT   ] CWWKZ0058I: Monitoring dropins for applications.
[AUDIT   ] CWWKS4104A: LTPA keys created in 3.087 seconds. LTPA key file: /opt/ibm/wlp/output/defaultServer/resources/security/ltpa.keys
[AUDIT   ] CWPKI0803A: SSL certificate created in 7.370 seconds. SSL key file: /opt/ibm/wlp/output/defaultServer/resources/security/key.jks
[AUDIT   ] CWWKT0016I: Web application available (default_host): http://liberty-74fd798747-pwghp:9080/hellomysql/
[AUDIT   ] CWWKZ0001I: Application hellomysql started in 0.994 seconds.
[AUDIT   ] CWWKF0012I: The server installed the following features: [localConnector-1.0].
[AUDIT   ] CWWKF0011I: The server defaultServer is ready to run a smarter planet.
user1
user2
user3
user4
user5
user6
ubuntu@icp31-single:~/hellomysql$