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环境搭建elasticsearch

本文主要讲如何使用使用docker搭建elasticsearch。

下载镜像

这里利用hangxin1940搭好的镜像,不过是es的1.4.2版本。

docker pull hangxin1940/docker-elasticsearch-cn:v1.6.0

启动容器

docker run -d -p 9200:9200 -p 9300:9300 --name es hangxin1940/docker-elasticsearch-cn:v1.6.0

查看es(这里的ip是docker的default machine的ip)

访问http://192.168.99.100:9200/

{
status: 200,
name: "node1",
cluster_name: "cn-out-of-box",
version: {
number: "1.6.0",
build_hash: "cdd3ac4dde4f69524ec0a14de3828cb95bbb86d0",
build_timestamp: "2015-06-09T13:36:34Z",
build_snapshot: false,
lucene_version: "4.10.4"
},
tagline: "You Know, for Search"
}

查看集群状态

http://192.168.99.100:9200/_plugin/head/

未分类

也可以用命令行

curl -XGET http://192.168.99.100:9200/_cluster/health?pretty

返回

{
  "cluster_name" : "cn-out-of-box",
  "status" : "yellow",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 1,
  "active_shards" : 1,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 1,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0
}

这里目前只是单节点的,后续弄成集群看看。

查看插件

http://192.168.99.100:9200/_plugin/oob

未分类

增删改查

增加

curl -XPUT 'http://192.168.99.100:9200/twitter/tweet/1' -d '{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elastic Search"
}'

返回

{"_index":"twitter","_type":"tweet","_id":"1","_version":1,"created":true}%

查询

curl -XGET 'http://192.168.99.100:9200/twitter/tweet/1'

返回

{"_index":"twitter","_type":"tweet","_id":"1","_version":1,"found":true,"_source":{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elastic Search"
}}%

未分类

高级查询:选择字段

curl -XGET 'http://192.168.99.100:9200/twitter/tweet/1?fields=message,user&pretty=true'

返回

{
  "_index" : "twitter",
  "_type" : "tweet",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "fields" : {
    "message" : [ "trying out Elastic Search" ],
    "user" : [ "kimchy" ]
  }
}

高级查询:选择格式

curl -XGET 'http://192.168.99.100:9200/twitter/tweet/1?fields=message,user&format=yaml'

返回

---
_index: "twitter"
_type: "tweet"
_id: "1"
_version: 1
found: true
fields:
  message:
  - "trying out Elastic Search"
  user:
  - "kimchy"

更新

curl -X PUT http://192.168.99.100:9200/twitter/tweet/1 -d '{"message": "hello world", "user": "patterncat"}'

返回

{"_index":"twitter","_type":"tweet","_id":"1","_version":2,"created":false}%

这个是覆盖更新,不是局部更新:

 ~  curl -XGET 'http://192.168.99.100:9200/twitter/tweet/1'
{"_index":"twitter","_type":"tweet","_id":"1","_version":2,"found":true,"_source":{"message": "hello world", "user": "patterncat"}}%

删除

curl -XDELETE 'http://192.168.99.100:9200/twitter/tweet/1'

返回

{"found":true,"_index":"twitter","_type":"tweet","_id":"1","_version":3}%

查看mapping

{
    "twitter": {
        "mappings": {
            "tweet": {
                "properties": {
                    "message": {
                        "type": "string"
                    }, 
                    "post_date": {
                        "type": "date", 
                        "format": "dateOptionalTime"
                    }, 
                    "user": {
                        "type": "string"
                    }
                }
            }
        }
    }
}

索引分析

http://192.168.99.100:9200/twitter/_analyze?field=message&text=hello%20world

{
    "tokens": [
        {
            "token": "hello", 
            "start_offset": 0, 
            "end_offset": 5, 
            "type": "<ALPHANUM>", 
            "position": 1
        }, 
        {
            "token": "world", 
            "start_offset": 6, 
            "end_offset": 11, 
            "type": "<ALPHANUM>", 
            "position": 2
        }
    ]
}

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

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创建的集群下使用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部署应用确实很方便。

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部署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路径拉取代码的

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 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

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

重磅系列干货,如何使用Docker、Docker Compose和Rancher搭建deployment pipeline。

本文为最后一篇,讨论如何实现consul的服务发现。

在这篇文章中,我们将讨论如何用Rancher实现consul的服务发现。

如果你还没有准备好,推荐你阅读本系列中先前的文章:

第一篇:CI /CD和Docker入门
第二篇:使部署逻辑向使用Docker Compose更进一步
第三篇:借力Rancher完成容器编排

在这构建部署流水线系列的最后一篇文章中,我们将探讨在转换到Rancher进行集群调度时面临的一些挑战。在之前的文章中,我们通过使用Rancher执行调度,让运维人员无须再负责选择每一次容器运行的位置。要使用这个新方案,我们必须让环境的其他部分知道调度程序放置这些服务的位置,以及如何访问它们。我们还将讨论如何使用标签来操作调度程序,以调整容器放置位置,并避免端口绑定冲突。最后,我们将通过利用Rancher的回滚功能优化我们的升级过程。

在引入Rancher之前,我们的环境是一个相当静态的环境。我们总是将容器部署到相同的主机上,而部署到不同的主机则意味着我们需要更新一些配置文件以反映新位置。例如,如果我们要添加’java-service-1’应用程序的一个附加实例,我们还需要更新load balancer以指向附加实例的IP。使用调度器让我们无法预测容器部署的位置,并且我们需要动态配置环境,使其能自动适应变化。为此,我们需要使用服务注册和服务发现。

服务注册表为我们提供了应用程序在环境中的位置的单一来源。和硬编码服务位置不同,我们的应用程序可以通过API查询服务注册表,并在我们的环境发生变化时自动重新配置。Rancher使用Rancher的DNS和元数据服务提供了开箱即用的服务发现。然而,混合使用Docker和非Docker应用程序时,我们不能完全依赖Rancher来处理服务发现。我们需要一个独立的工具来跟踪我们所有服务的位置,consul就符合这个要求。

我们不会详细说明如何在您的环境中设置Consul,但是,我们将简要描述我们在ABC公司使用Consul的方式。在每个环境中,我们都有一个部署为容器的Consul集群。我们在环境中的每个主机上都部署一个Consul代理,如果主机正在运行Docker,我们还会部署一个注册器容器。注册器监视每个守护进程的Docker事件API,并在生命周期事件期间自动更新Consul。例如,在新容器被部署后,注册器会自动在Consul中注册该服务。当容器被删除时,注册器撤销它的注册。

Consul服务列表

在Consul中注册所有服务后,我们可以在负载均衡器中运行consul-template,根据Consul中存储的服务数据动态填充上游列表。对于我们的NGINX负载均衡器,我们可以创建一个模板来填充’java-service-1’应用程序的后端:

# upstreams.conf
upstream java-service-1 { 
{{range _, $element := service "java-service-1"}}    
       server {{.Address}}:{{.Port}}; 
{{else}}     
       server 127.0.0.1:65535; # force a 502{{end}} }

此模板在Consul中查找注册为“java-service-1”的服务的列表。然后它将循环该列表,添加具有该特定应用程序实例的IP地址和端口的服务线。如果在Consul中没有注册任何“java-service-1”应用程序,我们默认抛出502以避免NGINX中的错误。

我们可以在守护进程模式下运行consul-template,使其监控Consul的更改,在发生更改时重新渲染模板,然后重新加载NGINX以应用新配置。

TEMPLATE_FILE=/etc/nginx/upstreams.conf.tmpl
RELOAD_CMD=/usr/sbin/nginx -s reload 
consul-template -consul consul.stage.abc.net:8500      
       -template "${TEMPLATE_FILE}:${TEMPLATE_FILE//.tmpl/}:${RELOAD_CMD}"

通过使用我们的负载均衡器设置来动态地改变其余的环境变化,我们可以完全依赖Rancher调度器来做出我们的服务应该在哪里运行的复杂的决定。但是,我们的“java-service-1”应用程序在Docker主机上绑定TCP端口8080,如果在同一主机上调度了多个应用程序容器,则会导致端口绑定冲突并最终失败。为了避免这种情况,我们可以通过调度规则来操作调度器。

通过在docker-compose.yml文件中使用容器标签来提出条件,是Rancher给我们的一种操作调度器的方法。条件可以包括亲和规则、否定、至“软”强制(意味着尽可能地避免)。在我们使用’java-service-1’应用程序的情况下,我们知道在给定时间只有一个容器可以在主机上运行,因此我们可以基于容器名称设置反关联性规则。这将使调度程序查找一个未运行名称为“java-service-1”的容器的Docker主机。我们的docker-compose.yml文件看起来像下面这样:

java-service-1:   
   image: registry.abc.net/java-service-1:${VERSION}   
   container_name: java-service-1   
   ports:    
         - 8080:8080   
    labels:     
         io.rancher.scheduler.affinity:container_label_ne: io.rancher.stack_service.name=java-service-1

注意“标签”键的引入。所有调度规则都作为标签被添加。标签可以被添加到Docker主机和容器。当我们在Rancher注册我们的主机时,我们可以将它们与标签关联,以后就可以切断调度部署。例如,如果我们有一组使用SSD驱动器进行存储优化的Docker主机,我们可以添加主机标签storage=ssd。

Rancher主机标签

需要利用优化存储主机的容器可以添加标签来强制调度程序仅在匹配的主机上部署它们。我们将更新我们的“java-service-1”应用程序,以便只部署在存储优化的主机上:

java-service-1:   
    image: registry.abc.net/java-service-1:${VERSION}   
    container_name: java-service-1   
    ports:    
         - 8080:8080   
    labels:     
         io.rancher.scheduler.affinity:container_label_ne: io.rancher.stack_service.name=java-service-1     
         io.rancher.scheduler.affinity:host_label: storage=ssd

通过使用标签,我们可以根据所需的容量,而不是个别主机运行特定的容器集,来精细地调整我们的应用程序部署。切换到Rancher进行集群调度,即使您仍然有必须在特定主机上运行的应用程序。

最后,我们可以利用Rancher的回滚功能优化我们的服务升级。在我们的部署工作流中,通过调用rancher-compose来指示Rancher在该服务堆栈上执行升级以部署服务。升级过程大致如下:

  1. 通过拉取一个新的镜像来启动升级
  2. 逐一地,现有容器被停止并且新容器被启动
  3. 部署程序登录到UI并选择“完成升级”时,升级完成,
  4. 已停止的旧服务容器被删除

Rancher升级

当给定服务的部署非常少时,此工作流就好了。但是,当某个服务处于“升级”状态(在部署者选择“完成升级”之前)时,在执行“完成升级”或是“回滚”操作之前,你都不能对它进行任何新的升级”。rancher-compose实用程序让我们可以选择以编程方式选择要执行的操作,以部署程序者的身份执行操作。例如,如果您对服务进行自动测试,则可以在rancher-compose升级返回后调用此类测试。根据这些测试的状态,rancher-compose可以被再次调用,这次我们告诉堆栈“完成升级”或“回滚”。我们部署Jenkins作业的一个原始示例可能如下:

# for the full job, see part 3 of this series
/usr/local/bin/rancher-compose --verbose    
    -f ${docker_dir}/docker-compose.yml    
    -r ${docker_dir}/rancher-compose.yml    
    up -d --upgrade 
JAVA_SERVICE_1_URL=http://java-service-1.stage.abc.net:8080/api/v1/status 
if curl -s ${JAVA_SERVICE_1_URL} | grep -q "OK"; then   

# looks good, confirm or "finish" the upgrade   
    /usr/local/bin/rancher-compose --verbose     
         -f ${docker_dir}/docker-compose.yml      
         -r ${docker_dir}/rancher-compose.yml      
         up --confirm-upgrade 
else   

     # looks like there's an error, rollback the containers   
     # to the previously deployed version   
     /usr/local/bin/rancher-compose --verbose      
        -f ${docker_dir}/docker-compose.yml      
        -r ${docker_dir}/rancher-compose.yml      
        up --rollback 
fi

这个逻辑将调用我们的应用程序端点来执行简单的状态检查。如果输出显示的是‘OK’,那么我们完成升级,否则我们需要回滚到以前部署的版本。如果您没有自动测试,另一个选择是简单地总是完成或“确认”升级。

# for the full job, see part 3 of this series
/usr/local/bin/rancher-compose --verbose    
    -f ${docker_dir}/docker-compose.yml    
    -r ${docker_dir}/rancher-compose.yml    
    up -d --upgrade --confirm-upgrade

如果不久以后,您确定需要回滚,就使用相同的部署作业简单地重新部署以前的版本。这确实不像Rancher的升级和回滚功能那么友好,但它通过使堆栈不处于“升级”的状态来解锁将来的升级。

当服务在Rancher中回滚时,容器将被重新部署到以前的版本。当使用通用标记如“latest”或“master”部署服务时,可能会出现意外的后果。例如,让我们假设’java-service-1’应用程序以前被部署了标签’latest’。对图像进行更改,推送到注册表,Docker标签“latest”被更新为指向此新映像我们使用标签“latest”继续升级,在测试后决定应用程序需要回滚。使用Rancher滚动堆栈仍然会重新部署最新的映像,因为标签“latest”尚未被更新为指向上一个映像。回滚可以在纯技术术语中实现,但是部署最近的工作副本的预期效果完全无法实现。在ABC公司,我们通过始终使用与应用程序版本相关的特定标记来避免这种情况。因此,不要使用标记latest”部署我们的“java-service-1”应用程序,我们可以使用版本标签“1.0.1-22-7e56158”。这保证回滚将始终指向我们的应用程序在环境中的最新工作部署。

我们希望我们分享的经验对你们有所帮助。这有助于我们有条不紊地采用Docker,稳步改进我们的流程,并让我们的团队能熟悉这些概念。对更自动化的部署工作流进行增量更改,使组织能够更快地实现自动化的优势,部署团队可以更加务实地决定他们在流水线中需要什么。我们的经历证明Rancher在可行性、自动化、甚至团队协作方面都是成功的。我们希望分享这些我们在Docker应用过程中获得的经验教训将有助于您自己的应用过程。