分类 linux 下的文章

以 docker 方式运行 jenkins

简介说明

docker 是官方推荐的一种 jenkins 启动方式。

jenkins

打开 jenkins 的官网,点击进入的是:

里面是 weekly releases 的镜像,对于官方推荐的 2.7.2 版本,是需要到 docker 官方仓库里面去找:

可以读一读底下的 Full Description ,有一些怎么使用 jenkins 镜像的说明

开始使用

拉取镜像
docker pull jenkins:2.7.2

通用的启动方式,用 -p 参数指定暴露的端口,用 -v 参数挂载 jenkins_home
docker run -p 8080:8080 -p 50000:50000 -v /your/home:/var/jenkins_home jenkins:2.7.2

  • 8080 端口是访问 jenkins 网页的端口,如果你想在 80 端口访问,就改成 -p 80:8080
  • 50000 端口与 slave 有关,参考 jnlp-slave 这个镜像,里面使用的 port。
  • -v 参数挂载了宿主机一个指定目录到 /var/jenkins_home ,相当于设置了自定义的 JENKINS_HOME

jenkins 容器中添加 git, maven 等组件

一般我们使用jenkins的时候,需要用到 git, jdk, maven 等等的组件,在使用 docker-jenkins 的时候,可以用 -v 参数挂载到 /usr/bin 目录中,并不需要再在镜像中进行安装,也不需要基于jenkins镜像重新写dockerfile,保持镜像干净。

jenkins 容器中的公钥私钥

很常用的 jenkins 构建操作有:在 Execute shell 步骤中用 ssh 的方式远程登录上其他服务器,服务器上面的执行 shell 脚本。 ssh 方式需要用到的 public key 和 private key 也是可以用 -v 参数挂载到 /var/jenkins_home/.ssh 目录中。注意,这里不挂载到容器的 /root/.ssh 目录,因为整个 jenkins 容器是以 jenkins 用户来运行的。

在 jenkins 容器中调用 docker

有两种方式来调用 docker :

  1. 通常用在 docker 单节点下,可以挂载 docker 到 jenkins 容器中,添加如下参数:
    -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/usr/bin/docker
  2. 通常用在 docker 集群中,可以使用 jenkins 的 docker 插件,连接到 Docker server REST API(http://master-ip:2375):

简单的方式启动 Docker server REST API

在安装容器管理工具 shipyard 的文档中,发现可以用 shipyard/docker-proxy 这个镜像来暴露 2375 端口, 得到一个 Docker server REST API

docker run \
    -ti \
    -d \
    -p 2375:2375 \
    --hostname=$HOSTNAME \
    --restart=always \
    --name shipyard-proxy \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -e PORT=2375 \
    shipyard/docker-proxy:latest

http://master-ip:2375 填入 jenkins 系统设置中的 Docker URL 就可以了(master-ip 是 docker server 的 ip)。

dockerurl

一个 jenkins 示例

我启动的 jenkins 里面包含 git, docker, 一份宿主机的公钥密钥, 自定义 JENKINS_HOME, 使用的启动命令如下:

docker run \
    -d -p 8080:8080 -p 50000:50000 \
    -v /home/docker/jenkins_home/:/var/jenkins_home \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v $(which docker):/usr/bin/docker \
    -v $(which git):/usr/bin/git \
    -v ~/.ssh:/var/jenkins_home/.ssh \
    --name=jenkins jenkins:2.7.2

在 jenkins 设置任务来测试挂载的 git, docker, 以及公钥密钥是否正常

jenkins-shell

  • git clone 用来测试 git 和 密钥是否生效
  • docker run hello-world 用来测试在宿主机运行的 docker 容器

构建任务的 Console Output:

console

git 使用密钥正常克隆了代码,docker 正常启动了容器

期待更多

以后再总结下 docker 方式的 jenkins slave,以及 jenkins 的 docker 插件,jenkins 的 Docker CI 流程等等。

Blog服务器迁移小记 - all in docker

之前一直用 linode 的 vps,上面有 docker 起来的亲友团 shadowsocks 翻墙管理 ss-panel ,和放在 LNMP 上面的 typecho 博客。网友翔子说准备要换到新的香港vps,于是匆匆忙忙就只做了两个动作备份数据:

  1. docker commit ss-panel 的容器,然后直接 docker push 上去 docker.io。因为实在不想压缩下载经本地了,那个神经病镜像原来已经3G多 =。=
  2. 备份了 mysql 的博客 sql (连博客代码都忘记拉回下来,还好原来本地有一份以前的备份,看了名字,文件夹 sae_ 开头的,久经风霜有点历史的代码了。原来是从 SAE 那边一直转移过来的)

然后。。。感觉慢慢来,最重要的备份都备份好啦,nginx 的配置没有备份,还有些 bypy 什么的工具都没有备份,都随意了。反正不急。。。但想不到的是。。

昨晚月黑风高,linode 的 vps 突然欠费停机了,登进去后台,大大的欠费公告!
天那噜,网友翔子带着他的信用卡跑路了!跑路了!!

欠费

于是只能快速拿到新 vps 的ip、账号、密码,开始迁移!!

1 vps 原来已经装好了 docker 了,首先迁移的是 shadowsocks 翻墙管理平台 ss-panel !

  1. docker pull ss-panel的镜像地址 (为了安全,镜像地址我就不写了,> <)
  2. docker run -p 88:80 -name=ss-panel 这个 ss-panel 镜像
  3. 在 iptable 添加一些开放的端口,ip 地址为容器的 ip:
iptables -t nat -A DOCKER ! -i docker0 -p tcp -m tcp --dport 1040 -j DNAT --to-destination 172.17.0.4:1040

iptables -t nat -A POSTROUTING -s 172.17.0.4/32 -d 172.17.0.4/32 -p tcp -m tcp --dport 1040 -j MASQUERADE
  1. 到域名注册商修改A记录,泛域名 *.jmcn.cc 和 域名 jmcn.cc 都绑到 vps 的 ip 。

大功告成!

顺便打个广告:想要翻墙快来找我,包年 RMB 60 就可以啦 !!

2 然后是 typecho 博客的迁移步骤开始:

  • 想了想,特么的 2G 内存啊,一下子吃光了,LNMP 不装 https://lnmp.org/ 的包了,直接上 docker 的 LNMP 吧!
  1. 找到 github 上面有 docker-lnmp ,看了下并没有什么毛病,而且可以 docker-compose 启动,于是就用这个吧,按自己需要再改造一哈。
  2. 安装 docker-compose,按官网的方法:
curl -L https://github.com/docker/compose/releases/download/1.7.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose

chmod +x /usr/local/bin/docker-compose
  1. git clone [email protected]:micooz/docker-lnmp.git ,修改 docker-compose.yml 文件,分别有下面几点,组成的架构如图:

架构

  • 添加 nginx-proxy 作为 80 端口的负载均衡(Load Balancer),可以支持虚拟域名,方便域名管理,这点棒棒的!
# nginx-proxy
nginx-proxy:
  image: 'jwilder/nginx-proxy'
  ports:
    - "80:80"
  volumes:
    - /var/run/docker.sock:/tmp/docker.sock:ro
  • 因为我的 nginx-proxy 不用 ssl,所以要注释掉下面一层 nginx 的 443 端口和 certificates ,并加上虚拟域名的环境变量:
# web server
nginx:
  image: nginx:latest
  ports:
    - "80"
  volumes:
    # app
    - ./app/src:/usr/share/nginx/html
    # nginx configs
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    - ./nginx/conf.d/:/etc/nginx/conf.d/:ro
    # certificates
    # - ./nginx/ca/server.crt/:/etc/nginx/server.crt:ro
    # - ./nginx/ca/server.key/:/etc/nginx/server.key:ro
  environment:
    - VIRTUAL_HOST=*.jmcn.cc,jmcn.cc
  restart: always
  links:
    - fpm:__DOCKER_PHP_FPM__
  • 为了方便操作 mysql ,顺手把 phpmyadmin 也加上去了:
phpmyadmin:
    image: index.docker.io/phpmyadmin/phpmyadmin 
    ports:
        - "80"
    links:
        - mysql
    environment:
        - PMA_HOST=mysql
        - VIRTUAL_HOST=mysql.jmcn.cc
  1. 开始 docker-compose 构建和启动:
cd docker-lnmp

docker-compose build

docker-compose up -d
  1. 第4步彻底完成后,就有几个容器启动成功了:
  • dockerlnmp_nginx-proxy_1 对外端口 80
  • dockerlnmp_nginx_1 不对外开放端口
  • dockerlnmp_mysql_1 对外端口 3306
  • dockerlnmp_fpm_1 不对外开放端口
  • dockerlnmp_phpmyadmin_1 不对外开放端口
  1. 一开始访问 http://jmcn.cc 的时候,就会看到 phpinfo( ) 打印出来的信息。
  2. 将博客代码放到 .app/src 文件夹中,重新运行 install.php ,检查看能不能连到数据库。想着之后再重新导入 sql 就好了。然后发现只检测到 SQLite 的 Pdo 驱动,那应该是 fpm 构建的dockerfile 缺少了 mysql 的扩展了。
  3. 修改 php-fpm/Dockerfile 文件,在最后添加 RUN docker-php-ext-install pdo_mysql,最后的 Dockerfile 如下:
######
# See: https://hub.docker.com/_/php/
######

FROM php:7.0.5-fpm
MAINTAINER micooz <[email protected]>

######
# You can install php extensions using docker-php-ext-install
######

RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng12-dev \
    && docker-php-ext-install -j$(nproc) iconv mcrypt \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd
RUN docker-php-ext-install pdo_mysql
  1. 重新访问 http://jmcn.cc/install.php ,这次能检查到 mysql 驱动了!

mysql

host 填入 mysql,(这个是mysql的服务名);端口填 3306 ;然后是用户名 root 和 密码。

到 phpmyadmin 新建一个数据库,把这个数据库名填进去,然后安装 typecho 博客成功后,再重新导入之前备份的 sql 。

  1. 我的博客复活了!!!欢迎访问 http://jmcn.cc
  2. 等等,我点了好多 .html 的链接页面都报 404 了!发现原来是 nginx 配置没有开启页面伪静态啊!我的天!好多事情啊~
  3. 那么,就去修改 nginx 配置吧,修改 nginx/conf.d/default.conf 文件,加入 rewrite 重写,部分配置如下:
location / {
    if (!-e $request_filename) {
            rewrite ^(.*)$ /index.php$1 last;
    }
        root   /usr/share/nginx/html;
        index  index.html index.htm index.php;
    }

期间试过 js/css/jpg/png 等等的静态文件设置缓存,发现有点问题,迟点再埋这个坑,暂时可以不用。

  1. 重新启动这个 dockerlnmp_nginx_1 容器:
docker rm -f dockerlnmp_nginx_1

docker-compose up -d
  1. 就这样,我的博客完完全全复活了!!!欢迎访问 http://jmcn.cc

3 补充内容,不错的 docker 容器管理工具 —— shipyard !

shipyard 一年前安装过,现在变得漂亮又好用,还支持 swarm !

有两种部署方式:

  1. 我用了 Automated 的方式部署:
curl -sSL https://shipyard-project.com/deploy | bash -s
  1. 部署成功后,将 shipyard-controller 的容器删除掉,按自己口味自定义一下:
docker rm -f shipyard-controller
  1. 我不想它对外开放 8080 端口,并且要设置个域名给它访问:
docker run \
    -ti \
    -d \
    -e VIRTUAL_HOST=docker.jmcn.cc \
    --restart=always \
    --name shipyard-controller \
    --link shipyard-rethinkdb:rethinkdb \
    --link shipyard-swarm-manager:swarm \
    shipyard/shipyard:latest \
    server \
    -d tcp://swarm:3375

于是,我访问 http://docker.jmcn.cc 就能在线管理容器啦,方便快捷启动容器,不需要每次都 ssh 进 vps 操作。

  • 最后再附送一个 2048 小游戏,当然,它就是在 shipyard 上面启动出来的: http://2048.jmcn.cc

kubernetes 在 ubuntu 上的部署实践

教程系列:

官方教程:
http://kubernetes.io/docs/getting-started-guides/ubuntu/
网上的教程:
http://blog.csdn.net/wangtaoking1/article/details/49122977
脚本的结构解释:
http://blog.csdn.net/delphiwcdj/article/details/50411711

从虚拟机开始

本人使用的脚本设置了2台虚拟机(Ubuntu 14.04 LTS 64bit server),1台为master和minion,另外一台是minion

虚拟机需求:

  1. The nodes have installed docker version 1.2+ and bridge-utils to manipulate linux bridge.
  2. All machines can communicate with each other. Master node needs to be connected to the Internet to download the necessary files, while worker nodes do not.
  3. These guide is tested OK on Ubuntu 14.04 LTS 64bit server, but it can not work with Ubuntu 15 which uses systemd instead of upstart.
  4. Dependencies of this guide: etcd-2.2.1, flannel-0.5.5, k8s-1.1.8, may work with higher versions.
  5. All the remote servers can be ssh logged in without a password by using key authentication.

(摘自官网教程

虚拟机新建步骤

使用root账号:
sudo passwd root
su root

允许远程登陆:
vi /etc/ssh/sshd_config
PermitRootLogin without-password修改为:PermitRootLogin yes

service ssh restart

免密码登陆:
ssh-keygen -t rsa
touch /root/.ssh/authorized_keys
cat /root/id_rsa.pub >> /root/.ssh/authorized_keys
master需要将公钥添加到每一台机器,如果不添加,到时候运行安装脚本需要手动输入密码

换源,阿里源或163源:
vi /etc/apt/source.list

deb http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ trusty-backports main restricted universe multiverse

sudo apt-get update 
sudo apt-get upgrade 

安装docker:
sudo apt-get install curl
curl -fsSL https://get.docker.com/ | sh

安装bridge-utils:
sudo apt-get install bridge-utils

准备工作

在master机器上,下载相关文件:
cd /root
下载 http://pan.baidu.com/s/1bpt4QtD,放在/root路径下
软件版本号:

  • FLANNEL_VERSION=0.5.5
  • ETCD_VERSION=2.3.1
  • KUBE_VERSION=1.2.4

git clone -b v1.2.4 [email protected]:JMcn/kubernetes.git

本仓库是fork官方仓库的tag1.2.4经过修改而成,主要改动如下:

  1. 全部仓库地址:gcr.io/google_containers 替换为 index.alauda.cn/googlecontainer
  2. cluster/addons/dns/skydns-rc.yaml.in 添加 - --kube_master_url
  3. 更改 cluster/ubuntu/config-default.sh
  4. 修改 cluster/ubuntu/download-release.sh
  5. cluster/ubuntu/reconfDocker.sh 在 etcdctl 后面添加 --no-sync 参数
  6. 添加认证文件cluster/easy-rsa.tar.gz 和 添加 Kube-UI 插件

如果第5点没有修改,etcdctl 后面不加 --no-sync 参数,执行安装脚本时,etcd会报如下错误:

etcd cluster has no published client endpoints.
Try '--no-sync' if you want to access non-published client endpoints(http://127.0.0.1:2379,http://127.0.0.1:4001).
Error:  client: no endpoints available

编译各节点所用到的软件,安装kubectl工具:

cd kubernetes/cluster/ubuntu
./download-release.sh

sudo cp kubernetes/cluster/ubuntu/binaries/kubectl /usr/local/bin/kubectl
sudo chmod +x /usr/local/bin/kubectl

修改配置文件

打开kubernetes/cluster/ubuntu/config-default.sh文件
修改节点信息:

export nodes=${nodes:-"[email protected] [email protected]"}
role="ai i"
export roles=($role)
export NUM_NODES=${NUM_NODES:-2}
export SERVICE_CLUSTER_IP_RANGE=${SERVICE_CLUSTER_IP_RANGE:-192.168.3.0/24} 
export FLANNEL_NET=${FLANNEL_NET:-172.16.0.0/16}

修改插件配置

ENABLE_CLUSTER_DNS="${KUBE_ENABLE_CLUSTER_DNS:-true}"
# DNS_SERVER_IP must be a IP in SERVICE_CLUSTER_IP_RANGE
DNS_SERVER_IP=${DNS_SERVER_IP:-"192.168.3.10"}
DNS_DOMAIN=${DNS_DOMAIN:-"cluster.local"}
DNS_REPLICAS=${DNS_REPLICAS:-1}
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"

添加dns主机ip
修改文件kubernetes/cluster/addons/dns/skydns-rc.yaml.in

执行安装脚本

cd kubernetes/cluster
KUBERNETES_PROVIDER=ubuntu ./kube-up.sh

成功最后会提示:Cluster validation succeeded

输入kubectl get nodes可以看到节点信息

安装插件

cd kubernetes/cluster/ubuntu
KUBERNETES_PROVIDER=ubuntu ./deployAddons.sh
输入kubectl cluster-info可以查看服务的链接:

Kubernetes master is running at http://10.211.55.13:8080
KubeDNS is running at http://10.211.55.13:8080/api/v1/proxy/namespaces/kube-system/services/kube-dns
KubeUI is running at http://10.211.55.13:8080/api/v1/proxy/namespaces/kube-system/services/kube-ui
kubernetes-dashboard is running at http://10.211.55.13:8080/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard

其他插件,例如监控的kibana+elasticsearch,可以进入kubernetes/cluster/addons目录安装:

kubectl create -f fluentd-elasticsearch/

部署私有 Docker registry

Insecure Registry(不需要验证的仓库)

命令行执行:

docker run -d -p 5000:5000 --restart=always --name registry \
  -v /home/mamaci/registry:/var/lib/registry \
  registry:2.4.0

其他机器拉取报错:
Error response from daemon: Get https://xxx.xxx.xxx.xxx:5000/v1/_ping: tls: oversized record received with length 20527

解决方法:
vi /etc/default/docker
添加:DOCKER_OPTS="--insecure-registry ip:5000"
主和其他机器都需要加!

参考:
http://www.open-open.com/lib/view/open1456539405281.html
https://github.com/docker/distribution/blob/master/docs/insecure.md

最后添加上daocloud的加速器,修改为:
DOCKER_OPTS="--insecure-registry ip:5000 --registry-mirror=http://b8b310cb.m.daocloud.io"

TestNG

安装

Help -> Install New Software
点击“Add..”按钮
Name:TestNG
location:http://beust.com/eclipse

注释

@BeforeSuite:注解的方法将只运行一次,运行所有测试前此套件中。
@AfterSuite:注解的方法将只运行一次此套件中的所有测试都运行之后。
@BeforeClass:注解的方法将只运行一次先行先试在当前类中的方法调用。
@AfterClass:注解的方法将只运行一次后已经运行在当前类中的所有测试方法。
@BeforeTest:注解的方法将被运行之前的任何测试方法属于内部类的<test>标签的运行。
@AfterTest:注解的方法将被运行后,所有的测试方法,属于内部类的<test>标签的运行。
@BeforeGroups:组的列表,这种配置方法将之前运行。此方法是保证在运行属于任何这些组第 一个测试方法,该方法被调用。
@AfterGroups:组的列表,这种配置方法后,将运行。此方法是保证运行后不久,最后的测试 方法,该方法属于任何这些组被调用。
@BeforeMethod:注解的方法将每个测试方法之前运行。
@AfterMethod:被注释的方法将被运行后,每个测试方法。

@DataProvider:标志着一个方法,提供数据的一个测试方法。注解的方法必须返回一个Object[][],其中每个对象[]的测试方法的参数列表中可以分配。 该@Test 方法,希望从这个 DataProvider 的接收数据,需要使用一个 dataProvider 名称等于这个注解的名字。
@Factory:作为一个工厂,返回 TestNG 的测试类的对象将被用于标记的方法。该方法必 须返回 Object[]。
@Listeners:定义一个测试类的监听器。
@Parameters:介绍如何将参数传递给@Test 方法。
@Test:标记一个类或方法作为测试的一部分。

testng.xml 文件

TestNG 官方文档:http://testng.org/doc/documentation-main.html

测试技巧

  1. 依赖测试
    @Test (dependsOnMethods = {"verifyLogin"})
  2. 忽略测试
    @Test(enable = false)
  3. 参数化测试

通过 testng.xml 传递参数给测试代码:
@Parameters("test1")

<suite name="suite1">
    <parameter name="test1" value="testng" /> <!-- 定义 test1 的 value -->
...
</suite>

通过 DataProvider 传递参数:

//定义对象数组 
@DataProvider(name="user")
    public Object[][] Users(){ 
        return new Object[][]{
                  {"testingwtb","a123456"},
    }; 
}
//调用 user 数组的值
@Test(dataProvider="user")
public void testCase(...){
...
}