上一篇大概介绍了JWT的用法,实现了一个简单的登录注册以及邮箱验证。而这一篇呢就负责把我们的项目部署到自己的服务器上去。本文需要有一定Docker基础。理解Image,Container及其一些基础用法。
准备工作
首先我们得有一台服务器。我这里用的是阿里云ECS,华北节点。具体的购买操作的话百度会有教程。购买完成以后你会有一个公网的ip,以及一个通过ssh登录服务器的密码。
关于Docker的话,如果不了解的可以先去看文档。其实引用知乎大神的话来说,Docker的Container(容器)就像轮船上的集装箱。集装箱各自装着各自的货物,互不影响。比如一个Redis服务,一个Mongodb服务,都可以放到一个单独的Container(集装箱)里面。而这些容器,又依赖一个执行环境。这个执行环境就是Docker所说的Image(镜像)。每一个Container管理着自己的生命周期。
下面来说说Jenkins。Jenkins是一款由Java开发的开源软件项目,主要是用来持续集成的。相当于就是预先写好脚本,调试成功之后,下一次如果再需要部署的时候就会自动执行上一次存储的脚本,无需再修改。简单来说,我们用Jenkins持续集成Node.js项目之后,就不需要每一次都登录到服务器,把本地的文件传到服务器,在执行pm2 restart xxx或者node xxx等工作。只需要在你的Jenkins项目中点击立即构建就可以完成,非常方便。
安装docker
首先我们需要先安装docker,因为Jenkins是依赖docker的。linux平台CentOS 7的用户可以直接运行如下命令即可安装最新版的Docker。
$ sudo yum install docker
对于CentOS 6.5的用户需要先获取epel源并导入。
$ wget -c http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
$ rpm -ivh epel-release-6-8.noarch.rpm
$ rpm -import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-6
然后通过yum安装Docker
$ yum install docker-io --enablerepo=epel
下面启动Docker,并设置Docker为开机启动
$ sudo service docker start
$ sudo chkconfig docker on
以上是linux系统的安装步骤,如果你的系统是mac,需要从Docker官网下载。下载完成后执行安装。
安装Jenkins
Docker安装完成之后,我们需要通过docker来安装和启动Jenkins,执行如下命令安装Jenkins
docker pull jenkins:latest
首先我们需要下载jenkins镜像。很多人会想,既然jenkins是java开发的,那一定需要配置java的环境。答案肯定是否定的,因为jenkins镜像里面已经配置好java的环境,我们无需关心。镜像下载完毕后,我们需要把Jenkins的文件存储地址挂载到我们的主机上面。为了方便以后项目配置的迁移等操作。
注意:我们目前所做的一系列操作都是在自己内网机器上进行的操作,不要放到外网服务器中。
下面我们来配置Jenkins的工作目录,用于挂载Docker Container里面Jenkins工作目录。
$ mkdir /var/jenkins_home
$ sudo docker run -d --name myjenkins -p 49001:8080 -v /var/jenkins_home:/var/jenkins_home jenkins
首先我们在本机的var目录下创建jenkins_home目录,然后我们启动一个name为myjenkins的Docker。-d 的意思是程序在后台执行。-p 是把本机的49001端口映射到Container的8080端口,8080端口是Jenkins启动后默认监听的端口。-v 表示把Container中/var/jenkins_home(后者)挂载到本机/var/jenkins_home目录。最后的jenkins是该Docker启动依赖的镜像。
如果一切顺利的话我们就可以看到jenkins的欢迎页面了
当然我们这里是已经登录以后的。由于版本不同,新版本可能在进入到主页之前需要你预先安装插件。可以安装完毕后再进入。
开始部署
接下来我们就要对Jenkins进行一些简单的配置。我们需要实现一个这样的需求:把我们写好的代码传到github,然后Jenkins从github上拉取代码传到服务器,再启动一个包含pm2和node环境的Container将代码跑起来。
首先我们来安装几个插件。进入 系统管理>管理插件>可选插件,右上角过滤里面输入Git,如果报403的话在列表中直接找Git Plugin(The Plugin integrates GIT with Jenkins)插件;然后安装Publish Over SSH(Send build artifacts over SSH)。
安装完毕之后我们需要重启Jenkins。如果自动重启失败可以在浏览器键入下面地址。
http://192.168.1.116:49001/restart
重启完成之后,我们就需要对插件进行设置了。进入 系统管理>系统设置。首先为了连接到我们的远程服务器,我们需要添加服务器配置。找到你得Publish over SSH插件栏
如图所示,我们需要填入远端服务器的IP地址、SSH登录密码、用户名以及我们所连接的远程目录。如果你是通过SSH key连接远程服务器的,在Path to key一栏需要填入key在你本机的路径。可以点击下面的Test Configuration按钮来测试服务器是否可以连接成功。
然后我们来创建一个项目,这里我们已经构建好了一个名为node_access_count的账户。
这里选择构建一个自由风格的软件项目。
点击ok进入到项目主页,点击左边菜单栏的配置按钮,在配置页我们找到"源码管理"选项,在这里我们要来配置github源码。
首先我们在Repostiories一栏填入代码的地址,在Credentials一栏点击add添加github帐号,如第二张图所示。最后在添加需要拉取的分支,这样拉取代码这一块就配置完毕。
然后把页面下拉,拉到构建一栏。这就是自动化部署的核心所在。构建表示我们要如何向生产服务器发布一个应用。简单来说就是配置一系列自动化脚本。我们完成构建需要做以下事情:
1.从git或者svn拉取最新代码。
2.本地打包,排除.git文件。
3.把代码包通过SSH发布到远程服务器。
4.停止原先远程服务器正在运行的服务,删除原来的代码,解压缩心代码。
5.通过新package.json安装依赖包,然后启动服务
首先我们点击增加构建步骤,选择Execute shell。由于之前我们已经配置好从github自动拉取源码,现在我们无需配置,只需要对拉取的文件进行打包。
首先我们从github拉取的代码会存放在/var/jenkins_home/workspace/node_access_count目录下,node_access_count是你项目的名字。所以我们要对这个路径下的文件进行压缩。这里我们用到的是tar命令,对tar不了解的先看这篇文章。
tar -czvf /tmp/node_access_count.tar.gz -C /var/jenkins_home/workspace/node_access_count . --exclude="*.git"
mv /tmp/node_access_count.tar.gz /var/jenkins_home/workspace/node_access_count/
-C的意思是把tar的工作目录更改到/var/jenkins_home/workspace/node_access_count下,也就是对/var/jenkins_home/workspace/node_access_count的文件进行压缩;--exclude就是忽略所有以.git结尾的文件。然后我们把压缩完成的文件解压到/var/jenkins_home/workspace/node_access_count/目录下。
上述步骤完毕以后,我们需要把打包完成的代码发送到远程服务器上。这时继续点击添加构建步骤,选择Send files or execute commands over SSH。然后选择我们一开始添加的SSH服务器。
在Source files中填入文件名称,也就是刚才我们压缩的文件。这里的默认文件路径为/var/jenkins_home/workspace/node_access_count/,上一步我们已经将文件放到该目录下。
Remote directory意思是文件存放在远程服务器上的什么位置,这里我们填入var/,文件将会被保存在服务端/var/目录下。
接下来就要写发布的脚本了,要注意这里的脚本都是运行在服务端的。由于是用Docker,所以我们每次发布的时候都需要先删除一次之前的Container。
docker rm -f nodeCountAccess
然后我们创建目录
mkdir /var/node
mkdir /var/node/docker_node
mkdir /var/log/pm2
我们会把解压后的源码放到mkdir /var/node/docker_node,然后我们会挂载Container中的pm2 log输出文件到服务器的/var/log/pm2。首先解压。
tar -xvf /var/node_access_count.tar.gz -C /var/node/docker_node
然后我们起一个docker来安装node程序需要的依赖包
docker run --rm -v /var/node/docker_node:/var/node/docker_node -w /var/node/docker_node/ wangsidi/node_pm2 npm install
首先我们将服务器的/var/node/docker_node挂载到Container的/var/node/docker_node。-w的意思是指定命令的执行目录为Container的/var/node/docker_node/。我们用wangsidi/node_pm2(如果没有会自动下载,这是一个包含node和pm2的镜像)镜像来执行npm install命令。
因为我们的程序涉及mongodb服务,所以我们需要起一个mongodb的Container来link到最终的服务器Container。
sudo docker run --name some-mongo -d mongo
这里mongo镜像如果不存在系统会自动pull。mongodb的Container起起来之后,会默认监听Container的27017端口。我们可以先起一个Container连接some-mongo,查看里面的环境变量。打开终端执行下面命令。(这条命令不需要写在Jenkins里面)
sudo docker run --rm=true -it --link some-mongo:mongo mongo /bin/bash
先用ssh命令进入到你得远程服务器。root是用户名,@后面是IP。进入之后我们启动一个用完即删的docker连接some-mongo并进入Container内部的bash。输入"env"。
我们可以看到,MONGO_PORT_27017_TCP_ADDR和MONGO_PORT_27017_TCP_PORT分别表示mongodb服务的IP和端口,这是我们之后连接数据库需要用到的。
回到Jenkins,现在mongodb的Container已经存在,所以接下来我们只需要启动一个docker运行代码并连接到some-mongo这个Container,我们的服务就启动成功。下面来操作。
docker run -d --name "nodeCountAccess" -p 80:3000 -v /var/node/docker_node:/var/node/docker_node -v /var/log/pm2:/root/.pm2/logs/ --link some-mongo:mongo -w /var/node/docker_node/ wangsidi/node_pm2 pm2 start --no-daemon app.js
这里docker的名字叫nodeCountAccess(和一开始删除的名字相同)。-p 把主机80端口映射到Container的3000端口(80是访问ip地址或域名默认访问的端口;3000端口是我的node程序监听的端口,大家可以根据自己的程序进行修改)。要注意pm2启动的时候会在/root/.pm2/logs/下生成日志文件,我们把它挂载到主机的/var/log/pm2,也就是我们一开始创建的那个目录。start --no-daemon表明pm2不以守护进程的方式启动,否则docker将不会被docker ps -a命令看到。
最后我们删除一开是传上来的/var/node_access_count.tar.gz(其实应该是可以被覆盖的)。
rm -rf /var/node_access_count.tar.gz
把涉及到的命令粘贴到Exec command里面。至此项目配置完毕。
点击保存回到项目主页,然后点击立即构建
点击你正在构建的项目,可以进入到详情,点击Console Output可以查看log输出。如果有报错什么的,可以在这里查看原因。
构建中小球的颜色会有三种。蓝色表示成功;红色表示出错;黄色表示虽然构建成功,但中间出现错误。红色和黄色都表明构建存在问题,我们需要重新检查和修改脚本。
如果一切顺利,你的应用已经部署成功,打开浏览器访问你的ip+端口试试。童鞋们可以试着访问我构建的项目http://112.126.78.86。
查错
如果你构建成功,却没有访问到也别急。这很有可能说明是你程序内部出了错误。由于我们把Container内部的pm2 log挂载到了服务器,我们可以进入的服务器的/var/log/pm2中进行查看。
app-error-0.log表示抛出异常的错误输出,app-out-0.log表示你程序中的一些console。你可以打开app-error-0.log看看里面有没有错误日志。绝大多数的错误都会被记录在里面。
end
以上就是我Docker+Jenkins实现自动化部署的全部过程,如果你构建成功的话,Docekr就算基本入门了。如果想要深入学习的话可以研究Docker的源码和官方文档。另外此次我没有使用Dockerfile来配置,相对来说Dockerfile更加简洁明了。关于Dockerfile的内容我会在后面的文章中给出。
这是我们的公众号,喜欢的可以关注交流。
如果你也喜欢全栈,请关注React全栈开发吧!!