Docker学习二(部署一个可以自由获取环境变量的SpringWeb)

引言

Dokcer学习一 已经可以简单的安装一个Docker 容器并暴露某个端口给外部使用了。
安装不是目的, 应用才是王道。 发布一个快速部署的Web容器才是Docker的用处。

当前有需求:

Tomcat启动在Docker容器中,它需要告知注册中心自己暴露对外的Host以及Port。
传统的方式只可以获取到Tomcat启动时绑定的端口, 需要获取Docker对外映射的宿主机的Host以及开放的端口。

环境部署

应用程序目录:/opt

安装JDK.

# wget http://download.oracle.com/otn-pub/java/jdk/8u91-b14/jdk-8u91-linux-x64.tar.gz

# ls
jdk-8u91-linux-x64.tar.gz

# tar -xzvf jdk-8u91-linux-x64.tar.gz

# jdk1.8.0_91/bin/java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

安装Tomcat

# wget http://mirrors.hust.edu.cn/apache/tomcat/tomcat-8/v8.5.0/bin/apache-tomcat-8.5.0.tar.gz
# tar -xzvf apache-tomcat-8.5.0.tar.gz 

# apache-tomcat-8.5.0/bin/startup.sh 
Using CATALINA_BASE:   /opt/apache-tomcat-8.5.0
Using CATALINA_HOME:   /opt/apache-tomcat-8.5.0
Using CATALINA_TMPDIR: /opt/apache-tomcat-8.5.0/temp
Using JRE_HOME:        /opt/jdk1.8.0_91
Using CLASSPATH:       /opt/apache-tomcat-8.5.0/bin/bootstrap.jar:/opt/apache-tomcat-8.5.0/bin/tomcat-juli.jar
Tomcat started.

# curl -I -s 127.0.0.1:8080
HTTP/1.1 200 


将程序委托给Supervisord管理

# echo '#! /bin/bash

export JAVA_HOME=/opt/jdk1.8.0_91
export CLASSPATH=.:$JAVA_HOME:/lib:$JAVA_HOME/jre/lib:
export PATH=$PATH:$JAVA_HOME/bin

/opt/apache-tomcat-8.5.0/bin/startup.sh' > /etc/supervisor/conf.d/tomcat.sh

# echo '[supervisord]
nodaemon=true
[program:tomcat]
command=/etc/supervisor/conf.d/tomcat.sh' > /etc/supervisor/conf.d/tomcat.conf

当然, 每次变更一定要记住提交。

# docker commit acf27ff06413 tomcat

此时就可以开始访问Tomcat服务了。

# docker run -it -d -p 22 -p 8080 tomcat /usr/bin/supervisord

# docker ps -a
IMAGE       STATUS              PORTS                                         
tomcat      Up 6 seconds        0.0.0.0:32774->22/tcp, 0.0.0.0:32773->8080/tcp

# curl -I 127.0.0.1:32773
HTTP/1.1 200

将Docker镜像导出以便于本地使用

# docker export d3219e60d2b2 > docker_tomcat.tar
# gzip docker_tomcat.tar
# docker start d3219e60d2b2

# docker ps -a
CONTAINER ID     COMMAND                     PORTS                                         
d3219e60d2b2     "/usr/bin/supervisord"      0.0.0.0:32776->22/tcp, 0.0.0.0:32775->8080/tcp

# docker cp docker_tomcat.tar.gz 63730c15f13e:/opt/apache-tomcat-8.5.0/webapps/ROOT/docker_tomcat.html

# wget -b -c 公网ip:32775/docker_tomcat.html > docker_tomcat.tar.gz

此处分享:
服务器选购需谨慎。 上方下载共计耗时12小时。 选择更适合自己的方式很重要。

Web部署

源码展示 (非必须)

UnmodifiedSet.java

import java.util.Collection;
import java.util.Iterator;
import java.util.Set;

/**
 *
 * 毕竟将Guava引入到pom里面去也是件比较麻烦的事情, 因此直接给定一个默认的实现
 *
 * @author http://blog.csdn.net/qyp199312/
 * @since 2016-05-09
 */
public class UnmodifiedSet<E> implements Set<E> {

    protected transient int size = 0;

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public boolean contains(Object o) {
        return false;
    }

    @Override
    public Iterator<E> iterator() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Object[] toArray() {
        throw new UnsupportedOperationException();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean remove(Object o) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException();
    }
}

UnmodifiedSet.java

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 *
 * 毕竟将Guava引入到pom里面去也是件比较麻烦的事情, 因此直接给定一个默认的实现
 *
 * @see java.util.Collections.UnmodifiableMap
 * @author http://blog.csdn.net/qyp199312/
 * @since 2016-05-09
 */
public class UnmodifiableMap<K, V> implements Map<K, V> {

    @Override
    public int size() {
        return entrySet().size();
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    @Override
    public boolean containsKey(Object key) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean containsValue(Object value) {
        throw new UnsupportedOperationException();
    }

    @Override
    public V get(Object key) {
        throw new UnsupportedOperationException();
    }

    @Override
    public V put(K key, V value) {
        throw new UnsupportedOperationException();
    }

    @Override
    public V remove(Object key) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<K> keySet() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Collection<V> values() {
        throw new UnsupportedOperationException();
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        throw new UnsupportedOperationException();
    }

    @Override
    public String toString() {
        Iterator<Entry<K,V>> i = entrySet().iterator();
        if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

}

EnvironmentUtils.java

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 * 可获取到 java "-D key=value" 的环境变量
 *
 * @author http://blog.csdn.net/qyp199312/
 * @since 2016-05-09
 */
public class EnvironmentUtils {

    private static final transient Map<String, String> enVir = System.getenv();
    private static final transient Map<Object, Object> profile = System.getProperties();

    private static final ConcurrentHashMap<String, String> properties = new ConcurrentHashMap<String, String>();

    private static final Set<String> fileName = new HashSet<String>();

    static {
        cp(enVir, properties);
        cp(profile, properties);
    }

    private static final <K, V> void cp(Map<K, V> source,
                                        Map<String, String> target) {
        for (Entry<K, V> entry : source.entrySet()) {
            target.put(String.valueOf(entry.getKey()), String.valueOf(entry.getValue()));
        }
    }

    public static Map<String, String> environment() {
        return new UnmodifiableMap<String, String>() {

            @Override
            public int size() {
                return properties.size();
            }

            @Override
            public String get(Object k) {
                return properties.get(k);
            }

            @Override
            public boolean containsKey(Object k) {
                return properties.containsKey(k);
            }

            @Override
            public Set<String> keySet() {
                return new UnmodifiedSet<String>() {

                };
            }

        };
    }

    public static Map<String, String> load(String filePath) throws IOException {
        if (fileName.contains(filePath)) {
            return environment();
        }

        InputStream is = EnvironmentUtils.class.getResourceAsStream(filePath);
        if (is == null) {
            is = EnvironmentUtils.class.getClassLoader().getResourceAsStream(filePath);
        }
        if (is == null) {
            File file = new File(filePath);
            if (file.exists()) {
                is = new FileInputStream(file);
            }
        }
        if (is != null) {
            Properties p = new Properties();
            p.load(is);
            cp(p, properties);
            fileName.add(filePath);
            is.close();
        }
        return environment();
    }

    public static String get(String k) {
        return properties.get(k);
    }

    public static String getWithReplace(String k, String replace) {
        String v;
        return (v = properties.get(k)) == null ? replace : v;
    }

    public static String getIfNull(String k) {
        return getWithReplace(k, "");
    }

    /**
     * 获取字段模糊匹配开头Value <p>
     * 不使用缓存, 在多线程环境下执行 #load 的时候可能会出现数据错误
     *
     * @param k 模糊匹配的起始
     * @return
     */
    public static Map<String, String> getStart(final String k) {
        return new UnmodifiableMap<String, String>() {

            @Override
            public String get(Object key) {
                if (String.valueOf(key).startsWith(k)) {
                    return properties.get(k);
                }
                return null;
            }

            @Override
            public Set<Entry<String, String>> entrySet() {
                return new UnmodifiedSet<Entry<String, String>>() {

                    {
                        Iterator it = iterator();
                        while (it.hasNext()) {
                            super.size ++;
                        }
                    }

                    public Iterator<Entry<String, String>> iterator() {

                        return new Iterator<Entry<String, String>>() {

                            private Iterator<Entry<String, String>> data = properties.entrySet().iterator();
                            private Entry<String, String> next = null;
                            private int size = properties.size();
                            private int current = size;

                            @Override
                            public boolean hasNext() {
                                return (next = nextData()) != null;
                            }

                            public Entry<String, String> nextData() {
                                int size = this.size;
                                while (data.hasNext()) {
                                    if (current != size) {
                                        size --;
                                        continue;
                                    }
                                    Entry<String, String> next = data.next();
                                    if (next.getKey().startsWith(k)) {
                                        current --;
                                        return next;
                                    }
                                }
                                return null;
                            }

                            @Override
                            public Entry<String, String> next() {
                                return next;
                            }

                            public void remove() {
                                throw new UnsupportedOperationException("remove");
                            }
                        };
                    }
                };
            }
        };
    }
}

GateController.java

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class GateController {

    @RequestMapping("key")
    @ResponseBody
    public String key(@RequestParam("key") String key) {
        return EnvironmentUtils.get(key);
    }

    @RequestMapping("start")
    @ResponseBody
    public String start(@RequestParam("key") String key) {
        return String.valueOf(EnvironmentUtils.getStart(key));
    }
}

开始访问

部署并启动服务

# docker run -it -d -p 22 -p 8080 tomcat /usr/bin/supervisord

# docker ps -a
IMAGE    COMMAND                  PORTS                                        
tomcat   "/usr/bin/supervisord"   0.0.0.0:32769->22/tcp, 0.0.0.0:32768->8080/tcp

# docker cp web2-1.0-SNAPSHOT.war ccb21ae25ca7:/opt/apache-tomcat-8.5.0/webapps/

访问示例

# curl 127.0.0.1:32768/web2-1.0-SNAPSHOT/start?key=java.vm
{java.vm.vendor=Oracle Corporation, java.vm.specification.version=1.8, java.vm.specification.vendor=Oracle Corporation, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, java.vm.specification.name=Java Virtual Machine Specification, java.vm.info=mixed mode, java.vm.version=25.91-b14}

# curl 127.0.0.1:32768/web2-1.0-SNAPSHOT/key?key=java.vm.info
mixed mode

Get Port(Tomcat Only)

让Web获取Docker运行容器的端口

    @RequestMapping("docker_port")
    @ResponseBody
    public String docker_port()
            throws MalformedObjectNameException, AttributeNotFoundException, MBeanException, ReflectionException,
            InstanceNotFoundException {
        MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        Set<ObjectName> set = server.queryNames(
                new ObjectName("*:type=Connector,*"),
                Query.match(
                        Query.attr("protocol"),
                        Query.value("HTTP/1.1"))
        );
        if (set != null) {
            for (ObjectName name : set) {
                Object scheme = server.getAttribute(name, "scheme");
                String port = name.getKeyProperty("port");
                if (scheme != null && "http".equalsIgnoreCase(scheme.toString())) {
                    return port;
                }
            }
        }
        return String.valueOf(EnvironmentUtils.getStart("port"));
    }

测试结果:

# curl 127.0.0.1:49164/navigation-0.0.1-SNAPSHOT/docker_port   
8080

让Web获取宿主机端口

web程序无法获取容器绑定端口。但是可以通过一定的方式得知:

  • Tomcat遵循JMX规范,启动时将告知JMX启动端口以及Host,由此获取即可
  • 将启动端口以及Host配置在环境变量里面,直接获取

脚本:

$ 在 bin/startup.sh倒数第二行加入:
export JAVA_OPTS=" $JAVA_OPTS -Dhost_port=10086 -Dhost_mapping=domain.com"

测试结果:

# curl 127.0.0.1:49164/navigation-0.0.1-SNAPSHOT/key?key=host_mapping
domain.com

# curl 127.0.0.1:49164/navigation-0.0.1-SNAPSHOT/key?key=host_port
10086

通过发布时加入环境变量的方式可以轻松获取到宿主机Host以及映射给Docker Tomcat的端口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值