Ansible的源码安装

1.获取Ansibl的源码码

git clone git://github.com/ansible/ansible.git –recursive

2.安装Ansible

cd ./ansible

source ./hacking/env-setup

3.创建ansible的配置目录/etc/ansible,拷贝配置文件到该目录。

mkdir -p /etc/ansible

cp ./example/(ansible.cfg,hosts) /etc/ansible

4.在/etc/ansible/hosts文件中添加主机,可以参照文件中的方式

ansible all -m ping

ansible all -m shell -a ‘uptime’

PS:这种安装方法在当前bash下生效,当退出当前bash就失效。

ansible-tower装以及破解

一、环境准备:
CentOS Linux release 7.5.1804 (Core)
主机地址:9.110.187.203
ansible版本:2.6.4
ansible-tower版本:3.2.6

[root@localhost ~]#yum -y install ansible

[root@localhost ~]# ansible –version

ansible 2.6.4

二、安装ansible-tower:

[root@localhost ~]#wget https://releases.ansible.com/ansible-tower/setup-bundle/ansible-tower-setup-bundle-3.2.6-1.el7.tar.gz

[root@localhost ~]#tar -zxvf ansible-tower-setup-bundle-3.2.6-1.el7.tar.gz

[root@localhost ~]#cd ansible-tower-setup-bundle-3.2.6-1.el7

修改配置文件inventory将里面所有的密码都修改为自己的密码,如下三处的标红地方:

[root@localhost ansible-tower-setup-bundle-3.2.6-1.el7]# pwd

/root/ansible-tower-setup-bundle-3.2.6-1.el7

image.png

[root@localhost ansible-tower-setup-bundle-3.2.6-1.el7]# ./setup.sh

如果网络没有问题的话耐心等待安装完成即可.

安装完成没报错的话即可访问web页面:https://9.110.187.203/#/,默认初始页面如下:

默认用户为admin,密码为inventory文件admin_password字段配置的密码,我这里配置的密码也为admin.

导入license,没有的话,点击REQUEST LICENSE,去官方(https://www.ansible.com/license)申请免费试用,填写个人信息后(邮箱要填写正确,其他信息可随便填写)会把license发到填写的邮箱.

提交license并登录成功后默认初始页面如下:

[root@localhost ansible-tower-setup-bundle-3.2.6-1.el7]# cd /var/lib/awx/venv/awx/lib/python2.7/site-packages/tower_license

vi __init__.py将119行和120行修改为如下内容,特别需要注意格式,如下:

即:_check_cloudforms_subscription,添加return True

修改完重新编译一下:

[root@localhost  tower_license]# python -m py_compile __init__.py

[root@localhost  tower_license]# python -O -m py_compile __init__.py

重启服务:

[root@localhost  tower_license]# ansible-tower-service restart

重新打开settings–VIEW YOUR LICENSE,发现”Hosts Available”变成了9999999台,说明破解成功

nginx的日志配置

nginx有一个非常灵活的日志记录模式。每个级别的配置可以有各自独立的访问日志。日志格式通过log_format命令来定义。 ngx_http_log_module 是用来定义请求日志格式的。

nginx日志相关的配置有log_format、access_log、open_log_file_cache、log_not_found、log_subrequest、rewrite_log、error_log。这些配置主要分2类:日志记录格式,日志类型及位置。

  • access_log指令

语法: access_log path [format [buffer=size [flush=time]]];

access_log path format gzip[=level] [buffer=size] [flush=time];
access_log syslog:server=address[,parameter=value] [format];
access_log off;

默认值: access_log logs/access.log combined;
配置段: http, server, location, if in location, limit_except

gzip压缩等级。
buffer设置内存缓存区大小。
flush保存在缓存区中的最长时间。
不记录日志:access_log off;
使用默认combined格式记录日志:access_log logs/access.log 或 access_log logs/access.log combined;

  • log_format指令

语法: log_format name string …;
默认值: log_format combined “…”;
配置段: http

name表示格式名称,string表示等义的格式。log_format有一个默认的无需设置的combined日志格式,相当于apache的combined日志格式,如下所示:

log_format  combined  ‘$remote_addr – $remote_user  [$time_local]  ‘
‘ “$request”  $status  $body_bytes_sent  ‘
‘ “$http_referer”  “$http_user_agent” ‘;

如果nginx位于负载均衡器,squid,nginx反向代理之后,web服务器无法直接获取到客户端真实的IP地址了。 $remote_addr获取反向代理的IP地址。反向代理服务器在转发请求的http头信息中,可以增加X-Forwarded-For信息,用来记录 客户端IP地址和客户端请求的服务器地址。如下所示

log_format  porxy  ‘$http_x_forwarded_for – $remote_user  [$time_local]  ‘
‘ “$request”  $status $body_bytes_sent ‘
‘ “$http_referer”  “$http_user_agent” ‘;

日志格式允许包含的变量注释如下:

$remote_addr, $http_x_forwarded_for 记录客户端IP地址
$remote_user 记录客户端用户名称
$request 记录请求的URL和HTTP协议
$status 记录请求状态
$body_bytes_sent 发送给客户端的字节数,不包括响应头的大小; 该变量与Apache模块mod_log_config里的“%B”参数兼容。
$bytes_sent 发送给客户端的总字节数。
$connection 连接的序列号。
$connection_requests 当前通过一个连接获得的请求数量。
$msec 日志写入时间。单位为秒,精度是毫秒。
$pipe 如果请求是通过HTTP流水线(pipelined)发送,pipe值为“p”,否则为“.”。
$http_referer 记录从哪个页面链接访问过来的
$http_user_agent 记录客户端浏览器相关信息
$request_length 请求的长度(包括请求行,请求头和请求正文)。
$request_time 请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。
$time_iso8601 ISO8601标准格式下的本地时间。
$time_local 通用日志格式下的本地时间。

发送给客户端的响应头拥有“sent_http_”前缀。 比如$sent_http_content_range。

实例如下:

http {
log_format  main  ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘”$status” $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for” ‘
‘”$gzip_ratio” $request_time $bytes_sent $request_length’; log_format srcache_log ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘”$status” $body_bytes_sent $request_time $bytes_sent $request_length ‘
‘[$upstream_response_time] [$srcache_fetch_status] [$srcache_store_status] [$srcache_expire]’;

open_log_file_cache max=1000 inactive=60s;

server {
server_name ~^(www\.)?(.+)$;
access_log logs/$2-access.log main;
error_log logs/$2-error.log;

location /srcache {
access_log logs/access-srcache.log srcache_log;
}
}
}

  • open_log_file_cache指令

语法: open_log_file_cache max=N [inactive=time] [min_uses=N] [valid=time];
open_log_file_cache off;
默认值: open_log_file_cache off;
配置段: http, server, location

对于每一条日志记录,都将是先打开文件,再写入日志,然后关闭。可以使用open_log_file_cache来设置日志文件缓存(默认是off),格式如下:

参数注释如下:
max:设置缓存中的最大文件描述符数量,如果缓存被占满,采用LRU算法将描述符关闭。
inactive:设置存活时间,默认是10s
min_uses:设置在inactive时间段内,日志文件最少使用多少次后,该日志文件描述符记入缓存中,默认是1次
valid:设置检查频率,默认60s
off:禁用缓存
实例如下:

open_log_file_cache max=1000 inactive=20s valid=1m min_uses=2;

  • log_not_found指令

语法: log_not_found on | off;
默认值: log_not_found on;
配置段: http, server, location
是否在error_log中记录不存在的错误。默认是。

  • log_subrequest指令

语法: log_subrequest on | off;
默认值: log_subrequest off;
配置段: http, server, location
是否在access_log中记录子请求的访问日志。默认不记录。

  • rewrite_log指令

由ngx_http_rewrite_module模块提供的。用来记录重写日志的。对于调试重写规则建议开启。 Nginx重写规则指南
语法: rewrite_log on | off;
默认值: rewrite_log off;
配置段: http, server, location, if
启用时将在error log中记录notice级别的重写日志。

  • error_log指令

语法: error_log file | stderr | syslog:server=address[,parameter=value] [debug | info | notice | warn | error | crit | alert | emerg];
默认值: error_log logs/error.log error;
配置段: main, http, server, location
配置错误日志。

基于Redis的分布式锁

应用场景

当多个机器(多个进程)会对同一条数据进行修改时,并且要求这个修改是原子性的。这里有两个限定:(1)多个进程之间的竞争,意味着JDK自带的锁失效;(2)原子性修改,意味着数据是有状态的,修改前后有依赖。

实现方式

  • 基于Redis实现,主要基于redis的setnx(set if not exist)命令;
  • 基于Zookeeper实现;
  • 基于version字段实现,乐观锁,两个线程可以同时读取到原有的version值,但是最终只有一个可以完成操作;

这三种方式中,我接触过第一和第三种。基于redis的分布式锁功能更加强大,可以实现阻塞和非阻塞锁。

基于Redis的实践

锁的实现

  • 锁的key为目标数据的唯一键,value为锁的期望超时时间点;
  • 首先进行一次setnx命令,尝试获取锁,如果获取成功,则设置锁的最终超时时间(以防在当前进程获取锁后奔溃导致锁无法释放);如果获取锁失败,则检查当前的锁是否超时,如果发现没有超时,则获取锁失败;如果发现锁已经超时(即锁的超时时间小于等于当前时间),则再次尝试获取锁,取到后判断下当前的超时时间和之前的超时时间是否相等,如果相等则说明当前的客户端是排队等待的线程里的第一个尝试获取锁的,让它获取成功即可。
    基于redis实现分布式锁逻辑.png
public class RedisDistributionLock {

    private static final Logger logger = LoggerFactory.getLogger(RedisDistributionLock.class);

    //key的TTL,一天
    private static final int finalDefaultTTLwithKey = 24 * 3600;

    //锁默认超时时间,20秒
    private static final long defaultExpireTime = 20 * 1000;

    private static final boolean Success = true;
    
    @Resource( name = "redisTemplate")
    private RedisTemplate<String, String> redisTemplateForGeneralize;

    /**
     * 加锁,锁默认超时时间20秒
     * @param resource
     * @return
     */
    public boolean lock(String resource) {
        return this.lock(resource, defaultExpireTime);
    }

    /**
     * 加锁,同时设置锁超时时间
     * @param key 分布式锁的key
     * @param expireTime 单位是ms
     * @return
     */
    public boolean lock(String key, long expireTime) {

        logger.debug("redis lock debug, start. key:[{}], expireTime:[{}]",key,expireTime);
        long now = Instant.now().toEpochMilli();
        long lockExpireTime = now + expireTime;

        //setnx
        boolean executeResult = redisTemplateForGeneralize.opsForValue().setIfAbsent(key,String.valueOf(lockExpireTime));
        logger.debug("redis lock debug, setnx. key:[{}], expireTime:[{}], executeResult:[{}]", key, expireTime,executeResult);

        //取锁成功,为key设置expire
        if (executeResult == Success) {
            redisTemplateForGeneralize.expire(key,finalDefaultTTLwithKey, TimeUnit.SECONDS);
            return true;
        }
        //没有取到锁,继续流程
        else{
            Object valueFromRedis = this.getKeyWithRetry(key, 3);
            // 避免获取锁失败,同时对方释放锁后,造成NPE
            if (valueFromRedis != null) {
                //已存在的锁超时时间
                long oldExpireTime = Long.parseLong((String)valueFromRedis);
                logger.debug("redis lock debug, key already seted. key:[{}], oldExpireTime:[{}]",key,oldExpireTime);
                //锁过期时间小于当前时间,锁已经超时,重新取锁
                if (oldExpireTime <= now) {
                    logger.debug("redis lock debug, lock time expired. key:[{}], oldExpireTime:[{}], now:[{}]", key, oldExpireTime, now);
                    String valueFromRedis2 = redisTemplateForGeneralize.opsForValue().getAndSet(key, String.valueOf(lockExpireTime));
                    long currentExpireTime = Long.parseLong(valueFromRedis2);
                    //判断currentExpireTime与oldExpireTime是否相等
                    if(currentExpireTime == oldExpireTime){
                        //相等,则取锁成功
                        logger.debug("redis lock debug, getSet. key:[{}], currentExpireTime:[{}], oldExpireTime:[{}], lockExpireTime:[{}]", key, currentExpireTime, oldExpireTime, lockExpireTime);
                        redisTemplateForGeneralize.expire(key, finalDefaultTTLwithKey, TimeUnit.SECONDS);
                        return true;
                    }else{
                        //不相等,取锁失败
                        return false;
                    }
                }
            }
            else {
                logger.warn("redis lock,lock have been release. key:[{}]", key);
                return false;
            }
        }
        return false;
    }

    private Object getKeyWithRetry(String key, int retryTimes) {
        int failTime = 0;
        while (failTime < retryTimes) {
            try {
                return redisTemplateForGeneralize.opsForValue().get(key);
            } catch (Exception e) {
                failTime++;
                if (failTime >= retryTimes) {
                    throw e;
                }
            }
        }
        return null;
    }

    /**
     * 解锁
     * @param key
     * @return
     */
    public boolean unlock(String key) {
        logger.debug("redis unlock debug, start. resource:[{}].",key);
        redisTemplateForGeneralize.delete(key);
        return Success;
    }
}

自定义注解使用分布式锁

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RedisLockAnnoation {

    String keyPrefix() default "";

    /**
     * 要锁定的key中包含的属性
     */
    String[] keys() default {};

    /**
     * 是否阻塞锁;
     * 1. true:获取不到锁,阻塞一定时间;
     * 2. false:获取不到锁,立即返回
     */
    boolean isSpin() default true;

    /**
     * 超时时间
     */
    int expireTime() default 10000;

    /**
     * 等待时间
     */
    int waitTime() default 50;

    /**
     * 获取不到锁的等待时间
     */
    int retryTimes() default 20;
}

实现分布式锁的逻辑

@Component
@Aspect
public class RedisLockAdvice {

    private static final Logger logger = LoggerFactory.getLogger(RedisLockAdvice.class);

    @Resource
    private RedisDistributionLock redisDistributionLock;

    @Around("@annotation(RedisLockAnnoation)")
    public Object processAround(ProceedingJoinPoint pjp) throws Throwable {
        //获取方法上的注解对象
        String methodName = pjp.getSignature().getName();
        Class<?> classTarget = pjp.getTarget().getClass();
        Class<?>[] par = ((MethodSignature) pjp.getSignature()).getParameterTypes();
        Method objMethod = classTarget.getMethod(methodName, par);
        RedisLockAnnoation redisLockAnnoation = objMethod.getDeclaredAnnotation(RedisLockAnnoation.class);

        //拼装分布式锁的key
        String[] keys = redisLockAnnoation.keys();
        Object[] args = pjp.getArgs();
        Object arg = args[0];
        StringBuilder temp = new StringBuilder();
        temp.append(redisLockAnnoation.keyPrefix());
        for (String key : keys) {
            String getMethod = "get" + StringUtils.capitalize(key);
            temp.append(MethodUtils.invokeExactMethod(arg, getMethod)).append("_");
        }
        String redisKey = StringUtils.removeEnd(temp.toString(), "_");

        //执行分布式锁的逻辑
        if (redisLockAnnoation.isSpin()) {
            //阻塞锁
            int lockRetryTime = 0;
            try {
                while (!redisDistributionLock.lock(redisKey, redisLockAnnoation.expireTime())) {
                    if (lockRetryTime++ > redisLockAnnoation.retryTimes()) {
                        logger.error("lock exception. key:{}, lockRetryTime:{}", redisKey, lockRetryTime);
                        throw ExceptionUtil.geneException(CommonExceptionEnum.SYSTEM_ERROR);
                    }
                    ThreadUtil.holdXms(redisLockAnnoation.waitTime());
                }
                return pjp.proceed();
            } finally {
                redisDistributionLock.unlock(redisKey);
            }
        } else {
            //非阻塞锁
            try {
                if (!redisDistributionLock.lock(redisKey)) {
                    logger.error("lock exception. key:{}", redisKey);
                    throw ExceptionUtil.geneException(CommonExceptionEnum.SYSTEM_ERROR);
                }
                return pjp.proceed();
            } finally {
                redisDistributionLock.unlock(redisKey);
            }
        }
    }
}

kubernetes(k8s)第七部分之yaml文件详解

# yaml格式的pod定义文件完整内容:
apiVersion: v1       #必选,版本号,例如v1
kind: Pod       #必选,Pod
metadata:       #必选,元数据
name: string       #必选,Pod名称
namespace: string    #必选,Pod所属的命名空间
labels:      #自定义标签
– name: string     #自定义标签名字
annotations:       #自定义注释列表
– name: string
spec:         #必选,Pod中容器的详细定义
containers:      #必选,Pod中容器列表
– name: string     #必选,容器名称
image: string    #必选,容器的镜像名称
imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
command: [string]    #容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string]     #容器的启动命令参数列表
workingDir: string     #容器的工作目录
volumeMounts:    #挂载到容器内部的存储卷配置
– name: string     #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string    #存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean    #是否为只读模式
ports:       #需要暴露的端口库号列表
– name: string     #端口号名称
containerPort: int   #容器需要监听的端口号
hostPort: int    #容器所在主机需要监听的端口号,默认与Container相同
protocol: string     #端口协议,支持TCP和UDP,默认TCP
env:       #容器运行前需设置的环境变量列表
– name: string     #环境变量名称
value: string    #环境变量的值
resources:       #资源限制和请求的设置
limits:      #资源限制的设置
cpu: string    #Cpu的限制,单位为core数,将用于docker run –cpu-shares参数
memory: string     #内存限制,单位可以为Mib/Gib,将用于docker run –memory参数
requests:      #资源请求的设置
cpu: string    #Cpu请求,容器启动的初始可用数量
memory: string     #内存清楚,容器启动的初始可用数量
livenessProbe:     #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
exec:      #对Pod容器内检查方式设置为exec方式
command: [string]  #exec方式需要制定的命令或脚本
httpGet:       #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
– name: string
value: string
tcpSocket:     #对Pod内个容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0  #容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 0   #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 0    #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged:false
restartPolicy: [Always | Never | OnFailure]#Pod的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
nodeSelector: obeject  #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
imagePullSecrets:    #Pull镜像时使用的secret名称,以key:secretkey格式指定
– name: string
hostNetwork:false      #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes:       #在该pod上定义共享存储卷列表
– name: string     #共享存储卷名称 (volumes类型有很多种)
emptyDir: {}     #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
hostPath: string     #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: string     #Pod所在宿主机的目录,将被用于同期中mount的目录
secret:      #类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
scretname: string
items:
– key: string
path: string
configMap:     #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
– key: string
path: string