这是一个swarm集群,从零开始搭建服务器环境,Docker-machine+Docker swarm+Nginx+Nginx代理

Docker-machine+Docker swarm+Nginx+Nginx代理

准备环境:

  • 6台centos7操作系统(需要关闭防火墙,关闭selinux)

  • IP地址划分
Docker-machine Swarm node1 Swarm node1 Swarm node1 Nginx服务 Nginx代理
192.168.100.70 192.168.100.71 192.168.100.72 192.168.100.73 192.168.100.74 192.168.100.75
  • docker版本

  • 初始环境要求:

准备的是全新的操作系统,只需IP地址配置ok,全网可以互通。

项目执行之前需要实现的要求

  • 全部不使用root,关闭密码验证,使用ssh密钥进行验证。

  • 全部机器使用IP地址为70的这台作为堡垒机进行管理。

  • 要求nginx代理为外部的提供访问的地址,(如果允许,可以再搭建一台dns解析服务和使用一台win7来进行测试。)

  • 后续更多需求可根据实际进行添加。

修改不使用root,使用新用户admin操作

远程软件连接

使用终端软件连接,暂时没有关闭密钥验证和不使用root登录,所以使用root先登录

上传expect脚本

将我当初写的expect脚本复制到作为堡垒机的100.70这台服务器

简介:

  • login.exp:里面包含了登录、新建用户、授权sudo使用权限、设置用户密码、复制公钥到新建用户的ssh目录下并关闭sshd服务的端口号和禁止密码验证登录操作。
  • scp.exp:里面包含了将公钥上传到指定的服务器的指定目录下,以便提供给login.exp登录进行操作。
  • sshlogin.sh:这里定义的是exp脚本中将会使用位置变量的参数,在这里定义后,exp脚本可以引用shell脚本的变量。
  • wyf.pub:这是公钥文件,自己去生成一个
  • passwd.txt:这个是shell脚本sshlogin.sh使用for循环的时候会使用到的各个位置变量的参数值。
  • use.txt:中文操作手册

    文件内容:

login.exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/expect -f

set ipaddress [lindex $argv 0]

set passwd [lindex $argv 1]

set user [lindex $argv 2]

set pubkey [lindex $argv 3]

set sshport [lindex $argv 4]

#set sudo echo "$user ALL=(ALL) ALL

set timeout 30

#登入操作
spawn ssh root@$ipaddress

expect {
"yes/no" { send "yes\r";exp_continue }
"password:" { send "$passwd\r" }
}

expect {
"*from*" {
send "useradd $user -m -s /bin/bash\r"
send "echo '$user ALL=(ALL) ALL' >> /etc/sudoers\r"
send "chown $user: /tmp/$pubkey\r"}
}

#修改用户密码
send "passwd $user\r"
expect {
"UNIX" { send "pwD@1234\r";exp_continue}
}

expect {
"#" { send "su - $user\r"
send "mkdir -p /home/$user/.ssh/\r"
send "chmod 700 /home/$user/.ssh\r"
send "cat /tmp/$pubkey > /home/$user/.ssh/authorized_keys\r"
send "chmod 600 /home/$user/.ssh/authorized_keys\r"
send "exit\r"
send "sed -i 's/#Port 22/Port $sshport/' /etc/ssh/sshd_config\r"
send "sed -i 's/Port 22/Port $sshport/' /etc/ssh/sshd_config\r"
send "sed -i 's/PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config\r"
#send "systemctl restart sshd\r"
send "exit\r"
}
}
interact

scp.exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/expect -f

set ipaddress [lindex $argv 0]

set passwd [lindex $argv 1]

set user [lindex $argv 2]

set pubkey [lindex $argv 3]

set timeout 30

#上传密钥wyf
spawn scp $pubkey root@$ipaddress:/tmp/

expect {
"yes/no" { send "yes\r"}
"password:" { send "$passwd\r" }
"#" { send "exit\r" }}
interact
#expect "$pubkey"

sshlogin.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash

for i in `awk '{print $1}' $PWD/passwd.txt`

do

j=`awk -v I="$i" '{if(I==$1)print $2}' $PWD/passwd.txt`
u=`awk -v I="$i" '{if(I==$1)print $3}' $PWD/passwd.txt`
p=`awk -v I="$i" '{if(I==$1)print $4}' $PWD/passwd.txt`
port=`awk -v I="$i" '{if(I==$1)print $5' $PWD/passwd.txt`
expect $PWD/scp.exp $i $j $u $p $port
expect $PWD/login.exp $i $j $u $p $port

done

wyf.pub

1
ssh-dss AAAAB3NzaC1kc3MAAACBANVB2rrIvkUGCJA6b/8h+GyKd1GVpxZFAM1n8sP2bo7TSkWpkxV81Y4zkdaYomsFDXLGnaI2QbHqv/pA0PndJFQ1NfYqbBw6FaGI1+cG8mgJBCPwVPmQnDvf4TPzJkEpdV1CK/wYjRVjuFL2lYOd8hepkNN4c96Dx5STdMyBSH+rAAAAFQCtUm3dYyX3j9pKY+yev3OKmB/LtwAAAIEAytEQ4hgceD7T00soGq/o0aS42QJoeZLJd/jwdIAqkUdg5y8q4lqzmlO2OaxD3YVf9eOYiuokZQu2qP5A+2iTbebpoqDDP8NksCgGPY+7viyKpUy+wrcm/Gwa0YA9B2mzpHudxXKuw/2wjtBKTjh+gFx7zlXBFHYaMdV8zsP/QkAAAACBALJc+rMJXSsS7+3i874iJIlP/r2qxrfkO4+qjmZoTbGYnhOzEzYbwP6newO27MBkaOHSPlWFh8kSQtiputcvg9GawG6Y6WO/0bfgHfxrDg0NnmQJgvcjMStGeuLwYB4NNG2KsomciN4hUruWHg0/+VwV8qknTHYFMJmOJC1ketuS

passwd.txt

1
192.168.100.71 pwd@123 wyf5 wyf.pub 234

use.txt

1
2
3
4
5
6
7
8
9
10
11
复制当前目录下的所有文件到同一个文件夹中

只需要定义passwd.txt中的值即可
格式为:列如像这样
IP地址 root密码 新建用户名 公钥文件 sshd端口号
192.168.100.71 pwd@123 wyf5 wyf.pub 234

定义后执行sh脚本文件即可

前置动作,yum -y install expect
执行脚本的系统最好是使用英文的,不然在提示栏会显示中文。这是基于centos系统的

expect脚本的配置文件修改

将其他5台服务器的信息写到passwd文件中

安装expect脚本执行命令

1
yum -y install expect(这里我已经安装)

检查各脚本是否有错误的地方

在login.exp这个脚本中重启sshd服务的操作,我注释掉了,先不重启,我们待会分别进去查看是否修改了配置文件,也是确保修改正确后再进行重启操作。

执行过程中,因为中文支持的原因提示的密码输入不一样,所以将提示修改为这样的(这里可以看出操作系统不同,所提示的也不同,需要对应操作系统来修改这里)

因为操作系统的原因,每台sshd_config的配置文件也不同,比如centos7的port前面会有一个#号,其他系统可能不同;
所以我们可以修改写两个sed

还有一个问题需要说明,我这里写成了222端口是问题的,比如#port 22,第一次修改后为port 222,第二次又会匹配下一条sed,既会变成port 2222。所以尽量不要用22开头的作为修改后的端口号
执行后在查看其中一台

后续重启sshd可能不成功,因为selinux和防火墙的原因,需要关闭。
查看无问题了,既重新启动sshd服务。

分别登录admin账户


我这里的

  • root密码是pwd@123
  • admin密码是qwaszx@)!&
  • wyf公钥的密码是1836

上面这里因为关闭了密钥验证,所以这里要使用密钥的验证。

安装docker的等一系列服务

新增项目测试:

这里的100.70机器是全新的一台机器,当我们只安装docker-machine的时候看是否一定需要docker服务

查看三台节点服务器情况:

验证并没有安装docker服务

上传docker-machine到服务器并移动到全局变量下

1
2
3
4
5
6
7
8
9
10
$ls
docker-machine

$sudo mv docker-machine /usr/bin/
$sudo chmod +x /usr/bin/docker-machine
$docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS

$docker-machine version
docker-machine version 0.12.2, build 9371605

先查看其他作为swarm集群的几台机器的情况



可以看到上面的三个节点已经安装好了docker,

这里为什么需要sudo呢?

因为docker安装的时候是使用的root用户,admin用户是没有权限操作docker的。所以我们这里可以将admin用户加入到docker组(这个组是安装docker的时候自动创建的,这样admin就可以操作docker服务了)

1
sudo gpasswd -a ${USER} docker

或者

1
sudo usermod -a -G docker admin

重启docker

1
sudo systemctl restart docker

这里有个小问题

查看组,

可以看到docker组中有admin这个用户,但是还是无法使用docker命令,从权限可以看出docker组是用读取写入权限的。但是admin还是无法使用docker

解决方法就是关闭这个终端,重新连接

使用docker-machine 的驱动进行连接并管理其他服务器

100.70作为docker-machine管理器,并使用admin用户操作。

在admin用户下生成密钥对

将生成的公钥放到远程主机的admin(远程主机的admin账户是可以操作docker的)账户去,会提示输入远程主机的admin用户密码

这里出现的问题就是,我们已经关闭了每台主机的密码验证,这种是无法上传的,所以只有分别复制过去

第一个ssh是本地远程的xshell的公钥,第二个是100.70下的admin用户生成的公钥

测试能否远程

1
ssh -p 222 admin@192.168.100.71

开始关联

注意,create 命令本是要创建虚拟主机并安装Docker,因为本例中的目标主机已经存在docker。所以已经安装会跳过的。-d 是 –driver的简写形式,主要用来指定使用什么驱动程序来创建目标主机。Docker Machine支持在云服务器上创建主机,就是靠使用不同的驱动来实现了。本例中使用 generic就可以了。接下来以 –generic开头的三个参数主要是指定操作的目标主机和使用的账户。最后一个参数 node1是虚拟机的名称,Docker Machine 会用它来设置目标主机的名称。

1
docker-machine create -d generic --generic-ip-address=192.168.100.71 --generic-ssh-port=222 --generic-ssh-user=admin --generic-ssh-key ~/.ssh/id_rsa node1

这里会出错,原因是admin用户无法执行在创建过程中需要执行的命令。所以我们修改,将ssh密钥导入到root用户

删除node1,再次进行创建

1
docker-machine create -d generic --generic-ip-address=192.168.100.71 --generic-ssh-port=222 --generic-ssh-user=root --generic-ssh-key ~/.ssh/id_rsa node1

等待过10分钟,基本就可以了

将另外的两个节点也加入进来

1
for i in 2 3 ; do docker-machine create -d generic --generic-ip-address=192.168.100.7$i --generic-ssh-port=222 --generic-ssh-user=root --generic-ssh-key ~/.ssh/id_rsa node$i; done

测试效果

现在测试100.70在不安装docker服务的情况下,使用了docker 节点的变量能否使用docker

从上面可以看出,并不能,原因是因为没有安装docker客户端。这里我们就不研究如何只安装客户端了,还是安装上docker服务吧,使用admin用户来安装

安装教程
https://docs.docker.com/engine/installation/linux/docker-ce/centos/#install-using-the-repository

这里当安装好了以后

上面可以看出安装的是docker-ce17.09版本,因为之前的变量导入还在,所以看到客户端版本为17.09,服务端是17.06。
移除变量后,再次查看,提示服务端并没运行,这样不就实现了我们的想法,只安装了客户端。

如果想运行服务,需要加入:

1
2
sudo systemctl enable docker
sudo systemctl start docker

这里我不加入开机启动,和启动服务端。

建立swarm集群

将node1作为swarm集群的创建者

1
docker swarm init --advertise-addr $(docker-machine ip node1)

退出node1

1
eval $(docker-machine env -u)

分别将node2和node3,将作为节点服务器加入到集群中

1
for i in 2 3; do docker-machine ssh node$i docker swarm join --token SWMTKN-1-2ov80t4wgnrmh4t1d48a0n3uxrrmaojkv9raf1733irvfiw4w2-d0vrx62myyg5wetxkd7r3jl7a $(docker-machine ip node1):2377; done

查看集群的节点信息

批量修改加速器(这样无法实现),必须要ssh进去后操作后才可以实现

1
for i in 1 2 3; do docker-machine ssh node$i curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://ac1e54d0.m.daocloud.io && sudo systemctl restart docker; done

建立一个nginx测试容器

这里是三台机器组成集群,副本因子为3,

1
docker-machine ssh node1 docker service create -p 80:80 --name swarm-nginx --replicas 3 fsoppelsa/swarm-nginx

1
docker-machine ssh node1 docker service ps swarm-nginx

从上面可以看出node1作为集群建立者,管理者,也是会作为work节点建立容器的
这里我们可以猜想是不是作为节点管理者,在副本因子定义为2的时候,会不会优先选择非管理者建立节点

测试:

1
docker-machine ssh node1 docker service create -p 8080:80 --name swarm-nginx8080 --replicas 2 fsoppelsa/swarm-nginx

结果证明:没有定义策略的情况下,还是建立到了管理者node1上面

使用调度策略(约束)来建立一个不在管理者上建立容器的service

1
docker-machine ssh node1 docker service create --constraint 'node.role!=manager' -p 88:80 --name swarm-nginx88 --replicas 3 fsoppelsa/swarm-nginx

Swarm集群已经建立,清空所有的service。

搭建jenkins服务器(192.168.100.70)

搭建java和mvn环境

解压

1
2
3
4
tar -zxf jdk-8u111-linux-x64.tar.gz
sudo mv jdk1.8.0_111/ /usr/local/java
tar zxf apache-maven-3.3.9-bin.tar.gz
sudo mv apache-maven-3.3.9 /usr/local/maven

设置环境变量
/etc/profile

1
2
3
4
export JAVA_HOME=/usr/local/java
export PATH=\$PATH:/usr/local/java/bin
export MAVEN_HOME=/usr/local/maven
export PATH=\$PATH:/usr/local/maven/bin

生效

1
2
source /etc/profile
mvn -v

安装jenkins服务

建立一个jenkins目录

1
2
mkdir Jenkins
mv jenkins.war ./jenkins

安装分屏命令screen

1
sudo yum -y install screen

建立一个名称为jenkins的screen分屏

1
screen -S jenkins

Ctrl+a+d返回终端

进入jenkins分屏

1
2
screen -r jenkins
sudo /usr/local/java/bin/java -jar ./jenkins.war --httpPort=404


Ctrl+a+d返回终端

Web浏览器打开登录http://192.168.100.70:404

切换为终端查看,并复制到web页面去

选择安装

安装好了后建立一个jenkins账户,密码为jenkins的管理员账户

修改jenkins的配置

Maven的配置

使用本地的maven

Java和git使用本地的

新建一个jenkins-swarm项目测试

自定义工作目录

1
runuser -l admin -c 'docker-machine ssh node1 docker service create -p 80:80 --name swarm-nginx --replicas 3 fsoppelsa/swarm-nginx'

这里因为docker-machine管理是使用的admin用户,而运行jenkins的root用户。所以root用户也无法使用docker-machine管理,这里我们使用runuser这个命令来执行,(是不是起到了安全的作用,我们可以建立一个用户来专门作为管理docker-machine的,)

保存后,点击立即构建

登录docker-machine的机器验证

访问验证


这里有一个猜想:
这里的docker-machine因为是使用的admin用户,所以docker-machine管理的主机的配置文件都放在/home/admin/.docker下,权限为700,这里我将权限设置为770后,并将root组设置为宿组,还是无法使用docker-machine,最后提示权限太高,导致无法运行。修改为700后即可。

构建一个maven项目

安装jenkins插件,

建立一个名为test-oss的maven项目

Git代码来源自己去配置配置好。存储位置:/tmp/jenkins-buildenv/${JOB_NAME}/workspace
build配置,指定pom.xml位置,第一次操作使用mvn install ,后续都使用mvn clean package -Dmaven.test.skip=true

Build操作使用shell脚本完成

因为运行jenkins的账户是root,所以使用runuser命令来操作,首先将jenkins的build后的代码复制到docker swarm的manager上,然后再由manager创建集群时候指定挂载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
BUILD_ID=DONTKILLME

#项目定义变量
IMAGE=huisebug/jetty:1
WARPAG=17find-op.war
PORT=8100
PROJECTPATH=17find/17find-op
WEBAPPS=/home/admin/${JOB_NAME}/


#启动
#runuser -l admin -c "mkdir $WEBAPPS"
runuser -l admin -c "cd $WEBAPPS && rm -rf * && /usr/local/java/bin/jar xf $WORKSPACE/$PROJECTPATH/target/$WARPAG"


#runuser -l admin -c "docker-machine ssh node1 mkdir /root/swarmsource/"
#runuser -l admin -c "docker-machine ssh node1 rm -rf /root/swarmsource/${JOB_NAME}/"
runuser -l admin -c "docker-machine scp -r $WEBAPPS node1:/root/swarmsource/ "
runuser -l admin -c " \
docker-machine ssh node1 docker service create \
--mount 'type=bind,src=/root/swarmsource/${JOB_NAME}/,dst=/usr/local/jetty/webapps/ROOT/' \
--name ${JOB_NAME} -p $PORT:8080 \
--constraint 'node.role!=manager' \
--replicas 2 $IMAGE \
"

查看效果,稍微等下

做到这里的时候总结出一些更好的想法:

  1. 因为jenkins如果是使用的war的形式运行的,那么他会需要创建文件夹和配置文件,这样就需要管理权限,这样将会使用到root的权限,这样是有点不安全的,所以如果在真实环境中,推荐jenkins还是使用docker容器这样的方式来运行。
  2. 还有一种解决方式就是node1作为集群的manager,我们可以使用约束来不让他参与dockerservice的创建work节点容器,只作为jenkins这类的代码拉取工作。就可以省去我们这里的操作,需要在docker-machine控制台将文件夹复制到node1里面去,再运行dockerservice create的创建。
  3. docker swarm mode是使用service的方式建立集群并负载均衡。这样可以将项目整合在一起加强做集群
  4. 这里我的操作都没用到docker-machine env的操作,因为为了使用shell,只得使用ssh 节点 命令的方式来进行操作。
  5. Nginx服务器和nginx代理其实可以整合,但是我们为了优化配置,代理分开