Spring Cloud开发实践(四): Docker部署

目录

Docker的安装和命令可以参考 https://www.cnblogs.com/milton/p/9866963.html

资源规划

这一步要区分传统资源和Docker资源, 为后面的细节定好基础. 因为Docker的特性, 适合于占CPU和MEM资源, 但是对HDD资源要求不高的应用. 不适合用于对数据有持久化要求以及安全性要求较高的应用, 例如MySQL和日志系统.

在开发和测试环境, 你可以随手启动一个MySQL来配合开发, 但是在生产环境目前还是不太现实的, 需要整套运维工具跟进对Docker的配合后才能实现足够的安全性.

生产环境涉及的资源如下:

  • 堡垒机, VPN, 网关, 用于生产环境的底层访问控制, >= 1
  • 存档备份, 低性能大容量带冗余的存储, >=1
  • CI工具, Jenkins, 用于在生产环境进行自动部署, =1
  • RMDB 数据库, MySQL等, >=2
  • NoSQL 数据库, MongoDB, Redis 等, >=2
  • 缓存服务, Ehcache, Redis等, >=1
  • 文件服务, FastDF等, >=1
  • 配置和服务注册服务, Zookeeper, EurekaServer等, >=3
  • 日志服务, Kafka等, >=1
  • 搜索服务, Elasticsearch等, >=1
  • JVM节点, 这是用于运行jar项目的节点, =N
  • WEB服务, Nginx等, >=2

其中与业务开发关系最大, 适合用Docker进行部署的就是JVM节点部分, 还有缓存服务, WEB服务, 日志服务, 配置和注册服务, 也是适合通过Docker管理的. 这边假定仅仅使用Docker配置JVM节点.

网络划分

三类网络的私有地址范围

10.0.0.0       -  10.255.255.255
172.16.0.0     -  172.31.255.255
192.168.0.0    - 192.168.255.255

与环境现有子网综合考虑. 需要达到的目标是

  • 每个容器有独立的 IP,运维就可以像连接虚机一样通过ssh连接容器
  • 需要跨宿主机之间的容器通信
  • 访问控制, 安全性和隔离性
  • 性能损失尽量小

如果是在IDC自建机房的规划, 网络的实现方案可以参考这一篇 http://www.cnblogs.com/sammyliu/p/5926343.html

方案一: 基于bridge

将默认的docker bridge网桥替换为 linuxbridge, 把 linuxbridge 网段的 ip 加入到容器里, 实现容器与传统环境应用的互通.

首先在宿主机上添加一个linux bridge, 把宿主机网卡(可以是物理机的, 也可以是虚拟机的), 把这个网卡加入bridge里面, bridge配上网卡原本的管理IP.
然后创建一个新的docker bridge网络, 指定bridge子网, 并将该网络的网桥绑定到上一步创建的网桥上.

docker network create --gateway 10.199.45.200 --subnet 10.199.45.0/24 -o com.docker.network.bridge.name=br-oak --aux-address "DefaultGatewayIPv4=10.199.45.1" oak-net

容器启动时候指定网络为第二步中创建的bridge网络, 同时为容器指定一个该网络子网内的IP. 容器启动后网络IP默认即可与外界互通.

这是最容易实现的方式, 如果要实现Docker在网络内跨宿主机迁移, 需要宿主机位于同一VLAN, 并且为了避免IP冲突, 要实现整个VLAN的IP的统一管理.

方案二: 基于Calico

这个还没有仔细研究, 不介绍了.

安全组配置

如果是在公共云服务例如阿里云上, 并且docker数量也比较小的情况下, 就直接用宿主机端口映射就好了. 可以创建子网, 可以配置阿里云的安全组进行访问控制. 根据 对不同目标(外网, 安全组)的访问许可, 设置不同的安全组. 安全组的设置原则是, 1) 全局禁止安全组之间互访, 2) 设置资源组 + 此资源的授权组. 例如

全局组: 全部虚机
可访问全局组的: 堡垒机, VPN

外网: 默认
可访问外网的: 如公共接口模块, CI工具, 可分为一组

DB组: 只允许从特定组访问的, 分为一组
可访问DB组的: 如公共接口模块, 备份计划模块等, 可分为一组

COMMON组:
可访问COMMON组的: 下游边界业务模块等

部署Spring Cloud Jar

编写Dockerfile. 因为java:8默认不带vi, nano, ifconfig这些命令, 建议基于java:8制作一个自己的image, 把nano和net-tools装上, 这样方便运维查看容器环境信息.

FROM java:8
ADD target/scot-eureka.jar scot-eureka.jar
ENTRYPOINT ["java", "-jar", "/scot-eureka.jar"]

一个更完整的Dockerfile例子

FROM openjdk:8-jre-alpine
VOLUME ./mysql:/var/lib/mysql
ADD /build/libs/application.jar app.jar
ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar

or

FROM openjdk:8-jre
RUN echo "Europe/Berlin" > /etc/timezone && dpkg-reconfigure -f noninteractive tzdata
RUN mkdir /data
WORKDIR /data
ADD myapp.jar /data/myapp.jar

ENV springprofiles="" \
    MAXRAMIFNOLIMIT=4096

ENTRYPOINT MAXRAM=$(expr `cat /sys/fs/cgroup/memory/memory.limit_in_bytes` / 1024 / 1024) && \
           MAXRAM=$(($MAXRAM>$MAXRAMIFNOLIMIT?$MAXRAMIFNOLIMIT:$MAXRAM))m && \
           echo "MaxRam: $MAXRAM" && \
           java -XX:MaxRAM=$MAXRAM -Djava.security.egd=file:/dev/./urandom -jar -Dspring.profiles.active="$springprofiles" myapp.jar

#when "-XX:+UseCGroupMemoryLimitForHeap" isn't experimental anymore, you can use the following
#ENTRYPOINT java -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom -jar -Dspring.profiles.active="$springprofiles" myapp.jar           
             
EXPOSE 8080

制作Docker Image

docker build -t scot-eureka .

启动Docker container

docker run -d --name eureka -p 8762:8761 scot-eureka
# or
docker run -m 1024MB -d --name container-name -p 9100:9100 -e JAVA_OPTS="-Xmx512M -Xms512M -XX:MaxRAM=1024M" imagename:tag

其中8761是jar本身打开的端口, 8762是从外部(容器IP或者本机IP)去访问的端口, 可以通过 docker ps 或者 docker port [docker-name | container-id] 查看映射关系

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
2ea7392a8b51        scot-eureka         "java -jar /scot-eur…"   25 minutes ago      Up 25 minutes       0.0.0.0:8762->8761/tcp   eureka

这时候就可以通过容器或本机的 IP:8762 端口去访问eureka.jar启动的服务了.

在实际环境中, 需要事先配置好IP信息, package时写入jar, 给docker创建自定义的network, 在run的时候--net 指定network, --ip指定IP. 或者使用zookeeper提供的配置服务, 在启动时实时获取IP配置.

对JVM内存参数的要求

在docker容器中可以查看默认的heap size, 这个和docker run -m 指定的内存大小是无关的, 只和宿主机内存容量有关, 一般为这个容量的1/4 (参考 https://blog.csanchez.org/2017/05/31/running-a-jvm-in-a-container-without-getting-killed/ )

$ java -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 1.67G
    Ergonomics Machine Class: server
    Using VM: Java HotSpot(TM) 64-Bit Server VM
 
java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)

在容器中

$ docker run -m 100MB openjdk:8u121 java -XshowSettings:vm -version
VM settings:
    Max. Heap Size (Estimated): 444.50M
    Ergonomics Machine Class: server
    Using VM: OpenJDK 64-Bit Server VM

在Java8之前, Max memory = [-Xmx] + [-XX:MaxPermSize] + number_of_threads * [-Xss] 在Java8之后用Metaspace代替了PermGen, 而Metaspace仅受主机实际内存限制. 仅仅使用-Xmx限制内存大小是不够的, 需要用-XX:MaxRAM限制实际使用的内存大小, 才能避免被Docker Daemon关闭. 在8u131+之后, 可以用 UseCGroupMemoryLimitForHeap 参数进行检测

$ docker run -m 2G -ti --rm openjdk:8-jdk java -XshowSettings:vm -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1 -version
Max. Heap Size (Estimated): 1.74G

It turns out that Java VisualVM doesn't know anything about OffHeap, and, as a result, it can be very tricky to investigate your Java application's memory consumption using this tool. Also, it's crucial to understand the JVM options you use. As for me, it was the discovery that specifying -Xmx=512m tells the JVM to allocate a 512mb heap. It’s not telling the JVM to limit its entire memory usage to 512mb. There are code caches and all sorts of other off heap data.

For specifying total memory, you should use the -XX:MaxRAM parameter. Be aware that with MaxRam=512m your heap will be approximately 250mb. Be careful and pay attention to your app JVM options.

posted on 2018-10-26 18:38  Milton  阅读(444)  评论(0编辑  收藏  举报

导航