docker搭建rabbitmq集群

本文主要讲述如何用docker搭建rabbitmq的集群。

下载镜像

采用bijukunjummen该镜像。

git clone https://github.com/bijukunjummen/docker-rabbitmq-cluster.git

运行

启动集群

cd docker-rabbitmq-cluster/cluster
docker-compose up -d
......
Status: Downloaded newer image for bijukunjummen/rabbitmq-server:latest
docker.io/bijukunjummen/rabbitmq-server: this image was pulled from a legacy registry.  Important: This registry version will not be supported in future versions of docker.
Creating cluster_rabbit1_1
Creating cluster_rabbit2_1
Creating cluster_rabbit3_1

默认启动了三个节点

rabbit1:
  image: bijukunjummen/rabbitmq-server
  hostname: rabbit1
  ports:
    - "5672:5672"
    - "15672:15672"

rabbit2:
  image: bijukunjummen/rabbitmq-server
  hostname: rabbit2
  links:
    - rabbit1
  environment:
   - CLUSTERED=true
   - CLUSTER_WITH=rabbit1
   - RAM_NODE=true
  ports:
      - "5673:5672"
      - "15673:15672"

rabbit3:
  image: bijukunjummen/rabbitmq-server
  hostname: rabbit3
  links:
    - rabbit1
    - rabbit2
  environment:
   - CLUSTERED=true
   - CLUSTER_WITH=rabbit1
  ports:
        - "5674:5672"

查看

docker@default:~$ docker ps
CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                                                                                  NAMES
ba5f665bb213        bijukunjummen/rabbitmq-server   "/bin/sh -c /opt/rabb"   10 minutes ago      Up 10 minutes       4369/tcp, 9100-9105/tcp, 15672/tcp, 25672/tcp, 0.0.0.0:5674->5672/tcp                  cluster_rabbit3_1
b9466e206b2b        bijukunjummen/rabbitmq-server   "/bin/sh -c /opt/rabb"   10 minutes ago      Up 10 minutes       4369/tcp, 9100-9105/tcp, 25672/tcp, 0.0.0.0:5673->5672/tcp, 0.0.0.0:15673->15672/tcp   cluster_rabbit2_1
b733201aeadf        bijukunjummen/rabbitmq-server   "/bin/sh -c /opt/rabb"   10 minutes ago      Up 10 minutes       4369/tcp, 0.0.0.0:5672->5672/tcp, 9100-9105/tcp, 0.0.0.0:15672->15672/tcp, 25672/tcp   cluster_rabbit1_1
88196436c434        daocloud.io/daocloud/daomonit   "/usr/local/bin/daomo"   37 hours ago        Up 2 hours                                                                                                 daomonit

访问:

http://192.168.99.100:15672,弹出登陆界面

未分类

输入guest/guest

未分类

docker环境搭建zk集群

对于个人开发者而言,学习分布式的好多东东,都比较费劲,因为手头的机器不够。若是单机使用虚拟机来启动一个个虚拟server话,又比较耗费资源,要求单机性能够好。幸好docker这种轻量级的东东出现了。本文主要是记录使用docker搭建zk集群的过程。

下载zk镜像

这次使用garland搭好的镜像。

docker pull garland/zookeeper

构建zk集群

docker run -d 
 --name=zk1 
 --net=host 
 -e SERVER_ID=1 
 -e ADDITIONAL_ZOOKEEPER_1=server.1=localhost:2888:3888 
 -e ADDITIONAL_ZOOKEEPER_2=server.2=localhost:2889:3889 
 -e ADDITIONAL_ZOOKEEPER_3=server.3=localhost:2890:3890 
 -e ADDITIONAL_ZOOKEEPER_4=clientPort=2181 
 garland/zookeeper

docker run -d 
 --name=zk2 
 --net=host 
 -e SERVER_ID=2 
 -e ADDITIONAL_ZOOKEEPER_1=server.1=localhost:2888:3888 
 -e ADDITIONAL_ZOOKEEPER_2=server.2=localhost:2889:3889 
 -e ADDITIONAL_ZOOKEEPER_3=server.3=localhost:2890:3890 
 -e ADDITIONAL_ZOOKEEPER_4=clientPort=2182 
 garland/zookeeper

docker run -d 
 --name=zk3 
 --net=host 
 -e SERVER_ID=3 
 -e ADDITIONAL_ZOOKEEPER_1=server.1=localhost:2888:3888 
 -e ADDITIONAL_ZOOKEEPER_2=server.2=localhost:2889:3889 
 -e ADDITIONAL_ZOOKEEPER_3=server.3=localhost:2890:3890 
 -e ADDITIONAL_ZOOKEEPER_4=clientPort=2183 
 garland/zookeeper

查看集群

查看zk1(follower)

root@xixicat:~# echo stat | nc 127.0.0.1 2181
Zookeeper version: 3.4.6-1569965, built on 02/20/2014 09:09 GMT
Clients:
 /127.0.0.1:55531[0](queued=0,recved=1,sent=0)

Latency min/avg/max: 0/1/31
Received: 63
Sent: 62
Connections: 1
Outstanding: 0
Zxid: 0x100000005
Mode: follower
Node count: 5

查看zk2(leader)

root@xixicat:~# echo stat | nc 127.0.0.1 2182
Zookeeper version: 3.4.6-1569965, built on 02/20/2014 09:09 GMT
Clients:
 /127.0.0.1:53244[0](queued=0,recved=1,sent=0)

Latency min/avg/max: 0/0/0
Received: 1
Sent: 0
Connections: 1
Outstanding: 0
Zxid: 0x100000005
Mode: leader
Node count: 5

查看zk3(follower)

root@xixicat:~# echo stat | nc 127.0.0.1 2183
Zookeeper version: 3.4.6-1569965, built on 02/20/2014 09:09 GMT
Clients:
 /127.0.0.1:33983[0](queued=0,recved=1,sent=0)

Latency min/avg/max: 0/0/0
Received: 1
Sent: 0
Connections: 1
Outstanding: 0
Zxid: 0x100000005
Mode: follower
Node count: 5

daocloud加速

之前折腾了好几次,国内访问docker.io的网速还是不行,于是就使用了daocloud加速,前提是你得装docker-machine。试用了下,速度提升了不少。

未分类

docker搭建redis集群

这里参照了Docker 搭建redis 集群这篇文章来,非常顺利。

下载镜像

docker pull redis

准备配置文件

mkdir /home/docker/redis/  
wget https://raw.githubusercontent.com/antirez/redis/3.0/redis.conf -O /home/docker/redis/redis.conf
cd /home/docker/redis/  
sed -i 's/# slaveof <masterip> <masterport>/slaveof redis-master 6379/g' redis.conf  

启动redis容器

docker run --name redis-master -p 6379:6379 -d redis
docker run --link redis-master:redis-master -v /home/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf --name redis-slave1 -d redis redis-server /usr/local/etc/redis/redis.conf
docker run --link redis-master:redis-master -v /home/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf --name redis-slave2 -d redis redis-server /usr/local/etc/redis/redis.conf
docker run --link redis-master:redis-master -v /home/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf --name redis-slave3 -d redis redis-server /usr/local/etc/redis/redis.conf 

查看redis集群

redis-cli 
127.0.0.1:6379> info
# Server
redis_version:3.0.6
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:48bba53ba79f07ac
redis_mode:standalone
os:Linux 3.13.0-27-generic x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.9.2
process_id:1
run_id:6d552d47e50137b6ee40697d7254891aa5dbdb68
tcp_port:6379
uptime_in_seconds:21554
uptime_in_days:0
hz:10
lru_clock:10637013
config_file:

# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# Memory
used_memory:1926248
used_memory_human:1.84M
used_memory_rss:4419584
used_memory_peak:1963096
used_memory_peak_human:1.87M
used_memory_lua:36864
mem_fragmentation_ratio:2.29
mem_allocator:jemalloc-3.6.0

# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1453456066
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

# Stats
total_connections_received:5
total_commands_processed:64476
instantaneous_ops_per_sec:3
total_net_input_bytes:2426080
total_net_output_bytes:92455
instantaneous_input_kbps:0.14
instantaneous_output_kbps:0.02
rejected_connections:0
sync_full:3
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:222
migrate_cached_sockets:0

# Replication
role:master
connected_slaves:3
slave0:ip=172.17.0.2,port=6379,state=online,offset=30087,lag=1
slave1:ip=172.17.0.3,port=6379,state=online,offset=30101,lag=0
slave2:ip=172.17.0.4,port=6379,state=online,offset=30087,lag=1
master_repl_offset:30101
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:30100

# CPU
used_cpu_sys:7.37
used_cpu_user:5.34
used_cpu_sys_children:0.00
used_cpu_user_children:0.00

# Cluster
cluster_enabled:0

# Keyspace

ansible-playbook之循环(Loops)

1. 标准的Loops:也是最常用的一种循环

例:当需要安装10个软件包时,不用写10次任务,只需要写一次然后进行循环即可

#注:已经安装的软件系统即不在安装
[root@nfs-server playbook]# cat yum_list.yml 
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks: 
  - name: "需要安装的软件清单"
    yum: name={{ item }} state=present
    with_items:
      - lrzsz
      - vim
      - sysstat
[root@nfs-server playbook]# ansible-playbook yum_list.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [需要安装的软件清单] ***************************************************************************************************************************************
changed: [192.168.2.101] => (item=[u'lrzsz', u'vim', u'sysstat'])
changed: [192.168.2.111] => (item=[u'lrzsz', u'vim', u'sysstat'])

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=1    changed=1    unreachable=0    failed=0   
192.168.2.111              : ok=1    changed=1    unreachable=0    failed=0

2. 字典格式的循环:with_items

#写法一:
[root@nfs-server playbook]# cat dict_list.yml
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks:
  - name: "字典格式的循环"
    debug: msg="name ---->{{ item.name }},age---->{{ item.age }}"
    with_items:
      - {name: "Liu Zhengwei",age: 28}
      - {name: "Jia Dongli",age: 25}
#写法二:
[root@nfs-server playbook]# cat dict_list.yml
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks:
  - name: "字典格式的循环"
    debug: msg="name ---->{{ item.name }},age---->{{ item.age }}"
    with_items:
      - name: "Liu Zhengwei"
        age: 28
      - name: "Jia Dongli"
        age: 25
#注:以上两种写法效果是一样的
[root@nfs-server playbook]# ansible-playbook dict_list.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [字典格式的循环] *****************************************************************************************************************************************
ok: [192.168.2.101] => (item={u'age': 28, u'name': u'Liu Zhengwei'}) => {
    "item": {
        "age": 28, 
        "name": "Liu Zhengwei"
    }, 
    "msg": "name ---->Liu Zhengwei,age---->28"
}
ok: [192.168.2.101] => (item={u'age': 25, u'name': u'Jia Dongli'}) => {
    "item": {
        "age": 25, 
        "name": "Jia Dongli"
    }, 
    "msg": "name ---->Jia Dongli,age---->25"
}
ok: [192.168.2.111] => (item={u'age': 28, u'name': u'Liu Zhengwei'}) => {
    "item": {
        "age": 28, 
        "name": "Liu Zhengwei"
    }, 
    "msg": "name ---->Liu Zhengwei,age---->28"
}
ok: [192.168.2.111] => (item={u'age': 25, u'name': u'Jia Dongli'}) => {
    "item": {
        "age": 25, 
        "name": "Jia Dongli"
    }, 
    "msg": "name ---->Jia Dongli,age---->25"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=1    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=1    changed=0    unreachable=0    failed=0

3. 嵌套Loops(列表格式的循环,用于1对多或者多对多关系时)–> with_nes

[root@nfs-server playbook]# cat netsted_list.yml 
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks:
  - name: "实现1对多关系的循环"
    debug: msg="name--->{{ item[0] }},value--->{{  item[1] }}"
    with_nested:
      - ['A']
      - ['a','b','c' ]
[root@nfs-server playbook]# ansible-playbook netsted_list.yml -l 192.168.2.101

PLAY [webservers] **************************************************************************************************************************************

TASK [实现1对多关系的循环] **************************************************************************************************************************************
ok: [192.168.2.101] => (item=[u'A', u'a']) => {
    "item": [
        "A", 
        "a"
    ], 
    "msg": "name--->A,value--->a"
}
ok: [192.168.2.101] => (item=[u'A', u'b']) => {
    "item": [
        "A", 
        "b"
    ], 
    "msg": "name--->A,value--->b"
}
ok: [192.168.2.101] => (item=[u'A', u'c']) => {
    "item": [
        "A", 
        "c"
    ], 
    "msg": "name--->A,value--->c"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=1    changed=0    unreachable=0    failed=0

4. 散列loops:with_dict(支持更丰富的数据结构)

注:with_dict的写法在新版本中进行了改变,必须要写成字典的形式,跟我下面写的格式一样

[root@nfs-server playbook]# cat with_dict.yml 
---
- hosts: webservers
  gather_facts: False
  remote_user: root
  vars:
    user:
      shencan:
        name: shencan
        shell: bash
      ruifengyun:
        name: ruifengyun
        shell: zsh
  tasks:
  - name: debug loops
    debug: "msg=name--->{{ item.key }} value--->{{ item.value.name }} shell--->{{ item.value.shell }}"
    with_dict: "{{ user }}"

[root@nfs-server playbook]# ansible-playbook with_dict.yml -l 192.168.2.101

PLAY [webservers] **************************************************************************************************************************************

TASK [debug loops] *************************************************************************************************************************************
ok: [192.168.2.101] => (item={'key': u'ruifengyun', 'value': {u'shell': u'zsh', u'name': u'ruifengyun'}}) => {
    "item": {
        "key": "ruifengyun", 
        "value": {
            "name": "ruifengyun", 
            "shell": "zsh"
        }
    }, 
    "msg": "name--->ruifengyun"
}
ok: [192.168.2.101] => (item={'key': u'shencan', 'value': {u'shell': u'bash', u'name': u'shencan'}}) => {
    "item": {
        "key": "shencan", 
        "value": {
            "name": "shencan", 
            "shell": "bash"
        }
    }, 
    "msg": "name--->shencan"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=1    changed=0    unreachable=0    failed=0

5. 文件匹配loops:with_fileglob

在工作中,我们经常遇到需要针对一个目录下指定格式的文件进行处理,这个时候直接引用with_fileglob循环去匹配我们需要处理的文件即可

[root@nfs-server playbook]# cat with_fileglob.yml
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks:
  - name: debug loops
    debug: "msg=files-->{{ item }}"
    with_fileglob:
      - /var/log/nginx/*.gz
[root@nfs-server playbook]# ansible-playbook with_fileglob.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [debug loops] *************************************************************************************************************************************
ok: [192.168.2.101] => (item=/var/log/nginx/host.access.log-20170816.gz) => {
    "item": "/var/log/nginx/host.access.log-20170816.gz", 
    "msg": "files-->/var/log/nginx/host.access.log-20170816.gz"
}
ok: [192.168.2.101] => (item=/var/log/nginx/error.log-20170815.gz) => {
    "item": "/var/log/nginx/error.log-20170815.gz", 
    "msg": "files-->/var/log/nginx/error.log-20170815.gz"
}
ok: [192.168.2.101] => (item=/var/log/nginx/error.log-20170823.gz) => {
    "item": "/var/log/nginx/error.log-20170823.gz", 
    "msg": "files-->/var/log/nginx/error.log-20170823.gz"
}

ok: [192.168.2.111] => (item=/var/log/nginx/access.log-20170817.gz) => {
    "item": "/var/log/nginx/access.log-20170817.gz", 
    "msg": "files-->/var/log/nginx/access.log-20170817.gz"
}
ok: [192.168.2.111] => (item=/var/log/nginx/error.log-20170825.gz) => {
    "item": "/var/log/nginx/error.log-20170825.gz", 
    "msg": "files-->/var/log/nginx/error.log-20170825.gz"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=1    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=1    changed=0    unreachable=0    failed=0

6. 随机选择loops:with_random_choice(会从给定的值中随便选取一个显示)

[root@nfs-server playbook]# cat with_random_choice.yml
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks:
  - name: debug loops
    debug: 'msg="name --> {{ item }}"'
    with_random_choice:
      - "Beijing"
      - "Shanghai"
      - "TianJin"
[root@nfs-server playbook]# ansible-playbook with_random_choice.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [debug loops] *************************************************************************************************************************************
ok: [192.168.2.101] => (item=Beijing) => {
    "item": "Beijing", 
    "msg": "name --> Beijing"
}
ok: [192.168.2.111] => (item=Shanghai) => {
    "item": "Shanghai", 
    "msg": "name --> Shanghai"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=1    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=1    changed=0    unreachable=0    failed=0

7. 条件判断loops

在某些情况下,我们需要检测某个task是否达到了预想的状态,如果没有达到,就需要退出整个剧本。

until:检测条件

retries:检测次数

delay:每次检测的间隔时长

[root@nfs-server playbook]# cat if_else.yml
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks:
  - name: "对task的执行结果进行判断"
    shell: cat /etc/fstab
    register: info
    until: info.stdout.startswith("sysfs")
    retries: 5
    delay: 5
[root@nfs-server playbook]# ansible-playbook if_else.yml 

PLAY [webservers] **************************************************************************************************************************************

TASK [对task的执行结果进行判断] **********************************************************************************************************************************
FAILED - RETRYING: 对task的执行结果进行判断 (5 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (5 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (4 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (4 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (3 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (3 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (2 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (2 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (1 retries left).
FAILED - RETRYING: 对task的执行结果进行判断 (1 retries left).
fatal: [192.168.2.111]: FAILED! => {"attempts": 5, "changed": true, "cmd": "cat /etc/fstab", "delta": "0:00:00.003310", "end": "2017-09-04 01:09:18.651001", "failed": true, "rc": 0, "start": "2017-09-04 01:09:18.647691", "stderr": "", "stderr_lines": [], "stdout": "n#n# /etc/fstabn# Created by anaconda on Sun Jun 26 03:08:48 2016n#n# Accessible filesystems, by reference, are maintained under '/dev/disk'n# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more infon#n/dev/mapper/vg_test2-lv_root /                       ext4    defaults        1 1nUUID=8699d6c2-883b-41a0-8282-5be27641ee02 /boot                   ext4    defaults        1 2n/dev/mapper/vg_test2-lv_swap swap                    swap    defaults        0 0ntmpfs                   /dev/shm                tmpfs   defaults        0 0ndevpts                  /dev/pts                devpts  gid=5,mode=620  0 0nsysfs                   /sys                    sysfs   defaults        0 0nproc                    /proc                   proc    defaults        0 0n/dev/cdrom              /media/cdrom            iso9660 defaults        0 0", "stdout_lines": ["", "#", "# /etc/fstab", "# Created by anaconda on Sun Jun 26 03:08:48 2016", "#", "# Accessible filesystems, by reference, are maintained under '/dev/disk'", "# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info", "#", "/dev/mapper/vg_test2-lv_root /                       ext4    defaults        1 1", "UUID=8699d6c2-883b-41a0-8282-5be27641ee02 /boot                   ext4    defaults        1 2", "/dev/mapper/vg_test2-lv_swap swap                    swap    defaults        0 0", "tmpfs                   /dev/shm                tmpfs   defaults        0 0", "devpts                  /dev/pts                devpts  gid=5,mode=620  0 0", "sysfs                   /sys                    sysfs   defaults        0 0", "proc                    /proc                   proc    defaults        0 0", "/dev/cdrom              /media/cdrom            iso9660 defaults        0 0"]}
fatal: [192.168.2.101]: FAILED! => {"attempts": 5, "changed": true, "cmd": "cat /etc/fstab", "delta": "0:00:00.002489", "end": "2017-09-04 01:11:02.560507", "failed": true, "rc": 0, "start": "2017-09-04 01:11:02.558018", "stderr": "", "stderr_lines": [], "stdout": "n#n# /etc/fstabn# Created by anaconda on Sun Jun 26 03:11:47 2016n#n# Accessible filesystems, by reference, are maintained under '/dev/disk'n# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more infon#n/dev/mapper/vg_test3-lv_root /                       ext4    defaults        1 1nUUID=e48217af-0ad9-45be-aa68-b0b1bbc88c97 /boot                   ext4    defaults        1 2n/dev/mapper/vg_test3-lv_swap swap                    swap    defaults        0 0ntmpfs                   /dev/shm                tmpfs   defaults        0 0ndevpts                  /dev/pts                devpts  gid=5,mode=620  0 0nsysfs                   /sys                    sysfs   defaults        0 0nproc                    /proc                   proc    defaults        0 0n/dev/cdrom              /media/cdrom            iso9660 defaults        0 0", "stdout_lines": ["", "#", "# /etc/fstab", "# Created by anaconda on Sun Jun 26 03:11:47 2016", "#", "# Accessible filesystems, by reference, are maintained under '/dev/disk'", "# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info", "#", "/dev/mapper/vg_test3-lv_root /                       ext4    defaults        1 1", "UUID=e48217af-0ad9-45be-aa68-b0b1bbc88c97 /boot                   ext4    defaults        1 2", "/dev/mapper/vg_test3-lv_swap swap                    swap    defaults        0 0", "tmpfs                   /dev/shm                tmpfs   defaults        0 0", "devpts                  /dev/pts                devpts  gid=5,mode=620  0 0", "sysfs                   /sys                    sysfs   defaults        0 0", "proc                    /proc                   proc    defaults        0 0", "/dev/cdrom              /media/cdrom            iso9660 defaults        0 0"]}
    to retry, use: --limit @/ansible/playbook/if_else.retry

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=0    changed=0    unreachable=0    failed=1   
192.168.2.111              : ok=0    changed=0    unreachable=0    failed=1

8. register同时接收多个变量进行传递

#注:接收到的多个值可以用jinja的for循环方式显示每个值
[root@nfs-server playbook]# cat register_vars.yml
---
- hosts: webservers
  remote_user: root
  gather_facts: False
  tasks:
  - name: "register接受多个值测试"
    shell: "{{ item }}"
    with_items:
      - hostname
      - uname
    register: ret
  - name: "显示接收到的值"
    debug: 'msg="{% for i in ret.results %} {{ i.stdout }} {% endfor%}"'
[root@nfs-server playbook]# ansible-playbook register_vars.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [register接受多个值测试] *********************************************************************************************************************************
changed: [192.168.2.101] => (item=hostname)
changed: [192.168.2.111] => (item=hostname)
changed: [192.168.2.101] => (item=uname)
changed: [192.168.2.111] => (item=uname)

TASK [显示接收到的值] *****************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": " lamp1  Linux "
}
ok: [192.168.2.111] => {
    "msg": " lamp2  Linux "
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=1    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=1    unreachable=0    failed=0

Docker创建的集群下使用ansible部署zookeeper

使用文章“Docker创建的集群下使用ansible部署hadoop”中创建的集群进行zookeeper的安装

未分类

在cluster-master上制作zookeeper安装包

下载

官方源下载显得十分缓慢,所以还是选择国内的镜像源,将zookeeper下载到/opt

[root@cluster-master opt]# wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/stable/zookeeper-3.4.10.tar.gz

创建链接

下载完成后将zookeeper-3.4.10.tar.gz解压并创建链接,方便管理

[root@cluster-master opt]# tar -zxvf zookeeper-3.4.10.tar.gz
[root@cluster-master opt]# ln -s zookeeper-3.4.10 zookeeper

修改配置文件

/opt/zookeeper/conf中已经提供了zoo_sample.cfg配置模板,复制一份zoo.cfg进行修改即可使用,我的配置项如下:

[root@cluster-master conf]# cp zoo_sample.cfg zoo.cfg
[root@cluster-master conf]# vi zoo.cfg

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/home/zookeeper/data
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1

server.2=172.18.0.2:2888:3888
server.3=172.18.0.3:2888:3888
server.4=172.18.0.4:2888:3888
server.5=172.18.0.5:2888:3888

dataDir做了从新定义,server项使用IP的最后一位,也是为了方便管理

创建shell脚本,完成安装步骤

在/opt/zookeeper下新建postinstall.sh创建dataDir目录和myid文件,并写入zoo.cfg中定义的myid值

vi /opt/zookeeper/postinstall.sh

#!/bin/bash

# zookeeper conf file
conf_file="/opt/zookeeper/conf/zoo.cfg"
# get myid
IP=$(/sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6 |
     awk '{print $2}')
ID=$(grep ${IP} ${conf_file}|cut -d = -f 1|cut -d . -f 2)

# get dataDir
dataDir=$(grep dataDir ${conf_file}|grep -v '^#'|cut -d = -f 2)

# create dataDir and myid file
mkdir -p ${dataDir}
:>${dataDir}/myid
echo ${ID} > ${dataDir}/myid

打包配置完成后的zookeeper,准备上传至slave主机

将链接zookeeper和目录zookeeper-3.4.10打包并压缩

[root@cluster-master opt]# tar -zcvf zookeeper-dis.tar.gz zookeeper zookeeper-3.4.10

创建yaml,安装zookeeper

[root@cluster-master opt]# vi install-zookeeper.yaml
---
- hosts: slaves
  tasks:
    - name: install ifconfig
      yum: name=net-tools state=latest
    - name: unarchive zookeeper
      unarchive: src=/opt/zookeeper-dis.tar.gz dest=/opt
    - name: postinstall
      shell: bash /opt/zookeeper/postinstall.sh

分发安装文件到slave主机

[root@cluster-master opt]# ansible-playbook install-zookeeper.yaml

启动zookeeper

此时,zookeeper集群已经可以正常启动

[root@cluster-master opt]# ansible cluster -m command -a "/opt/zookeeper/bin/zkServer.sh start"

查看状态

[root@cluster-master bin]# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: follower

运行客户端

[root@cluster-master bin]# ./zkCli.sh -server localhost:2181
Connecting to localhost:2181
2017-08-29 18:05:36,078 [myid:] - INFO  [main:Environment@100] - Client environment:zookeeper.version=3.4.10-39d3a4f269333c922ed3db283be479f9deacaa0f, built on 03/23/2017 10:13 GMT
2017-08-29 18:05:36,091 [myid:] - INFO  [main:Environment@100] - Client environment:host.name=cluster-master
2017-08-29 18:05:36,091 [myid:] - INFO  [main:Environment@100] - Client environment:java.version=1.8.0_141
2017-08-29 18:05:36,098 [myid:] - INFO  [main:Environment@100] - Client environment:java.vendor=Oracle Corporation
2017-08-29 18:05:36,098 [myid:] - INFO  [main:Environment@100] - Client environment:java.home=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.141-1.b16.el7_3.x86_64/jre
2017-08-29 18:05:36,098 [myid:] - INFO  [main:Environment@100] - Client environment:java.class.path=/opt/zookeeper/bin/../build/classes:/opt/zookeeper/bin/../build/lib/*.jar:/opt/zookeeper/bin/../lib/slf4j-log4j12-1.6.1.jar:/opt/zookeeper/bin/../lib/slf4j-api-1.6.1.jar:/opt/zookeeper/bin/../lib/netty-3.10.5.Final.jar:/opt/zookeeper/bin/../lib/log4j-1.2.16.jar:/opt/zookeeper/bin/../lib/jline-0.9.94.jar:/opt/zookeeper/bin/../zookeeper-3.4.10.jar:/opt/zookeeper/bin/../src/java/lib/*.jar:/opt/zookeeper/bin/../conf:
2017-08-29 18:05:36,099 [myid:] - INFO  [main:Environment@100] - Client environment:java.library.path=/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib
2017-08-29 18:05:36,100 [myid:] - INFO  [main:Environment@100] - Client environment:java.io.tmpdir=/tmp
2017-08-29 18:05:36,100 [myid:] - INFO  [main:Environment@100] - Client environment:java.compiler=<NA>
2017-08-29 18:05:36,100 [myid:] - INFO  [main:Environment@100] - Client environment:os.name=Linux
2017-08-29 18:05:36,100 [myid:] - INFO  [main:Environment@100] - Client environment:os.arch=amd64
2017-08-29 18:05:36,100 [myid:] - INFO  [main:Environment@100] - Client environment:os.version=3.10.0-514.26.2.el7.x86_64
2017-08-29 18:05:36,101 [myid:] - INFO  [main:Environment@100] - Client environment:user.name=root
2017-08-29 18:05:36,101 [myid:] - INFO  [main:Environment@100] - Client environment:user.home=/root
2017-08-29 18:05:36,101 [myid:] - INFO  [main:Environment@100] - Client environment:user.dir=/opt/zookeeper-3.4.10/bin
2017-08-29 18:05:36,124 [myid:] - INFO  [main:ZooKeeper@438] - Initiating client connection, connectString=localhost:2181 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@25f38edc
2017-08-29 18:05:36,205 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1032] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
Welcome to ZooKeeper!
JLine support is enabled
2017-08-29 18:05:36,730 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@876] - Socket connection established to localhost/127.0.0.1:2181, initiating session
2017-08-29 18:05:36,795 [myid:] - INFO  [main-SendThread(localhost:2181):ClientCnxn$SendThread@1299] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x25e2f22aa660001, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 1] 

总结

使用到了之前创建的集群和ansible,发现使用ansible部署应用确实很方便。

Docker部署GitLab

现在docker越来越火, 很多开源服务纷纷支持docker, gitlab也不例外, 现在官方也支持docker部署. 在此之前, 也有第三方支持过docker, 安装步骤相对来讲会复杂一点, 会把gitlab, postgresql和redis分别打包成镜像, 然后通过容器连接使用, 这里不介绍这种方式.

一、确保机器上安装了docker并启动

# 安装docker
yum install docker
# 启动docker
systemctl start docker

安装完成后建议配置加速器(比如阿里云镜像加速), 否则拉取镜像会非常慢, 特别是gitlab镜像, 还是非常大的

二. 拉取镜像并启动

# 拉取镜像
docker pull gitlab/gitlab-ce
# 启动
docker run --detach 
--publish 22443:443 --publish 2280:80  --publish 2222:22 
--name gitlab 
--memory 4g 
--restart always 
--volume /srv/gitlab/config:/etc/gitlab 
--volume /srv/gitlab/logs:/var/log/gitlab 
--volume /srv/gitlab/data:/var/opt/gitlab 
gitlab/gitlab-ce:latest

–publish 暴露了容器的三个端口, 分别是https对应的443, http对应80以及ssh对应的22(如果不需要配置https, 可以不暴露) –memory 限制容器最大内存暂用4G, 这是官方推荐的 –volume 指定挂载目录, 这个便于我们在本地备份和修改容器的相关数据

未分类

三. 修改配置文件并重启

# 打开挂载的配置目录
vi /srv/gitlab/config/gitlab.rb

###################################################
# 添加外部请求的域名(如果不支持https, 可以改成http)
external_url 'https://gitlab.yinnote.com'
# 修改gitlab对应的时区 
gitlab_rails['time_zone'] = 'PRC'
# 开启邮件支持 
gitlab_rails['gitlab_email_enabled'] = true
gitlab_rails['gitlab_email_from'] = '[email protected]'
gitlab_rails['gitlab_email_display_name'] = 'Yinnote GitLab'
# 配置邮件参数
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.mxhichina.com"
gitlab_rails['smtp_port'] = 25
gitlab_rails['smtp_user_name'] = "[email protected]"
gitlab_rails['smtp_password'] = "xxxxxx"
gitlab_rails['smtp_domain'] = "yinnote.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false        
###################################################

(选配) 如果配置了https, 需要导入证书

# 进入挂载配置目录
cd /srv/gitlab/config
# 创建密钥文件夹, 并放入证书
mkdir ssl
# 内容如下

未分类

重启服务

# 方法一: 重启容器(其中xxxxxx是容器id)
docker restart xxxxxx

# 方法二: 登陆容器, 重启配置
docker exec -it  xxxxxx bash   
gitlab-ctl reconfigure
  1. 通过ssh方式拉取代码时, 记住端口号是2222, 不是默认的22

  2. 如果没有配置https, 是无法通过https路径拉取代码的

Docker compose network笔记

1. 简述

默认docker compose仅仅会为你的app生成一个network, 所有依赖的服务都会加入这个网络中, 在docker-compose文件中定义的Projectname就是每一个镜像的hostname;

# 例子
# For example, suppose your app is in a directory called myapp,
# and your docker-compose.yml looks like this
version: "3"
services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres
    ports:
      - "8001:5432"
# 说明: A network called myapp_default is created.
我们可以使用 postgres://db:5432 方式链接postgres数据库

2. 自定义network,实现复杂网络结构

现实情况中的场景往往更复杂,一个network很难满足更复杂的需求; 比如说, 不同的Project在不同的网络中;

# 例子:
version: "3"
services:

  proxy:
    build: ./proxy
    networks:
      - frontend
  app:
    build: ./app
    networks:
      - frontend
      - backend
  db:
    image: postgres
    networks:
      - backend
networks:
  frontend:
    # Use a custom driver
    driver: custom-driver-1
  backend:
    # Use a custom driver which takes special options
    driver: custom-driver-2
    driver_opts:
      foo: "1"
      bar: "2"
# 说明
app可以和proxy和db互通, 但是proxy和db之间不能互通;

3. 修改默认网络

version: "3"
services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres
networks:
  default:
    # Use a custom driver
    driver: custom-driver-1

4. 加入一个已存在的网络

networks:
  default:
    external:
      name: my-pre-existing-network
# 一个实际应用场景
# 在项目重构迁移阶段, 老系统的测试环境old_test_docker_compose.yml, 新系统的测试环境new_test_docker_compose.yml, 老系统有部分服务需要调用新系统的
# 1. 第一步: 创建一个网络
docker network create share
# 2. 第二步: 在新老系统都添加这个网络为默认网络
networks:
  default:
    external:
      name: share

ansible变量

ansible变量的使用方法

1、主机变量:直接在/etc/ansible/hosts文件中,主机的后边设置key=value的格式

注:inventory_hostname是ansible自带的变量,代表组中的每个主机

#在配置文件中定义主机变量
[root@nfs-server playbook]# cat /etc/ansible/hosts
[webservers]
192.168.2.101 key=101
192.168.2.111 key=111
#写playbook,测试主机变量
[root@nfs-server playbook]# cat hosts_vars.yml
---
- hosts: webservers
  remote_user: root
  tasks:
  - name: 显示主机变量
    debug: msg="The server ip is {{ inventory_hostname }},The key is {{ key }}"
#执行playbook,查看主机变量是否正常显示
[root@nfs-server playbook]# ansible-playbook hosts_vars.yml 

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.111]
ok: [192.168.2.101]

TASK [显示主机变量] ************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The key is 101"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The key is 111"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0

2、主机组变量

#在配置文件中定义主机组变量
[root@nfs-server playbook]# cat /etc/ansible/hosts
[webservers]
192.168.2.101 
192.168.2.111 
[webservers:vars]
ansiber_version=2.3
key=nginx
#写playbook,测试主机组变量
[root@nfs-server playbook]# cat hosts_group_vars.yml
---
- hosts: webservers
  remote_user: root
  tasks:
  - name: "显示主机组变量"
    debug: msg="The server ip is {{ inventory_hostname }},The key is {{ key }}"
#执行playbook,查看主机组变量是否正常显示
[root@nfs-server playbook]# ansible-playbook hosts_group_vars.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.111]
ok: [192.168.2.101]

TASK [显示主机组变量] *****************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The key is nginx"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The key is nginx"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0

3、通过/etc/ansible下的文件定义主机以及主机组变量

主机变量目录:host_vars(注:需要在此目录下为每个主机创建一个文件,在此文件中定义变量)

组变量目录:group_vars(注:需要在此目录下创建一个以主机组名命名的文件,在此文件中定义变量)

注:以上两个目录均不存在,需要自己手动创建;mkdir /etc/ansible/{host_vars,group_vars}

注:当需要为所有主机或者所有主机组创建一样的变量时,在host_vars和group_vars目录下,创建all文件并写入变量即可~

#在配置文件中定义主机组变量
[root@nfs-server playbook]# cat /etc/ansible/hosts
[webservers]
192.168.2.101 
192.168.2.111 
#在host_vars目录下创建以主机名为名字的文件,并在此文件中配置变量,变量格式为: key: value
[root@nfs-server ansible]# head host_vars/*
==> host_vars/192.168.2.101 <==
key: 101

==> host_vars/192.168.2.111 <==
key: 111
#在group_vars目录创建以主机组为名字的文件,并在此文件中配置变量,变量格式为:key: value
[root@nfs-server ansible]# head group_vars/*
name: nginx
#测试主机变量:利用上面写的主机变量playbook进行测试
[root@nfs-server playbook]# ansible-playbook hosts_vars.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.111]
ok: [192.168.2.101]

TASK [显示主机变量和主机组变量] ************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The key is 101"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The key is 111"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0   
#测试主机组变量:利用上面写的主机组变量playbook进行测试(把key名字改为了name)
[root@nfs-server playbook]# ansible-playbook hosts_group_vars.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.101]
ok: [192.168.2.111]

TASK [显示主机组变量] *****************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The application is nginx"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The application is nginx"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0

4、通过命令行传入变量,或者变量文件

-e,–extra-vars:此参数表示从命令行传入变量

-e “key=value”:直接传递变量的格式

-e “@vars.yml”:传递变量文件的格式

#在playbook中引用一个key为YOURNAME,此key通过命令行传入
[root@nfs-server playbook]# cat hosts_vars.yml 
---
- hosts: webservers
  remote_user: root
  tasks:
  - name: 显示主机变量和主机组变量
    debug: msg="The server ip is {{ inventory_hostname }},The key is {{ YOURNAME }}"
[root@nfs-server playbook]# ansible-playbook hosts_vars.yml -e "YOURNAME=liuzhengwei"

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.101]
ok: [192.168.2.111]

TASK [显示主机变量和主机组变量] ************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The key is liuzhengwei"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The key is liuzhengwei"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0 
#在playbook中引用一个key为YOURNAME,此key通过提前定义的变量文件传入
#下面文件为变量文件
[root@nfs-server playbook]# cat vars/vars.yml 
abc: LIUZHENGWEI
#通过命令行将此变量文件传入
[root@nfs-server playbook]# ansible-playbook hosts_vars.yml -e "@vars/vars.yml"

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.111]
ok: [192.168.2.101]

TASK [显示主机变量和主机组变量] ************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The key is LIUZHENGWEI"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The key is LIUZHENGWEI"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0

5.在playbook文件中定义变量:格式为key: value

#在playbook文件中定义变量abc
[root@nfs-server playbook]# cat hosts_vars.yml
---
- hosts: webservers
  remote_user: root
  vars:
    abc: liuzhengwei
  tasks:
  - name: 显示主机变量和主机组变量
    debug: msg="The server ip is {{ inventory_hostname }},The key is {{ abc }}"
#执行playbook测试变量是否生效
[root@nfs-server playbook]# ansible-playbook hosts_vars.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.111]
ok: [192.168.2.101]

TASK [显示主机变量和主机组变量] ************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The key is liuzhengwei"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The key is liuzhengwei"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0

6、在playbook文件中引用定义了变量的文件:vars_files

#在playbook文件中引用定义了变量的文件vars/vars.yml
[root@nfs-server playbook]# cat vars/vars.yml 
abc: LIUZHENGWEI
[root@nfs-server playbook]# cat hosts_vars.yml 
---
- hosts: webservers
  remote_user: root
  vars_files:
    - vars/vars.yml
  tasks:
  - name: 显示主机变量和主机组变量
    debug: msg="The server ip is {{ inventory_hostname }},The key is {{ abc }}"
#执行playbook测试变量是否生效
[root@nfs-server playbook]# ansible-playbook hosts_vars.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.101]
ok: [192.168.2.111]

TASK [显示主机变量和主机组变量] ************************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The server ip is 192.168.2.101,The key is LIUZHENGWEI"
}
ok: [192.168.2.111] => {
    "msg": "The server ip is 192.168.2.111,The key is LIUZHENGWEI"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=2    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=2    changed=0    unreachable=0    failed=0

7、使用register内的变量

ansible playbook内task之间可以互相传递变量,比如我们总共有2个tasks,其中第2个task是否执行需要判断第一个task运行后的结果,这个时候我们就得在task之间传递数据,需要把第一个task执行的结果传递给第2个task.Ansible task之间传递数据使用register方式。

#注:这是第1个task把执行hostname的结果register给info这个变量
[root@nfs-server playbook]# cat register_var.yml 
---
- hosts: webservers
  remote_user: root
  tasks:
  - name: register variables
    shell: hostname
    register: info
  - name: display variables
    #debug: msg="The variable is {{ info['stdout'] }}"
    debug: msg="The variable is {{ info.stdout }}"
#注:register返回结果是字典的方式,所以用info.stdout或info['stdout']的方式返回结果
[root@nfs-server playbook]# ansible-playbook register_var.yml

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.101]
ok: [192.168.2.111]

TASK [register variables] ******************************************************************************************************************************
changed: [192.168.2.101]
changed: [192.168.2.111]

TASK [display variables] *******************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "The variable is lamp1"
}
ok: [192.168.2.111] => {
    "msg": "The variable is lamp2"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=3    changed=1    unreachable=0    failed=0   
192.168.2.111              : ok=3    changed=1    unreachable=0    failed=0

8、使用vars_prompt传入变量(vars_prompt是以交互式的方式给定义好的参数传入变量值)

注:交互式传入的变量值可以是加密的也可以是不加密的,用private进行定义

#交互式输入变量值,传递给tasks,private=no表示输入的变量值不加密,private=yes表示加密
[root@nfs-server playbook]# cat vars_prompt.yml 
---
- hosts: all
  remote_user: root
  vars_prompt:
  - name: "one"
    prompt: "please input one value"
    private: no
  - name: "two"
    prompt: "please input two value"
    private: yes
  tasks:
  - name: display one value
    debug: msg="one value is {{ one }}"
  - name: display two value
    debug: msg="two value is {{ two }}"
[root@nfs-server playbook]# ansible-playbook vars_prompt.yml 
please input one value: Liu Zhengwei
please input two value: 

PLAY [webservers] **************************************************************************************************************************************

TASK [Gathering Facts] *********************************************************************************************************************************
ok: [192.168.2.101]
ok: [192.168.2.111]

TASK [display one value] *******************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "one value is Liu Zhengwei"
}
ok: [192.168.2.111] => {
    "msg": "one value is Liu Zhengwei"
}

TASK [display two value] *******************************************************************************************************************************
ok: [192.168.2.101] => {
    "msg": "two value is age is 28"
}
ok: [192.168.2.111] => {
    "msg": "two value is age is 28"
}

PLAY RECAP *********************************************************************************************************************************************
192.168.2.101              : ok=3    changed=0    unreachable=0    failed=0   
192.168.2.111              : ok=3    changed=0    unreachable=0    failed=0

如何使用Docker、Docker-Compose和Rancher搭建部署Pipeline(二)

该系列文章分享了用Docker、Docker-Compose和Rancher完成容器部署工作流的经验。

本文将分享如何解决自动化不够、测试困难、追踪及新增环境变量易错等痛点,且使部署逻辑向使用Docker Compose的应用更近一步。

在这一系列文章的第一篇中,我们分享了只用Docker时我们开发的初步的工作流,如何创建一个基础的构建和部署流水线。容器的部署方式不再是在登陆server的时候从内存中输入Docker命令。我们已经通过Jenkins server实现了镜像的自动化构建。我们使用脚本将Docker命令进行封装,将其存储到GitHub中并且设置版本。目前我们正采取措施,通过逐步改善现有过程来实现持续部署。然而,仍有一些痛点需要我们去解决。在这篇文章中,我们将看看如何使用Docker Compose 和 Ansible来改善此设计。

在部署镜像时,工程师需要登录到服务器,并从shell运行我们的Docker wrapper脚本。这不是很好的解决方法,因为它也需要开发者进行等待。没有任何一方会从在这种方式中获益(作为一个工程师,当你去做某件你很了解并且很容易自动化的事情时,你有多少次被打断了?)由于每一次部署都是通过操作者电脑中的SSH会话来执行的,因此部署过程是不可见的。

如果你对我们的部署脚本还有印象,你会发现它看起来像下面的代码段:

未分类

实际上,我们做的是将Docker run命令语句进行抽象,由此工程师将不需要知道每个图像成功运行时所需要的确切的参数。虽然这改善了必须全部记住并且手动输入所有Docker参数的现状,但同时也会带来新的问题:

  • 每个容器的逻辑都存储在同一文件中,这使得对应用程序部署逻辑的更改更难追踪;
  • 当开发者需要测试或者修改参数时,需要被迫理清脚本中的逻辑,而不是能够在某一特定的程序中轻松地阅读和修改参数。

在我们的工作流中,Docker Compose是一个更适合使用的工具,它同样可以将部署参数进行编码,并且在YAML文件中指定,此文件就是docker-compose.yml。Docker Compose不仅帮助我们解决了上面提到的难点,而且也可以使我们从社区未来的工作中获益。下面让我们理清部署脚本,并且为我们的JAVA程序示例创建一个Compose文件。首先,我们需要基于原来的部署逻辑创建一个docker-compose.yml文件:

未分类

现在,部署容器只需要在与docker-compose.yml文件相同目录下输入以下命令:

docker-compose up

它将根据compose文件中设置的参数启动一个容器。在compose文件中一个重要的变量是${VERSION} 。Docker Compose 可以从当前的shell环境中插入compose文件里所列出的参数。我们可以通过简单地运行以下语句来设置参数:

VERSION=1.0.0 docker-compose up

它将从我们的私有镜像仓库挑出标记1.0.0的镜像,以此启动java-service-1程序。如果没有设置VERSION变量,Docker Compose将产生一条警告信息,并且用空字符串代替变量值,由此,具有最新版本标签的镜像将会被挑出。因此,正确地设置变量是相当重要的。

作为开发过程的一部分,我们希望开发人员能够在本地建立服务并且测试他们的服务。然而,由于docker-compose.yml指向私有镜像仓库的镜像,运行docker-compose将从最近构建的镜像中开启服务而不是从本地资源中开启。理想情况下,开发者可以通过运行以下代码使用典型的docker-compose工作流:

未分类

Docker Compose能在不修改docker-compose.yml文件的情况下,让我们做到这一点。我们可以使用多个文件来覆盖我们在本地测试中想要改变的任何参数。在docker-compose.override.yml中,我们指定一个key而不是一个镜像,并且移除了对VERSION变量的需求。由于这是一个覆盖文件,我们不需要复制任何额外的设置,如端口设置:

未分类

使用Docker Compose而非部署脚本之后,我们可以:

  • 在源代码中存储每个compose文件,这与Dockerfile类似;
  • 不再需要复杂的部署脚本;
  • 允许开发人员在本地轻松地测试并修改应用程序。

现在我们有了java-service-1程序的compose文件,我们可以将它从我们的部署脚本中删除,因此文件组织与下面的结构类似:

未分类

此时,我们仍然没有解决镜像构建和部署之间的问题。在docker-compose.yml文件中包含了所有的部署逻辑,但是它如何在环境中运行直至结束的呢?正好现在我们在运行与UNIX和TPC socket相关的Docker守护进程,是时候讨论一些与安全有关的问题了。

我们的情况是,工程师登录到服务器上,手动运行每个服务器所需容器的部署脚本。默认情况下,当在局部运行Docker命令时,它将使用UNIX socket /var/run/docker.sock连接Docker守护进程;或者让守护进程监听TCP socket,这允许用户远程连接到每个Docker守护进程,使得工程师能够像登录到主机一样运行命令。这为连接方式提供了更大的灵活性,但是没有考虑到一些开销和安全问题:

  • 通过网络连接增加了安全隐患;
  • 增加了对于基于主机或者基于网络的ACLs需求;
  • 保护守护进程需要分布式CA和客户端认证。

另一种可能的方法是不使用基于UNIX socket的方式运行Docker守护进程,而使用SSH来运行命令。已经建立的ACLs将保护SSH端口,并且它只允许通过SSH授权的特定的用户才能使用Docker守护进程。虽然这不是最简洁的方法,但是它有助于保持较低的运行开销,并且使安全隐患降到最低。这点是非常重要的,尤其是对于细粒度的稀疏的任务队列而言。

为了有利于通过SSH运行Docker命令,我们可以使用Ansible——一个流行的编排和配置管理工具。它是无代理的,并且允许通过SSH连接运行“剧本”(服务器任务集合)。一个运行docker-compose命令的简单的剧本如下所示:

未分类

如果你对Ansible没有过多了解,你也许可以通过上面的剧本大致了解到我们想做什么。它们按顺序一步步执行,具体如下所示:

  1. Ansible将通过SSH连接到目标服务器(允许通过使用DESTINATION变量来指定主机)

  2. 在每个服务器中,Ansible会通过执行shell命令登录到公司私有的镜像仓库

  3. Ansible将位于Jenkins(运行ansible剧本的服务器)中的docker-compose.yml文件复制到每个目标服务器中的/tmp/docker-compose.yml下

  4. 在每个目标服务器中运行docker-compose命令

  5. 通过删除远程的/tmp/docker-compose.yml文件进行清理

一个shell脚本可以被运用在同一个事件中。然而在Ansible中,我们将很容易的使任务并行化并且得到经过良好测试的模块,通过使Ansible与新的部署剧本相结合,我们可以远程启动容器,相较于工程师登录到主机、人工运行命令,这是一个重要的进步。为了在部署过程和状态中提供更大的可视性,我们将建立Jenkins任务来运行Ansible代码。通过使用Jenkins,在未来我们可以轻松地将构建和部署任务集成起来,从而得到额外的好处。

Jenkins任务需要两个参数:目标主机(传递给剧本中的DESTINATION变量)和部署镜像的版本(在docker-compose.yml文件中插入VERSION变量)。大多数任务的构建部分是一个shell构建器,它将试图找到程序中的docker-compose.yml文件,然后通过传递变量(用-e)到剧本中,运行ansible-playbook命令:

未分类

虽然看起来我们似乎只对工作流做了微小的变化,但是我们正一步一步地向构建一个持续部署模型迈进:

  • 部署是可以被审查的。我们使用日志来记录输出什么、何时输出、以及哪些主机是目标主机等信息,这一切都归功于Jenkins。
  • 程序部署逻辑已经从一个单一的脚本分散到存储在程序源代码中的单独的docker-compose.yml文件中,这意味着我们可以轻松地通过git更改程序部署逻辑。在程序源文件或者部署文件发生变化时,我们也可以容易地进行构建和部署。

虽然这些改进解决了某些问题,但是它们所带来的新的问题也成为了焦点:

  • 哪个容器的哪个版本会被部署到何地?
  • 容器在被部署后会处于哪种状态?
  • 我们如何确定哪个主机成为程序的目标主机?

在这一系列接下来的文章中,我们将探讨怎样运行Rancher以及使用它的原因,尤其是它如何解决上述的问题。与此同时,我们也讨论它在业务和开发团队中所起到的意想不到的桥梁作用。

如何使用Docker、Docker-Compose和Rancher搭建部署Pipeline(三)

该系列文章分享了用Docker、Docker-Compose和Rancher完成容器部署工作流的经验。本文将探讨怎样运行Rancher,解决只使用Docker-Compose时面临的主机负载失衡、获取服务状态困难、多环境下主机与集群难以管理等的问题。

在这一部分,我们将一步步的走进Rancher,细致的探讨Rancher将如何解决在部署与容器管理时出现的种种的问题。回顾教程的第二部分,你会发现我们已经将应用的部署迁移至Docker Compose,并且已经建立了一系列工作步骤来部署我们的应用。这将使得开发人员能够轻松的对他们的应用部署逻辑进行修正,运维人员也可以查看应用的部署时间。当然,在上一个部分教程的一系列操作中,也存在一些显而易见的问题需要解决。

使用Docker-Compose时面临的挑战

首先,运维人员必须手动地调整所有服务的执行计划。部署人员需要决定将哪一个应用部署至哪一台主机,这意味着部署人员需要时刻对每一台主机的剩余可用资源都有了解,如果某一台主机或者容器崩溃了,部署的操作人员将需要对应用进行重新部署。实际生产中,这意味着主机常常处于负载失衡的状态,并且服务在崩溃之后需要很长时间才能得到恢复。

其次,使用Docker-Compose时,想要获得你的服务的当前状态是十分困难的。举个例子来说,我们经常会从运维人员、项目经理以及开发者口中听到这样的问题:“现在部署环境中运行的到底是XX应用程序的哪个版本?”如果我们采用的是手动调整服务的执行计划的方式,想要得到这个问题的答案通常需要询问指定的进行操作的工程师,工程师们需要登陆服务器并运行docker中的ps命令来查看容器的信息。然而面对这些问题,Rancher将会给我们提供极大的便利:每个人都可以非常容易地获取已经部署的服务的信息,而不需要临时请求运维人员的帮助。

使用Rancher之前,我们试着了解过不少其他能够管理Docker主机或集群的解决方案。然而这些解决方案都没有注意到这是对Docker主机或集群在多种环境(multi-environment)下的管理,这将成为最大的麻烦与负担之一。如果有服务以不同的负载运行在8种不同的环境下,我们需要的是一个统一的方式来管理集群,而不会想要访问8个不同的服务。并且,我们希望让重新构建环境对于我们而言,变成分分钟就能完成的任务,这样开发者就可以随意地更改开发环境。然而,对于生产环境而言,我们希望提供给他们的只是有限的只读访问权限。面对这样的需求,一个采用基于角色的访问控制(RBAC)模型的集中管理方案就显得十分必要了。我们最初决定尝试Rancher就是因为它在部署上非常简单。

当Rancher面临这些挑战

在短短半天的时间里,使用AWS ELB、Elasticache、RDS和现有的Docker主机,我们已经将Rancher部署好并成功运行。能够方便地配置认证信息也是Rancher的优点之一。

我们并不会深入Rancher本身部署的细节,Rancher部署文档中已经说的很明白了。相反,我们将从刚刚完成初始设置那一步开始,说明将如何将原有的设置(教程第一部分和第二部分中所提及的)迁移进来。

我们就从创建不同的环境开始吧,为了使得这个过程尽量简单些,我们将对开发环境(dev)、部署环境(stage)以及生产环境(prod)分别进行设置。每个环境都已有运行在Ubuntu之上的Docker主机,且这些Docker主机是由内部的Ansible配置的,Ansible安装了Docker、我们的监控代理、并进行了一些组织特定的更改。在Rancher上,你只需要运行一条命令,将Docker主机在Rancher server内部进行注册,就可以将已有的Docker主机添加至每个环境中。

添加一台Rancher主机

在大多数情况下,想要添加一台主机需要经过一系列的操作:通过鼠标在网页上完成一些点击,接下来切换至某个特定的环境,最后在终端系统上输入命令。然而,如果你使用Rancher API,我们可以在Ansible工具的帮助下使得这一系列的操作转化为完全自动化的设置。出于好奇,在下面我们截取了playbook中有关这一操作的部分内容(大多是根据 Hussein Galas的repo中的内容做出的逻辑上的修改而得到的)。

name: install dependencies for uri module
  apt: name=python-httplib2 update_cache=yes
  name: check if the rancher-agent is running
  command: docker ps –filter ‘name=rancher-agent’
  register: containers
  name: get registration command from rancher
  uri:
  method: GET
  user: “{{ RANCHER_API_KEY }}”
  password: “{{ RANCHER_SECRET_KEY }}”
  force_basic_auth: yes
  status_code: 200
  url: “https://rancher.abc.net/v1/projects/{{ RANCHER_PROJECT_ID }}/registrationtokens”
  return_content: yes
  validate_certs: yes
  register: rancher_token_url
  when: “‘rancher-agent’ not in containers.stdout”
  name: register the host machine with rancher
  shell: >
  docker run -d –privileged
  -v /var/run/docker.sock:/var/run/docker.sock
  {{ rancher_token_url.json[‘data’][0][‘image’] }}
  {{ rancher_token_url.json[‘data’][0][‘command’].split() | last}}
  when: “‘rancher-agent’ not in containers.stdout”

随着工作的一步步进行,我们已经完成了环境的创建并已经将主机在Rancher server中注册,现在就让我们来了解一下,如何将我们的部署工作流整合至Rancher中。我们知道,对于每一台Docker来说,其中都有着一些正在运行的容器,这些系统的部署是通过Ansible工具借助Jenkins完成的。Rancher提供了以下开箱即用的功能:

  • 管理已有的容器(比如:启动、修改、查看日志、启动一个交互式的shell)
  • 获得关于运行中的和停止运行的容器的信息(比如:镜像信息、初始化命令信息、命令信息,端口映射信息以及环境变量信息)
  • 查看主机和容器层级上的资源使用情况(比如:CPU使用率、内存占用率、以及磁盘和网络的使用情况)

独立的容器

很快,我们就已经将Docker主机注册至Rancher Server中,现在我们可以查看容器在各种环境下的运行状态信息了。不仅如此,如果想要将这些信息分享给其他团队,我们仅仅需要针对某个环境给予他们一些有限的权限。通过以上的方式,在想要获得状态信息时我们就完全没有必要请求操作人员登录Docker主机,再通过人工的方式去查询,同时这样也减少了申请获得环境信息的请求的数目,因为我们已经将某些访问权限分配至各个团队了。举个例子来说,如果为开发团队分配环境信息的只读权限,那么将会在开发团队与部署操作团队之间架起一座沟通的桥梁,这样两个团队都会对这个环境的状态比以往更加的关心。在这个基础上,故障的排除也变成了一种小组间相互合作的过程,而不是以往的那种单向的、依赖同步信息流的解决方式,相互合作的方式也会减少解决突发事件的总时间。

到现在为止,我们已经将已有的Docker主机加入Rancher Server,并且基于已经阅读完了的教程的第一部分关于Jenkins和Rancher的内容,下一步,我们打算改进的部分是我们已有的部署流水线,我们将会对已有的部署流水线进行修改,以便于使用Rancher compose,Rancher Compose将代替之前Ansible工具提到的Docker compose。不过在我们深入下一部分之前,我们首先需要了解关于Rancher的应用、调度、Docker Compose和Rancher Compose的一些信息。

应用与服务:Rancher将每个独立的容器(指的是部署在Rancher之外的容器,或者是通过Rancher UI生成的一次性功能的容器)、应用和服务彼此分离开。简单地说,应用是一组服务,而所有容器都需要利用服务(关于应用和服务的内容之后将会由更加详细的介绍)以构建一个应用。独立的容器需要手动地进行调度。

调度:在之前的部署技术中,运维人员需要决定容器应当在哪一台主机上运行。如果使用的是部署脚本,那么意味着运维人员需要决定部署脚本在哪一台或哪几台主机上运行;如果使用Ansible,这将意味着运维人员需要决定哪些主机或组需要到Jenkins中工作。不论是哪一种方式,都需要运维人员去做一些决定,但是在大多数情况下,他们做出的决定都缺乏一些可靠的依据,这对我们的部署工作很是不利(比如说某一台主机的CPU使用率高达100%)。很多解决方案,比如像Docker Swarm、Kubernetes、Mesos和Rancher都采用了调度器来解决这类问题。对于需要执行的某个操作,调度器将会请求获得一组主机的信息,并判断出哪几台是适合执行这个操作的。调度器会根据默认的需求设定或者用户定义的特定需求,比如CPU使用率高低、亲和性或反亲和性规则(比如:禁止在同一台主机上部署两个相同容器)等类似的需求,以逐渐缩小主机选择的范围。如果我是一个负责部署的运维人员,调度器将会极大的减少我的工作负担(尤其是我在深夜加班忙于部署时),因为调度器对以上信息的计算比我快的多,也准的多。Rancher在我们通过应用部署服务的时候能够提供一个开箱即用调度器。

Docker compose:Rancher使用Docker compose来创建应用并定义服务。由于我们已经将服务转化为Docker compose的文件,我们在此基础上创建应用就变得容易了许多。应用可以手动的从UI界面中创建,也可以通过Rancher compose在命令行(CLI)下快速的创建。

Rancher compose:Rancher compose是一种通过命令行(CLI)让我们得以对Rancher中的每一种环境的应用和服务进行方便的管理的工具。同时,通过rancher-compse.yml文件,Rancher compose还能允许对Rancher工具进行一些其他访问。这是一个纯粹的附加的文件,将不会取代原有的docker-compose.yml文件。在rancher-compose.yml文件中,你可以定义以下内容,比如说:

  • 每种服务的升级策略信息
  • 每种服务的健康检查信息
  • 每种服务的需求规模信息

这些都是Rancher中非常实用的亮点,如果你使用Docker Compose或者Docker daemon,这些内容你都是获取不到的。如果想要查看Rancher Compose能提供的所有特性,你可以查看这个文档

通过将已有的部署工作交给Rancher Compose来替代之前的Ansible工具,我们能够很轻松的将服务迁移并部署为Rancher应用的形式。之后,我们就能够去除DESTINATION参数了,但我们依然保留VERSION参数,因为我们在插入docker-compose.uml文件的时候还要使用它。以下是使用Jenkins部署时,部署逻辑的shell片段:

export RANCHER_URL=http://rancher.abc.net/
  export RANCHER_ACCESS_KEY=…
  export RANCHER_SECRET_KEY=…

if [ -f docker/docker-compose.yml ]; then
  docker_dir=docker
  elif [ -f /opt/abc/dockerfiles/java-service-1/docker-compose.yml ]; then
  docker_dir=/opt/abc/dockerfiles/java-service-1
  else
  echo “No docker-compose.yml found. Can’t continue!”
  exit 1
  fi

if ! [ -f ${docker_dir}/rancher-compose.yml ]; then
  echo “No rancher-compose.yml found. Can’t continue!”
  exit 1
  fi

/usr/local/bin/rancher-compose –verbose 
  -f ${docker_dir}/docker-compose.yml 
  -r ${docker_dir}/rancher-compose.yml 
  up -d –upgrade

阅读完代码段,我们可以发现其主要包括以下内容:

我们定义了以环境变量的方式如何访问我们的Rancher server。
需要找到docker-compose.yml文件,否则将会任务将会报错退出。
需要找到rancher-compose.yml文件,否则任务将会报错退出。
运行Rancher-compose,并告诉它不要block并且使用-d命令输出日志,使用-upgrade命令更新一个已经存在的服务。

也许你已经发现了,在绝大部分,代码的逻辑都是相同的,而最大的区别就是使用rancher-compose代替使用Ansible工具完成部署,并对每一个服务添加了rancher-compose.yml文件。具体到我们的java-service-1应用,docker-compose文件和rancher-compose文件现在是这样的:

docker-compose.yml
  java-service-1:
  image: registry.abc.net/java-service-1:${VERSION}
  container_name: java-service-1
  expose:
  – 8080
  ports:
  – 8080:8080
  rancher-compose.yml
  java-service-1:
  scale: 3

在开始部署工作之前,我们先回顾一下部署工作的流程:

  • 开发人员将代码的修改推送至git上
  • 使用Jenkins对代码进行单元测试,在测试工作结束之后触发下游工作
  • 下游工作采用新的代码构建一个docker镜像,并将其推送至我们自己的Docker镜像仓库中
  • 创建包含应用名、版本号、部署环境的deployment ticket
DEPLOY-111:
  App: JavaService1, branch “release/1.0.1”
  Environment: Production
  • 部署工程师针对应用运行Jenkins的部署工作,运行时需要将版本号作为参数。
  • Rancher compose开始运行,对于某个环境创建或更新应用,并且当达到所需规模的时候,结束这个工作
  • 部署工程师以及开发工程师分别手动地对服务进行校验
  • 部署工程师在Rancher UI中确认完成升级

关键点

使用Rancher进行我们的服务部署时,我们从Rancher内建的调度、弹性伸缩、还原、升级、和回滚等工具中获得极大的便利,使得我们在部署过程中没有花太大的力气。同时我们发现,在将部署工作从Ansible工具中迁移至Rancher的工作量也是很小的,仅仅需要在原有的基础上增加rancher-compose.yml文件。然而,使用Rancher来处理我们容器的调度意味着我们将难以确认我们的应用到底是在哪台主机上运行的。比方说,之前我们并没有决定java-service-1应用在哪里运行,对于后端,在进行负载均衡相关操作时,该应用就没有一个静态的IP。我们需要找到一种办法,使得我们的各种应用之间能够相互察觉到对方。最终,对于我们的java-service-1应用,我们将明确地将应用容器所在的docker主机的8080端口与应用绑定,不过,如果有其他服务与应用绑定为相同的端口,它将会启动失败。通常负责调度决策的工程师将会对以上的事务进行处理。然而,我们最好将这些信息通知调度器以避免这样的事情发生。

在本教程的最后一个部分,我们将继续探索一些方案来解决在使用亲和性规则、主机标签、服务探索以及智能升级和回滚等特性时出现的问题。