使用awk获取每行的最大值

需求

  • 有一个数字文本,每行都是数字,以空格分开;现在需要将每行中最大值取出来

文本如下:

[root@localhost ~]#cat urfile
1   1     2
1   2     1
1   3     1
  • 使用awk解决
[root@localhost ~]#awk 'BEGIN{n=1;max=0;nu=0}{for(n=1;n=max)max=a[$n]}{print max}{max=0}}' urfile
2
2
3

解释说明

使用数组将每个数字记录,然后将每个值与max变量比较,若比max大,则将内容赋予max;使用nu记录行号,每行执行最后将max置0

awk用法与示例介绍

awk:模式扫描和数据处理语言

描述: awk是一种编程语言,用于Linux/unix下对文本和数据进行扫描与处理,数据可以来自标准输入、文件、管道。工作流程是:逐行扫描文件,寻找特定匹配模式的行,并进行相应的处理动作。awk读取文件文件内容每一行时,将对比该行是否与给定的模式相匹配,如果匹配,则执行相应处理动作,否则不对该行进行处理。如果没有指定的处理脚本,则把匹配的行显示到标准输出(默认print动作),如果没有指定模式匹配,则默认匹配所有数据

语法:

awk  [ POSIX or GNU style options ] -f program-file [ --  ] file ...
awk [ POSIX or GNU style options ] [ --  ]  program-text file ...

选项:

  • -F fs 使用fs作为输入行的分隔符(默认是空格或者制表符)

  • -v var=val 在处理过程之前,给var设置一个变量val

  • -f program-file 从文件中读取awk的处理内容

内置变量:

  • ARGC 命令行参数个数

  • ARGV 命令行参数的一个排列,索引从0到ARGC-1

  • ARGIND ARGV最近处理文件的索引

  • FILENAME 当前输入文档的名称

  • FNR 当前输入文档的记录编号

  • NR 输入流的当前记录编号(行号)

  • FS 字段分隔符

  • NF 当前记录的字段个数

  • OFS 输出字段分隔符,默认为空格

  • RS 输出记录分隔符默认是换行符n

  • ORS 输出记录分隔符,默认是换行符n

AWK patterns may be one of the following:

BEGIN
END
/regular expression/
relational expression
pattern && pattern
pattern || pattern
pattern ? pattern : pattern
(pattern)
! pattern
pattern1, pattern2

例子:

1、-F 指定分隔符

[root@python ~]# cat test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol  
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject
[root@python ~]# awk '{print $2}' test.txt
48049/tcp
48128/udp
48129/tcp
48129/udp
48556/udp
48619/tcp
[root@python ~]# awk -F'/' '{print $2}'             test.txt
tcp               # 3GPP Cell Broadcast         Service Protocol
udp               # Image Systems Network       Services
tcp               # Bloomberg locator
udp               # Bloomberg locator
udp               # com-bardac-dw
tcp               # iqobject

#指定空格或者/作为分分隔符。+代表重复前面的字符一次或者多次
[root@python ~]# awk -F'[ /]+' '{print $2}'         test.txt
48049
48128
48129
48129
48556
48619

2、-v 变量赋值

[root@python ~]# awk -v a=2 '{print $a}' test.txt
48049/tcp
48128/udp
48129/tcp
48129/udp
48556/udp
48619/tcp
[root@python ~]# awk -v a=342 'BEGIN{print a}'
342

3、-f 从文件中读取awk的内容

#编辑awk脚本文件
[root@python ~]# cat a.txt 
/^$/ {print "BLANK LINE"}
#有几个空行就打印多少行的BLANK LINE
[root@python ~]# awk -f a.txt /etc/ssh/ssh_config
BLANK LINE
BLANK LINE
BLANK LINE
BLANK LINE

4、记录和字段

#$0表示将匹配的内容完全输出
[root@python ~]# echo "I am a bird"|awk '{print $0}'
I am a bird
[root@python ~]# echo "I am a bird"|awk '{print $1,$2}'
I am

#NF表达总的字段的个数(可以理解为列数)
[root@python ~]# echo "I am a bird"|awk '{print NF}'
4

#$NF表示最后一个字段
[root@python ~]# echo "I am a bird"|awk '{print $NF}'
bird

#NR表示输入流的当前记录编号,可以理解为(匹配的行编号)
[root@python ~]# echo "I am a bird"|awk '{print NR}'
1
[root@python ~]# echo -e "I am a birdnhello"|awk '{print NR}'
1
2

5、OFS指定输出的分隔符

echo "I am a bird"|awk 'BEGIN{OFS="#"}{print $1,$2,$3,$4}'
I#am#a#bird

6、正则匹配

#匹配含有tcp的行
[root@python ~]# awk '/tcp/{print $0}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
blp5            48129/tcp               # Bloomberg locator
iqobject        48619/tcp               # iqobject

#匹配以blp5开头的行
[root@python ~]# awk '/^blp5/{print $0}' test.txt
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator

#匹配以tor结束的行
[root@python ~]# awk '/tor$/{print $0}' test.txt
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator

#逻辑或||
[root@python ~]# awk '/blp5/||/3gpp/{print $0}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator

#逻辑与&&
[root@python ~]# awk '/blp5/&&/tcp/{print $0}' test.txt
blp5            48129/tcp               # Bloomberg locator

#逻辑非
[root@python ~]# awk '!/blp5/&& !/3gpp/{print $0}' test.txt
isnetserv       48128/udp               # Image Systems Network Services
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject

7、匹配范围

[root@python ~]# awk '/3gpp/,/blp5/{print $0}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               #       Bloomberg locator

8、BEGIN和END格式(打印标签)

#打印行首
[root@python ~]# awk 'BEGIN{print "SERVICEttPORTtttDESCRIPION"}{print $0}' test.txt
SERVICE         PORT                    DESCRIPION
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject

#打印行尾
[root@python ~]# awk '{print $0}END{print "The ending..."}' test.txt
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject
The ending...

#行首行尾都打印
[root@python ~]# awk 'BEGIN{print "SERVICEttPORTtttDESCRIPION"}{print $0}END{print "The ending..."}' test.txt
SERVICE         PORT                    DESCRIPION
3gpp-cbsp       48049/tcp               # 3GPP Cell Broadcast Service Protocol
isnetserv       48128/udp               # Image Systems Network Services
blp5            48129/tcp               # Bloomberg locator
blp5            48129/udp               # Bloomberg locator
com-bardac-dw   48556/udp               # com-bardac-dw
iqobject        48619/tcp               # iqobject
The ending...

9、表达式与操作符:如果在awk定义的变量中没有初始化,则初始值为空字符串或者0,字符操作一定要加引号(a=”How are you”)。

#统计所有的空白行
[root@python ~]# awk '/^$/{print x+=1}' /etc/ssh/ssh_config
1
2
3
4

#打印出空白行的总个数
[root@python ~]# awk '/^$/{x+=1}END{print x}' /etc/ssh/ssh_config
4

#~(匹配) 、!~(不匹配).打印出root的ID号。匹配第一个字段为root的行,打印出其UID
[root@python ~]# awk -F':' '$1~/root/{print $3}' /etc/passwd
0

#打印出UID大于400的用户
[root@python ~]# awk -F':' '$3>400{print $1}' /etc/passwd
saslauth
mysql
dianel
[root@python ~]# awk -F':' '$3>400{x+=1}END{print x}' /etc/passwd
3

10、awk的高级应用

if条件判断

#判断boot分区可用容量小于20M时报警,否则就显示OK
[root@python ~]# df|grep 'boot'|awk '{if($4<20000)print"alart";else print "ok"}'
ok

[root@python ~]# seq 5|awk '{if($0==3)print $0}'
3
[root@python ~]# seq 5|awk '{if($0==3)print $0;else print "no"}'
no
no
3
no
no

while循环:语法格式

while (condition) statement
do statement while (condition)

两种格式:

#因为i和total都初始化,默认为0
[root@python ~]# awk 'BEGIN{do {i++;total+=i}while(i<100)print total}' 
5050

[root@python ~]# awk 'BEGIN{while(i<100){i++;total+=i}print total}' 
5050

for循环:语法格式

1.for (expr1; expr2; expr3) statement
2.for (var in array) statement

[root@python ~]# awk 'BEGIN{for(i=0;i<101;i++){total+=i} print total}'
5050

break和continue

  • break:跳出循环

  • continue:终止当前循环

打印IP:

[root@python ~]# ifconfig eth0|awk '/Bcast/'|awk -F'[ :]+' '{print $4}'
192.168.1.13

配置apt upgrade禁止更新ubuntu系统内核

Ubuntu系统下的apt工具用来管理软件包很是方便,就连Linux系统内核都可以使用apt来更新。

而apt upgrade命令比较不够灵活,这一个命令下去,系统中所有可以升级的软件包都会被升级,包括系统内核,这也是Linux系统的一个软件包。有的时候,有些软件包我们不想升级,特别是系统内核。

可以用下面的方法来避免使用apt upgrade命令时,升级某个软件包,麦新杰就用系统内核包来举例说明。

首先,使用命令uname -r查看系统使用的内核名称;

使用dpkg –get-selections linux-image*命令查询已经安装内核的列表:

xinlin@ubuntu:~$
xinlin@ubuntu:~$ dpkg --get-selections linux-image*
linux-image-4.10.0-27-generic install
linux-image-4.4.0-31-generic install
linux-image-4.4.0-83-generic install
linux-image-extra-4.4.0-31-generic install
linux-image-extra-4.4.0-83-generic install
linux-image-generic install
xinlin@ubuntu:~$
xinlin@ubuntu:~$ uname -r
4.10.0-27-generic
xinlin@ubuntu:~$

可以看出系统使用的内核是linux-image-4.10.0-27-generic。

将正在使用的内核软件包设置为hold状态,这个命令只能是root用户执行,不建议修改dpkg工具相关文件的属性。(位置:/usr/lib/dpkg)

xinlin@ubuntu:~$
xinlin@ubuntu:~$ su
Password:
root@ubuntu:/home/xinlin# cd
root@ubuntu:~# echo "linux-image-4.10.0-27-generic hold" | dpkg --set-selections
root@ubuntu:~#
root@ubuntu:~# exit
exit
xinlin@ubuntu:~$
xinlin@ubuntu:~$ dpkg --get-selections linux-image-4.10.0-27-generic
linux-image-4.10.0-27-generic hold
xinlin@ubuntu:~$

这样,就实现了使用apt upgrade命令,不升级hold软件包的功能。

注意:设置为hold状态的软件包,对于apt upgrade命令,不会升级,但是依然可以通过apt install命令将其升级并将状态切回install。因此,从安全性角度来看,这样做其实效果不大,仅仅只是避免了偶然的操作失误。本人还是推荐执行apt install来进行“定点升级”。

解决Apache获取客户端真实IP的问题

起因:

服务器端部署apache+php应用,使用$_server[‘remote_addr’]获取的ip为127.0.0.1。虽然说可以通过其他方案来获取正确的ip,但强迫症发作,觉得这是不可接受的。

解决方案:

作为反代服务器的NGINX配置:

确保server中包含以下内容:

server{
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

作为后端服务器的Apache配置:

1、启用mod_remoteip模块

a2enmod remoteip

或(debian系统有效):

sudo ln -s /etc/apache2/mods-available/remoteip.load /etc/apache2/mods-enabled/remoteip.load

2、修改配置文件(以debian为例)

方案一:在使用Cloudflare后,客户端的真实ip会被放在HTTP_CF_CONNECTING_IP中,直接使用即可

在apache2.conf中或虚拟主机的配置文件中添加以下内容

<IfModule remoteip_module>
RemoteIPHeader CF-Connecting-IP
RemoteIPInternalProxy 127.0.0.1/24
</IfModule>

修改apache access log格式(在/etc/apache2/site-enable/*.conf中添加或修改)

LogFormat "%{CF-Connecting-IP}i %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i"" proxied
CustomLog ${APACHE_LOG_DIR}/access-example.com.log proxied

方案二:手动剔除Cloudflare所有的ip

Cloudflare使用的ip可以在https://www.cloudflare.com/ips/查询

<IfModule remoteip_module>
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1/24
#CloudFlare IP Ranges
RemoteIPInternalProxy 103.21.244.0/22
RemoteIPInternalProxy 103.22.200.0/22
RemoteIPInternalProxy 103.31.4.0/22
RemoteIPInternalProxy 104.16.0.0/12
RemoteIPInternalProxy 108.162.192.0/18
RemoteIPInternalProxy 131.0.72.0/22
RemoteIPInternalProxy 141.101.64.0/18
RemoteIPInternalProxy 162.158.0.0/15
RemoteIPInternalProxy 172.64.0.0/13
RemoteIPInternalProxy 173.245.48.0/20
RemoteIPInternalProxy 188.114.96.0/20
RemoteIPInternalProxy 190.93.240.0/20
RemoteIPInternalProxy 197.234.240.0/22
RemoteIPInternalProxy 198.41.128.0/17
</IfModule>

其他:若未使用cdn:

<IfModule remoteip_module>
RemoteIPHeader X-Forwarded-For
RemoteIPInternalProxy 127.0.0.1/24
</IfModule>

3、补充:若后端服务器为NGINX

在配置文件中添加:

set_real_ip_from 127.0.0.1/24;
real_ip_header X-Forwarded-For;

Ubuntu系统的APACHE HTTPS安装和配置

Ubuntu下HTTPS配置非常简单,对大部分用户而言,使用普通的自签名证书,只需按照步骤进行就可以了,无需了解密钥、证书的更多知识,更深的背景知识还有RSA算法、DES算法、X509规范、CA机构…等等,随便哪个方向都够学习一阵子的,所幸的是有了OpenSSL、OpenSSH等这些开源免费的软件,把很多底层的算法、规范都集成了,对上层应用而言,只需一二三操作即可,至多到官网去查查一些特殊的命令集。

一、安装Apache

$sudo apt-get install apache2

使用此方式安装的APACHE,配置比较分散,一般如下:

  • 默认站点在 /var/www/
  • 配置文件在 /etc/apache2/
  • 日志在 /var/log/apache/
  • 启动脚本是 /usr/bin/apache2ctl 或者 /etc/init.d/apache2

二、安装openssl

Ubuntu默认已经安装了OPENSSL,如果没安装,

$sudo apt-get install openssl

三、开启SSL模块

$sudo a2enmod ssl

这条命令相当于

sudo ln -s /etc/apache2/mods-available/ssl.load /etc/apache2/mods-enabled
sudo ln -s /etc/apache2/mods-available/ssl.conf /etc/apache2/mods-enabled

如果没有a2enmod指令,也可直接在apache2.conf中设置SSL模块加载:

LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so

四、创建证书

创建证书有两种:一种是自签名证书,另外一种是第三方CA机构签名证书。第一种随便使用,只是没有经过官方认可的机构认证而已,后一种则是正规的签名证书,有发证机构签名。其实很多所谓的大网站上使用的SSL证书,一样都是自签名的,主要是因为这个证书只做为在线验证使用,保证传输数据安全即可,不过使用这种证书,对常规浏览器和一些软件而言,一般均会弹出警告,让你确认这个签名证书的有效性。正规签名证书也不过只是多了一重保障而已,而且浏览器、软件等可以自己鉴别。

1、自签名证书

可使用apache内置的工具创建默认的自签名证书,通过-days参数指定有效期。

$sudo apache2-ssl-certificate

注意:上述命令可能在最新的apache中默认未安装,如果使用的是LAMP套件,一般会有这个模块。

不过我们还可以使用openssl命令(如果openssl是自编译安装的,没有注册该命令的话,可以使用绝对路径,比如类似/usr/local/openssl/openssl这样)创建:

$sudo openssl req -x509 -newkey rsa:1024 -keyout apache.pem -out apache.pem -nodes -days 999

Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:SH
Locality Name (eg, city) []:SH
Organization Name (eg, company) [Internet Widgits Pty Ltd]:ABC
Organizational Unit Name (eg, section) []:RD
Common Name (eg, YOUR name) []:myservername
Email Address []:[email protected]

注意:在要求输入Common Name (eg, YOUR name) 时,输入你的主机名(授权主机)。

创建完成后,当前目录下有个apache.pem文件,已经包含密钥和证书。可以把这个证书拷贝到/etc/apache2/下创建一个ssl目录然后拷贝到:

/etc/apache2/ssl/apache.pem

2、第三方CA机构签署证书

生成此证书,需要向第三方提交一个“生成证书请求文件(CSR)”,生成这个CSR需要两步:

  • 生成私钥KEY
  • 生成请求CSR

A、运行如下命令生成私钥:

[root@localhost tmp]#  openssl genrsa -des3 1024 -out server.key
Generating RSA private key, 1024 bit long modulus
...++++++
.......................................................++++++
e is 65537 (0x10001)
Enter pass phrase:
Verifying - Enter pass phrase:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,32BDD10A4F977F7E

A7FOhETnGpQnRcCoe1VtOtb8yq12xi6ljq/6wYB6MsGGdGjhftF1FxnSMd9Jx4o2
gjyUJNQs4zYkrtAaY6WYdOMiswymxiSYlKnX3l3uA6d6NqUpoyFxN7mgxqvbrdjq
EaGSLo1d63B6THIq9mOGNm3l1xKtiUZVwHqVaHdb1F/RD6YshwE9yE5bAXjKJKat
sTewoVPxj0bjEDBz49K4m+epUrh7UQ7ZyjMiefvCgg0OxFB3H8zdG0SHa1sV4fG4
9R+4PPoIIlLty4oyAYRwNVUWV47qGV4Jub11s50azVwtS9CV31HZQt48zkcUZ9WF
2PBRQ3c4AMkewzfvjEUIF7bfHcBMl9ugu4fZfJTUxJbA4vHvoVczXhvcTaf6awHn
4YpEX/T3xWE8ObyOjvVh5Utl39INOqzxVKGoZF1ogLFm60SokYx0r+Y19jrz2084
Nri4mHlYNymY+tviTFUUIHZ+8FRnkq0vnW68ejiSzG0Xyr2DDzc7pi6J58bqQ3yR
eNJuK8KVjXxkRn1HXtGL/C6ZpJ4qs6VVzX291vPrr7luWrXGsAcRudLWRFZDSoUB
lFw1CY9ol2TOX2mvt6JryhoH08x3s2prlIen10N35sVELB/nb1/8kkpztCbPHiA/
IH2A3g/WexCAatmCMuqlxW8Cwe98AUZduuZsAZeDoyXk7VxD7YhPKZmKPKOx3gZv
2S1ZpzsNgZh9HhpXsRxjwbYnyICtcUY+dQZXk1w//BP2syjcffXqqhp2FPK3SG7l
PsvHIWQGeTUe5uILP7S/Bbi/KrFAkDguGJHge0mmHgL9gi8RvODtKQ==
-----END RSA PRIVATE KEY-----
[root@localhost tmp]#

这里使用了-des3参数,将会需要输入一个密码对私钥进行加密,每次使用此私钥也需要输入此密码,如不需对私钥加密请不要使用-des3选项。输入两次密码后,将会生成server.key私钥文件。

B、生成请求文件

运行如下命令生成证书请求文件(CSR)

openssl req -new -key server.key –out server.csr

把这个CSR文件传给CA机构,然后他们会使用此请求文件生成证书。

五、编辑HTTPS(SSL)配置

A、添加监听端口

编辑Apache端口配置(/etc/apache2/ports.conf),加入443端口(SSL缺省使用):

Listen 80

Listen 443

B、设置site-enabled

上文安装完后,会在/etc/apache2/sites-available/目录下生成一个缺省的default-ssl文件。缺省的网页目录仍然是/var/www/。我们可以创建一个链接到site-enabled目录。

ln -s /etc/apache2/sites-available/default-ssl /etc/apache2/sites-enabled/001-ssl

C、修改配置文件

确认HTTP监听端口改为80

#vi /etc/apache2/sites-enabled/000-default
NameVirtualHost *:80

ServerAdmin webmaster@localhost
#[......]

HTTPS监听端口缺省443:

把端口改为443,在下加入SSL认证配置,其它的根据需要自己定制 与普通配置无异:

#vi /etc/apache2/sites-enabled/001-ssl
NameVirtualHost *:443

<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/apache.pem
ServerAdmin webmaster@localhost
DocumentRoot /var/www
ServerName myServerName
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/>
Options FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
# This directive allows us to have apache2's default start page
# in /apache2-default/, but still have / go to the right place
#RedirectMatch ^/$ /apache2-default/
</Directory>

六、重启APACHE

# service httpd restart

或,

# /etc/init.d/apache2 restart

测试https://localhost/,IE会弹出security alert(安全警告),如下图:

未分类

Firefox会提示是否添加Exception信息,如下图:

未分类

Linux Crontab定时任务必备招式介绍

crontab 简介

这里的伙伴大多数做客户端开发的,可能对服务端相关的内容关注的相对少一些。 crontab 是这样一个工具,他能够根据你给出的配置在指定的时间执行任务。比如定期删除过期的日志文件(很多服务端环境,会生成大量的日志文件,在你不经意间就会把你的硬盘填满)。当然,它的应用不仅限于服务端,对于自己本地的电脑来说,也有它的用处。

crontab 是 Unix 系统的标配,几乎我们常见的大多数类 Unix 系统都自带 crontab, 包括 MacOS。 Crontab 名字的来源是 chronos – 一种极客们对时间的叫法。 关于它的更多内容,大家可以参考 Wikipedia 上面的描述:https://en.wikipedia.org/wiki/Cron。

使用方法

简单做了介绍之后,咱们来看看怎么使用。 大家可以在自己电脑的命令行中输入:

$ crontab -l

这个命令, 然后你应该会看到类似的输出:

crontab: no crontab for xxx

这个命令是用于列出你电脑上已有的计划任务,因为我们现在还没有指定任何计划任务,所以这个输出就像上面那样,告诉我们没有计划任务。 如果想添加自己的计划任务,可以输入这个命令:

$ crontab -e

这个命令会用 vi 打开一个命令行编辑器,你可以在这里编辑计划任务的描述。 crontab 描述文件中每一行代表一个任务,每一行的格式如下:

* * * * * rm /home/someuser/tmp/*

前面 5 个星号代表时间设置,从左到右每个星号的位置分别对应

分钟(0-59) 小时(0-23) 每月中的天(1-31) 每年中的月(1-12) 每周中的星期几(0-6) 0 代表星期日

每个星号的位置,可以是星号本身,也可以是具体的数字。 如果是星号本身,代表不加限制。 如果是数字,代表指定的时间。 举几个例子:

*  *   *  *  *    // 5个都是个星号,代表每分钟都会执行。 
30 *   *  *  *    // 每到 30分的时候执行一次,也就是每小时执行两次。
*  18  *  *  *    // 每天的 18 点执行一次。
*  */2 *  *  *    // 每隔 2 小时执行一次, */ 是间隔时段的表示法。

以上是 crontab 时间设置个几个常用示例, 只要按照我们上述的每隔时间点的规则来使用,几乎可以满足我们绝大部分计划任务的需求。

在时间配置之后, 就是我们要执行的脚本文件,比如我们想在每天早上 8 点的时候清空机器上的临时文件,就可以这样配置:

* 8 * * * rm /home/someuser/tmp/*

每天 8点的时候,就会执行后面的 rm 命令, 删除临时文件夹中的文件。 后面的这个命令行内容,可以是任何的 Shell 命令,包括 pipe 操作,比如:

* 8 * * * rm /home/update.sh > /var/log/update.log

这个配置会在每天 8 点的时候, 执行一个叫做 update 的脚本, 并将日志输出流重定向到 update.log 文件中。 这时我们的命令行界面应该是这样的:

未分类

编辑完成后, 按下 Esc 键, 然后输入 :wq(代表保存并退出,这个是 vi 文本工具的使用方法), 在命令行左下角是下面图片中的状态的时候,按下回车键:

未分类

这样就保存了我们刚刚编辑的任务列表,我们再输出 crontab -l 命令,可以看到任务被成功添加了。

这样就 ok 了。剩下的事情就交给 crontab 来处理了。

结语

crontab 是 Unix 系统中标配的计划任务工具,它的原理并不复杂,crontab 是一个守护进程,不停的检测它的任务列表是否符合执行条件,如果符合,就执行任务。这里跟大家聊的都是 crontab 比较常规的用法,更详细的文档,大家可以通过 man crontab 命令自行查看。相信它对大家的日常工作还是比较有帮助的。

使用Jenkins Ansible Docker Swarm实现自动化编译部署

自动化部署在项目部署过程中很重要,一旦自动化部署完成,我们就可以减轻我们手动的操作的步骤以及出错的概率。下面介绍一种通用的自动化部署。从打包编译到上线,一条命令就完成所有操作。简单而高效

1、Jenkins部署这里不在赘述,直接从新建项目开始。

项目截图,如下:增加两个变量如图,

  • BranchTobuild 默认是master分支,在编译的时候也可以指定分支。

  • PushDockerImage 默认布尔值是选中,直接把镜像推到镜像仓库。

未分类

增加Pipeline配置,主要是分了几个步骤。所以用pipeline,也可以不用这种方式,直接脚本也行

所有执行步骤写入Jenkinsfile中,并把文件放入项目的根目录,这样才能调用。

未分类

Jenkinsfile内容如下:包括编译、打包、推送docker镜像到仓库,ansible部署。

#cat Jenkinsfile
pipeline {

   //定义了执行jenkins的机器
    agent {
        label ‘master’
    }
    stages {
        stage (‘Prepare’) {
            steps {
                sh “echo Prepare”
                //准备阶段,没有暂不处理
            }
        }

        //build打包过程,最后会生产apk包
        stage (‘build’) {
            steps {
                sh ‘echo “start Build”‘
                script {
                    if (params.buildDebug) {
                        sh ‘echo “build debug version”‘
                        sh ‘chmod +x ./gradlew’
                        sh “./gradlew clean”
                        sh “./gradlew build –debug”
                    } else {
                        sh ‘echo “build release version”‘
                        sh ‘chmod +x ./gradlew’
                        sh “./gradlew clean”
                        sh “./gradlew build”
                    }
                }
            }
        }
        stage (‘Test’) {
            steps {
                sh “echo Test”
                //测试阶段,没有暂不处理
            }
        }

       //发布阶段,项目中包括了gradle.properties文件,几个变量:版本、项目、产品,按照这几个名词生产镜像的名
        stage (‘Deploy’) {
            steps {
                sh ‘echo “Start Docker Build And Push Images”‘
                script {
                    if (params.PushDockerImage) {
                        def props = readProperties  file: ‘gradle.properties’
                                    def VERSION = props[‘version’]
                                    def PRODUCT = props[‘product’]
                                    def ARTIFACT = props[‘artifact’]
                        sh ‘echo “start Build”‘
                                    //开始修改Dockerfile
                                    sh “sed -i ‘s#${PRODUCT}#${PRODUCT}#g’ Dockerfile”
                                    sh “sed -i ‘s#${VERSION}#${VERSION}#g’ Dockerfile”
                                    sh “sed -i ‘s#${ARTIFACT}#${ARTIFACT}#g’ Dockerfile”
                        sh “docker build -t registry.sreop.com:5000/${PRODUCT}/${ARTIFACT}:${VERSION} -f Dockerfile .”
                        sh ‘echo “Publish Images To registry.leautolink.com”‘
                        sh “docker push registry.leautolink.com:5000/${PRODUCT}/${ARTIFACT}:${VERSION}”

                        //ansible playbook 部署到线上或者测试环境
                        sh “sudo /usr/bin/ansible-playbook  /data/base-docker-compose/product/light/prod/playbook.yml”
                    }
                }
            }
        }
    }
}

2、每个项目需要一个Dockefile

cat Dockerfile
# 基础镜像
FROM registry.sreop.com:5000/alpine-java:8u121b13_jdk_unlimited
# 维护者信息
MAINTAINER [email protected]
# 镜像操作命令
RUN mkdir -p /data/bin/${PRODUCT}/${ARTIFACT}
# 指定后续命令的执行目录
WORKDIR /data/bin/${PRODUCT}/${ARTIFACT}
# 对外连接端口号
EXPOSE 12429
# 向镜像中增加文件
ADD ./build/libs/${ARTIFACT}-${VERSION}.jar .
# 容器启动命令
CMD java -Djava.security.egd=file:/dev/./urandom -jar ${ARTIFACT}-${VERSION}.jar –spring.profiles.active=prod

3、写ansible playbook,为推送到Docker集群平台,并部署到线上或者测试平台。

注意:docker-compose.yml要拷贝到每个docker节点机器上。功能是:获取各个变量,拉取镜像。最后生产docker services

– name: light Pull Image
  hosts: docker-swarm-prod
  remote_user: root
  tasks:
    – name: get product var 
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml |head -n1|awk -F’/’ ‘{print $2}’
      register: product
    – name: get artifact var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1 |awk -F’/’ ‘{print $3}’|sed ‘s#(.*):(.*)#1#g’
      register: artifact
    – name: get version var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1 |awk -F’/’ ‘{print $3}’|sed ‘s#(.*):(.*)#2#g’
      register: version
    – name: docker pull image
      shell: docker pull registry.leautolink.com:5000/”{{ product.stdout }}”/”{{ artifact.stdout }}”:”{{ version.stdout }}” || /bin/true

– name: Remove Docker Old Verison
  hosts: docker-swarm-prod-lead 
  remote_user: root
  tasks:
    – name: get product var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1|awk -F’/’ ‘{print $2}’
      register: product
    – name: get artifact var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1 |awk -F’/’ ‘{print $3}’|sed ‘s#(.*):(.*)#1#g’
      register: artifact
    – name: get version var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1 |awk -F’/’ ‘{print $3}’|sed ‘s#(.*):(.*)#2#g’
      register: version
    – name: remove light
      shell: docker stack rm {{ artifact.stdout }} || /bin/true

– name: Start New verison
  hosts: docker-swarm-prod-lead 
  remote_user: root
  tasks:
    – name: get product var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1|awk -F’/’ ‘{print $2}’
      register: product
    – name: get artifact var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1 |awk -F’/’ ‘{print $3}’|sed ‘s#(.*):(.*)#1#g’
      register: artifact
    – name: get version var
      shell: grep image /data/base-docker-compose/product/light/prod/docker-compose.yml|head -n1 |awk -F’/’ ‘{print $3}’|sed ‘s#(.*):(.*)#2#g’
      register: version
    – name: start light
      shell: docker stack deploy -c /data/base-docker-compose/product/light/prod/docker-compose.yml {{ artifact.stdout }} || /bin/true 

4、swarm集群部署compose.yml

# cat docker-compose.yml 
version: “3”
services:
  config-server:
    image: registry.sreop.com:5000/leradio/light:1.0.1-RELEASE
    command: java -Djava.security.egd=file:/dev/./urandom -jar light-1.0.1-RELEASE.jar –spring.cloud.config.profile=prod –spring.profiles.active=prod
    ports:
      – 12429:12429
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
      restart_policy:
        condition: on-failure
networks:
  frontend:

初步体验docker swarm集群

之前用的阿里云容器服务,但由于acsrouting的路由错乱问题,被逼上自建docker swarm的梁山。今天尝试自己搭建docker swarm,竟然轻松搞定,简单的超乎想象。

以下是实际搭建操作步骤:

1、创建集群

# docker swarm init --advertise-addr 10.251.242.231
Swarm initialized: current node (m9dfl7r9wo1e9jxsp3oe5du3x) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token xxx 10.251.242.231:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

主:10.251.242.231 是 docker 主机的内网IP地址

2、docker info 查看刚刚创建的集群信息

Swarm: active
 NodeID: m9dfl7r9wo1e9jxsp3oe5du3x
 Is Manager: true
 ClusterID: j01wzizw7gy0ck95p1d7a4pmv
 Managers: 1
 Nodes: 1
 Orchestration:
  Task History Retention Limit: 5

3、docker node ls 查看节点信息

ID                            HOSTNAME               STATUS              AVAILABILITY        MANAGER STATUS
m9dfl7r9wo1e9jxsp3oe5du3t *   swarm-websites-node1   Ready               Active              Leader

4、创建用于部署应用容器的网络

# docker network create --driver=overlay --attachable cnblogs

5、创建用于部署Docker Flow Proxy容器的网络(Docker Flow Proxy支持根据主机名将请求转发至对应的应用容器)

docker network create --driver overlay proxy

6、查看刚刚创建的网络

# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
rl96kcw8ytpa        cnblogs             overlay             swarm
61kbxtvnivkx        proxy               overlay             swarm

7、部署 Docker Flow Proxy stack

7.1 下载 docker-compose-stack.yml 文件

# curl -o docker-compose-stack.yml 
    https://raw.githubusercontent.com/
vfarcic/docker-flow-proxy/master/docker-compose-stack.yml

7.2 部署 stack

# docker stack deploy -c docker-compose-stack.yml proxy

8、编写示例应用 openapi 的 docker-compose-stack.yml

version: '3.2'
services:
  api:
    image: open-api:latest
    deploy:
      replicas: 2
      update_config:
        delay: 5s
      labels:
        - com.df.notify=true
        - com.df.distribute=true
        - com.df.serviceDomain=api.cnblogs.com
        - com.df.port=80
    networks:
      - cnblogs
      - proxy
networks:
  cnblogs:
    external: true
  proxy:
    external: true

9、部署示例应用的 stack

docker stack deploy -c docker-compose-stack.yml openapi

10、部署完成后可以通过 http://api.cnblogs.com 访问运行在 openapi_api service 中的站点

11、添加机器进集群作为节点

docker swarm join --token xxxxxxxxx IP:2377

12、将节点提升为manager

docker node promote ID或HOSTNAME

Centos7安装配置Python3.6 Django virtualenv gunicorn supervisor环境

跟着网上的教程走发现行不通阿!好多都是写个大概,而且每人的环境都是有些许差异的,比如说权限问题阿,等等都会造成安装的失败

说明:本教程在你已经拥有Centos7系统,已经安装好nginx服务器,已经安装了Python3.6 Django virtualenv gunicorn supervisor的前提下进行

接下来开始了!

1、新建你的django项目,假设项目名为Hello

 django-admin.py startproject Hello

2、想好你需要的端口号,假设端口号为8001(下面的端口号均以8001来举例,你可以换成你所需要的端口号),接下来启动服务器看看能不能运行,分两种情况

2.1 如果你只是想在本地运行则

python manage.py runserver127.0.0.1:8001

2.2 如果你想要外网也可以访问则

python manage.py runserver0.0.0.0:8001

3、接下来在浏览器中输入 “服务器ip:8001” ,比如我服务器的公网IP为 192.163.189.166 则输入192.163.189.166:8001,可能会出现三种情况!

3.1 成功运行

3.2 出现 DisallowedHost at / Invalid HTTP_HOST header: ‘10.211.55.6:8001’. You may need to add u’10.211.55.6′ to ALLOWED_HOSTS. 类似错误,解决方法:
进入项目目录下的Hello目录(注意项目目录名是和该名称相同的,此Hello和manage.py同级打开setting.py将ALLOWED_HOSTS = []改为ALLOWED_HOSTS = [‘*’]

3.3 如果在确保地址输入正确,端口也正确的前提下浏览器出现了 Unable to connect 错误,那么很可能是你的Centos7没有开启8001端口号的原因,解决方法

开启端口号

firewall-cmd --zone=public --add-port=8001/tcp --permanent (--permanent意思是永久生效,重启后继续生效)

重启防火墙

firewall-cmd --reload

此时再访问浏览器,如果还是访问不了,那可能是我没遇到的情况,还请自行搜索解决哦

4、配置virtualenv gunicorn

4.1 在项目根目录下输入指令 virtualenv venv (venv可以是其他名字了)

4.2 虚拟环境生成后接着要在虚拟环境中安装django 和 gunicorn 了

pip install django
pip install gunicorn

4.3 在项目根目录下创建gunicorn.conf 用来配置gunicorn,我的配置为

workers = 4
bind = '0.0.0.0:8088'

5、配置supervisor

supervisor的配置文件一般在/etc/supervisord.conf

5.1 vim /etc/supervisord.conf

5.2 在末尾加入

[program:hello]
command=/项目路径/venv/bin/gunicorn -c /项目路径/gunicorn.conf Hello.wsgi:application
directory=/项目路径
autostart=true
autorestart=true
stdout_logfile=/项目路径/logs/gunicorn.log
stderr_logfile=/项目路径/logs/gunicorn.err  

5.3 重启 supervisor

unlink /tmp/supervisor.sock
supervisord -c /etc/supervisord.conf

6、配置nignx

6.1 打开nignx.conf

6.2 在合适地方加入

location /  {
     proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
     proxy_set_header Host $http_host;
     proxy_redirect off;
     proxy_pass http://192.163.189.166:8001;   #http://外网ip:8001,如果是本机访问则
http://127.0.0.1:8001
              }

6.3 重启nginx

systemctl restart nginx

7、好啦,接下来在浏览器中输入 http://192.163.189.166:8001 应该能访问咯。

使用python的pyenv工具管理python virtualenv虚拟环境

pyenv安装

1. 安装依赖包

yum -y install git gcc make patch zlib-devel gdbm-devel openssl-devel sqlite-devel bzip2-devel readline-devel

2. 安装pyenv

curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash

3. 设置环境变量

echo >> .bash_profile << EOF
# pyenv settings
export PATH="~/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
EOF

4. 使之生效

. .bash_profile或者source .bash_profile

这时候pyenv就可以使用了

pyenv使用指南

1、pyenv versions查看系统的上安装的Python版本。

其中前面的*表示当前工作目录正在使用的版本,其中 的 system表示系统自带的 Python 版本:

$ pyenv versions
* system (set by /Users/yulongjun/.pyenv/version)

2、pyenv install 安装其他版本的Python。
例如安装3.6.1和2.7.13版本:

$ pyenv install 3.6.1   # 安装3.6.1版本的Python
$ pyenv install 2.7.13  # 安装2.7.13版本的Python
$ pyenv versions        # 可以看到3个版本,system为系统自带的版本
* system (set by /usr/local/var/pyenv/version)
  2.7.13
  3.6.1
$ cd                   # 到家目录
$ mkdir Python36       # 创建Python3.6的工作目录
$ cd Python36
$ pyenv local 3.6.1    # 使当前工作目录使用Python3.6.1版本
$ python -V            # 查看一下当前目录用Python的版本,确实是3.6.1
Python3.6.1
$ pip -V               # 查看一下pip版本,是3.6的pip
pip 9.0.1 from /usr/local/var/pyenv/versions/3.6.1/lib/python3.6/site-packages 
$ cd                   # 回到家目录
$ mkdir Python27       # 创建python2.7的工作目录
$ cd Python27
$ pyenv local 2.7.13   # 使当前工作目录使用Python2.7.13版本
$ python -V            # 查看一下当前目录用Python的版本,确实是2.7.13
Python 2.7.13
$ pip -V               # 查看一下pip版本,是2.7的pip
pip 9.0.1 from /usr/local/var/pyenv/versions/2.7.13/lib/python2.7/site-packages (python 2.7)

pyenv-virtualenv的使用方法

pyenv-virtualenv是用来创建一个干净的虚拟Python环境的命令,通常在创建干净的新项目时候使用。使用方法如下:

1、创建虚拟环境–pyenv virtualenv 版本号 虚拟环境名。

$ pyenv virtualenv 3.6.1 venv-3.6.1

2、创建项目,让项目使用干净的Python3.6.1的虚拟环境:

[yulongjun@yulongjun ~]$ mkdir Learning-Python3
[yulongjun@yulongjun ~]$ cd Learning-Python3/
[yulongjun@yulongjun Learning-Python3]$ pyenv local venv-3.6.1
(venv-3.6.1) [yulongjun@yulongjun Learning-Python3]$ cd ..
[yulongjun@yulongjun ~]$ cd Learning-Python3/
(venv-3.6.1) [yulongjun@yulongjun Learning-Python3]$

我们会发现:只要我们进入Learning-Python3目录,就会自动激活virtualenv,退出Learning-Python3目录,就会关闭virtualenv。

如果要关闭自动激活,可以运行命令pyenv deactivate,要重新启用的话,运行pyenv activate 虚拟环境名。