zoukankan      html  css  js  c++  java
  • Apollo(阿波罗) 分布式配置中心

     

    第一部分: Apollo简介

    随着程序功能的日益复杂,程序的配置日益增多:各种功能的开关、参数的配置、服务器的地址……

    对程序配置的期望值也越来越高:配置修改后实时生效,灰度发布,分环境、分集群管理配置,完善的权限、审核机制……

    在这样的大环境下,传统的通过配置文件、数据库等方式已经越来越无法满足开发人员对配置管理的需求。

    Apollo配置中心应运而生!

    1、Apollo简介

    Apollo支持4个维度管理Key-Value格式的配置:

    application (应用)
    environment (环境)
    cluster (集群)
    namespace (命名空间)
    

    配置基本概念

    配置是独立于程序的只读变量
        配置独立于程序的,同一个程序在不同的配置下有不同的行为
        配置对于程序是只读的,程序通过读取配置来改变自己的行为,程序不应该去改变配置
    配置伴随应用的整个生命周期
        配置贯穿于应用的整个生命周期,应用在启动时通过读取配置来初始化,在运行时根据配置调整行为
    
    配置可以有多种加载方式
          配置文件、环境变量、启动参数、基于数据库
    
    配置需要治理
          权限控制(由于配置能改变程序的行为,不正确的配置甚至能引起灾难,所以对配置的修改必须有比较完善的权限控制)
          不同环境、集群配置管理(同一份程序在不同的环境(开发,测试,生产)、不同的集群(如不同的数据中心)经常需要有不同的配置,所以需要有完善的环境、集群配置管理)
     
    

    为什么需要Apollo

    统一管理不同环境、不同集群的配置
          Apollo提供了统一界面集中式管理不同环境(environment)、不同集群(cluster)、不同命名空间(namespace)的配置。
          同一份代码部署在不同的集群,可以有不同的配置,比如zookeeper的地址等
          通过命名空间(namespace)可以很方便地支持多个不同应用共享同一份配置,同时还允许应用对共享的配置进行覆盖
    
    配置修改实时生效(热发布)
          用户在Apollo修改完配置并发布后,客户端能实时(1秒)接收到最新的配置,并通知到应用程序
    
    版本发布管理
        所有的配置发布都有版本管理,从来很方便的支持配置回滚
    
    灰度发布
        支持配置的灰度发布,比如点了发布后,只对部分应用实例生效,等观察一段时间没问题后再推给所有应用实例
    
    权限管理、发布审核、操作审计
         应用和配置的管理都有完善的权限管理机制
         所有操作都有审计日志
    客户端配置信息监控
          可以在界面上方便地看到配置在被哪些实例使用
    提供Java和.Net原生客户端
    
    提供开放平台API
    
    部署简单
    

     2、Apollo配置中心设计

    基础模型

    用户在配置中心对配置进行修改并发布
    配置中心通知Apollo客户端有配置更新
    Apollo客户端从配置中心拉取最新的配置、更新本地配置并通知到应用

    架构模块

    Config Service提供配置的读取、推送等功能,服务对象是Apollo客户端
    
    Admin Service提供配置的修改、发布等功能,服务对象是Apollo Portal(管理界面)
    
    Config Service和Admin Service都是多实例、无状态部署,所以需要将自己注册到Eureka中并保持心跳
    
    在Eureka之上我们架了一层Meta Server用于封装Eureka的服务发现接口
    
    Client通过域名访问Meta Server获取Config Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Client侧会做load balance、错误重试
    
    Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Portal侧会做load balance、错误重试
    
    为了简化部署,我们实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中
    

     

     为什么需要eureka

    由于config-server和admin-server会部署多个,哪个前端client(比如JAVA)和portal怎么找到对应的config和admin呢?Apollo配置中心是基于Spring Cloud开发的,我们引入eureka作为服务注册及服务发现,
    Client和Portal通过eureka获取到对应config-server和admin-server的地址,然后直接于config-server或admin-server联系

    各模块概要介绍

    Config Service
    
    提供配置获取接口
    提供配置更新推送接口(基于Http long polling)
    服务端使用Spring DeferredResult实现异步化,从而大大增加长连接数量
    目前使用的tomcat embed默认配置是最多10000个连接(可以调整),使用了4C8G的虚拟机实测可以支撑10000个连接,所以满足需求(一个应用实例只会发起一个长连接)。
    接口服务对象为Apollo客户端
    
    
    Admin Service
    
    提供配置管理接口
    提供配置修改、发布等接口
    接口服务对象为Portal
    
    Meta Server
    
    Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port)
    Client通过域名访问Meta Server获取Config Service服务列表(IP+Port)
    Meta Server从Eureka获取Config Service和Admin Service的服务信息,相当于是一个Eureka Client
    增设一个Meta Server的角色主要是为了封装服务发现的细节,对Portal和Client而言,永远通过一个Http接口获取Admin Service和Config Service的服务信息,而不需要关心背后实际的服务注册和发现组件
    Meta Server只是一个逻辑角色,在部署时和Config Service是在一个JVM进程中的,所以IP、端口和Config Service一致
    
    Eureka
    
    基于Eureka和Spring Cloud Netflix提供服务注册和发现
    Config Service和Admin Service会向Eureka注册服务,并保持心跳
    为了简单起见,目前Eureka在部署时和Config Service是在一个JVM进程中的(通过Spring Cloud Netflix)
    
    Portal
    
    提供Web界面供用户管理配置
    通过Meta Server获取Admin Service服务列表(IP+Port),通过IP+Port访问服务
    在Portal侧做load balance、错误重试
    
    Client
    Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能
    通过Meta Server获取Config Service服务列表(IP+Port),通过IP+Port访问服务
    在Client侧做load balance、错误重试
    

      

    服务端设计

    配置发布后的实时推送设计

     

    上图简要描述了配置发布的大致过程:
    
    用户在Portal操作配置发布
    Portal调用Admin Service的接口操作发布
    Admin Service发布配置后,发送ReleaseMessage给各个Config Service
    Config Service收到ReleaseMessage后,通知对应的客户端
    

    实现方式如下:
    
    Admin Service在配置发布后会往ReleaseMessage表插入一条消息记录,消息内容就是配置发布的AppId+Cluster+Namespace,参见DatabaseMessageSender
    Config Service有一个线程会每秒扫描一次ReleaseMessage表,看看是否有新的消息记录,参见ReleaseMessageScanner
    Config Service如果发现有新的消息记录,那么就会通知到所有的消息监听器(ReleaseMessageListener),如NotificationControllerV2,消息监听器的注册过程参见ConfigServiceAutoConfiguration
    NotificationControllerV2得到配置发布的AppId+Cluster+Namespace后,会通知对应的客户端
    

      

    Config Service通知客户端的实现方式

    那NotificationControllerV2在得知有配置发布后是如何通知到客户端的呢?
    
    实现方式如下:
    
    客户端会发起一个Http请求到Config Service的notifications/v2接口,也就是NotificationControllerV2,参见RemoteConfigLongPollService
    NotificationControllerV2不会立即返回结果,而是通过Spring DeferredResult把请求挂起
    如果在60秒内没有该客户端关心的配置发布,那么会返回Http状态码304给客户端
    如果有该客户端关心的配置发布,NotificationControllerV2会调用DeferredResult的setResult方法,传入有配置变化的namespace信息,同时该请求会立即返回。客户端从返回的结果中获取到配置变化的namespace后,会立即请求Config Service获取该namespace的最新配置。
    

      

     

    客户端设计

    上图简要描述了Apollo客户端的实现原理:
    
    客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
    客户端还会定时从Apollo配置中心服务端拉取应用的最新配置。
        这是一个fallback机制,为了防止推送机制失效导致配置不更新
        客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
        定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定System Property: apollo.refreshInterval来覆盖,单位为分钟
    
    客户端从Apollo配置中心服务端获取到应用的最新配置后,会保存在内存中
    客户端会把从服务端获取到的配置在本地文件系统缓存一份
        在遇到服务不可用,或网络不通的时候,依然能从本地恢复配置
    应用程序可以从Apollo客户端获取最新的配置、订阅配置更新通知


    总结:

    推拉结合
    保持长连接,配置实时推送
    定期拉配置(fallback)
    配置缓存在内存
    本地缓存一份
    应用程序
    通过apollo客户端获取最新配置
    订阅配置更新通知

     

    3  客户端获取配置

    Application.yml

    #启动加载apollo
    apollo:
      bootstrap:
        enabled: true
        namespaces:  application,datasource
    #拦截feign调用 后去request请求参数开启
    hystrix:
      command:
        default:
          execution:
            isolation:
              strategy: SEMAPHORE
    logging:
      config: classpath:logback-spring.xml
    

    src/main/resources/META-INF/app.properties 

    # test
    app.id=100003
    

    配置中心地址

    有几种传递metaserver地址的方式

    1)启动参数: -Ddev_meta=http://192.168.31.20
    
    2)apollo-core.jar中添加apollo-env.properties 

    3)classpath中单独放一份:apollo-env.properties (src/main/resources/apollo-env.properties)
    dev.meta=http://192.168.31.20
    fat.meta=http://172.16.6.190:8081
    uat.meta=http://172.16.6.190:8083
    pro.meta=http://apollo.glp168.com

    运行环境Env的方式

    1)启动参数: -Denv=ENV
    
    2)配置文件(推荐)
    
    Linux:  /opt/settings/server.properties
    
    Windows: C:optsettingsserver.properties
    
    支持:DEV/FAT/UAT/PRO/LOCAL
    3)环境变量 ENV


    一个环境中的一个app,在不同的集群中可以有不同的配置

    1)启动参数: -Dapollo.cluster=app_cluster_v1
    
    2)通过配置文件
    Linux        /opt/settings/server.properties
    windows   C:optsettingsserver.properties
    

    本地缓存

    Linux:   /opt/data/{appid}/config-cache
    
    windows:   C:optdata{appid}config-cache
    
    注意应用需要有读写权限
    
    文件名: {appId}-{cluster}-{namespace}:properties

    案例:

    [root@bss-core-df948cbb8-vv98w config-cache]# pwd
    /opt/data/100004/config-cache
    [root@bss-core-df948cbb8-vv98w config-cache]# ll
    total 8
    -rw-r--r-- 1 root root 1021 8月 12 10:49 100004+default+application.properties
    -rw-r--r-- 1 root root 469 8月 12 10:49 100004+default+datasource.properties

      

    Maven Dependency

        <dependency>
            <groupId>com.ctrip.framework.apollo</groupId>
            <artifactId>apollo-client</artifactId>
            <version>1.1.0</version>
        </dependency>
    

      

    API使用方式

    获取默认namespace的配置(application)

    Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
    String someKey = "someKeyFromDefaultNamespace";
    String someDefaultValue = "someDefaultValueForTheKey";
    String value = config.getProperty(someKey, someDefaultValue);
    

    通过上述的config.getProperty可以获取到someKey对应的实时最新的配置值。

    另外,配置值从内存中获取,所以不需要应用自己做缓存

    监听配置变化事件

    Config config = ConfigService.getAppConfig(); //config instance is singleton for each namespace and is never null
    config.addChangeListener(new ConfigChangeListener() {
        @Override
        public void onChange(ConfigChangeEvent changeEvent) {
            System.out.println("Changes for namespace " + changeEvent.getNamespace());
            for (String key : changeEvent.changedKeys()) {
                ConfigChange change = changeEvent.getChange(key);
                System.out.println(String.format("Found change - key: %s, oldValue: %s, newValue: %s, changeType: %s", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType()));
            }
        }
    });
    

      

    获取公共Namespace的配置

    String somePublicNamespace = "CAT";
    Config config = ConfigService.getConfig(somePublicNamespace); //config instance is singleton for each namespace and is never null
    String someKey = "someKeyFromPublicNamespace";
    String someDefaultValue = "someDefaultValueForTheKey";
    String value = config.getProperty(someKey, someDefaultValue);
    

      

    第二部分:部署案例

    Portal部署一套,用于管理Dev,FAT,UAT,PRO环境

    MetaServer/ConfigService/AdminService  需要每个环境独立部署,每个环境独立数据库DB

    EurekaServer和MetaServer和ConfigService住在同一个JVM进程,AdminService使用另一个JVM进程

    AdminService和ConfigService使用同一个数据库,PortalService使用不同的数据库

    ApolloConfigDB: Eureka服务URL列表,各环境不同
    
    update apolloconfigbetadb.ServerConfig set ServerConfig.Value="http://config-p.aa.com/eureka" where ServerConfig.Key="eureka.service.url";
    
    ApolloPortalDB: 调整支持多个环境
    
    select * from ServerConfig;
    
    update Serverconfig set Value="dev,fat,uat,pro" where Id=1;
    

    在Kubernetes平台运行ConfigService和AdminService、PortalService (版本以:1.5.1)

    1)部署ConfigService

    第一步:制作镜像

    https://github.com/ctripcorp/apollo/releases/download/v1.5.1/apollo-configservice-1.5.1-github.zip  软件包

    https://raw.githubusercontent.com/ctripcorp/apollo/1.5.1/scripts/db/migration/configdb/V1.0.0__initialization.sql  数据库

    startup.sh脚本更改  

    [root@master01 scripts]# cat startup.sh
    #!/bin/bash
    SERVICE_NAME=apollo-configservice
    ## Adjust log dir if necessary
    LOG_DIR=/opt/logs/apollo-config-server
    ## Adjust server port if necessary
    SERVER_PORT=8080
    APOLLO_CONFIG_SERVICE_NAME=$(hostname -i)
    SERVER_URL="http://${APOLLO_CONFIG_SERVICE_NAME}:${SERVER_PORT}"
    
    ## Adjust memory settings if necessary
    #export JAVA_OPTS="-Xms6144m -Xmx6144m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=4096m -XX:MaxNewSize=4096m -XX:SurvivorRatio=8"
    
    ## Only uncomment the following when you are using server jvm
    #export JAVA_OPTS="$JAVA_OPTS -server -XX:-ReduceInitialCardMarks"
    
    ########### The following is the same for configservice, adminservice, portal ###########
    export JAVA_OPTS="$JAVA_OPTS -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=9 -XX:+DisableExplicitGC -XX:+ScavengeBeforeFullGC -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -Duser.timezone=Asia/Shanghai -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
    export JAVA_OPTS="$JAVA_OPTS -Dserver.port=$SERVER_PORT -Dlogging.file=$LOG_DIR/$SERVICE_NAME.log -XX:HeapDumpPath=$LOG_DIR/HeapDumpOnOutOfMemoryError/"
    
    # Find Java
    if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
        javaexe="$JAVA_HOME/bin/java"
    elif type -p java > /dev/null 2>&1; then
        javaexe=$(type -p java)
    elif [[ -x "/usr/bin/java" ]];  then
        javaexe="/usr/bin/java"
    else
        echo "Unable to find Java"
        exit 1
    fi
    
    if [[ "$javaexe" ]]; then
        version=$("$javaexe" -version 2>&1 | awk -F '"' '/version/ {print $2}')
        version=$(echo "$version" | awk -F. '{printf("%03d%03d",$1,$2);}')
        # now version is of format 009003 (9.3.x)
        if [ $version -ge 011000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        elif [ $version -ge 010000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        elif [ $version -ge 009000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        else
            JAVA_OPTS="$JAVA_OPTS -XX:+UseParNewGC"
            JAVA_OPTS="$JAVA_OPTS -Xloggc:$LOG_DIR/gc.log -XX:+PrintGCDetails"
            JAVA_OPTS="$JAVA_OPTS -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:+CMSClassUnloadingEnabled  -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=5M"
        fi
    fi
    
    printf "$(date) ==== Starting ==== 
    "
    
    cd `dirname $0`/..
    chmod 755 $SERVICE_NAME".jar"
    ./$SERVICE_NAME".jar" start
    
    rc=$?;
    
    if [[ $rc != 0 ]];
    then
        echo "$(date) Failed to start $SERVICE_NAME.jar, return code: $rc"
        exit $rc;
    fi
    
    tail -f /dev/null
    

      

    Dockerfile文件

    cat  >Dockerfile <<-EOF 
    FROM stanleyws/jre8:8u112
    
    ENV VERSION 1.5.1
    
    RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&
        echo "Asia/Shanghai" > /etc/timezone
    
    ADD apollo-configservice-${VERSION}.jar /apollo-configservice/apollo-configservice.jar
    ADD config/ /apollo-configservice/config
    ADD scripts/ /apollo-configservice/scripts
    
    CMD ["/apollo-configservice/scripts/startup.sh"]
    
    EOF
      
    

    build-command.sh

    [root@master01 dockerfile]# cat build-command.sh 
    docker build -t registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-configservice:v1.5.1 .
    sleep 2
    docker push registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-configservice:v1.5.1
    

    导入configservice数据库,执行sql

    (由于每个环境需要单独一个数据库)

    创建数据库: Create database ApolloConfigTestDB;
    授权: grant INSERT,DELETE,UPDATE,SELECT on ApolloConfigTestDB.* to "apolloconfig"@"172.19.167.%" identified by "123456";

    https://raw.githubusercontent.com/ctripcorp/apollo/1.5.1/scripts/db/migration/configdb/V1.0.0__initialization.sql 数据库 (注意把创建数据库的语句删除,修改Use)
    然后更改eureka地址: update apolloconfigbetadb.ServerConfig set ServerConfig.Value="http://config-p.aa.com/eureka" where ServerConfig.Key="eureka.service.url";

     

    第二步: 编写K8S文件

    #configmap
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: apollo-configservice-cm
      namespace: test
    data:
      application-github.properties: |
        # DataSource
        spring.datasource.url = jdbc:mysql://172.19.166.17:3306/ApolloConfigTestDB?characterEncoding=utf8
        spring.datasource.username = apolloconfig
        spring.datasource.password = 123456
        eureka.service.url = http://config-p.aa.com/eureka
      app.properties: |
        appId=100003171
    ---
    
    kind: Ingress
    apiVersion: extensions/v1beta1
    metadata: 
      name: apollo-configservice
      namespace: test
    spec:
      rules:
      - host: config-p.aa.com
        http:
          paths:
          - path: /
            backend: 
              serviceName: apollo-configservice
              servicePort: 8080
    
    --- 
    kind: Service
    apiVersion: v1
    metadata: 
      name: apollo-configservice
      namespace: test
    spec:
      ports:
      - protocol: TCP
        port: 8080
        targetPort: 8080
        nodePort: 30108
      selector: 
        app: apollo-configservice
      type: NodePort 
      sessionAffinity: None
    ---
    kind: Deployment
    apiVersion: apps/v1 
    metadata:
      name: apollo-configservice
      namespace: test
      labels: 
        name: apollo-configservice
    spec:
      replicas: 1
      selector:
        matchLabels: 
          name: apollo-configservice
      template:
        metadata:
          labels: 
            app: apollo-configservice 
            name: apollo-configservice
        spec:
          nodeSelector:
            role: test
          imagePullSecrets:
            - name: aliyun-docker-secret
          volumes:
          - name: configmap-volume
            configMap:
              name: apollo-configservice-cm
          containers:
          - name: apollo-configservice
            image: registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-configservice:v1.5.1 
            ports:
            - containerPort: 8080
              protocol: TCP
            volumeMounts:
            - name: configmap-volume
              mountPath: /apollo-configservice/config
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
            imagePullPolicy: IfNotPresent
            resources:
              requests:
                cpu: 400m
                memory: 1024Mi
              limits:
                cpu: 800m
                memory: 2Gi
          restartPolicy: Always
          terminationGracePeriodSeconds: 30
          securityContext: 
            runAsUser: 0
          schedulerName: default-scheduler
      strategy:
        type: RollingUpdate
        rollingUpdate: 
          maxUnavailable: 1
          maxSurge: 1
      revisionHistoryLimit: 5
      progressDeadlineSeconds: 600
    

     

     第二步: 部署adminiservice

    1)镜像制作

    https://github.com/ctripcorp/apollo/releases/download/v1.5.1/apollo-adminservice-1.5.1-github.zip

    https://raw.githubusercontent.com/ctripcorp/apollo/1.5.1/scripts/db/migration/portaldb/V1.0.0__initialization.sql

    startup.sh脚本更改

    [root@master01 dockerfile]# cat scripts/startup.sh
    #!/bin/bash
    SERVICE_NAME=apollo-adminservice
    ## Adjust log dir if necessary
    LOG_DIR=/opt/logs/apollo-adminservice
    ## Adjust server port if necessary
    SERVER_PORT=8080
    APOLLO_ADMIN_SERVICE_NAME=$(hostname -i)
    # SERVER_URL="http://localhost:${SERVER_PORT}"
    SERVER_URL="http://${APOLLO_ADMIN_SERVICE_NAME}:${SERVER_PORT}"
    
    ## Adjust memory settings if necessary
    #export JAVA_OPTS="-Xms2560m -Xmx2560m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=1536m -XX:MaxNewSize=1536m -XX:SurvivorRatio=8"
    
    ## Only uncomment the following when you are using server jvm
    #export JAVA_OPTS="$JAVA_OPTS -server -XX:-ReduceInitialCardMarks"
    
    ########### The following is the same for configservice, adminservice, portal ###########
    export JAVA_OPTS="$JAVA_OPTS -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=9 -XX:+DisableExplicitGC -XX:+ScavengeBeforeFullGC -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -Duser.timezone=Asia/Shanghai -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
    export JAVA_OPTS="$JAVA_OPTS -Dserver.port=$SERVER_PORT -Dlogging.file=$LOG_DIR/$SERVICE_NAME.log -XX:HeapDumpPath=$LOG_DIR/HeapDumpOnOutOfMemoryError/"
    
    # Find Java
    if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
        javaexe="$JAVA_HOME/bin/java"
    elif type -p java > /dev/null 2>&1; then
        javaexe=$(type -p java)
    elif [[ -x "/usr/bin/java" ]];  then
        javaexe="/usr/bin/java"
    else
        echo "Unable to find Java"
        exit 1
    fi
    
    if [[ "$javaexe" ]]; then
        version=$("$javaexe" -version 2>&1 | awk -F '"' '/version/ {print $2}')
        version=$(echo "$version" | awk -F. '{printf("%03d%03d",$1,$2);}')
        # now version is of format 009003 (9.3.x)
        if [ $version -ge 011000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        elif [ $version -ge 010000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        elif [ $version -ge 009000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        else
            JAVA_OPTS="$JAVA_OPTS -XX:+UseParNewGC"
            JAVA_OPTS="$JAVA_OPTS -Xloggc:$LOG_DIR/gc.log -XX:+PrintGCDetails"
            JAVA_OPTS="$JAVA_OPTS -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:+CMSClassUnloadingEnabled  -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=5M"
        fi
    fi
    
    printf "$(date) ==== Starting ==== 
    "
    
    cd `dirname $0`/..
    chmod 755 $SERVICE_NAME".jar"
    ./$SERVICE_NAME".jar" start
    
    rc=$?;
    
    if [[ $rc != 0 ]];
    then
        echo "$(date) Failed to start $SERVICE_NAME.jar, return code: $rc"
        exit $rc;
    fi
    
    tail -f /dev/null
    

      

    Dockerfile文件

    [root@master01 dockerfile]# cat Dockerfile 
    FROM stanleyws/jre8:8u112
    
    ENV VERSION 1.5.1
    
    RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&
        echo "Asia/Shanghai" > /etc/timezone
    
    ADD apollo-adminservice-${VERSION}.jar /apollo-adminservice/apollo-adminservice.jar
    ADD config/ /apollo-adminservice/config
    ADD scripts/ /apollo-adminservice/scripts
    
    CMD ["/apollo-adminservice/scripts/startup.sh"]
    

      

    Build-command.sh

    [root@master01 dockerfile]# cat build-command.sh 
    #!/bin/bash
    docker build -t registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-adminservice:v1.5.1 .
    sleep 2
    docker push registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-adminservice:v1.5.1
    

      

    2)编写k8s文件

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: apollo-adminservice-cm
      namespace: test
    data:
      application-github.properties: |
        # DataSource
        spring.datasource.url = jdbc:mysql://172.19.166.17:3306/ApolloConfigTestDB?characterEncoding=utf8
        spring.datasource.username = apolloconfig
        spring.datasource.password = 123456
        eureka.service.url = http://config-p.aa.com/eureka
      app.properties: |
        appId=100003172
    
    ---
    kind: Deployment
    apiVersion: apps/v1 
    metadata:
      name: apollo-adminservice
      namespace: test
      labels: 
        name: apollo-adminservice
    spec:
      replicas: 1
      selector:
        matchLabels: 
          name: apollo-adminservice
      template:
        metadata:
          labels: 
            app: apollo-adminservice 
            name: apollo-adminservice
        spec:
          nodeSelector:
            role: test
          imagePullSecrets:
            - name: aliyun-docker-secret
          volumes:
          - name: configmap-volume
            configMap:
              name: apollo-adminservice-cm
          containers:
          - name: apollo-adminservice
            image: registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-adminservice:v1.5.1 
            ports:
            - containerPort: 8080
              protocol: TCP
            volumeMounts:
            - name: configmap-volume
              mountPath: /apollo-adminservice/config
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
            imagePullPolicy: IfNotPresent
            resources:
              requests:
                cpu: 400m
                memory: 1024Mi
              limits:
                cpu: 800m
                memory: 2Gi
          imagePullSecrets:
          - name: harbor
          restartPolicy: Always
          terminationGracePeriodSeconds: 30
          securityContext: 
            runAsUser: 0
          schedulerName: default-scheduler
      strategy:
        type: RollingUpdate
        rollingUpdate: 
          maxUnavailable: 1
          maxSurge: 1
      revisionHistoryLimit: 7
      progressDeadlineSeconds: 600
    

      

    第三步: 部署portal服务

    1)镜像制作

    https://github.com/ctripcorp/apollo/releases/download/v1.5.1/apollo-portal-1.5.1-github.zip

    https://raw.githubusercontent.com/ctripcorp/apollo/1.5.1/scripts/db/migration/portaldb/V1.0.0__initialization.sql

    startup.sh启动文件

    #!/bin/bash
    SERVICE_NAME=apollo-portal
    ## Adjust log dir if necessary
    LOG_DIR=/opt/logs/apollo-portal-server
    ## Adjust server port if necessary
    SERVER_PORT=8080
    APOLLO_PORTAL_SERVICE_NAME=$(hostname -i)
    # SERVER_URL="http://localhost:$SERVER_PORT"
    SERVER_URL="http://${APOLLO_PORTAL_SERVICE_NAME}:${SERVER_PORT}"
    
    ## Adjust memory settings if necessary
    #export JAVA_OPTS="-Xms2560m -Xmx2560m -Xss256k -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=384m -XX:NewSize=1536m -XX:MaxNewSize=1536m -XX:SurvivorRatio=8"
    
    ## Only uncomment the following when you are using server jvm
    #export JAVA_OPTS="$JAVA_OPTS -server -XX:-ReduceInitialCardMarks"
    
    ########### The following is the same for configservice, adminservice, portal ###########
    export JAVA_OPTS="$JAVA_OPTS -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=9 -XX:+DisableExplicitGC -XX:+ScavengeBeforeFullGC -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+ExplicitGCInvokesConcurrent -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -Duser.timezone=Asia/Shanghai -Dclient.encoding.override=UTF-8 -Dfile.encoding=UTF-8 -Djava.security.egd=file:/dev/./urandom"
    export JAVA_OPTS="$JAVA_OPTS -Dserver.port=$SERVER_PORT -Dlogging.file=$LOG_DIR/$SERVICE_NAME.log -XX:HeapDumpPath=$LOG_DIR/HeapDumpOnOutOfMemoryError/"
    
    # Find Java
    if [[ -n "$JAVA_HOME" ]] && [[ -x "$JAVA_HOME/bin/java" ]]; then
        javaexe="$JAVA_HOME/bin/java"
    elif type -p java > /dev/null 2>&1; then
        javaexe=$(type -p java)
    elif [[ -x "/usr/bin/java" ]];  then
        javaexe="/usr/bin/java"
    else
        echo "Unable to find Java"
        exit 1
    fi
    
    if [[ "$javaexe" ]]; then
        version=$("$javaexe" -version 2>&1 | awk -F '"' '/version/ {print $2}')
        version=$(echo "$version" | awk -F. '{printf("%03d%03d",$1,$2);}')
        # now version is of format 009003 (9.3.x)
        if [ $version -ge 011000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        elif [ $version -ge 010000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        elif [ $version -ge 009000 ]; then
            JAVA_OPTS="$JAVA_OPTS -Xlog:gc*:$LOG_DIR/gc.log:time,level,tags -Xlog:safepoint -Xlog:gc+heap=trace"
        else
            JAVA_OPTS="$JAVA_OPTS -XX:+UseParNewGC"
            JAVA_OPTS="$JAVA_OPTS -Xloggc:$LOG_DIR/gc.log -XX:+PrintGCDetails"
            JAVA_OPTS="$JAVA_OPTS -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSClassUnloadingEnabled -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:+CMSClassUnloadingEnabled  -XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=5M"
        fi
    fi
    
    printf "$(date) ==== Starting ==== 
    "
    
    cd `dirname $0`/..
    chmod 755 $SERVICE_NAME".jar"
    ./$SERVICE_NAME".jar" start
    
    rc=$?;
    
    if [[ $rc != 0 ]];
    then
        echo "$(date) Failed to start $SERVICE_NAME.jar, return code: $rc"
        exit $rc;
    fi
    
    tail -f /dev/null
    

      

    Dockerfile文件

    FROM stanleyws/jre8:8u112
    
    ENV VERSION 1.5.1
    
    RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime &&
        echo "Asia/Shanghai" > /etc/timezone
    
    ADD apollo-portal-${VERSION}.jar /apollo-portal/apollo-portal.jar
    ADD config/ /apollo-portal/config
    ADD scripts/ /apollo-portal/scripts
    
    CMD ["/apollo-portal/scripts/startup.sh"]
    

      

    build-command.sh 构建镜像

    #!/bin/bash
    docker build -t registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-portal:v1.5.1 .
    sleep 2
    docker push registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-portal:v1.5.1
    

    导入数据

    https://raw.githubusercontent.com/ctripcorp/apollo/1.5.1/scripts/db/migration/portaldb/V1.0.0__initialization.sql
    
    修改Serverconfig (默认有dev环境,根据你部署了几套configserver和adminserver,如果部署可测试环境、预发布环境、生产环境,那么就要修改该Serverconfig表)
    
    select * from ServerConfig;
    
    update Serverconfig set Value="dev,fat,uat,pro" where Id=1;

    授权:
    grant INSERT,DELETE,UPDATE,SELECT on ApolloPortalDB.* to "apolloportal"@"172.19.167.%" identified by "123456";

    2)编写k8s文件

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: apollo-portal-cm
      namespace: devops 
    data:
      application-github.properties: |
        # DataSource
        spring.datasource.url = jdbc:mysql://172.19.166.17:3306/ApolloPortalDB?characterEncoding=utf8
        spring.datasource.username = apolloportal
        spring.datasource.password = 123456
      app.properties: |
        appId=100003173
      apollo-env.properties: |
        fat.meta=http://config-p.distrii.com
        uat.meta=http://config-b.distrii.com
    
    kind: Service
    apiVersion: v1
    metadata: 
      name: apollo-portal
      namespace: devops
    spec:
      ports:
      - protocol: TCP
        port: 8080
        targetPort: 8080
      selector: 
        app: apollo-portal
      type: ClusterIP
      sessionAffinity: None
    ---
    kind: Ingress
    apiVersion: extensions/v1beta1
    metadata: 
      name: apollo-portal
      namespace: devops
    spec:
      rules:
      - host: portal.aa.com
        http:
          paths:
          - path: /
            backend: 
              serviceName: apollo-portal
              servicePort: 8080
    
    kind: Deployment
    apiVersion: apps/v1 
    metadata:
      name: apollo-portal
      namespace: devops
      labels: 
        name: apollo-portal
    spec:
      replicas: 1
      selector:
        matchLabels: 
          name: apollo-portal
      template:
        metadata:
          labels: 
            app: apollo-portal 
            name: apollo-portal
        spec:
          nodeSelector:
            role: devops
          imagePullSecrets:
            - name: aliyun-docker-secret
          volumes:
          - name: configmap-volume
            configMap:
              name: apollo-portal-cm
          containers:
          - name: apollo-portal
            image: registry.cn-shanghai.aliyuncs.com/xhaihua/apollo-portal:v1.5.1 
            ports:
            - containerPort: 8080
              protocol: TCP
            volumeMounts:
            - name: configmap-volume
              mountPath: /apollo-portal/config
            terminationMessagePath: /dev/termination-log
            terminationMessagePolicy: File
            imagePullPolicy: Always 
            resources:
              requests:
                cpu: 400m
                memory: 512Mi
              limits:
                cpu: 800m
          restartPolicy: Always
          terminationGracePeriodSeconds: 30
          securityContext: 
            runAsUser: 0
          schedulerName: default-scheduler
      strategy:
        type: RollingUpdate
        rollingUpdate: 
          maxUnavailable: 1
          maxSurge: 1
      revisionHistoryLimit: 7
      progressDeadlineSeconds: 600
    

      

    第三部分: Apollo Portal界面操作

    1)修改管理员apollo密码 (或者新建用户)

    2)新建应用

    一个应用可以多个集群,默认有一个default集群

     集群分配权限

    点击右上方的集群名称,然后出现集群配置的tab,点击 授权

     添加名称空间namespace (Namespace分配权限)

    新增配置

     发布配置

     发布回滚

  • 相关阅读:
    python的基本操作while循环体
    python中类的神奇方法应用案例
    Python中类的神奇方法
    python 中类的初始化方法
    Python中类的创建和self的作用
    投掷骰子的游戏,键值对
    字典常用方法
    剪刀石头布
    math 模块
    PYTHON内置模块
  • 原文地址:https://www.cnblogs.com/louis2008/p/Apollo-config.html
Copyright © 2011-2022 走看看