开发了一个功能简洁、部署方便的个人博客系统 - eblog,详情见:eblog
可以看到eblog由多个服务组成,这么多服务如何做到一键部署的?这么多镜像如何生成的?本文将为大家揭秘快速部署相关的问题。镜像制作相关文件见:镜像制作文件
快速部署
针对多服务的部署,自然最优的方案是docker,docker介绍见:容器化技术的使用和优缺点
将各个服务做成镜像,将运行环境等都集成到镜像中,方便各处部署和维护。那么这么多镜像如何组织起来?有两种方案:
- docker compose
- shell脚本
docker compose需要在服务器上额外安装,个人用的是1核2G的云服务器,不适合安装太多东西,另外docker compose不够灵活,而shell可根据项目情况获取需要的数据,可以自行定义各个步骤,就使用了通用的shell脚本。
eblog部署脚本见:eblog部署shell脚本
镜像包单独提供。
脚本分为下面几个部分:
- 全局变量
- 全局操作
- 各个服务的部署
全局变量
整个项目镜像包名称、各个服务对应的镜像名称、挂载目录。这里填写各个服务制作生成的镜像名称,以及各个镜像生成的总的镜像包。如果有新的镜像,这些镜像名称需要替换。
还有指明挂载目录,这是宿主机上的位置,用于存储容器内的文件。
全局操作
生成项目需要的全局密码、服务器IP、加载镜像。项目用到mariadb,需要给mariadb设置密码,这里随机生成密码,同时各个连接mariadb的服务需要填写这个密码。部分服务需要用到服务器的IP。加载镜像使用了docker原始的命令:docker load -i
。
各个服务的部署
按照依赖关系,即是:先启动基础服务、再执行初始化,最后启动后端服务和前端服务,依次部署各个服务。
类别 | 名称 | 作用 |
---|---|---|
基础镜像 | portainer镜像 | 提供镜像、容器等管理服务 |
基础镜像 | mariadb镜像 | 提供mariadb服务 |
基础镜像 | ngxin镜像 | 提供ngxin服务 |
初始化镜像 | sql镜像 | 建库建表,一般只执行一次 |
后端镜像 | java镜像 | 博客后端服务 |
前端镜像 | 静态文件镜像 | 前端界面 |
下面详细讲解服务启动命令:
- 容器的启动也是使用原生的docker命令:
docker run
-d
,使容器在后台运行--net=host
,网络模式使用host,即是使用同宿主机在同一个网络-v
,即是目录挂载,将容器内的文件挂载在宿主机上,实现了容器和宿主机的文件读写--name
,容器的名称-e
,环境变量,容器内的服务会读取这个变量- 最后是使用的镜像名称
- 脚本中会看到
sleep 1m
命令,作用是担心上一步的服务还未完全启动,而下一步的服务又依赖这个服务,所以停顿1分钟之后再启动下一步服务。例如mariadb初始化服务需要等mariadb启动后才能执行。
在Linux终端上输入命令./deploy.sh
,即会按照脚本内容顺序执行命令,完成整个项目的启动。
镜像制作
镜像分为三类:
- 项目依赖的镜像。指为了制作项目镜像,需要以一些已有的镜像作为基础,这些镜像从docker hub拉取。
- 项目可直接使用的镜像。主要是指基础镜像,拉取后直接使用。
- 项目的镜像。项目编排及运行需要的镜像。
mariadb镜像
属可直接使用的镜像,从docker hub拉取即可,也可以打上tag,记录版本、拉取时间等。
# 构建mariadb基础镜像
sudo docker pull mariadb:latest
sudo docker tag mariadb:latest mariadb:20200329_204923
类似的镜像有:redis、mongo等,拉取后直接启动就能连接服务使用。
sql脚本镜像
用于连接mariadb,执行sql语句。这里将sql脚本镜像同mariadb服务镜像分开了,mariadb可重启,sql脚本可选择是否初始化,避免重启mariadb导致数据库初始化,同时后续有新的建库建表操作可单独增加sql镜像即可。首先需要以mysql-client为基础,先拉取这个镜像
# 构建mariadb初始化基础镜像
# 镜像来源:https://registry.hub.docker.com/r/arey/mysql-client
sudo docker pull arey/mysql-client:latest
sudo docker tag arey/mysql-client:latest mysql-client:20210809_194301
Dockerfile需要指明依赖的基础镜像,再将sql脚本和执行sql的shell脚本拷贝到镜像中,最后执行shell。内容如下:
# 使用mysql-client镜像
FROM mysql-client:20210809_194301
# 设置时区
ENV TZ=Asia/Shanghai
# 将sql语句、shell脚本拷贝到scripts文件夹下
ADD *.sql /scripts/
ADD init_db.sh /scripts/
# 执行shell,覆盖父类ENTRYPOINT
ENTRYPOINT ["./scripts/init_db.sh"]
sql文件即是建库建表的语句。init_db.sh主要是连接mariadb执行sql。内容见:init_db.sh
核心语句是:
mysql -uroot -p$MYSQL_ROOT_PASSWORD -h127.0.0.1 --default-character-set=utf8mb4 -e "source $file_name;"
nginx镜像
按照mariadb的思路,nginx镜像应该也分成基础镜像和初始化镜像两个部分。基础镜像提供nginx服务,初始化镜像初始化项目需要的静态文件、路由转发等信息。但是本项目中,路由转发等信息比较少,且不会经常变动,重启nginx也不会导致数据丢失,因此这里的nginx镜像包含了项目的路由转发等配置文件。
ngxin镜像包含下面文件:
nginx.conf:主要是eblog用到的路由转发信息,在Dockerfile中会指明需要拷贝到nginx镜像中。同时会指明静态文件的位置,静态文件位置会挂载到宿主机上。
startup.sh:启动nginx
前端镜像
eblog前端使用vue开发,vue项目在编译后会生成静态文件,包括html、js、css、图片等数据。这些就是全部的前端文件,制作镜像时将这些文件移动到镜像中,启动镜像时将这些文件释放到容器内的目录中。而这个目录会挂载到宿主机上,而宿主机的这个位置和nginx静态文件指向的位置相同,最终nginx会读取到前端镜像的静态文件。
Dockerfile文件内容如下:
# 来源镜像
FROM alpine:20200330_212034
ENV TZ=Asia/Shanghai
# 将vue生成后的静态文件拷贝到临时目录中
ADD web /tmp/web
# 将之前的静态文件清理掉,再将临时目录中的静态文件移动到project目录中
# 不能直接清理掉web目录,已经挂载了
CMD rm -rf /opt/project/web/index.html && rm -rf /opt/project/web/static && mv /tmp/web/* /opt/project/web/
后端服务镜像
后端镜像以jkd镜像为基础,加入jar包即可。Dockerfile文件内容如下:
# 来源镜像
FROM openjdk:8-jdk-alpine
ENV TZ=Asia/Shanghai
ARG JAR_NAME=post-service.jar
ADD ${JAR_NAME} app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
项目的镜像制作目录
以后端服务的镜像为例,主要包括下面目录及文件:
主要包括:Dockerfile、docker_build_service.sh、docker_build.sh等
- Dockerfile
指明镜像是以哪个镜像为基础的,生成镜像需要拷贝哪些文件,容器启动时会执行哪些命令。 - docker_build_service.sh
打镜像之前需要做很多准备工作,例如后端镜像需要先生成jar包,前端镜像需要先构建静态文件,再需要将生成的文件拷贝到docker目录中。同时需要生成唯一且容易区分的镜像名称,eblog镜像名称命名规则是:工程名称:版本号_分支名称_日期_时间_commitid。这些信息需要shell脚本来获取。最终执行docker build
命令。这些操作基本相同,项目中做成了通用脚本,各个项目再传递参数调用。 - docker_build.sh
调用docker_build_service.sh文件,传递需要的参数。