携程Apollo配置中心初探

1. 背景

平时开发最头疼之一就是各种配置:

  1. 一个项目往往会包含各式各样的配置信息,且不说数据库、redis、memcache这些常用的配置,还会有很多业务上的配置。
  2. 线上、测试和开发环境配置各不一样,每个环境都要保存一份
  3. 每次上线的时候,都要挨个check一下,
  4. 更改某个配置,需要重新上线代码
  5. ….

所以配置中心,在devops的开发中,是必不可少的,配置中心,也可以有效的避免,因为更改配置代码,导致的代码运行出错的风险。

2. Apollo

携程的Apollo配置中心,在业内算是比较有名的,github上面大概有8.5k的star。很多知名的公司也都在使用。至于实现的原理直接看github上面的wiki即可。

3. DO IT

3.1 创建项目

3.1.1 创建项目,clone项目

3.1.2 按需更改docker-compose.yml

feilongdeMacBook-Pro:apollo2 feilong$ cd scripts/docker-quick-start/
feilongdeMacBook-Pro:docker-quick-start feilong$ ll
total 8
-rw-r--r--  1 feilong  wheel  663 Oct 27 14:56 docker-compose.yml
drwxr-xr-x  4 feilong  wheel  128 Oct 27 14:56 sql

因为我不是java技术栈,所以以docker运行。

本地由于8080端口被占用,所以把端口改为8082

# docker-compose.yml
version: '2'

services:
  apollo-quick-start:
    image: nobodyiam/apollo-quick-start
    container_name: apollo-quick-start
    depends_on:
      - apollo-db
    ports:
      - "8082:8080"
      - "8070:8070"
    links:
      - apollo-db

  apollo-db:
    image: mysql:5.7
    container_name: apollo-db
    environment:
      TZ: Asia/Shanghai
      MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
    depends_on:
      - apollo-dbdata
    ports:
      - "13306:3306"
    volumes:
      - ./sql:/docker-entrypoint-initdb.d
    volumes_from:
      - apollo-dbdata

  apollo-dbdata:
    image: alpine:latest
    container_name: apollo-dbdata
    volumes:
      - /var/lib/mysql

通过docker-compose启动Apollo服务

feilongdeMacBook-Pro:docker-quick-start feilong$ docker-compose up -d
Creating network "docker-quick-start_default" with the default driver
Creating apollo-dbdata ... done
Creating apollo-db     ... done
Creating apollo-quick-start ... done
feilongdeMacBook-Pro:docker-quick-start feilong$

访问地址 localhost:8070,如果启动失败的话,请参考 #1473 docker_quick_start 起不来

3.2 配置

Apollo内置了账号 apollo/admin,登录之后,可以看到有个默认的应用SampleApp

通过后台创建一个用于演示的redis的配置信息

创建结束后,点击“发布”按钮,发布最新的配置

3.3 运行测试

除了配置的server端,还要有接受配置的client端,我是PHP技术栈,所以就以PHP为主

3.3.1 初始化项目

配置php项目,通过composer引入Apollo的SDK

feilongdeMacBook-Pro:apollo feilong$ composer require multilinguals/apollo-client --ignore-platform-reqs
Using version ^0.1.1 for multilinguals/apollo-client
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
 - Installing multilinguals/apollo-client (v0.1.1): Downloading (100%)
Writing lock file
Generating autoload files

新建一个pull.php,为了能够实时获取最新的配置,需要长时间运行。

<?php
require_once 'vendor/autoload.php';

use Org\Multilinguals\Apollo\Client\ApolloClient;

// docker-compose里面配置的API服务的端口
$serverIp = '192.168.1.72:8082';
// 在Apollo的后台可以查到
$appId = 'SampleApp';
$namespaces = array('application');
$apollo = new ApolloClient($serverIp, $appId, $namespaces);
$apollo->save_dir = 'config';

$restart = true; //auto start if failed
do {
    $error = $apollo->start();
    if ($error) {
        echo('error:'.$error."\n");
    }
} while ($error && $restart);

3.3.2 运行项目

新建一个config的目录,运行pull.php文件,获取配置信息

新建一个窗口,查看下config文件夹下的文件

发现新建了一个配置文件,里面是相应的配置信息

后台做一些更新操作

发布配置,查看配置文件

4. 总结

通过配置中心,我们就不用在部署的时候,手动一直更改项目的配置文件,可以实现自动化,降低人为风险。

此外,Apollo是版本控制的,支持回滚操作,这样,就算是出现手误,也能及时回滚配置,及时生效。

docker之libnetwork插件化网络功能

1. 背景

docker的版本不断迭代,从1.7开始,docker将网络的部分以插件化的方式剥离出来,允许用户通过命令实现不同的后端,增加了docker的灵活性。剥离出来的网络叫做libnetwork,显而易见,它希望将来为不同的容器定义统一网络层的规范。

2. 容器的网络模型(CNM)

容器网络模型(Container Networking Model,CNM)最开始的提出者是思科公司员工Erik,设计了一个docker扩展的网桥模型。后来,他将这个模型反馈给docker的团队,得到了docker的极大的认可,也是libnetwork的雏形。

CNM主要包含三点sandbox、endpoint和network。

之前有篇文章是DOCKER之命名空间 ,文章简要说了一下网络的隔离,讲述了一些docker的网络基本知识。其实也是libnetwork的一个缩影。

sandbox相当于网卡配置、路由表、dns配置、网络命名空间等

endpoint相当于容器内部的虚拟网卡(eth0等)

network代表一个或者多个网络

当然,这些概念并不是对等,这里只是有一个类比的关系。

可见,具体底层网络如何实现,每个容器的endpoint所连接的network,以及sandbox如何进行隔离,都可以不用关心。只要libnetwork提供网络和接入点,只需要容器给街上或者拔下(这里指的是endpoint和network的链接和断开),剩下的都是直接由驱动自己实现。

3. docker网络bridge

docker自带了查看网络的命令

localhost:~ feilong$ docker network ls
NETWORK ID          NAME                  DRIVER              SCOPE
5adfcce2b464        bridge                bridge              local
c2d85093b647        docker_default        bridge              local
0eec2b9c7d57        go_default            bridge              local
604e2a13b930        host                  host                local
bded596e1021        marvin2_default       bridge              local
6c006e90b785        marvin_code-network   bridge              local
ef31b0632a2f        marvin_default        bridge              local
72169048d0e7        none                  null                local
6eb2e51b63e2        redis_default         bridge              local
6c6e4cc36312        registry_default      bridge              local
localhost:~ feilong$

从查询结果,可以看出,我的电脑上面,有很多的网络信息。docker默认是携带3种网络:bridge、host、none,这三个网络是不能被删除的

ocalhost:~ feilong$ docker network rm host
Error response from daemon: host is a pre-defined network and cannot be removed
localhost:~ feilong$ docker network rm bridge
Error response from daemon: bridge is a pre-defined network and cannot be removed
localhost:~ feilong$ docker network rm none
Error response from daemon: none is a pre-defined network and cannot be removed
localhost:~ feilong$

我们在运行容器的时候,指定的网络类型(–network=bridge|–network=host|–network=none)其实就是来源于这里,又由于docker默认是使用bridge的网络类型,通常是省略这个参数。

使用docker-compose发生了什么?

我们在使用docker-compose的时候,通常是不用设置–link参数的,那是因为,docker会认为使用docker-compose启动多个容器,但是这几个容器是组成了一个service,会自动创建一个bridge类型的网络,然后将启动的容器,放到这个网络中,和其他的网络进行隔离。

# docker-compose.yaml
version: '3'

services: 
  busybox1:
    image: busybox
    container_name: busybox1
  busybox2:
    image: busybox
    container_name: busybox2
  busybox3:
    image: busybox
    container_name: busybox3

查看docker的网络

localhost:test feilong$ docker network ls
NETWORK ID          NAME                  DRIVER              SCOPE
5adfcce2b464        bridge                bridge              local
c2d85093b647        docker_default        bridge              local
0eec2b9c7d57        go_default            bridge              local
604e2a13b930        host                  host                local
bded596e1021        marvin2_default       bridge              local
6c006e90b785        marvin_code-network   bridge              local
ef31b0632a2f        marvin_default        bridge              local
72169048d0e7        none                  null                local
6eb2e51b63e2        redis_default         bridge              local
6c6e4cc36312        registry_default      bridge              local
localhost:test feilong$

运行docker-compose,然后查看网络

localhost:test feilong$ docker-compose up -d
Creating network "test_default" with the default driver
Creating busybox2 ... done
Creating busybox3 ... done
Creating busybox1 ... done
localhost:test feilong$ docker network ls
NETWORK ID          NAME                  DRIVER              SCOPE
5adfcce2b464        bridge                bridge              local
c2d85093b647        docker_default        bridge              local
0eec2b9c7d57        go_default            bridge              local
604e2a13b930        host                  host                local
bded596e1021        marvin2_default       bridge              local
6c006e90b785        marvin_code-network   bridge              local
ef31b0632a2f        marvin_default        bridge              local
72169048d0e7        none                  null                local
6eb2e51b63e2        redis_default         bridge              local
6c6e4cc36312        registry_default      bridge              local
fca18d6c1259        test_default          bridge              local

从上面,我们看出,在运行docker-compose的时候,就已经创建了一个网络,叫做 test_default

–link ?

我们知道,想要将两个容器链接起来,可以使用–link参数(在使用命令行 docker run的时候)。其实,我们在进行docker run的时候,创建的容器,加入了一个默认的网络“bridge”,但是在“bridge”中,默认是不能进行连接的,需要加上–link,使bridge内部的容器进行连接。这样也是为了隔离容器。试想一下,如果不采用这个机制,那么网络空间就不再是隔离的状态了,就和docker的设计不太相符。

我们分别运行一个test1和test2容器

feilongdeMacBook-Pro:~ feilong$ docker run -itd --name test1 busybox
bfca24444ce4ee16d6e2af76cf0967d0923dbbed4310c811cdbefef5d6b5b67f
feilongdeMacBook-Pro:~ feilong$ docker run -itd --name test2 busybox
48a5c7e9c042de472ed5edd328a53952fe9552357eff7b1c81f4d9e3bbed1c8c
feilongdeMacBook-Pro:~ feilong$ docker ps | grep test
48a5c7e9c042        busybox                              "sh"                     6 seconds ago       Up 5 seconds                                  test2
bfca24444ce4        busybox                              "sh"                     10 seconds ago      Up 9 seconds                                  test1
b742b60be6a0        registry:latest                      "/entrypoint.sh /etc…"   22 hours ago        Up 3 hours          0.0.0.0:32768->5000/tcp   thirsty_almeida
feilongdeMacBook-Pro:~ feilong$

然后我们进入test1,ping一下test2

feilongdeMacBook-Pro:~ feilong$ docker exec -it test1 sh
/ # ping test2
ping: bad address 'test2'
/ #

结果很明显,是不通的!

但是他们是在同一个网络里面的

4. 总结

libnetwork通过CNM,抽象了下层网络的实现让docker可以根据需要,实现不同的网络技术,docke将swarm引擎加到了新的版本中,以提供集群网络的更好支持。