环境:OSE 3.0.1
本文我们创建一个复杂一些的应用,Java + MySQL。
在上一篇文章我们已经了解了如何一步步创建需要的对象,了解了 OpenShift 构建应用的整个过程。
现在我们使用 Template 创建一个复杂应用。
1. 为 MySQL 配置 NFS
[root@master ~]# mkdir /var/export/membermysql
[root@master ~]# chown nfsnobody:nfsnobody /var/export/membermysql
[root@master ~]# chmod 700 /var/export/membermysql
[root@master ~]# vim /etc/exports
/var/export/membermysql *(rw,async,all_squash)
[root@master ~]# exportfs -a
2. 创建 PV
[root@master ~]# oc login -u system:admin
[root@master ~]# oc create -f mysqlPV.json
mysqlPV.json 内容如下:
{
"apiVersion": "v1",
"kind": "PersistentVolume",
"metadata": {
"name": "mysqldb-member-volume"
},
"spec": {
"capacity": {
"storage": "1Gi"
},
"accessModes": [ "ReadWriteOnce" ],
"nfs": {
"path": "/var/export/membermysql",
"server": "master.pod0.example.com"
},
"persistentVolumeReclaimPolicy": "Recycle"
}
}
3. 创建 project
[student@workstation ~]$ oc login -u devloper -p openshift
[student@workstation ~]$ oc new-project member
4. 创建 secret
[student@workstation ~]$ oc create -f secret.json
serviceaccounts/eap-service-account
secrets/eap-app-secret-member
secret.json 内容如下:
{
"kind": "List",
"apiVersion": "v1",
"metadata": {},
"items": [
{
"kind": "ServiceAccount",
"apiVersion": "v1",
"metadata": {
"name": "eap-service-account"
},
"secrets": [
{
"name": "eap-app-secret-member"
}
]
},
{
"kind": "Secret",
"apiVersion": "v1",
"metadata": {
"annotations": {
"description": "Default secret file with name 'jboss' and password 'mykeystorepass'"
},
"name": "eap-app-secret-member"
},
"data": {
"keystore.jks": "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"
}
}
]
}
以上这些步骤还需要手动去做,下面就开始根据 Template 创建其它所有资源。
5. 导出指定的 Template 定义
(1)查看[student@workstation ~]$ oc get templates -n openshift
(2)查看 eap6-mysql-persistent-sti template 的参数定义
[student@workstation ~]$ oc process --parameters eap6-mysql-persistent-sti -n openshift
(3)导出 template 定义
[student@workstation ~]$ oc -o json export template eap6-mysql-persistent-sti -n openshift > eap6-mysql-persistent-sti.json
(4)导出 template 定义
oc process -f \
eap6-mysql-persistent-sti.json -v \
APPLICATION_NAME=member,\
APPLICATION_HOSTNAME=member.cloudapps0.example.com,\
GIT_URI=http://workstation.pod0.example.com/member,\
DB_JNDI=java:jboss/datasources/memberDS,\
DB_DATABASE=member,\
EAP_HTTPS_SECRET=eap-app-secret-member,\
EAP_HTTPS_KEYSTORE=keystore.jks,\
EAP_HTTPS_NAME=jboss,\
EAP_HTTPS_PASSWORD=mykeystorepass,\
DB_USERNAME=member,\
DB_PASSWORD=member > processedtemplate.json
processedtemplate.json 内容如下:
{
"kind": "List",
"apiVersion": "v1beta3",
"metadata": {},
"items": [
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"annotations": {
"description": "The web server's http port."
},
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member"
},
"spec": {
"ports": [
{
"port": 8080,
"targetPort": 8080
}
],
"selector": {
"deploymentConfig": "member"
}
}
},
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"annotations": {
"description": "The web server's https port."
},
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "secure-member"
},
"spec": {
"ports": [
{
"port": 8443,
"targetPort": 8443
}
],
"selector": {
"deploymentConfig": "member"
}
}
},
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"annotations": {
"description": "Ping service for clustered applications."
},
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member-ping"
},
"spec": {
"portalIP": "None",
"ports": [
{
"port": 8888,
"targetPort": 8888
}
],
"selector": {
"deploymentConfig": "member"
}
}
},
{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"annotations": {
"description": "The database server's port."
},
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member-mysql"
},
"spec": {
"ports": [
{
"port": 3306,
"targetPort": 3306
}
],
"selector": {
"deploymentConfig": "member-mysql"
}
}
},
{
"apiVersion": "v1",
"id": "member-http-route",
"kind": "Route",
"metadata": {
"annotations": {
"description": "Route for application's http service."
},
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member-http-route"
},
"spec": {
"host": "member.cloudapps0.example.com",
"to": {
"name": "member"
}
}
},
{
"apiVersion": "v1",
"id": "member-https-route",
"kind": "Route",
"metadata": {
"annotations": {
"description": "Route for application's https service."
},
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member-https-route"
},
"spec": {
"host": "member.cloudapps0.example.com",
"tls": {
"termination": "passthrough"
},
"to": {
"name": "secure-member"
}
}
},
{
"apiVersion": "v1",
"kind": "ImageStream",
"metadata": {
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member"
}
},
{
"apiVersion": "v1",
"kind": "BuildConfig",
"metadata": {
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member"
},
"spec": {
"output": {
"to": {
"kind": "ImageStreamTag",
"name": "member:latest"
}
},
"source": {
"contextDir": "",
"git": {
"ref": "master",
"uri": "http://workstation.pod0.example.com/member"
},
"type": "Git"
},
"strategy": {
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
"name": "jboss-eap6-openshift:6.4",
"namespace": "openshift"
}
},
"type": "Source"
},
"triggers": [
{
"github": {
"secret": "qj8gkw0O"
},
"type": "GitHub"
},
{
"generic": {
"secret": "JIeMaJxi"
},
"type": "Generic"
},
{
"imageChange": {},
"type": "ImageChange"
}
]
}
},
{
"apiVersion": "v1",
"kind": "DeploymentConfig",
"metadata": {
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member"
},
"spec": {
"replicas": 1,
"selector": {
"deploymentConfig": "member"
},
"strategy": {
"type": "Recreate"
},
"template": {
"metadata": {
"labels": {
"application": "member",
"deploymentConfig": "member"
},
"name": "member"
},
"spec": {
"containers": [
{
"env": [
{
"name": "DB_SERVICE_PREFIX_MAPPING",
"value": "member-mysql=DB"
},
{
"name": "DB_JNDI",
"value": "java:jboss/datasources/memberDS"
},
{
"name": "DB_USERNAME",
"value": "member"
},
{
"name": "DB_PASSWORD",
"value": "member"
},
{
"name": "DB_DATABASE",
"value": "member"
},
{
"name": "TX_DATABASE_PREFIX_MAPPING",
"value": "member-mysql=DB"
},
{
"name": "DB_MIN_POOL_SIZE",
"value": ""
},
{
"name": "DB_MAX_POOL_SIZE",
"value": ""
},
{
"name": "DB_TX_ISOLATION",
"value": ""
},
{
"name": "OPENSHIFT_DNS_PING_SERVICE_NAME",
"value": "member-ping"
},
{
"name": "OPENSHIFT_DNS_PING_SERVICE_PORT",
"value": "8888"
},
{
"name": "EAP_HTTPS_KEYSTORE_DIR",
"value": "/etc/eap-secret-volume"
},
{
"name": "EAP_HTTPS_KEYSTORE",
"value": "keystore.jks"
},
{
"name": "EAP_HTTPS_NAME",
"value": "jboss"
},
{
"name": "EAP_HTTPS_PASSWORD",
"value": "mykeystorepass"
},
{
"name": "HORNETQ_CLUSTER_PASSWORD",
"value": "gAqelq4m"
},
{
"name": "HORNETQ_QUEUES",
"value": ""
},
{
"name": "HORNETQ_TOPICS",
"value": ""
}
],
"image": "member",
"imagePullPolicy": "Always",
"name": "member",
"ports": [
{
"containerPort": 8080,
"name": "http",
"protocol": "TCP"
},
{
"containerPort": 8443,
"name": "https",
"protocol": "TCP"
},
{
"containerPort": 8888,
"name": "ping",
"protocol": "TCP"
}
],
"readinessProbe": {
"exec": {
"command": [
"/bin/bash",
"-c",
"/opt/eap/bin/readinessProbe.sh"
]
}
},
"volumeMounts": [
{
"mountPath": "/etc/eap-secret-volume",
"name": "eap-keystore-volume",
"readOnly": true
}
]
}
],
"serviceAccount": "eap-service-account",
"volumes": [
{
"name": "eap-keystore-volume",
"secret": {
"secretName": "eap-app-secret-member"
}
}
]
}
},
"triggers": [
{
"imageChangeParams": {
"automatic": true,
"containerNames": [
"member"
],
"from": {
"kind": "ImageStream",
"name": "member"
}
},
"type": "ImageChange"
}
]
}
},
{
"apiVersion": "v1",
"kind": "DeploymentConfig",
"metadata": {
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member-mysql"
},
"spec": {
"replicas": 1,
"selector": {
"deploymentConfig": "member-mysql"
},
"strategy": {
"type": "Recreate"
},
"template": {
"metadata": {
"labels": {
"application": "member",
"deploymentConfig": "member-mysql"
},
"name": "member-mysql"
},
"spec": {
"containers": [
{
"env": [
{
"name": "MYSQL_USER",
"value": "member"
},
{
"name": "MYSQL_PASSWORD",
"value": "member"
},
{
"name": "MYSQL_DATABASE",
"value": "member"
},
{
"name": "MYSQL_LOWER_CASE_TABLE_NAMES",
"value": ""
},
{
"name": "MYSQL_MAX_CONNECTIONS",
"value": ""
},
{
"name": "MYSQL_FT_MIN_WORD_LEN",
"value": ""
},
{
"name": "MYSQL_FT_MAX_WORD_LEN",
"value": ""
},
{
"name": "MYSQL_AIO",
"value": ""
}
],
"image": "mysql",
"imagePullPolicy": "Always",
"name": "member-mysql",
"ports": [
{
"containerPort": 3306,
"protocol": "TCP"
}
],
"volumeMounts": [
{
"mountPath": "/var/lib/mysql/data",
"name": "member-mysql-pvol"
}
]
}
],
"volumes": [
{
"name": "member-mysql-pvol",
"persistentVolumeClaim": {
"claimName": "member-mysql-claim"
}
}
]
}
},
"triggers": [
{
"imageChangeParams": {
"automatic": true,
"containerNames": [
"member-mysql"
],
"from": {
"kind": "ImageStreamTag",
"name": "mysql:latest",
"namespace": "openshift"
}
},
"type": "ImageChange"
}
]
}
},
{
"apiVersion": "v1",
"kind": "PersistentVolumeClaim",
"metadata": {
"labels": {
"application": "member",
"template": "eap6-mysql-persistent-sti"
},
"name": "member-mysql-claim"
},
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"resources": {
"requests": {
"storage": "512Mi"
}
}
}
}
]
}
6. 发布应用
[student@workstation ~]$ oc create -f processedtemplate.json
也可以通过 oc new-app 命令 + template + 参数定义,直接创建应用
注意,参数 option 是 -p=, 而不是 -v 。
[student@workstation ~]$ oc new-app --template=eap6-mysql-persistent-sti \
-p=APPLICATION_NAME=member,\
APPLICATION_HOSTNAME=member.cloudapps0.example.com,\
GIT_URI=http://workstation.pod0.example.com/member,\
DB_JNDI=java:jboss/datasources/memberDS,\
DB_DATABASE=member,\
EAP_HTTPS_SECRET=eap-app-secret-member,\
EAP_HTTPS_KEYSTORE=keystore.jks,\
EAP_HTTPS_NAME=jboss,\
EAP_HTTPS_PASSWORD=mykeystorepass,\
DB_USERNAME=member,\
DB_PASSWORD=member
7. 访问应用
http://member.cloudapps0.example.com
8. Java 应用 pod 是如何访问 MySQL pod 的?
(1)查看了源代码,发现使用的是 JPA,只在 persistent.xml 中定义了 mysql datasource 的 JNDI 名字。
<persistence
version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="primary">
<!-- If you are running in a production environment, add a managed
data source, this example data source is just for development and testing! -->
<!-- The datasource is deployed as WEB-INF/hello-world-ds.xml, you
can find it in the source at src/main/webapp/WEB-INF/hello-world-ds.xml -->
<jta-data-source>java:jboss/datasources/hello-worldDS</jta-data-source>
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="false"/>
</properties>
</persistence-unit>
</persistence>
(2)进入应用 pod 后,进入 eap 6 安装目录,找到 /opt/eap/standalone/configuration/standalone-openshift.xml 文件,发现其中详细定义了 mysql datasource。
<datasources>
<xa-datasource jndi-name="java:jboss/datasources/memberDS" pool-name="member_mysql" use-java-context="true" enabled="true">
<xa-datasource-property name="ServerName">172.30.178.212</xa-datasource-property>
<xa-datasource-property name="Port">3306</xa-datasource-property>
<xa-datasource-property name="DatabaseName">member</xa-datasource-property>
<driver>mysql</driver>
<security>
<user-name>member</user-name>
<password>member</password>
</security>
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"></valid-connection-checker>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"></exception-sorter>
</validation>
</xa-datasource>
<datasource jta="false" jndi-name="java:jboss/datasources/memberDSObjectStore" pool-name="member_mysqlObjectStorePool" enabled="true">
<connection-url>jdbc:mysql://172.30.178.212:3306/member</connection-url>
<driver>mysql</driver>
<security>
<user-name>member</user-name>
<password>member</password>
</security>
</datasource>
</datasources>
说明:172.30.178.212 是 member-mysql service 暴露的 IP 地址,3306 是 member-mysql service 暴露的端口,表明应用 pod 是通过 member-mysql service 去访问 member-mysql pod 的。