kubernetes构建一套基础tomcat8 jdk8镜像

1、RC类型tomcat基础镜像

#1、编写yaml文件
apiVersion: v1 #api版本 固定
kind: ReplicationController  #kind类型ReplicationController
metadata:   
  name: zk-web
spec:           #spec.selector是RC的pod选择器,spec.selector是RC的pod选择器,确保当前集群上始终有且仅有replicas个pod实例运行,这里我们spec.replicas=1表示只能运行一个(名为的zk-web)pod 实例
  replicas: 1   #指启动几个副本
  selector:     #pod选择器
    app: zk-web
  template:     #当集群中运行的pod数量小于replicas时,RC会根据spec.template 段定义的pod模版来生成一个新的pod实例,labels属性指定了该pod的标签,这里的labels必须匹配RC的spec.selecto
    metadata:
      labels:
        app: zk-web
    spec:       #spec.image表示镜像地址。
      containers:
        - name: zk-web
          image: harbor.suixingpay.com/study/tomcat-app:v2
          ports:   #表示容器服务的端口
          - containerPort: 8080
          env:  #表示定义环境变量
          - name: MYSQL_SERVICE_HOST
            value: 'zk-mysql'
          - name: MYSQL_SERVICE_PORT
            value: '3306'

2、TOMCAT Docerfile

FROM harbor.suixingpay.com/study/centos:6.7
RUN yum install wget -y
RUN wget -O /etc/yum.repos.d/epel.repo http://172.16.132.241/epel/epel.repo
RUN wget -O /etc/yum.repos.d/CentOS-Base.repo http://172.16.132.241/centos/CentOS-Base.repo
RUN yum makecache fast
RUN yum install tar  wget openssl openssh-server sudo  -y
RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config
RUN echo "root:root" | chpasswd
##优化
RUN echo '*               -       nofile          65535 ' >>/etc/security/limits.conf
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# install JDK1.8
RUN  wget http://172.16.132.241/soft/jdk/8u111/jdk-8u111-linux-x64.tar.gz
RUN  tar zxvf jdk-8u111-linux-x64.tar.gz  -C /opt
#RUN echo 'JAVA_HOME="/opt/jdk_1.8"' >>/etc/profile
#RUN echo 'CLASSPATH="$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib"'>>/etc/profile
#RUN echo 'PATH="$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$PATH:$HOMR/bin"' >>/etc/profile
ENV JAVA_HOME /opt/jdk1.8.0_111
ADD apache-tomcat-8.0.14.tar.gz /opt
RUN cd /opt/ && ls
EXPOSE 22
EXPOSE 8080
#RUN cd /opt/ && ls &&tar zxvf /opt/apache-tomcat-8.0.14.tar.gz
ENV CATALINA_HOME /opt/apache-tomcat-8.0.14
ENV PATH $PATH:$CATALINA_HOME/bin
ADD tomcat.sh /etc/init.d/tomcat
RUN chmod +x /etc/init.d/tomcat
#CMD ["/opt/tomcat.sh start"]
# /sbin/service sshd start
ENTRYPOINT /sbin/service sshd start &&  /sbin/service tomcat start && tail -f /opt/apache-tomcat-8.0.14/logs/catalina.out
#EXPOSE 22
#EXPOSE 8080
##tomcat.sh
export JAVA_HOME=/opt/jdk1.8.0_111
export TOMCAT_HOME=/opt/apache-tomcat-8.0.14
case $1 in
start)
  sh $TOMCAT_HOME/bin/startup.sh
;;
stop)
  sh $TOMCAT_HOME/bin/shutdown.sh
;;
restart)
  sh $TOMCAT_HOME/bin/shutdown.sh
  sh $TOMCAT_HOME/bin/startup.sh
;;
esac
exit 0
##生成镜像
[root@k8s-harbor01 tomcat]# docker build -t harbor.suixingpay.com/zhaikun/cent_tomcat:3.1 .
[root@k8s-harbor01 tomcat]# docker push harbor.suixingpay.com/zhaikun/cent_tomcat:3.1

3、RC类型基础镜像

apiVersion: v1
kind: ReplicationController
metadata:
  name: zk-jdk-tomcat
spec:
  replicas: 1
  selector:
    app: zk-jdk-tomcat
  template:
    metadata:
      labels:
        app: zk-jdk-tomcat
    spec:
      containers:
        - name: zk-jdk-tomcat
          image: harbor.suixingpay.com/zhaikun/cent_tomcat:3.1
          ports:
          - containerPort: 8080

4、SERVER类型基础镜像

apiVersion: v1
kind: Service
metadata:
  name: zk-jdk-tomcat
spec:
  type: NodePort
  ports:
    - port: 8080
      nodePort: 30009
  selector:
    app: zk-jdk-tomcat

Jenkins集成Docker

大概过程如下图:

未分类

由于需要用到docker打包镜像,jenkins宿主机上需要安装docker,原先的jenkins server安装在centos6上无法运行docker,所以这里单独用一台centos7安装一个jenkins server。

jenkins的安装方法有多种:

1、下载jar包,直接通过java运行。

2、用tomcat作为容器运行。

3、通过yum安装。

4、通过docker运行。

安装过程都很简单,这里就不详细说明了。

1、安装插件。打开jenkins页面,安装CloudBees Docker Build and Publish plugin和Publish Over SSH Plugin插件。

未分类

2、新建一个项目。

未分类

3、配置项目。

未分类

4、填写项目名称。jenkins的工作空间与这里的项目名称对应,默认在/var/lib/jenkins/workspace下。

未分类

5、拉取代码。填写项目地址和需要拉取的分支,这里用了一个Additional Behabiours,拉取代码会自动创建一个目录并将代码放在该目录下。

未分类

6、build镜像,并将镜像push到harbor镜像库。填写相应的docker、dockerfile以及镜像库地址信息。

这里需要设置docker启动参数。

#vim /etc/sysconfig/docker
OPTIONS='--insecure-registry 172.60.0.107 -H 0.0.0.0:2375 -H unix:///var/run/docker.sock'
#systemctl restart docker

未分类

7、通过脚本发布到k8s。这里需要设置SSH互信(具体过程略..)。

未分类

8、在k8s master上编写发布脚本。

#vim stg-che001-56-waybill.sh

#!/bin/bash

#update the version number
sed -ri "s@(image.*v).*@1${BUILD_NUMBER}@" /root/che001-56-stg/che001-56-waybill.yaml

#Apply the configuration change
kubectl delete -f /root/che001-56-stg/che001-56-waybill.yaml && kubectl create -f /root/che001-56-stg/che001-56-waybill.yaml

9、配置完成后,点击立即构建。

未分类

10、点击Console Output可以查看构建日志。如果最后提示SUCCESS,则表示构建成功。(这里日志太长,只截取了一半)

未分类

未分类

iptables中DNAT、SNAT和MASQUERADE的理解

DNAT(Destination Network Address Translation,目的地址转换) 通常被叫做目的映谢。而SNAT(Source Network Address Translation,源地址转换)通常被叫做源映谢。

这是我们在设置Linux网关或者防火墙时经常要用来的两种方式。以前对这两个都解释得不太清楚,现在我在这里解释一下。

首先,我们要了解一下IP包的结构,如下图所示:

未分类

在任何一个IP数据包中,都会有Source IP Address与Destination IP Address这两个字段,数据包所经过的路由器也是根据这两个字段是判定数据包是由什么地方发过来的,它要将数据包发到什么地方去。而iptables的DNAT与SNAT就是根据这个原理,对Source IP Address与Destination IP Address进行修改。

然后,我们再看看数据包在iptables中要经过的链(chain):

未分类

图中正菱形的区域是对数据包进行判定转发的地方。在这里,系统会根据IP数据包中的destination ip address中的IP地址对数据包进行分发。如果destination ip adress是本机地址,数据将会被转交给INPUT链。如果不是本机地址,则交给FORWARD链检测。

这也就是说,我们要做的DNAT要在进入这个菱形转发区域之前,也就是在PREROUTING链中做,比如我们要把访问202.103.96.112的访问转发到192.168.0.112上:

iptables -t nat -A PREROUTING -d 202.103.96.112 -j DNAT --to-destination 192.168.0.112

这个转换过程当中,其实就是将已经达到这台Linux网关(防火墙)上的数据包上的destination ip address从202.103.96.112修改为192.168.0.112然后交给系统路由进行转发。

而SNAT自然是要在数据包流出这台机器之前的最后一个链也就是POSTROUTING链来进行操作

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j SNAT --to-source 58.20.51.66

这个语句就是告诉系统把即将要流出本机的数据的source ip address修改成为58.20.51.66。这样,数据包在达到目的机器以后,目的机器会将包返回到58.20.51.66也就是本机。如果不做这个操作,那么你的数据包在传递的过程中,reply的包肯定会丢失。

假如当前系统用的是ADSL/3G/4G动态拨号方式,那么每次拨号,出口IP都会改变,SNAT就会有局限性。

iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE

重点在那个『 MASQUERADE 』!这个设定值就是『IP伪装成为封包出去(-o)的那块装置上的IP』!不管现在eth0的出口获得了怎样的动态ip,MASQUERADE会自动读取eth0现在的ip地址然后做SNAT出去,这样就实现了很好的动态SNAT地址转换。

k8s部署之使用CFSSL创建证书

一、安装CFSSL

curl -s -L -o /bin/cfssl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
curl -s -L -o /bin/cfssljson https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
curl -s -L -o /bin/cfssl-certinfo https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x /bin/cfssl*

二、容器相关证书类型

  • client certificate: 用于服务端认证客户端,例如etcdctl、etcd proxy、fleetctl、docker客户端
  • server certificate: 服务端使用,客户端以此验证服务端身份,例如docker服务端、kube-apiserver
  • peer certificate: 双向证书,用于etcd集群成员间通信

三、创建CA证书

1、生成默认CA配置

mkdir /opt/ssl
cd /opt/ssl
cfssl print-defaults config > ca-config.json
cfssl print-defaults csr > ca-csr.json

修改ca-config.json,分别配置针对三种不同证书类型的profile,其中有效期43800h为5年

{
    "signing": {
        "default": {
            "expiry": "43800h"
        },
        "profiles": {
            "server": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth"
                ]
            },
            "client": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "client auth"
                ]
            },
            "peer": {
                "expiry": "43800h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            }
        }
    }
}

修改ca-csr.config

{
    "CN": "Self Signed Ca",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "SH",
            "O": "Netease",
            "ST": "SH",            
            "OU": "OT"
        }    ]
}

生成CA证书和私钥

cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
生成ca.pem、ca.csr、ca-key.pem(CA私钥,需妥善保管)

2、签发Server Certificate

cfssl print-defaults csr > server.json
vim server.json
{
    "CN": "Server",
    "hosts": [
        "192.168.1.1"
       ],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "CN",
            "L": "SH",
            "ST": "SH"
        }
    ]
}
生成服务端证书和私钥
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server server.json | cfssljson -bare server

3、签发Client Certificate

cfssl print-defaults csr > client.json
vim client.json
{
    "CN": "Client",
    "hosts": [],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "CN",
            "L": "SH",
            "ST": "SH"
        }
    ]
}
生成客户端证书和私钥
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client client.json | cfssljson -bare client

4、签发peer certificate

cfssl print-defaults csr > member1.json
vim member1.json
{
    "CN": "member1",
    "hosts": [
        "192.168.1.1"
    ],
    "key": {
        "algo": "ecdsa",
        "size": 256
    },
    "names": [
        {
            "C": "CN",
            "L": "SH",
            "ST": "SH"
        }
    ]
}
为节点member1生成证书和私钥:
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer member1.json | cfssljson -bare member1
针对etcd服务,每个etcd节点上按照上述方法生成相应的证书和私钥

5、最后校验证书

校验生成的证书是否和配置相符

openssl x509 -in ca.pem -text -noout
openssl x509 -in server.pem -text -noout
openssl x509 -in client.pem -text -noout

四、k8s集群所需证书

未分类

Haproxy基于ACL做访问控制

haproxy配置文档 https://cbonte.github.io/haproxy-dconv/

基于ACL做访问控制(四层代理)

网络拓扑

未分类

环境

前端HAProxy 172.16.253.108
后端web1    172.16.253.105
后端web2    172.16.252.1
client      172.16.253.177

安装HAProxy

HAProxy

[root@HAProxy ~]# yum install haproxy -y
[root@HAProxy ~]# rpm -ql haproxy
[root@HAProxy ~]# iptables -F
[root@HAProxy ~]# setenforce 0
[root@HAProxy ~]# systemctl enable haproxy
[root@HAProxy ~]# cp /etc/haproxy/haproxy.cfg{,.bak}
[root@HAProxy ~]# vim /etc/haproxy/haproxy.cfg

web1

[root@web1 ~]# yum -y install httpd
[root@web1 ~]# vim /var/www/html/index.html 
<h1> Backend Server 1 </h1>
[root@web1 ~]# systemctl start httpd
[root@web1 ~]# setenforce 0
[root@web1 ~]# iptables -F

web 2

[root@web2 ~]# yum -y install httpd
[root@web2 ~]# vim /var/www/html/index.html 
<h1> Backend Server 2 </h1>
[root@web2 ~]# service httpd start 
[root@web2 ~]# setenforce 0
[root@web2 ~]# iptables -F
  • block阻塞主机访问

172.16.251.196用户访问stats状态界面,并显示错误网页http://172.16.253.108:10080/403.html

HAProxy

[root@HAProxy ~]# vim /etc/haproxy/haproxy.cfg
    frontend myweb *:80
        default_backend websrvs

    backend websrvs
        balance roundrobin
        server srv1 172.16.253.105:80 check weight 2
        server srv2 172.16.252.1:80 check weight 1
    listen stats
        bind *:9000
        acl allowstats src 172.16.251.196
        block if allowstats  \阻塞allowstats中的IP访问stats界面
        errorloc 403 http://172.16.253.108:10080/403.html
        stats enable
        stats uri /myproxy?admin
        stats realm "HAProxy Stats Page"
        stats auth admin:admin
        stats admin if TRUE
[root@HAProxy ~]# systemctl restart haproxy 

访问测试

172.16.251.196使用浏览器访问测试http://172.16.253.108:10080/403.html 
  • http-request允许某主机访问stats状态界面

允许172.16.251.196用户访问http://172.16.253.108服务器的HAProxy的状态界面

HAProxy

[root@HAProxy ~]# vim /etc/haproxy/haproxy.cfg
    frontend myweb *:80
        default_backend websrvs

    backend websrvs
        balance roundrobin
        server srv1 172.16.253.105:80 check weight 2
        server srv2 172.16.252.1:80 check weight 1
    listen stats
        bind *:9000
        acl allowstats src 172.16.251.196
        # http-request allow if allowstats  \允许allowstats中的IP访问stats状态界面
        http-request deny  unless allowstats \除了allowstats之外全部拒绝访问,即仅允许allowstats访问
        # http-request deny if allowstats \拒绝allowstats访问
        errorloc 403 http://172.16.253.108:10080/403.html \错误网页文件
        stats enable
        stats uri /myproxy?admin
        stats realm "HAProxy Stats Page"
        stats auth admin:admin
        stats admin if TRUE
[root@HAProxy ~]# systemctl restart haproxy 

访问测试

图形化浏览器
    172.16.251.196使用浏览器访问测试http://172.16.253.108:10080/403.html    
字符界面 
    [root@client ~]# curl --basic --user admin:admin http://172.16.253.108:9000/myproxy?admin   

基于ACL做访问控制(七层代理)

动态网页存放在动态服务器组中,静态网页存放在静态服务器组中

拓扑环境

环境

前端HAProxy 172.16.253.108
后端web1    172.16.253.105
后端web2    172.16.253.191
client      172.16.253.177
  • web1使用虚拟主机技术搭建两个web server,用来存放动态网页内荣容
  • web2使用虚拟主机搭建两个web server用来替代静态网页内容

web1创建虚拟主机

[root@web1 ~]# yum -y install php httpd
[root@web1 ~]# mkdir /data/web/vhost{1,2} -pv
[root@web1 ~]# vim /data/web/vhost1/index.php
<h1> Application Server 1</h1>
<?php
    phpinfo();
?>
[root@web1 ~]# vim /data/web/vhost2/index.php
<h1> Application Server 2</h1>
<?php
    phpinfo();
?>

虚拟主机1的配置文件
[root@web1 ~]# vim /etc/httpd/conf.d/vhost1.conf \编辑vhost1虚拟主机的配置文件
<VirtualHost *:80>
    ServerName www1.danran.com
    DocumentRoot "/data/web/vhost1"
    <Directory "/data/web/vhost1">
            Options FollowSymLinks \允许使用连接文件目录
            AllowOverride None \不允许其他配置文件覆盖此文件中的设置
            Require all granted
    </Directory>
</VirtualHost>

虚拟主机2的配置文件
[root@web1 ~]# vim /etc/httpd/conf.d/vhost2.conf
[root@web1 ~]# vim /etc/httpd/conf.d/vhost2.conf
Listen 8080
<VirtualHost *:8080>
    ServerName www2.danran.com
    DocumentRoot "/data/web/vhost2"
    <Directory "/data/web/vhost2">
            Options FollowSymLinks
            AllowOverride None
            Require all granted
    </Directory>
</VirtualHost>

[root@web1 ~]# systemctl restart httpd.service 
[root@web1 ~]# ss -ntl

web2创建虚拟主机

[root@web2 ~]# yum -y install httpd
[root@web2 ~]# mkdir -pv /data/web/vhost{1,2}
[root@web2 ~]# find /usr/share/ -iname "*.jpg" -exec cp {} /data/web/vhost1/ ;
[root@web2 ~]# find /usr/share/ -iname "*.jpg" -exec cp {} /data/web/vhost2/ ;
[root@web2 ~]# vim /data/web/vhost1/index.html
<h1> Image Server 1 </h1>
[root@web2 ~]# vim /data/web/vhost2/index.html
<h1> Image Server 2 </h1>

编辑虚拟主机1的配置文件
[root@web2 ~]# vim  /etc/httpd/conf.d/vhost1.conf 
<VirtualHost *:80>
    ServerName www1.danran.com
    DocumentRoot "/data/web/vhost1"
    <Directory "/data/web/vhost1">
            Options FollowSymLinks
            AllowOverride None
            Require all granted
    </Directory>
</VirtualHost>

编辑虚拟主机2的配置文件
[root@web2 ~]# vim  /etc/httpd/conf.d/vhost2.conf 
Listen 8080
<VirtualHost *:8080>
    ServerName www2.danran.com
    DocumentRoot "/data/web/vhost1"
    <Directory "/data/web/vhost1">
            Options FollowSymLinks
            AllowOverride None
            Require all granted
    </Directory>
</VirtualHost>

[root@web2 ~]# systemctl start httpd.service 

HAProxy

[root@HAProxy ~]# vim /etc/haproxy/haproxy.cfg
    frontend myweb *:80
        cookie WEBSRV indirect nocache
        acl static path_end .jpg .jpeg .png .gif .txt .html \定义ACL的组static以.jpg .jpeg .png .gif .txt .html结尾的文件
        use_backend staticsrvs  if static  \当符合条件时使用static主机组
        default_backend dynsrvs  \当不符合use_bckend条件时使用默认default_backend主机组

        backend dynsrvs \定义动态主机组
            balance roundrobin
            server dynsrv1 172.16.253.105:80 check cookie dynsrv1
            server dynsrv2 172.16.253.105:8080 check cookie dynsrv2
        backend staticsrvs  \定义静态主机组
            balance roundrobin
            server staticsrv1 172.16.253.191:80 check
            server staticsrv2 172.16.253.191:8080 check
[root@HAProxy ~]# systemctl restart haproxy

client

[root@client ~]# curl http://172.16.253.108/index.html
<h1> Image Server 1 </h1>
[root@client ~]# curl http://172.16.253.108/index.html
<h1> image Server 2 </h1>
[root@client ~]# curl http://172.16.253.108/index.php
<h1> Application Server 2</h1>
[root@client ~]# curl http://172.16.253.108/index.php
<h1> Application Server 2</h1>

拒绝curl访问web

HAProxy

[root@HAProxy ~]# vim /etc/haproxy/haproxy.cfg
    frontend myweb *:80
        cookie WEBSRV indirect nocache
        acl static path_end .jpg .jpeg .png .gif .txt .html \定义ACL的组static以.jpg .jpeg .png .gif .txt .html结尾的文件
        use_backend staticsrvs  if static  \当符合条件时使用static主机组
        default_backend dynsrvs  \当不符合use_bckend条件时使用默认default_backend主机组
        acl bad_browsers hdr_reg(User-Agent) .*curl.* \定义请求报文中包含curl的ACL组为bad_browsers
        block if bad_browsers \阻塞bad_browsers组的访问

        backend dynsrvs \定义动态主机组
            balance roundrobin
            server dynsrv1 172.16.253.105:80 check cookie dynsrv1
            server dynsrv2 172.16.253.105:8080 check cookie dynsrv2
        backend staticsrvs  \定义静态主机组
            balance roundrobin
            server staticsrv1 172.16.253.191:80 check
            server staticsrv2 172.16.253.191:8080 check
[root@HAProxy ~]# systemctl restart haproxy

client

[root@client ~]# curl http://172.16.253.108/index.html
<html><body><h1>403 Forbidden</h1>
Request forbidden by administrative rules.
</body></html>

定义仅允许danran.com域内的的主机访问

HAProxy

[root@HAProxy ~]# vim /etc/haproxy/haproxy.cfg
    frontend myweb *:80
        cookie WEBSRV indirect nocache
        acl static path_end .jpg .jpeg .png .gif .txt .html \定义ACL的组static以.jpg .jpeg .png .gif .txt .html结尾的文件
        use_backend staticsrvs  if static  \当符合条件时使用static主机组
        default_backend dynsrvs  \当不符合use_bckend条件时使用默认default_backend主机组
        acl valid_referers hdr_reg(Referer) .danran.com
        block unless valid_referers \阻塞除了valid_referers组之外的所有人的访问

        backend dynsrvs \定义动态主机组
            balance roundrobin
            server dynsrv1 172.16.253.105:80 check cookie dynsrv1
            server dynsrv2 172.16.253.105:8080 check cookie dynsrv2
        backend staticsrvs  \定义静态主机组
            balance roundrobin
            server staticsrv1 172.16.253.191:80 check
            server staticsrv2 172.16.253.191:8080 check
[root@HAProxy ~]# systemctl restart haproxy

client

模拟www.danran.com主机访问
[root@client ~]# curl -e "http://www.danran.com/index.php" http://172.16.253.108/index.php 
<h1> Application Server 2</h1>

hadoop集群时间同步

测试环境:

192.168.217.130 master master.hadoop
192.168.217.131 node1 node1.hadoop
192.168.217.132 node2 node2.hadoop

一、设置master服务器时间

查看本地时间和时区

[root@master ~]# date
Mon Feb 27 09:54:09 CST 2017

选择时区

[root@master ~]# tzselect

未分类

未分类

 [root@master ~]# cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

修改时间(date -s 00:00:00或者网络同步:apt-get install ntpdate ; ntpdate cn.pool.ntp.org)
写入硬盘时间(hwclock -w)

二、在master服务器上检查时间服务是否安装

[root@node1 ~]# rpm -q ntp
ntp-4.2.4p8-2.el6.x86_64

如果没有安装,用yum安装

[root@node1 ~]# yum install ntp

按上面的安装方式在内网每台服务器上都安装好NTP软件包。
完成后,都需要配置NTP服务为自启动

[root@master ~]# chkconfig ntpd on
[root@master ~]# chkconfig --list ntpd
ntpd           0:off1:off2:on3:on4:on5:on6:off

三、在master上更改相关配置文件

[root@master ~]# vim /etc/ntp.conf 

进行如下修改:

未分类

第一处新增,意思是从IP地址192.168.217.1-192.168.217.254,默认网关255.255.255.0的服务器都可以使用我们的NTP服务器来同步时间

第二处新增,指明互联网和局域网中作为NTP服务器的IP

第三处是修改,将原有注释去掉,是当服务器与公用的时间服务器失去联系时以本地时间为客户端提供时间服务

配置文件修改完成,保存退出,启动服务。

[root@master ~]#  service ntpd start

启动后,一般需要5-10分钟左右的时候才能与外部时间服务器开始同步时间。可以通过命令查询NTPD服务情况。

查看服务连接和监听

[root@master ~]# netstat -tlunp | grep ntp  
udp        0      0 192.168.217.130:123         0.0.0.0:*                               4990/ntpd           
udp        0      0 127.0.0.1:123               0.0.0.0:*                               4990/ntpd           
udp        0      0 0.0.0.0:123                 0.0.0.0:*                               4990/ntpd           
udp        0      0 fe80::20c:29ff:fee7:123     :::*                                    4990/ntpd           
udp        0      0 ::1:123                     :::*                                    4990/ntpd           
udp        0      0 :::123                      :::*                                    4990/ntpd 

重新启动服务

[root@master ~]# service ntpd restart

可设置crontab每天和NTP服务器同步一次(以和互联网时间同步为例)

[root@master ~]# crontab -l
10 23 * * * root (/usr/sbin/ntpdate cn.pool.ntp.org && /sbin/hwclock -w) &> /var/log/ntpdate.log

四、 将其他节点的时间与master进行同步

在其他每一个节点运行命令

[root@node1 ~]# ntpdate master
27 Feb 10:10:15 ntpdate[32724]: adjust time server 192.168.217.130 offset -0.170230 sec
[root@node2 ~]# ntpdate master
27 Feb 10:10:23 ntpdate[30874]: adjust time server 192.168.217.130 offset -0.149563 sec

这时候发现节点间的时间同步了,但ntpdate只在开机运行,我们若要设置为1小时同步一次

[root@node1 ~]# crontab -l
* */1 * * * /usr/sbin/ntpdate master

Docker Swarm + HAProxy 实现高可用

什么是高可用性?

  • 高可用性: High Availability (简称 HA)
  • 可用性 (Availability) = 可靠性 (Reliability) + 可维护性 (Maintainability)
  • 自动检测、自动切换、自动恢复
  • 主从方式、互备方式、集群方式

集群的三大核心概念

  • 集群 (Cluster)
  • 节点 (Node)
  • 服务 (Service)

本文将介绍 Docker Swarm + HAProxy 来实现服务的高可用性

环境信息

  • 本机 Mac
  • Docker Version 17.06.1-ce-mac24 (18950)

创建节点(虚拟机)

未分类

在 Mac 执行如下命令创建节点

$ docker-machine create manager1
$ docker-machine create work1
$ docker-machine create work2
$ docker-machine create work3

未分类

进入节点(虚拟机)

再开启另外的4个窗口,分别执行

$ docker-machine ssh manager1
$ docker-machine ssh work1
$ docker-machine ssh work2
$ docker-machine ssh work3

未分类

初始化集群

1、初始化 Manager

未分类

在 manager1 节点上执行

docker swarm init --advertise-addr 192.168.99.100`

未分类

命令成功后,会有提示,如何将 worker 加入集群,即 docker swarm … 这行命令。

提示:如果忘记此处的 token,可以使用 docker swarm join-token worker 命令查看。

2、将 work1,work2,work3 加入集群

未分类

在 work1, work2, work3 上分别执行,此行命令详见上一步中的提示

docker swarm join --token SWMTKN-1-1rt31l3671hig69vkfqha994rydnwr67gxt6o0suv71zzobpgn-b930rnc1pzewxv9h73plq58rd 192.168.99.100:2377

未分类

笔记:集群中加入worker的命令是

docker swarm join --token [token] [manager-ip]:[manager-port]

创建服务

此处以 nginx 为例。首先,创建有 2 个副本的 nginx 服务,在 manger1 上执行

$ docker service create --replicas 2 
-d 
-p 8080:80 
--name any-nginx 
registry.docker-cn.com/library/nginx

提示:此处用到了镜像加速仓库

执行完毕之后,查看服务列表信息

$ docker service ls

未分类

查看服务信息

$ docker service ps vv

笔记:查看服务信息命令 docker service ps [服务ID]

未分类

从服务信息可以看到 nginx 已运行在 manager1 和 work1 上

未分类

分别访问 http://192.168.99.100:8080 和 http://192.168.99.101:8080 ,会得到如下结果:

未分类

分别在 manager1 和 work1 上查看容器运行情况,结果如下:

未分类

服务的扩容与缩容

将 any-nginx 扩容为 3 份,在 manager1 上执行

$ docker service scale any-nginx=3

再次查看服务信息,可看到 any-nginx.3 在 work2 上运行

未分类

未分类

服务高可用测试

目前,有3个运行的容器保证服务的可用性,如果其中一个容器意外关闭,会发生什么情况呢?

将 work2 上正在运行的容器关闭,在 work2 上执行:

未分类

再次查看服务信息,可以看到 any-nginx.3 在 work2 上自动重启了

未分类

另外一个问题,如果 work2 宕机了,会发生什么情况?

在 Mac 上关闭 work2 节点:

$ docker-machine stop work2

未分类

未分类

不难看出 work2 节点关闭之后,work3 节点自动启动了 any-nginx.3,保证了整个集群运行的还是 3 个副本。

补充:若work1,work2,work3全部关闭,manager1 上将运行 3 个 any-nginx 容器。此处不演示。

总结:在内存允许的条件下,docker swarm 集群会稳定运行指定数量的容器,具有自我修复的能力,以此来保证服务的高可用性!

使用 HAProxy 来添加外部负载均衡

未分类

此处 HAProxy 将运行于本机,以下命令均在本机操作。

1、创建 HAProxy 配置文件 haproxy.cfg

$ vim haproxy.cfg
global
    daemon
    maxconn 25600
defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms
frontend http-in
    bind *:80
    default_backend servers
backend servers
    server server1 192.168.99.100:8080 maxconn 32 check
    server server2 192.168.99.101:8080 maxconn 32 check
    server server3 192.168.99.102:8080 maxconn 32 check
    server server4 192.168.99.103:8080 maxconn 32 check

2、创建 Dockerfile 自定义镜像

$ vim Dockerfile
FROM haproxy
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

3、制作镜像

$ docker build -t any-haproxy .

4、启动 HAProxy

$ docker run -d -p 8080:80 any-haproxy

未分类

5、测试,访问 http://127.0.0.1:8080/

未分类

Flask-Login 使用和进阶

在我们使用 Flask 构建一个系统时,用户登录注册是一个必不可少的过程,我们可以自己实现一个登录的功能,但是由于涉及的面很多,所以我们更多情况下还是寻求已有的模块来进行使用。

在 Flask 中比较常用的就是 Flask-Login 了,这里就以 Flask-Login 为例,给大家介绍一下如何使用Flask-Login 进行登录注销,以及帮助大家解答一些可能比较常见的问题。

使用入门

首先,我们先概述一下我们这里的例子,我们这个例子有三个 url,分别是:

/auth/login     用于登录
/auth/logout    用于注销
/test               用于测试,需要登录才能访问

好,这就是我们的前提概述了,下面我们就开始介绍。

安装必要的库

毫无疑问,我们要使用 Flask-Login ,那就必须安装它,安装使用 pip 还是很简单的,此外,因为我们登录涉及到登录表单,所以,还需要安装 wtform ,所以最后总共需要安装的有:

pip install Flask==0.10.1
pip install Flask-Login==0.3.2
pip install Flask-WTF==0.12
pip install WTForms==2.1

编写 web 框架

首先,在开始登录之前,我们先把整个 web 的框架搭建出来,也就是,我们要能够先在不登录的情况下访问到上面提到的三个url,这个架构比较简单了,我就直接放在一个叫做 app.py 的文件中了。

#!/usr/bin/env python
# encoding: utf-8
from flask import Flask, Blueprint

app = Flask(__name__)

# url redirect
auth = Blueprint('auth', __name__)

@auth.route('/login', methods=['GET', 'POST'])
def login():
    return "login page"

@auth.route('/logout', methods=['GET', 'POST'])
def logout():
    return "logout page"    

# test method
@app.route('/test')
def test():
    return "yes , you are allowed"

app.register_blueprint(auth, url_prefix='/auth')
app.run(debug=True)

现在,我们可以尝试一下运行一下这个框架,使用

python app.py

运行即可,然后打开浏览器,分别访问一下:

http://localhost:5000/test
http://localhost:5000/auth/login
http://localhost:5000/auth/logout

看一下是否都正常。

设置登录才能查看

现在框架已经设置完毕,那么我们就可以尝试一下设置登录需求的,也就是说我们将 test 和 auth/logout 这两个 page 设置成登录之后才能查看。因为这个功能已经和 login 有关系了,所以这时我们就需要使用到 Flask-Login 了。

我们可以这样来改变代码:

#!/usr/bin/env python
# encoding: utf-8
from flask import Flask, Blueprint
from flask.ext.login import LoginManager, login_required

app = Flask(__name__)

# 以下这段是新增加的============
app.secret_key = 's3cr3t'
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
    return None
# 以上这段是新增加的============

auth = Blueprint('auth', __name__)

@auth.route('/login', methods=['GET', 'POST'])
def login():
    return "login page"

@auth.route('/logout', methods=['GET', 'POST'])
@login_required
def logout():
    return "logout page"

# test method
@app.route('/test')
@login_required
def test():
    return "yes , you are allowed"

app.register_blueprint(auth, url_prefix='/auth')
app.run(debug=True)

其实我们就增加了两项代码,一项是初始化 LoginManager 的,另外一项就是给 test 和 auth.logout 添加了 login_required 的装饰器,表示要登录了才能访问。

你也许会有疑问:@login_manager.user_loader 这个装饰器是干嘛用的。这个在后面的 Question 中有详细得介绍,在这里我们只需要知道这个函数需要返回指定 id 的用户,如果没有就返回 None。这里因为设置框架所以就默认返回 None。

用户授权

到此,我们发现访问 test 是不能访问的,会被重定向到 login 的那个 page。那我们看一下我们现在的代码,我们发现 login_required 有了,那么就差 login 了,好,接下来就写 login,所以我们就来看看 Flask-Login 的文档,找找,我们会发现一个叫做:login_user 的函数,看看它的原型:

flask.ext.login.login_user(user, remember=False, force=False, fresh=True)

这里需要一个 user 的对象,所以我们就先构建一个 Model,其实,这个 Model 还是有一点讲究的,所以我们最好是继承自 Flask-Login 的 UserMixin , 然后需要实现几个方法, Model 为:

# user models
    class User(UserMixin):
        def is_authenticated(self):
            return True

        def is_actice(self):
            return True

        def is_anonymous(self):
            return False

        def get_id(self):
            return "1"

这里给所有的函数都返回了默认值,默认对应的情况是这个用户已经登录,并且是有效的。

然后在 login 的 view 里面 login_user,logout 的 view 里面 logout_user,这样整个登录过程就连接起来了,最后的代码是这样的:

#!/usr/bin/env python
    # encoding: utf-8
    from flask import Flask, Blueprint
    from flask.ext.login import (LoginManager, login_required, login_user,
                                 logout_user, UserMixin)

    app = Flask(__name__)


    # user models
    class User(UserMixin):
        def is_authenticated(self):
            return True

        def is_actice(self):
            return True

        def is_anonymous(self):
            return False

        def get_id(self):
            return "1"

    # flask-login
    app.secret_key = 's3cr3t'
    login_manager = LoginManager()
    login_manager.session_protection = 'strong'
    login_manager.login_view = 'auth.login'
    login_manager.init_app(app)

    @login_manager.user_loader
    def load_user(user_id):
        user = User()
        return user

    auth = Blueprint('auth', __name__)

    @auth.route('/login', methods=['GET', 'POST'])
    def login():
        user = User()
        login_user(user)
        return "login page"

    @auth.route('/logout', methods=['GET', 'POST'])
    @login_required
    def logout():
        logout_user()
        return "logout page"

    # test method
    @app.route('/test')
    @login_required
    def test():
        return "yes , you are allowed"

    app.register_blueprint(auth, url_prefix='/auth')
    app.run(debug=True)

Summary

到此,这就是一个比较精简的 Flask-Login 教程了,通过这个框架大家可以自行扩展,达到更丰富的功能,后续会连续这个 Login 功能继续讲解一下权限控制。

Question

未登录访问鉴权页面如何处理?

如果未登录访问了一个作了 login_required 限制的 view,那么 Flask-Login 会默认 flash一条消息,并且将重定向到 log in view,如果你没有指定 log in view,那么 Flask-Login 将会抛出一个 401 错误。

如何指定 log in view

指定 log in view 只需要直接设置 login_manager 即可:

login_manager.login_view = "auth.login"

如何自定义 flash 消息

如果需要自定义 flash 的消息,那么还是简单设置 login_manager,

login_manager.login_message = u"请登录!"

还可以设置 flash 消息的级别,一般设置成 info 或者 error:

login_manager.login_message_category = "info"

自定义未登录处理函数

如果你不想使用默认的规则,那么你也可以自定义未登录情况的处理函数,只需要使用 login_manager 的 unauthorized_handler 装饰器即可。

@login_manager.unauthorized_handler
    def unauthorized():
        # do stuff
        return render_template("some template")

匿名用户是怎么处理的?有哪些属性?

在 Flask-Login 中,如果一个匿名用户访问站点,那么 current_user 对象会被设置成一个 AnonymousUserMixin 的对象,AnonymousUserMixin 对象有以下方法和属性:

  • is_active and is_authenticated are False
  • is_anonymous is True
  • get_id() returns None

自定义匿名用户 Model

如果你有需求自定义匿名用户的 Model,那么你可以通过设置 login_manager 的 anonymous_user 属性来实现,而赋值的对象只需是可调用对象(class 和 function都行)即可。

login_manager.anonymous_user = MyAnonymousUser

Flask-Login 如何加载用户的

当一个请求过来的时候,如果 ctx.user 没有值,那么 Flask-Login 就会使用 session 中 session[‘user_id’] 作为参数,调用 login_manager 中使用 user_loader 装饰器设置的 callback 函数加载用户,需要注意的是,如果指定的 user_id 无效,不应该抛出异常,而是应该返回 None。

@login_manager.user_loader
    def load_user(user_id):
        return User.get(user_id)

session[‘user_id’] 其实是在调用 login_in 函数之后自动设置的。

如何控制 Flask-Login 的 session 过期时间

在 Flask-Login 中,如果你不特殊处理的话,session 是在你关闭浏览器之后就失效的。也就是说每次重新打开页面都是需要重新登录的。

如果你需要自己控制 session 的过期时间的话,

  1. 首先需要设置 login_manager 的 session类型为永久的,
  2. 然后再设置 session 的过期时间
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=5)

同时,还需要注意的是 cookie 的默认有效期其实是 一年 的,所以,我们最好也设置一下:

login_manager.remember_cookie_duration=timedelta(days=1)

如何在同域名下的多个系统共享登录状态

这个需求可能在公司里面会比较常见,也就是说我们一个公司域名下面会有好多个子系统,但是这些子系统都是不同部门开发的,那么,我们如何在这不同系统间共享登录状态?也就是说,只要在某一个系统登录了,在使用其他系统的时候也共享着登录的状态,不需要再次登录,除非登录失效。

Centos下安装 PHP7.0.2

PHP7 已经出来挺长一段时间了,不过还一直使用较低版本的 PHP。最近有一台闲置的 VPS,准备用来建一个简单的动态页面,便想尝试下 PHP7 的一些新特性。以下简单的记录下安装步骤。

一、安装前准备(安装编译工具及库文件)

yum install make apr* autoconf automake curl-devel gcc gcc-c++ gtk+-devel zlib-devel openssl openssl-devel pcre-devel gd gettext gettext-devel kernel keyutils patch perl kernel-headers compat* mpfr cpp glibc libgomp libstdc++-devel ppl cloog-ppl keyutils-libs-devel libcom_err-devel libsepol-devel libselinux-devel krb5-devel  libXpm* freetype freetype-devel freetype* fontconfig fontconfig-devel libjpeg* libpng* php-common php-gd ncurses* libtool* libxml2 libxml2-devel patch

安装前说明 : PHP7安装包存放路径 /usr/local/src , PHP7安装目录 /usr/local/php7

二、下载 PHP7

wget http://cn2.php.net/distributions/php-7.0.2.tar.gz

解压&进入目录

tar -zxvf php-7.0.2.tar.gz

cd php-7.0.2

三、生成 Makefile

./configure --prefix=/usr/local/php7 --enable-fpm --with-fpm-user=nginx --with-fpm-group=nginx --with-mysqli --with-zlib --with-curl --with-gd --with-jpeg-dir --with-png-dir --with-freetype-dir --with-openssl --enable-mbstring --enable-xml --enable-session --enable-ftp --enable-pdo -enable-tokenizer --enable-zip

四、编译

make

如果上面 make 出现如下错误:

cc: Internal error: Killed (program cc1)

Please submit a full bug report.

See <http://bugzilla.redhat.com/bugzilla> for instructions.

make: *** [ext/fileinfo/libmagic/apprentice.lo] Error 1

原因是内存不够,一般内存小的 VPS 可能碰到该问题,内存足够一般不会碰到该问题。解决方法是在 configure 时加上 –disable-fileinfo 参数(disable前面是两个 – ), 你需要重新执行 configure 操作,然后再进行 make 编译 。

如果 make 出现下面这中错误:

collect2: ld returned 1 exit status

make: *** [sapi/cli/php] Error 1

解决办法:

make ZEND_EXTRA_LIBS='-liconv'

ln -s /usr/local/lib/libiconv.so.2   /usr/lib64/

五、安装

make install

六、操作配置文件

cp /usr/local/src/php-7.0.2/php.ini-development  /usr/local/php7/lib/php.ini

cp /usr/local/php7/etc/php-fpm.conf.default /usr/local/php7/etc/php-fpm.conf

cp /usr/local/php7/etc/php-fpm.d/www.conf.default /usr/local/php7/etc/php-fpm.d/www.conf

cp /usr/local/src/php-7.0.2/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm

chmod +x /etc/init.d/php-fpm

七、启动 php

/etc/init.d/php-fpm start

CentOS 6.x 上搭建docker

Docker是一个能够把开发应用程序自动部署到容器的开源引擎。它由Docker公司的团队编写,基于Apache 2.0开源协议授权。它提供了一个简单、轻量的建模方式,使开发生命周期更高效快速,鼓励了面向服务的架构设计。

一、前提条件

1、内核

Docker 运行环境对内核要求比较高,一般建议直接在Ubuntu这样的平台上运行。但作为一个容器标准,Docker也支持其他如 CentOS,Mac OS X,Windows等其他平台。
目前Docker支持以下Red Hat和Red Hat系发行版:(不支持32位)

  • (RHEL|CentOS) 6.5及以上版本(64位)
  • Fedora 19及以上版本(64位)

在运行于这些系统时,需要 内核版本 >= 3.8 ,因为这些内核包含了运行Docker的一些特定修改。
查看内核版本:

# uname -r
2.6.32-431.el6.x86_64

注意:3.8 > 内核版本 >= 2.6.32-431 这些内核版本虽然也能支持Docker运行,但是支持得不好,仍会在运行时出现很多bug。推荐 内核版本 >= 3.8 。

2、检查Device Mapper

Docker默认使用AUFS作为存储驱动,但是AUFS并没有被包括在Linux的主线内核中。CentOS中可以使用Device Mapper作为存储驱动,这是在2.6.9内核版本引入的新功能。
需要先确认是否启用该功能:

# ls -l /sys/class/misc/device-mapper
lrwxrwxrwx 1 root root 0 8月  31 17:33 /sys/class/misc/device-mapper -> ../../devices/virtual/misc/device-mapper

如果没有检测到Device Mapper,需要安装其软件包:

# yum install device-mapper -y

然后重新加载 dm_mod 内核模块:

# modprobe dm_mod

二、升级内核版本

1、查看当前内核版本

# cat /etc/redhat-release
CentOS release 6.5 (Final)
# uname -r
2.6.32-431.el6.x86_64

CentOS 6.5 系统默认的内核版本是 2.6.32-431.el6.x86_64,不符合Docker的安装条件,需要升级内核版本。

2、升级内核版本至3.10.0

在yum的 ELRepo 源中,有 mainline(3.13.x)、long-term(3.10.x)这2个内核版本,考虑到long-term更稳定,会长期更新,所以选择这个版本。

导入 Public key

# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org

安装ELRepo源

# rpm -ivh http://www.elrepo.org/elrepo-release-6-8.el6.elrepo.noarch.rpm

安装kernel-lt 或 kernel-ml(任选一个)

# kernel-lt,内核版本:3.10.x
# yum --enablerepo=elrepo-kernel install kernel-lt -y
# 或者kernel-ml,内核版本:3.13.x
# yum --enablerepo=elrepo-kernel install kernel-ml -y

编辑 grub.conf 文件,修改 Grub 引导顺序

# cat /etc/grub.conf
device (hd0) HD(1,800,64000,ab0fd74d-ecf9-42ff-8c69-0fa5d3af4fd2)
default=1
timeout=5
splashimage=(hd0,1)/grub/splash.xpm.gz
hiddenmenu
title CentOS (3.10.107-1.el6.elrepo.x86_64)
    root (hd0,1)
    kernel /vmlinuz-3.10.107-1.el6.elrepo.x86_64 ro root=/dev/mapper/vg_localhost-lv_root rd_NO_LUKS rd_NO_MD rd_LVM_LV=vg_localhost/lv_swap crashkernel=128M LANG=zh_CN.UTF-8 rd_LVM_LV=vg_localhost/lv_root  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
    initrd /initramfs-3.10.107-1.el6.elrepo.x86_64.img
title CentOS (2.6.32-431.el6.x86_64)
    root (hd0,1)
    kernel /vmlinuz-2.6.32-431.el6.x86_64 ro root=/dev/mapper/vg_localhost-lv_root rd_NO_LUKS rd_NO_MD rd_LVM_LV=vg_localhost/lv_swap crashkernel=128M LANG=zh_CN.UTF-8 rd_LVM_LV=vg_localhost/lv_root  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_DM rhgb quiet
    initrd /initramfs-2.6.32-431.el6.x86_64.img

确认刚安装的内核在哪个位置,然后设置default值(从0开始)。一般新安装的内核会在第一个位置,所以设置 default=0。

重启服务器,查看内核版本

# reboot
# uname -r
3.10.107-1.el6.elrepo.x86_64

三、安装Docker

对于CentOS 6.5,Docker可以在 EPEL 源中找到,安装 EPEL 源

# rpm -Uvh http://www.gtlib.gatech.edu/pub/fedora-epel/6/i386/epel-release-6-8.noarch.rpm

删除CentOS 6.5默认自带的Docker

# yum remove docker -y

安装docker-io的RPM包

# yum install docker-io -y

查看Docker版本

# docker version
Client version: 1.7.1
Client API version: 1.19
Go version (client): go1.4.2
Git commit (client): 786b29d/1.7.1
OS/Arch (client): linux/amd64
Server version: 1.7.1
Server API version: 1.19
Go version (server): go1.4.2
Git commit (server): 786b29d/1.7.1
OS/Arch (server): linux/amd64

启动Docker服务

# service docker start