设想一下这些场景:Nuxt 版本出现重大更新须要升级;新功能的开发须要添加新的生产环境依赖;线上版本出现 bug 急需快速回滚至上一版本;开发环境和生产环境依赖版本一致性的保持……java
咱们须要解决的不只仅是不停机更新,还要使开发环境和生产环境的版本保持强一致性,而且能够轻松地追溯历史版本,以及更新过程使用户无感知等等。综合以上特质,能够选择 docker 一试。node
Docker 是一个开源的应用容器引擎,基于 Go 语言并听从 Apache2.0 协议开源。linux
它号称本身是实如今任何地方安全构建、分享和运行现代应用的最快实现方式。它可让咱们把应用及其依赖所有打包到一个容器中,从而轻松地实如今任何系统下极速迁移。就像其 logo 展现的那样,一艘鲸鱼样子的大船载满集装箱飘荡在海面上。各个 OS 就是海洋,docker 承载着一个个容器飘荡在 OS 的海洋里。nginx
以 Nuxt 应用为例,若有须要,咱们能够将其运行须要依赖的 Nginx 、 NodeJs 、应用自己及其依赖等通通打包到一块儿。此时就算你拿到一个新的服务器,只要上面安装了 docker,只须要几个简单的指令就可让应用运行起来,而不须要再进行繁琐地配置。git
具体的使用细节再也不絮叨了,茫茫多的 docker 官方文档 正向你招手~菜鸟教程和 docker 中文社区也是系统学习的不错选择。web
惯例,抛出 docker cli 的经常使用指令:docker
# 基于当前文件夹下的 dockerfile 建立一个镜像
docker build -t helloworld .
# 上面指令的全写
docker build --tag=helloworld .
# 运行这个镜像,并将本机 4000 端口映射到容器对外暴露的 80 端口,外部经过 4000端口访问
docker run -p 4000:80 helloworld # 使 container 在后台运行
docker run -d -p 4000:80 helloworld # 全部正在运行的容器列表
docker container ls
# 全部容器列表
docker container ls -a
# 停用一个容器
docker container stop <hash>
# 强制中止一个容器
docker container kill <hash>
# 移除一个容器
docker container rm <hash>
# 移除全部容器
docker container rm $(docker container ls -a -q)
# 全部镜像列表
docker image ls -a
# 移除一个镜像
docker image rm <image id>
# 移除全部镜像
docker image rm $(docker image ls -a -q)
# 登陆注册过的 docker hub
docker login
# 为镜像打标签
docker tag <image> username/repository:tag
# 将镜像推送至远程仓库
docker push username/repository:tag
# 运行这个镜像
docker run username/repository:tag
复制代码
此时,容器 (container) 即当前承载应用的进程,它拥有一个独立的文件系统,里面包含了应用所需的全部代码、依赖和运行时等等。这被称之为镜像 (image) 的存在,正是咱们须要在以后生产并保存起来的东西。shell
假如每一次版本发布咱们都生成一个镜像 (image),并基于项目和版本号为其打上独一无二的标签,而后把它们保存到一块儿,须要的时候随时取用,依赖更新的问题天然而然获得解决。而当须要发布新版本的时候,咱们只须要提早拉新版本的镜像到生产环境,而后移除旧版本镜像正在运行的容器,同时执行新版本的容器,开启新的 container,这样就能够实现无缝升级,也实现了某种意义上的不停机。npm
接下来,咱们要有一个存储版本镜像的仓库。docker 本身推出了一个镜像组织和托管平台,docker hub,学习的时候能够简单地用它来熟悉整个操做流程。以后能够搭建本身的私有仓库来知足实际的业务需求。由于咱们实际业务中各类服务器都基于阿里云产品,因此这里也选择阿里云容器镜像服务来管理镜像。json
仓库的问题解决了,咱们又但愿在每次 push 一个 tag 的时候可以自动地生成一个镜像并上传至仓库。咱们的项目代码托管在私有 GitLab 上,它自己就集成了不错的 CI/CD 功能,能够做为备选的目标之一。咱们这里选择使用 Jenkins (一个基于 java 的持续集成工具)并结合 GitLab 的 webhooks 来实现这一需求。
最终方案定为 GitLab + Jenkins + Docker + 阿里云镜像服务,下面来看一下大致的实施步骤。
安装 java -> yum install java
添加 Jenkins 库至 yum -> sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
导入公钥 -> sudo rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
安装 -> sudo yum -y install jenkins
查看 Jenkins 根目录 -> cd /var/lib/jenkins
配置 Jenkins 根权限:
vim /etc/sysconfig/jenkins
,修改或添加项 ->JENKINS_USER="root"
和JENKINS_GROUP="root"
,保存退出 执行gpasswd -a root jenkins
这里jenkins服务的默认端口为 8080,可在配置文件中修改,保存后执行service Jenkins restart
重启服务
启动Jenkins -> service jenkins start
。此时访问http://<服务器公网ip>:8080
开启 Jenkins 界面
在 /var/lib/jenkins/secrets/initalAdminPassword
下获取管理员密码以开启 Jenkins
设置管理员帐号密码后,便可开始使用 Jenkins
Docker 社区版和企业版,这里选择安装社区版本
$ sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest- logrotate docker-logrotate docker-engine
复制代码
$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2
$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
复制代码
$ sudo yum install docker-ce docker-ce-cli containerd.io
$ sudo systemctl start docker
$ sudo docker run hello-world
nuxt-demo
为例的 Dockerfile 配置文件,因为 docker 自身拥有守护进程和健康检查的功能,因此能够考虑放弃 pm2:FROM node:10.16.0
ENV HOST 0.0.0.0
RUN mkdir -p /app COPY . /app WORKDIR /app EXPOSE 3000
RUN npm install RUN npm run build CMD ["npm", "start"]
复制代码
访问阿里云容器镜像服务
登陆后,建立命名空间,建立镜像仓库,这里以 psl_one
为例
因为这里经过 jenkins 来构建 docker 镜像,因此再也不配置代码源,选择本地仓库并建立 (阿里云镜像服务自己也提供了镜像构建功能)
![]()
![]()
进入新建立的镜像仓库,能够在镜像版本中查看已上传的镜像列表,在基本信息中查看该仓库的基本信息,其第三条说明将镜像推送到Registry中有接下来要用到的指令:
$ sudo docker login --username=[username] registry.cn-qingdao.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn-qingdao.aliyuncs.com/[命名空间]/hjxy_test:[镜像版本号]
$ sudo docker push registry.cn-qingdao.aliyuncs.com/[命名空间]/hjxy_test:[镜像版本号]
复制代码
首先安装一些必要的插件。进入系统管理->插件管理->可选插件,在过滤中搜索须要的插件
http://mirror.xmission.com/jenkins/updates/update-center.json
Localization: Chinese (Simplified)
简体中文语言包SSH Plugin
经过ssh链接远程服务器执行shell脚本Git Parameter Plug-In
在参数化构建过程选项下得到git相关参数,如branch/tag等Generic Webhook Trigger Plugin
构建触发器插件,接收一个http请求,与webhook配合,触发job执行构建nvm-wrapper
提供一个nodejs的构建环境Email Extension Plugin
设置邮件通知的插件进入系统管理 -> 系统设置,进行 ssh 配置
进入系统管理 -> 管理用户,查看|生成用户 id 和用户 token
进入系统管理 -> 系统设置,配置邮件通知,这里以 qq 邮箱为例
点击新建任务,选择建立一个自由风格的项目,为 nuxt_demo 建立一个镜像构建、上传并部署的 jenkins job。约定这个任务只用于当开发者向仓库提交 tag 时触发构建,不能进行主动构建(就算主动构建,由于缺乏 tag 信息也会失败)
Generic Webhook Trigger
因为 GitLab 的 webhook 传递来的 ref 信息形如
refs/tags/v1.0.0
,因此须要在执行脚本中进行字符串拆分,获取须要的部分:$ref|cut -c11-
Execute shell script on remote host using ssh
ssh site
即刚才在系统设置中配置的ssh ,pre build script
能够置空或根据须要填写,这里主要配置post build script
,其中***-deploy.sh
为远程服务器中的部署脚本,将在构建任务结束后触发执行
这里只选择 ![]()
tag push events
,点击add webhook
添加钩子,并点击 test按钮测试是否可用若成功,将触发相应的 jenkins job 执行 ![]()
/var/myprojects/shell_script/***-deploy.sh
#!/bin/bash
echo "deploy start"
preImageId=$(docker inspect -f {{.Image}} <Container Name>) # 获取当前运行容器的镜像Id,稍后用以删除镜像
docker login -u <username> -p <password> <Docker Registry> # 登陆远程镜像仓库
docker <Image>:$1 # 拉取最新构建的镜像
docker rm -f <Container Name> || true # 强制删除当前运行的容器
docker run -d --name <Container Name> -p 3000:3001 --restart=always <Image>:$1 # 使用最新拉取的镜像开启新的容器
docker rm -f <Container Name2> || true # 强制删除当前运行的容器
docker run -d --name <Container Name2> -p 3001:3001 --restart=always <Image>:$1 # 使用最新拉取的镜像开启新的容器
docker image rm -f ${preImageId} # 移除上一个版本的镜像
echo "deploy end"
复制代码
upstream nuxt_demo {
server localhost:3000 max_fails=1 fail_timeout=15s weight=1;
server localhost:3001 max_fails=1 fail_timeout=15s weight=1;
}
server {
listen 80;
server_name test.xyz.docker.com;
location / {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Nginx-Proxy true;
proxy_cache_bypass $http_upgrade;
proxy_pass http://nuxt_demo;
}
}
复制代码
参考第六节中的部分,但再也不配置构建步骤。 在general选项卡中勾选参数化构建过程选项,并配置以下参数:
build with parameters
build with parameters
将看到以下界面。releasetag 默认值为配置时填写的 notag,此时可参照 taglist 填写正确的值执行构建操做:
以上,提供一种可行思路的大体操做步骤,如要在生产环境实行还需细细考虑具体的场景和更多的细节问题~