2017年7月12日星期三

OpenShift_085:在 Web Console 上部署第三方镜像

环境:OCP 3.5

本文以部署 mywebsql 镜像为例说明,如何在 Web Console 上部署第三方镜像。
关于如何使用命令行部署第三方镜像,请参考《部署 mywebsql docker image 访问 mysql 数据库》。

1. 直接 Deploy Image
部署不成功,这是因为 Web Console 不支持 https 安全验证。


2. 以 Image Stream 方式部署
(1)允许 root 用户访问容器
oc login -u system:admin

oadm policy add-scc-to-user anyuid -z default
(2)创建 Image Stream:mywebsql
oc create -f mywebsql-is.json -n openshift
其中 mywebsql-is.json 内容如下:
{
    "kind": "ImageStream",
    "apiVersion": "v1",
    "metadata": {
        "name": "mywebsql",
        "creationTimestamp": null
    },
    "spec": {
        "dockerImageRepository": "registry.example.com:5000/quantumobject/docker-mywebsql",
        "tags": [
            {
                "name": "latest",
                "annotations": null,
                "from": {
                    "kind": "DockerImage",
                    "name": "registry.example.com:5000/quantumobject/docker-mywebsql"
                },
                "generation": 1,
                "importPolicy": {
                    "insecure": true
                }
            }
        ]
    }
}

(3)部署 Image Stream:mywebsql



3. 创建 template mywebsql(在 Master 机器上操作)
为了以后创建更简单,这里导出 mywebsql 的所有对象,并存为 template。
oc export dc,svc,route -o json --as-template=mywebsql > mywebsql.template
修改 mywebsql.template,

"image": "registry.example.com:5000/quantumobject/docker-mywebsql@sha256:a34f5050ae56bab4d8456a86666c9b431d7bead26e71a1b1003fd874b68bfb8d",
改为
"image": ""
这样做的目的是,根据 template 创建应用后,自动发布,不用再手工点击 Deploy。

oc create -f mywebsql.template -n openshift

4. 测试
oc project test
oc delete all --all


OpenShift_084:安装配置 JBoss AMQ(Route SSL)

环境:OCP 3.5

本文在 OpenShift 平台上部署一个 JBoss AMQ,配置消息持久化,通过 Route SSL 访问。

1. 修改默认的 template amq62-persistent-ssl 
oc edit template amq62-persistent-ssl -n openshift

kind: ImageStreamTag
name: jboss-amq-62:1.3
改为
kind: ImageStreamTag
name: jboss-amq-62:latest

为方便起见,建议把所有有关 amq62 的 template 都修改一遍。
oc edit template amq62-basic -n openshift
oc edit template amq62-persistent -n openshift
oc edit template amq62-persistent-ssl -n openshift
oc edit template amq62-ssl -n openshift

2. 创建 NFS Server(在 Registry 机器上操作)
yum install –y nfs-utils

export volname=amq-vol
mkdir -p /srv/nfs/${volname}
chown nfsnobody:nfsnobody /srv/nfs/${volname}
chmod 700 /srv/nfs/${volname}
echo "/srv/nfs/${volname} *(rw,sync,all_squash)" >> /etc/exports
systemctl enable nfs-server
systemctl restart rpcbind
systemctl restart nfs-server nfs-lock nfs-idmap

3. 测试 NFS Server 工作是否正常 (在 Node1/Node2 机器上操作)
export volname=amq-vol
mkdir -p /mnt/nfs
mount -t nfs registry.example.com:/srv/nfs/${volname} /mnt/nfs
umount /mnt/nfs

4. 创建 PV (在 Master 机器上操作,如果出错,此步需要重做)
echo '{
  "apiVersion": "v1",
  "kind": "PersistentVolume",
  "metadata": {
    "name": "amq-volume"
  },
  "spec": {
    "capacity": {
        "storage": "512Mi"
        },
    "accessModes": [ "ReadWriteMany" ],
    "nfs": {
        "path": "/srv/nfs/amq-vol",
        "server": "registry.example.com"
    },
    "persistentVolumeReclaimPolicy": "Recycle"
  }
}' | oc create -f -

如果出错,执行以下命令清理,然后重做

5. 创建各种对象
(1)oc new-project amq-demo
(2)创建 service account,用于 A-MQ 部署
  echo '{"kind": "ServiceAccount", "apiVersion": "v1", "metadata": {"name": "amq-service-account"}}' | oc create -f -
(3)给 amq-service-account 赋予 view 权限
  oc policy add-role-to-user view system:serviceaccount:amq-demo:amq-service-account
(4)A-MQ 需要一个 broker keyStore,一个 client keyStore,一个 client trustStore 其中包括 broker keyStore
(4.1)生成一个自签名的证书,作为 broker keyStore
keytool -genkey -dname "CN=Ma Ping,OU=SA,O=Redhat,L=CY,ST=Beijing,C=CN" -alias broker -keyalg RSA -keysize 1024 -keystore broker.ks -keypass redhat -storepass redhat -validity 365
(4.2)导出刚刚生成的 broker keyStore 证书,以便分享
keytool -export -alias broker -keystore broker.ks -storepass redhat -file broker_cert
(4.3)生成一个自签名的证书,作为 client keyStore
keytool -genkey -dname "CN=Ma Ping,OU=SA,O=Redhat,L=CY,ST=Beijing,C=CN" -alias client -keyalg RSA -keysize 1024 -keystore client.ks -keypass redhat -storepass redhat -validity 365
(4.4)创建 client trustStore,并导入之前导出的 broker 证书
keytool -import -alias broker -keystore client.ts -storepass redhat -file broker_cert
(4.5)使用 broker keyStore 创建 secret
oc secrets new amq-app-secret broker.ks
(4.6)把 secret 添加到之前创建的 service account
oc secrets add sa/amq-service-account secret/amq-app-secret

6. 部署
为简单起见,基于 amq62-persistent-ssl template,创建 my-amq62-persistent-ssl
oc get template amq62-persistent-ssl -n openshift -o json > my-amq62-persistent-ssl.json
去掉不必要的 service,只保留 broker-amq-tcp 和 broker-amq-tcp-ssl。
修改后,my-amq62-persistent-ssl.json 内容如下:
{
    "apiVersion": "v1",
    "kind": "Template",
    "labels": {
        "template": "my-amq62-persistent-ssl",
        "xpaas": "1.3.1"
    },
    "metadata": {
        "annotations": {
            "description": "Application template for JBoss A-MQ brokers. These are deployed as standalone and use persistent storage for saving messages. This template supports SSL and requires usage of OpenShift secrets.",
            "iconClass": "icon-jboss",
            "tags": "messaging,amq,jboss,xpaas",
            "version": "1.3.1"
        },
        "creationTimestamp": "2017-05-24T06:57:08Z",
        "name": "my-amq62-persistent-ssl",
        "namespace": "openshift",
        "resourceVersion": "20544",
        "selfLink": "/oapi/v1/namespaces/openshift/templates/my-amq62-persistent-ssl",
        "uid": "346b140a-404e-11e7-999f-080027873f56"
    },
    "objects": [
        {
            "apiVersion": "v1",
            "kind": "Service",
            "metadata": {
                "annotations": {
                    "description": "The broker's OpenWire port."
                },
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}-amq-tcp"
            },
            "spec": {
                "ports": [
                    {
                        "port": 61616,
                        "targetPort": 61616
                    }
                ],
                "selector": {
                    "deploymentConfig": "${APPLICATION_NAME}-amq"
                }
            }
        },
        {
            "apiVersion": "v1",
            "kind": "Service",
            "metadata": {
                "annotations": {
                    "description": "The broker's OpenWire (SSL) port."
                },
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}-amq-tcp-ssl"
            },
            "spec": {
                "ports": [
                    {
                        "port": 61617,
                        "targetPort": 61617
                    }
                ],
                "selector": {
                    "deploymentConfig": "${APPLICATION_NAME}-amq"
                }
            }
        },
        {
            "apiVersion": "v1",
            "kind": "DeploymentConfig",
            "metadata": {
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}-amq"
            },
            "spec": {
                "replicas": 1,
                "selector": {
                    "deploymentConfig": "${APPLICATION_NAME}-amq"
                },
                "strategy": {
                    "rollingParams": {
                        "maxSurge": 0
                    },
                    "type": "Rolling"
                },
                "template": {
                    "metadata": {
                        "labels": {
                            "application": "${APPLICATION_NAME}",
                            "deploymentConfig": "${APPLICATION_NAME}-amq"
                        },
                        "name": "${APPLICATION_NAME}-amq"
                    },
                    "spec": {
                        "containers": [
                            {
                                "env": [
                                    {
                                        "name": "AMQ_USER",
                                        "value": "${MQ_USERNAME}"
                                    },
                                    {
                                        "name": "AMQ_PASSWORD",
                                        "value": "${MQ_PASSWORD}"
                                    },
                                    {
                                        "name": "AMQ_TRANSPORTS",
                                        "value": "${MQ_PROTOCOL}"
                                    },
                                    {
                                        "name": "AMQ_QUEUES",
                                        "value": "${MQ_QUEUES}"
                                    },
                                    {
                                        "name": "AMQ_TOPICS",
                                        "value": "${MQ_TOPICS}"
                                    },
                                    {
                                        "name": "MQ_SERIALIZABLE_PACKAGES",
                                        "value": "${MQ_SERIALIZABLE_PACKAGES}"
                                    },
                                    {
                                        "name": "AMQ_SPLIT",
                                        "value": "${AMQ_SPLIT}"
                                    },
                                    {
                                        "name": "AMQ_MESH_DISCOVERY_TYPE",
                                        "value": "${AMQ_MESH_DISCOVERY_TYPE}"
                                    },
                                    {
                                        "name": "AMQ_MESH_SERVICE_NAME",
                                        "value": "${APPLICATION_NAME}-amq-tcp"
                                    },
                                    {
                                        "name": "AMQ_MESH_SERVICE_NAMESPACE",
                                        "valueFrom": {
                                            "fieldRef": {
                                                "fieldPath": "metadata.namespace"
                                            }
                                        }
                                    },
                                    {
                                        "name": "AMQ_KEYSTORE_TRUSTSTORE_DIR",
                                        "value": "/etc/amq-secret-volume"
                                    },
                                    {
                                        "name": "AMQ_TRUSTSTORE",
                                        "value": "${AMQ_TRUSTSTORE}"
                                    },
                                    {
                                        "name": "AMQ_TRUSTSTORE_PASSWORD",
                                        "value": "${AMQ_TRUSTSTORE_PASSWORD}"
                                    },
                                    {
                                        "name": "AMQ_KEYSTORE",
                                        "value": "${AMQ_KEYSTORE}"
                                    },
                                    {
                                        "name": "AMQ_KEYSTORE_PASSWORD",
                                        "value": "${AMQ_KEYSTORE_PASSWORD}"
                                    },
                                    {
                                        "name": "AMQ_STORAGE_USAGE_LIMIT",
                                        "value": "${AMQ_STORAGE_USAGE_LIMIT}"
                                    }
                                ],
                                "image": "jboss-amq-62",
                                "imagePullPolicy": "Always",
                                "name": "${APPLICATION_NAME}-amq",
                                "ports": [
                                    {
                                        "containerPort": 8778,
                                        "name": "jolokia",
                                        "protocol": "TCP"
                                    },
                                    {
                                        "containerPort": 61616,
                                        "name": "tcp",
                                        "protocol": "TCP"
                                    },
                                    {
                                        "containerPort": 61617,
                                        "name": "tcp-ssl",
                                        "protocol": "TCP"
                                    }
                                ],
                                "readinessProbe": {
                                    "exec": {
                                        "command": [
                                            "/bin/bash",
                                            "-c",
                                            "/opt/amq/bin/readinessProbe.sh"
                                        ]
                                    }
                                },
                                "volumeMounts": [
                                    {
                                        "mountPath": "/etc/amq-secret-volume",
                                        "name": "broker-secret-volume",
                                        "readOnly": true
                                    },
                                    {
                                        "mountPath": "/opt/amq/data",
                                        "name": "${APPLICATION_NAME}-amq-pvol"
                                    }
                                ]
                            }
                        ],
                        "serviceAccountName": "amq-service-account",
                        "terminationGracePeriodSeconds": 60,
                        "volumes": [
                            {
                                "name": "broker-secret-volume",
                                "secret": {
                                    "secretName": "${AMQ_SECRET}"
                                }
                            },
                            {
                                "name": "${APPLICATION_NAME}-amq-pvol",
                                "persistentVolumeClaim": {
                                    "claimName": "${APPLICATION_NAME}-amq-claim"
                                }
                            }
                        ]
                    }
                },
                "triggers": [
                    {
                        "imageChangeParams": {
                            "automatic": true,
                            "containerNames": [
                                "${APPLICATION_NAME}-amq"
                            ],
                            "from": {
                                "kind": "ImageStreamTag",
                                "name": "jboss-amq-62:latest",
                                "namespace": "${IMAGE_STREAM_NAMESPACE}"
                            }
                        },
                        "type": "ImageChange"
                    },
                    {
                        "type": "ConfigChange"
                    }
                ]
            }
        },
        {
            "apiVersion": "v1",
            "kind": "PersistentVolumeClaim",
            "metadata": {
                "labels": {
                    "application": "${APPLICATION_NAME}"
                },
                "name": "${APPLICATION_NAME}-amq-claim"
            },
            "spec": {
                "accessModes": [
                    "ReadWriteMany"
                ],
                "resources": {
                    "requests": {
                        "storage": "${VOLUME_CAPACITY}"
                    }
                }
            }
        }
    ],
    "parameters": [
        {
            "description": "The name for the application.",
            "name": "APPLICATION_NAME",
            "required": true,
            "value": "broker"
        },
        {
            "description": "Split the data directory for each node in a mesh.",
            "name": "AMQ_SPLIT",
            "value": "false"
        },
        {
            "description": "Protocols to configure, separated by commas.  Allowed values are: `openwire`, `amqp`, `stomp` and `mqtt`.",
            "name": "MQ_PROTOCOL",
            "value": "openwire"
        },
        {
            "description": "Queue names, separated by commas.  These queues will be automatically created when the broker starts.  If left empty, queues will be still created dynamically.",
            "name": "MQ_QUEUES"
        },
        {
            "description": "Topic names, separated by commas.  These topics will be automatically created when the broker starts.  If left empty, topics will be still created dynamically.",
            "name": "MQ_TOPICS"
        },
        {
            "description": "List of packages that are allowed to be serialized for use in ObjectMessage, separated by commas. If your app doesn't use ObjectMessages, leave this blank. This is a security enforcement. For the rationale, see http://activemq.apache.org/objectmessage.html",
            "name": "MQ_SERIALIZABLE_PACKAGES"
        },
        {
            "description": "Size of persistent storage for database volume.",
            "name": "VOLUME_CAPACITY",
            "required": true,
            "value": "512Mi"
        },
        {
            "description": "User name for standard broker user.  It is required for connecting to the broker.  If left empty, it will be generated.",
            "from": "user[a-zA-Z0-9]{3}",
            "generate": "expression",
            "name": "MQ_USERNAME",
            "value": "admin"
        },
        {
            "description": "Password for standard broker user.  It is required for connecting to the broker.  If left empty, it will be generated.",
            "from": "[a-zA-Z0-9]{8}",
            "generate": "expression",
            "name": "MQ_PASSWORD",
            "value": "admin"
        },
        {
            "description": "The discovery agent type to use for discovering mesh endpoints.  'dns' will use OpenShift's DNS service to resolve endpoints.  'kube' will use Kubernetes REST API to resolve service endpoints.  If using 'kube' the service account for the pod must have the 'view' role, which can be added via 'oc policy add-role-to-user view system:serviceaccount:\u003cnamespace\u003e:default' where \u003cnamespace\u003e is the project namespace.",
            "name": "AMQ_MESH_DISCOVERY_TYPE",
            "value": "kube"
        },
        {
            "description": "Name of a secret containing SSL related files",
            "name": "AMQ_SECRET",
            "required": true,
            "value": "amq-app-secret"
        },
        {
            "description": "SSL trust store filename",
            "name": "AMQ_TRUSTSTORE",
            "required": true,
            "value": "broker.ks"
        },
        {
            "description": "SSL trust store password",
            "name": "AMQ_TRUSTSTORE_PASSWORD",
            "required": true,
            "value": "redhat"
        },
        {
            "description": "SSL key store filename",
            "name": "AMQ_KEYSTORE",
            "required": true,
            "value": "broker.ks"
        },
        {
            "description": "Password for accessing SSL keystore",
            "name": "AMQ_KEYSTORE_PASSWORD",
            "required": true,
            "value": "redhat"
        },
        {
            "description": "The A-MQ storage usage limit",
            "name": "AMQ_STORAGE_USAGE_LIMIT",
            "value": "1 gb"
        },
        {
            "description": "Namespace in which the ImageStreams for Red Hat Middleware images are installed. These ImageStreams are normally installed in the openshift namespace. You should only need to modify this if you've installed the ImageStreams in a different namespace/project.",
            "name": "IMAGE_STREAM_NAMESPACE",
            "required": true,
            "value": "openshift"
        }
    ]
}

(1)部署 my-amq62-persistent-ssl template
Add to Project --> Browse Catalog 输入 amq62,选择 my-amq62-persistent-ssl
其余保持默认,点击 Create,等待 Pods 启动成功。
(2)为 Service:broker-amq-tcp-ssl 创建 Route
(3)修改 Route,勾选中 Secure route,修改 TLS Termination,下拉列表选择 Passthrough。

7. 测试
(1)消息生产者:SSLPublisher.java

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;

import org.apache.activemq.ActiveMQSslConnectionFactory;

/**
 * 使用SSL连接器 队列消息生产者
 */
public class SSLPublisher {

    public static void main(String[] args) throws JMSException, Exception {
        /*
         * 配置参数 密钥和证书文件的访问目录 密钥密码 SSL链接地址
         */
        String keyStore = "/Users/maping/Apache/broker.ks";
        String trustStore = "/Users/maping/Apache/client.ts";
        String keyStorePassword = "redhat";
        String url = "ssl://broker-amq-tcp-ssl-amq-demo.apps.example.com:443";

        // 创建SSL连接器工厂类
        ActiveMQSslConnectionFactory sslConnectionFactory = new ActiveMQSslConnectionFactory();
        // 设置参数,并加载SSL密钥和证书信息
        sslConnectionFactory.setUserName("admin");
        sslConnectionFactory.setPassword("admin");
        sslConnectionFactory.setBrokerURL(url);
        sslConnectionFactory.setKeyAndTrustManagers(SSLUtils.loadKeyManager(keyStore, keyStorePassword), SSLUtils.loadTrustManager(trustStore),
                new java.security.SecureRandom());

        // 连接ActiveMQ
        Connection conn = sslConnectionFactory.createConnection();
        conn.start();
        Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination dest = session.createQueue("sslDemo");

        // 创建消息生产者,发送一条报文消息
        MessageProducer mp = session.createProducer(dest);
        Message msg = session.createTextMessage("Hello SSL!");
        mp.send(msg);
        System.out.println("success");

        // 发送完成,释放连接
        session.close();
        conn.close();
    }
}

(2)消息消费者:SSLListener.java

import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQSslConnectionFactory;

/**
 * 使用SSL连接器 队列消息消费者
 */
public class SSLListener {

    public static void main(String[] args) throws JMSException, Exception {
        /*
         * 配置参数 密钥和证书文件的访问目录 密钥密码 SSL链接地址
         */
        String keyStore = "/Users/maping/Apache/broker.ks";
        String trustStore = "/Users/maping/Apache/client.ts";
        String keyStorePassword = "redhat";
        String url = "ssl://broker-amq-tcp-ssl-amq-demo.apps.example.com:443";

        // 创建SSL连接器工厂类
        ActiveMQSslConnectionFactory sslConnectionFactory = new ActiveMQSslConnectionFactory();
        // 设置参数,并加载SSL密钥和证书信息
        sslConnectionFactory.setUserName("admin");
        sslConnectionFactory.setPassword("admin");
        sslConnectionFactory.setBrokerURL(url);
        sslConnectionFactory.setKeyAndTrustManagers(SSLUtils.loadKeyManager(keyStore, keyStorePassword), SSLUtils.loadTrustManager(trustStore),
                new java.security.SecureRandom());

        // 连接ActiveMQ
        Connection conn = sslConnectionFactory.createConnection();
        conn.start();
        Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination dest = session.createQueue("sslDemo");
        // 设置消息消费者,在匿名内部类中打印消息内容
        MessageConsumer mc = session.createConsumer(dest);
        mc.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message msg) {
                if (msg instanceof TextMessage) {
                    try {
                        TextMessage tmsg = (TextMessage) msg;
                        System.out.println(tmsg.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(msg.toString());
                }
            }
        });

        // 不关闭连接,让客户端一直连着ActiveMQ
    }
}
(3)SSL 工具类:SSLListener.java

import java.io.FileInputStream;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

/**
 * SSL 工具类 加载密钥和证书文件
 */
public class SSLUtils {

    /**
     * 加载证书文件
     *
     * @param trustStore
     * @return
     * @throws java.security.NoSuchAlgorithmException
     * @throws java.security.KeyStoreException
     * @throws java.io.IOException
     * @throws java.security.GeneralSecurityException
     */
    public static TrustManager[] loadTrustManager(String trustStore) throws java.security.NoSuchAlgorithmException, java.security.KeyStoreException,
            java.io.IOException, java.security.GeneralSecurityException {
        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream(trustStore), null);
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        System.out.println("init TrustManagers finish");
        return tmf.getTrustManagers();
    }

    /**
     * 加载密钥文件
     *
     * @param keyStore
     * @param keyStorePassword
     * @return
     * @throws java.security.NoSuchAlgorithmException
     * @throws java.security.KeyStoreException
     * @throws java.security.GeneralSecurityException
     * @throws java.security.cert.CertificateException
     * @throws java.io.IOException
     * @throws java.security.UnrecoverableKeyException
     */
    public static KeyManager[] loadKeyManager(String keyStore, String keyStorePassword) throws java.security.NoSuchAlgorithmException,
            java.security.KeyStoreException, java.security.GeneralSecurityException, java.security.cert.CertificateException, java.io.IOException,
            java.security.UnrecoverableKeyException {
        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream(keyStore), keyStorePassword.toCharArray());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, keyStorePassword.toCharArray());
        System.out.println("init KeyManager finish");
        return kmf.getKeyManagers();
    }
}

(4)在 Web Console 中查看,点击 Pod,点击 Open Java Console


8. 如果有错,执行以下命令清除,然后重做
oc project amq-demo
oc delete pvc broker-amq-claim
oc delete pv amq-volume
oc delete all --all
oc delete template my-amq62-persistent-ssl -n openshift
oc create -f my-amq62-persistent-ssl.json -n openshift

参考文献:
1. http://blog.csdn.net/dailywater/article/details/52833307

OpenShift_083:安装配置 JBoss AMQ(NodePort)

环境:OCP 3.5

本文以最简单的方式在 OpenShift 平台上部署一个 JBoss AMQ。使用 NodePort 方式直接暴露 Node 上的 TCP 端口,供外部访问。

1. 修改默认的 template amq62-basic
oc edit template amq62-basic -n openshift

kind: ImageStreamTag
name: jboss-amq-62:1.3
改为
kind: ImageStreamTag
name: jboss-amq-62:latest

为方便起见,建议把所有有关 amq62 的 template 都修改一遍。
oc edit template amq62-basic -n openshift
oc edit template amq62-persistent -n openshift
oc edit template amq62-persistent-ssl -n openshift
oc edit template amq62-ssl -n openshift

2. 部署
(1)oc new-project test
(2)Add to Project --> Browse Catalog 输入 amq62,选择 amq62-basic
MQ_USERNAME:admin
MQ_PASSWORD:admin
其余保持默认,点击 Create
等待 Pods 启动成功。
(3)修改 Service:broker-amq-tcp,把 ClusterIP 改为 NodePort
(4)查看 Node Port 端口
oc get svc broker-amq-tcp
输出如下:
NAME             CLUSTER-IP      EXTERNAL-IP   PORT(S)           AGE
broker-amq-tcp   172.30.35.185   <nodes>       61616:32472/TCP   6m
(5)查看 Pod 运行在哪个 Node 上
oc get pod -o wide
NAME                 READY     STATUS    RESTARTS   AGE       IP            NODE
broker-amq-1-kk5cz   1/1       Running   0          7m        10.128.0.37   node2.example.com

3. 测试
  cd ~/Apache/activemq/bin
(1)生产消息
./activemq producer --brokerUrl tcp://node2.example.com:32472 --user admin --password admin --destination queue://testQueue --messageSize 1024 --messageCount 10 --parallelThreads 10
(2)消费消息
./activemq consumer --brokerUrl tcp://node2.example.com:32472 --user admin --password admin --destination queue://testQueue --messageCount 10 --parallelThreads 10

在 Web Console 中查看,点击 Pod,点击 Open Java Console

5. 如果有错,执行以下命令清除,然后重做
 oc delete dc/broker-amq  svc/broker-amq-amqp  svc/broker-amq-mqtt  svc/broker-amq-stomp svc/broker-amq-tcp

2017年7月4日星期二

Cloud_032:SDN 介绍(摘录+整理)

1. 什么是 SDN ?
软件定义网络(Software Defined Network,SDN)是一种新型网络架构,是网络虚拟化的一种实现方式。
在数据中心里,网络技术是最为封闭的一部分,这导致长期以来网络成为数据中心发展的瓶颈,SDN 就是要打破这种网络的封闭性,让网络也能变得和虚拟池化后的计算、存储资源一样,成为一种可灵活调配的资源。
SDN 的核心理念是将网络层面的功能和数据层面的功能与网络设备硬件解耦,变成抽象化的功能,再通过外置的控制器来控制这些抽象化对象。
SDN 包含控制器和 OpenFlow 两大部分:
(1)控制器是一个软件,可以对整个网络设备进行管理和控制。
(2)OpenFlow 是控制器与网络设备之间互通的语言;
控制器通过 OpenFlow 对所有网络设备进行管理和控制。

2. SDN 与 Overlay 的关系
Overlay 提供了一种解决数据层面转发和多租户隔离的技术手段。SDN 定义了一种控制和管理的网络架构。
VxLAN 是部署 SDN 的先决条件,两者在未来数据中心里的作用并不冲突,可以有机地结合,共同发挥作用。

3. VDC
虚拟数据中心(Virtual Data Center),即对数据中心全面实现虚拟化,包括服务器的虚拟化、安全的虚拟化、网络的虚拟化。
VDC 中的网络虚拟化的实现技术是 VMware 的 Neutron。
Neutron 的目的是为 OpenStack 云更灵活地划分物理网络,在多租户环境下提供给每个租户独立的网络环境。
Neutron 提供 API 来实现这种目标。
用户可以使用 Neutron API 创建自己的网络对象,如果要和物理环境下的概念映射的话,这个网络对象相当于一个巨大的交换机,可以拥有无限多个动态可创建和销毁的虚拟端口。
SDN 实现的是三层以下的虚拟化,而 VDC 关注的是四到七层的虚拟化。
Neutron 提供接近应用层的网络虚拟化技术,是对 SDN 的有效补充。

2017年7月2日星期日

Cloud_031:VxLAN 原理详解(摘录+整理)

1. 什么是 VxLAN ?
虚似扩展局域网(Virtual Extensible LAN,VxLAN)是一种网络虚似化技术,是由 Ciscio科和 VMware 共同提出来的,是对 VLAN 的一种扩展,用于满足日益增长的云计算部署要求。

2. 为什么要使用 VxLAN ?
(1)虚拟机规模受网络规格限制
在大二层网络环境下,数据报文是通过查询 MAC 地址表进行二层转发,而 MAC 地址表的容量限制了虚拟机的数量。
(2)网络隔离能力限制
VLAN 在大规模的虚拟化网络中部署存在如下限制:
仅能表示 4096 个 VLAN,无法满足大二层网络中大量用户群的需求;
传统二层网络无法满足网络动态调整的需求。
(3)虚拟机迁移范围受网络架构限制
为了保证虚拟机动态迁移过程中业务不中断,不仅需要保持虚拟机的 IP 地址、MAC 地址不变,运行状态(比如TCP 会话)也必须保持不变,这就要求网络是一个二层网络,并且要求网络本身具备多路径的冗余备份和可靠性。

3. VxLAN 报文格式



4. VxLAN 网络结构




4. VxLAN 网关





参考文献:
1. http://developer.huawei.com/cn/ict/Products/Agile_Network/Components/DCN/detail/net/VXLAN
2. http://www.cnblogs.com/hbgzy/p/5279269.html
3. http://www.sdnlab.com/7241.html

Cloud_30:大二层网络 (摘录+整理)

首先要说明的是,大二层网络是针对虚拟化数据中心的虚拟机动态迁移这一特定需求而提出的概念,对于其它类型的网络并无特殊的价值和意义。
为了保证虚拟机动态迁移过程中业务不中断,不仅需要保持虚拟机的 IP 地址、MAC 地址不变,运行状态(比如TCP 会话)也必须保持不变,这就要求迁移的起始和目标位置必须在同一个二层网络,并且要求网络本身具备多路径的冗余备份和可靠性。
所以,为了实现虚拟机的大范围甚至跨地域的动态迁移,就要求把虚拟机迁移可能涉及的所有服务器都纳入同一个二层网络域。
这就是大二层网络的需求由来,一个真正的大二层网络至少要能容纳 1 万以上的主机,才能称之为大二层网络。
而传统的基于 VLAN + xSTP 的二层网络,由于环路和广播风暴、以及 xSTP 协议的性能限制等原因,通常能容纳的主机数量不会超过 1千,无法实现大二层网络。


当前,实现大二层网络的主要技术有以下几种:

1. 网络设备虚拟化技术
网络设备虚拟化是将相互冗余的两台或多台物理网络设备组合在一起,虚拟化成一台逻辑网络设备,在整个网络中只呈现为一个节点。例如华为的 CSS 框式堆叠、iStack 盒式堆叠、SVF 框盒堆叠技术等。


网络设备虚拟化再配合链路聚合技术,就可以把原来网络的多节点、多链路的结构变成逻辑上单节点、单链路的结构,解决了二层网络中的环路问题。没有了环路问题,就不需要xSTP,二层网络就可以范围无限(只要虚拟网络设备的接入能力允许),从而实现大二层网络。

2. 大二层转发技术
大二层转发技术是通过定义新的转发协议,改变传统二层网络的转发模式,将三层网络的路由转发模式引入到二层网络中。例如 TRILL、SPB 等。
以 TRILL 为例,TRILL 协议在原始以太帧外封装一个TRILL 帧头,再封装一个新的以太帧来实现对原始以太帧的透明传输,支持 TRILL 的交换机可通过 TRILL 帧头里的 Nickname 标识来进行转发,而 Nickname 就像路由一样,可通过 IS-IS 路由协议进行收集、同步和更新。


3. Overlay 技术
前两种技术都是网络设备厂商提供的,需要使用专用的网络设备。而 Overlay 技术是 IT 厂商提出的,目的就是为了摆脱对网络设备厂商的依赖。

Overlay 技术是通过用隧道封装的方式,将源主机发出的原始二层报文封装后在现有网络中进行透明传输,从而实现主机之间的二层通信。通过封装和解封装,相当于一个大二层网络叠加在现有的基础网络之上,所以称为 Overlay 技术。
Overlay 技术通过隧道封装的方式,忽略承载网络的结构和细节,可以把整个承载网络当作一台“巨大无比的二层交换机”, 每一台主机都是直连在“交换机”的一个端口上。而承载网络之内如何转发都是 “交换机”内部的事情,主机完全不可见。
Overlay 技术主要有 VxLAN、NVGRE、STT 等。


参考文献:
1. http://blog.sina.com.cn/s/blog_4878aa7d0102vjm9.html

Cloud_029:VPN 原理详解 (摘录+整理)

虚拟专用网络(Virtual Private Network,VPN),是一个在一个公用网络中建立一个临时的、安全的连接,是一条穿过公用网络的安全、稳定的隧道。
说得再通俗一点,VPN 实际上是"线路中的线路",所不同的是,由 VPN 组成的线路物理上是不存在的,是通过技术手段虚拟出来的。它为两台计算机建立一个逻辑上的专用通道,具有良好的保密和不受干扰性,使双方能进行自由而安全的点对点连接。
VPN 根据用户的身份和权限,在茫茫的广域网中为用户拉出一条专线。

VPN 是对企业内部网的扩展,它可以帮助异地用户、公司分支机构、商业伙伴及供应商同公司的内部网建立可信的安全连接,并保证数据的安全传输。
客户不必再租用昂贵的专线,只需购买 VPN 硬件设备或 VPN 软件产品以及一定的上网费用。


Cloud_28:VLAN 原理详解(摘录+整理)

1. 什么是 LAN ?
局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。局域网是封闭型的,可以由两台计算机组成,也可以由上千台计算机组成。
局域网一般是通过二层交换机建立的,二层交换机是一种基于 MAC 地址识别、能够完成封装转发数据包功能的网络设备。

2. 什么是广播 ?什么是广播域 ?
广播是一种信息的传播方式,指网络中的某一设备同时向网络中所有的其它设备发送数据,这个数据所能广播到的范围称为广播域。
广播域是指网络中所有能接收到同样广播消息的设备的集合。

3. 什么是 VLAN ?
局域网中的机器 A 想要和机器 B 通信,要在广播域中进行 ARP 广播,来获取机器 B 的 MAC 地址。
这种广播会影响到网络整体的传输性能。

图中,是一个由 5 台二层交换机(交换机1~5)以及连接大量客户机构成的局域网。
交换机 1 收到 ARP 请求后,会把它转发给除接收端口外的其它所有端口,同样,交换机 2 收到 ARP 请求后,也会把它转发给除接收端口外的其它所有端口,.....,最终 ARP 请求会被转发到同一网络中的所有客户机上。
如此一来,一方面广播信息消耗了网络整体的带宽;另一方面,收到广播信息的计算机还要消耗一部分 CPU 来对它进行处理。造成了网络带宽和 CPU 的无谓消耗。
请注意一下,这个 ARP 请求原本是为了获得计算机 B 的 MAC 地址而发出的,也就是说只要计算机 B 能收到就可以了,但结果却是传遍整个网络,导致所有的计算机都收到了它。

那么,网络上都有哪些广播信息呢?
(1)ARP 请求:建立 IP 地址和 MAC 地址的映射关系。
(2)RIP:一种路由协议,每隔 30 秒路由器都会对邻近的其它路由器广播一次路由信息。
(3)DHCP:用于自动设定 IP 地址的协议。
(4)NetBEUI:Windows下使用的网络协议。
(5)IPX:Novell Netware 使用的网络协议。
(6)Apple Talk:苹果公司的 Macintosh 计算机使用的网络协议。

因此,如果能够有效地分割广播域,就可以减少网络中不必要的消息广播。
在二层交换机上分割广播域的技术,就是 VLAN。

4. 如何实现 VLAN ?
在交换机上生成红、蓝两个 VLAN:同时设置端口 1 和 2 属于红色 VLAN;端口 3 和 4 属于蓝色 VLAN。
这样设置以后,广播消息只会转发给同属于一个 VLAN 的其它端口。
就这样,VLAN 通过限制广播转发的范围分割了广播域。可以理解为将一台交换机在逻辑上分割成了数台虚拟交换机。


注意,在交换机上设置 VLAN 后,如果未做其它处理,VLAN 之间是互不相通的。
明明接在同一台交换机上,但却无法通信,这个事实可能让人难以接受。
那么,如果 VLAN 之间需要通信时怎么办 ?
VLAN 之间的通信需要路由器提供中继服务,可以使用普通的路由器,也可以使用三层交换机。

5. VLAN 的划分方式

5.1 静态 VLAN
静态 VLAN 是基于端口的 VLAN,顾名思义,就是明确指定各端口属于哪个 VLAN 的设定方法。
由于需要一个个端口地指定,因此当网络中的计算机数目超过上百台后,设定操作就会变得烦杂无比。
并且,客户机每次变更所连端口,都必须同时更改该端口所属 VLAN,这显然不适合那些需要经常改变拓补结构的网络。


5.2 动态 VLAN
动态 VLAN 分为三类:
(1)基于 MAC 地址的 VLAN
(2)基于 IP 地址的 VLAN
(3)基于用户的 VLAN

5.2.1 基于 MAC 地址的 VLAN
通过查询并记录端口所连计算机上网卡的 MAC 地址来决定端口的所属 VLAN。即无论机器连在交换机哪个端口,该机器都属于同一个 VLAN。
如果计算机更换了网卡,还是需要更改设定。
由于是基于 MAC 地址决定所属 VLAN,因此这是一种在 OSI 第二层设定访问连接的办法。


5.2.2 基于 IP 地址的 VLAN
通过所连计算机的 IP 地址来决定端口所属 VLAN。即只要机器的 IP 地址不变,该机器都属于同一个 VLAN。
因此,与基于 MAC 地址的 VLAN 相比,基于 IP 地址的 VLAN 设定更为灵活,是一种在 OSI 的第三层设定访问连接的方法。


5.2.3 基于用户的 VLAN
根据交换机各端口连接的的计算机上的当前登录的用户来决定该端口属于哪个 VLAN。
这里的用户是操作系统的用户,是一种在 OSI 的第四层以上设定访问连接的方法。

6. 如何实现跨多台交换机的 VLAN ?
到此为止,讲的都是使用单台交换机如何设置 VLAN。如何实现跨多台交换机的 VLAN ?
如下图所示,此时,交换机 1 和交换机 2 该如何连接呢 ?


最简单的方法,当然是在交换机 1 和交换机 2 上各设一个红、蓝 VLAN 专用的接口,然后用网线互联。
但是,这个办法从扩展性和管理效率来看都不好。
比如,在现有网络基础上再新建 VLAN 时,为了让这个 VLAN 能够互通,就需要在交换机间连接新的网线。并且,VLAN 越多,交换机间互联所需的端口也越来越多,交换机端口的利用效率会很低,是对资源的一种浪费,也限制了网络的扩展。
为了避免这种低效率的连接方式,人们想办法让交换机间互联的网线集中到一根上,这就是汇聚链接(Trunk Link)。

6.1 什么是汇聚链接 ?
汇聚链接(Trunk Link)指的是能够转发多个不同 VLAN 的通信的端口。
汇聚链路上流通的数据帧,都被附加了用于识别分属于哪个 VLAN 的特殊信息。
用户只需要简单地将交换机间互联的端口设定为汇聚链接就可以了。
我们看看汇聚链接是如何实现跨越交换机间的 VLAN 的:
(1) A 发送的数据帧从交换机 1 经过汇聚链路到达交换机 2 时,在数据帧上附加了表示属于红色 VLAN 的标记。
(2) 交换机 2 收到数据帧后,经过检查 VLAN 标识后,发现这个数据帧是属于红色 VLAN 的,因此去除标记后,根据需要将原始的数据帧只转发给其它属于红色 VLAN 的端口。


另外,由于汇聚链路上流通着多个 VLAN 的数据,负载较重。因此,在设定汇聚链接时,必须支持 100Mbps 以上的传输速度。
另外,默认条件下,汇聚链接(端口)会转发交换机上存在的所有 VLAN 的数据。换一个角度看,可以认为汇聚链接(端口)同时属于交换机上所有的 VLAN。

6.2 VLAN 的汇聚方式
在交换机的汇聚链接上,通过对数据帧附加 VLAN 信息,可以构建跨越多台交换机的 VLAN。
附加VLAN信息的方法,最具有代表性的有:IEEE 802.1Q 和 ISL。

6.2.1 IEEE 802.1Q
IEEE 802.1Q,俗称“Dot One Q”,是经过IEEE认证的对数据帧附加VLAN识别信息的协议。
在此,请大家先回忆一下以太网数据帧的标准格式。
IEEE 802.1Q 所附加的 VLAN 识别信息,位于以太网数据帧中的“发送源 MAC 地址”与“类型”之间。具体内容为 2 字节的 TPID(Tag Protocol IDentifier)和 2 字节的 TCI(Tag Control Information),共计 4 字节。
TPID 的值固定为 0x8100,它标示网络帧承载的 802.1Q 类型,交换机通过它来确定数据帧内附加了基于 IEEE 802.1Q 的 VLAN 信息。
真正的 VLAN ID,保存在 TCI中,一共有 12 位,因此最多可供识别 4096 个 VLAN。
在数据帧中添加了 4 字节的内容,那么循环冗余校验(Cyclic Redundancy Check, CRC) 值也会有所变化。
而当数据帧离开汇聚链路时,TPID 和 TCI 会被去除,这时会重新计算 CRC 。


6.2.2 ISL(Inter Switch Link)
ISL 是 Cisco 支持的一种与 IEEE 802.1Q 类似的、用于在汇聚链路上附加 VLAN 信息的协议。
使用 ISL 后,每个数据帧头部都会被附加 26 字节的 ISL Header,并且在帧尾附带上通过对包括 ISL Header 在内的整个数据帧进行计算后得到的 4字节 CRC 值。
总共增加了 30 字节的信息。
在使用 ISL 的环境下,由于原先的数据帧及其 CRC 都被完整保留,无需重新计算 CRC,因此当数据帧离开汇聚链路时,只要简单地去除 ISL Header和新 CRC 就可以了。



7. 如何实现 VLAN 之间的路由 ?
在使用路由器进行 VLAN 间路由时,与构建横跨多台交换机的 VLAN 时的情况类似,还是会遇到“该如何连接路由器与交换机”这个问题。
路由器和交换机的接线方式,大致有以下两种:
(1)将路由器与交换机上的每个 VLAN 分别连接。
(2)不论 VLAN 有多少个,路由器与交换机之间使用汇聚链接,只用一条网线连接。
采用(1)的办法,不难想象它的扩展性很成问题。每增加一个新的 VLAN,都需要消耗路由器的端口和交换机上的访问链接,而且还需要重新布设一条网线。
采用(2)的办法,即使之后在交换机上新建 VLAN,仍只需要一条网线连接交换机和路由器。只需要在路由器上新设一个对应新 VLAN 的子接口就可以了。


因此一般采用第 (2) 种方法,具体实现过程为:
(1)首先将用于连接路由器的交换机端口设为汇聚链接(Trunk Link),而路由器上的端口也必须支持汇聚链路。
(2)双方用于汇聚链路的协议自然也必须相同。
(3)在路由器上定义对应各个 VLAN 的子接口。尽管实际与交换机连接的物理端口只有一个,但可以把它分割为多个虚拟端口。
VLAN 将交换机从逻辑上分割成了多台,因而用于 VLAN 间路由的路由器,也必须拥有分别对应各个 VLAN 的虚拟接口。

7.1 同一 VLAN 内的通信

(1)计算机 A 发出 ARP 请求信息,请求解析 B 的 MAC 地址。
(2)交换机收到数据帧后,检索 MAC 地址列表中与收信端口同属一个 VLAN 的表项。结果发现,计算机 B 连接在端口 2 上,于是交换机将数据帧转发给端口 2,最终计算机 B 收到该帧。
收发信双方同属一个 VLAN 之内的通信,一切处理均在交换机内完成。

7.2 不同 VLAN 间的通信

(1)计算机 A 从通信目标的 IP 地址(192.168.2.1)得出 计算机 C 与本机不属于同一个网段。因此会向设定的默认网关发数据帧。在发送数据帧之前,需要先用 ARP 获取路由器的 MAC 地址。
(2)得到路由器的 MAC 地址 R 后,接下来就是按图中所示的步骤发送往 计算机 C 的数据帧。

参考文献:
1. http://blog.csdn.net/phunxm/article/details/9498829/