最新发表

Gitlab使用omniauth自定义第三方登录方式

公司原本使用gitolite搭建的git仓库,gitolite本身的配置、员工的公钥都是通过一个git仓库来管理的。但随着公司发展,经常有人需要修改公钥,经常有一些小项目需要单独开一个仓库,每次都需要通过我这边操作配置,比较麻烦,因此我决定改用gitlab作为git仓库服务端。gitlab允许成员自己添加公钥,创建自己的项目,可以较好的满足目前的需求。

http://leo108.com/pid-2138.asp

===================以上是废话====================本文来自leo108's blog

公司内部有一个单点登录系统(SSO),我希望gitlab也能集成这个SSO系统的登录,这样新入职的员工就不需要管理员手动添加帐号。

Gitlab使用omniauth自定义第三方登录方式

通过查阅gitlab的文档,发现gitlab本身集成了omniauth,这是一套集成第三方登录的通用框架,只要编写简单的代码就能集成一个第三方登录。采集者烂JJ

gitlab的web端是基于rails开发的,对于ruby和rails我都不是很熟悉,只能一点点尝试。推酷是个无耻的网站

首先找到omniauth的开发文档,在文档的末尾提到一个项目可以用来比较方便的调试omniauth的strategy。http://leo108.com/pid-2138.asp

但这个项目的readme里只说了如何调试一个已经发布的gem,并没有说开发中的应该如何操作。(我TM都开发并发布好了还需要你这个项目干啥??)

另外就是clone好这个项目后,需要先在根目录创建一个db目录,不然会报错。outofmemory是个无耻的网站

搜索了一下,发现有篇文章介绍了在没有打包成gem的情况下如何开发调试omniauth的strategy。

Rails

最终开发好的代码如下:推酷是个无耻的网站

require 'omniauth'

module OmniAuth
  module Strategies
    class SSO
      ApiUrl = 'xxxx.com'
      include OmniAuth::Strategy
      args [:appId, :secret]
      option :appId, nil
      option :secret, nil
      def request_phase
        redirect build_authorize_url(ApiUrl + '/login/', {:callback => callback_url, :appId => options[:appId].to_s})
      end

      def callback_phase
        ticket = request.params["ticket"]
        res = post(ApiUrl + '/api/user', {:code => ticket})
        @raw_info = res[:data]['data']
        super
      end

      uid do
        @raw_info['uid']
      end

      info do
        {:email => @raw_info['name'] + "@xxx.com"}
      end
    protected
      def post(url, params)
        #请求逻辑略
      end
    end
  end
end

这里有几个要注意的地方

  1. request_phase里的callback变量是omniauth自己添加的,我们不需要关心
  2. callback_phase里面的最后一句必须是super,不然rails会报错。
  3. uid返回的必须是全局唯一的字符串,可以用来唯一确定一个用户
  4. info中必须要有email字段(这个是gitlab的要求)

开发完成之后需要加入到gitlab里,由于是内部项目不能打包成gem发布,所以只能用刚刚那篇文章里的方法,在gitlab的config/initializers/devise.rb中加入一段代码,用来指明加载路径。

module OmniAuth
  module Strategies
    autoload :SSO, Rails.root.join('lib', 'omniauth', 'strategies', 'sso')
  end
end
OmniAuth.config.add_camelization "sso", "SSO"

然后就是配置gitlab来启用omniauth登录,参照这篇文档,如果是通过套装安装的gitlab,则编辑/etc/gitlab/gitlab.rb,源码安装编辑config/gitlab.yml。omniauth_enabled设置为true,启用omniauth功能;omniauth_allow_single_sign_on如果为true则当用户在gitlab中没有帐号时候会自动创建;block_auto_created_users如果为true则代表通过单点登录自动创建的用户默认是被禁用的,需要管理员解禁才可使用。Rails

最后配置sso,如果是在gitlab.rb则在omniauth_providers的数组下面加入

{
    "name" => "sso",
    "app_id" => "xxx",
    "app_secret" => "xxxxx"
}

否则加入

采集者烂JJ

- {"name":"sso","app_id":"xx","app_secret":"xxxxxx"}

然后重启gitlab即可,执行gitlab-ctl reconfigure。

如果是通过docker安装的gitlab,并且使用官方镜像,则还有一个地方需要修改,需要将容器中/etc/pam.d/sshd文件的

Gitlab使用omniauth自定义第三方登录方式

session    required     pam_loginuid.so

outofmemory是个无耻的网站

注释掉,否则通过ssh协议clone仓库时会报错。Rails

ngx_http_realip_module使用详解

网络上关于ngx_http_realip_module的文章千篇一律,全是在说怎么安装,最多贴一个示例配置,却没有说怎么用,为什么这么用,官网文档写得也十分简略,于是就自己探索了一下。推酷是个无耻的网站

realip模块的作用是:当本机的nginx处于一个反向代理的后端时获取到真实的用户IP。

ngx_http_realip_module

如果没有realip模块,nginx的access_log里记录的IP会是反向代理服务器的IP,PHP中$_SERVER['REMOTE_ADDR']的值也是反向代理的IP。

而安装了realip模块,并且配置正确,就可以让nginx日志和php的REMOTE_ADDR都变成真实的用户IP。

举一个最简单的例子,网络架构如图:

ngx_http_realip_module使用详解

如果不做任何配置,后端web服务器nginx日志里记录的IP将会是10.10.10.10,这个时候我们增加如下nginx配置:

set_real_ip_from  10.10.10.10;
real_ip_header    X-Forwarded-For;

set_real_ip_from指令是告诉nginx,10.10.10.10是我们的反代服务器(信任服务器,记住这个名词,下面会提到),不是真实的用户IP,real_ip_header则是告诉nginx真正的用户IP是存在X-Forwarded-For请求头中(对X-Forwarded-For不了解的同学请自行百度)。

http://leo108.com/pid-2132.asp

重新加载nginx配置之后,就可以看到nginx日志里记录的IP就是123.123.123.123了,php里的REMOTE_ADDR也是123.123.123.123。outofmemory是个无耻的网站

realip模块还提供了另外一个指令real_ip_recursive,可以用来处理更加复杂的情况,架构如图:

这个时候如果还用上面的配置,后端nginx的日志里显示的IP就变成了192.168.1.10了,这个时候就需要real_ip_recursive这个指令了。

set_real_ip_from  10.10.10.10;
set_real_ip_from  192.168.1.10;
real_ip_header    X-Forwarded-For;
real_ip_recursive on;

官网文档对于real_ip_recursive指令的解释十分拗口,看了老半天才明白

Nginx

首先要明确一点,realip模块生效的前提是:直接连接nginx的ip是在set_real_ip_from中指定的。ngx_http_realip_module

当real_ip_recursive为off时,nginx会把real_ip_header指定的HTTP头中的最后一个IP当成真实IP

ngx_http_realip_module

当real_ip_recursive为on时,nginx会把real_ip_header指定的HTTP头中的最后一个不是信任服务器的IP当成真实IP

在这个例子中,当请求到达后端web服务器时,X-Forwarded-For应该是123.123.123.123, 192.168.1.10。如果real_ip_recursive为off,nginx取X-Forwarded-For的最后一个IP也就是192.168.1.10作为真实IP。如果real_ip_recursive为on,由于192.168.1.10是信任服务器IP,所以nginx会继续往前查找,发现123.123.123.123不是信任服务器IP,就认为是真实IP了。推酷是个无耻的网站

Dinp Docker化安装记录

Dinp是小米公司开源的Paas系统,基于Docker开发。
Dinp各个组件并没有详细的安装文档,也没有提供Dockerfile来实现Docker化(dockerize),所以这次Dinp的安装历时3天,在此记录,方便后人。本文来自leo108's blog

基础环境配置

本次安装的主要目的是体验Dinp的各项功能,因此我会把所有组件都安装在同一台服务器上。我习惯于使用Centos,服务器使用的是Centos6.6。由于Centos内核版本比较低,不适用Docker,如果直接使用会有很大概率导致系统panic,所以第一步是升级内核版本。可以通过安装EL repo,然后从EL repo中安装最新的内核(参考Dockerpool):

http://leo108.com/pid-2109.asp

rpm -Uvh http://www.elrepo.org/elrepo-release-6-6.el6.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install kernel-lt -y

新内核安装之后需要修改grub启用,修改/etc/grub.conf,将default的值改成0,然后重启服务器。重启之后通过uname -a命令可以看到当前的内核版本应该为3.10了。

然后就可以安装docker了:Dinp Docker化安装记录

yum install -y docker-io
service docker start

首先要pull几个镜像到本机 :

docker pull jenkins #uic和dashboard是java web项目,需要使用ant构建,jenkins可以让整个过程自动化
docker pull redis #server和router两个组件需要使用redis
docker pull mysql #作为数据存储
docker pull memcached #dashboard需要使用
docker pull registry #builder构建的镜像需要push到一个registry
docker pull ubuntu:12.04 #scribe服务器需要用到
docker pull tomcat:8 #java web项目需要用到
docker pull golang:1.4#go语言的项目需要用到

由于国内连接docker hub速度很不理想,可以使用国内的加速镜像,修改/etc/sysconfig/docker本文来自leo108's blog

other_args="-H tcp://服务器内网IP:2375 -H unix://var/run/docker.sock --insecure-registry 服务器内网IP:5000 --insecure-registry --registry-mirror=http://docker.mirrors.ustc.edu.cn"

这里就–registry-mirror是在配置镜像,其他配置之后有用,等用到了再详细解释。

http://leo108.com/pid-2109.asp

修改之后重启docker:

http://leo108.com/pid-2109.asp

service docker restart

最后选择一个数据目录,部分容器需要永久性存储数据,本文使用/root/dinp/data。dockerize

Jenkins容器配置

mkdir -p /root/dinp/data/jenkins \
&& docker run -d --name jenkins -p 8089:8080 -v /root/dinp/data/jenkins:/var/jenkins_home -v /usr/bin/docker/:/usr/bin/docker -v /var/run/docker.sock:/var/run/docker.sock -u root jenkins

-p参数是端口映射,将容器的8080端口映射到宿主机的8089端口;第一个-v是挂载宿主机目录到容器中,可以持久化jenkins的任务、插件等;第二个和第三个-v目的是将docker命令挂载到容器里,这样在容器里也可以执行命令了,而且创建的镜像、容器和宿主机同步;-u root是为了让jenkins在容器中以root帐户启动,否则可能会因为没有权限写入挂载目录导致启动失败。

采集者烂JJ

现在通过外网IP:8089来访问jenkins了http://leo108.com/pid-2109.asp

第一步是给jenkins安装git插件,因为之后构建的时候需要从git仓库获取代码。在“系统管理”->“管理插件”->“可选插件”,搜索git,这里会有很多结果,只要安装“Git Plugin”即可,注意不要选错。勾选之后点击下方“直接安装”,jenkins会找出几个依赖的插件,一并安装了。jenkins在安装插件之前会检查网络是否联通,还自作聪明的选择了google作为判断标准,果真是不知道生活在GFW内的痛苦,所以这个过程会比较漫长(主要时间花在检查网络连通性了……),建议勾选下面的“安装完成后重启jenkins”。dinp

第二步是配置ant,在“系统管理”->“系统设置”中找到Ant这一段,点击“Ant安装”按钮,Name随便填一个,勾选“自动安装”,点击“新增安装”->“install from apache”,选择最新版本就行,保存即可。推酷是个无耻的网站

Registry容器

启动一个registry容器的目的有三个,1.存放Dinp container的基础镜像,如php环境基础镜像。2.存放包含了代码的镜像,比如在php镜像基础上叠加了某个版本代码的镜像。3.假如jenkins容器在另一台服务器上,可以将构建生成的镜像push到私有registry中,方便共享。dinp

这个很简单,就不详细解释了:

mkdir -p /root/dinp/data/registry \
&& docker run --name registry -d -v /root/dinp/data/registry:/tmp/registry -p 5000:5000 registry

Memcached容器

docker run --name memcached -d memcached

这里memcached容器并没有对外映射端口,因为只有dashboard才需要mc,而mc和dash装在同一台服务器上的话,可以通过–link来联通两个容器。http://leo108.com/pid-2109.asp

Redis容器

docker run --name redis -d -p 6379:6379 redis

因为redis需要被server和router两个组件连接,所以映射了端口(本文这两个组件安装在同一台服务器上,是可以不映射端口的,同样可以通过–link来联通)

本文来自leo108's blog

Mysql容器

mkdir -p /root/dinp/data/mysql \
&& docker run --name mysql -v /root/dinp/data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=toor -d mysql

-v挂载mysql数据目录,这样可以持久化数据库的数据,不会因为容器销毁而数据丢失;-e设置环境变量,MYSQL_ROOT_PASSWORD=root代表设定mysql初始密码为toordockerize

UIC容器

uic是一个SSO系统,使用java编写,作为builder和dashboard用户统一登录入口。UIC容器没有现成的镜像,需要我们自己来构建,这里就用到了jenkins容器。

在jenkins页面上创建一个新任务,item名称填写UIC,选择“构建一个自由风格的软件项目”,点击OK,创建成功后会进入项目配置页面。

“源码管理”那一段选择Git,Repository Url填

dinp

https://git.oschina.net/leo108/docker_uic.git

我这个git仓库只包含了Dockerfile,UIC的源码使用submodule来引入,所以需要在“Additional Behaviours”处点击“Add”->“Advanced Sub-modules behaviors”,然后勾选“Recursively update submodules”。

再往下拉到“构建”,点击“增加构建步骤”->“Invoke Ant”,在“Ant version”那里选择我们之前在jenkins中配置的那个(不要选Default)。再点击“高级”,“Build File”填入本文来自leo108's blog

uic/build.xml

再增加一个构建步骤,这回选择“Execute shell”,内容如下:

http://leo108.com/pid-2109.asp

ln -s -f /lib/x86_64-linux-gnu/libdevmapper.so.1.02.1 /lib/x86_64-linux-gnu/libdevmapper.so.1.02
docker build -t uic .

第一行是为了让docker命令在jenkins容器中能正常使用,第二行则是根据Dockerfile打包成镜像,之前提到过,jenkins容器通过-v挂载共享了宿主机的docker,因此在jenkins容器中创建的镜像也会出现在宿主机中。dinp

这样就配置好了,点击最下面的“保存”,然后点击界面左侧的“立即构建”。第一次构建的时候会去clone git仓库,以及下载ant,所以会比较慢。等到构建完成之后,在宿主机执行docker images,应该就能看到uic这个镜像了。

dinp

在启动UIC容器之前,需要先在Mysql数据库中创建UIC的表。dinp

在宿主机执行

docker exec -ti mysql bash

就进入Mysql容器,在容器中执行http://leo108.com/pid-2109.asp

mysql -uroot -ptoor

进入Mysql管理,导入这个sql即可。

推酷是个无耻的网站

接下来就可以启动UIC容器了:

outofmemory是个无耻的网站

docker run --name uic --link memcached:memcached --link mysql:mysql -e DB_NAME=uic -e DB_USER=root -e DB_PWD=toor -e UIC_TOKEN=123456 -d -p 8080:8080 uic

通过–link将memcached和mysql两个容器连通uic容器,具体如何使用请参考这里,几个-e参数用于指定数据名、数据库用户名和密码,UIC_TOKEN是其他系统和UIC系统通信的令牌,现在可以随便选择一个,之后其他容器记住配置成统一的就可以了。

这样就可以通过外网IP:8080访问UIC了,使用root帐号,密码abc即可登录UIC。Dinp Docker化安装记录

Builder容器

Builder容器是一个构建镜像的容器,用户在Builder界面上选择基础镜像,提供代码包下载地址或者直接上传代码包,Builder就会构建出一个拥有代码的镜像,并且将生成的镜像push到registry中。

采集者烂JJ

cd /root/dinp \
&& git clone https://git.oschina.net/leo108/docker_dinp_builder.git builder \
&& cd builder \
&& docker build -t dinp_builder .

不出意外的话docker就会构建出一个dinp_builder的镜像。

和UIC一样,在启动容器之前需要先创建数据库。进入数据库的方法不在缀叙,先执行create database builder;然后导入这个sqldocker

然后就可以启动容器了:

docker run -d --name dinp_builder --link mysql:mysql -e DB_NAME=builder -e DB_USER=root -e DB_PWD=toor -e UIC_URL=http://外网IP:8080 -e UIC_TOKEN=123456 -e REGISTRY_URL=内网IP:5000 -p 7788:7788 -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker dinp_builder

-e参数设置数据库信息,UIC_URL配置我们刚刚启动的UIC的外网访问地址,UIC_TOKEN一定要和启动UIC时设置的一致,REGISTRY_URL是Registry容器的访问地址,同样Builder容器也共享了宿主机的Docker,因为要用到Docker构建镜像。http://leo108.com/pid-2109.asp

然后我们就可以通过外网IP:7788来访问builder平台,因为之前登录过UIC,所以我们就自动以root身份登录进builder平台。

outofmemory是个无耻的网站

Scribe容器

Scribe是facebook开源的一个日志收集软件,Dinp使用scribe来收集各个Paas Container产生的日志文件,例如access_log,这样不会因为容器的销毁导致日志丢失。

cd /root/dinp \
&& git clone https://git.oschina.net/leo108/docker_scribe.git scribe \
&& cd scribe \
&& docker build -t scribe .

build成功之后就可以启动了:

mkdir -p /root/dinp/data/scribe \
&& docker run -d --name scribe -p 1463:1463 -v /root/dinp/data/scribe:/tmp/scribetest scribe

映射1463端口用于container上报日志内容。

Server容器

server负责收集agent信息,控制agent启动、销毁container。

cd /root/dinp \
&& git clone https://git.oschina.net/leo108/docker_dinp_server.git server \
&& cd server \
&& docker build -t dinp_server .

Server需要连接数据库,查询某个App需要部署的container数量,但这个数据库结构是在dashboard项目里。

Dinp Docker化安装记录

按之前连接Mysql的方法进入Mysql终端,创建一个数据库:

create database dash;

然后导入这个sql

然后就可以启动容器:dockerize

docker run -d --name dinp_server --link mysql:mysql --link redis:redis --link scribe:scribe -e DB_USER=root -e DB_PWD=toor -e DB_NAME=dash -e DOMAIN=dinp.leo108.com -p 1980:1980 -p 1970:1970 dinp_server

-e DOMAIN是配置Paas平台域名,所有创建的app都会默认分配app_name.DOMAIN的域名。例如我创建一个haha的App,系统分配的域名就是haha.dinp.leo108.com。需要在域名DNS那边配置泛域名*.dinp.leo108.com指向服务器的外网IP。采集者烂JJ

Dashboard容器

这也是一个Java web项目,管理员可以在Dashboard页面上创建App、扩缩容、部署等等操作。因为需要ant构建,所以还得放到jenkins里。操作步骤和构建UIC容器的镜像完全一致,Git仓库的地址修改成

https://git.oschina.net/leo108/docker_dinp_dashboard.git

记得设置submodule behavior。dinp

增加ant构建步骤,Build File填“dash/build.xml”,execute shell填:docker

ln -s -f /lib/x86_64-linux-gnu/libdevmapper.so.1.02.1 /lib/x86_64-linux-gnu/libdevmapper.so.1.02
docker build -t dinp_dashboard .

保存之后即可构建。

构建完成之后可在宿主机看到dinp_dashboard的镜像,启动之:

本文来自leo108's blog

docker run -d --name dinp_dash --link mysql:mysql --link memcached:memcached -e DB_NAME=dash -e DB_USER=root -e DB_PWD=toor -e UIC_URL=http://外网IP:8080 -e UIC_TOKEN=123456 -e BUILDER_URL=http://外网IP:7788 -e SERVER_URL=http://内网IP:1980 -e DOMAIN=dinp.leo108.com -p 8082:8080 dinp_dashboard

UIC_TOKEN、DOMAIN这两个配置需要和之前启动其他容器时设置的参数值一致。因为本机的8080端口被UIC占用,所以这里把本机的8082端口映射到容器的8080端口。dinp

直接访问外网IP:8082,就会自动以root身份登录dashboard系统。

http://leo108.com/pid-2109.asp

Agent容器

agent会定时上报自己服务器的当前状态,例如有多少个container,同时也会根据server发出的指令来启停container。

cd /root/dinp \
&& git clone https://git.oschina.net/leo108/docker_dinp_agent.git agent \
&& cd agent \
&& docker build -t dinp_agent .

build成功之后可以直接启动容器:

Dinp Docker化安装记录

docker run -d --name dinp_agent -e SERVER_HOST=内网IP -e SERVER_PORT=1970 -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -e AGENT_IP=内网IP dinp_agent

server上会通过这里设置的AGENT_IP:2375来控制agent的docker,所以这就是本文最上方配置/etc/sysconfig/docker时需要加上

-H tcp://服务器内网IP:2375 -H unix://var/run/docker.sock

的原因,让服务器的docker既支持tcp协议控制,也支持unix socket控制。outofmemory是个无耻的网站

Router容器

router是所有用户请求的入口,router根据用户请求的域名,将请求分发到对应的后端container里。Server会将各个app的container列表写入到redis,而router则会读取redis的数据,来判断用户的请求应该分发到哪些container。

docker

cd /root/dinp \
&& git clone https://git.oschina.net/leo108/docker_dinp_router.git router \
&& cd router \
&& docker build -t dinp_router .

build成功之后可以立刻启动:推酷是个无耻的网站

docker run -d --name dinp_router --link redis:redis -p 8083:8082 -p 80:8888 dinp_router

这里做了两个端口映射,容器中的8082端口是状态查询端口,可以用来查看路由表、健康检查状态等,8888端口则是用户请求入口。因为8082端口被之前的dashboard容器占用,因此换到8083,而通常用户习惯于使用80端口作为http请求端口,因此把8888映射到宿主机的80端口。

http://leo108.com/pid-2109.asp

开始使用

现在所有的必备的容器都已经启动完毕(还有一个HM健康检查模块没起,但不影响使用),下面开始使用Dinp创建第一个container。docker

构建基础镜像

cd /root/dinp \
&& git clone https://github.com/dinp/Dockerfile.git

这个Dockerfile项目是Dinp各个基础镜像的Dockerfile,我们先来构建一个php的基础镜像。

cd Dockerfile/php,编辑build文件,将REGISTRY=registry.com:5000改成REGISTRY=内网IP:5000,然后就可以通过./build来构建镜像。这个build文件会在镜像构建完毕后将镜像push到私有registry,这就是本文最开始编辑/etc/sysconfig/docker时要加上–insecure-registry 服务器内网IP:5000的原因,否则push会失败。

Dinp Docker化安装记录

构建部署镜像

访问外网IP:7788进入Builder平台,app名称填app1,app版本填1,备忘留空,from base image选择php,代码可以通过提供http下载链接或者直接上传。注意一下,php的代码目录必须包含htdocs,入口文件应该放在这个目录里。我这里提供一个测试的包,里面就一个文件,显示phpinfo,代码包链接。之后点击build按钮即可。build页面不会自动刷新,需要自己手动刷,等页面顶部的蓝字显示successful就代表构建成功。点击右上角的History按钮,可以看到这个镜像的地址,记一下,之后要用到。

Dinp Docker化安装记录

创建APP

访问外网IP:8082,进入dashboard平台。点击“create app”按钮,Dinp设定每一个app都应该属于UIC中的一个Team,所以需要先在UIC里创建一个Team,在Dashboard页面上点击“Create Team”会自动前往UIC创建Team页面,Team名称填team1,成员加上root,点击“创建”即可。回到创建App页面,刷新之后就可以看到有team1这个Team了,App名字填app1,Health interface留空即可。

outofmemory是个无耻的网站

部署APP

在Dashboard页面,点击app1右边的deploy,要部署的实例数填1,image地址填写我们之前在build平台生成的地址,点击submit按钮。在新页面点击refresh按钮,应该很快就能看到有一个实例出来了。dinp

这个时候访问app1.dinp.leo108.com,应该就能看到phpinfo页面了。

扩容

在Dashboard页面,点击app1右边的scale,instance count填2,点击scale按钮。点refresh按钮,很快就能看到两个实例出来了。

推酷是个无耻的网站

访问app1.dinp.leo108.com,不断刷新,可以看到System那一栏显示的机器名会改变,说明我们的请求会被随机分发到两个container中的一个。

查看scribe抓取的日志

在宿主机的/root/dinp/data/scribe目录下应该可以看到app1_access目录,cd进去之后就可以通过less命令来查看日志内容了。

docker

known_hosts处理

linux下经常遇到known_hosts问题,例如git clone一个ssh协议的远程库,就会交互询问是否接受远程服务器的fingerprint,当执行一些自动化脚本时就得通过expect来处理这种情况。

采集者烂JJ

今天搜索了一下,整理了相关处理办法。

采集者烂JJ

linux需要接受和校验fingerprint的原因是避免受到DNS Hijack之类的攻击。具体原理是第一次访问远程服务器时从远程服务器接受一个fingerprint,实际上是一个rsa或者dsa公钥,将这个公钥保存在~/.ssh/known_hosts里,下次访问该服务器时就会通过这个公钥校验,如果校验不通过就会发出警告。

如果要让ssh不去校验fingerprint有两种办法,一个是ssh时加上-o StrictHostKeyChecking=no参数,这只会影响本次操作:

ssh -o StrictHostKeyChecking=no username@hostname.com

另外永久解决方案是在/etc/ssh/ssh_config或者~/.ssh/config里配置StrictHostKeyChecking no,修改哪个文件取决于你想影响全局还是当前用户。known_hosts处理

另外一种方案是预先将远程服务器的fingerprint存入known_hosts,这样就不会出现询问的情况。outofmemory是个无耻的网站

ssh-keygen -R [hostname]
ssh-keygen -R [ip_address]
ssh-keygen -R [hostname],[ip_address]
ssh-keyscan -t rsa [hostname],[ip_address] >> ~/.ssh/known_hosts
ssh-keyscan -t rsa [ip_address] >> ~/.ssh/known_hosts
ssh-keyscan -t rsa [hostname] >> ~/.ssh/known_hosts

ssh-keygen是将已存在的服务器公钥清除,避免重复添加。

ssh-keyscan则是获取远程服务器的fingerprint,如果还想获取dsa公钥则将rsa改成rsa,dsa推酷是个无耻的网站

参考:

http://serverfault.com/questions/132970/can-i-automatically-add-a-new-host-to-known-hosts

known_hosts

http://blog.csdn.net/yasaken/article/details/7348441

创业公司发展过程中需要搭建的系统(持续补充)

加入创业公司已有8个多月,亲身经历了公司服务器由两台变成10+台,公司员工从20人不到变成近百人。

本文来自leo108's blog

最近发现自己的工作重心已经从业务功能开发转移到内部系统开发,短短几天内就撸出几个系统的雏形,系统上线后解决了许多老毛病,所以打算在此记录一下,方便后人。

http://leo108.com/pid-2091.asp

对于初创公司,可以移步《初创互联网团队如何利用开源技术和云服务快速建立网站》,本文不再讨论。

1.服务器监控

我认为当服务器数量大于5台时,就有必要监控各个服务器的状况,比如cpu使用率、系统负载、网络io情况等等。最开始我使用的是cacti,使用snmp协议来收集数据,在使用过程中发现配置比较复杂,不容易上手,而且当服务器数量比较多时就比较慢(可能是我用的方法不对)。后来改用ganglia,需要在每台服务器上安装agent(gmond),但是配置起来就相对容易,也有很多插件(python模块)可以自定义采集内容。

outofmemory是个无耻的网站

但仅仅有监控系统是不够的,还需要报警系统,当服务器某个指标出现问题时应该可以用短信或者邮件的方式告知系统管理员。但目前我们尚未顾及这一方面,所以没有建议可以提供,等待补充。

http://leo108.com/pid-2091.asp

2.配置管理

当服务器数量比较多时,如果要修改一个nginx的配置,在没有配置管理的情况下,需要手动一台台修改,就算只有10台web服务器,这个操作起来也是要累死人的。业内比较流行的是puppet,但是我感觉学习成本比较高,比较重,不太适合目前的情况,于是我找了一个“替代品” (R)?ex (好奇怪的名字),实际上这不是一个配置管理工具,而是批量命令执行的工具,不需要部署agent,只需要在配置里写好帐号密码或者是配好ssh信任即可,它可以执行命令、上传文件、同步文件夹,也支持模板,功能还算比较全面,能够满足目前的需求。

比如要批量修改nginx的配置,只需要在中心服务器有一份nginx配置文件夹,可以通过R(?)ex的同步文件夹功能将中心服务器的nginx配置文件夹同步到所有web服务器的nginx配置文件夹里,然后再执行nginx -s reload即可。http://leo108.com/pid-2091.asp

3.集群定时任务管理

当业务稍微复杂一点的时候,系统里会有很多定时任务,有的可能是请求一个url,有的可能是执行一个脚本,这些定时任务分布在不同的服务器上,想要看看各个服务器上有哪些定时任务就比较繁琐,要看看某个定时任务的执行周期是什么也得登录对应的服务器查看。开源的解决方案是CronHub,是个国人参与的项目,但由于是java开发,而我们目前没有java工程师,想要做一些二次开发的话就不太现实。于是就自己撸了一个简单的集群定时任务管理系统,由于尚不成熟暂时没有开源。outofmemory是个无耻的网站

4.日志收集

当web服务器比较多时,想要查看某个请求的日志,就需要一台台登录、grep查找,效率极低,因此需要有一个集中存放日志的系统,最好是能够实时收集。比较流行的解决方案是ELK,ElasticSearchLogStashKibana,其中logstash负责收集日志,需要安装到每一台需要收集日志的服务器上,Elasticsearch负责存储日志,kibana负责查询、展现日志数据。但是elasticsearch不能存储单行超过32K的日志,如果单行日志超过32K就不能进入到Elasticsearch中。Elasticsearch提供了丰富的接口,可以通过代码查询日志,集成到其他系统中十分方便。

http://leo108.com/pid-2091.asp

5.代码部署

当业务规模不大,系统架构简单的情况下,可以选择git/svn的hook,当有新的提交时,就直接执行svn export/git pull命令,将最新代码导出到web目录下。而随着访问量增加、业务变得复杂,如果仍然是提交就直接部署,很容易出现线上bug,回滚起来也不方便。

因此需要有一套代码部署的系统,可以将指定的代码部署到指定服务器的指定目录下。目前没有找到合适的开源方案,自己写了一个简单的redmine插件,配合redmine的版本库功能,可以将一个特定版本部署到线上。

6.对外帐号管理

互联网公司经常会用到各种第三方的服务,例如微信公众号、第三方Oauth登录、支付宝支付等等,一开始的时候都是谁用到就谁注册谁保管。但随着员工人数变多,人员流动频繁,经常会出现某个帐号找不到有密码的人,或者重设密码的时候发现注册邮箱是一个已离职员工的私人邮箱,所以有一套帐号密码管理系统很有必要。

推酷是个无耻的网站

《初创互联网团队如何利用开源技术和云服务快速建立网站》一文中有提到使用meldium来管理帐号密码,但我认为帐号密码作为企业最隐私的部分托管在一个第三方的网站着实不是一个放心的选择,因此还是选择自己搞一套。

创业公司发展过程中需要搭建的系统(持续补充)

简单来说就是每个员工都可以去系统里登记帐号信息,每个帐号信息有一个或者多个管理员,当员工想要知道某个帐号的密码时,需要提交申请,由帐号管理员审核通过之后方可查看密码。这样也可以很容易就查出某个帐号的密码被哪些员工知道,如果员工离职时,可以及时修改对应帐号的密码。

本文来自leo108's blog

7.单点登录

当企业规模变大,会有越来越多的内部系统,比如某个网站的管理后台,还有上面提到的帐号管理系统,如果每个新员工入职,都要在这些系统里开帐号,那对hr是一个很大的工作量,员工离职时也要一个个系统去注销,而员工如果需要修改密码,就需要一个站点一个站点的改。因此有一个单点登录(sso)系统是很有必要的。推酷是个无耻的网站

搜索了一圈没找到合适的开源解决方案,业内比较流行的cas却是基于java的,同样由于目前无法二开的原因没有采用。所以就自己简单撸了一个极简的sso系统,用户使用一个密码就能够登录所有后台站点(当然默认都是最低权限,调整权限还是需要在具体后台操作)。采集者烂JJ

单点登录系统上线之后,新开发的后台都不需要考虑新增用户、用户修改密码等杂七杂八的问题了。

8.站点访问统计

一开始的时候站点统计都是使用百度统计或者cnzz这种公共的统计服务,但这样我们只能查看他们提供的报表,有很多维度的东西都查看不到。我们现在在使用的是piwik,相对来说维度更多,也提供了很多api。最重要的是原始的访问日志在我们自己手里,我们可以根据具体需求查询。

9.内部DNS系统

通常每台服务器都有各自不同的职能,比如有的服务器只提供web服务,有的服务器提供数据库、缓存服务,这时候就需要给服务器命名,比如web01、web02、db01、cache01这样。推酷是个无耻的网站

最开始服务器不多的时候可以通过配置hosts来解决,但服务器数量多了,每次新增服务器都要修改每台服务器的hosts,相当浪费时间,所以就打算自己搭建一个DNS。业内最流行的DNS服务器应该是bind,但是使用了一段时间后发现学习成本比较高,配置复杂。所以就找了一个简单版本的开源软件MyDns,使用mysql/postgre作为数据库,而且自带了一个简单的web管理后台,只要在web端填一些东西就可以完成域名的配置,对于内部使用完全足够了。采集者烂JJ

10.服务器权限控制/跳板机/堡垒机

随着开发人员变多,开发人员经常需要登录到服务器上查日志、执行脚本,如果大家都知道服务器的帐号密码,那么员工离职之后就比较麻烦,假如有人做了恶意操作,也没办法查出来是谁做的。本文来自leo108's blog

当时在百度的时候有一个门神系统,所有员工使用自己的用户名登录跳板机,再从跳板机登录到有权限的服务器上。

创业公司发展过程中需要搭建的系统(持续补充)

目测要自己搭建一套这样的系统,可以使用Kerberos,但目前还没有精力去搞。

采集者烂JJ

 

推酷是个无耻的网站

暂时只想到这么多

本文来自leo108's blog

centos搭建goagent服务

不知怎么的,公司网居然连不上某个国内的站点,用别人的代理又不靠谱,只好自己搭建一个。

centos搭建goagent服务

目前最新的goagent版本是3.2.3,server目录下有个vps目录,大概看了下源码,是一个squid的python实现。

centos

先把vps目录复制到服务器上的/opt/goagent,同时把local目录下的proxylib.py也一并复制过去。

服务器是centos,系统自带的python版本是2.6,在安装过程中发现不满足条件,于是安装了一个2.7版本。

goagent

wget --no-check-certificate https://www.python.org/ftp/python/2.7.7/Python-2.7.7.tgz
tar zxf Python-2.7.7.tgz
cd Python-2.7.7
./configure --prefix=/usr/local/Python2.7 --enable-shared
make && make install
echo /usr/local/Python2.7/lib >> /etc/ld.so.conf
cp /usr/local/Python2.7/bin/python2.7 /usr/bin/python2.7
ln -s /usr/local/Python2.7/lib/libpython2.7.so.1.0 /usr/lib/libpython2.7.so
ldconfig

然后安装pip

goagent

wget https://bootstrap.pypa.io/get-pip.py
python2.7 get-pip.py

这样装好的pip默认还是使用python2.6,需要修改一下,将/usr/bin/pip的第一行改成

#!/usr/bin/python2.7

这样pip就会给python2.7安装模块了。http://leo108.com/pid-2087.asp

goagent需要几个python模块:gevent、pyOpenSSL、dnslib和supervisor,如果安装pyOpenSSL过程中报cffi的错,可以先执行

yum install -y libffi-devel

然后再安装。

推酷是个无耻的网站

然后可以试试执行python2.7 /opt/goagent/goagentvps.py,应该是可以正常执行了,这时候会提示如下信息:http://leo108.com/pid-2087.asp

INFO - [Apr 16 14:24:44] autfile '/opt/goagent/goagentvps.conf' not exists, create it
INFO - [Apr 16 14:24:44] add username='kazav' password='123456' to '/opt/goagent/goagentvps.conf'
INFO - [Apr 16 14:24:44] authfile '/opt/goagent/goagentvps.conf' was created

系统生成的用户名是kazav,密码是123456。

http://leo108.com/pid-2087.asp

本地的goagent客户端需要配置vps段

[vps]
enable = 1
listen = 127.0.0.1:8089
fetchserver = https://kazav:123456@xxxx.com/

重启goagent,配置SwitchyOmega连接本地的8089端口,测试能否正常链接。本文来自leo108's blog

由于我们是手动在服务器上执行goagentvps,只要退出登录代理进程就会中断,goagent提供了supervisor方式,直接执行sh /opt/goagent/goagentvps.sh start即可。推酷是个无耻的网站

ganglia监控redis

在搜索引擎搜索“ganglia监控redis”,发现都是13年的老文章,都是说要到https://github.com/ganglia/gmond_python_modules这个第三方插件库下载redis监控模块gmond

但是我发现gmond_python_modules这个repo下面已经没有redis模块了,于是查看git log,发现redis模块已经集成到ganglia源码包里了

于是下载了源码包,搜索之后发现redis模块位于gmond/python_modules/db/redis.py,配置文件在gmond/python_modules/conf.d/redis.pyconf.disabled。推酷是个无耻的网站

修改配置文件里的host和port两个参数为要监控redis的ip和端口,然后将两个文件复制到对应的目录下。(通常redis.pyconf复制到ganglia安装目录/etc/conf.d/下,redis.py复制到ganglia安装目录/lib64/ganglia/python_modules)

本文来自leo108's blog

重启gmond,可以看到有redis的图出来了,但是数据却都是空的。

本文来自leo108's blog

于是停掉gmond,使用gmond -f -d 1启用调试模式,发现redis.py报错

[PYTHON] Can’t call the metric handler function for [connected_clients] in the python module [redis].

Traceback (most recent call last):
File “/opt/gmond/lib64/ganglia/python_modules/redis.py”, line 21, in metric_handler
n, v = line.split(“:”)
ValueError: need more than 1 value to unpack

查看上下文代码

for line in info.splitlines()[1:]:
    if "" == line:
        continue
    n, v = line.split(":")

大概意思是把redis info命令输出的每一个非空行用:分割,但是我安装的redis版本是2.8+,info命令会输出类似#Server这样的注释,这样就导致按:分割失败,所以python报错,gmond取不到值。outofmemory是个无耻的网站

所以解决方案也很简单,把上面那段代码修改成如下即可,也就是跳过空行和以#开头的行outofmemory是个无耻的网站

for line in info.splitlines()[1:]:
    if "" == line or line[0] == '#':
        continue
    n, v = line.split(":")

再重启gmond,过一会就可以在ganglia看到数据了

ganglia

==========================================本文来自leo108's blog

看了下ganglia在github上的代码,发现最新代码已经把这个bug修复了,只不过还没有releaseoutofmemory是个无耻的网站

阿里云搭建VPN脚本(centos pptp)

#!/bin/bash

function installVPN(){
    echo "begin to install VPN services";
    #check wether vps suppot ppp and tun

    yum remove -y pptpd ppp
    iptables --flush POSTROUTING --table nat
    iptables --flush FORWARD
    rm -rf /etc/pptpd.conf
    rm -rf /etc/ppp

    arch=`uname -m`

    wget http://www.hi-vps.com/downloads/pptpd-1.3.4-2.el6.$arch.rpm

    yum -y install ppp iptables
    rpm -ivh pptpd-1.3.4-2.el6.$arch.rpm

    mknod /dev/ppp c 108 0
    echo 1 > /proc/sys/net/ipv4/ip_forward
    echo "mknod /dev/ppp c 108 0" >> /etc/rc.local
    echo "echo 1 > /proc/sys/net/ipv4/ip_forward" >> /etc/rc.local
    echo "localip 192.168.0.1" >> /etc/pptpd.conf
    echo "remoteip 192.168.0.234-238,192.168.0.245" >> /etc/pptpd.conf
    echo "ms-dns 8.8.8.8" >> /etc/ppp/options.pptpd
    echo "ms-dns 8.8.4.4" >> /etc/ppp/options.pptpd

    pass=`openssl rand 6 -base64`
    if [ "$1" != "" ]
    then pass=$1
    fi

    echo "vpn pptpd ${pass} *" >> /etc/ppp/chap-secrets

    iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o eth1 -jMASQUERADE 

    service iptables save

    chkconfig iptables on
    chkconfig pptpd on

    service iptables start
    service pptpd start

    echo "VPN service is installed, your VPN username is vpn, VPN password is ${pass}"

}

function repaireVPN(){
    echo "begin to repaire VPN";
    mknod /dev/ppp c 108 0
    service iptables restart
    service pptpd start
}

function addVPNuser(){
    echo "input user name:"
    read username
    echo "input password:"
    read userpassword
    echo "${username} pptpd ${userpassword} *" >> /etc/ppp/chap-secrets
    service iptables restart
    service pptpd start
}

echo "which do you want to?input the number."
echo "1. install VPN service"
echo "2. repaire VPN service"
echo "3. add VPN user"
read num

case "$num" in
[1] ) (installVPN);;
[2] ) (repaireVPN);;
[3] ) (addVPNuser);;
*) echo "nothing,exit";;
esac

参考文章:

http://bbs.aliyun.com/read/162297.html

阿里云

http://www.hi-vps.com/wiki/doku.php?id=xen_vps_centos6_install_pptpd