理解OpenShift(1):网络之 Router 和 Route

理解OpenShift(2):网络之 DNS(域名服务)

理解OpenShift(3):网络之 SDN

理解OpenShift(4):用户及权限管理

理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume

** 本文基于 OpenShift 3.11,Kubernetes 1.11 进行测试 ***

1. OpenShift 为什么需要 Router 和 Route?

顾名思义,Router 是路由器,Route 是路由器中配置的路由。OpenShift 中的这两个概念是为了解决从集群外部(就是从除了集群节点以外的其它地方)访问服务的需求。不晓得为什么OpenShift 要将Kubernetes 中的 Ingress 改为 Router,我倒是觉得 Ingress 名字更贴切。

从外部通过 router 和从内部通过 servide 访问 pod 中的应用两个过程的简单的示意图如下:

上图中,某个应用的三个pod 分别位于 node1,node2 和 node3 上。OpenShift 中有三层IP地址概念:

  • pod 自己的 IP 地址,可以类比为 OpenStack 中虚拟机的固定IP。它只有在集群内才有意义。
  • service 的 IP 地址。Service 通常有 ClusterIP,这也是一种集群内部的IP 地址。
  • 应用的外部 IP 地址,可以类比为OpenStack 中的浮动IP,或者IDC IP(和浮动IP 之间是NAT 映射关系)。

因此,要从集群外部访问 pod 中的应用,无非两种方式:

  • 一种是利用一个代理(proxy),把外部 IP 地址转化为后端的 Pod IP 地址。这就是 OpenShift router/route 的思路。OpenShift 中的 router 服务,是一个运行在特定节点(通常是基础架构节点)上的集群基础服务,由集群管理员负责创建和管理。它可以有多个副本(pod)。router 中可有多个 route,每个 route 能通过外部HTTP 请求的域名找出其后端的 pod 列表,并进行网络包的转发。也就是将pod 中的应用暴露到外网域名,使得用户可以外面通过域名访问到应用。这实际上是一种七层负载均衡器。OpenShift 默认采用 HAProxy 来实现,当然也支持其它实现,比如 F5.
  • 另一种是将服务直接暴露到集群外。这种方式具体会在『服务 Service』那一篇文章中详细解释。

2. OpenShift 如何利用 HAProxy 实现 router 和 route?

2.1 Router 部署

使用 ansible 采用默认配置部署 OpenShift 集群时,在集群 Infra 节点上,会以 Host networking 方式运行一个 HAProxy 的 pod,它会在所有网卡的 80 和 443 端口上进行监听。

[root@infra-node3 cloud-user]# netstat -lntp | grep haproxy
tcp 127.0.0.1: 0.0.0.0:* LISTEN /haproxy
tcp 127.0.0.1: 0.0.0.0:* LISTEN /haproxy
tcp 0.0.0.0: 0.0.0.0:* LISTEN /haproxy
tcp 0.0.0.0: 0.0.0.0:* LISTEN /haproxy

其中,172.0.0.1 上的 10443 和 10444 是HAproxy 自己使用的。下文会有解释。

因此,在每个 infra 节点上,只能有一个 HAProxy pod,因为这些端口只能被占用一次。如果调度器找不到满足要求的节点,则router 服务的调度就会失败:

/ nodes are available:  node(s) didn't have free ports for the requested pod ports, 5 node(s) didn't match node selector

OpenShift HAProxy Router 支持两种部署方式:

  • 一种是常见的单Router 服务部署,它有一个或多个实例(pod),分布在多个节点上,负责整个集群上部署的服务的对外访问。
  • 另一种是分片(sharding)部署。此时,会有多个 Router 服务,每个Router 服务负责指定的若干project,两者之间采用标签(label)进行映射。这是为了解决单个 Router 的性能不够问题而提出的解决方案。

OpenShift 提供了 oc adm router 命令来创建 router 服务。

创建router:

[root@master1 cloud-user]# oc adm router router2 --replicas= --service-account=router
info: password for stats user admin has been set to J3YyPjlbqf
--> Creating router router2 ...
warning: serviceaccounts "router" already exists
clusterrolebinding.authorization.openshift.io "router-router2-role" created
deploymentconfig.apps.openshift.io "router2" created
service "router2" created
--> Success

详细的部署方法请参见官方文档 https://docs.openshift.com/container-platform/3.11/install_config/router/default_haproxy_router.html。

2.2 Router pod 中的 HAProxy 进程

在 Router 服务的每个 pod 之中,openshift-router 进程启动了一个 haproy 进程:

UID        PID  PPID  C STIME TTY          TIME CMD
+ Nov21 ? :: /usr/bin/openshift-router
+ : ? :: /usr/sbin/haproxy -f /var/lib/haproxy/conf/haproxy.config -p /var/lib/haproxy/run/haproxy.pid -x /var/lib/haproxy/run/haproxy.sock -sf

查看 haproxy 使用的配置文件(只是部分):

global
maxconn
daemon
ca-base /etc/ssl
crt-base /etc/ssl
。。。。 defaults
maxconn # Add x-forwarded-for header. # server openshift_backend 127.0.0.1:
errorfile /var/lib/haproxy/conf/error-page-.http 。。。
timeout http-request 10s
timeout http-keep-alive 300s # Long timeout for WebSocket connections.
timeout tunnel 1h frontend public bind :
mode http
tcp-request inspect-delay 5s
tcp-request content accept if HTTP
monitor-uri /_______internal_router_healthz # Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
http-request del-header Proxy # DNS labels are case insensitive (RFC ), we need to convert the hostname into lowercase
# before matching, or any requests containing uppercase characters will never match.
http-request set-header Host %[req.hdr(Host),lower] # check if we need to redirect/force using https.
acl secure_redirect base,map_reg(/var/lib/haproxy/conf/os_route_http_redirect.map) -m found
redirect scheme https if secure_redirect use_backend %[base,map_reg(/var/lib/haproxy/conf/os_http_be.map)] default_backend openshift_default # public ssl accepts all connections and isn't checking certificates yet certificates to use will be
# determined by the next backend in the chain which may be an app backend (passthrough termination) or a backend
# that terminates encryption in this router (edge)
frontend public_ssl bind :
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type } # if the connection is SNI and the route is a passthrough don't use the termination backend, just use the tcp backend
# for the SNI case, we also need to compare it in case-insensitive mode (by converting it to lowercase) as RFC says
acl sni req.ssl_sni -m found
acl sni_passthrough req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_sni_passthrough.map) -m found
use_backend %[req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_tcp_be.map)] if sni sni_passthrough # if the route is SNI and NOT passthrough enter the termination flow
use_backend be_sni if sni # non SNI requests should enter a default termination backend rather than the custom cert SNI backend since it
# will not be able to match a cert to an SNI host
default_backend be_no_sni 。。。
backend be_edge_http:demoprojectone:jenkins
mode http
option redispatch
option forwardfor
balance leastconn
timeout server 4m timeout check 5000ms
http-request set-header X-Forwarded-Host %[req.hdr(host)]
http-request set-header X-Forwarded-Port %[dst_port]
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto-Version h2 if { ssl_fc_alpn -i h2 }
http-request add-header Forwarded for=%[src];host=%[req.hdr(host)];proto=%[req.hdr(X-Forwarded-Proto)];proto-version=%[req.hdr(X-Forwarded-Proto-Version)]
cookie 4376ea64d7d0abf11209cfe5f7cca1e7 insert indirect nocache httponly secure
server pod:jenkins--84nrt:jenkins:10.128.2.13: 10.128.2.13: cookie 8669a19afc9f0fed6824feb9fb1cf4ac weight 。。。

为了简单期间,上面只是配置文件的部分内容,它主要包括三种类型:

  • 全局配置,比如最大连接数 maxconn,超时时间 timeout 等;以及front部分,即前端配置,HAProxy 默认会在 443 和 80 两个端口上分别监听外部 https 和 http 请求。
  • backend,即每个服务的后端配置,里面有很多关键内容,比如后端协议(mode)、负载均衡方法(balance)、后端列表(server,这里是pod,包括其IP 地址和端口)、证书等。

因此,OpenShift 的路由器功能需要能对这三部分进行管理和控制。

关于负载均衡器和 HAProxy 的详细介绍,可以参考 Neutron 理解 (7): Neutron 是如何实现负载均衡器虚拟化的 这篇文章。

2.3 全局配置管理

要指定或修改 HAProxy 的全局配置,OpenShift 有提供两种方式:

(1)第一种是使用 oc adm router 命令在创建 router 时候指定各种参数,比如 --max-connections 用于设置最大连接数。比如:

oc adm router --max-connections= --ports='81:80,444:443' router3

创建出来的HAProxy 的 maxconn 将是 20000,router3 这个服务对外暴露出来的端口是 81 和 444,但是 HAProxy pod 的端口依然是 80 和 443.

(2)通过设置 dc/<dc router名> 的环境变量来设置 router 的全局配置。

在官方文档 https://docs.openshift.com/container-platform/3.4/architecture/core_concepts/routes.html#haproxy-template-router 中有完整的环境变量列表。比如运行以下命令后,

 oc set env dc/router3 ROUTER_SERVICE_HTTPS_PORT= ROUTER_SERVICE_HTTP_PORT= STATS_PORT=

router3 会重新部署,新部署的HAProxy 的 https 监听端口是 444,http 监听端口是 80,统计端口是 1937.

2.4 OpenShift passthrough 类型的 route 与 HAProxy backend

(1)通过OpenShift Console 或者 oc 命令创建一条 route,它将 sit 项目的 jenkins 服务暴露到域名 sitjenkins.com.cn:

在界面上创建 route:

结果:

Name:                   sitjenkins.com.cn
Namespace: sit
Labels: app=jenkins-ephemeral
template=jenkins-ephemeral-template
Annotations: <none>
Requested Host: sitjenkins.com.cn
Path: <none>
TLS Termination: passthrough
Endpoint Port: web Service: jenkins
Weight: (%)
Endpoints: 10.128.2.15:, 10.131.0.10: 

这里,service name 起了一个中介作用,把 route 和服务的端点(也就是pod)连接了起来。

(2)router 服务的两个 pod 中的 HAProxy 进程的配置文件中多了一个backend:

# Secure backend, pass through
backend be_tcp:sit:sitjenkins.com.cn
balance source hash-type consistent
timeout check 5000ms}
server pod:jenkins--bqhfj:jenkins:10.128.2.15: 10.128.2.15: weight check inter 5000ms
server pod:jenkins--h2fff:jenkins:10.131.0.10: 10.131.0.10: weight check inter 5000ms

其中,这些后端 server 其实就是 pod,它们是 openshift 通过步骤(1)中的 service name 找到的。balance 是负载均衡策略,后文会解释。

(3)文件 /var/lib/haproxy/conf/os_sni_passthrough.map 中多了一条记录

sh-4.2$ cat /var/lib/haproxy/conf/os_sni_passthrough.map
^sitjenkins\.com\.cn(:[-]+)?(/.*)?$

(4)文件 /var/lib/haproxy/conf/os_tcp_be.map 中多了一条记录

sh-4.2$ cat /var/lib/haproxy/conf/os_tcp_be.map
^sitjenkins\.com\.cn(:[-]+)?(/.*)?$ be_tcp:sit:sitjenkins.com.cn

(5)HAProxy 根据上面的 map 文件为该条 route 选择第(2)步中增加的 backend的逻辑如下

frontend public_ssl  #解释:前端协议 https,

  bind :443  ##前端端口 443
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type } # if the connection is SNI and the route is a passthrough don't use the termination backend, just use the tcp backend
# for the SNI case, we also need to compare it in case-insensitive mode (by converting it to lowercase) as RFC says
acl sni req.ssl_sni -m found ##检查 https request 支持 sni
acl sni_passthrough req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_sni_passthrough.map) -m found ##检查通过 sni 传来的 hostname 在 os_sni_patthrough.map 文件中
use_backend %[req.ssl_sni,lower,map_reg(/var/lib/haproxy/conf/os_tcp_be.map)] if sni sni_passthrough ##从 oc_tcp_be.map 中根据 sni hostname 获取 backend name # if the route is SNI and NOT passthrough enter the termination flow
use_backend be_sni if sni # non SNI requests should enter a default termination backend rather than the custom cert SNI backend since it
# will not be able to match a cert to an SNI host
default_backend be_no_sni

(6)HAPorxy 进程会重启,从而应用修改了的配置文件。

理解(5)中的脚本需要的一些背景知识:

从上面的蓝色注释中,我们能看到 HAProxy 进程通过 https 请求中通过 SNI 传入的域名 sitjenkins.com.cn ,在 os_tcp_be.map 文件中获取到了 backend 名称 be_tcp:sit:sitjenkins.com.cn,这样就和(2)步骤中的 backend 对应上了。

OpenShift 的 router 使用的 HAProxy 采用基于域名的负载均衡路由方式,示例如下,具体说明请参加官方文档。

2.5 OpenShift edge 和 re-encrypt 类型的 route 与 HAProxy

HAProxy 前端:前端依然是在 443 端口监听外部 HTTPS 请求

frontend public_ssl
bind :443
.....
# if the route is SNI and NOT passthrough enter the termination flow
use_backend be_sni if sni

但是,当 TLS 终止类型不是 passthrough (edge 或者 re-encrypt)时,会使用backend be_sni。

backend be_sni
server fe_sni 127.0.0.1: weight send-prox

而这个后端是由本机的 127.0.0.1:10444 提供服务,因此又转到了前端 fe_sni:

frontend fe_sni
# terminate ssl on edge
bind 127.0.0.1: ssl no-sslv3 crt /var/lib/haproxy/router/certs/default.pem crt-list /var/lib/haproxy/conf/cert_config.map accept-proxy
mode http
。。。。。。 # map to backend
# Search from most specific to general path (host case).
# Note: If no match, haproxy uses the default_backend, no other
# use_backend directives below this will be processed.
use_backend %[base,map_reg(/var/lib/haproxy/conf/os_edge_reencrypt_be.map)] default_backend openshift_default

map 映射文件:

sh-4.2$ cat /var/lib/haproxy/conf/os_edge_reencrypt_be.map
^edgejenkins\.com\.cn(:[-]+)?(/.*)?$ be_edge_http:sit:jenkins-edge

Edge 类型 route 的 HAProxy 后端:

backend be_edge_http:sit:jenkins-edge
mode http
option redispatch
option forwardfor
balance leastconn timeout check 5000ms
.....
server pod:jenkins--bqhfj:jenkins:10.128.2.15: 10.128.2.15: cookie 71c6bd03732fa7da2f1b497b1e4c7993 weight check inter 5000ms
server pod:jenkins--h2fff:jenkins:10.131.0.10: 10.131.0.10: cookie fa8d7fb72a46958a7add1406e6d26cc8 weight check inter 5000ms

Re-encrypt 类型 route 的 HAProxy 后端:

# Plain http backend or backend with TLS terminated at the edge or a
# secure backend with re-encryption.
backend be_secure:sit:reencryptjenkins.com.cn
mode http
。。。。

http-request set-header X-Forwarded-Host %[req.hdr(host)]
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    http-request set-header X-Forwarded-Proto-Version h2 if { ssl_fc_alpn -i h2 }

  server pod:jenkins--bqhfj:jenkins:10.128.2.15: 10.128.2.15: cookie ... weight  ssl verifyhost jenkins.sit.svc verify required ca-file /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt check inter 5000ms #与后端的链路采用 ssl 加密,并且要检查hostname
server pod:jenkins--h2fff:jenkins:10.131.0.10: 10.131.0.10: cookie ... weight ssl verifyhost jenkins.sit.svc verify required ca-file /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt check inter 5000ms

这里可以看出来重新使用密钥对连接进行加密,但是不知道为何 mode 依然是 http,而不是 https。

2.6 设置和修改 route 配置

route 配置主要有以下几个比较重要的:

(1)SSL 终结方式。共三种:

  • edge:TLS 在 router 上被终结,然后非SSL网络包被转发给后端 pod。因此需要在 router 上安装 TLS 证书。不安装的话,会使用 router 的默认证书。
  • passthrough:加密网络包直接被发给 pod,router 上不做TLS 终结,因为不需要在 router 上配置证书或密钥。
  • Re-encryption:是 edge 的一种变种。首先 router 上会使用一个证书做 TSL 终结,然后使用另外的证书再进行加密,然后发给后端 pod。因此,整个网络路径都是加密的。

设置:

(2)负载均衡策略。也有三种:

  • roundrobin:根据权重轮流使用所有后端。
  • leastconn:选择最少连接的后端接收请求。
  • source:将源IP进行哈希,确保来自同一个源IP的请求发给同一个后端。
设置:
  • 要修改整个 router 的负载均衡策略,可使用 ROUTER_TCP_BALANCE_SCHEME 环境变量,为该 router 的所有 passthrough 类型的 route设置负载均衡策略,使用 ROUTER_LOAD_BALANCE_ALGORITHM 为其它类型的 route 设置策略。
  • 可以使用 haproxy.router.openshift.io/balance 为某个 route 设置负载均衡策略。

举例:

  • 设置整个 router 的环境变量:oc set env dc/router ROUTER_TCP_BALANCE_SCHEME=roundrobin
改完以后,该 router 实例会重新部署,所有 passthrough 的 route 都是 roundrobin 类型的了。默认为 source 类型。
  • 修改某个 route 的负载均衡的策略:oc edit route aaaa.svc.cluster.local
aaarticlea/png;base64,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" alt="" />
修改完成后,HAProxy 中对应该 route 的 backend 中的 balance 值会被修改为 leastconn。

2.7 一个 route 将流量分给多个后端服务

该功能常用于一些开发测试流程,比如做A/B 测试。

在下面的配置中,有一个应用三个版本的部署,前端一个 route,各服务使用不同的权重。

下面是 HAProxy 配置文件中的 backend 配置,采用 roundrobin 负载均衡模式:

 

3. OpenShift router 服务如何实现高可用?

OpenShift router 服务支持两种高可用模式。

3.1 单 router 服务多副本,并利用和DNS/LB 实现高可用

这种模式只部署一个 router 服务,它支持集群的所有对外暴露的服务。要实现HA,需要设置副本数(replicas)大于1,使得会在超过一台服务器上创建pod,然后再通过DNS轮询或者四层负载均衡。

因为 router/pod 中的 HAProxy 要实现本地配置文件,因此实际上它们是有状态容器。OpenShift 采用 etcd 作为配置的统一存储,openshift-router 进程应该是采取某种机制(被通知或定时拉取)从 etcd 中获取 router 和 route 的配置,然后再修改本地的配置文件,再重启 HAPorxy 进程来应用新修改了的配置文件。 要深入了解这里面的工作原理,可以去看源代码。

因为master 上的服务也需要有LB(8443端口),router 服务也需要LB(80和443端口)。因此,要么采用两个LB:

图片来源

要么采用一个LB 来支持 master 上的服务和 router 服务:

图片来源

3.2 多 router 服务通过分片(sharding)实现高可用

这种模式下,管理员需要创建和部署多个 router 服务,每个router 服务支持一个或几个 project/namespace。router 和 project/namespace 之间的映射使用标签(label)来实现。具体的配置请参考官网 https://docs.openshift.com/container-platform/3.11/install_config/router/default_haproxy_router.html。实际上,和一些产品(比如mysql,memedcache)的分片功能类似,该功能更多地是为了解决性能问题,而无法完全解决高可用问题。

4. 常见问题如何排查?

从上面的分析可以看出,要使得 router 和 route 都正常工作,至少要确保以下几个环节都是没问题的:

  1. 客户端使用 route 中配置的域名和端口来访问服务。
  2. DNS 能将域名解析到目标 router 所在的服务器(在使用分片配置时比较复杂,尤其需要注意)。
  3. 如有采用另外的四层负载均衡器的话,它得配置正确、工作正常。
  4. HAProxy 能通过域名匹配到正确的backend。
  5. router 和 route 的配置被正确地反映到了 HAProxy 的配置文件中了。
  6. HAProxy 进程重启了,从而读取了新修改的配置文件。
  7. 后端 pod 列表正确,并且至少有一个 pod 正常工作。

如果您看到如下的错误页面,则说明上面的第3到7点至少有一处不能正常功能。此时,进行有针对性的排查即可。

感谢您的阅读,欢迎关注我的微信公众号:

 

理解OpenShift(1):网络之 Router 和 Route的更多相关文章

  1. 理解OpenShift(2):网络之 DNS(域名服务)

    理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...

  2. 理解OpenShift(3):网络之 SDN

    理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...

  3. 理解OpenShift(7):基于 Prometheus 的集群监控

    理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...

  4. 理解OpenShift(6):集中式日志处理

    理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...

  5. 理解OpenShift(5):从 Docker Volume 到 OpenShift Persistent Volume

    理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...

  6. 理解OpenShift(4):用户及权限管理

    理解OpenShift(1):网络之 Router 和 Route 理解OpenShift(2):网络之 DNS(域名服务) 理解OpenShift(3):网络之 SDN 理解OpenShift(4) ...

  7. 【react router路由】<Router> <Siwtch> <Route>标签

    博客 https://www.jianshu.com/p/ed5e56994f13?from=timeline 文档 http://react-guide.github.io/react-router ...

  8. 深入理解 Neutron -- OpenStack 网络实现(4):网络名字空间

    问题导读1.如何查看网络名字空间?2.网络名字空间开头的名字有什么规律?3.dhcp服务是如何实现的?4.router的实现是通过iptables进行的是否正确?5.SNAT和DNAT规则有什么作用? ...

  9. 深入理解 Neutron -- OpenStack 网络实现(3):VXLAN 模式

    问题导读1.VXLAN 模式下,网络的架构跟 GRE 模式类似,他们的不同点在什么地方?2.网络节点的作用是什么?3.tap-xxx.qr-xxx是指什么? 接上篇:深入理解 Neutron -- O ...

随机推荐

  1. 使用mint-ui中弹框组件与原生弹框阻止父页面不滑动方法

    1,使用mint-ui框架中<mt-popup></mt-popup>,在组件中加入 lockScroll="true" 阻止父页面不滑动. 2,原生弹框中 ...

  2. CentOS 每个版本的区别

    当我们下载CentOS 7 时会发现有几个版本可以选择,如下: 1.CentOS-7-DVD版本:DVD是标准安装盘,一般下载这个就可以了. 2.CentOS-7-NetInstall版本:网络安装镜 ...

  3. 201621123075 week5 继承、多态、抽象类和接口

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 接口.abstract.implements.comparable 1.2 尝试使用思维导图将这些关键词组织起来.注:思维导图一 ...

  4. react native 环境报错

    按照react native中文网的文档安装 1.brew 管理软件 2.node 在终端启动工程后就报下面这个错误 这个错误是在RN在第一次配置环境启动一个工程的时候 ,在这过程中下载的缓存文件不完 ...

  5. useradd语法

    在Linux中 useradd 命令用来创建或更新用户信息. useradd 命令属于比较难用的命令 (low level utility for adding users),所以 Debian 系的 ...

  6. 宝塔面板Windows 面板5.0 memcache安装

    a 软件管理>相应的PHP版本里(php5.6)>>配置>配置修改>编辑框拉到底 ;extension=php_memcache.dll 前的“;”号去掉>保存&g ...

  7. python爬虫挂代理

    以下是GET的方法,使用的代理接口网站是 http://www.xicidaili.com/nn/ #-*- coding:utf-8 -*- from bs4 import BeautifulSou ...

  8. iOS开发 SVN回退到某一个版本

    1.选择你要恢复的工程,(注意:从SVN上checkout下来的工程不要改变,移动位置) 2.找到SVN的导航栏,点击“Working Copy”--->"Revert" 3 ...

  9. Git版本库创建(包含文件权限设置 Linux环境下)

    确保git服务已安装成功,如果没有安装git服务查看:Git源码安装 Linux指定安装目录 1.创建git用户,并设置密码.并禁止git用户通过shell登录服务器(注意如果需要安装gitolite ...

  10. flask自定义转换器

    根据具体的需求,有些时候是需要用到正则来灵活匹配URL,但是Flask的路由匹配机制是不能直接在路由里直接写正则的,这时候就需要使用转换器! Flask的默认转换器: DEFAULT_CONVERTE ...