biglittleant

不被嘲笑的梦想,是不值得去实现的

0%

saltstack简介

SaltStack是一种新型的基础设施管理软件,简单易部署,可伸缩的足以管理成千上万的服务器,和足够快的速度控制,与他们交流,以毫秒为单位。SaltStack提供了一个动态基础设施通信总线用于编排,远程执行、配置管理等等。SaltStack项目于2011年启动,年增长速度较快,五年期固定基础设施编制和配置管理的开源项目。SaltStack社区致力于保持slat项目集中、友好、健康、开放。

简单来说它的两大基础功能就是:配置管理、远程命令执行。剩下就是根据你的需求自由组合,实现更复杂的功能和系统管理

saltstack运行的模式

1:local
2:master/minion
3:salt SSH(可以不依赖客户端)

saltstack三大功能

远程执行
配置管理
云管理(不是特别成熟,不建议生产环境使用)
Salt即可以批量执行命令,也可以单机执行。通常单机执行用于测试:

  1. 单机(立即)执行。 使用salt-call命令单机执行操作
  2. 批量(立即)执行。最常用的操作。使用salt命令,对匹配的minion节点执行操作

saltstack安装实战

salt解析基于DNS,如果做实验发现执行特别慢,可以尝试配置好域名解析.

服务器安装

salt-master可以使用两个源来安装,一个是EPEL源,一个salt的官方源。
推荐使用salt的官方源。

安装epel包

1
2
rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-6.noarch.rpm
rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm

配置saltstack的官方yum源

centos 7:

1
2
3
4
5
6
7
8
9
vim /etc/yum.repo.d/saltstack-repo
####################
# Enable SaltStack's package repository
[saltstack-repo]
name=SaltStack repo for RHEL/CentOS 7
baseurl=https://repo.saltstack.com/yum/rhel7
enabled=1
gpgcheck=0
gpgkey=https://repo.saltstack.com/yum/rhel7/SALTSTACK-GPG-KEY.pub

centos6

1
2
3
4
5
6
7
8
9
vim /etc/yum.repo.d/saltstack-repo
####################
# Enable SaltStack's package repository
[saltstack-repo]
name=SaltStack repo for RHEL/CentOS 6
baseurl=https://repo.saltstack.com/yum/rhel6
enabled=1
gpgcheck=0
gpgkey=https://repo.saltstack.com/yum/rhel6/SALTSTACK-GPG-KEY.pub

服务端安装salt-master 软件并启动

1
2
3
yum install salt-master -y
[root@centos6 ~]# /etc/init.d/salt-master start
Starting salt-master daemon: [ OK ]

配置saltstack服务端如下内容:

配置文件需要顶头写,不能有空格。
vim /etc/salt/master

原内容:

1
2
3
4
5
6
7
8
9
10
11
12
#default_include: master.d/*.conf
#interface: 0.0.0.0
# file_roots:
# base:
# - /srv/salt/
# dev:
# - /srv/salt/dev/services
# - /srv/salt/dev/states
# prod:
# - /srv/salt/prod/services
# - /srv/salt/prod/states

修改后的文件:

1
2
3
4
5
6
7
8
default_include: master.d/*.conf
interface: 0.0.0.0
file_roots:
base:
- /etc/salt/states
prod:
- /etc/salt/states/prod
[root@centos6 ~]# mkdir /etc/salt/states/prod -p

修改完配置文件后,需要重启服务

1
2
3
[root@centos6 ~]# /etc/init.d/salt-master restart
Stopping salt-master daemon: [ OK ]
Starting salt-master daemon: [ OK ]

注意:salt的书写一定要注意格式。基础环境(base),生产环境(prod),测试环境(test)分三个目录。通过不同的目录创建不同的内容。

关于salt-master配置文件更多的内容可以参考salt配置文件详解

补充一个线上的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
interface: 192.168.56.11
ipv6: False
max_open_files: 65535
worker_threads: 5
timeout: 30
state_verbose: False
file_roots:
base:
- /data/salt/base
prod:
- /data/salt/prod
config:
- /data/salt/config
linux:
- /data/salt/linux
log_file: /var/log/salt/master
log_level_logfile: debug
mysql.host: '192.168.56.13'
mysql.user: 'salt'
mysql.pass: '123456'
mysql.db: 'salt'
mysql.port: 3306
external_auth:
pam:
saltapi:
- .*
- '@runner'
- '@jobs'
rest_cherrypy:
port: 8088
ssl_crt: /etc/pki/tls/certs/localhost.crt
ssl_key: /etc/pki/tls/private/localhost_nopass.key

客户端安装方法:

安装salt-minion 软件

1
yum install salt-minion -y

配置并启动saltstack客户端。

1
2
3
4
[root@centos6 ~]# cd /etc/salt/
[root@centos6 salt]# vim minion
master: 192.168.56.11
id: minion.saltstack.com

重启minion进程

1
2
3
4
5
6
[root@centos6 salt]# /etc/init.d/salt-minion start
Starting salt-minion daemon: [ OK ]

[root@centos6 salt]# /etc/init.d/salt-minion restart
Stopping salt-minion daemon: [ OK ]
Starting salt-minion daemon: [ OK ]

ID 存放在这个文件中

1
2
[root@data-1-1 salt]# cat /etc/salt/minion_id
data-1-1

删除旧的key文件

1
[root@centos6 salt]# mv minion_id /tmp/

master的key存放位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# cd /etc/salt/pki/master/
# tree
+-- master.pem
+-- master.pub
+-- minions
| +-- linux-node1 (minion的公钥)
| +-- linux-node2 (minion的公钥)
+-- minions_autosign
+-- minions_denied
+-- minions_pre
+-- minions_rejected

5 directories, 4 files
[root@linux-node1 master]#

master端修改minion端的key名称

1
2
3
4
5
6
7
8
9
10
[root@linux-node1 minions]# mv linux-node1 linux-node3
[root@linux-node1 minions]# ls
linux-node2 linux-node3
[root@linux-node1 minions]# salt-key -L
Accepted Keys:
linux-node2
linux-node3
Denied Keys:
Unaccepted Keys:
Rejected Keys:

salt-key命令补充

  • salt-key -d www.etiantian.org 删除一个key 。
  • salt-key -A 同意所有未注册的minion端。
  • salt-key -a www.etiantian.org 同意一个机器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@centos6 ~]# salt-key
Accepted Keys:
Denied Keys:
Unaccepted Keys:
centos6.6-mupan
minion.saltstack.com
Rejected Keys:
[root@centos6 ~]# salt-key -a minion.saltstack.com ####允许客户端连入服务器。
The following keys are going to be accepted:
Unaccepted Keys:
minion.saltstack.com
Proceed? [n/Y] y
Key for minion minion.saltstack.com accepted.
[root@centos6 ~]#

salt-stakck 状态查询

  • 查看还在运行的程序:salt-run jobs.active
  • 查看以前的操作历史记录来:salt-run jobs.list_jobs
  • 查看minion支持的module列表的命令 salt '*' sys.list_modules
  • 查看CMD模块下的所有function。salt '*' sys.list_functions cmd
  • 查看cmd 模块的详细用法。salt '*' sys.doc cmd

salt远程执行

命令格式:salt 'Trgetiong' module

  • 目标:Trgetiong
  • 模块:Module
  • 返回:Returners

目标匹配方式

通配符使用:

1
2
3
4
5
6
7
salt '*' test.ping
salt '*.example.net' test.ping
salt '*.example.*' test.ping
salt 'web?.example.net' test.ping
salt 'web[1-5]' test.ping
salt 'web[1,3]' test.ping
salt 'web-[x-z]' test.ping

正则表达式使用:

1
salt -E 'web1-(prod|devel)' test.ping

也可以在top.sls中使用正则表示:

1
2
3
4
base:
'web1-(prod|devel)':
- match: pcre
- webserver

lists使用:

1
salt -L 'web1,web2,web3' test.ping

IP地址使用:

1
2
3
[root@centos6 ~]# salt -S '192.168.2.0/24' test.ping
minion.saltstack.com:
True

Grains使用:

1
salt -G 'os:centos' test.ping

也可以在top.sls中使用Grains表示:

1
2
3
4
5
6
base:
'os:centos':
- match: grain
- init.env_init
- zabbix-agent-linux.config
- salt.agent

Pillar使用:

1
salt -I 'master:True' test.ping

salt 异步执行方法

1
2
3
4
5
[root@xxx ~]# salt --async test01 cmd.run "echo 'test'"
Executed command with job ID: 20150915151813222323
[root@xxx ~]# salt-run jobs.lookup_jid 20150915151813222323
Fabric:
test

还有个-v参数在返回结果的同时,一同返回本次任务的jid,如果超时还是会返回jid的。

1
2
3
4
5
[root@test ~]# salt -v test01 test.ping
Executing job with jid 20150917172323566343
-------------------------------------------
test01:
True

如果是调用saltapi来实现异步执行语句和获取jid信息,参数可以这样:

1
2
params = {'client':'local_async', 'fun':'test.echo', 'tgt':'test01', 'arg1':'hello'}
params = {'client':'runner', 'fun':'jobs.lookup_jid', 'jid': '20150827163231404925'}

salt权限控制

salt 使用 pam 模块对普通账号进行权限控制

salt 扩展认证 PAM,可以利用PAM 认证机制对系统账户做出功能操作上的限制。依赖的模块salt.states.external_auth

开启PAM认证

编辑 /etc/salt/master

1
2
3
4
external_auth:
pam:
salt:
- '*'

创建 salt 用户 设置密码

验证

salt PAM 认证用户 只能通过-a pam 参数才能使用

1
salt -a pam test.ping

会提示登陆用户,输入密码,如果正确返回结果,说明pam认证已经生效

实例 :

1
2
3
4
5
6
7
8
9
10
11
12
/etc/salt/master

external_auth:
pam:
admin:
- '*' # 所有模块
salt:
- test.ping # test.ping 模块
test:
- 'dev*' # 主机配置规则
- test.ping # Salt 模块

解释:

  • admin 用户可管理所有主机,使用所有salt功能模块
  • salt 用户可管理所有主机,仅能使用test.ping模块
  • test 用户只能管理部分主机,仅能使用test.ping模块

实例1: 以salt用户为例,认证用户必须打开PAM 参数才能操作

失败操作:

1
2
 salt '*' test.ping
Failed to authenticate, is this user permitted to execute commands?

成功操作:

1
2
3
4
5
 salt -a pam '*' test.ping
username: salt
password:
ubuntu-master:
True

实例2: 以salt用户为例,执行未授权模块被拒绝

1
2
3
4
salt -a pam '*' state.highstate
username: salt
password:
Failed to authenticate, is this user permitted to execute commands?

以test用户为例,操作未授权主机被拒绝
失败操作

1
2
3
4
salt -a pam '*' test
username: test
password:
Failed to authenticate, is this user permitted to execute commands?

成功操作

1
2
3
4
5
salt -a pam 'test-001' test
username: test
password:
test-001:
True

支持文档:

官方帮助文档
官方github
saltstack 中文用户组
Saltstack SLS文件解读
[官方的 salt-states 仓库地址] (https://github.com/saltstack/salt-states)

来自于社区的一些salt states例子:

https://github.com/blast-hardcheese/blast-salt-states
https://github.com/kevingranade/kevingranade-salt-state
https://github.com/uggedal/states
https://github.com/mattmcclean/salt-openstack/tree/master/salt
https://github.com/rentalita/ubuntu-setup/
https://github.com/brutasse/states
https://github.com/bclermont/states
https://github.com/pcrews/salt-data

参考文档
文档: http://docs.saltstack.com/topics/tutorials/starting_states.html
文档: http://docs.saltstack.com/ref/states/writing.html
文档: http://docs.saltstack.com/ref/states/ordering.html
文档: http://www.ituring.com.cn/article/37783
文档: http://docs.saltstack.com/topics/tutorials/starting_states.html

问题汇总:

报错一:

1
2
3
4
5
6
7
8
9
10
----------
ID: limit-conf-config
Function: file.managed
Name: /etc/securilty/limits.conf
Result: False
Comment: Parent directory not present
Started: 17:05:44.606040
Duration: 11.448 ms
Changes:

解决办法:

出现这个的原因是Minion上没有name参数定义的目录. 在minion上定义对应的目录就可以了

报错:salt 管理配置文件时,报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@master files]# salt '*' state.highstate
agent.niu.com:
Data failed to compile:
----------
Rendering SLS init.limit failed, render error: expected '<document start>', but found '<block mapping start>'
in "<unicode string>", line 2, column 1:
limit-conf-config:
^
Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/salt/state.py", line 2509, in render_state
sls, rendered_sls=mods
File "/usr/lib/python2.7/site-packages/salt/template.py", line 79, in compile_template
ret = render(input_data, saltenv, sls, **render_kwargs)
▽ File "/usr/lib/python2.7/site-packages/salt/renderers/yaml.py", line 50, in render
data = load(yaml_data, Loader=get_yaml_loader(argline))
File "/usr/lib64/python2.7/site-packages/yaml/__init__.py", line 71, in load
return loader.get_single_data()
File "/usr/lib64/python2.7/site-packages/yaml/constructor.py", line 37, in get_single_data
node = self.get_single_node()
File "/usr/lib64/python2.7/site-packages/yaml/composer.py", line 39, in get_single_node
if not self.check_event(StreamEndEvent):
File "/usr/lib64/python2.7/site-packages/yaml/parser.py", line 98, in check_event
self.current_event = self.state()
File "/usr/lib64/python2.7/site-packages/yaml/parser.py", line 174, in parse_document_start
self.peek_token().start_mark)
ParserError: expected '<document start>', but found '<block mapping start>'
in "<unicode string>", line 2, column 1:
limit-conf-config:
^

解决办法:

配置文件的路径配置错误。导致报错。修改配置文件,问题解决。

报错:saltstack 执行后CPU占用率为百分之百。

1
2
[root@10-10-121-200 ~]# salt 'linux-backup-saltminion' test.ping
Salt request timed out. The master is not responding. If this error persists after verifying the master is up, worker_threads may need to be increased.

解决办法:

使用top 查看CPU占用率为百分之百。
降低salt的线程数。

报错:配置完return后,不能向数据库写入数据:

1
2
mysql> select * from salt_returns;
Empty set (0.00 sec)

解决思路:

当遇到问题是可以将log的级别更改为debug模式来调试。

解决办法:

因为是客户端直接返回给数据库,所有客户端需要数据库的连接权限。顾所有数据库都需要添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
vim /etc/salt/minion
mysql.host: '192.168.2.150'
mysql.user: 'salt'
mysql.pass: 'salt'
mysql.db: 'salt'
mysql.port: 3306
[root@centos6 ~]# /etc/init.d/salt-minion restart
Stopping salt-minion daemon: [ OK ]
Starting salt-minion daemon: [ OK ]
[root@centos6 ~]#

报错: 执行state.sls 报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
root@10.10.121.200 files]# salt '*.service' state.sls ssh.config env=prod
chuye.backup1.service:
Data failed to compile:
----------
Requisite declaration file.sshd-files in SLS ssh.config is not formed as a single key dictionary
----------
Requisite declaration file.sshd-files in SLS ssh.config is not formed as a single key dictionary
chuye.logstash1.service:
Data failed to compile:
----------
Requisite declaration file.sshd-files in SLS ssh.config is not formed as a single key dictionary
----------
Requisite declaration file.sshd-files in SLS ssh.config is not formed as a single key dictionary
chuye.logstash2.service:
Data failed to compile:
----------
Requisite declaration file.sshd-files in SLS ssh.config is not formed as a single key dictionary
----------
Requisite declaration file.sshd-files in SLS ssh.config is not formed as a single key dictionary

解决办法:

1
2
3
4
- require:
- file.sshd-files
- watch:
- file.sshd-files

上面的值配置错误

1
2
3
4
- require:
- file: sshd-files
- watch:
- file: sshd-files

报错:在文件中调用grains报错

1
2
3
4
5
6
7
8
9
----------
ID: zabbix-agent-install
Function: file.managed
Name: /etc/zabbix_agentd.conf.bak
Result: False
Comment: Unable to manage file: Jinja variable 'item' is undefined
Started: 16:12:39.454923
Duration: 17.407 ms
Changes:

解决办法:

1
2
3
Hostname={{ grains[fqdn] }}
没有加引号
Hostname={{ grains['fqdn'] }}

报错:调用jinjia模板的时候多了一对括号

1
2
3
#!/bin/sh
-IPADDR=['10.10.86.159']
+IPADDR=chuye.logstash2.service

解决办法:

1
2
3
4
5
Solution:

Address {{ grains['fqdn_ip4'][0] }}
Result:
Address 111.111.111.111

安装方法

1
2
3
4
yum install python-pip -y
pip install docker-compose
# docker-compose -v
docker-compose version 1.8.1, build 878cff1

命令简介

Compose区分Version 1和Version 2(Compose 1.6.0+,Docker Engine 1.10.0+)。Version 2支持更多的指令。Version 1没有声明版本默认是”version 1”。Version 1将来会被弃用。
版本1指的是忽略version关键字的版本;版本2必须在行首添加version: ‘2’。

docker-compose 命令行工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# docker-compose
Define and run multi-container applications with Docker.

Usage:
docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
docker-compose -h|--help

Options:
-f, --file FILE Specify an alternate compose file (default: docker-compose.yml)
-p, --project-name NAME Specify an alternate project name (default: directory name)
--verbose Show more output
-v, --version Print version and exit
-H, --host HOST Daemon socket to connect to

--tls Use TLS; implied by --tlsverify
--tlscacert CA_PATH Trust certs signed only by this CA
--tlscert CLIENT_CERT_PATH Path to TLS certificate file
--tlskey TLS_KEY_PATH Path to TLS key file
--tlsverify Use TLS and verify the remote
--skip-hostname-check Don't check the daemon's hostname against the name specified
in the client certificate (for example if your docker host
is an IP address)

Commands:
build Build or rebuild services
bundle Generate a Docker bundle from the Compose file
config Validate and view the compose file
create Create services
down Stop and remove containers, networks, images, and volumes
events Receive real time events from containers
exec Execute a command in a running container
help Get help on a command
kill Kill containers
logs View output from containers
pause Pause services
port Print the public port for a port binding
ps List containers
pull Pulls service images
push Push service images
restart Restart services
rm Remove stopped containers
run Run a one-off command
scale Set number of containers for a service
start Start services
stop Stop services
unpause Unpause services
up Create and start containers
version Show the Docker-Compose version information
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
### 启动容器(默认是前台启动,并打印日志)默认情况,如果该服务的容器已经存在, docker-compose up 将会停止并尝试重新创建他们(保持使用 volumes-from 挂载的卷),以保证 docker-compose.yml 的修改生效。如果你不想容器被停止并重新创建,可以使用 docker-compose up --no-recreate。如果需要的话,这样将会启动已经停止的容器。
docker-compose up
Creating kafka_zk_1
Creating kafka_kafka_1

### 如果想后台运行:
docker-compose up -d

### 查看状态:
docker-compose ps

### 停止服务:
docker-compose stop
Stopping kafka_kafka_1 ... done
Stopping kafka_zk_1 ... done

###重新启动服务:
docker-compose restart


build 构建或重新构建服务。服务一旦构建后,将会带上一个标记名,例如 web_db。可以随时在项目目录下运行 docker-compose build 来重新构建服务。

help 获得一个命令的帮助。

kill 通过发送 SIGKILL 信号来强制停止服务容器。支持通过参数来指定发送的信号,例如 $ docker-compose kill -s SIGINT

logs 查看服务的输出。

port 打印绑定的公共端口。

ps 列出所有容器。

pull 拉取服务镜像。

rm 删除停止的服务容器。

run 在一个服务上执行一个命令。

scale 设置同一个服务运行的容器个数。例如:$ docker-compose scale web=2 worker=3

docker-compose.yml 配置文件解释

每个docker-compose.yml必须定义image或者build中的一个,其它的是可选的。

image

指定镜像tag或者镜像ID。示例:

1
2
3
4
5
image: kafka
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd

注意,在version 1里同时使用image和build是不允许的,version 2则可以,如果同时指定了两者,会将build出来的镜像打上名为image标签。

build

用来指定一个包含Dockerfile文件的路径。一般是当前目录.。Fig将build并生成一个随机命名的镜像。

注意,在version 1里bulid仅支持值为字符串。version 2里支持对象格式。

1
2
3
4
5
6
7
8

build: ./dir

build:
context: ./dir
dockerfile: Dockerfile-alternate
args:
buildno: 1

context \ arg

context为路径,dockerfile为需要替换默认docker-compose的文件名,args为构建(build)过程中的环境变量,用于替换Dockerfile里定义的ARG参数,容器中不可用。示例:

1
2
3
4
5
6
7
8
Dockerfile:

ARG buildno
ARG password

RUN echo "Build number: $buildno"
RUN script-requiring-password.sh "$password"

1
2
3
4
5
6
7
8
9
10
11
12
13
docker-compose.yml:

build:
context: .
args:
buildno: 1
password: secret

build:
context: .
args:
- buildno=1
- password=secret

command

用来覆盖缺省命令。示例:

1
2
3
4
command: bundle exec thin -p 3000
command也支持数组形式:

command: [bundle, exec, thin, -p, 3000]

用于链接另一容器服务,如需要使用到另一容器的mysql服务。可以给出服务名和别名;也可以仅给出服务名,这样别名将和服务名相同。同docker run –link。示例:

1
2
3
4
links:
- db
- db:mysql
- redis

使用了别名将自动会在容器的/etc/hosts文件里创建相应记录:

1
2
3
172.17.2.186  db
172.17.2.186 mysql
172.17.2.187 redis

所以我们在容器里就可以直接使用别名作为服务的主机名。

ports

用于暴露端口。同docker run -p。示例:

1
2
3
4
5
ports:
- "3000"
- "8000:8000"
- "49100:22"
- "127.0.0.1:8001:8001"

expose

expose提供container之间的端口访问,不会暴露给主机使用。同docker run –expose。

1
2
3
expose:
- "3000"
- "8000"

volumes

挂载数据卷。同docker run -v。示例:

1
2
3
4
5
volumes:
- /var/lib/mysql
- cache/:/tmp/cache
- ~/configs:/etc/configs/:ro
volumes_from

挂载数据卷容器,挂载是容器。同docker run –volumes-from。示例:

1
2
3
4
5
6
volumes_from:
- service_name
- service_name:ro
- container:container_name
- container:container_name:rw
container:container_name格式仅支持version 2。

environment

添加环境变量。同docker run -e。可以是数组或者字典格式:

1
2
3
4
5
6
7
environment:
RACK_ENV: development
SESSION_SECRET:

environment:
- RACK_ENV=development
- SESSION_SECRET

depends_on

用于指定服务依赖,一般是mysql、redis等。
指定了依赖,将会优先于服务创建并启动依赖。

links也可以指定依赖。

链接搭配docker-compose.yml文件或者Compose之外定义的服务,通常是提供共享或公共服务。格式与links相似:

1
2
3
4
external_links:
- redis_1
- project_db_1:mysql
- project_db_1:postgresql

注意,external_links链接的服务与当前服务必须是同一个网络环境。

extra_hosts

添加主机名映射。

1
2
3
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"

将会在/etc/hosts创建记录:

1
2
162.242.195.82  somehost
50.31.209.229 otherhost

extends

继承自当前yml文件或者其它文件中定义的服务,可以选择性的覆盖原有配置。

1
2
3
extends:
file: common.yml
service: webapp

service必须有,file可选。service是需要继承的服务,例如web、database。

net

设置网络模式。同docker的–net参数。

1
2
3
4
net: "bridge"
net: "none"
net: "container:[name or id]"
net: "host"

dns

自定义dns服务器。

1
2
3
4
dns: 8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9

其他参数

cpu_shares, cpu_quota, cpuset, domainname, hostname, ipc, mac_address, mem_limit, memswap_limit, privileged, read_only, restart, shm_size, stdin_open, tty, user, working_dir
这些命令都是单个值,含义请参考docker run。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cpu_shares: 73
cpu_quota: 50000
cpuset: 0,1

user: postgresql
working_dir: /code

domainname: foo.com
hostname: foo
ipc: host
mac_address: 02:42:ac:11:65:43

mem_limit: 1000000000
mem_limit: 128M
memswap_limit: 2000000000
privileged: true

restart: always

read_only: true
shm_size: 64M
stdin_open: true
tty: true

例如:

$ docker-compose run ubuntu ping docker.com
将会启动一个 ubuntu 服务,执行 ping docker.com 命令。

默认情况下,所有关联的服务将会自动被启动,除非这些服务已经在运行中。

该命令类似启动容器后运行指定的命令,相关卷、链接等等都将会按照期望创建。

两个不同点:

给定命令将会覆盖原有的自动运行命令;
不会自动创建端口,以避免冲突。
如果不希望自动启动关联的容器,可以使用 –no-deps 选项,例如

$ docker-compose run –no-deps web python manage.py shell
将不会启动 web 容器所关联的其它容器。

实战演示

1、定义Dockerfile,方便迁移到任何地方;
2、编写docker-compose.yml文件;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
zk:
image: kafka:v1
net: host
stdin_open: true
tty: true
expose:
- "2181"
command: bin/zookeeper-server-start.sh config/zookeeper.properties
kafka:
image: kafka:v1
net: host
stdin_open: true
tty: true
ports:
- "9092:9092"
command: bin/kafka-server-start.sh config/server.properties

3、运行docker-compose up启动服务

1
2
3
4
[root@linux kafka]# docker-compose up -d
Creating kafka_zk_1
Creating kafka_kafka_1

4、查看运行的服务

1
2
3
4
5
6
[root@linux kafka]# docker-compose ps
Name Command State Ports
--------------------------------------------------------------
kafka_kafka_1 bin/kafka-server-start.sh ... Up
kafka_zk_1 bin/zookeeper-server-start ... Up
[root@linux kafka]#

停止服务

1
2
docker stop kafka_kafka_1
docker stop kafka_zk_1

删除服务

1
docker-compose rm zk kafka

docker快速入门

docker的主要特性

  • 文件系统隔离:每个进程容器运行在完全独立的根文件系统里。
  • 资源隔离:可以使用cgroup为每个进程容器分配不同的系统资源,例如CPU和内存。
  • 网络隔离:每个进程容器运行在自己的网络命名空间里,拥有自己的虚拟接口和IP地址。
  • 写时复制:采用写时复制方式创建根文件系统,这让部署变得极其快捷,并且节省内存和硬盘空间。
  • 日志记录:Docker将会收集和记录每个进程容器的标准流(stdout/stderr/stdin),用于实时检索或批量检索。
  • 变更管理:容器文件系统的变更可以提交到新的映像中,并可重复使用以创建更多的容器。无需使用模板或手动配置。
  • 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上,例如运行一个一次性交互shell。

安装docker软件及系统环境准备

操作系统环境:

1
2
3
4
5
6
7
[root@localhost ~15:58:47]#cat /etc/redhat-release
CentOS Linux release 7.1.1503 (Core)
[root@localhost ~11:48:09]#uname -r
3.10.0-229.el7.x86_64
[root@localhost ~11:48:24]#uname -m
x86_64
[root@localhost ~11:48:26]#

快速安装docker:

1
2
3
yum install -y docker
systemctl start docker
systemctl enable docker

将docker应用程序迁移到别的位置:

1
2
3
mv /var/lib/docker /data/
ln -s /data/docker/ /var/lib/docker
systemctl restart docker

docker常用命令讲解:

1
2
3
4
5
6
7
8
docker search centos  ##搜索镜像
docker pull centos ##拉取官方的镜像
docker run -it --name test1 centos /bin/bash #启动容器
docker stop ”CONTAINER id“ #停止容器
docker ps -l #查看正在运行的容器
docker ps -a # 查看所有启动过的容器
docker rm "CONTAINER id" #删除容器
docker rmi "image name" #删除镜像

进入docker命令:

方法一:

1
2
docker attach #进入容器
docker exec -it nginx1 bash

上面进入docker容器的命令,退出的时候,docker镜像也停止了。

方法二:

1
2
3
4
5
6
7
8
cat in.sh
#!/bin/sh
CNAME=$1
CPID=$(docker inspect --format "{{.State.Pid}}" $CNAME)
nsenter --target "$CPID" --mount --uts --ipc --net --pid

执行方法:
sh in.sh names(docker容器名)

推荐使用方法二,进入docker容器

docker网络

1
2
3
4
docker run -P #随机映射一个端口
docker -p hostPort:dockerPort #指定端口映射
docker -p ip:hostPort:dockerPort #指定固定IP的固定端口映射
docker ip::dockerPort #指定固定ip映射端口

docker映射文件系统

1
2
3
4
docker run -v /data  #挂载容器的/data目录
docker run -v src:dst ##挂载容器的目录到真实目录中

docker --volumes-from #挂载容器到容器目录中。

注意:如果直接挂载一个文件,很多文件编辑工具,包括 vi 或者 sed --in-place,可能会造成文件 inode 的改变,从 Docker 1.1 .0起,这会导致报错误信息。所以最简单的办法就直接挂载文件的父目录。

docker拷贝文件

1
docker cp 446cd224ed6f:/etc/hosts /etc/backup  ##从docker中拷贝文件到实体服务器

基于文件系统拷贝文件

还有一个方法,算是比较麻烦的。
新版本的docker中这种方式以及废弃了。

1
2
3
4
5
6
7
8
9
10
11
#### 获取容器Id
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4e52a6a5db69 kafka:v1 "bin/zookeeper-server" 2 hours ago Up About an hour kafka_zk_1
446cd224ed6f kafka:v1 "bin/kafka-server-sta" 2 hours ago Exited (1) About an hour ago kafka_kafka_1
#### 获取完整得ID值
[root@linux src]# docker inspect -f '{{.ID}}' 446cd224ed6f
446cd224ed6f5d6fd14a65f80264156c074bd2cf9af34910d7123b7dda1d3926

#### 拷贝文件到容器中
cp /data/scripts/docker_in.sh /var/lib/docker/devicemapper/mnt/446cd224ed6f5d6fd14a65f80264156c074bd2cf9af34910d7123b7dda1d3926/rootfs/root

Docker 的远程访问

我们可以从一台安装了docker的机器访问另一台安装了docker的机器。一般情况下我们使用当前机器的docker客户端访问当前机器的Server端。下面演示如何访问其他docker服务端。

第一台IP:192.168.56.11
第二台IP:192.168.56.12

使用第二台安装有docker的服务器做演示。为区分,设置label不同。

配置HTTP访问

修改守护进程(Server)默认的启动配置:

1
2
3
#默认是:-H fd://
修改为:-H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock -H fd:// --label name=server_1

可设置多个连接方式。

第一台访问第二台机器的docker服务:

通过http连接Server:

1
2
curl http://192.168.12.4:2375/info
## 访问的是服务器192.168.12.4:2375的info接口,返回服务器相关信息。

通过docker客户端访问Server:

1
docker -H tcp://192.168.12.4:2375 info

如果是是第一台机器访问第一台机器Docker的服务端,则使用127.0.0.1:2375就行了。

和服务器端一样,客户端也支持三种连接方式,默认是 -H unix:///var/run/docker.sock:

  • -H unix:///path/to/sock
  • tcp://host:port
  • fd://socketfd

docker客户端使用docker info默认访问的是本地Server。可以修改环境变量DOCKER_HOST改变默认连接。命令行直接输入:

1
2
export DOCKER_HOST="tcp://127.0.0.1:2375"
# 127.0.0.1:237可以替换为实际的Server地址。

如果想恢复本地连接,将DOCKER_HOST置空即可:

1
export DOCKER_HOST=""

编写docker file

1
2
3
4
5
6
7
8
9
10
FROM #指定基础镜像
MAINTAINER #镜像的作者
RUN #在shell或者exec的环境中执行的命令。
ADD #docker镜像中添加文件。
WORKDIR #指定RUN,CMD,ENTERPOINT命令的工作目录。
VOLUME #授权访问物理机的目录
EXPOSE#指定容器运行时的监听端口。
CMD #提供容器默认的执行命令。
ENV #设置环境变量。
USER # 给镜像设置一个UID。

一个dockerfile中只能有一个CMD,如果有多个只有最后一个会执行。

dockerfile最佳实践:

  • 所有的dockerfile 都必须FROM命令开始。
  • 避免dockerfile映射公网的端口。
  • 将经常改动的地方放在dockerfile的下面。

dockerfile实例

1: 创建一个目录来存放dockerfile需要的文件:

1
mkdir /data/dockerfile/nginx -p

2: 编写dockerfile文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
cd /data/dockerfile/nginx/
vim Dockerfile ##首字母必须大小
# this is my first Dockerfile
#Version 1.0
#Author: niuhengbo


#Base images
From centos
#MAINTAINER
MAINTAINER biglittleant


#ADD
ADD nginx-1.10.1.tar.gz /usr/local/src

#RUN
RUN yum install -y wget gcc gcc-c++ make openssl-devel pcre pcre-devel
RUN useradd -s /sbin/nologin -M www

#WORKDIR
WORKDIR /usr/local/src/nginx-1.10.1
RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-http_stub_status_module &&make && make install
RUN echo "daemon off;" >> /usr/local/nginx/conf/nginx.conf

ENV PATH /usr/local/nginx/sbin:$PAHT
EXPOSE 80
CMD ["nginx"]

3: 上传dockerfile需要的软件到目录中.

1
2
3
4
5
[root@linux-node1 nginx10:58:08]#ll
total 892
-rw-r--r-- 1 root root 598 Jun 16 10:34 Dockerfile
-rw-r--r-- 1 root root 909077 Jun 15 17:18 nginx-1.10.1.tar.gz
[root@linux-node1 nginx10:58:10]#

4: 生成docker镜像.

1
2
3
4
5
6
7
8
9
docker build -t nginx-file:v1 /data/dockerfile/nginx/
docker images
[root@linux-node1 nginx10:38:45]#docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
nginx-file v1 a775b585a5f3 6 minutes ago 381 MB
docker.io/centos latest e934940aaa90 4 weeks ago 196.7 MB
docker.io/nginx latest bbb75b846e7b 4 months ago 134.5 MB
[root@linux-node1 nginx10:45:34]#

其他的docker命令

1
2
3
4
5
6
7
## 配置容器主机名
docker run -h HOSTNAME or --hostname=HOSTNAME
#导出镜像
docker save centos > centos-16-02-17.tar
#存入镜像
docker load --input ubuntu_14.04.tar
docker load < ubuntu_14.04.tar

常用docker命令

关闭所有正在运行容器

1
docker ps | awk  '{print $1}' | xargs docker stop

删除所有容器应用

1
docker ps | awk  '{print $1}' | xargs docker stop

或者

1
docker rm $(docker ps -a -q)

docker报错信息

在虚拟机中运行docker是有个警告

1
Usage of loopback devices is strongly discouraged for production use. Either use `--storage-opt dm.thinpooldev` or use `--storage-opt dm.no_warn_on_loop_devices=true` to suppress this warning.

解决办法:

1
2
[root@localhost ~]# vim /etc/sysconfig/docker-storage
DOCKER_STORAGE_OPTIONS="--storage-opt dm.no_warn_on_loop_devices=true"

链接补充:https://www.gitbook.com/book/yeasy/docker_practice/details

报错一 python3.5 使用压缩的时候提示没有zlib模块。

1
2
3
4
5
6
7
  File "1.py", line 6, in <module>
tar = tarfile.open("sample.tar.gz", "w:gz")
File "/usr/local/python3/lib/python3.5/tarfile.py", line 1580, in open
return func(name, filemode, fileobj, **kwargs)
File "/usr/local/python3/lib/python3.5/tarfile.py", line 1624, in gzopen
raise CompressionError("gzip module is not available")
tarfile.CompressionError: gzip module is not available

解决办法

先按照zlib模块,在编译安装

1
2
3
4
5
yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel -y

./configure --prefix=/data/app/python-3.5.2 --enable-shared
make
make install

执行命令继续报错

1
python3: error while loading shared libraries: libpython3.5m.so.1.0: cannot open shared object file: No such file or directory

解决办法

1
2
echo /data/app/python3/lib/ >> /etc/ld.so.conf.d/local.conf
ldconfig

go语言安装过程

1
2
3
4
5
[root@linux-node1 ~]# yum install -y gcc glibc gcc-c++ make git
[root@linux-node1 ~]# cd /usr/local/src
[root@linux-node1 ~]# wget https://storage.googleapis.com/golang/go1.6.1.linux-amd64.tar.gz
[root@linux-node1 ~]# tar zxf go1.6.1.linux-amd64.tar.gz
[root@ linux-node1 src]# mv go /usr/local/

配置环境变量

1
2
3
4
5
6
[root@ linux-node1 ~]# mkdir /usr/local/go/work/
[root@ linux-node1 ~]# vim ~/.bash_profile
export GOROOT=/usr/local/go
export GOPATH=/usr/local/go/work
PATH=$PATH:$HOME/bin:$GOROOT/bin:$GOPATH/bin
[root@linux-node1 ~]# source .bash_profile

检查安装是否正常

1
2
[root@linux-node1 ~]#go version
go version go1.6.1 linux/amd64

VSFTP简介

FTP是仅基于TCP的服务,不支持UDP。与众不同的是FTP使用2个端口,一个数据端口和一个命令端口(也可叫做控制端口)。通常来说这两个端口是21(命令端口)和20(数据端口)。但FTP工作方式的不同,数据端口并不总是20。这就是主动与被动FTP的最大不同之处。

 

vsftpd提供了3种ftp登录形式:

anonymous(匿名帐号)

使用anonymous是应用广泛的一种FTP服务器.如果用户在FTP服务器上没有帐号,那么用户可以以anonymous为用户名,以自己的电子邮件地址为密码进行登录.当匿名用户登录FTP服务器后,其登录目录为匿名FTP服务器的根目录/var/ftp.为了减轻FTP服务器的负载,一般情况下,应关闭匿名帐号的上传功能.

real(真实帐号)

real也称为本地帐号,就是以真实的用户名和密码进行登录,但前提条件是用户在FTP服务器上拥有自己的帐号.用真实帐号登录后,其登录的目录为用户自己的目录,该目录在系统建立帐号时系统就自动创建.

guest(虚拟帐号)

如果用户在FTP服务器上拥有帐号,但此帐号只能用于文件传输服务,那么该帐号就是guest,guest是真实帐号的一种形式,它们的不同之处在于,geust登录FTP服务器后,不能访问除宿主目录以外的内容.

安装vsftpd

1
2
3
4
5
yum install vsftpd
## 启动ftp
systemctl start vsftpd
## 或者
/etc/init.d/vsftpd start

查看服务器端口是否存在

1
ss -lntup |grep 22

配置文件解释

生产配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
egrep -v "#|^$" /etc/vsftpd/vsftpd.conf
anonymous_enable=no ##禁止匿名账号登录
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_file=/var/log/xferlog
xferlog_std_format=YES
chroot_local_user=YES
listen=NO
listen_ipv6=YES
pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES

默认配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#使用户不能离开主目录
chroot_list_enable=YES

#设定不允许匿名访问
anonymous_enable=NO
#设定本地用户可以访问。注:如使用虚拟宿主用户,在该项目设定为NO的情况下所有虚拟用户将无法访问
local_enable=YES

write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
xferlog_std_format=YES
#服务器独立运行
listen=YES

pam_service_name=vsftpd
userlist_enable=YES
tcp_wrappers=YES

配置日志和上传下载

1
2
3
4
5
6
7
8
9
10
#设定支持ASCII模式的上传和下载功能
ascii_upload_enable=YES
ascii_download_enable=YES

#配置vsftpd日志(可选)
xferlog_enable=YES
xferlog_std_format=YES
xferlog_file=/var/log/xfer.log
dual_log_enable=YES
vsftpd_log_file=/var/log/vsftpd.log

如果启用vsftpd日志需手动建立日志文件

1
2
touch /var/log/xfer.log
touch /var/log/vsftpd.log

配置到这里,一个普通的ftp就可以交付使用。
使用系统账号可以登录自己的ftp。

配置匿名用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 设定允许匿名访问
anonymous_enable=YES

# 增加匿名访问目录
anon_root=/var/ftp/pub

# 打开匿名用户上传/删除/重命名权限
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES

# 匿名用户的掩码
anon_umask=022

# 匿名登录时,不检验密码
no_anon_password=YES

# 匿名用户10分钟无操作则掉线
idle_session_timeout=600

默认情况下匿名用户没有创建目录的权限,增加匿名用户的权限

1
chown -R ftp.ftp pub/

配置虚拟用户登录

所谓虚拟用户就是没有使用真实的帐户,只是通过映射到真实帐户和设置权限的目的。虚拟用户不能登录CentOS系统。

创建虚拟用户6步走:

  1. 在vsftpd.cong文件中添加支持配置

  2. 建立虚拟FTP用户的账号数据库文件

  3. 创建FTP根目录及虚拟用户映射的系统用户

  4. 建立支持虚拟用户的PAM认证文件

  5. 在个别虚拟用户建立独立的配置文件以实现权限访问

  6. 重启vsftpd服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
vi /etc/vsftpd/vsftpd.conf
#PAM认证文件名。PAM将根据/etc/pam.d/vsftpd进行认证
pam_service_name=vsftpd
#设定启用虚拟用户功能
guest_enable=YES
#指定虚拟用户的宿主用户,CentOS中已经有内置的ftp用户了
guest_username=ftp
#设定虚拟用户个人vsftp的CentOS FTP服务文件存放路径。存放虚拟用户个性的CentOS FTP服务文件(配置文件名=虚拟用户名)
user_config_dir=/etc/vsftpd/vuser_conf
#配置vsftpd日志(可选)
xferlog_enable=YES
xferlog_std_format=YES
xferlog_file=/var/log/xferlog
dual_log_enable=YES
vsftpd_log_file=/var/log/vsftpd.log

进行认证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#安装Berkeley DB工具

yum install db4 db4-utils -y

#创建用户密码本,注意奇行是用户名,偶行是密码
cat >>/etc/vsftpd/vuser_passwd.txt<<EOF
test
123456
EOF

#生成虚拟用户认证的db文件
db_load -T -t hash -f /etc/vsftpd/vuser_passwd.txt /etc/vsftpd/vuser_passwd.db


# 指定认证文件名称,全部注释掉原来语句,再增加以下两句
vi /etc/pam.d/vsftpd

auth required pam_userdb.so db=/etc/vsftpd/vuser_passwd
account required pam_userdb.so db=/etc/vsftpd/vuser_passwd

#创建虚拟用户配置文件,为虚拟用户配置独立的权限
mkdir /etc/vsftpd/vuser_conf/
#文件名等于vuser_passwd.txt里面的账户名,否则下面设置无效
vi /etc/vsftpd/vuser_conf/test

#虚拟用户根目录,根据实际情况修改
local_root=/data/ftp
write_enable=YES
anon_umask=022
anon_world_readable_only=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES

虚拟用户根目录必须具有FTP账号的权限。
为了使服务器能够使用上述生成的数据库文件,对客户端进行身份验证,需要调用系统的PAM模块。
PAM(Plugable Authentication Module)为可插拔认证模块,不必重新安装应用系统,通过修改指定的配置文件,调整对该程序的认证方式。
PAM模块配置文件路径为/etc/pam.d/目录,此目录下保存着大量与认证有关的配置文件,并以服务名称命名。

配置PASV(被动模式)

vsftpd默认没有开启PASV模式,FTP只能通过PORT模式连接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#开启PASV模式
pasv_enable=YES
#最小端口号
pasv_min_port=40000
#最大端口号
pasv_max_port=40080
pasv_promiscuous=YES

#在防火墙配置内开启40000到40080端口(如果没有防火墙可以忽略)
-A INPUT -m state --state NEW -m tcp -p -dport 40000:40080 -j ACCEPT

#重启iptabls和vsftpd
service iptables restart
service vsftpd restart

现在可以使用PASV模式连接你的FTP服务器了~

设置FTP根目录权限

1
2
3
4
5
6
7
#最新的vsftpd要求对主目录不能有写的权限所以ftp为755,主目录下面的子目录再设置777权限
mkdir /data/ftp
chmod -R 755 /data
chmod -R 777 /data/ftp

#建立限制用户访问目录的空文件
touch /etc/vsftpd/chroot_list

vsftpd的高级玩法

配置SSL连接

我们登录ftp时的密码都是明文登录的,这样极不安全,所以我们可以使用基于ssl的ftp登录传输方式。
首先我们要为ftp签署证书;再在配置文件中添加下列内容

1
2
3
4
5
6
7
8
9
ssl_enable=YES     启用ssl
ssl_tlsv1=YES 启用tls v1版本
ssl_sslv2=YES 启用ssl v2版本
ssl_sslv3=YES
allow_anon_data_ssl=NO 匿名用户一般不需要
force_local_data_ssl=YES本地用户传输时是否使用ssl
force_local_logins_ssl=YES本地用户登录时是否使用ssl
rsa_cert_file=/etc/vsftpd/ssl/vsftpd.crt 证书路径
rsa_private_key_file=/etc/vsftpd/ssl/vsftpd.key私钥路径

注意事项

常见问题

默认情况下,ftp的根目录为/var/ftp,为了安全,这个目录默认不允许设置为777权限,否则ftp将无法访问。但是我们要匿名上传文件,需要“other”用户的写权限,正确的做法:
在/var/ftp中建立一个pub(名子自己起吧)文件夹,将个文件夹权限设置为777(视具体需要自己设),在upload这个文件夹中,匿名用户可以上传文件、创建文件夹、删除文件等。一般至此,便实现vsftpd匿名用户的上传下载了。如果还不行可能selinux的问题,关闭即可。

知识补充

主动连接和被动连接的区别

PORT(主动模式)

PORT中文称为主动模式,工作的原理: FTP客户端连接到FTP服务器的21端口,发送用户名和密码登录,登录成功后要list列表或者读取数据时,客户端随机开放一个端口(1024以上),发送 PORT命令到FTP服务器,告诉服务器客户端采用主动模式并开放端口;FTP服务器收到PORT主动模式命令和端口号后,通过服务器的20端口和客户端开放的端口连接,发送数据。

PASV(被动模式)

PASV是Passive的缩写,中文成为被动模式,工作原理:FTP客户端连接到FTP服务器的21端口,发送用户名和密码登录,登录成功后要list列表或者读取数据时,发送PASV命令到FTP服务器, 服务器在本地随机开放一个端口(1024以上),然后把开放的端口告诉客户端, 客户端再连接到服务器开放的端口进行数据传输。

两种模式的对比

  • 主动模式,服务器端使用20端口连接客户端的随机端口发送数据。
  • 被动模式:服务器端开发随机端口供客户端连接。
  • 主动模式:需要客户端必须开放端口给服务器,很多客户端都是在防火墙内,开放端口给FTP服务器访问比较困难。
  • 被动模式:只需要服务器端开放端口给客户端连接就行了。

我在实际项目中碰到的问题是,FTP的客户端和服务器分别在不同网络,两个网络之间有至少4层的防火墙,服务器端只开放了21端口, 客户端机器没开放任何端口。FTP客户端连接采用的被动模式,结果客户端能登录成功,但是无法LIST列表和读取数据。很明显,是因为服务器端没开放被动模式下的随机端口导致。

遇到的问题

由于被动模式下,服务器端开放的端口随机,但是防火墙要不能全部开放,解决的方案是,在ftp服务器配置被动模式下开放随机端口在 50000-60000之间(范围在ftp服务器软件设置,可以设置任意1024上的端口段),然后在防火墙设置规则,开放服务器端50000-60000之间的端口端。

主动模式下,客户端的FTP软件设置主动模式开放的端口段,在客户端的防火墙开放对应的端口段。

帮助文档

常见错误

报错一:
点击详细错误的时候是下面的提示
vsftpd: refusing to run with writable root inside chroot() 错误的解决办法
一下是解决办法:
为了避免一个安全漏洞,从 vsftpd 2.3.5 开始,chroot 目录必须不可写。使用命令:

1
2
# chmod a-w /home/user
user 为FTP所连接的目录

报错二:
500 OOPS: bad bool value in config file for: tcp_wrappers

tcp_wrappers 多了一个空格,删除空格问题解决。

salt环境介绍

本文的在master中配置的file_roots如下:

1
2
3
4
5
6
7
file_roots:
base:
- /srv/salt/base
prod:
- /srv/salt/prod
test:
- /srv/salt/test

salt目录配置

1
2
3
4
5
6
7
8
9
[root@localhost java]# pwd
/srv/salt/prod/java
[root@localhost prod]# tree java/
java/
├── files
│   └── jdk-8u65-linux-x64.tar.gz
└── java-install.sls

1 directory, 2 files

salt文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@localhost java]# cat java-install.sls
java_install:
file.managed:
- name: /usr/local/src/jdk-8u65-linux-x64.tar.gz
- source: salt://java/files/jdk-8u65-linux-x64.tar.gz
- user: root
- group: root
- mode: 755
cmd.run:
- name: mkdir -p /data/app/ && cd /usr/local/src/ && tar -zxf jdk-8u65-linux-x64.tar.gz && mv jdk1.8.0_65/ /data/app/ && ln -s /data/app/jdk1.8.0_65/ /data/app/java
- unless: test -d /data/app/jdk1.8.0_65/
- require:
- file: java_install

java_init:
file.append:
- name: /etc/profile
- text:
- '###setup java by niu at 2015-12-10'
- export JAVA_HOME=/data/app/java
- export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH

注意文件的缩进和空格。
jinja模板对空格有强依赖。

在所有minion端安装java程序。

1
salt '*' state.sls java.java-install env=prod

登录minion端 查看结果。

1
2
3
4
> java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

注意

  1. salt文件的书写要注意空格的缩进。
  2. 以上内容只在centos6,7系统下测试过。

Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计语言和Java平台的总称。由James Gosling和同事们共同研发,并在1995年正式推出。

Java分为三个体系:

  • JavaSE(J2SE)(Java2 Platform Standard Edition,java平台标准版)
  • JavaEE(J2EE)(Java 2 Platform,Enterprise Edition,java平台企业版)
  • JavaME(J2ME)(Java 2 Platform Micro Edition,java平台微型版)。

系统版本

1
2
3
4
5
6
cat /etc/redhat-release
# CentOS Linux release 7.1.1503 (Core)
uname -r
# 3.10.0-229.el7.x86_64
uname -m
# x86_64

安装过程

上传java到/usr/local/src/目录下。

1
2
3
4
cd /usr/local/src/
tar -zxf jdk-8u65-linux-x64.tar.gz
mv jdk1.8.0_65/ /data/app/
ln -s /data/app/jdk1.8.0_65/ /data/app/java
为java配置环境变量
1
2
3
4
5
vim /etc/profile
export JAVA_HOME=/data/app/java
export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH
export JRE_HOME=/data/app/java/jre
export CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar
使profile文件中配置的环境变量生效
1
source /etc/profile
验证JAVA是否安装成功
1
2
3
4
> java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

它类似于静态语言中的数据类型声明,比如声明一个字段为String, 以后这个变量都只能存储String类型的数据。同样的, 一个number类型的 mapping 字段只能存储number类型的数据。

如何理解elastic中各参数的定义

  • index —-> database
  • type —–> table
  • mapping —-> 表结构

默认情况不需要自定义mapping, 当新的type或者field引入时,Elasticsearch会自动创建并且注册有合理的默认值的mapping, 只有要覆盖默认值时才必须要提供mapping定义。

mapping字段

自定义Mapping

下面是一个简单的Mapping定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl -XPUT 'http://localhost:9200/test-index' -d '
{
"settings": {
"number_of_shards": 3, //设置分片
"number_of_replicas": 1 //设置副本集
},
"mappings": {
"user": { // 对应type名
"_all": { "enabled": false },
"properties": { //指定每个字段的映射类型或属性。
"title": { "type": "string" },
"body": { "type": "string" },
"user_id": {
"type": "string",
"index": "not_analyzed"
},
"created": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
}
}
}

其中login_log是type(相当于关系数据库中的表),在login_log中我们定义了name、age、about、ip、last_time这6个列。

type字段

type字段用来规定字段的数据类型。
Elasticsearch支持以下数据类型:

文本: string
数字: byte, short, integer, long
浮点数: float, double
布尔值: boolean
Date: date

index字段

index 属性控制string如何被索引,它有三个可选值:

  • analyzed:First analyze the string, then index it. In other words, index this field as full text.

  • not_analyzed:Index this field, so it is searchable, but index the value exactly as specified. Do not analyze it.

  • no:Don’t index this field at all. This field will not be
    searchable.

对于string类型的filed index 默认值是: analyzed.对于URL这些不需要分词的字段 ,我们可以将它设置为:not_analyzed。

analyzer 字段

analyzer 字段用来指定改字段用什么分词器去分词。
elastic默认的分词是:standard analyzer 。
elastic其他分词器:whitespace, simple, or english 。
我们也可以自己安装指定的分词器并指定。

1
2
{ "name": { "type": "string", "analyzer": "ik" }
\\ 指定使用ik分词器对name字段进行分词。

增加mapping字段

将写好的mapping字段保存到mapping.json(名字随便起)文件中。

1
POST 'http://192.168.56.12:9200/test-index' --d @mapping.json

更新mapping字段

将新的mapping字段保存到mapping-1.json(名字随便起)文件中。

1
POST 'http://192.168.56.12:9200/test-index' --d @mapping-1.json

查看mapping字段

1
2
3
4
5
6
7
curl -XGET 'http://192.168.56.12:9200/test-index/user/_mapping?pretty'
##字段解释
- XGET method类型
- http://192.168.56.12:9200 elastic的搜索端口
- test-index 索引名称。
- user 索引的type类型。
- _mapping?pretty pretty以人类可读的形式显示。

mapping补充

可以修改的项:

  • 增加新的类型定义
  • 增加新的字段
  • 增加新的分析器

不允许修改的项:

  • 更改字段类型(比如文本改为数字)
  • 更改存储为不存储,反之亦然
  • 更改索引属性的值
  • 更改已索引文档的分析器

线上使用mapping字段展示

这是一个logstash的mapping字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
{
"logstash": {
"order": 0,
"template": "logstash-*-*",
"settings": { //配置索引的参数
"index.refresh_interval": "30s",
"index.translog.flush_threshold_ops": "50000"
},
"mappings": { // 必须字段
"_default_": { //定义默认参数
"dynamic_templates": [ // 动态字段定义
{
"message_field": {
"mapping": {
"index": "analyzed",
"omit_norms": true,
"type": "string",
"fields": {
"raw": {
"ignore_above": 256, //只保留256个字符
"index": "not_analyzed", //不分词
"type": "string", // 字段类型string
"doc_values": true
}
}
},
"match_mapping_type": "string", //匹配哪些字段
"match": "message" // 匹配哪些名称
}
},
{
"string_fields": {
"mapping": {
"index": "analyzed",
"omit_norms": true,
"type": "string",
"fields": {
"raw": {
"ignore_above": 256,
"index": "not_analyzed",
"type": "string",
"doc_values": true
}
}
},
"match_mapping_type": "string",
"match": "*"
}
}
],
"_all": {
"enabled": false
},
"properties": {
"geoip": {
"path": "full",
"dynamic": true,
"type": "object",
"properties": {
"location": {
"type": "geo_point"
}
}
},
"@timestamp": {
"format": "dateOptionalTime",
"index": "not_analyzed",
"type": "date",
"doc_values": true
},
"@version": {
"index": "not_analyzed",
"type": "string"
}
}
}
},
"aliases": {}
}
}

动态字段

格式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PUT my_index
{
"mappings": {
"my_type": {
"dynamic_templates": [ // 定义这是动态字段
{
"integers": { //名称
"match_mapping_type": "long", //匹配规则
"mapping": {
"type": "integer"}}},
{
"strings": { //名称
"match_mapping_type": "string", //匹配规则
"mapping": {
"type": "string",
"fields": { //过滤出一个子字段
"raw": { //名字叫raw,线上会有两个字段name,name.raw ,raw 可以更好成别的字符串。
"type": "string",
"index": "not_analyzed",
"ignore_above": 256
}}}}}]}}}

match、unmatch和match_mapping_type

  • 符合 match 的规则会被匹配。
  • 符合 unmatch 的规则会被忽略。
  • match_mapping_type 匹配类型。
1
2
3
"match_mapping_type": "string",
"match": "long_*",
"unmatch": "*_text",

match_pattern

  • match_pathern 正则匹配。
1
2
"match_pattern": "regex",
"match": "^profit_\d+$"

path_match and path_unmatch

  • path_match 路径匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
PUT my_index
{
"mappings": {
"my_type": {
"dynamic_templates": [
{
"full_name": {
"path_match": "name.*",
"path_unmatch": "*.middle",
"mapping": {
"type": "string",
"copy_to": "full_name"
}}}]}}}

{name} and {dynamic_type}

  • 可以使用的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUT my_index
{
"mappings": {
"my_type": {
"dynamic_templates": [
{
"named_analyzers": {
"match_mapping_type": "string",
"match": "*",
"mapping": {
"type": "string",
"analyzer": "{name}"
}
}
},
{
"no_doc_values": {
"match_mapping_type":"*",
"mapping": {
"type": "{dynamic_type}",
"doc_values": false
}}}]}}}

  • elasticsearch 数据导入导出工具
  • 支持基于HTTP导出、导入。
  • 支持保存到文件。

安装

1
npm install elasticdump -g

简单使用

拷贝分词器信息

1
2
3
4
5
elasticdump \
--input=http://192.168.56.13:9200/thread \
--output=http://192.168.56.12:9200/thread1 \
--type=analyzer

拷贝mapping信息

1
2
3
4
elasticdump \
--input=http://192.168.56.13:9200/thread \
--output=http://192.168.56.12:9200/thread1 \
--type=mapping

拷贝数据

1
2
3
4
5
elasticdump \
--input=http://192.168.56.13:9200/thread \
--output=http://192.168.56.12:9200/thread1 \
--type=data \
--limit=1000

limit 设置每次拷贝多少内容,默认是100。
拷贝数据的同时mapping也会被拷贝。

将数据保存到本地

1
2
3
4
5
6
7
8
9
10
elasticdump \
--input=http://192.168.56.13:9200/thread \
--output=thread.json \
--type=mapping

elasticdump \
--input=http://192.168.56.13:9200/thread \
--output=thread-data.json \
--type=data

保存到压缩文件中

1
2
3
4
5
6
# Backup and index to a gzip using stdout:
elasticdump \
--input=http://production.es.com:9200/my_index \
--output=$ \
| gzip > /data/my_index.json.gz

基于搜索保存文件内容

1
2
3
4
elasticdump \
--input=http://production.es.com:9200/my_index \
--output=query.json \
--searchBody '{"query":{"term":{"username": "admin"}}}'

基于全局搜索的结果保存

1
2
3
4
5
6
elasticdump \
--input=http://es.com:9200/api/search \
--input-index=my_index/my_type \
--output=http://es.com:9200/api/search \
--output-index=my_index \
--type=mapping

帮助文档

elastic-dump-github